]> git.neil.brown.name Git - history.git/commitdiff
Linux 2.4.0-test10pre5 2.4.0-test10pre5
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:39:35 +0000 (15:39 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:39:35 +0000 (15:39 -0500)
    - Mikael Pettersson: more Pentium IV cleanup.
    - David Miller: non-x86 platforms missed "pte_same()".
    - Russell King: NFS invalidate_inode_pages() can do bad things!
    - Randy Dunlap: usb-core.c is gone - module fix
    - Ben LaHaise: swapcache fixups for the new atomic pte update code
    - Oleg Drokin: fix nm256_audio memory region confusion
    - Randy Dunlap: USB printer fixes
    - David Miller: sparc updates
    - David Miller: off-by-one error in /proc socket dumper
    - David Miller: restore non-local bind() behaviour.
    - David Miller: wakeups on socket shutdown()
    - Jeff Garzik: DEPCA net drvr fixes and CodingStyle
    - Jeff Garzik: netsemi net drvr fix
    - Jeff Garzik & Andrea Arkangeli: keyboard cleanup
    - Jeff Garzik: VIA audio update
    - Andrea Arkangeli: mxcsr initialization cleanup and fix
    - Gabriel Paubert: better twd_i387_to_fxsr() emulation
    - Andries Brouwer: proper error return in ext2 mkdir()

48 files changed:
Documentation/DocBook/via-audio.tmpl
arch/i386/kernel/i387.c
arch/i386/kernel/microcode.c
arch/i386/kernel/setup.c
arch/i386/kernel/traps.c
arch/i386/mm/init.c
arch/sparc/mm/init.c
arch/sparc64/defconfig
arch/sparc64/kernel/ioctl32.c
arch/sparc64/mm/init.c
arch/sparc64/solaris/socksys.c
drivers/char/pc_keyb.c
drivers/char/q40_keyb.c
drivers/net/depca.c
drivers/net/natsemi.c
drivers/sbus/audio/audio.c
drivers/sbus/char/envctrl.c
drivers/sound/nm256_audio.c
drivers/sound/via82cxxx_audio.c
drivers/usb/Makefile
drivers/usb/printer.c
drivers/usb/usb-core.c [deleted file]
drivers/usb/usb.c
drivers/video/mdacon.c
fs/ext2/namei.c
include/asm-generic/pgtable.h
include/asm-i386/bugs.h
include/asm-i386/elf.h
include/asm-i386/i387.h
include/asm-i386/user.h
include/asm-sparc/pgtable.h
include/asm-sparc64/envctrl.h
include/asm-sparc64/pgtable.h
include/linux/mm.h
include/linux/sysctl.h
mm/filemap.c
mm/vmscan.c
net/ipv4/af_inet.c
net/ipv4/raw.c
net/ipv4/sysctl_net_ipv4.c
net/ipv4/tcp_ipv4.c
net/ipv4/udp.c
net/ipv6/af_inet6.c
net/ipv6/raw.c
net/ipv6/tcp_ipv6.c
net/ipv6/udp.c
net/packet/af_packet.c
net/unix/af_unix.c

index f1fc19a5d8aa88bddc613d7585ae5e75af14a778..6c0677322d8c1521ca929230a243ddc7589f545e 100644 (file)
   <chapter id="intro">
       <title>Introduction</title>
   <para>
-       The Via VT82C686A and VT82C686A "super southbridge" chips contain
-       AC97-compatible audio logic which features dual full-duplex 16-bit stereo
-       PCM sound channels, plus a third PCM channel intended for use
+       The Via VT82C686A "super southbridge" chips contain
+       AC97-compatible audio logic which features dual 16-bit stereo
+       PCM sound channels (full duplex), plus a third PCM channel intended for use
        in hardware-assisted FM synthesis.
   </para>
   <para>
        The current Linux kernel audio driver for this family of chips
-       supports audio playback, but recording and hardware-assisted
-       FM support features are not yet available.
+       supports audio playback and recording, but hardware-assisted
+       FM features, and hardware buffer direct-access (mmap)
+       support are not yet available.
   </para>
   <para>
        This driver supports any Linux kernel version after 2.3.50.
      <title>Known Bugs And Assumptions</title>
   <para>
   <variablelist>
-    <varlistentry><term>Recording support</term>
-    <listitem>
-    <para>
-       Recording support is currently missing.
-    </para>
-    </listitem></varlistentry>
-
     <varlistentry><term>MMAP support</term>
     <listitem>
     <para>
     </para>
     </listitem></varlistentry>
 
-    <varlistentry><term>Broken apps</term>
-    <listitem>
-    <para>
-       Applications which attempt to open the sound device in read/write
-       mode (O_RDWR) will fail.  This is incorrect OSS behavior, but since
-       this driver will eventually support recording as well as playback,
-       we will be able to (in the future) support even broken programs which
-       unconditionally use O_RDWR.
-    </para>
-    </listitem></varlistentry>
-
   </variablelist>
        
   </para>
      <title>Random Notes</title>
   <para>
        Two /proc pseudo-files provide diagnostic information.  This is generally
-       not useful to most users.  Power users can disable VIA_PROC_FS macro in the 
-       driver source code, and remove the /proc support code.  In any case, once
+       not useful to most users.  Power users can disable CONFIG_SOUND_VIA82CXXX_PROCFS,
+       and remove the /proc support code.  Once
        version 2.0.0 is released, the /proc support code will be disabled by
        default.  Available /proc pseudo-files:
   </para>
        a vendor id of 0x1106, and a device id of 0x3058.  Subsystem vendor
        and device ids are not examined.
   </para>
-  <para>
-       Only supports a single sound chip, as this is a motherboard chipset.
-       Some architecture remains for multiple cards, feel free to submit
-       a patch to clean some of that up.
-  </para>
-  <para>
-       No consideration for SMP, this chipset is not known to be found on
-       any SMP motherboards.  However, spin_locks must be used anyway in order
-       to handle interrupts correctly.
-  </para>
   <para>
        GNU indent formatting options:  -kr -i8 -pcs
   </para>
   <chapter id="changelog">
       <title>Driver ChangeLog</title>
 
+<sect1 id="version1112"><title>
+Version 1.1.12
+</title>
+  <itemizedlist spacing=compact>
+   <listitem>
+    <para>
+    mmap bug fixes from Linus.
+    </para>
+   </listitem>
+  </itemizedlist>
+</sect1>
+
+<sect1 id="version1111"><title>
+Version 1.1.11
+</title>
+  <itemizedlist spacing=compact>
+   <listitem>
+    <para>
+    Many more bug fixes.  mmap enabled by default, but may still be buggy.
+    </para>
+   </listitem>
+
+   <listitem>
+    <para>
+    Uses new and spiffy method of mmap'ing the DMA buffer, based
+    on a suggestion from Linus.
+    </para>
+   </listitem>
+  </itemizedlist>
+</sect1>
+
+<sect1 id="version1110"><title>
+Version 1.1.10
+</title>
+  <itemizedlist spacing=compact>
+   <listitem>
+    <para>
+    Many bug fixes.  mmap enabled by default, but may still be buggy.
+    </para>
+   </listitem>
+  </itemizedlist>
+</sect1>
+
+<sect1 id="version119"><title>
+Version 1.1.9
+</title>
+  <itemizedlist spacing=compact>
+   <listitem>
+    <para>
+    Redesign and rewrite audio playback implementation.  (faster and smaller, hopefully)
+    </para>
+   </listitem>
+
+   <listitem>
+    <para>
+    Implement recording and full duplex (DSP_CAP_DUPLEX) support.
+    </para>
+   </listitem>
+
+   <listitem>
+    <para>
+    Make procfs support optional.
+    </para>
+   </listitem>
+
+   <listitem>
+    <para>
+    Quick interrupt status check, to lessen overhead in interrupt
+    sharing situations.
+    </para>
+   </listitem>
+
+   <listitem>
+    <para>
+    Add mmap(2) support.  Disabled for now, it is still buggy and experimental.
+    </para>
+   </listitem>
+
+   <listitem>
+    <para>
+    Surround all syscalls with a semaphore for cheap and easy SMP protection.
+    </para>
+   </listitem>
+
+   <listitem>
+    <para>
+    Fix bug in channel shutdown (hardware channel reset) code.
+    </para>
+   </listitem>
+
+   <listitem>
+    <para>
+    Remove unnecessary spinlocks (better performance).
+    </para>
+   </listitem>
+
+   <listitem>
+    <para>
+    Eliminate "unknown AFMT" message by using a different method
+    of selecting the best AFMT_xxx sound sample format for use.
+    </para>
+   </listitem>
+
+   <listitem>
+    <para>
+    Support for realtime hardware pointer position reporting
+    (DSP_CAP_REALTIME, SNDCTL_DSP_GETxPTR ioctls)
+    </para>
+   </listitem>
+
+   <listitem>
+    <para>
+    Support for capture/playback triggering
+    (DSP_CAP_TRIGGER, SNDCTL_DSP_SETTRIGGER ioctls)
+    </para>
+   </listitem>
+
+   <listitem>
+    <para>
+    SNDCTL_DSP_SETDUPLEX and SNDCTL_DSP_POST ioctls now handled.
+    </para>
+   </listitem>
+
+   <listitem>
+    <para>
+    Rewrite open(2) and close(2) logic to allow only one user at
+    a time.  All other open(2) attempts will sleep until they succeed.
+    FIXME: open(O_RDONLY) and open(O_WRONLY) should be allowed to succeed.
+    </para>
+   </listitem>
+
+   <listitem>
+    <para>
+    Reviewed code to ensure that SMP and multiple audio devices
+    are fully supported.
+    </para>
+   </listitem>
+
+  </itemizedlist>
+</sect1>
+
 <sect1 id="version118"><title>
 Version 1.1.8
 </title>
index 5044c7cadb37a522a095f6b9c27894f0cda774cd..c3d052e8a2e11c8be8242909f95848312a21dbb6 100644 (file)
 #define HAVE_HWFP 1
 #endif
 
+/*
+ * The _current_ task is using the FPU for the first time
+ * so initialize it and set the mxcsr to its default
+ * value at reset if we support FXSR and then
+ * remeber the current task has used the FPU.
+ */
+void init_fpu(void)
+{
+       __asm__("fninit");
+       if ( HAVE_FXSR )
+               load_mxcsr(0x1f80);
+               
+       current->used_math = 1;
+}
+
 /*
  * FPU lazy state save handling.
  */
@@ -66,16 +81,16 @@ void restore_fpu( struct task_struct *tsk )
 
 static inline unsigned short twd_i387_to_fxsr( unsigned short twd )
 {
-       unsigned short ret = 0;
-       int i;
-
-       for ( i = 0 ; i < 8 ; i++ ) {
-               if ( (twd & 0x3) != 0x3 ) {
-                       ret |= (1 << i);
-               }
-               twd = twd >> 2;
-       }
-       return ret;
+       unsigned int tmp; /* to avoid 16 bit prefixes in the code */
+       /* Transform each pair of bits into 01 (valid) or 00 (empty) */
+        tmp = ~twd;
+        tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
+        /* and move the valid bits to the lower byte. */
+        tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
+        tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
+        tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
+        return tmp;
 }
 
 static inline unsigned long twd_fxsr_to_i387( struct i387_fxsave_struct *fxsave )
@@ -92,8 +107,8 @@ static inline unsigned long twd_fxsr_to_i387( struct i387_fxsave_struct *fxsave
                if ( twd & 0x1 ) {
                        st = (struct _fpxreg *) FPREG_ADDR( fxsave, i );
 
-                       switch ( st->exponent ) {
-                       case 0xffff:
+                       switch ( st->exponent & 0x7fff ) {
+                       case 0x7fff:
                                tag = 2;                /* Special */
                                break;
                        case 0x0000:
index 80536a1c4deb2b8c8808b5599d368b97ab66c2a7..f0e3ae633122ade376aadd6970738d49191261b7 100644 (file)
@@ -177,7 +177,7 @@ static void do_update_one(void *unused)
 
        req->err = 1; /* assume the worst */
 
-       if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6){
+       if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 != 6){
                printk(KERN_ERR "microcode: CPU%d not an Intel P6\n", cpu_num);
                return;
        }
index 9059adcbcb4a12f5f80295d571066c761d1e1936..7ac7fdb366f7ddfa32335207a796b71ce4ce47af 100644 (file)
@@ -1549,7 +1549,7 @@ void __init identify_cpu(struct cpuinfo_x86 *c)
                        /* Pentium IV. */
                        if (c->x86 == 15) {
                                get_model_name(c);
-                               goto name_decoded;
+                               return;
                        }
 
                        /* Names for the Pentium II/Celeron processors 
@@ -1688,12 +1688,12 @@ int get_cpuinfo(char * buffer)
 #endif
                p += sprintf(p,"processor\t: %d\n"
                        "vendor_id\t: %s\n"
-                       "cpu family\t: %c\n"
+                       "cpu family\t: %d\n"
                        "model\t\t: %d\n"
                        "model name\t: %s\n",
                        n,
                        c->x86_vendor_id[0] ? c->x86_vendor_id : "unknown",
-                       c->x86 + '0',
+                       c->x86,
                        c->x86_model,
                        c->x86_model_id[0] ? c->x86_model_id : "unknown");
 
index 953c23d5504c73723b367f985578ac10a2f3bc80..ae87ded927f5f816f634e297224046c5f77d749d 100644 (file)
@@ -741,11 +741,7 @@ asmlinkage void math_state_restore(struct pt_regs regs)
        if (current->used_math) {
                restore_fpu(current);
        } else {
-               /*
-                *      Our first FPU usage, clean the chip.
-                */
-               __asm__("fninit");
-               current->used_math = 1;
+               init_fpu();
        }
        current->flags |= PF_USEDFPU;   /* So we fnsave on switch_to() */
 }
index 4b3cc4dfc3b621d33762b30083406e47248cabaa..39a6ce0f808d78d649262f30dc34256803a68697 100644 (file)
@@ -495,28 +495,7 @@ void __init paging_init(void)
  * This function cannot be __init, since exceptions don't work in that
  * section.
  */
-static int do_test_wp_bit(unsigned long vaddr)
-{
-       char tmp_reg;
-       int flag;
-
-       __asm__ __volatile__(
-               "       movb %0,%1      \n"
-               "1:     movb %1,%0      \n"
-               "       xorl %2,%2      \n"
-               "2:                     \n"
-               ".section __ex_table,\"a\"\n"
-               "       .align 4        \n"
-               "       .long 1b,2b     \n"
-               ".previous              \n"
-               :"=m" (*(char *) vaddr),
-                "=q" (tmp_reg),
-                "=r" (flag)
-               :"2" (1)
-               :"memory");
-       
-       return flag;
-}
+static int do_test_wp_bit(unsigned long vaddr);
 
 void __init test_wp_bit(void)
 {
@@ -652,6 +631,30 @@ void __init mem_init(void)
 
 }
 
+/* Put this after the callers, so that it cannot be inlined */
+static int do_test_wp_bit(unsigned long vaddr)
+{
+       char tmp_reg;
+       int flag;
+
+       __asm__ __volatile__(
+               "       movb %0,%1      \n"
+               "1:     movb %1,%0      \n"
+               "       xorl %2,%2      \n"
+               "2:                     \n"
+               ".section __ex_table,\"a\"\n"
+               "       .align 4        \n"
+               "       .long 1b,2b     \n"
+               ".previous              \n"
+               :"=m" (*(char *) vaddr),
+                "=q" (tmp_reg),
+                "=r" (flag)
+               :"2" (1)
+               :"memory");
+       
+       return flag;
+}
+
 void free_initmem(void)
 {
        unsigned long addr;
index 457d4fed03b1d284bffbdb0bae61aecf0e05140a..9cbbb1c9c9c900936114545109cfb5e5d09e0e0d 100644 (file)
@@ -1,4 +1,4 @@
-/*  $Id: init.c,v 1.93 2000/08/31 11:40:55 anton Exp $
+/*  $Id: init.c,v 1.94 2000/10/19 00:49:51 davem Exp $
  *  linux/arch/sparc/mm/init.c
  *
  *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
index 0637e5bbdb9db6f9ac577502ee1f88ef66e8632a..571038083206aa904790651fae5896f5fcafdbba 100644 (file)
@@ -264,6 +264,7 @@ CONFIG_BLK_DEV_NS87415=y
 # CONFIG_BLK_DEV_PDC202XX is not set
 # CONFIG_PDC202XX_BURST is not set
 # CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_SLC90E66 is not set
 # CONFIG_BLK_DEV_TRM290 is not set
 # CONFIG_BLK_DEV_VIA82CXXX is not set
 # CONFIG_IDE_CHIPSETS is not set
index 84b4224e54c6de08664c9ce58a3533606c87a479..9f7cd59e90571b29dfdeeda547e663d719f0b460 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.98 2000/08/16 12:33:00 davem Exp $
+/* $Id: ioctl32.c,v 1.99 2000/10/17 16:20:33 davem Exp $
  * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
  *
  * Copyright (C) 1997-2000  Jakub Jelinek  (jakub@redhat.com)
@@ -3221,9 +3221,16 @@ COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+7, int))
 /* Little p (/dev/rtc, /dev/envctrl, etc.) */
 COMPATIBLE_IOCTL(RTCGET)
 COMPATIBLE_IOCTL(RTCSET)
-COMPATIBLE_IOCTL(I2CIOCSADR)
-COMPATIBLE_IOCTL(I2CIOCGADR)
-COMPATIBLE_IOCTL(D7SIOCRD)
+COMPATIBLE_IOCTL(ENVCTRL_RD_WARNING_TEMPERATURE)
+COMPATIBLE_IOCTL(ENVCTRL_RD_SHUTDOWN_TEMPERATURE)
+COMPATIBLE_IOCTL(ENVCTRL_RD_CPU_TEMPERATURE)
+COMPATIBLE_IOCTL(ENVCTRL_RD_FAN_STATUS)
+COMPATIBLE_IOCTL(ENVCTRL_RD_VOLTAGE_STATUS)
+COMPATIBLE_IOCTL(ENVCTRL_RD_SCSI_TEMPERATURE)
+COMPATIBLE_IOCTL(ENVCTRL_RD_ETHERNET_TEMPERATURE)
+COMPATIBLE_IOCTL(ENVCTRL_RD_MTHRBD_TEMPERATURE)
+COMPATIBLE_IOCTL(ENVCTRL_RD_CPU_VOLTAGE)
+/* COMPATIBLE_IOCTL(D7SIOCRD) same value as ENVCTRL_RD_VOLTAGE_STATUS */
 COMPATIBLE_IOCTL(D7SIOCWR)
 COMPATIBLE_IOCTL(D7SIOCTM)
 /* Little m */
index eafbe27cae471b6045d7c0b85a2450bdb8d26959..65fbd6e3762dc94232428a8ad29347bc8e17bd2d 100644 (file)
@@ -1,4 +1,4 @@
-/*  $Id: init.c,v 1.156 2000/09/21 06:34:48 anton Exp $
+/*  $Id: init.c,v 1.157 2000/10/19 00:49:52 davem Exp $
  *  arch/sparc64/mm/init.c
  *
  *  Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu)
index 2ad56ad9d052fa64f28d5a1440a29fb8c3d99975..bf2182381ec3787156015033a5246e79694b52fe 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: socksys.c,v 1.16 2000/07/27 00:03:48 davem Exp $
+/* $Id: socksys.c,v 1.17 2000/10/19 00:49:53 davem Exp $
  * socksys.c: /dev/inet/ stuff for Solaris emulation.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
index 117ae4ec1100cc1b55f045acff7d2b3f8e38d68e..4a77583256a8e33db5b1cbecddfb6dbb9fcfa99b 100644 (file)
@@ -65,7 +65,7 @@ static void aux_write_ack(int val);
 static void __aux_write_ack(int val);
 #endif
 
-spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
 static unsigned char handle_kbd_event(void);
 
 /* used only by send_data - set by keyboard_interrupt */
@@ -448,7 +448,7 @@ static unsigned char handle_kbd_event(void)
        unsigned char status = kbd_read_status();
        unsigned int work = 10000;
 
-       while (status & KBD_STAT_OBF) {
+       while ((--work > 0) && (status & KBD_STAT_OBF)) {
                unsigned char scancode;
 
                scancode = kbd_read_input();
@@ -467,13 +467,10 @@ static unsigned char handle_kbd_event(void)
                }
 
                status = kbd_read_status();
-               
-               if (!--work) {
-                       printk(KERN_ERR "pc_keyb: controller jammed (0x%02X).\n",
-                               status);
-                       break;
-               }
        }
+               
+       if (!work)
+               printk(KERN_ERR "pc_keyb: controller jammed (0x%02X).\n", status);
 
        return status;
 }
@@ -481,14 +478,13 @@ static unsigned char handle_kbd_event(void)
 
 static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-       unsigned long flags;
-
 #ifdef CONFIG_VT
        kbd_pt_regs = regs;
 #endif
-       spin_lock_irqsave(&kbd_controller_lock, flags);
+
+       spin_lock_irq(&kbd_controller_lock);
        handle_kbd_event();
-       spin_unlock_irqrestore(&kbd_controller_lock, flags);
+       spin_unlock_irq(&kbd_controller_lock);
 }
 
 /*
index 7ee9818efbdf1925727a836601ffe5f2db5bf522..a0e41b189130a0882f011c04d11d4e43d44c45d8 100644 (file)
@@ -94,7 +94,7 @@ static unsigned char q40ecl[]=
 };
 
 
-spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
 
 
 /*
@@ -340,11 +340,10 @@ static int qprev=0;
 
 static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-       unsigned long flags;
        unsigned char status;
 
        disable_keyboard();
-       spin_lock_irqsave(&kbd_controller_lock, flags);
+       spin_lock(&kbd_controller_lock);
        kbd_pt_regs = regs;
 
        status = IRQ_KEYB_MASK & master_inb(INTERRUPT_REG);
@@ -386,7 +385,7 @@ static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs)
              keyup=1;
          }
 exit:
-       spin_unlock_irqrestore(&kbd_controller_lock, flags);
+       spin_unlock(&kbd_controller_lock);
        master_outb(-1,KEYBOARD_UNLOCK_REG); /* keyb ints reenabled herewith */
        enable_keyboard();
 }
index eb78e1e0ebc0cefe18be859f6d04f0b4e936c822..448baa8007d7b328a83870b46bffa78d49634424 100644 (file)
       0.5     14-Nov-98   Re-spin for 2.1.x kernels.
       0.51    27-Jun-99   Correct received packet length for CRC from
                            report by <worm@dkik.dk>
+      0.52    16-Oct-00   Fixes for 2.3 io memory accesses
+                          Fix show-stopper (ints left masked) in depca_interrupt
+                          by <peterd@pnd-pc.demon.co.uk>
 
     =========================================================================
 */
@@ -378,14 +381,21 @@ struct depca_private {
     char devname[DEPCA_STRLEN];    /* Device Product String                  */
     char adapter_name[DEPCA_STRLEN];/* /proc/ioports string                  */
     char adapter;                  /* Adapter type                           */
-    char mca_slot;                 /* MCA slot, if MCA else -1               */    struct depca_rx_desc *rx_ring; /* Pointer to start of RX descriptor ring */
-    struct depca_tx_desc *tx_ring; /* Pointer to start of TX descriptor ring */
+    char mca_slot;                 /* MCA slot, if MCA else -1               */
     struct depca_init  init_block;/* Shadow Initialization block            */
-    char *rx_memcpy[NUM_RX_DESC];  /* CPU virt address of sh'd memory buffs  */
-    char *tx_memcpy[NUM_TX_DESC];  /* CPU virt address of sh'd memory buffs  */
-    u_long bus_offset;             /* (E)ISA bus address offset vs LANCE     */
-    u_long sh_mem;                /* Physical start addr of shared mem area */
-    u_long dma_buffs;             /* LANCE Rx and Tx buffers start address. */
+/* CPU address space fields */
+    struct depca_rx_desc *rx_ring; /* Pointer to start of RX descriptor ring */
+    struct depca_tx_desc *tx_ring; /* Pointer to start of TX descriptor ring */
+    void *rx_buff[NUM_RX_DESC];    /* CPU virt address of sh'd memory buffs  */
+    void *tx_buff[NUM_TX_DESC];    /* CPU virt address of sh'd memory buffs  */
+    void *sh_mem;                  /* CPU mapped virt address of device RAM  */
+/* Device address space fields */
+    u_long device_ram_start;       /* Start of RAM in device addr space      */
+/* Offsets used in both address spaces */
+    u_long rx_ring_offset;         /* Offset from start of RAM to rx_ring    */
+    u_long tx_ring_offset;         /* Offset from start of RAM to tx_ring    */
+    u_long buffs_offset;          /* LANCE Rx and Tx buffers start address. */
+/* Kernel-only (not device) fields */
     int        rx_new, tx_new;            /* The next free ring entry               */
     int rx_old, tx_old;                   /* The ring entries to be free()ed.       */
     struct net_device_stats stats;
@@ -517,215 +527,228 @@ depca_probe(struct net_device *dev)
 static int __init 
 depca_hw_init(struct net_device *dev, u_long ioaddr, int mca_slot)
 {
-  struct depca_private *lp;
-  int i, j, offset, netRAM, mem_len, status=0;
-  s16 nicsr;
-  u_long mem_start=0, mem_base[] = DEPCA_RAM_BASE_ADDRESSES;
+       struct depca_private *lp;
+       int i, j, offset, netRAM, mem_len, status=0;
+       s16 nicsr;
+       u_long mem_start=0, mem_base[] = DEPCA_RAM_BASE_ADDRESSES;
 
-  STOP_DEPCA;
+       STOP_DEPCA;
 
-  nicsr = inb(DEPCA_NICSR);
-  nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | IM);
-  outb(nicsr, DEPCA_NICSR);
-
-  if (inw(DEPCA_DATA) == STOP) {
-    do {
-      strcpy(name, (adapter_name ? adapter_name : ""));
-      mem_start = (mem ? mem & 0xf0000 : mem_base[mem_chkd++]);
-      DepcaSignature(name, mem_start);
-    } while (!mem && mem_base[mem_chkd] && (adapter == unknown));
-
-    if ((adapter != unknown) && mem_start) {        /* found a DEPCA device */
-      dev->base_addr = ioaddr;
-
-      if (mca_slot != -1) {
-       printk("%s: %s at 0x%04lx (MCA slot %d)", dev->name, name, 
-                                                          ioaddr, mca_slot);
-      } else if ((ioaddr & 0x0fff) == DEPCA_EISA_IO_PORTS) { /* EISA slot address */
-       printk("%s: %s at 0x%04lx (EISA slot %d)", 
-                           dev->name, name, ioaddr, (int)((ioaddr>>12)&0x0f));
-      } else {                             /* ISA port address */
-       printk("%s: %s at 0x%04lx", dev->name, name, ioaddr);
-      }
+       nicsr = inb(DEPCA_NICSR);
+       nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | IM);
+       outb(nicsr, DEPCA_NICSR);
 
-      printk(", h/w address ");
-      status = get_hw_addr(dev);
-      for (i=0; i<ETH_ALEN - 1; i++) { /* get the ethernet address */
-       printk("%2.2x:", dev->dev_addr[i]);
-      }
-      printk("%2.2x", dev->dev_addr[i]);
+       if (inw(DEPCA_DATA) != STOP) {
+               return -ENXIO;
+       }
+
+       do {
+               strcpy(name, (adapter_name ? adapter_name : ""));
+               mem_start = (mem ? mem & 0xf0000 : mem_base[mem_chkd++]);
+               DepcaSignature(name, mem_start);
+       } while (!mem && mem_base[mem_chkd] && (adapter == unknown));
+
+       if ((adapter == unknown) || !mem_start) { /* DEPCA device not found */
+               return -ENXIO;
+       }
+
+       dev->base_addr = ioaddr;
+
+       if (mca_slot != -1) {
+               printk("%s: %s at 0x%04lx (MCA slot %d)", dev->name, name, 
+                                                         ioaddr, mca_slot);
+       } else if ((ioaddr & 0x0fff) == DEPCA_EISA_IO_PORTS) { /* EISA slot address */
+               printk("%s: %s at 0x%04lx (EISA slot %d)", 
+                      dev->name, name, ioaddr, (int)((ioaddr>>12)&0x0f));
+       } else {                             /* ISA port address */
+               printk("%s: %s at 0x%04lx", dev->name, name, ioaddr);
+       }
+
+       printk(", h/w address ");
+       status = get_hw_addr(dev);
+       for (i=0; i<ETH_ALEN - 1; i++) { /* get the ethernet address */
+               printk("%2.2x:", dev->dev_addr[i]);
+       }
+       printk("%2.2x", dev->dev_addr[i]);
+
+       if (status != 0) {
+               printk("      which has an Ethernet PROM CRC error.\n");
+               return -ENXIO;
+       }
 
-      if (status == 0) {
        /* Set up the maximum amount of network RAM(kB) */
        netRAM = ((adapter != DEPCA) ? 64 : 48);
-       if ((nicsr & _128KB) && (adapter == de422)) netRAM = 128;
+       if ((nicsr & _128KB) && (adapter == de422))
+               netRAM = 128;
        offset = 0x0000;
 
        /* Shared Memory Base Address */ 
        if (nicsr & BUF) {
-         offset = 0x8000;              /* 32kbyte RAM offset*/
-         nicsr &= ~BS;                 /* DEPCA RAM in top 32k */
-         netRAM -= 32;
+               offset = 0x8000;        /* 32kbyte RAM offset*/
+               nicsr &= ~BS;           /* DEPCA RAM in top 32k */
+               netRAM -= 32;
        }
        mem_start += offset;            /* (E)ISA start address */
        if ((mem_len = (NUM_RX_DESC*(sizeof(struct depca_rx_desc)+RX_BUFF_SZ) +
                        NUM_TX_DESC*(sizeof(struct depca_tx_desc)+TX_BUFF_SZ) +
-                       sizeof(struct depca_init))) <=
-           (netRAM<<10)) {
-         printk(",\n      has %dkB RAM at 0x%.5lx", netRAM, mem_start);
-
-         /* Enable the shadow RAM. */
-         if (adapter != DEPCA) {
-           nicsr |= SHE;
-           outb(nicsr, DEPCA_NICSR);
-         }
+                       sizeof(struct depca_init)))
+           > (netRAM<<10)) {
+               printk(",\n       requests %dkB RAM: only %dkB is available!\n",
+                       (mem_len >> 10), netRAM);
+               return -ENXIO;
+       }
+
+       printk(",\n      has %dkB RAM at 0x%.5lx", netRAM, mem_start);
+
+       /* Enable the shadow RAM. */
+       if (adapter != DEPCA) {
+               nicsr |= SHE;
+               outb(nicsr, DEPCA_NICSR);
+       }
  
-         /* Define the device private memory */
-         dev->priv = (void *) kmalloc(sizeof(struct depca_private), GFP_KERNEL);
-         if (dev->priv == NULL)
-           return -ENOMEM;
-         lp = (struct depca_private *)dev->priv;
-         memset((char *)dev->priv, 0, sizeof(struct depca_private));
-         lp->adapter = adapter;
-         lp->mca_slot = mca_slot;
-         lp->lock = SPIN_LOCK_UNLOCKED;
-         sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
-         request_region(ioaddr, DEPCA_TOTAL_SIZE, lp->adapter_name);
-
-         /* Initialisation Block */
-         lp->sh_mem = mem_start;
-         mem_start += sizeof(struct depca_init);
-
-         /* Tx & Rx descriptors (aligned to a quadword boundary) */
-         mem_start = (mem_start + ALIGN) & ~ALIGN;
-         lp->rx_ring = (struct depca_rx_desc *)mem_start;
-
-         mem_start += (sizeof(struct depca_rx_desc) * NUM_RX_DESC);
-         lp->tx_ring = (struct depca_tx_desc *)mem_start;
-
-         mem_start += (sizeof(struct depca_tx_desc) * NUM_TX_DESC);
-         lp->bus_offset = mem_start & 0x00ff0000;
-         mem_start &= LA_MASK;           /* LANCE re-mapped start address */
-
-         lp->dma_buffs = mem_start;
-
-         /* Finish initialising the ring information. */
-         lp->rxRingMask = NUM_RX_DESC - 1;
-         lp->txRingMask = NUM_TX_DESC - 1;
-
-         /* Calculate Tx/Rx RLEN size for the descriptors. */
-         for (i=0, j = lp->rxRingMask; j>0; i++) {
-           j >>= 1;
-         }
-         lp->rx_rlen = (s32)(i << 29);
-         for (i=0, j = lp->txRingMask; j>0; i++) {
-           j >>= 1;
-         }
-         lp->tx_rlen = (s32)(i << 29);
+       /* Define the device private memory */
+       dev->priv = (void *) kmalloc(sizeof(struct depca_private), GFP_KERNEL);
+       if (dev->priv == NULL)
+               return -ENOMEM;
+       lp = (struct depca_private *)dev->priv;
+       memset((char *)dev->priv, 0, sizeof(struct depca_private));
+       lp->adapter = adapter;
+       lp->mca_slot = mca_slot;
+       lp->lock = SPIN_LOCK_UNLOCKED;
+       sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
+       if (!request_region(ioaddr, DEPCA_TOTAL_SIZE, lp->adapter_name)) {
+               printk(KERN_ERR "depca: I/O resource 0x%x @ 0x%lx busy\n",
+                      DEPCA_TOTAL_SIZE, ioaddr);
+               return -EBUSY;
+       }
+
+       /* Initialisation Block */
+       lp->sh_mem = ioremap(mem_start, mem_len);
+       if (lp->sh_mem == NULL) {
+               printk(KERN_ERR "depca: cannot remap ISA memory, aborting\n");
+               return -EIO;
+       }
+       lp->device_ram_start = mem_start & LA_MASK;
+       
+       offset = 0;
+       offset += sizeof(struct depca_init);
+
+       /* Tx & Rx descriptors (aligned to a quadword boundary) */
+       offset = (offset + ALIGN) & ~ALIGN;
+       lp->rx_ring = (struct depca_rx_desc *)(lp->sh_mem + offset);
+       lp->rx_ring_offset = offset;
+
+       offset += (sizeof(struct depca_rx_desc) * NUM_RX_DESC);
+       lp->tx_ring = (struct depca_tx_desc *)(lp->sh_mem + offset);
+       lp->tx_ring_offset = offset;
+
+       offset += (sizeof(struct depca_tx_desc) * NUM_TX_DESC);
+
+       lp->buffs_offset = offset;
+
+       /* Finish initialising the ring information. */
+       lp->rxRingMask = NUM_RX_DESC - 1;
+       lp->txRingMask = NUM_TX_DESC - 1;
+
+       /* Calculate Tx/Rx RLEN size for the descriptors. */
+       for (i=0, j = lp->rxRingMask; j>0; i++) {
+               j >>= 1;
+       }
+       lp->rx_rlen = (s32)(i << 29);
+       for (i=0, j = lp->txRingMask; j>0; i++) {
+               j >>= 1;
+       }
+       lp->tx_rlen = (s32)(i << 29);
 
-         /* Load the initialisation block */
-         depca_init_ring(dev);
+       /* Load the initialisation block */
+       depca_init_ring(dev);
 
-         /* Initialise the control and status registers */
-         LoadCSRs(dev);
+       /* Initialise the control and status registers */
+       LoadCSRs(dev);
 
-         /* Enable DEPCA board interrupts for autoprobing */
-         nicsr = ((nicsr & ~IM)|IEN);
-         outb(nicsr, DEPCA_NICSR);
+       /* Enable DEPCA board interrupts for autoprobing */
+       nicsr = ((nicsr & ~IM)|IEN);
+       outb(nicsr, DEPCA_NICSR);
 
-         /* To auto-IRQ we enable the initialization-done and DMA err,
-            interrupts. For now we will always get a DMA error. */
-         if (dev->irq < 2) {
+       /* To auto-IRQ we enable the initialization-done and DMA err,
+          interrupts. For now we will always get a DMA error. */
+       if (dev->irq < 2) {
 #ifndef MODULE
-           unsigned char irqnum;
-           autoirq_setup(0);
-           
-           /* Assign the correct irq list */
-           switch (lp->adapter) {
-           case DEPCA:
-           case de100:
-           case de101:
-             depca_irq = de1xx_irq;
-             break;
-           case de200:
-           case de201:
-           case de202:
-           case de210:
-           case de212:
-             depca_irq = de2xx_irq;
-             break;
-           case de422:
-             depca_irq = de422_irq;
-             break;
-           }
+               unsigned char irqnum;
+               autoirq_setup(0);
+
+               /* Assign the correct irq list */
+               switch (lp->adapter) {
+               case DEPCA:
+               case de100:
+               case de101:
+                       depca_irq = de1xx_irq;
+                       break;
+               case de200:
+               case de201:
+               case de202:
+               case de210:
+               case de212:
+                       depca_irq = de2xx_irq;
+                       break;
+               case de422:
+                       depca_irq = de422_irq;
+                       break;
+               }
 
-           /* Trigger an initialization just for the interrupt. */
-           outw(INEA | INIT, DEPCA_DATA);
+               /* Trigger an initialization just for the interrupt. */
+               outw(INEA | INIT, DEPCA_DATA);
          
-           irqnum = autoirq_report(1);
-           if (!irqnum) {
-             printk(" and failed to detect IRQ line.\n");
-             status = -ENXIO;
-           } else {
-             for (dev->irq=0,i=0; (depca_irq[i]) && (!dev->irq); i++) {
-               if (irqnum == depca_irq[i]) {
-                 dev->irq = irqnum;
-                 printk(" and uses IRQ%d.\n", dev->irq);
-               }
-             }
+               irqnum = autoirq_report(1);
+               if (!irqnum) {
+                       printk(" and failed to detect IRQ line.\n");
+                       status = -ENXIO;
+               } else {
+                       for (dev->irq=0,i=0; (depca_irq[i]) && (!dev->irq); i++) {
+                               if (irqnum == depca_irq[i]) {
+                                       dev->irq = irqnum;
+                                       printk(" and uses IRQ%d.\n", dev->irq);
+                               }
+                       }
              
-             if (!dev->irq) {
-               printk(" but incorrect IRQ line detected.\n");
-               status = -ENXIO;
-             }
-           }
+                       if (!dev->irq) {
+                               printk(" but incorrect IRQ line detected.\n");
+                               status = -ENXIO;
+                       }
+               }
 #endif /* MODULE */
-         } else {
-           printk(" and assigned IRQ%d.\n", dev->irq);
-         }
-         if (status) release_region(ioaddr, DEPCA_TOTAL_SIZE);
        } else {
-         printk(",\n      requests %dkB RAM: only %dkB is available!\n", 
-                                                       (mem_len>>10), netRAM);
-         status = -ENXIO;
+               printk(" and assigned IRQ%d.\n", dev->irq);
        }
-      } else {
-       printk("      which has an Ethernet PROM CRC error.\n");
-       status = -ENXIO;
-      }
-    } else {
-      status = -ENXIO;
-    }
-    if (!status) {
-      if (depca_debug > 1) {
-       printk(version);
-      }
 
-      /* The DEPCA-specific entries in the device structure. */
-      dev->open = &depca_open;
-      dev->hard_start_xmit = &depca_start_xmit;
-      dev->stop = &depca_close;
-      dev->get_stats = &depca_get_stats;
-      dev->set_multicast_list = &set_multicast_list;
-      dev->do_ioctl = &depca_ioctl;
-      dev->tx_timeout = depca_tx_timeout;
-      dev->watchdog_timeo = TX_TIMEOUT;
-
-      dev->mem_start = 0;
-       
-      /* Fill in the generic field of the device structure. */
-      ether_setup(dev);
-    } else {                           /* Incorrectly initialised hardware */
-      if (dev->priv) {
-       kfree(dev->priv);
-       dev->priv = NULL;
-      }
-    }
-  } else {
-    status = -ENXIO;
-  }
+       if (!status) {
+               if (depca_debug > 1) {
+                       printk(version);
+               }
 
-  return status;
+               /* The DEPCA-specific entries in the device structure. */
+               dev->open = &depca_open;
+               dev->hard_start_xmit = &depca_start_xmit;
+               dev->stop = &depca_close;
+               dev->get_stats = &depca_get_stats;
+               dev->set_multicast_list = &set_multicast_list;
+               dev->do_ioctl = &depca_ioctl;
+               dev->tx_timeout = depca_tx_timeout;
+               dev->watchdog_timeo = TX_TIMEOUT;
+
+               dev->mem_start = 0;
+
+               /* Fill in the generic field of the device structure. */
+               ether_setup(dev);
+       } else {          /* Incorrectly initialised hardware */
+               release_region(ioaddr, DEPCA_TOTAL_SIZE);
+               if (dev->priv) {
+                       kfree(dev->priv);
+                       dev->priv = NULL;
+               }
+       }
+
+       return status;
 }
 
 \f
@@ -741,7 +764,7 @@ depca_open(struct net_device *dev)
   nicsr = inb(DEPCA_NICSR);
 
   /* Make sure the shadow RAM is enabled */
-  if (adapter != DEPCA) {
+  if (lp->adapter != DEPCA) {
     nicsr |= SHE;
     outb(nicsr, DEPCA_NICSR);
   }
@@ -781,39 +804,43 @@ depca_open(struct net_device *dev)
 static void
 depca_init_ring(struct net_device *dev)
 {
-  struct depca_private *lp = (struct depca_private *)dev->priv;
-  u_int i;
-  u_long p;
-
-  /* Lock out other processes whilst setting up the hardware */
-  netif_stop_queue(dev);
-
-  lp->rx_new = lp->tx_new = 0;
-  lp->rx_old = lp->tx_old = 0;
+       struct depca_private *lp = (struct depca_private *)dev->priv;
+       u_int i;
+       u_long offset;
+
+       /* Lock out other processes whilst setting up the hardware */
+       netif_stop_queue(dev);
+
+       lp->rx_new = lp->tx_new = 0;
+       lp->rx_old = lp->tx_old = 0;
+
+       /* Initialize the base address and length of each buffer in the ring */
+       for (i = 0; i <= lp->rxRingMask; i++) {
+               offset = lp->buffs_offset + i*RX_BUFF_SZ;
+               writel((lp->device_ram_start + offset) | R_OWN,
+                      &lp->rx_ring[i].base);
+               writew(-RX_BUFF_SZ, &lp->rx_ring[i].buf_length);
+               lp->rx_buff[i] = lp->sh_mem + offset;
+       }
 
-  /* Initialize the base addresses and length of each buffer in the ring */
-  for (i = 0; i <= lp->rxRingMask; i++) {
-    writel((p=lp->dma_buffs+i*RX_BUFF_SZ) | R_OWN, &lp->rx_ring[i].base);
-    writew(-RX_BUFF_SZ, &lp->rx_ring[i].buf_length);
-    lp->rx_memcpy[i]=(char *)(p+lp->bus_offset);
-  }
-  for (i = 0; i <= lp->txRingMask; i++) {
-    writel((p=lp->dma_buffs+(i+lp->txRingMask+1)*TX_BUFF_SZ) & 0x00ffffff,
-                                                        &lp->tx_ring[i].base);
-    lp->tx_memcpy[i]=(char *)(p+lp->bus_offset);
-  }
+       for (i = 0; i <= lp->txRingMask; i++) {
+               offset = lp->buffs_offset + (i + lp->rxRingMask+1)*TX_BUFF_SZ;
+               writel((lp->device_ram_start + offset) & 0x00ffffff,
+                      &lp->tx_ring[i].base);
+               lp->tx_buff[i] = lp->sh_mem + offset;
+       }
 
-  /* Set up the initialization block */
-  lp->init_block.rx_ring = ((u32)((u_long)lp->rx_ring)&LA_MASK) | lp->rx_rlen;
-  lp->init_block.tx_ring = ((u32)((u_long)lp->tx_ring)&LA_MASK) | lp->tx_rlen;
+       /* Set up the initialization block */
+       lp->init_block.rx_ring = (lp->device_ram_start + lp->rx_ring_offset) | lp->rx_rlen;
+       lp->init_block.tx_ring = (lp->device_ram_start + lp->tx_ring_offset) | lp->tx_rlen;
 
-  SetMulticastFilter(dev);
+       SetMulticastFilter(dev);
 
-  for (i = 0; i < ETH_ALEN; i++) {
-    lp->init_block.phys_addr[i] = dev->dev_addr[i];
-  }
+       for (i = 0; i < ETH_ALEN; i++) {
+               lp->init_block.phys_addr[i] = dev->dev_addr[i];
+       }
 
-  lp->init_block.mode = 0x0000;            /* Enable the Tx and Rx */
+       lp->init_block.mode = 0x0000;            /* Enable the Tx and Rx */
 }
 
 
@@ -951,10 +978,10 @@ depca_rx(struct net_device *dev)
          skb->dev = dev;
          if (entry < lp->rx_old) {         /* Wrapped buffer */
            len = (lp->rxRingMask - lp->rx_old + 1) * RX_BUFF_SZ;
-           memcpy_fromio(buf, lp->rx_memcpy[lp->rx_old], len);
-           memcpy_fromio(buf + len, lp->rx_memcpy[0], pkt_len-len);
+           memcpy_fromio(buf, lp->rx_buff[lp->rx_old], len);
+           memcpy_fromio(buf + len, lp->rx_buff[0], pkt_len-len);
          } else {                          /* Linear buffer */
-           memcpy_fromio(buf, lp->rx_memcpy[lp->rx_old], pkt_len);
+           memcpy_fromio(buf, lp->rx_buff[lp->rx_old], pkt_len);
          }
 
          /* 
@@ -1103,9 +1130,9 @@ static void LoadCSRs(struct net_device *dev)
   u_long ioaddr = dev->base_addr;
 
   outw(CSR1, DEPCA_ADDR);                /* initialisation block address LSW */
-  outw((u16)(lp->sh_mem & LA_MASK), DEPCA_DATA);
+  outw((u16)lp->device_ram_start, DEPCA_DATA);
   outw(CSR2, DEPCA_ADDR);                /* initialisation block address MSW */
-  outw((u16)((lp->sh_mem & LA_MASK) >> 16), DEPCA_DATA);
+  outw((u16)(lp->device_ram_start >> 16), DEPCA_DATA);
   outw(CSR3, DEPCA_ADDR);                /* ALE control */
   outw(ACON, DEPCA_DATA);
 
@@ -1121,7 +1148,7 @@ static int InitRestartDepca(struct net_device *dev)
   int i, status=0;
 
   /* Copy the shadow init_block to shared memory */
-  memcpy_toio((char *)lp->sh_mem, &lp->init_block, sizeof(struct depca_init));
+  memcpy_toio(lp->sh_mem, &lp->init_block, sizeof(struct depca_init));
 
   outw(CSR0, DEPCA_ADDR);                /* point back to CSR0 */
   outw(INIT, DEPCA_DATA);                /* initialize DEPCA */
@@ -1134,11 +1161,11 @@ static int InitRestartDepca(struct net_device *dev)
     outw(IDON | INEA | STRT, DEPCA_DATA);
     if (depca_debug > 2) {
       printk("%s: DEPCA open after %d ticks, init block 0x%08lx csr0 %4.4x.\n",
-            dev->name, i, lp->sh_mem, inw(DEPCA_DATA));
+            dev->name, i, virt_to_phys(lp->sh_mem), inw(DEPCA_DATA));
     }
   } else {
     printk("%s: DEPCA unopen after %d ticks, init block 0x%08lx csr0 %4.4x.\n",
-            dev->name, i, lp->sh_mem, inw(DEPCA_DATA));
+            dev->name, i, virt_to_phys(lp->sh_mem), inw(DEPCA_DATA));
     status = -1;
   }
 
@@ -1569,12 +1596,20 @@ DepcaSignature(char *name, u_long paddr)
 {
   u_int i,j,k;
   const char *signatures[] = DEPCA_SIGNATURE;
+  void *ptr;
   char tmpstr[16];
 
   /* Copy the first 16 bytes of ROM */
+  ptr = ioremap(paddr + 0xc000, 16);
+  if (ptr == NULL) {
+         printk(KERN_ERR "depca: I/O remap failed at %lx\n", paddr+0xc000);
+         adapter = unknown;
+         return;
+  }
   for (i=0;i<16;i++) {
-    tmpstr[i] = readb(paddr+0xc000+i);
+    tmpstr[i] = readb(ptr + i);
   }
+  iounmap(ptr);
 
   /* Check if PROM contains a valid string */
   for (i=0;*signatures[i]!='\0';i++) {
@@ -1716,10 +1751,10 @@ static int load_packet(struct net_device *dev, struct sk_buff *skb)
     */
     if (end < entry) {                         /* wrapped buffer */
       len = (lp->txRingMask - entry + 1) * TX_BUFF_SZ;
-      memcpy_toio(lp->tx_memcpy[entry], skb->data, len);
-      memcpy_toio(lp->tx_memcpy[0], skb->data + len, skb->len - len);
+      memcpy_toio(lp->tx_buff[entry], skb->data, len);
+      memcpy_toio(lp->tx_buff[0], skb->data + len, skb->len - len);
     } else {                                   /* linear buffer */
-      memcpy_toio(lp->tx_memcpy[entry], skb->data, skb->len);
+      memcpy_toio(lp->tx_buff[entry], skb->data, skb->len);
     }
 
     /* set up the buffer descriptors */
@@ -1795,17 +1830,17 @@ static void depca_dbg_open(struct net_device *dev)
 {
   struct depca_private *lp = (struct depca_private *)dev->priv;
   u_long ioaddr = dev->base_addr;
-  struct depca_init *p = (struct depca_init *)lp->sh_mem;
+  struct depca_init *p = &lp->init_block;
   int i; 
 
   if (depca_debug > 1){
-    /* Copy the shadow init_block to shared memory */
-    memcpy_toio((char *)lp->sh_mem,&lp->init_block,sizeof(struct depca_init));
-
+    /* Do not copy the shadow init block into shared memory */
+    /* Debugging should not affect normal operation! */
+    /* The shadow init block will get copied across during InitRestartDepca */
     printk("%s: depca open with irq %d\n",dev->name,dev->irq);
-    printk("Descriptor head addresses:\n");
-    printk("\t0x%lx  0x%lx\n",(u_long)lp->rx_ring, (u_long)lp->tx_ring);
-    printk("Descriptor addresses:\nRX: ");
+    printk("Descriptor head addresses (CPU):\n");
+    printk("        0x%lx  0x%lx\n",(u_long)lp->rx_ring, (u_long)lp->tx_ring);
+    printk("Descriptor addresses (CPU):\nRX: ");
     for (i=0;i<lp->rxRingMask;i++){
       if (i < 3) {
        printk("0x%8.8lx ", (long) &lp->rx_ring[i].base);
@@ -1819,7 +1854,7 @@ static void depca_dbg_open(struct net_device *dev)
       }
     }
     printk("...0x%8.8lx\n", (long) &lp->tx_ring[i].base);
-    printk("\nDescriptor buffers:\nRX: ");
+    printk("\nDescriptor buffers (Device):\nRX: ");
     for (i=0;i<lp->rxRingMask;i++){
       if (i < 3) {
        printk("0x%8.8x  ", readl(&lp->rx_ring[i].base));
@@ -1833,21 +1868,21 @@ static void depca_dbg_open(struct net_device *dev)
       }
     }
     printk("...0x%8.8x\n", readl(&lp->tx_ring[i].base));
-    printk("Initialisation block at 0x%8.8lx\n",lp->sh_mem);
-    printk("\tmode: 0x%4.4x\n",readw(&p->mode));
-    printk("\tphysical address: ");
+    printk("Initialisation block at 0x%8.8lx(Phys)\n",virt_to_phys(lp->sh_mem));
+    printk("        mode: 0x%4.4x\n",p->mode);
+    printk("        physical address: ");
     for (i=0;i<ETH_ALEN-1;i++){
-      printk("%2.2x:",(u_char)readb(&p->phys_addr[i]));
+      printk("%2.2x:", p->phys_addr[i]);
     }
-    printk("%2.2x\n",(u_char)readb(&p->phys_addr[i]));
-    printk("\tmulticast hash table: ");
+    printk("%2.2x\n", p->phys_addr[i]);
+    printk("        multicast hash table: ");
     for (i=0;i<(HASH_TABLE_LEN >> 3)-1;i++){
-      printk("%2.2x:",(u_char)readb(&p->mcast_table[i]));
+      printk("%2.2x:", p->mcast_table[i]);
     }
-    printk("%2.2x\n",(u_char)readb(&p->mcast_table[i]));
-    printk("\trx_ring at: 0x%8.8x\n",readl(&p->rx_ring));
-    printk("\ttx_ring at: 0x%8.8x\n",readl(&p->tx_ring));
-    printk("dma_buffs: 0x%8.8lx\n",lp->dma_buffs);
+    printk("%2.2x\n", p->mcast_table[i]);
+    printk("        rx_ring at: 0x%8.8x\n", p->rx_ring);
+    printk("        tx_ring at: 0x%8.8x\n", p->tx_ring);
+    printk("buffers (Phys): 0x%8.8lx\n",virt_to_phys(lp->sh_mem)+lp->buffs_offset);
     printk("Ring size:\nRX: %d  Log2(rxRingMask): 0x%8.8x\n", 
           (int)lp->rxRingMask + 1, 
           lp->rx_rlen);
@@ -2031,6 +2066,7 @@ cleanup_module(void)
 {
   struct depca_private *lp = thisDepca.priv;
   if (lp) {
+    iounmap(lp->sh_mem);
 #ifdef CONFIG_MCA      
     if(lp->mca_slot != -1)
       mca_mark_as_unused(lp->mca_slot);
index e02298ded5c26db7f604a5c1569c3562b5d0a0f3..254b42880b40f12130dcf6d94b80c88fa373b9c5 100644 (file)
@@ -24,6 +24,8 @@
        Version 1.0.1:
                - Spinlock fixes
                - Bug fixes and better intr performance (Tjeerd)
+       Version 1.0.2:
+               - Now reads correct MAC address from eeprom
 
 */
 
@@ -33,7 +35,7 @@ static const char version1[] =
 static const char version2[] =
 "  http://www.scyld.com/network/natsemi.html\n";
 static const char version3[] =
-"  (unofficial 2.4.x kernel port, version 1.0.1, September 5, 2000 Jeff Garzik, Tjeerd Mulder)\n";
+"  (unofficial 2.4.x kernel port, version 1.0.2, October 6, 2000 Jeff Garzik, Tjeerd Mulder)\n";
 /* Updated to recommendations in pci-skeleton v2.03. */
 
 /* Automatically extracted configuration info:
@@ -405,8 +407,13 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
        printk(KERN_INFO "%s: %s at 0x%lx, ",
                   dev->name, natsemi_pci_info[chip_idx].name, ioaddr);
 
-       for (i = 0; i < ETH_ALEN/2; i++)
-               ((u16 *)dev->dev_addr)[i] = be16_to_cpu(eeprom_read(ioaddr, i + 7));
+       for (i = 0; i < ETH_ALEN/2; i++) {
+               /* weird organization */
+               unsigned short a;
+               a = (le16_to_cpu(eeprom_read(ioaddr, i + 6)) >> 15) + 
+                   (le16_to_cpu(eeprom_read(ioaddr, i + 7)) << 1);
+               ((u16 *)dev->dev_addr)[i] = a;
+       }
        for (i = 0; i < ETH_ALEN-1; i++)
                        printk("%2.2x:", dev->dev_addr[i]);
        printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);
@@ -513,7 +520,8 @@ static int eeprom_read(long addr, int location)
        for (i = 16; i > 0; i--) {
                writel(EE_ChipSelect | EE_ShiftClk, ee_addr);
                eeprom_delay(ee_addr);
-               retval = (retval << 1) | ((readl(ee_addr) & EE_DataOut) ? 1 : 0);
+               /* data bits are LSB first */
+               retval = (retval >> 1) | ((readl(ee_addr) & EE_DataOut) ? 0x8000 : 0);
                writel(EE_ChipSelect, ee_addr);
                eeprom_delay(ee_addr);
        }
index 9f2d52d5ff495ec149f11247d9d596389db59e16..5062a4300b0c08d960d2b3c2de749ced23c03168 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: audio.c,v 1.55 2000/10/10 01:07:39 davem Exp $
+/* $Id: audio.c,v 1.56 2000/10/19 00:50:02 davem Exp $
  * drivers/sbus/audio/audio.c
  *
  * Copyright 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
index dbba073de9a2d14409f484f8831be5e0c642bf5a..31c9de932fd2fa94c9a0e0f108df8227e3f3778c 100644 (file)
@@ -1,23 +1,29 @@
-/* $Id: envctrl.c,v 1.17 2000/06/19 06:24:47 davem Exp $
+/* $Id: envctrl.c,v 1.18 2000/10/17 16:20:35 davem Exp $
  * envctrl.c: Temperature and Fan monitoring on Machines providing it.
  *
  * Copyright (C) 1998  Eddie C. Dost  (ecd@skynet.be)
+ * Copyright (C) 2000  Vinh Truong    (vinh.truong@eng.sun.com)
+ * VT - The implementation is to support Sun Microelectronics (SME) platform
+ *      environment monitoring.  SME platforms use pcf8584 as the i2c bus 
+ *      controller to access pcf8591 (8-bit A/D and D/A converter) and 
+ *      pcf8571 (256 x 8-bit static low-voltage RAM with I2C-bus interface).
+ *      At board level, it follows SME Firmware I2C Specification. Reference:
+ *     http://www-eu2.semiconductors.com/pip/PCF8584P
+ *     http://www-eu2.semiconductors.com/pip/PCF8574AP
+ *     http://www-eu2.semiconductors.com/pip/PCF8591P
+ * 
  */
 
-#include <linux/version.h>
 #include <linux/config.h>
 #include <linux/module.h>
-
-#define __KERNEL_SYSCALLS__
 #include <linux/sched.h>
-#include <linux/unistd.h>
 #include <linux/errno.h>
 #include <linux/delay.h>
 #include <linux/ioport.h>
 #include <linux/init.h>
-#include <linux/malloc.h>
 #include <linux/miscdevice.h>
-#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
 
 #include <asm/ebus.h>
 #include <asm/uaccess.h>
 
 #define ENVCTRL_MINOR  162
 
-
-#undef U450_SUPPORT            /* might fry you machine, careful here !!! */
-
-
-#undef DEBUG
-#undef DEBUG_BUS_SCAN
-
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0)
-#define schedule_timeout(a) { current->timeout = jiffies + (a); schedule(); }
-#endif
-
 #define PCF8584_ADDRESS        0x55
 
 #define CONTROL_PIN    0x80
 #define CLK_8          0x18
 #define CLK_12         0x1c
 
+#define OBD_SEND_START 0xc5    /* value to generate I2c_bus START condition */
+#define OBD_SEND_STOP  0xc3    /* value to generate I2c_bus STOP condition */
 
-#define I2C_WRITE      0x00
-#define I2C_READ       0x01
-
-/* PCF8584 register offsets */
-#define I2C_DATA       0x00UL
-#define I2C_CSR                0x01UL
-#define I2C_REG_SIZE   0x02UL
-
-struct i2c_device {
-       unsigned char           addr;
-       struct i2c_device       *next;
-};
-
-static unsigned long i2c_regs;
-static struct i2c_device  *i2c_devices;
-
-static int errno;
-
-#define MAX_TEMPERATURE                111
-#define MAX_FAN_SPEED          63
-
-
-/*
- * UltraAXi constants.
+/* Monitor type of i2c child device.
+ * Firmware definitions.
  */
-#define AXI_THERM_ADDR         0x9e
-#define AXI_THERM_PORT_CPU     0
-#define AXI_THERM_PORT_MOD     1
-#define AXI_THERM_PORT_PCI     2
-#define AXI_THERM_PORT_DISK    3
-
-#define AXI_FAN_ADDR           0x4e
-#define AXI_FAN_PORT_FRONT     0
-#define AXI_FAN_PORT_BACK      1
-
-#define AXI_PIO_ADDR           0x70
+#define PCF8584_MAX_CHANNELS            8
+#define PCF8584_FANSTAT_TYPE            3  /* fan status monitor */
+#define PCF8584_VOLTAGE_TYPE            2  /* voltage monitor    */
+#define PCF8584_TEMP_TYPE              1  /* temperature monitor*/
 
-/*
- * Ultra 450 constants.
+/* Monitor type of i2c child device.
+ * Driver definitions.
  */
-#define U450_FAN_ADDR          0x4e
-#define U450_FAN_PORT_CPU      0
-#define U450_FAN_PORT_PS       1
-
-#define U450_PIO_ADDR          0x70
-#define U450_TIMER_ADDR                0xa0
-
-static unsigned char
-axi_cpu_temp_table[256] =
-{
-       0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x67,
-       0x66, 0x65, 0x64, 0x63, 0x61, 0x60, 0x5f, 0x5e,
-       0x5d, 0x5b, 0x5a, 0x59, 0x58, 0x57, 0x55, 0x54,
-       0x53, 0x52, 0x50, 0x4f, 0x4e, 0x4d, 0x4c, 0x4a,
-       0x49, 0x48, 0x47, 0x46, 0x44, 0x43, 0x42, 0x41,
-       0x40, 0x3e, 0x3d, 0x3c, 0x3c, 0x3b, 0x3b, 0x3a,
-       0x3a, 0x39, 0x39, 0x38, 0x38, 0x37, 0x37, 0x36,
-       0x36, 0x35, 0x35, 0x34, 0x34, 0x33, 0x33, 0x32,
-       0x32, 0x31, 0x31, 0x30, 0x30, 0x2f, 0x2f, 0x2e,
-       0x2d, 0x2d, 0x2c, 0x2c, 0x2b, 0x2b, 0x2a, 0x2a,
-       0x29, 0x29, 0x28, 0x28, 0x27, 0x27, 0x26, 0x26,
-       0x25, 0x25, 0x24, 0x24, 0x23, 0x23, 0x22, 0x22,
-       0x21, 0x21, 0x20, 0x20, 0x1f, 0x1f, 0x1e, 0x1e,
-       0x1e, 0x1e, 0x1d, 0x1d, 0x1d, 0x1d, 0x1c, 0x1c,
-       0x1c, 0x1c, 0x1b, 0x1b, 0x1b, 0x1b, 0x1a, 0x1a,
-       0x1a, 0x1a, 0x1a, 0x19, 0x19, 0x19, 0x19, 0x18,
-       0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x17, 0x16,
-       0x16, 0x16, 0x16, 0x16, 0x16, 0x15, 0x15, 0x15,
-       0x15, 0x14, 0x14, 0x14, 0x14, 0x13, 0x13, 0x13,
-       0x13, 0x12, 0x12, 0x12, 0x12, 0x12, 0x11, 0x11,
-       0x11, 0x11, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x0f,
-       0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d,
-       0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b,
-       0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
-       0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08,
-       0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06,
-       0x06, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04,
-       0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02,
-       0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-static unsigned char
-axi_mod_temp_table[256] =
-{
-       0x65, 0x64, 0x63, 0x62, 0x61, 0x60, 0x5f, 0x5e,
-       0x5d, 0x5c, 0x5b, 0x5a, 0x59, 0x58, 0x57, 0x56,
-       0x55, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4f, 0x4e,
-       0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x48, 0x47, 0x46,
-       0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3f, 0x3e,
-       0x3d, 0x3c, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x39,
-       0x39, 0x38, 0x38, 0x37, 0x37, 0x36, 0x36, 0x35,
-       0x35, 0x35, 0x34, 0x34, 0x33, 0x33, 0x32, 0x32,
-       0x31, 0x31, 0x30, 0x30, 0x2f, 0x2f, 0x2e, 0x2e,
-       0x2e, 0x2d, 0x2d, 0x2c, 0x2c, 0x2b, 0x2b, 0x2a,
-       0x2a, 0x29, 0x29, 0x29, 0x28, 0x28, 0x27, 0x27,
-       0x26, 0x26, 0x25, 0x25, 0x24, 0x24, 0x23, 0x23,
-       0x23, 0x22, 0x22, 0x21, 0x21, 0x20, 0x20, 0x1f,
-       0x1f, 0x1e, 0x1e, 0x1e, 0x1d, 0x1d, 0x1d, 0x1d,
-       0x1d, 0x1c, 0x1c, 0x1c, 0x1c, 0x1b, 0x1b, 0x1b,
-       0x1b, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x19, 0x19,
-       0x19, 0x19, 0x18, 0x18, 0x18, 0x18, 0x17, 0x17,
-       0x17, 0x17, 0x17, 0x16, 0x16, 0x16, 0x16, 0x15,
-       0x15, 0x15, 0x15, 0x14, 0x14, 0x14, 0x14, 0x13,
-       0x13, 0x13, 0x13, 0x13, 0x12, 0x12, 0x12, 0x12,
-       0x11, 0x11, 0x11, 0x11, 0x10, 0x10, 0x10, 0x10,
-       0x10, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e,
-       0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c,
-       0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a,
-       0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08,
-       0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06,
-       0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05,
-       0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03,
-       0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
-       0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-static unsigned char
-axi_fan_speeds[112] =
-{
-       0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
-       0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
-       0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
-       0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x20,
-       0x22, 0x23, 0x25, 0x27, 0x28, 0x2a, 0x2b, 0x2d,
-       0x2f, 0x30, 0x32, 0x33, 0x35, 0x37, 0x38, 0x3a,
-       0x3b, 0x3d, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f
-};
-
-
-struct therm_regs {
-       u32     addr;
-       u32     port;
-       u32     min_temp;
-       u32     warning;
-       u32     shutdown;
-       u32     num;
-       u32     den;
-};
+#define ENVCTRL_NOMON                  0
+#define ENVCTRL_CPUTEMP_MON            1    /* cpu temperature monitor */
+#define ENVCTRL_CPUVOLTAGE_MON         2    /* voltage monitor         */
+#define ENVCTRL_FANSTAT_MON            3    /* fan status monitor      */
+#define ENVCTRL_ETHERTEMP_MON          4    /* ethernet temperarture */
+                                            /* monitor                     */
+#define ENVCTRL_VOLTAGESTAT_MON                5    /* voltage status monitor  */
+#define ENVCTRL_MTHRBDTEMP_MON         6    /* motherboard temperature */
+#define ENVCTRL_SCSITEMP_MON           7    /* scsi temperarture */
+
+/* Child device type.
+ * Driver definitions.
+ */
+#define I2C_ADC                                0    /* pcf8591 */
+#define I2C_GPIO                       1    /* pcf8571 */
 
-struct thermistor {
-       char                    name[8];
-       struct therm_regs       regs;
-       unsigned char           (*temperature) (struct thermistor *);
-       unsigned char           (*fan_speed) (struct thermistor *);
-       struct thermistor       *next;          /* all thermistors */
-       struct thermistor       *chain;         /* thermistors for one fan */
+/* Data read from child device may need to decode
+ * through a data table and a scale.
+ * Translation type as defined by firmware.
+ */
+#define ENVCTRL_TRANSLATE_NO           0
+#define ENVCTRL_TRANSLATE_PARTIAL      1
+#define ENVCTRL_TRANSLATE_COMBINED     2
+#define ENVCTRL_TRANSLATE_FULL         3     /* table[data] */
+#define ENVCTRL_TRANSLATE_SCALE                4     /* table[data]/scale */
+
+/* Driver miscellaneous definitions. */
+#define ENVCTRL_MAX_CPU                        4
+#define CHANNEL_DESC_SZ                        256
+
+struct pcf8584_reg {
+        unsigned char data;
+        unsigned char csr;
 };
 
-struct fan_regs {
-       u32     addr;
-       u32     port;
+/* Each child device can be monitored by up to PCF8584_MAX_CHANNELS.
+ * Property of a port or channel as defined by the firmware.
+ */
+struct pcf8584_channel {
+        unsigned char chnl_no;
+        unsigned char io_direction;
+        unsigned char type;
+        unsigned char last;
 };
 
-struct fan {
-       char                    name[8];
-       struct fan_regs         regs;
-       int                     (*set_speed)(struct fan *, unsigned char value);
-       int                     (*check_failure)(struct fan *);
-       unsigned char           value;
-       struct thermistor       *monitor;
-       struct fan              *next;
+/* Each child device may have one or more tables of bytes to help decode
+ * data. Table property as defined by the firmware.
+ */ 
+struct pcf8584_tblprop {
+        unsigned int type;
+        unsigned int scale;  
+        unsigned int offset; /* offset from the beginning of the table */
+        unsigned int size;
 };
 
-
-struct environment {
-       struct thermistor       *thermistors;
-       struct fan              *fans;
-       unsigned char           *cpu_temp_table;
-       unsigned char           *cpu_fan_speeds;
-       unsigned char           *ps_temp_table;
-       unsigned char           *ps_fan_speeds;
-       void                    (*enable) (struct environment *);
-       void                    (*disable) (struct environment *);
-       void                    (*keep_alive) (struct environment *);
-       int                     interval;
-       pid_t                   kenvd_pid;
-       wait_queue_head_t       kenvd_wait;
-       int                     terminate;
+/* i2c child */
+struct i2c_child_t {
+       /* Either ADC or GPIO. */
+       unsigned char i2ctype;
+        unsigned long addr;    
+        struct pcf8584_channel chnl_array[PCF8584_MAX_CHANNELS];
+
+       /* Channel info. */ 
+       unsigned int total_chnls;       /* Number of monitor channels. */
+       unsigned char fan_mask;         /* Byte mask for fan status channels. */
+       unsigned char voltage_mask;     /* Byte mask for voltage status channels. */
+        struct pcf8584_tblprop tblprop_array[PCF8584_MAX_CHANNELS];
+
+       /* Properties of all monitor channels. */
+       unsigned int total_tbls;        /* Number of monitor tables. */
+        char *tables;                  /* Pointer to table(s). */
+       char chnls_desc[CHANNEL_DESC_SZ]; /* Channel description. */
+       char mon_type[PCF8584_MAX_CHANNELS];
 };
 
+volatile static struct pcf8584_reg *i2c = NULL;
+static struct i2c_child_t i2c_childlist[ENVCTRL_MAX_CPU*2];
+static unsigned char chnls_mask[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
+static unsigned int warning_temperature = 0;
+static unsigned int shutdown_temperature = 0;
+static char read_cpu;
 
-static struct environment      envctrl;
-
+/* Forward declarations. */
+static struct i2c_child_t *envctrl_get_i2c_child(unsigned char);
 
-#ifdef DEBUG_BUS_SCAN
-struct i2c_addr_map {
-       unsigned char addr;
-       unsigned char mask;
-       char *name;
-};
-
-static struct i2c_addr_map devmap[] = {
-       { 0x70, 0xf0, "PCF8574A" },
-       { 0x40, 0xf0, "TDA8444" },
-       { 0x90, 0xf0, "PCF8591" },
-       { 0xa0, 0xf0, "PCF8583" },
-};
-#define NR_DEVMAP (sizeof(devmap) / sizeof(devmap[0]))
-#endif
-
-static __inline__ int
-PUT_DATA(unsigned long data, char *buffer, int user)
+/* Function description: Read a byte from an i2c controller register.
+ * Return: A byte from the passed in address.
+ */
+static inline unsigned char envctrl_readb(volatile unsigned char *p)
 {
-       if (user) {
-               u8 tmp = readb(data);
-               if (put_user(tmp, buffer))
-                       return -EFAULT;
-       } else {
-               *buffer = readb(data);
-       }
-       return 0;
+       return readb(p);
 }
 
-static __inline__ int
-GET_DATA(unsigned long data, const char *buffer, int user)
+/* Function description: Write a byte to an i2c controller register.
+ * Return: Nothing.
+ */
+static inline void envctrl_writeb(unsigned char val, volatile unsigned char *p)
 {
-       if (user) {
-               u8 tmp;
-               if (get_user(tmp, buffer))
-                       return -EFAULT;
-               writeb(tmp, data);
-       } else {
-               writeb(*buffer, data);
-       }
-       return 0;
+       writeb(val, p);
 }
 
-
-static int
-i2c_read(unsigned char dev, char *buffer, int len, int user)
+/* Function Description: Test the PIN bit (Pending Interrupt Not) 
+ *                      to test when serial transmission is completed .
+ * Return : None.
+ */
+static void envtrl_i2c_test_pin(void)
 {
-       unsigned char dummy;
-       unsigned char stat;
-       int error = -ENODEV;
-       int count = 0;
-
-       writeb((dev & 0xfe) | I2C_READ, i2c_regs + I2C_DATA);
-
-       while (!(readb(i2c_regs + I2C_CSR) & STATUS_BB))
-               udelay(1);
-
-       writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_STA | CONTROL_ACK,
-              i2c_regs + I2C_CSR);
-
-       do {
-               udelay(1);
-               while ((stat = readb(i2c_regs + I2C_CSR)) & STATUS_PIN)
-                       udelay(1);
-
-               if (stat & STATUS_LRB)
-                       goto stop;
+       int limit = 1000000;
 
-               error = 0;
-               if (len == 0) {
-                       count--;
+       while (--limit > 0) {
+               if (!(envctrl_readb(&i2c->csr) & STATUS_PIN)) 
                        break;
-               }
-
-               if (count == (len - 1))
-                       break;
-
-               if (count++ > 0) {
-                       error = PUT_DATA(i2c_regs + I2C_DATA, buffer++, user);
-                       if (error)
-                               break;
-               } else
-                       dummy = readb(i2c_regs + I2C_DATA);
-       } while (1);
-
-       writeb(CONTROL_ES0, i2c_regs + I2C_CSR);
-       if (!error && (count++ > 0))
-               error = PUT_DATA(i2c_regs + I2C_DATA, buffer++, user);
-       else
-               dummy = readb(i2c_regs + I2C_DATA);
-
-       udelay(1);
-       while ((stat = readb(i2c_regs + I2C_CSR)) & STATUS_PIN)
                udelay(1);
+       } 
 
-stop:
-       writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_STO | CONTROL_ACK,
-              i2c_regs + I2C_CSR);
-       if (!error && (count++ > 0))
-               error = PUT_DATA(i2c_regs + I2C_DATA, buffer++, user);
-       else
-               dummy = readb(i2c_regs + I2C_DATA);
-
-       if (error)
-               return error;
-       return count - 1;
+       if (limit <= 0)
+               printk(KERN_INFO "envctrl: Pin status will not clear.\n");
 }
 
-static int
-i2c_write(unsigned char dev, const char *buffer, int len, int user)
+/* Function Description: Test busy bit.
+ * Return : None.
+ */
+static void envctrl_i2c_test_bb(void)
 {
-       int error = -ENODEV;
-       int count = 0;
-       int timeout;
-
-       timeout = 1000000;
-       while (!(readb(i2c_regs + I2C_CSR) & STATUS_BB) && --timeout)
-               udelay(1);
-       if (!timeout) {
-               printk("%s [%d]: TIMEOUT\n", __FUNCTION__, __LINE__);
-               return -ENODEV;
-       }
-
-       writeb((dev & 0xfe) | I2C_WRITE, i2c_regs + I2C_DATA);
-       writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_STA | CONTROL_ACK,
-              i2c_regs + I2C_CSR);
-
-       do {
-               unsigned char stat;
-
-               udelay(1);
-               timeout = 1000000;
-               while (((stat = readb(i2c_regs + I2C_CSR)) & STATUS_PIN) && --timeout)
-                       udelay(1);
-
-               if (!timeout) {
-                       printk("%s [%d]: TIMEOUT\n", __FUNCTION__, __LINE__);
-                       break;
-               }
+       int limit = 1000000;
 
-               if (stat & STATUS_LRB)
+       while (--limit > 0) {
+               /* Busy bit 0 means busy. */
+               if (envctrl_readb(&i2c->csr) & STATUS_BB)
                        break;
+               udelay(1);
+       } 
 
-               error = count;
-               if (count == len)
-                       break;
-
-               error = GET_DATA(i2c_regs + I2C_DATA, buffer++, user);
-               if (error)
-                       break;
-
-               count++;
-       } while (1);
-
-       writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_STO | CONTROL_ACK, 
-              i2c_regs + I2C_CSR);
-       return error;
+       if (limit <= 0)
+               printk(KERN_INFO "envctrl: Busy bit will not clear.\n");
 }
 
-#ifdef U450_SUPPORT
-static int
-i2c_write_read(unsigned char dev, char *outbuf, int outlen,
-                                 char *inbuf, int inlen, int user)
+/* Function Description: Send the adress for a read access.
+ * Return : 0 if not acknowledged, otherwise acknowledged.
+ */
+static int envctrl_i2c_read_addr(unsigned char addr)
 {
-       unsigned char dummy;
-       unsigned char stat;
-       int error = -ENODEV;
-       int count = 0;
-
-       while (!(readb(i2c_regs + I2C_CSR) & STATUS_BB))
-               udelay(1);
-
-       writeb((dev & 0xfe) | I2C_WRITE, i2c_regs + I2C_DATA);
-       writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_STA | CONTROL_ACK, 
-              i2c_regs + I2C_CSR);
-
-       do {
-               unsigned char stat;
-
-               udelay(1);
-               while ((stat = readb(i2c_regs + I2C_CSR)) & STATUS_PIN)
-                       udelay(1);
+       envctrl_i2c_test_bb();
 
-               if (stat & STATUS_LRB)
-                       break;
+       /* Load address. */
+       envctrl_writeb(addr + 1, &i2c->data);
 
-               error = count;
-               if (count == outlen)
-                       break;
+       envctrl_i2c_test_bb();
 
-               error = GET_DATA(i2c_regs + I2C_DATA, outbuf++, user);
-               if (error)
-                       break;
+       envctrl_writeb(OBD_SEND_START, &i2c->csr);
 
-               count++;
-       } while (1);
+       /* Wait for PIN. */
+       envtrl_i2c_test_pin();
 
-       if (error < 0) {
-               writeb(CONTROL_PIN | CONTROL_ES0 |
-                      CONTROL_STO | CONTROL_ACK, i2c_regs + I2C_CSR);
-               return error;
+       /* CSR 0 means acknowledged. */
+       if (!(envctrl_readb(&i2c->csr) & STATUS_LRB)) {
+               return envctrl_readb(&i2c->data);
+       } else {
+               envctrl_writeb(OBD_SEND_STOP, &i2c->csr);
+               return 0;
        }
-
-       writeb(CONTROL_ES0 | CONTROL_STA | CONTROL_ACK, i2c_regs + I2C_CSR);
-       udelay(1);
-       writeb((dev & 0xfe) | I2C_READ, i2c_regs + I2C_DATA);
-
-       count = 0;
-       do {
-               udelay(1);
-               while ((stat = readb(i2c_regs + I2C_CSR)) & STATUS_PIN)
-                       udelay(1);
-
-               if (stat & STATUS_LRB)
-                       goto stop;
-
-               error = 0;
-               if (inlen == 0) {
-                       count--;
-                       break;
-               }
-
-               if (count == (inlen - 1))
-                       break;
-
-               if (count++ > 0) {
-                       error = PUT_DATA(i2c_regs + I2C_DATA, inbuf++, user);
-                       if (error)
-                               break;
-               } else
-                       dummy = readb(i2c_regs + I2C_DATA);
-       } while (1);
-
-       writeb(CONTROL_ES0, i2c_regs + I2C_CSR);
-       if (!error && (count++ > 0))
-               error = PUT_DATA(i2c_regs + I2C_DATA, inbuf++, user);
-       else
-               dummy = readb(i2c_regs + I2C_DATA);
-
-       udelay(1);
-       while ((stat = readb(i2c_regs + I2C_CSR)) & STATUS_PIN)
-               udelay(1);
-
-stop:
-       writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_STO | CONTROL_ACK,
-              i2c_regs + I2C_CSR);
-       if (!error && (count++ > 0))
-               error = PUT_DATA(i2c_regs + I2C_DATA, inbuf++, user);
-       else
-               dummy = readb(i2c_regs + I2C_DATA);
-
-       if (error)
-               return error;
-       return count - 1;
 }
-#endif /* U450_SUPPORT */
 
-static struct i2c_device *
-i2c_find_device(unsigned char addr)
+/* Function Description: Send the adress for write mode.  
+ * Return : None.
+ */
+static void envctrl_i2c_write_addr(unsigned char addr)
 {
-       struct i2c_device *dev;
+       envctrl_i2c_test_bb();
+       envctrl_writeb(addr, &i2c->data);
 
-       for (dev = i2c_devices; dev; dev = dev->next) {
-               if (dev->addr == addr)
-                       return dev;
-       }
-       return 0;
+       /* Generate Start condition. */
+       envctrl_writeb(OBD_SEND_START, &i2c->csr);
 }
 
-static void
-i2c_free_devices(void)
+/* Function Description: Read 1 byte of data from addr 
+ *                      set by envctrl_i2c_read_addr() 
+ * Return : Data from address set by envctrl_i2c_read_addr().
+ */
+static unsigned char envctrl_i2c_read_data(void)
 {
-       struct i2c_device *dev;
-
-       dev = i2c_devices;
-       while (dev) {
-               i2c_devices = dev->next;
-               kfree(dev);
-               dev = i2c_devices;
-       }
+       envtrl_i2c_test_pin();
+       envctrl_writeb(CONTROL_ES0, &i2c->csr);  /* Send neg ack. */
+       return envctrl_readb(&i2c->data);
 }
 
-static __init int i2c_scan_bus(void)
+/* Function Description: Instruct the device which port to read data from.  
+ * Return : None.
+ */
+static void envctrl_i2c_write_data(unsigned char port)
 {
-       struct i2c_device *dev, **last;
-       unsigned int addr;
-       int count = 0;
-
-       last = &i2c_devices;
-       for (addr = 0; addr < 256; addr += 2) {
-               if (i2c_write(addr, 0, 0, 0) == 0) {
-#ifdef DEBUG_BUS_SCAN
-                       int i;
-                       for (i = 0; i < NR_DEVMAP; i++)
-                               if ((addr & devmap[i].mask) == devmap[i].addr)
-                                       break;
-                       printk("envctrl: i2c device at %02x: %s\n", addr,
-                              i < NR_DEVMAP ? devmap[i].name : "unknown");
-#endif
-
-                       dev = kmalloc(sizeof(struct i2c_device), GFP_KERNEL);
-                       if (!dev) {
-                               printk("i2c: can't alloc i2c_device\n");
-                               i2c_free_devices();
-                               return -ENOMEM;
-                       }
-                       memset(dev, 0, sizeof(struct i2c_device));
-
-                       dev->addr = addr;
-
-                       *last = dev;
-                       last = &dev->next;
-
-                       count++;
-               }
-       }
-       if (!count) {
-               printk("%s: no devices found\n", __FUNCTION__);
-               return -ENODEV;
-       }
-       return 0;
+       envtrl_i2c_test_pin();
+       envctrl_writeb(port, &i2c->data);
 }
 
-
-static int
-read_8591(unsigned char dev, unsigned char offset, unsigned char *value)
+/* Function Description: Generate Stop condition after last byte is sent.
+ * Return : None.
+ */
+static void envctrl_i2c_stop(void)
 {
-       unsigned char data[2];
-
-       data[0] = 0x40 | offset;
-       if (i2c_write(dev, data, 1, 0) != 1)
-               return -1;
-       if (i2c_read(dev, data, 2, 0) != 2)
-               return -1;
-       *value = data[1];
-       return 0;
+       envtrl_i2c_test_pin();
+       envctrl_writeb(OBD_SEND_STOP, &i2c->csr);
 }
 
-static int
-write_8444(unsigned char dev, unsigned char offset, unsigned char value)
+/* Function Description: Read adc device.
+ * Return : Data at address and port.
+ */
+static unsigned char envctrl_i2c_read_8591(unsigned char addr, unsigned char port)
 {
-       unsigned char data[2];
+       /* Send address. */
+       envctrl_i2c_write_addr(addr);
 
-       data[0] = offset;
-       data[1] = value;
-       if (i2c_write(dev, data, 2, 0) != 2)
-               return -1;
-       return 0;
-}
+       /* Setup port to read. */
+       envctrl_i2c_write_data(port);
+       envctrl_i2c_stop();
 
-#ifdef U450_SUPPORT
-static int
-read_8583(unsigned char dev, unsigned char offset, unsigned char *value)
-{
-       unsigned char data;
+       /* Read port. */
+       envctrl_i2c_read_addr(addr);
 
-       data = offset;
-       if (i2c_write_read(dev, &data, 1, &data, 1, 0) != 1)
-               return -1;
-       *value = data;
-       return 0;
-}
-
-static int
-write_8583(unsigned char dev, unsigned char offset, unsigned char value)
-{
-       unsigned char data[2];
+       /* Do a single byte read and send stop. */
+       envctrl_i2c_read_data();
+       envctrl_i2c_stop();
 
-       data[0] = offset;
-       data[1] = value;
-       if (i2c_write(dev, data, 2, 0) != 2)
-               return -1;
-       return 0;
+       return envctrl_readb(&i2c->data);
 }
-#endif /* U450_SUPPORT */
 
-struct thermistor *
-find_thermistor(const char *name, struct thermistor *from)
+/* Function Description: Read gpio device.
+ * Return : Data at address.
+ */
+static unsigned char envctrl_i2c_read_8574(unsigned char addr)
 {
-       int n;
+       unsigned char rd;
 
-       if (!from)
-               from = envctrl.thermistors;
-       else
-               from = from->next;
+       envctrl_i2c_read_addr(addr);
 
-       n = strlen(name);
-       while (from && strncmp(from->name, name, n))
-               from = from->next;
+       /* Do a single byte read and send stop. */
+       rd = envctrl_i2c_read_data();
+       envctrl_i2c_stop();
 
-       return from;
+       return rd;
 }
 
-void
-check_temperatures(struct environment *env)
+/* Function Description: Decode data read from an adc device using firmware
+ *                       table.
+ * Return: Number of read bytes. Data is stored in bufdata in ascii format.
+ */
+static int envctrl_i2c_data_translate(unsigned char data, int translate_type,
+                                     int scale, char *tbl, char *bufdata)
 {
-       struct thermistor *t;
-
-       for (t = env->thermistors; t; t = t->next) {
-#ifdef DEBUG
-               printk("Thermistor `%s' [%02x:%d]: "
-                      "%d C (%d C, %d C)\n",
-                      t->name, t->regs.addr, t->regs.port,
-                      t->temperature(t), t->regs.warning, t->regs.shutdown);
-#endif
+       int len = 0;
 
-               /*
-                * Implement slow-down or shutdown here...
-                */
-       }
-}
+       switch (translate_type) {
+       case ENVCTRL_TRANSLATE_NO:
+               /* No decode necessary. */
+               len = 1;
+               bufdata[0] = data;
+               break;
 
-void
-check_fan_speeds(struct environment *env)
-{
-       unsigned char speed, max;
-       struct thermistor *t;
-       struct fan *f;
-
-       for (f = env->fans; f; f = f->next) {
-#ifdef DEBUG
-               printk("Fan `%s' [%02x:%d]:", f->name,
-                      f->regs.addr, f->regs.port);
-#endif
-               max = 0;
-               for (t = f->monitor; t; t = t->chain) {
-                       speed = t->fan_speed(t);
-                       if (speed > max)
-                               max = speed;
-#ifdef DEBUG
-                       printk(" %s:%02x", t->name, speed);
-#endif
-               }
+       case ENVCTRL_TRANSLATE_FULL:
+               /* Decode this way: data = table[data]. */
+               len = 1;
+               bufdata[0] = tbl[data];
+               break;
 
-               f->set_speed(f, max);
-#ifdef DEBUG
-               printk(" -> %02x\n", f->value);
-#endif
-       }
-}
+       case ENVCTRL_TRANSLATE_SCALE:
+               /* Decode this way: data = table[data]/scale */
+               sprintf(bufdata,"%d ", (tbl[data] * 10) / (scale));
+               len = strlen(bufdata);
+               bufdata[len - 1] = bufdata[len - 2];
+               bufdata[len - 2] = '.';
+               break;
 
-void
-envctrl_fans_blast(struct environment *env)
-{
-       struct fan *f;
+       default:
+               break;
+       };
 
-       for (f = env->fans; f; f = f->next)
-               f->set_speed(f, MAX_FAN_SPEED);
+       return len;
 }
 
-int
-kenvd(void *data)
+/* Function Description: Read cpu-related data such as cpu temperature, voltage.
+ * Return: Number of read bytes. Data is stored in bufdata in ascii format.
+ */
+static int envctrl_read_cpu_info(struct i2c_child_t *pchild,
+                                char mon_type, unsigned char *bufdata)
 {
-       struct environment *env = data;
-
-       MOD_INC_USE_COUNT;
-       lock_kernel();
-
-       env->kenvd_pid = current->pid;
-
-       exit_files(current);
-       exit_mm(current);
-
-       spin_lock_irq(&current->sigmask_lock);
-       siginitsetinv(&current->blocked, sigmask(SIGKILL));
-       recalc_sigpending(current);
-       spin_unlock_irq(&current->sigmask_lock);
-
-       current->session = 1;
-       current->pgrp = 1;
-       strcpy(current->comm, "kenvd");
-
-       if (env->enable)
-               env->enable(env);
-
-       while (!env->terminate) {
-
-               check_temperatures(env);
-               check_fan_speeds(env);
-               if (env->keep_alive)
-                       env->keep_alive(env);
-
-               current->state = TASK_INTERRUPTIBLE;
-               schedule_timeout(env->interval * HZ);
+       unsigned char data;
+       int i;
+       char *tbl, j = -1;
 
-               if (signal_pending(current)) {
-                       spin_lock_irq(&current->sigmask_lock);
-                       flush_signals(current);
-                       spin_unlock_irq(&current->sigmask_lock);
-                       break;
+       /* Find the right monitor type and channel. */
+       for (i = 0; i < PCF8584_MAX_CHANNELS; i++) {
+               if (pchild->mon_type[i] == mon_type) {
+                       if (++j == read_cpu) {
+                               break;
+                       }
                }
        }
 
-       if (env->disable)
-               env->disable(env);
+       if (j != read_cpu)
+               return 0;
 
-       env->kenvd_pid = 0;
-       wake_up(&envctrl.kenvd_wait);
+        /* Read data from address and port. */
+       data = envctrl_i2c_read_8591((unsigned char)pchild->addr,
+                                    (unsigned char)pchild->chnl_array[i].chnl_no);
 
-       MOD_DEC_USE_COUNT;
-       return 0;
-}
+       /* Find decoding table. */
+       tbl = pchild->tables + pchild->tblprop_array[i].offset;
 
-void
-envctrl_stop(void)
-{
-       DECLARE_WAITQUEUE(wait, current);
-       struct thermistor *t;
-       struct fan *f;
-       pid_t pid;
-
-       if (envctrl.kenvd_pid) {
-               pid = envctrl.kenvd_pid;
-
-               current->state = TASK_INTERRUPTIBLE;
-               add_wait_queue(&envctrl.kenvd_wait, &wait);
-
-               envctrl.terminate = 1;
-               kill_proc(pid, SIGKILL, 1);
-
-               schedule();
-
-               remove_wait_queue(&envctrl.kenvd_wait, &wait);
-               current->state = TASK_RUNNING;
-       }
-
-       t = envctrl.thermistors;
-       while (t) {
-               envctrl.thermistors = t->next;
-               kfree(t);
-               t = envctrl.thermistors;
-       }
-
-       f = envctrl.fans;
-       while (f) {
-               envctrl.fans = f->next;
-               kfree(f);
-               f = envctrl.fans;
-       }
-
-       if (envctrl.cpu_temp_table)
-               kfree(envctrl.cpu_temp_table);
-
-       if (envctrl.cpu_fan_speeds)
-               kfree(envctrl.cpu_fan_speeds);
-
-       if (envctrl.ps_temp_table)
-               kfree(envctrl.ps_temp_table);
-
-       if (envctrl.ps_fan_speeds)
-               kfree(envctrl.ps_fan_speeds);
-}
-
-
-static unsigned char
-axi_get_temperature(struct thermistor *t)
-{
-       unsigned char value;
-
-       if (read_8591(t->regs.addr, t->regs.port, &value) < 0)
-               return MAX_TEMPERATURE;
-       if (t->regs.port == AXI_THERM_PORT_CPU)
-               return axi_cpu_temp_table[value];
-       else
-               return axi_mod_temp_table[value];
+       return envctrl_i2c_data_translate(data, pchild->tblprop_array[i].type,
+                                         pchild->tblprop_array[i].scale,
+                                         tbl, bufdata);
 }
 
-static unsigned char
-axi_get_fan_speed(struct thermistor *t)
+/* Function Description: Read noncpu-related data such as motherboard 
+ *                       temperature.
+ * Return: Number of read bytes. Data is stored in bufdata in ascii format.
+ */
+static int envctrl_read_noncpu_info(struct i2c_child_t *pchild,
+                                   char mon_type, unsigned char *bufdata)
 {
-       unsigned char temp;
-
-       temp = t->temperature(t);
-       if (temp >= MAX_TEMPERATURE)
-               return MAX_FAN_SPEED;
-
-       return axi_fan_speeds[temp];
-}
+       unsigned char data;
+       int i;
+       char *tbl = NULL;
 
-static int
-axi_set_fan_speed(struct fan *f, unsigned char value)
-{
-       if (value != f->value) {
-               if (write_8444(f->regs.addr, f->regs.port, value))
-                       return -1;
-               f->value = value;
+       for (i = 0; i < PCF8584_MAX_CHANNELS; i++) {
+               if (pchild->mon_type[i] == mon_type)
+                       break;
        }
-       return 0;
-}
 
-static void
-axi_toggle_i2c_int(struct environment *env)
-{
-       unsigned char data;
+       if (i >= PCF8584_MAX_CHANNELS)
+               return 0;
 
-       if (i2c_read(AXI_PIO_ADDR, &data, 1, 0) != 1)
-               return;
+        /* Read data from address and port. */
+       data = envctrl_i2c_read_8591((unsigned char)pchild->addr,
+                                    (unsigned char)pchild->chnl_array[i].chnl_no);
 
-       data &= ~(0x08);
-       if (i2c_write(AXI_PIO_ADDR, &data, 1, 0) != 1)
-               return;
-       mdelay(1);
+       /* Find decoding table. */
+       tbl = pchild->tables + pchild->tblprop_array[i].offset;
 
-       data |= 0x08;
-       if (i2c_write(AXI_PIO_ADDR, &data, 1, 0) != 1)
-               return;
-       mdelay(1);
+       return envctrl_i2c_data_translate(data, pchild->tblprop_array[i].type,
+                                         pchild->tblprop_array[i].scale,
+                                         tbl, bufdata);
 }
 
-
-static int
-rasctrl_setup(int node)
+/* Function Description: Read fan status.
+ * Return : Always 1 byte. Status stored in bufdata.
+ */
+static int envctrl_i2c_fan_status(struct i2c_child_t *pchild,
+                                 unsigned char data,
+                                 char *bufdata)
 {
-       struct thermistor *t, **tlast;
-       struct fan *f, **flast;
-       char tmp[32];
-       int monitor;
-       int shutdown;
-       int warning;
-       int i;
+       unsigned char tmp, ret = 0;
+       int i, j = 0;
 
-       prom_getstring(prom_root_node, "name", tmp, sizeof(tmp));
-       if (strcmp(tmp, "SUNW,UltraSPARC-IIi-Engine")) {
-               printk("SUNW,rasctrl will work only on Ultra AXi\n");
-               return -ENODEV;
-       }
+       tmp = data & pchild->fan_mask;
 
-       monitor = prom_getintdefault(node, "env-monitor", 0);
-       if (monitor == 0)
-               return -ENODEV;
+       if (tmp == pchild->fan_mask) {
+               /* All bits are on. All fans are functioning. */
+               ret = ENVCTRL_ALL_FANS_GOOD;
+       } else if (tmp == 0) {
+               /* No bits are on. No fans are functioning. */
+               ret = ENVCTRL_ALL_FANS_BAD;
+       } else {
+               /* Go through all channels, mark 'on' the matched bits.
+                * Notice that fan_mask may have discontiguous bits but
+                * return mask are always contiguous. For example if we
+                * monitor 4 fans at channels 0,1,2,4, the return mask
+                * should be 00010000 if only fan at channel 4 is working.
+                */
+               for (i = 0; i < PCF8584_MAX_CHANNELS;i++) {
+                       if (pchild->fan_mask & chnls_mask[i]) {
+                               if (!(chnls_mask[i] & tmp))
+                                       ret |= chnls_mask[j];
 
-       envctrl.interval = prom_getintdefault(node, "env-mon-interval", 60);
-       warning = prom_getintdefault(node, "warning-temp", 55);
-       shutdown = prom_getintdefault(node, "shutdown-temp", 58);
-
-       tlast = &envctrl.thermistors;
-       for (i = 0; i < 4; i++) {
-               t = kmalloc(sizeof(struct thermistor), GFP_KERNEL);
-               if (!t)
-                       goto out;
-               memset(t, 0, sizeof(struct thermistor));
-
-               t->regs.addr = AXI_THERM_ADDR;
-               t->regs.port = i;
-               t->regs.warning = warning;
-               t->regs.shutdown = shutdown;
-
-               switch (i) {
-               case AXI_THERM_PORT_CPU:
-                       sprintf(t->name, "%.7s", "CPU");
-                       break;
-               case AXI_THERM_PORT_MOD:
-                       sprintf(t->name, "%.7s", "MOD");
-                       break;
-               case AXI_THERM_PORT_PCI:
-                       sprintf(t->name, "%.7s", "PCI");
-                       break;
-               case AXI_THERM_PORT_DISK:
-                       sprintf(t->name, "%.7s", "DISK");
-                       break;
+                               j++;
+                       }
                }
+       }
 
-               t->temperature = axi_get_temperature;
-               t->fan_speed = axi_get_fan_speed;
+       bufdata[0] = ret;
+       return 1;
+}
 
-               if (!i2c_find_device(t->regs.addr)) {
-                       printk("envctrl: `%s': i2c device %02x not found\n",
-                              t->name, t->regs.addr);
-                       kfree(t);
-                       continue;
+/* Function Description: Read voltage and power supply status.
+ * Return : Always 1 byte. Status stored in bufdata.
+ */
+static unsigned char envctrl_i2c_voltage_status(struct i2c_child_t *pchild,
+                                               unsigned char data,
+                                               char *bufdata)
+{
+       unsigned char tmp, ret = 0;
+       int i, j = 0;
+
+       tmp = data & pchild->voltage_mask;
+
+       /* Two channels are used to monitor voltage and power supply. */
+       if (tmp == pchild->voltage_mask) {
+               /* All bits are on. Voltage and power supply are okay. */
+               ret = ENVCTRL_VOLTAGE_POWERSUPPLY_GOOD;
+       } else if (tmp == 0) {
+               /* All bits are off. Voltage and power supply are bad */
+               ret = ENVCTRL_VOLTAGE_POWERSUPPLY_BAD;
+       } else {
+               /* Either voltage or power supply has problem. */
+               for (i = 0; i < PCF8584_MAX_CHANNELS; i++) {
+                       if (pchild->voltage_mask & chnls_mask[i]) {
+                               j++;
+
+                               /* Break out when there is a mismatch. */
+                               if (!(chnls_mask[i] & tmp))
+                                       break; 
+                       }
                }
 
-               *tlast = t;
-               tlast = &t->next;
+               /* Make a wish that hardware will always use the
+                * first channel for voltage and the second for
+                * power supply.
+                */
+               if (j == 1)
+                       ret = ENVCTRL_VOLTAGE_BAD;
+               else
+                       ret = ENVCTRL_POWERSUPPLY_BAD;
        }
 
-       flast = &envctrl.fans;
-       for (i = 0; i < 2; i++) {
-               f = kmalloc(sizeof(struct fan), GFP_KERNEL);
-               if (!f)
-                       goto out;
-               memset(f, 0, sizeof(struct fan));
-
-               f->regs.addr = AXI_FAN_ADDR;
-               f->regs.port = i;
-
-               switch (i) {
-               case AXI_FAN_PORT_FRONT:
-                       sprintf(f->name, "%.7s", "FRONT");
-                       t = NULL;
-                       while ((t = find_thermistor("CPU", t))) {
-                               t->chain = f->monitor;
-                               f->monitor = t;
-                       }
-                       break;
-               case AXI_FAN_PORT_BACK:
-                       sprintf(f->name, "%.7s", "BACK");
-                       t = NULL;
-                       while ((t = find_thermistor("PCI", t))) {
-                               t->chain = f->monitor;
-                               f->monitor = t;
-                       }
-                       break;
-               }
+       bufdata[0] = ret;
+       return 1;
+}
 
-               if (!f->monitor) {
-                       kfree(f);
-                       continue;
-               }
+/* Function Description: Read a byte from /dev/envctrl. Mapped to user read().
+ * Return: Number of read bytes. 0 for error.
+ */
+static ssize_t
+envctrl_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+       struct i2c_child_t *pchild;
+       unsigned char data[10];
+       int ret = 0;
+
+       /* Get the type of read as decided in ioctl() call.
+        * Find the appropriate i2c child.
+        * Get the data and put back to the user buffer.
+        */
+
+       switch ((int)(long)file->private_data) {
+       case ENVCTRL_RD_WARNING_TEMPERATURE:
+               if (warning_temperature == 0)
+                       return 0;
+
+               data[0] = (unsigned char)(warning_temperature);
+               ret = 1;
+               copy_to_user((unsigned char *)buf, data, ret);
+               break;
+
+       case ENVCTRL_RD_SHUTDOWN_TEMPERATURE:
+               if (shutdown_temperature == 0)
+                       return 0;
+
+               data[0] = (unsigned char)(shutdown_temperature);
+               ret = 1;
+               copy_to_user((unsigned char *)buf, data, ret);
+               break;
+
+       case ENVCTRL_RD_MTHRBD_TEMPERATURE:
+               if (!(pchild = envctrl_get_i2c_child(ENVCTRL_MTHRBDTEMP_MON)))
+                       return 0;
+               ret = envctrl_read_noncpu_info(pchild, ENVCTRL_MTHRBDTEMP_MON, data);
+               copy_to_user((unsigned char *)buf, data, ret);
+               break;
+
+       case ENVCTRL_RD_CPU_TEMPERATURE:
+               if (!(pchild = envctrl_get_i2c_child(ENVCTRL_CPUTEMP_MON)))
+                       return 0;
+               ret = envctrl_read_cpu_info(pchild, ENVCTRL_CPUTEMP_MON, data);
+
+               /* Reset cpu to the default cpu0. */
+               copy_to_user((unsigned char *)buf, data, ret);
+               break;
+
+       case ENVCTRL_RD_CPU_VOLTAGE:
+               if (!(pchild = envctrl_get_i2c_child(ENVCTRL_CPUVOLTAGE_MON)))
+                       return 0;
+               ret = envctrl_read_cpu_info(pchild, ENVCTRL_CPUVOLTAGE_MON, data);
+
+               /* Reset cpu to the default cpu0. */
+               copy_to_user((unsigned char *)buf, data, ret);
+               break;
+
+       case ENVCTRL_RD_SCSI_TEMPERATURE:
+               if (!(pchild = envctrl_get_i2c_child(ENVCTRL_SCSITEMP_MON)))
+                       return 0;
+               ret = envctrl_read_noncpu_info(pchild, ENVCTRL_SCSITEMP_MON, data);
+               copy_to_user((unsigned char *)buf, data, ret);
+               break;
+
+       case ENVCTRL_RD_ETHERNET_TEMPERATURE:
+               if (!(pchild = envctrl_get_i2c_child(ENVCTRL_ETHERTEMP_MON)))
+                       return 0;
+               ret = envctrl_read_noncpu_info(pchild, ENVCTRL_ETHERTEMP_MON, data);
+               copy_to_user((unsigned char *)buf, data, ret);
+               break;
+
+       case ENVCTRL_RD_FAN_STATUS:
+               if (!(pchild = envctrl_get_i2c_child(ENVCTRL_FANSTAT_MON)))
+                       return 0;
+               data[0] = envctrl_i2c_read_8574(pchild->addr);
+               ret = envctrl_i2c_fan_status(pchild,data[0], data);
+               copy_to_user((unsigned char *)buf, data, ret);
+               break;
+
+       case ENVCTRL_RD_VOLTAGE_STATUS:
+               if (!(pchild = envctrl_get_i2c_child(ENVCTRL_VOLTAGESTAT_MON)))
+                       return 0;
+               data[0] = envctrl_i2c_read_8574(pchild->addr);
+               ret = envctrl_i2c_voltage_status(pchild, data[0], data);
+               copy_to_user((unsigned char *)buf, data, ret);
+               break;
+
+       default:
+               break;
+
+       };
+
+       return ret;
+}
+
+/* Function Description: Command what to read.  Mapped to user ioctl().
+ * Return: Gives 0 for implemented commands, -EINVAL otherwise.
+ */
+static int
+envctrl_ioctl(struct inode *inode, struct file *file,
+             unsigned int cmd, unsigned long arg)
+{
+       char *infobuf;
 
-               if (!i2c_find_device(f->regs.addr)) {
-                       printk("envctrl: `%s': i2c device %02x not found\n",
-                              f->name, f->regs.addr);
-                       kfree(f);
-                       continue;
+       switch (cmd) {
+       case ENVCTRL_RD_WARNING_TEMPERATURE:
+       case ENVCTRL_RD_SHUTDOWN_TEMPERATURE:
+       case ENVCTRL_RD_MTHRBD_TEMPERATURE:
+       case ENVCTRL_RD_FAN_STATUS:
+       case ENVCTRL_RD_VOLTAGE_STATUS:
+       case ENVCTRL_RD_ETHERNET_TEMPERATURE:
+       case ENVCTRL_RD_SCSI_TEMPERATURE:
+               file->private_data = (void *)(long)cmd;
+               break;
+
+       case ENVCTRL_RD_CPU_TEMPERATURE:
+       case ENVCTRL_RD_CPU_VOLTAGE:
+               /* Check to see if application passes in any cpu number,
+                * the default is cpu0.
+                */
+               infobuf = (char *) arg;
+               if (infobuf == NULL) {
+                       read_cpu = 0;
+               }else {
+                       get_user(read_cpu, infobuf);
                }
 
-               *flast = f;
-               flast = &f->next;
+               /* Save the command for use when reading. */
+               file->private_data = (void *)(long)cmd;
+               break;
 
-               f->check_failure = NULL;
-               f->set_speed = axi_set_fan_speed;
-       }
-
-       envctrl.enable = axi_toggle_i2c_int;
-       envctrl.disable = envctrl_fans_blast;
+       default:
+               return -EINVAL;
+       };
 
-#ifdef DEBUG
-       printk("Warn: %d C, Shutdown %d C, Interval %d s, Monitor %d\n",
-               warning, shutdown, envctrl.interval, monitor);
-#endif
        return 0;
-
-out:
-       return -ENODEV;
-}
-
-
-#ifdef U450_SUPPORT
-
-static unsigned char
-envctrl_get_temperature(struct thermistor *t)
-{
-       unsigned char value;
-
-       if (read_8591(t->regs.addr, t->regs.port, &value) < 0)
-               return MAX_TEMPERATURE;
-       if (!strncmp(t->name, "CPU", 3))
-               return envctrl.cpu_temp_table[value];
-       else
-               return envctrl.ps_temp_table[value];
 }
 
-static unsigned char
-envctrl_get_fan_speed(struct thermistor *t)
+/* Function Description: open device. Mapped to user open().
+ * Return: Always 0.
+ */
+static int
+envctrl_open(struct inode *inode, struct file *file)
 {
-       unsigned char temp;
-
-       temp = t->temperature(t);
-       if (temp >= MAX_TEMPERATURE)
-               return MAX_FAN_SPEED;
-
-       if (!strncmp(t->name, "CPU", 3))
-               return envctrl.cpu_fan_speeds[temp];
-       else
-               return envctrl.ps_fan_speeds[temp];
+       file->private_data = 0;
+       MOD_INC_USE_COUNT;
+       return 0;
 }
 
+/* Function Description: Open device. Mapped to user close().
+ * Return: Always 0.
+ */
 static int
-envctrl_set_fan_speed(struct fan *f, unsigned char value)
+envctrl_release(struct inode *inode, struct file *file)
 {
-       if (value != f->value) {
-               if (write_8444(f->regs.addr, f->regs.port, value))
-                       return -1;
-               f->value = value;
-       }
-
+       MOD_DEC_USE_COUNT;
        return 0;
 }
 
-static unsigned char u450_default_thermisters[] =
-{
-       /* CPU0 */
-       0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x46,
-       0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01,
-       0x00, 0x00, 0x00, 0x01, 0x43, 0x50, 0x55, 0x30, 0x00,
-       /* CPU1 */
-       0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x46,
-       0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01,
-       0x00, 0x00, 0x00, 0x01, 0x43, 0x50, 0x55, 0x31, 0x00,
-       /* CPU2 */
-       0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x46,
-       0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01,
-       0x00, 0x00, 0x00, 0x01, 0x43, 0x50, 0x55, 0x32, 0x00,
-       /* CPU3 */
-       0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x46,
-       0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01,
-       0x00, 0x00, 0x00, 0x01, 0x43, 0x50, 0x55, 0x33, 0x00,
-       /* PS0 */
-       0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x5a,
-       0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x01,
-       0x00, 0x00, 0x00, 0x01, 0x50, 0x53, 0x30, 0x00,
-       /* PS1 */
-       0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x5a,
-       0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x01,
-       0x00, 0x00, 0x00, 0x01, 0x50, 0x53, 0x31, 0x00,
-       /* PS2 */
-       0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x5a,
-       0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x01,
-       0x00, 0x00, 0x00, 0x01, 0x50, 0x53, 0x32, 0x00,
-       /* AMB */
-       0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x28,
-       0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01,
-       0x00, 0x00, 0x00, 0x01, 0x41, 0x4d, 0x42, 0x00
-};
-
-static unsigned char u450_default_cpu_temp_factors[] =
-{
-       0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
-       0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
-       0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
-       0x96, 0x94, 0x92, 0x90, 0x8f, 0x8e, 0x8d, 0x8c,
-       0x8a, 0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82,
-       0x81, 0x80, 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a,
-       0x79, 0x79, 0x78, 0x78, 0x77, 0x76, 0x75, 0x74,
-       0x73, 0x72, 0x71, 0x70, 0x70, 0x6f, 0x6f, 0x6e,
-       0x6e, 0x6e, 0x6d, 0x6d, 0x6c, 0x6b, 0x6a, 0x6a,
-       0x69, 0x69, 0x68, 0x67, 0x66, 0x65, 0x65, 0x64,
-       0x64, 0x64, 0x63, 0x63, 0x62, 0x62, 0x61, 0x61,
-       0x60, 0x60, 0x5f, 0x5f, 0x5e, 0x5e, 0x5d, 0x5d,
-       0x5c, 0x5c, 0x5b, 0x5b, 0x5b, 0x5a, 0x5a, 0x5a,
-       0x59, 0x59, 0x58, 0x58, 0x57, 0x57, 0x56, 0x56,
-       0x55, 0x55, 0x54, 0x54, 0x53, 0x53, 0x52, 0x52,
-       0x52, 0x51, 0x51, 0x50, 0x50, 0x50, 0x50, 0x4f,
-       0x4f, 0x4f, 0x4e, 0x4e, 0x4e, 0x4d, 0x4d, 0x4d,
-       0x4c, 0x4c, 0x4c, 0x4b, 0x4b, 0x4b, 0x4a, 0x4a,
-       0x4a, 0x49, 0x49, 0x49, 0x48, 0x48, 0x48, 0x47,
-       0x47, 0x47, 0x46, 0x46, 0x46, 0x46, 0x45, 0x45,
-       0x45, 0x44, 0x44, 0x44, 0x44, 0x43, 0x43, 0x43,
-       0x43, 0x42, 0x42, 0x42, 0x42, 0x41, 0x41, 0x41,
-       0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e, 0x3e,
-       0x3e, 0x3d, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
-       0x3c, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
-       0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37, 0x37,
-       0x37, 0x37, 0x36, 0x36, 0x36, 0x35, 0x35, 0x35,
-       0x34, 0x34, 0x34, 0x33, 0x33, 0x33, 0x33, 0x32,
-       0x32, 0x32, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30,
-       0x2f, 0x2f, 0x2f, 0x2e, 0x2e, 0x2e, 0x2d, 0x2d,
-       0x2d, 0x2c, 0x2c, 0x2c, 0x2b, 0x2b, 0x2b, 0x2a,
-       0x2a, 0x2a, 0x29, 0x29, 0x29, 0x28, 0x28, 0x28
-};
-
-static unsigned char u450_default_cpu_fan_speeds[] =
-{
-       0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
-       0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
-       0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
-       0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x21, 0x22, 0x23,
-       0x24, 0x25, 0x26, 0x27, 0x28, 0x2a, 0x2b, 0x2d,
-       0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
-       0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f
-};
-
-static unsigned char u450_default_ps_temp_factors[] =
-{
-       0x9a, 0x96, 0x82, 0x7d, 0x78, 0x73, 0x6e, 0x6b,
-       0x69, 0x67, 0x64, 0x5f, 0x5a, 0x57, 0x55, 0x53,
-       0x51, 0x50, 0x4e, 0x4d, 0x4c, 0x4b, 0x49, 0x47,
-       0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3f,
-       0x3e, 0x3d, 0x3c, 0x3c, 0x3b, 0x3a, 0x39, 0x39,
-       0x38, 0x37, 0x37, 0x36, 0x35, 0x35, 0x34, 0x33,
-       0x32, 0x32, 0x32, 0x31, 0x31, 0x30, 0x30, 0x2f,
-       0x2f, 0x2e, 0x2e, 0x2d, 0x2d, 0x2c, 0x2c, 0x2b,
-       0x2a, 0x2a, 0x29, 0x29, 0x28, 0x28, 0x27, 0x27,
-       0x26, 0x26, 0x25, 0x25, 0x25, 0x25, 0x24, 0x24,
-       0x23, 0x23, 0x23, 0x22, 0x22, 0x22, 0x21, 0x21,
-       0x21, 0x20, 0x20, 0x20, 0x1f, 0x1f, 0x1e, 0x1e,
-       0x1e, 0x1d, 0x1d, 0x1d, 0x1d, 0x1c, 0x1c, 0x1c,
-       0x1b, 0x1b, 0x1b, 0x1a, 0x1a, 0x1a, 0x19, 0x19,
-       0x19, 0x18, 0x18, 0x18, 0x18, 0x17, 0x17, 0x17,
-       0x17, 0x16, 0x16, 0x16, 0x16, 0x15, 0x15, 0x15,
-       0x14, 0x14, 0x14, 0x13, 0x13, 0x13, 0x13, 0x13,
-       0x12, 0x12, 0x12, 0x12, 0x11, 0x11, 0x11, 0x11,
-       0x10, 0x10, 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0f,
-       0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d,
-       0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b,
-       0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
-       0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08,
-       0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06,
-       0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04,
-       0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03,
-       0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
-       0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
+static struct file_operations envctrl_fops = {
+       owner:          THIS_MODULE,
+       read:           envctrl_read,
+       ioctl:          envctrl_ioctl,
+       open:           envctrl_open,
+       release:        envctrl_release,
+};     
 
-static unsigned char u450_default_ps_fan_speeds[] =
-{
-       0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
-       0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
-       0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
-       0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x21, 0x22, 0x23,
-       0x24, 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x2a,
-       0x2b, 0x2d, 0x2e, 0x2f, 0x30, 0x30, 0x30, 0x30,
-       0x30, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
-       0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
-       0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f
+static struct miscdevice envctrl_dev = {
+       ENVCTRL_MINOR,
+       "envctrl",
+       &envctrl_fops
 };
 
-static void
-u450_toggle_i2c_int(struct environment *env)
+/* Function Description: Set monitor type based on firmware description.
+ * Return: None.
+ */
+static void envctrl_set_mon(struct i2c_child_t *pchild,
+                           char *chnl_desc,
+                           int chnl_no)
+{
+       /* Firmware only has temperature type.  It does not distinguish
+        * different kinds of temperatures.  We use channel description
+        * to disinguish them.
+        */
+       if (!(strcmp(chnl_desc,"temp,cpu")) ||
+           !(strcmp(chnl_desc,"temp,cpu0")) ||
+           !(strcmp(chnl_desc,"temp,cpu1")) ||
+           !(strcmp(chnl_desc,"temp,cpu2")) ||
+           !(strcmp(chnl_desc,"temp,cpu3")))
+               pchild->mon_type[chnl_no] = ENVCTRL_CPUTEMP_MON;
+
+       if (!(strcmp(chnl_desc,"vddcore,cpu0")) ||
+           !(strcmp(chnl_desc,"vddcore,cpu1")) ||
+           !(strcmp(chnl_desc,"vddcore,cpu2")) ||
+           !(strcmp(chnl_desc,"vddcore,cpu3")))
+               pchild->mon_type[chnl_no] = ENVCTRL_CPUVOLTAGE_MON;
+
+       if (!(strcmp(chnl_desc,"temp,motherboard")))
+               pchild->mon_type[chnl_no] = ENVCTRL_MTHRBDTEMP_MON;
+
+       if (!(strcmp(chnl_desc,"temp,scsi")))
+               pchild->mon_type[chnl_no] = ENVCTRL_SCSITEMP_MON;
+
+       if (!(strcmp(chnl_desc,"temp,ethernet")))
+               pchild->mon_type[chnl_no] = ENVCTRL_ETHERTEMP_MON;
+
+       if (!(strcmp(chnl_desc,"temp,ethernet")))
+               pchild->mon_type[chnl_no] = ENVCTRL_ETHERTEMP_MON;
+}
+
+/* Function Description: Initialize monitor channel with channel desc,
+ *                       decoding tables, monitor type, optional properties.
+ * Return: None.
+ */
+static void envctrl_init_adc(struct i2c_child_t *pchild, int node)
+{
+       char chnls_desc[CHANNEL_DESC_SZ];
+       int i, len, j = 0;
+       char *ptr;
+
+       /* Firmware describe channels into a stream separated by a '\0'.
+        * Replace all '\0' with a space.
+        */
+        len = prom_getproperty(node, "channels-description", chnls_desc,
+                              CHANNEL_DESC_SZ);
+        for (i = 0; i < len; i++) {
+                if (chnls_desc[i] == '\0')
+                        chnls_desc[i] = ' ';
+        }
+
+       ptr = strtok(chnls_desc, " ");
+       while (ptr != NULL) {
+               envctrl_set_mon(pchild, ptr, j);
+               ptr = strtok(NULL, " ");
+               j++;
+       }
+
+       /* Get optional properties. */
+        len = prom_getproperty(node, "warning-temp", (char *)&warning_temperature,
+                              sizeof(warning_temperature));
+        len = prom_getproperty(node, "shutdown-temp", (char *)&shutdown_temperature,
+                              sizeof(shutdown_temperature));
+}
+
+/* Function Description: Initialize child device monitoring fan status.
+ * Return: None.
+ */
+static void envctrl_init_fanstat(struct i2c_child_t *pchild)
 {
-       unsigned char tmp[80];
-       unsigned char data;
-       int i, n;
+       int i;
 
-       write_8583(U450_TIMER_ADDR, 0, 0x84);
-       write_8583(U450_TIMER_ADDR, 8, 0x0a);
-       write_8583(U450_TIMER_ADDR, 7, 0x00);
-       write_8583(U450_TIMER_ADDR, 0, 0x04);
+       /* Go through all channels and set up the mask. */
+       for (i = 0; i < pchild->total_chnls; i++)
+               pchild->fan_mask |= chnls_mask[(pchild->chnl_array[i]).chnl_no];
 
-       n = sprintf(tmp, "envctrl: PCF8583:");
-       for (i = 0; i < 16; i++) {
-               if (read_8583(U450_TIMER_ADDR, i, &data) < 0) {
-                       printk("envctrl: error reading PCF8583\n");
-                       break;
-               }
-               n += sprintf(tmp+n, " %02x", data);
-       }
-       printk("%s\n", tmp);
-
-#if 1
-       data = 0x70;
-       if (i2c_write(U450_PIO_ADDR, &data, 1, 0) != 1)
-               return;
-       mdelay(1);
-
-       data = 0x78;
-       if (i2c_write(U450_PIO_ADDR, &data, 1, 0) != 1)
-               return;
-       mdelay(1);
-#endif
+       /* We only need to know if this child has fan status monitored.
+        * We dont care which channels since we have the mask already.
+        */
+       pchild->mon_type[0] = ENVCTRL_FANSTAT_MON;
 }
 
-static void
-u450_set_egg_timer(struct environment *env)
+/* Initialize child device monitoring voltage status. */
+static void envctrl_init_voltage_status(struct i2c_child_t *pchild)
 {
-       unsigned char value;
-
-#if 0
-       write_8583(U450_TIMER_ADDR, 0x00, 0x84);
-       read_8583(U450_TIMER_ADDR, 0x07, &value);
-       write_8583(U450_TIMER_ADDR, 0x07, 0x00);
-       write_8583(U450_TIMER_ADDR, 0x00, 0x04);
-#else
-       read_8583(U450_TIMER_ADDR, 0x07, &value);
-       printk("envctrl: TIMER [%02x:07]: %02x\n", U450_TIMER_ADDR, value);
-       read_8583(U450_TIMER_ADDR, 0x00, &value);
-       printk("envctrl: TIMER [%02x:00]: %02x\n", U450_TIMER_ADDR, value);
-#endif
-}
+       int i;
 
-static int
-envctrl_setup(int node)
-{
-       struct thermistor *t, **tlast;
-       struct fan *f, **flast;
-       unsigned char *tmp = NULL, *p;
-       int len, n, err;
-       int defaults = 0;
-
-       len = prom_getproplen(node, "thermisters");
-       if (len <= 0) {
-               printk("envctrl: no property `thermisters', using defaults\n");
-               defaults++;
-               len = sizeof(u450_default_thermisters);
-       }
+       /* Go through all channels and set up the mask. */
+       for (i = 0; i < pchild->total_chnls; i++)
+               pchild->voltage_mask |= chnls_mask[(pchild->chnl_array[i]).chnl_no];
 
-       tmp = (unsigned char *)kmalloc(len, GFP_KERNEL);
-       if (!tmp) {
-               printk("envctrl: can't allocate property buffer\n");
-               return -ENODEV;
-       }
+       /* We only need to know if this child has voltage status monitored.
+        * We dont care which channels since we have the mask already.
+        */
+       pchild->mon_type[0] = ENVCTRL_VOLTAGESTAT_MON;
+}
 
-       if (defaults) {
-               memcpy(tmp, u450_default_thermisters, len);
-       } else {
-               err = prom_getproperty(node, "thermisters", tmp, len);
-               if (err < 0) {
-                       printk("envctrl: error reading property `thermisters'\n");
-                       kfree(tmp);
-                       return -ENODEV;
+/* Function Description: Initialize i2c child device.
+ * Return: None.
+ */
+static void envctrl_init_i2c_child(struct linux_ebus_child *edev_child,
+                                  struct i2c_child_t *pchild)
+{
+       int node, len, i, tbls_size = 0;
+
+       node = edev_child->prom_node;
+
+       /* Get device address. */
+       len = prom_getproperty(node, "reg",
+                              (char *) &(pchild->addr),
+                              sizeof(pchild->addr));
+
+       /* Get tables property.  Read firmware temperature tables. */
+       len = prom_getproperty(node, "translation",
+                              (char *) pchild->tblprop_array,
+                              (PCF8584_MAX_CHANNELS *
+                               sizeof(struct pcf8584_tblprop)));
+       if (len > 0) {
+                pchild->total_tbls = len / sizeof(struct pcf8584_tblprop);
+               for (i = 0; i < pchild->total_tbls; i++) {
+                       if ((pchild->tblprop_array[i].size + pchild->tblprop_array[i].offset) > tbls_size) {
+                               tbls_size = pchild->tblprop_array[i].size + pchild->tblprop_array[i].offset;
+                       }
                }
-       }
-
-       p = tmp;
-       err = -ENOMEM;
 
-       tlast = &envctrl.thermistors;
-       while (len > sizeof(struct therm_regs)) {
-               t = kmalloc(sizeof(struct thermistor), GFP_KERNEL);
-               if (!t) {
-                       printk("envctrl: can't allocate thermistor struct\n");
-                       goto out;
-               }
-               memset(t, 0, sizeof(struct thermistor));
-
-               memcpy(&t->regs, p, sizeof(struct therm_regs));
-               p += sizeof(struct therm_regs);
-               len -= sizeof(struct therm_regs);
-
-               n = strlen(p) + 1;
-               strncpy(t->name, p, 7);
-               p += n;
-               len -= n;
-
-               if (!i2c_find_device(t->regs.addr)) {
-                       printk("envctrl: `%s': i2c device %02x not found\n",
-                              t->name, t->regs.addr);
-                       kfree(t);
-                       continue;
+                pchild->tables = kmalloc(tbls_size, GFP_KERNEL);
+                len = prom_getproperty(node, "tables",
+                                      (char *) pchild->tables, tbls_size);
+                if (len <= 0) {
+                       printk("envctrl: Failed to get table.\n");
+                       return;
                }
+       }
 
-               t->temperature = envctrl_get_temperature;
-               t->fan_speed = envctrl_get_fan_speed;
+       /* Get the monitor channels. */
+       len = prom_getproperty(node, "channels-in-use",
+                              (char *) pchild->chnl_array,
+                              (PCF8584_MAX_CHANNELS *
+                               sizeof(struct pcf8584_channel)));
+       pchild->total_chnls = len / sizeof(struct pcf8584_channel);
 
-               *tlast = t;
-               tlast = &t->next;
-       }
+       for (i = 0; i < pchild->total_chnls; i++) {
+               switch (pchild->chnl_array[i].type) {
+               case PCF8584_TEMP_TYPE:
+                       envctrl_init_adc(pchild, node);
+                       break;
 
-       flast = &envctrl.fans;
-       for (n = 0; n < 2; n++) {
-               f = kmalloc(sizeof(struct fan), GFP_KERNEL);
-               if (!f)
-                       goto out;
-               memset(f, 0, sizeof(struct fan));
-
-               f->regs.addr = U450_FAN_ADDR;
-               f->regs.port = n;
-
-               switch (n) {
-               case U450_FAN_PORT_CPU:
-                       sprintf(f->name, "%.7s", "CPU");
-                       t = NULL;
-                       while ((t = find_thermistor("CPU", t))) {
-                               t->chain = f->monitor;
-                               f->monitor = t;
-                       }
+               case PCF8584_FANSTAT_TYPE:
+                       envctrl_init_fanstat(pchild);
+                       i = pchild->total_chnls;
                        break;
-               case U450_FAN_PORT_PS:
-                       sprintf(f->name, "%.7s", "PS");
-                       t = NULL;
-                       while ((t = find_thermistor("PS", t))) {
-                               t->chain = f->monitor;
-                               f->monitor = t;
+
+               case PCF8584_VOLTAGE_TYPE:
+                       if (pchild->i2ctype == I2C_ADC) {
+                               envctrl_init_adc(pchild,node);
+                       } else {
+                               envctrl_init_voltage_status(pchild);
                        }
+                       i = pchild->total_chnls;
                        break;
-               }
-
-               if (!f->monitor) {
-                       kfree(f);
-                       continue;
-               }
-
-               if (!i2c_find_device(f->regs.addr)) {
-                       printk("envctrl: `%s': i2c device %02x not found\n",
-                              f->name, f->regs.addr);
-                       kfree(f);
-                       continue;
-               }
-
-               *flast = f;
-               flast = &f->next;
 
-               f->check_failure = NULL;
-               f->set_speed = envctrl_set_fan_speed;
-       }
-
-       envctrl.cpu_temp_table = kmalloc(256, GFP_KERNEL);
-       if (!envctrl.cpu_temp_table) {
-               printk("envctrl: can't allocate temperature table\n");
-               goto out;
-       }
-       if (defaults) {
-               memcpy(envctrl.cpu_temp_table,
-                      u450_default_cpu_temp_factors, 256);
-       } else {
-               err = prom_getproperty(node, "cpu-temp-factors",
-                                      envctrl.cpu_temp_table, 256);
-               if (err < 0) {
-                       printk("envctrl: can't read `cpu-temp-factors'\n");
-                       goto out;
-               }
-       }
-
-       envctrl.cpu_fan_speeds = kmalloc(112, GFP_KERNEL);
-       if (!envctrl.cpu_fan_speeds) {
-               printk("envctrl: can't allocate fan speed table\n");
-               goto out;
-       }
-       if (defaults) {
-               memcpy(envctrl.cpu_fan_speeds,
-                      u450_default_cpu_fan_speeds, 112);
-       } else {
-               err = prom_getproperty(node, "cpu-fan-speeds",
-                                      envctrl.cpu_fan_speeds, 112);
-               if (err < 0) {
-                       printk("envctrl: can't read `cpu-fan-speeds'\n");
-                       goto out;
-               }
-       }
-
-       envctrl.ps_temp_table = kmalloc(256, GFP_KERNEL);
-       if (!envctrl.ps_temp_table) {
-               printk("envctrl: can't allocate temperature table\n");
-               goto out;
-       }
-       if (defaults) {
-               memcpy(envctrl.ps_temp_table,
-                      u450_default_ps_temp_factors, 256);
-       } else {
-               err = prom_getproperty(node, "ps-temp-factors",
-                                      envctrl.ps_temp_table, 256);
-               if (err < 0) {
-                       printk("envctrl: can't read `ps-temp-factors'\n");
-                       goto out;
-               }
-       }
-
-       envctrl.ps_fan_speeds = kmalloc(112, GFP_KERNEL);
-       if (!envctrl.ps_fan_speeds) {
-               printk("envctrl: can't allocate fan speed table\n");
-               goto out;
-       }
-       if (defaults) {
-               memcpy(envctrl.ps_fan_speeds,
-                      u450_default_ps_fan_speeds, 112);
-       } else {
-               err = prom_getproperty(node, "ps-fan-speeds",
-                                      envctrl.ps_fan_speeds, 112);
-               if (err < 0) {
-                       printk("envctrl: can't read `ps-fan-speeds'\n");
-                       goto out;
-               }
+               default:
+                       break;
+               };
        }
-
-       envctrl.enable = u450_toggle_i2c_int;
-       envctrl.keep_alive = u450_set_egg_timer;
-       envctrl.disable = envctrl_fans_blast;
-       envctrl.interval = 60;
-
-       kfree(tmp);
-       return 0;
-
-out:
-       if (tmp)
-               kfree(tmp);
-       return err;
-}
-#endif /* U450_SUPPORT */
-
-
-
-static loff_t
-envctrl_llseek(struct file *file, loff_t offset, int type)
-{
-       return -ESPIPE;
-}
-
-static ssize_t
-envctrl_read(struct file *file, char *buf, size_t count, loff_t *ppos)
-{
-       unsigned long addr = (unsigned long)file->private_data;
-
-       return i2c_read(addr, buf, count, 1);
-}
-
-static ssize_t
-envctrl_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
-{
-       unsigned long addr = (unsigned long)file->private_data;
-
-       return i2c_write(addr, buf, count, 1);
 }
 
-static int
-envctrl_ioctl(struct inode *inode, struct file *file,
-             unsigned int cmd, unsigned long arg)
+/* Function Description: Search the child device list for a device.
+ * Return : The i2c child if found. NULL otherwise.
+ */
+static struct i2c_child_t *envctrl_get_i2c_child(unsigned char mon_type)
 {
-       unsigned long data;
-       int addr;
+       int i, j;
 
-       switch (cmd) {
-               case I2CIOCSADR:
-                       if (get_user(addr, (int *)arg))
-                               return -EFAULT;
-                       data = addr & 0xfe;
-                       if (!i2c_find_device(addr & 0xfe))
-                               return -ENODEV;
-                       file->private_data = (void *)data;
-                       break;
-               case I2CIOCGADR:
-                       addr = (unsigned long)file->private_data;
-                       if (put_user(addr, (int *)arg))
-                               return -EFAULT;
-                       break;
-               default:
-                       return -EINVAL;
+       for (i = 0; i < ENVCTRL_MAX_CPU*2; i++) {
+               for (j = 0; j < PCF8584_MAX_CHANNELS; j++) {
+                       if (i2c_childlist[i].mon_type[j] == mon_type) {
+                               return (struct i2c_child_t*)(&(i2c_childlist[i]));
+                       }
+               }
        }
-       return 0;
+       return NULL;
 }
 
-static int
-envctrl_open(struct inode *inode, struct file *file)
-{
-       file->private_data = 0;
-       return 0;
-}
-
-static struct file_operations envctrl_fops = {
-       owner:          THIS_MODULE,
-       llseek:         envctrl_llseek,
-       read:           envctrl_read,
-       write:          envctrl_write,
-       ioctl:          envctrl_ioctl,
-       open:           envctrl_open,
-};
-
-static struct miscdevice envctrl_dev = {
-       ENVCTRL_MINOR,
-       "envctrl",
-       &envctrl_fops
-};
-
-#ifdef MODULE
-int init_module(void)
-#else
-int __init envctrl_init(void)
-#endif
+static int __init envctrl_init(void)
 {
 #ifdef CONFIG_PCI
-       struct linux_ebus *ebus;
-       struct linux_ebus_device *edev = 0;
-       pid_t pid;
-       int err;
-
+       struct linux_ebus *ebus = NULL;
+       struct linux_ebus_device *edev = NULL;
+       struct linux_ebus_child *edev_child = NULL;
+       int i = 0;
+
+       /* Traverse through ebus and ebus device list for i2c device and
+        * adc and gpio nodes.
+        */
        for_each_ebus(ebus) {
                for_each_ebusdev(edev, ebus) {
-                       if (!strcmp(edev->prom_name, "SUNW,envctrl"))
-                               goto ebus_done;
-                       if (!strcmp(edev->prom_name, "SUNW,rasctrl"))
-                               goto ebus_done;
+                       if (!strcmp(edev->prom_name, "i2c")) {
+                               i2c = ioremap(  edev->resource[0].start, 
+                                                               sizeof(struct pcf8584_reg));
+                               for_each_edevchild(edev, edev_child) {
+                                       if (!strcmp("gpio", edev_child->prom_name)) {
+                                               i2c_childlist[i].i2ctype = I2C_GPIO;
+                                               envctrl_init_i2c_child(edev_child, &(i2c_childlist[i++]));
+                                       }
+                                       if (!strcmp("adc", edev_child->prom_name)) {
+                                               i2c_childlist[i].i2ctype = I2C_ADC;
+                                               envctrl_init_i2c_child(edev_child, &(i2c_childlist[i++]));
+                                       }
+                               }
+                               goto done;
+                       }
                }
        }
-ebus_done:
-       if (!edev) {
-               printk("%s: ebus device not found\n", __FUNCTION__);
-               return -ENODEV;
-       }
-
-       i2c_regs = (unsigned long) ioremap(edev->resource[0].start, I2C_REG_SIZE);
-       writeb(CONTROL_PIN, i2c_regs + I2C_CSR);
-       writeb(PCF8584_ADDRESS >> 1, i2c_regs + I2C_DATA);
-       writeb(CONTROL_PIN | CONTROL_ES1, i2c_regs + I2C_CSR);
-       writeb(CLK_4_43 | BUS_CLK_90, i2c_regs + I2C_DATA);
-       writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_ACK, i2c_regs + I2C_CSR);
-       mdelay(10);
 
-       if (misc_register(&envctrl_dev)) {
-               printk("%s: unable to get misc minor %d\n",
-                      __FUNCTION__, envctrl_dev.minor);
+done:
+       if (!edev) {
+               printk("envctrl: I2C device not found.\n");
                return -ENODEV;
        }
 
-       err = i2c_scan_bus();
-       if (err) {
-               i2c_free_devices();
-               misc_deregister(&envctrl_dev);
-               return err;
-       }
+       /* Set device address. */
+       envctrl_writeb(CONTROL_PIN, &i2c->csr);
+       envctrl_writeb(PCF8584_ADDRESS, &i2c->data);
 
-       memset(&envctrl, 0, sizeof(struct environment));
+       /* Set system clock and SCL frequencies. */ 
+       envctrl_writeb(CONTROL_PIN | CONTROL_ES1, &i2c->csr);
+       envctrl_writeb(CLK_4_43 | BUS_CLK_90, &i2c->data);
 
-       err = -ENODEV;
-       if (!strcmp(edev->prom_name, "SUNW,rasctrl"))
-               err = rasctrl_setup(edev->prom_node);
-#ifdef U450_SUPPORT
-       else if (!strcmp(edev->prom_name, "SUNW,envctrl"))
-               err = envctrl_setup(edev->prom_node);
-#endif
+       /* Enable serial interface. */
+       envctrl_writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_ACK, &i2c->csr);
+       udelay(200);
 
-       if (err) {
-               envctrl_stop();
-               i2c_free_devices();
-               misc_deregister(&envctrl_dev);
-               return err;
+       /* Register the device as a minor miscellaneous device. */
+       if (misc_register(&envctrl_dev)) {
+               printk("envctrl: Unable to get misc minor %d\n",
+                      envctrl_dev.minor);
        }
 
-       init_waitqueue_head(&envctrl.kenvd_wait);
-
-       pid = kernel_thread(kenvd, (void *)&envctrl, CLONE_FS);
-       if (pid < 0) {
-               envctrl_stop();
-               i2c_free_devices();
-               misc_deregister(&envctrl_dev);
-               return -ENODEV;
+       /* Note above traversal routine post-incremented 'i' to accomodate 
+        * a next child device, so we decrement before reverse-traversal of
+        * child devices.
+        */
+       printk("envctrl: initialized ");
+       for(--i; i >= 0; --i)
+       {
+               printk("[%s 0x%lx]%s", 
+                       (I2C_ADC == i2c_childlist[i].i2ctype) ? ("adc") : 
+                       ((I2C_GPIO == i2c_childlist[i].i2ctype) ? ("gpio") : ("unknown")), 
+                       i2c_childlist[i].addr, (0 == i) ? ("\n") : (" "));
        }
 
        return 0;
@@ -1605,13 +946,18 @@ ebus_done:
 #endif
 }
 
-
-#ifdef MODULE
-void cleanup_module(void)
+static void __exit envctrl_cleanup(void)
 {
-       envctrl_stop();
-       i2c_free_devices();
-       iounmap(i2c_regs);
+       int i;
+
+       iounmap(i2c);
        misc_deregister(&envctrl_dev);
+
+       for (i = 0; i < ENVCTRL_MAX_CPU * 2; i++) {
+               if (i2c_childlist[i].tables)
+                       kfree(i2c_childlist[i].tables);
+       }
 }
-#endif
+
+module_init(envctrl_init);
+module_exit(envctrl_cleanup);
index c41c858d085db9f9ef3c68654c6302bbd9374e5a..27f432e173fdc501eaeb5da8a8ac25594631363a 100644 (file)
@@ -58,9 +58,7 @@ nm256_release_ports (struct nm256_info *card)
 
     for (x = 0; x < 2; x++) {
        if (card->port[x].ptr != NULL) {
-           u32 size = 
-               card->port[x].end_offset - card->port[x].start_offset;
-           release_region ((unsigned long) card->port[x].ptr, size);
+           iounmap (card->port[x].ptr);
            card->port[x].ptr = NULL;
        }
     }
@@ -1025,7 +1023,7 @@ nm256_peek_for_sig (struct nm256_info *card)
                pointer);
     }
 
-    release_region ((unsigned long) temp, 16);
+    iounmap (temp);
 }
 
 /* 
index a58b333fd138e8925fe0ed48fcd390030dc8d11e..c527b6aec1fe0bd6909765b083ff1f2ecce9aadd 100644 (file)
  * http://gtf.org/garzik/drivers/via82cxxx/
  *
  */
 
-#define VIA_VERSION    "1.1.8"
+
+#define VIA_VERSION    "1.1.12"
 
 
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/fs.h>
+#include <linux/mm.h>
 #include <linux/pci.h>
 #include <linux/init.h>
 #include <linux/proc_fs.h>
 #include <linux/soundcard.h>
 #include <linux/ac97_codec.h>
 #include <linux/smp_lock.h>
+#include <linux/ioport.h>
+#include <linux/wrapper.h>
 #include <asm/io.h>
 #include <asm/delay.h>
 #include <asm/uaccess.h>
 #include <asm/hardirq.h>
+#include <asm/semaphore.h>
 
 
 #undef VIA_DEBUG       /* define to enable debugging output and checks */
@@ -45,7 +49,7 @@
 #define DPRINTK(fmt, args...)
 #endif
 
-#define VIA_NDEBUG     /* define to disable lightweight runtime checks */
+#undef VIA_NDEBUG      /* define to disable lightweight runtime checks */
 #ifdef VIA_NDEBUG
 #define assert(expr)
 #else
         }
 #endif
 
-/* user switch: undefine to exclude /proc data */
+#if defined(CONFIG_PROC_FS) && \
+    defined(CONFIG_SOUND_VIA82CXXX_PROCFS)
 #define VIA_PROC_FS 1
-
-/* don't mess with this */
-#ifndef CONFIG_PROC_FS
-#undef VIA_PROC_FS
 #endif
 
-#define arraysize(x)            (sizeof(x)/sizeof(*(x)))
+#define VIA_SUPPORT_MMAP 1 /* buggy, for now... */
 
 #define MAX_CARDS      1
 
-#define        LINE_SIZE       10
-
 #define VIA_CARD_NAME  "VIA 82Cxxx Audio driver " VIA_VERSION
 #define VIA_MODULE_NAME "via82cxxx"
 #define PFX            VIA_MODULE_NAME ": "
 #define VIA_DMA_BUFFERS                16
 #define VIA_DMA_BUF_SIZE       PAGE_SIZE
 
+#ifndef AC97_PCM_LR_ADC_RATE
+#  define AC97_PCM_LR_ADC_RATE AC97_PCM_LR_DAC_RATE
+#endif
+
 /* 82C686 function 5 (audio codec) PCI configuration registers */
 #define VIA_ACLINK_CTRL                0x41
 #define VIA_FUNC_ENABLE                0x42
 #define VIA_BASE0_AC97_CTRL            0x80
 #define VIA_BASE0_SGD_STATUS_SHADOW    0x84
 #define VIA_BASE0_GPI_INT_ENABLE       0x8C
+#define VIA_INTR_OUT                   ((1<<0) |  (1<<4) |  (1<<8))
+#define VIA_INTR_IN                    ((1<<1) |  (1<<5) |  (1<<9))
+#define VIA_INTR_FM                    ((1<<2) |  (1<<6) | (1<<10))
+#define VIA_INTR_MASK          (VIA_INTR_OUT | VIA_INTR_IN | VIA_INTR_FM)
 
 /* VIA_BASE0_AUDIO_xxx_CHAN_TYPE bits */
 #define VIA_IRQ_ON_FLAG                        (1<<0)  /* int on each flagged scatter block */
 #define VIA_INT_SEL_ONE_LINE_LEFT      (1<<3)  /* int at less than one line to send */
 #define VIA_PCM_FMT_STEREO             (1<<4)  /* PCM stereo format (bit clear == mono) */
 #define VIA_PCM_FMT_16BIT              (1<<5)  /* PCM 16-bit format (bit clear == 8-bit) */
-#define VIA_PCM_FIFO                   (1<<6)  /* enable FIFO?  documented as "reserved" */
+#define VIA_PCM_REC_FIFO               (1<<6)  /* PCM Recording FIFO */
 #define VIA_RESTART_SGD_ON_EOL         (1<<7)  /* restart scatter-gather at EOL */
 #define VIA_PCM_FMT_MASK               (VIA_PCM_FMT_STEREO|VIA_PCM_FMT_16BIT)
 #define VIA_CHAN_TYPE_MASK             (VIA_RESTART_SGD_ON_EOL | \
-                                        VIA_PCM_FIFO | \
                                         VIA_IRQ_ON_FLAG | \
                                         VIA_IRQ_ON_EOL)
 #define VIA_CHAN_TYPE_INT_SELECT       (VIA_INT_SEL_LAST_SAMPLE_SENT)
 #define VIA_CR80_WRITE_MODE            0
 #define VIA_CR80_REG_IDX(idx)          ((((idx) & 0xFF) >> 1) << 16)
 
-/* h r puff n stuff */
-#define VIA_FMT_STEREO 0x01
-#define VIA_FMT_16BIT  0x02
-#define VIA_FMT_MASK   0x03
-#define VIA_DAC_SHIFT  0   
-#define VIA_ADC_SHIFT  4
-
-/* undocumented(?) values for setting rate, from Via's source */
-#define VIA_SET_RATE_IN                0x00320000 /* set input rate */
-#define VIA_SET_RATE_OUT       0x002c0000 /* set output rate */
-
+/* capabilities we announce */
+#ifdef VIA_SUPPORT_MMAP
+#define VIA_DSP_CAP (DSP_CAP_REVISION | DSP_CAP_DUPLEX | DSP_CAP_MMAP | \
+                    DSP_CAP_TRIGGER | DSP_CAP_REALTIME)
+#else
+#define VIA_DSP_CAP (DSP_CAP_REVISION | DSP_CAP_DUPLEX | \
+                    DSP_CAP_TRIGGER | DSP_CAP_REALTIME)
+#endif
 
 /* scatter-gather DMA table entry, exactly as passed to hardware */
 struct via_sgd_table {
        u32 addr;
-       u32 count;      /* includes additional bits also */
+       u32 count;      /* includes additional VIA_xxx bits also */
 };
+
 #define VIA_EOL (1 << 31)
 #define VIA_FLAG (1 << 30)
+#define VIA_STOP (1 << 29)
 
 
 enum via_channel_states {
@@ -220,27 +225,36 @@ enum via_channel_states {
 
 struct via_sgd_data {
        dma_addr_t handle;
-       volatile void *cpuaddr;
+       void *cpuaddr;
 };
 
 
 struct via_channel {
-       unsigned rate;          /* sample rate */
+       atomic_t n_bufs;
+       atomic_t hw_ptr;
+       wait_queue_head_t wait;
 
+       unsigned int sw_ptr;
+       unsigned int slop_len;
+       unsigned int n_irqs;
+       int bytes;
+
+       unsigned is_active : 1;
+       unsigned is_record : 1;
+       unsigned is_mapped : 1;
+       unsigned is_enabled : 1;
        u8 pcm_fmt;             /* VIA_PCM_FMT_xxx */
-       
-       atomic_t state;
-       atomic_t buf_in_use;
-       atomic_t next_buf;
-       
+
+       unsigned rate;          /* sample rate */
+
        volatile struct via_sgd_table *sgtable;
        dma_addr_t sgt_handle;
-       
+
        struct via_sgd_data sgbuf [VIA_DMA_BUFFERS];
-       
-       wait_queue_head_t wait;
-       
+
        long iobase;
+
+       const char *name;
 };
 
 
@@ -248,20 +262,21 @@ struct via_channel {
 struct via_info {
        struct pci_dev *pdev;
        long baseaddr;
-       
+
        struct ac97_codec ac97;
        spinlock_t lock;
        int card_num;           /* unique card number, from 0 */
 
        int dev_dsp;            /* /dev/dsp index from register_sound_dsp() */
-       
+
        unsigned rev_h : 1;
 
-       wait_queue_head_t open_wait;
-       int open_mode;
-       
+       struct semaphore syscall_sem;
+       struct semaphore open_sem;
+
        struct via_channel ch_in;
        struct via_channel ch_out;
+       struct via_channel ch_fm;
 };
 
 
@@ -269,6 +284,7 @@ struct via_info {
 static unsigned via_num_cards = 0;
 
 
+
 /****************************************************************
  *
  * prototypes
@@ -285,6 +301,9 @@ static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wa
 static int via_dsp_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
 static int via_dsp_open (struct inode *inode, struct file *file);
 static int via_dsp_release(struct inode *inode, struct file *file);
+#ifdef VIA_SUPPORT_MMAP
+static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma);
+#endif
 
 static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg);
 static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value);
@@ -292,8 +311,19 @@ static u8 via_ac97_wait_idle (struct via_info *card);
 
 static void via_chan_free (struct via_info *card, struct via_channel *chan);
 static void via_chan_clear (struct via_channel *chan);
-static void via_chan_pcm_fmt (struct via_info *card,
-                             struct via_channel *chan, int reset);
+static void via_chan_pcm_fmt (struct via_channel *chan, int reset);
+
+#ifdef VIA_PROC_FS
+static int via_init_proc (void);
+static void via_cleanup_proc (void);
+static int via_card_init_proc (struct via_info *card);
+static void via_card_cleanup_proc (struct via_info *card);
+#else
+static inline int via_init_proc (void) { return 0; }
+static inline void via_cleanup_proc (void) {}
+static inline int via_card_init_proc (struct via_info *card) { return 0; }
+static inline void via_card_cleanup_proc (struct via_info *card) {}
+#endif
 
 
 /****************************************************************
@@ -338,7 +368,7 @@ static struct pci_driver via_driver = {
  *     one of three PCM channels supported by the chip.
  *
  */
+
 static inline void via_chan_stop (int iobase)
 {
        if (inb (iobase + VIA_PCM_STATUS) & VIA_SGD_ACTIVE)
@@ -359,11 +389,11 @@ static inline void via_chan_stop (int iobase)
  *     one of three PCM channels supported by the chip.
  *
  */
+
 static inline void via_chan_status_clear (int iobase)
 {
        u8 tmp = inb (iobase + VIA_PCM_STATUS);
-       
+
        if (tmp != 0)
                outb (tmp, iobase + VIA_PCM_STATUS);
 }
@@ -376,70 +406,43 @@ static inline void via_chan_status_clear (int iobase)
  *     Start scatter-gather DMA for the given channel.
  *
  */
+
 static inline void sg_begin (struct via_channel *chan)
 {
        outb (VIA_SGD_START, chan->iobase + VIA_PCM_CONTROL);
 }
 
 
-/**
- *     via_chan_bufs_in_use - Number of buffers waiting to be consumed
- *     @chan: Channel for which DMA buffers will be counted
- *
- *     Count the number of buffers waiting to be consumed.  For a
- *     playback operation, this is the number of buffers which have
- *     yet to be sent to the DAC.  For a recording operation, this
- *     is the number of buffers waiting to be consumed by software
- *     calling read() system call.
+/****************************************************************
  *
- */
-static inline int via_chan_bufs_in_use (struct via_channel *chan)
-{
-       return atomic_read(&chan->next_buf) -
-              atomic_read(&chan->buf_in_use);
-}
-
-
-/**
- *     via_chan_full - Check for no-free-buffers condition
- *     @chan: Channel for which DMA full condition will be checked
+ * Miscellaneous debris
  *
- *     Count the number of buffers waiting to be consumed, and return
- *     true (non-zero) if no buffers are available to be filled on the
- *     given DMA channel.
  *
  */
-static inline int via_chan_full (struct via_channel *chan)
-{
-       return (via_chan_bufs_in_use (chan) == VIA_DMA_BUFFERS);
-}
 
 
 /**
- *     via_chan_empty - Check for no-buffers-in-use condition
- *     @chan: Channel for which DMA empty condition will be checked
+ *     via_syscall_down - down the card-specific syscell semaphore
+ *     @card: Private info for specified board
+ *     @nonblock: boolean, non-zero if O_NONBLOCK is set
  *
- *     Count the number of buffers waiting to be consumed, and return
- *     true (non-zero) if no buffers are currently in use.
+ *     Encapsulates standard method of acquiring the syscall sem.
  *
+ *     Returns negative errno on error, or zero for success.
  */
-static inline int via_chan_empty (struct via_channel *chan)
-{
-       return (atomic_read(&chan->next_buf) ==
-               atomic_read(&chan->buf_in_use));
-}
 
+static inline int via_syscall_down (struct via_info *card, int nonblock)
+{
+       if (nonblock) {
+               if (down_trylock (&card->syscall_sem))
+                       return -EAGAIN;
+       } else {
+               if (down_interruptible (&card->syscall_sem))
+                       return -ERESTARTSYS;
+       }
 
-/****************************************************************
- *
- * Miscellaneous debris
- *
- *
- */
+       return 0;
+}
 
 
 /**
@@ -469,101 +472,115 @@ static void via_stop_everything (struct via_info *card)
        via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN);
        via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_IN_CHAN);
        via_chan_status_clear (card->baseaddr + VIA_BASE0_FM_OUT_CHAN);
-       
+
        /*
         * clear any enabled interrupt bits, reset to 8-bit mono PCM mode
         */
-       outb (0, card->baseaddr + VIA_BASE0_PCM_OUT_CHAN);
-       outb (0, card->baseaddr + VIA_BASE0_PCM_IN_CHAN);
-       outb (0, card->baseaddr + VIA_BASE0_FM_OUT_CHAN);
+       outb (0, card->baseaddr + VIA_BASE0_PCM_OUT_CHAN_TYPE);
+       outb (0, card->baseaddr + VIA_BASE0_PCM_IN_CHAN_TYPE);
+       outb (0, card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE);
        DPRINTK ("EXIT\n");
 }
 
 
 /**
  *     via_set_rate - Set PCM rate for given channel
- *     @card: Private info for specified board
+ *     @ac97: Pointer to generic codec info struct
+ *     @chan: Private info for specified channel
  *     @rate: Desired PCM sample rate, in Khz
- *     @inhale_deeply: Boolean.  If non-zero (true), the recording sample rate
- *                     is set.  If zero (false), the playback sample rate
- *                     is set.
  *
  *     Sets the PCM sample rate for a channel.
  *
  *     Values for @rate are clamped to a range of 4000 Khz through 48000 Khz,
  *     due to hardware constraints.
- *
- *     FIXME:  @inhale_deeply argument is ignored, and %AC97_PCM_FRONT_DAC_RATE
- *     is the only rate which is really set.  This needs to be fixed when
- *     recording support is added.
  */
 
-static int via_set_rate (struct via_info *card, unsigned rate, int inhale_deeply)
+static int via_set_rate (struct ac97_codec *ac97,
+                        struct via_channel *chan, unsigned rate)
 {
+       int rate_reg;
 
-       DPRINTK ("ENTER, rate = %d, inhale = %s\n",
-                rate, inhale_deeply ? "yes" : "no");
+       DPRINTK ("ENTER, rate = %d\n", rate);
 
        if (rate > 48000)               rate = 48000;
        if (rate < 4000)                rate = 4000;
-       
-       via_ac97_write_reg (&card->ac97, AC97_POWER_CONTROL,
-               (via_ac97_read_reg (&card->ac97, AC97_POWER_CONTROL) & ~0x0200) |
+
+       rate_reg = chan->is_record ? AC97_PCM_LR_ADC_RATE :
+                           AC97_PCM_FRONT_DAC_RATE;
+
+       via_ac97_write_reg (ac97, AC97_POWER_CONTROL,
+               (via_ac97_read_reg (ac97, AC97_POWER_CONTROL) & ~0x0200) |
                0x0200);
-       via_ac97_write_reg (&card->ac97, AC97_PCM_FRONT_DAC_RATE, rate);
-       via_ac97_write_reg (&card->ac97, AC97_POWER_CONTROL,
-               via_ac97_read_reg (&card->ac97, AC97_POWER_CONTROL) & ~0x0200);
 
-       DPRINTK ("EXIT, returning 0\n");
-       return rate;
+       via_ac97_write_reg (ac97, rate_reg, rate);
+
+       via_ac97_write_reg (ac97, AC97_POWER_CONTROL,
+               via_ac97_read_reg (ac97, AC97_POWER_CONTROL) & ~0x0200);
+
+       udelay (10);
+
+       /* the hardware might return a value different than what we
+        * passed to it, so read the rate value back from hardware
+        * to see what we came up with
+        */
+       chan->rate = via_ac97_read_reg (ac97, rate_reg);
+
+       DPRINTK ("EXIT, returning rate %d Hz\n", chan->rate);
+       return chan->rate;
 }
 
 
-/**
- *     via_set_adc_rate - Set PCM rate for recording channel
- *     @card: Private info for specified board
- *     @rate: Desired PCM sample rate, in Khz
+/****************************************************************
+ *
+ * Channel-specific operations
  *
- *     Sets the PCM sample rate for a recording channel.
  *
- *     FIXME:  @inhale_deeply argument to via_set_rate is ignored, and %AC97_PCM_FRONT_DAC_RATE
- *     is the only rate which is really set.  Thus, this function will
- *     not work until via_set_rate is fixed.
  */
 
-static inline int via_set_adc_rate (struct via_info *card, int rate)
-{
-       return via_set_rate (card, rate, 1);
-}
-
 
 /**
- *     via_set_dac_rate - Set PCM rate for playback channel
- *     @card: Private info for specified board
- *     @rate: Desired PCM sample rate, in Khz
+ *     via_chan_init_defaults - Initialize a struct via_channel
+ *     @card: Private audio chip info
+ *     @chan: Channel to be initialized
  *
- *     Sets the PCM sample rate for a playback channel.
+ *     Zero @chan, and then set all static defaults for the structure.
  */
 
-static inline int via_set_dac_rate (struct via_info *card, int rate)
+static void via_chan_init_defaults (struct via_info *card, struct via_channel *chan)
 {
-       return via_set_rate (card, rate, 0);
+       memset (chan, 0, sizeof (*chan));
+
+       if (chan == &card->ch_out) {
+               chan->name = "PCM-OUT";
+               chan->iobase = card->baseaddr + VIA_BASE0_PCM_OUT_CHAN;
+       } else if (chan == &card->ch_in) {
+               chan->name = "PCM-IN";
+               chan->iobase = card->baseaddr + VIA_BASE0_PCM_IN_CHAN;
+               chan->is_record = 1;
+       } else if (chan == &card->ch_fm) {
+               chan->name = "PCM-OUT-FM";
+               chan->iobase = card->baseaddr + VIA_BASE0_FM_OUT_CHAN;
+       } else {
+               BUG();
+       }
+
+       init_waitqueue_head (&chan->wait);
+
+       chan->pcm_fmt = VIA_PCM_FMT_MASK;
+       chan->is_enabled = 1;
+
+       if (chan->is_record)
+               atomic_set (&chan->n_bufs, 0);
+       else
+               atomic_set (&chan->n_bufs, VIA_DMA_BUFFERS);
+       atomic_set (&chan->hw_ptr, 0);
 }
 
 
-/****************************************************************
- *
- * Channel-specific operations
- *
- *
- */
 /**
  *     via_chan_init - Initialize PCM channel
  *     @card: Private audio chip info
  *     @chan: Channel to be initialized
- *     @chan_ofs: Offset from PCI address, which determines the
- *                set of SGD registers to use.
  *
  *     Performs all the preparations necessary to begin
  *     using a PCM channel.
@@ -579,15 +596,14 @@ static inline int via_set_dac_rate (struct via_info *card, int rate)
  *     always "take" the address.
  */
 
-static int via_chan_init (struct via_info *card, 
-                         struct via_channel *chan, long chan_ofs)
+static int via_chan_init (struct via_info *card, struct via_channel *chan)
 {
        int i;
-       unsigned long flags;
-       
+
        DPRINTK ("ENTER\n");
 
-       memset (chan, 0, sizeof (*chan));
+       /* bzero channel structure, and init members to defaults */
+       via_chan_init_defaults (card, chan);
 
        /* alloc DMA-able memory for scatter-gather table */
        chan->sgtable = pci_alloc_consistent (card->pdev,
@@ -598,8 +614,8 @@ static int via_chan_init (struct via_info *card,
                DPRINTK ("EXIT\n");
                return -ENOMEM;
        }
-       
-       memset ((void*)chan->sgtable, 0, 
+
+       memset ((void*)chan->sgtable, 0,
                (sizeof (struct via_sgd_table) * VIA_DMA_BUFFERS));
 
        /* alloc DMA-able memory for scatter-gather buffers */
@@ -628,33 +644,26 @@ static int via_chan_init (struct via_info *card,
                         virt_to_phys(chan->sgbuf[i].cpuaddr),
                         chan->sgbuf[i].cpuaddr);
 #endif
-       }
 
-       init_waitqueue_head (&chan->wait);
-
-       chan->pcm_fmt = VIA_PCM_FMT_MASK;
-       chan->iobase = card->baseaddr + chan_ofs;
-       
-       spin_lock_irqsave (&card->lock, flags);
+               assert ((VIA_DMA_BUF_SIZE % PAGE_SIZE) == 0);
+       }
 
        /* stop any existing channel output */
        via_chan_clear (chan);
        via_chan_status_clear (chan->iobase);
-       via_chan_pcm_fmt (card, chan, 1);
-
-       spin_unlock_irqrestore (&card->lock, flags);
+       via_chan_pcm_fmt (chan, 1);
 
        /* set location of DMA-able scatter-gather info table */
        DPRINTK("outl (0x%X, 0x%04lX)\n",
                cpu_to_le32 (chan->sgt_handle),
-               card->baseaddr + chan_ofs + VIA_PCM_TABLE_ADDR);
+               chan->iobase + VIA_PCM_TABLE_ADDR);
 
        via_ac97_wait_idle (card);
        outl (cpu_to_le32 (chan->sgt_handle),
-             card->baseaddr + chan_ofs + VIA_PCM_TABLE_ADDR);
+             chan->iobase + VIA_PCM_TABLE_ADDR);
        udelay (20);
        via_ac97_wait_idle (card);
-       
+
        DPRINTK("inl (0x%lX) = %x\n",
                chan->iobase + VIA_PCM_TABLE_ADDR,
                inl(chan->iobase + VIA_PCM_TABLE_ADDR));
@@ -683,24 +692,23 @@ err_out_nomem:
  *     back to a known state, and releasing any allocated
  *     sound buffers.
  */
+
 static void via_chan_free (struct via_info *card, struct via_channel *chan)
 {
        int i;
-       unsigned long flags;
-       
+
        DPRINTK ("ENTER\n");
-       
+
        synchronize_irq();
 
-       spin_lock_irqsave (&card->lock, flags);
+       spin_lock_irq (&card->lock);
 
        /* stop any existing channel output */
        via_chan_stop (chan->iobase);
        via_chan_status_clear (chan->iobase);
-       via_chan_pcm_fmt (card, chan, 1);
+       via_chan_pcm_fmt (chan, 1);
 
-       spin_unlock_irqrestore (&card->lock, flags);
+       spin_unlock_irq (&card->lock);
 
        /* zero location of DMA-able scatter-gather info table */
        via_ac97_wait_idle(card);
@@ -708,15 +716,16 @@ static void via_chan_free (struct via_info *card, struct via_channel *chan)
 
        for (i = 0; i < VIA_DMA_BUFFERS; i++)
                if (chan->sgbuf[i].cpuaddr) {
+                       assert ((VIA_DMA_BUF_SIZE % PAGE_SIZE) == 0);
                        pci_free_consistent (card->pdev, VIA_DMA_BUF_SIZE,
-                                            (void*)chan->sgbuf[i].cpuaddr,
+                                            chan->sgbuf[i].cpuaddr,
                                             chan->sgbuf[i].handle);
                        chan->sgbuf[i].cpuaddr = NULL;
                        chan->sgbuf[i].handle = 0;
                }
 
        if (chan->sgtable) {
-               pci_free_consistent (card->pdev, 
+               pci_free_consistent (card->pdev,
                        (sizeof (struct via_sgd_table) * VIA_DMA_BUFFERS),
                        (void*)chan->sgtable, chan->sgt_handle);
                chan->sgtable = NULL;
@@ -728,7 +737,6 @@ static void via_chan_free (struct via_info *card, struct via_channel *chan)
 
 /**
  *     via_chan_pcm_fmt - Update PCM channel settings
- *     @card: Private audio chip info
  *     @chan: Channel to be updated
  *     @reset: Boolean.  If non-zero, channel will be reset
  *             to 8-bit mono mode.
@@ -741,25 +749,26 @@ static void via_chan_free (struct via_info *card, struct via_channel *chan)
  *     is set to the values stored in the channel
  *     information struct @chan.
  */
-static void via_chan_pcm_fmt (struct via_info *card,
-                             struct via_channel *chan, int reset)
+
+static void via_chan_pcm_fmt (struct via_channel *chan, int reset)
 {
        DPRINTK ("ENTER, pcm_fmt=0x%02X, reset=%s\n",
                 chan->pcm_fmt, reset ? "yes" : "no");
 
-       assert (card != NULL);
        assert (chan != NULL);
 
        if (reset)
                /* reset to 8-bit mono mode */
                chan->pcm_fmt = 0;
-       
+
        /* enable interrupts on FLAG and EOL */
        chan->pcm_fmt |= VIA_CHAN_TYPE_MASK;
-       
+
+       /* if we are recording, enable recording fifo bit */
+       if (chan->is_record)
+               chan->pcm_fmt |= VIA_PCM_REC_FIFO;
        /* set interrupt select bits where applicable (PCM & FM out channels) */
-       if (chan == &card->ch_out)
+       if (!chan->is_record)
                chan->pcm_fmt |= VIA_CHAN_TYPE_INT_SELECT;
 
        outb (chan->pcm_fmt, chan->iobase + 2);
@@ -780,10 +789,20 @@ static void via_chan_pcm_fmt (struct via_info *card,
 
 static void via_chan_clear (struct via_channel *chan)
 {
+       DPRINTK ("ENTER\n");
        via_chan_stop (chan->iobase);
-       atomic_set (&chan->state, sgd_stopped);
-       atomic_set (&chan->buf_in_use, 0);
-       atomic_set (&chan->next_buf, 0);
+       chan->is_active = 0;
+       chan->is_mapped = 0;
+       chan->is_enabled = 1;
+       chan->slop_len = 0;
+       chan->sw_ptr = 0;
+       chan->n_irqs = 0;
+       atomic_set (&chan->hw_ptr, 0);
+       if (chan->is_record)
+               atomic_set (&chan->n_bufs, 0);
+       else
+               atomic_set (&chan->n_bufs, VIA_DMA_BUFFERS);
+       DPRINTK ("EXIT\n");
 }
 
 
@@ -809,8 +828,8 @@ static int via_chan_set_speed (struct via_info *card,
 
        via_chan_clear (chan);
 
-       val = via_set_rate (card, val, chan == &card->ch_in);   
-       
+       val = via_set_rate (&card->ac97, chan, val);
+
        DPRINTK ("EXIT, returning %d\n", val);
        return val;
 }
@@ -840,23 +859,30 @@ static int via_chan_set_fmt (struct via_info *card,
                 "unknown");
 
        via_chan_clear (chan);
-       
+
+       assert (val != AFMT_QUERY); /* this case is handled elsewhere */
+
        switch (val) {
-       case AFMT_U8:
-               chan->pcm_fmt &= ~VIA_PCM_FMT_16BIT;
-               via_chan_pcm_fmt (card, chan, 0);
-               break;
        case AFMT_S16_LE:
-               chan->pcm_fmt |= VIA_PCM_FMT_16BIT;
-               via_chan_pcm_fmt (card, chan, 0);
+               if ((chan->pcm_fmt & VIA_PCM_FMT_16BIT) == 0) {
+                       chan->pcm_fmt |= VIA_PCM_FMT_16BIT;
+                       via_chan_pcm_fmt (chan, 0);
+               }
                break;
-       default:
-               printk (KERN_WARNING PFX "unknown AFMT\n");
-               val = -EINVAL;
+
+       case AFMT_U8:
+               if (chan->pcm_fmt & VIA_PCM_FMT_16BIT) {
+                       chan->pcm_fmt &= ~VIA_PCM_FMT_16BIT;
+                       via_chan_pcm_fmt (chan, 0);
+               }
                break;
+
+       default:
+               DPRINTK ("unknown AFMT: 0x%X\n", val);
+               val = AFMT_S16_LE;
        }
-       
-       DPRINTK ("EXIT, returning %d\n", val);
+
+       DPRINTK ("EXIT\n");
        return val;
 }
 
@@ -882,19 +908,19 @@ static int via_chan_set_stereo (struct via_info *card,
        DPRINTK ("ENTER, channels = %d\n", val);
 
        via_chan_clear (chan);
-       
+
        switch (val) {
 
        /* mono */
        case 1:
                chan->pcm_fmt &= ~VIA_PCM_FMT_STEREO;
-               via_chan_pcm_fmt (card, chan, 0);
+               via_chan_pcm_fmt (chan, 0);
                break;
 
        /* stereo */
        case 2:
                chan->pcm_fmt |= VIA_PCM_FMT_STEREO;
-               via_chan_pcm_fmt (card, chan, 0);
+               via_chan_pcm_fmt (chan, 0);
                break;
 
        /* unknown */
@@ -903,13 +929,13 @@ static int via_chan_set_stereo (struct via_info *card,
                val = -EINVAL;
                break;
        }
-       
+
        DPRINTK ("EXIT, returning %d\n", val);
        return val;
 }
 
 
-#if 0
+#ifdef VIA_CHAN_DUMP_BUFS
 /**
  *     via_chan_dump_bufs - Display DMA table contents
  *     @chan: Channel whose DMA table will be displayed
@@ -921,7 +947,7 @@ static int via_chan_set_stereo (struct via_info *card,
 static void via_chan_dump_bufs (struct via_channel *chan)
 {
        int i;
-       
+
        for (i = 0; i < VIA_DMA_BUFFERS; i++) {
                DPRINTK ("#%02d: addr=%x, count=%u, flag=%d, eol=%d\n",
                         i, chan->sgtable[i].addr,
@@ -931,9 +957,55 @@ static void via_chan_dump_bufs (struct via_channel *chan)
        }
        DPRINTK ("buf_in_use = %d, nextbuf = %d\n",
                 atomic_read (&chan->buf_in_use),
-                atomic_read (&chan->next_buf));
+                atomic_read (&chan->sw_ptr));
+}
+#endif /* VIA_CHAN_DUMP_BUFS */
+
+
+/**
+ *     via_chan_flush_frag - Flush partially-full playback buffer to hardware
+ *     @chan: Channel whose DMA table will be displayed
+ *
+ *     Flushes partially-full playback buffer to hardware.
+ */
+
+static void via_chan_flush_frag (struct via_channel *chan)
+{
+       DPRINTK ("ENTER\n");
+
+       assert (chan->slop_len > 0);
+
+       if (chan->sw_ptr == (VIA_DMA_BUFFERS - 1))
+               chan->sw_ptr = 0;
+       else
+               chan->sw_ptr++;
+
+       chan->slop_len = 0;
+
+       assert (atomic_read (&chan->n_bufs) > 0);
+       atomic_dec (&chan->n_bufs);
+
+       DPRINTK ("EXIT\n");
+}
+
+
+
+/**
+ *     via_chan_maybe_start - Initiate audio hardware DMA operation
+ *     @chan: Channel whose DMA is to be started
+ *
+ *     Initiate DMA operation, if the DMA engine for the given
+ *     channel @chan is not already active.
+ */
+
+static inline void via_chan_maybe_start (struct via_channel *chan)
+{
+       if (!chan->is_active && chan->is_enabled) {
+               chan->is_active = 1;
+               sg_begin (chan);
+               DPRINTK("starting channel %s\n", chan->name);
+       }
 }
-#endif
 
 
 /****************************************************************
@@ -942,7 +1014,7 @@ static void via_chan_dump_bufs (struct via_channel *chan)
  *
  *
  */
+
 /**
  *     via_ac97_wait_idle - Wait until AC97 codec is not busy
  *     @card: Private info for specified board
@@ -956,23 +1028,20 @@ static u8 via_ac97_wait_idle (struct via_info *card)
 {
        u8 tmp8;
        int counter = VIA_COUNTER_LIMIT;
-       
+
        DPRINTK ("ENTER/EXIT\n");
 
        assert (card != NULL);
        assert (card->pdev != NULL);
-       
+
        do {
-               if (current->need_resched)
-                       schedule ();
-               else
-                       udelay (10);
+               udelay (15);
 
-               spin_lock_irq (&card->lock);
                tmp8 = inb (card->baseaddr + 0x83);
-               spin_unlock_irq (&card->lock);
        } while ((tmp8 & VIA_CR83_BUSY) && (counter-- > 0));
 
+       if (tmp8 & VIA_CR83_BUSY)
+               printk (KERN_WARNING PFX "timeout waiting on AC97 codec\n");
        return tmp8;
 }
 
@@ -994,10 +1063,10 @@ static u8 via_ac97_wait_idle (struct via_info *card)
 
 static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg)
 {
-       u32 data;
+       unsigned long data;
        struct via_info *card;
        int counter;
-       
+
        DPRINTK ("ENTER\n");
 
        assert (codec != NULL);
@@ -1013,26 +1082,25 @@ static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg)
        for (counter = VIA_COUNTER_LIMIT; counter > 0; counter--) {
                if (inl (card->baseaddr + 0x80) & VIA_CR80_VALID)
                        goto out;
-               udelay(10);
-               if (current->need_resched)
-                       schedule ();
+
+               udelay (15);
        }
 
-       printk (KERN_WARNING PFX "timeout while reading AC97 codec\n");
+       printk (KERN_WARNING PFX "timeout while reading AC97 codec (0x%lX)\n", data);
        goto err_out;
 
 out:
-       data = inl (card->baseaddr + 0x80);
+       data = (unsigned long) inl (card->baseaddr + 0x80);
        outb (0x02, card->baseaddr + 0x83);
 
        if (((data & 0x007F0000) >> 16) == reg) {
-               DPRINTK ("EXIT, success, data=0x%x, retval=0x%x\n",
+               DPRINTK ("EXIT, success, data=0x%lx, retval=0x%lx\n",
                         data, data & 0x0000FFFF);
                return data & 0x0000FFFF;
        }
 
-       DPRINTK ("WARNING: not our index: reg=0x%x, newreg=0x%x\n",
-                reg, ((data & 0x007F0000) >> 16));
+       printk (KERN_WARNING "via82cxxx_audio: not our index: reg=0x%x, newreg=0x%lx\n",
+               reg, ((data & 0x007F0000) >> 16));
 
 err_out:
        DPRINTK ("EXIT, returning 0\n");
@@ -1058,7 +1126,7 @@ static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value)
        u32 data;
        struct via_info *card;
        int counter;
-       
+
        DPRINTK ("ENTER\n");
 
        assert (codec != NULL);
@@ -1068,17 +1136,16 @@ static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value)
 
        data = (reg << 16) + value;
        outl (data, card->baseaddr + VIA_BASE0_AC97_CTRL);
-       udelay (20);
+       udelay (10);
 
        for (counter = VIA_COUNTER_LIMIT; counter > 0; counter--) {
                if ((inb (card->baseaddr + 0x83) & VIA_CR83_BUSY) == 0)
                        goto out;
-               udelay(10);
-               if (current->need_resched)
-                       schedule ();
+
+               udelay (15);
        }
-       
-       printk (KERN_WARNING PFX "timeout after AC97 codec write\n");
+
+       printk (KERN_WARNING PFX "timeout after AC97 codec write (0x%X, 0x%X)\n", reg, value);
 
 out:
        DPRINTK ("EXIT\n");
@@ -1091,15 +1158,15 @@ static int via_mixer_open (struct inode *inode, struct file *file)
        struct via_info *card;
        struct pci_dev *pdev;
        struct pci_driver *drvr;
-       
+
        DPRINTK ("ENTER\n");
 
        pci_for_each_dev(pdev) {
                drvr = pci_dev_driver (pdev);
                if (drvr == &via_driver) {
-                       assert (pdev->driver_data != NULL);
-                       
-                       card = pdev->driver_data;
+                       assert (pci_get_drvdata (pdev) != NULL);
+
+                       card = pci_get_drvdata (pdev);
                        if (card->ac97.dev_mixer == minor)
                                goto match;
                }
@@ -1119,12 +1186,25 @@ static int via_mixer_ioctl (struct inode *inode, struct file *file, unsigned int
                            unsigned long arg)
 {
        struct ac97_codec *codec = file->private_data;
+       struct via_info *card;
+       int nonblock = (file->f_flags & O_NONBLOCK);
        int rc;
 
+       DPRINTK ("ENTER\n");
+
        assert (codec != NULL);
-       
+       card = codec->private_data;
+       assert (card != NULL);
+
+       rc = via_syscall_down (card, nonblock);
+       if (rc) goto out;
+
        rc = codec->mixer_ioctl(codec, cmd, arg);
-       
+
+       up (&card->syscall_sem);
+
+out:
+       DPRINTK ("EXIT, returning %d\n", rc);
        return rc;
 }
 
@@ -1146,39 +1226,16 @@ static struct file_operations via_mixer_fops = {
 };
 
 
-#if 0 /* values reasoned from debugging dumps of via's driver */
-static struct {
-       u8 reg;
-       u16 data;
-} mixer_init_vals[] __devinitdata = {
-       { 0x2, 0x404 },
-       { 0x4, 0x404 },
-       { 0x6, 0x404 },
-       { 0x18, 0x404 },
-       { 0x10, 0x404 },
-       { 0x1a, 0x404 },
-       { 0x1c, 0x404 },
-       { 0x1a, 0x404 },
-       { 0x1c, 0xc0c },
-       { 0x12, 0x808 },
-       { 0x10, 0x808 },
-       { 0xe, 0x2 },
-       { 0x2, 0x808 },
-       { 0x18, 0x808 },
-};
-#endif
-
-
 static int __init via_ac97_reset (struct via_info *card)
 {
        struct pci_dev *pdev = card->pdev;
        u8 tmp8;
        u16 tmp16;
-       
+
        DPRINTK ("ENTER\n");
 
        assert (pdev != NULL);
-       
+
 #ifndef NDEBUG
        {
                u8 r40,r41,r42,r43,r44,r48;
@@ -1201,7 +1258,7 @@ static int __init via_ac97_reset (struct via_info *card)
                         inl (card->baseaddr + 0x80),
                         inl (card->baseaddr + 0x84));
                spin_unlock_irq (&card->lock);
-               
+
        }
 #endif
 
@@ -1212,10 +1269,10 @@ static int __init via_ac97_reset (struct via_info *card)
         pci_write_config_byte (pdev, VIA_ACLINK_CTRL, VIA_CR41_AC97_ENABLE |
                                VIA_CR41_AC97_RESET | VIA_CR41_AC97_WAKEUP);
         udelay (100);
+
         pci_write_config_byte (pdev, VIA_ACLINK_CTRL, 0);
         udelay (100);
+
         pci_write_config_byte (pdev, VIA_ACLINK_CTRL,
                               VIA_CR41_AC97_ENABLE | VIA_CR41_PCM_ENABLE |
                                VIA_CR41_VRA | VIA_CR41_AC97_RESET);
@@ -1230,45 +1287,50 @@ static int __init via_ac97_reset (struct via_info *card)
        /* route FM trap to IRQ, disable FM trap */
        pci_write_config_byte (pdev, 0x48, 0x05);
        udelay(10);
-       
+
        /* disable all codec GPI interrupts */
        outl (0, pci_resource_start (pdev, 0) + 0x8C);
 
-       /* enable variable rate */
+       /* WARNING: this line is magic.  Remove this
+        * and things break. */
+       /* enable variable rate, variable rate MIC ADC */
        tmp16 = via_ac97_read_reg (&card->ac97, 0x2A);
-       via_ac97_write_reg (&card->ac97, 0x2A, tmp16 | 0x01);
-       
-       /* boost headphone vol if disabled */
-       tmp16 = via_ac97_read_reg (&card->ac97, AC97_HEADPHONE_VOL);
-       if (tmp16 == 0)
-               via_ac97_write_reg (&card->ac97, AC97_HEADPHONE_VOL, 0x1F1F);
-       
+       via_ac97_write_reg (&card->ac97, 0x2A, tmp16 | (1<<0));
+
        pci_read_config_byte (pdev, VIA_ACLINK_CTRL, &tmp8);
        if ((tmp8 & (VIA_CR41_AC97_ENABLE | VIA_CR41_AC97_RESET)) == 0) {
                printk (KERN_ERR PFX "cannot enable AC97 controller, aborting\n");
                DPRINTK ("EXIT, tmp8=%X, returning -ENODEV\n", tmp8);
                return -ENODEV;
        }
-       
+
        DPRINTK ("EXIT, returning 0\n");
        return 0;
 }
 
 
+static void via_ac97_codec_wait (struct ac97_codec *codec)
+{
+       assert (codec->private_data != NULL);
+       via_ac97_wait_idle (codec->private_data);
+}
+
+
 static int __init via_ac97_init (struct via_info *card)
 {
        int rc;
        u16 tmp16;
-       
+
        DPRINTK ("ENTER\n");
 
        assert (card != NULL);
 
-       memset (&card->ac97, 0, sizeof (card->ac97));   
+       memset (&card->ac97, 0, sizeof (card->ac97));
        card->ac97.private_data = card;
        card->ac97.codec_read = via_ac97_read_reg;
        card->ac97.codec_write = via_ac97_write_reg;
-       
+       card->ac97.codec_wait = via_ac97_codec_wait;
+
        card->ac97.dev_mixer = register_sound_mixer (&via_mixer_fops, -1);
        if (card->ac97.dev_mixer < 0) {
                printk (KERN_ERR PFX "unable to register AC97 mixer, aborting\n");
@@ -1288,9 +1350,10 @@ static int __init via_ac97_init (struct via_info *card)
                goto err_out;
        }
 
+       /* enable variable rate, variable rate MIC ADC */
        tmp16 = via_ac97_read_reg (&card->ac97, 0x2A);
-       via_ac97_write_reg (&card->ac97, 0x2A, tmp16 | 0x01);
-       
+       via_ac97_write_reg (&card->ac97, 0x2A, tmp16 | (1<<0));
+
        DPRINTK ("EXIT, returning 0\n");
        return 0;
 
@@ -1304,10 +1367,10 @@ err_out:
 static void via_ac97_cleanup (struct via_info *card)
 {
        DPRINTK("ENTER\n");
-       
+
        assert (card != NULL);
        assert (card->ac97.dev_mixer >= 0);
-       
+
        unregister_sound_mixer (card->ac97.dev_mixer);
 
        DPRINTK("EXIT\n");
@@ -1320,68 +1383,130 @@ static void via_ac97_cleanup (struct via_info *card)
  * Interrupt-related code
  *
  */
-static inline void via_interrupt_write (struct via_channel *chan)
+
+/**
+ *     via_intr_channel - handle an interrupt for a single channel
+ *     @chan: handle interrupt for this channel
+ *
+ *     This is the "meat" of the interrupt handler,
+ *     containing the actions taken each time an interrupt
+ *     occurs.  All communication and coordination with
+ *     userspace takes place here.
+ *
+ *     Locking: inside card->lock
+ */
+
+static void via_intr_channel (struct via_channel *chan)
 {
-       assert (atomic_read(&chan->buf_in_use) <
-               atomic_read(&chan->next_buf));
+       u8 status;
+       int n;
 
-       /* XXX sanity check: read h/w counter to 
-        * ensure no lost frames */
+       /* check pertinent bits of status register for action bits */
+       status = inb (chan->iobase) & (VIA_SGD_FLAG | VIA_SGD_EOL | VIA_SGD_STOPPED);
+       if (!status)
+               return;
 
-       atomic_inc (&chan->buf_in_use);
+       /* acknowledge any flagged bits ASAP */
+       outb (status, chan->iobase);
 
-       /* if SG ptr catches up with userland ptr, stop playback */
-       if (atomic_read(&chan->buf_in_use) == atomic_read(&chan->next_buf)) {
-               atomic_set (&chan->state, sgd_stopped);
-               via_chan_stop (chan->iobase);
+       /* grab current h/w ptr value */
+       n = atomic_read (&chan->hw_ptr);
+
+       /* sanity check: make sure our h/w ptr doesn't have a weird value */
+       assert (n >= 0);
+       assert (n < VIA_DMA_BUFFERS);
+
+       /* reset SGD data structure in memory to reflect a full buffer,
+        * and advance the h/w ptr, wrapping around to zero if needed
+        */
+       if (n == (VIA_DMA_BUFFERS - 1)) {
+               chan->sgtable[n].count = (VIA_DMA_BUF_SIZE | VIA_EOL);
+               atomic_set (&chan->hw_ptr, 0);
+       } else {
+               chan->sgtable[n].count = (VIA_DMA_BUF_SIZE | VIA_FLAG);
+               atomic_inc (&chan->hw_ptr);
        }
 
-       /* wake up anybody listening */
+       /* accounting crap for SNDCTL_DSP_GETxPTR */
+       chan->n_irqs++;
+       chan->bytes += VIA_DMA_BUF_SIZE;
+       if (chan->bytes < 0) /* handle overflow of 31-bit value */
+               chan->bytes = VIA_DMA_BUF_SIZE;
+
+       /* wake up anyone listening to see when interrupts occur */
        if (waitqueue_active (&chan->wait))
-               wake_up (&chan->wait);
-}
+               wake_up_all (&chan->wait);
+
+       DPRINTK ("%s intr, status=0x%02X, hwptr=0x%lX, chan->hw_ptr=%d\n",
+                chan->name, status, (long) inl (chan->iobase + 0x04),
+                atomic_read (&chan->hw_ptr));
+
+       /* all following checks only occur when not in mmap(2) mode */
+       if (chan->is_mapped)
+               return;
+
+       /* If we are recording, then n_bufs represents the number
+        * of buffers waiting to be handled by userspace.
+        * If we are playback, then n_bufs represents the number
+        * of buffers remaining to be filled by userspace.
+        * We increment here.  If we reach max buffers (VIA_DMA_BUFFERS),
+        * this indicates an underrun/overrun.  For this case under OSS,
+        * we stop the record/playback process.
+        */
+       if (atomic_read (&chan->n_bufs) < VIA_DMA_BUFFERS)
+               atomic_inc (&chan->n_bufs);
+       assert (atomic_read (&chan->n_bufs) <= VIA_DMA_BUFFERS);
+
+       if (atomic_read (&chan->n_bufs) == VIA_DMA_BUFFERS) {
+               chan->is_active = 0;
+               via_chan_stop (chan->iobase);
+       }
 
+       DPRINTK ("%s intr, channel n_bufs == %d\n", chan->name,
+                atomic_read (&chan->n_bufs));
+}
 
 
 static void via_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
        struct via_info *card = dev_id;
-       struct via_channel *chan;
-       u8 status;
+       u32 status32;
 
-       status = inb (card->baseaddr + 0x00);
-       if (status) {
-               assert (card->open_mode & FMODE_WRITE);
-               
-               chan = &card->ch_out;
-               
-               if (status & VIA_SGD_FLAG) {
-                       assert ((status & VIA_SGD_EOL) == 0);
-                       outb (VIA_SGD_FLAG, chan->iobase + 0x00);
-                       DPRINTK("FLAG intr, status=0x%02X\n", status);
-                       via_interrupt_write (chan);
-               }
-               
-               if (status & VIA_SGD_EOL) {
-                       assert ((status & VIA_SGD_FLAG) == 0);
-                       outb (VIA_SGD_EOL, chan->iobase + 0x00);
-                       DPRINTK("EOL intr, status=0x%02X\n", status);
-                       via_interrupt_write (chan);
-               }
-               
-               if (status & VIA_SGD_STOPPED) {
-                       outb (VIA_SGD_STOPPED, chan->iobase + 0x00);
-                       DPRINTK("STOPPED intr, status=0x%02X\n", status);
-               }
+       /* to minimize interrupt sharing costs, we use the SGD status
+        * shadow register to check the status of all inputs and
+        * outputs with a single 32-bit bus read.  If no interrupt
+        * conditions are flagged, we exit immediately
+        */
+       status32 = inl (card->baseaddr + VIA_BASE0_SGD_STATUS_SHADOW);
+       if (!(status32 & VIA_INTR_MASK))
+               return;
 
-#if 0
-               via_chan_dump_bufs (&card->ch_out);
-#endif
-       }
+       DPRINTK ("intr, status32 == 0x%08X\n", status32);
+
+       /* synchronize interrupt handling under SMP.  this spinlock
+        * goes away completely on UP
+        */
+       spin_lock (&card->lock);
+
+       if (status32 & VIA_INTR_OUT)
+               via_intr_channel (&card->ch_out);
+       if (status32 & VIA_INTR_IN)
+               via_intr_channel (&card->ch_in);
+       if (status32 & VIA_INTR_FM)
+               via_intr_channel (&card->ch_fm);
+
+       spin_unlock (&card->lock);
 }
 
 
+/**
+ *     via_interrupt_disable - Disable all interrupt-generating sources
+ *     @card: Private info for specified board
+ *
+ *     Disables all interrupt-generation flags in the Via
+ *     audio hardware registers.
+ */
+
 static void via_interrupt_disable (struct via_info *card)
 {
        u8 tmp8;
@@ -1390,7 +1515,7 @@ static void via_interrupt_disable (struct via_info *card)
        DPRINTK ("ENTER\n");
 
        assert (card != NULL);
-       
+
        spin_lock_irqsave (&card->lock, flags);
 
        pci_read_config_byte (card->pdev, VIA_FM_NMI_CTRL, &tmp8);
@@ -1415,13 +1540,22 @@ static void via_interrupt_disable (struct via_info *card)
 }
 
 
+/**
+ *     via_interrupt_init - Initialize interrupt handling
+ *     @card: Private info for specified board
+ *
+ *     Obtain and reserve IRQ for using in handling audio events.
+ *     Also, disable any IRQ-generating resources, to make sure
+ *     we don't get interrupts before we want them.
+ */
+
 static int via_interrupt_init (struct via_info *card)
 {
        DPRINTK ("ENTER\n");
 
        assert (card != NULL);
        assert (card->pdev != NULL);
-       
+
        /* check for sane IRQ number. can this ever happen? */
        if (card->pdev->irq < 2) {
                printk (KERN_ERR PFX "insane IRQ %d, aborting\n",
@@ -1429,7 +1563,7 @@ static int via_interrupt_init (struct via_info *card)
                DPRINTK ("EXIT, returning -EIO\n");
                return -EIO;
        }
-       
+
        if (request_irq (card->pdev->irq, via_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) {
                printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n",
                        card->pdev->irq);
@@ -1445,6 +1579,15 @@ static int via_interrupt_init (struct via_info *card)
 }
 
 
+/**
+ *     via_interrupt_cleanup - Shutdown driver interrupt handling
+ *     @card: Private info for specified board
+ *
+ *     Disable any potential interrupt sources in the Via audio
+ *     hardware, and then release (un-reserve) the IRQ line
+ *     in the kernel core.
+ */
+
 static void via_interrupt_cleanup (struct via_info *card)
 {
        DPRINTK ("ENTER\n");
@@ -1465,7 +1608,7 @@ static void via_interrupt_cleanup (struct via_info *card)
  * OSS DSP device
  *
  */
+
 static struct file_operations via_dsp_fops = {
        owner:          THIS_MODULE,
        open:           via_dsp_open,
@@ -1475,22 +1618,28 @@ static struct file_operations via_dsp_fops = {
        poll:           via_dsp_poll,
        llseek:         via_llseek,
        ioctl:          via_dsp_ioctl,
+#ifdef VIA_SUPPORT_MMAP
+       mmap:           via_dsp_mmap,
+#endif
 };
 
 
 static int __init via_dsp_init (struct via_info *card)
 {
        u8 tmp8;
-       
+
        DPRINTK ("ENTER\n");
 
        assert (card != NULL);
 
        /* turn off legacy features, if not already */
        pci_read_config_byte (card->pdev, VIA_FUNC_ENABLE, &tmp8);
-       tmp8 &= ~(VIA_CR42_SB_ENABLE | VIA_CR42_MIDI_ENABLE |
-                 VIA_CR42_FM_ENABLE);
-       pci_write_config_byte (card->pdev, VIA_FUNC_ENABLE, tmp8);
+       if (tmp8 & (VIA_CR42_SB_ENABLE | VIA_CR42_MIDI_ENABLE |
+                   VIA_CR42_FM_ENABLE)) {
+               tmp8 &= ~(VIA_CR42_SB_ENABLE | VIA_CR42_MIDI_ENABLE |
+                         VIA_CR42_FM_ENABLE);
+               pci_write_config_byte (card->pdev, VIA_FUNC_ENABLE, tmp8);
+       }
 
        via_stop_everything (card);
 
@@ -1519,232 +1668,404 @@ static void via_dsp_cleanup (struct via_info *card)
 }
 
 
+#ifdef VIA_SUPPORT_MMAP
+static struct page * via_mm_nopage (struct vm_area_struct * vma,
+                                   unsigned long address, int write_access)
+{
+       struct via_info *card = vma->vm_private_data;
+       struct via_channel *chan = &card->ch_out;
+       struct page *dmapage;
+       unsigned long pgoff;
+       int rd, wr;
+
+       DPRINTK ("ENTER, start %lXh, ofs %lXh, pgoff %ld, addr %lXh, wr %d\n",
+                vma->vm_start,
+                address - vma->vm_start,
+                (address - vma->vm_start) >> PAGE_SHIFT,
+                address,
+                write_access);
+
+       assert (VIA_DMA_BUF_SIZE == PAGE_SIZE);
+
+        if (address > vma->vm_end) {
+               DPRINTK ("EXIT, returning NOPAGE_SIGBUS\n");
+               return NOPAGE_SIGBUS; /* Disallow mremap */
+       }
+        if (!card) {
+               DPRINTK ("EXIT, returning NOPAGE_OOM\n");
+               return NOPAGE_OOM;      /* Nothing allocated */
+       }
+
+       pgoff = vma->vm_pgoff + (address - vma->vm_start) >> PAGE_SHIFT;
+       rd = card->ch_in.is_mapped;
+       wr = card->ch_out.is_mapped;
+
+#ifndef VIA_NDEBUG
+       {
+       unsigned long max_bufs = VIA_DMA_BUFFERS;
+       if (rd && wr) max_bufs *= 2;
+       /* via_dsp_mmap() should ensure this */
+       assert (pgoff < max_bufs);
+       }
+#endif
+
+       /* if full-duplex (read+write) and we have two sets of bufs,
+        * then the playback buffers come first, sez soundcard.c */
+       if (pgoff >= VIA_DMA_BUFFERS) {
+               pgoff -= VIA_DMA_BUFFERS;
+               chan = &card->ch_in;
+       } else if (!wr)
+               chan = &card->ch_in;
+
+       assert ((((unsigned long)chan->sgbuf[pgoff].cpuaddr) % PAGE_SIZE) == 0);
+
+       dmapage = virt_to_page (chan->sgbuf[pgoff].cpuaddr);
+       DPRINTK ("EXIT, returning page %p for cpuaddr %lXh\n",
+                dmapage, (unsigned long) chan->sgbuf[pgoff].cpuaddr);
+       get_page (dmapage);
+       return dmapage;
+}
+
+
+static int via_mm_swapout (struct page *page, struct file *filp)
+{
+       return 0;
+}
+
+
+struct vm_operations_struct via_mm_ops = {
+       nopage:         via_mm_nopage,
+       swapout:        via_mm_swapout,
+};
+
+
+static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct via_info *card;
+       int nonblock = (file->f_flags & O_NONBLOCK);
+       int rc = -EINVAL, rd=0, wr=0;
+       unsigned long max_size, size, start, offset;
+
+       assert (file != NULL);
+       assert (vma != NULL);
+       card = file->private_data;
+       assert (card != NULL);
+
+       DPRINTK ("ENTER, start %lXh, size %ld, pgoff %ld\n",
+                vma->vm_start,
+                vma->vm_end - vma->vm_start,
+                vma->vm_pgoff);
+
+       assert (VIA_DMA_BUF_SIZE == PAGE_SIZE);
+
+       max_size = 0;
+       if (file->f_mode & FMODE_READ) {
+               rd = 1;
+               max_size += (VIA_DMA_BUFFERS * VIA_DMA_BUF_SIZE);
+       }
+       if (file->f_mode & FMODE_WRITE) {
+               wr = 1;
+               max_size += (VIA_DMA_BUFFERS * VIA_DMA_BUF_SIZE);
+       }
+
+       start = vma->vm_start;
+       offset = (vma->vm_pgoff << PAGE_SHIFT);
+       size = vma->vm_end - vma->vm_start;
+
+       /* some basic size/offset sanity checks */
+       if (size > max_size)
+               goto out;
+       if (offset > max_size - size)
+               goto out;
+
+       rc = via_syscall_down (card, nonblock);
+       if (rc) goto out;
+
+       vma->vm_ops = &via_mm_ops;
+       vma->vm_private_data = card;
+
+       if (rd)
+               card->ch_in.is_mapped = 1;
+       if (wr)
+               card->ch_out.is_mapped = 1;
+
+       up (&card->syscall_sem);
+       rc = 0;
+
+out:
+       DPRINTK("EXIT, returning %d\n", rc);
+       return rc;
+}
+#endif /* VIA_SUPPORT_MMAP */
+
+
+static ssize_t via_dsp_do_read (struct via_info *card,
+                               char *userbuf, size_t count,
+                               int nonblock)
+{
+       const char *orig_userbuf = userbuf;
+       struct via_channel *chan = &card->ch_in;
+       size_t size;
+       int n, tmp;
+
+       /* if SGD has not yet been started, start it */
+       via_chan_maybe_start (chan);
+
+handle_one_block:
+       /* just to be a nice neighbor */
+       if (current->need_resched)
+               schedule ();
+
+       /* grab current channel software pointer.  In the case of
+        * recording, this is pointing to the next buffer that
+        * will receive data from the audio hardware.
+        */
+       n = chan->sw_ptr;
+
+       /* n_bufs represents the number of buffers waiting
+        * to be copied to userland.  sleep until at least
+        * one buffer has been read from the audio hardware.
+        */
+       tmp = atomic_read (&chan->n_bufs);
+       assert (tmp >= 0);
+       assert (tmp <= VIA_DMA_BUFFERS);
+       while (tmp == 0) {
+               if (nonblock || !chan->is_active)
+                       return -EAGAIN;
+
+               DPRINTK ("Sleeping on block %d\n", n);
+               interruptible_sleep_on (&chan->wait);
+
+               if (signal_pending (current))
+                       return -ERESTARTSYS;
+
+               tmp = atomic_read (&chan->n_bufs);
+       }
+
+       /* Now that we have a buffer we can read from, send
+        * as much as sample data possible to userspace.
+        */
+       while ((count > 0) && (chan->slop_len < VIA_DMA_BUF_SIZE)) {
+               size_t slop_left = VIA_DMA_BUF_SIZE - chan->slop_len;
+
+               size = (count < slop_left) ? count : slop_left;
+               if (copy_to_user (userbuf,
+                                 chan->sgbuf[n].cpuaddr + chan->slop_len,
+                                 size))
+                       return -EFAULT;
+
+               count -= size;
+               chan->slop_len += size;
+               userbuf += size;
+       }
+
+       /* If we didn't copy the buffer completely to userspace,
+        * stop now.
+        */
+       if (chan->slop_len < VIA_DMA_BUF_SIZE)
+               goto out;
+
+       /*
+        * If we get to this point, we copied one buffer completely
+        * to userspace, give the buffer back to the hardware.
+        */
+
+       /* advance channel software pointer to point to
+        * the next buffer from which we will copy
+        */
+       if (chan->sw_ptr == (VIA_DMA_BUFFERS - 1))
+               chan->sw_ptr = 0;
+       else
+               chan->sw_ptr++;
+
+       /* mark one less buffer waiting to be processed */
+       assert (atomic_read (&chan->n_bufs) > 0);
+       atomic_dec (&chan->n_bufs);
+
+       /* we are at a block boundary, there is no fragment data */
+       chan->slop_len = 0;
+
+       DPRINTK("Flushed block %u, sw_ptr now %u, n_bufs now %d\n",
+               n, chan->sw_ptr, atomic_read (&chan->n_bufs));
+
+       DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
+                inb (card->baseaddr + 0x00),
+                inb (card->baseaddr + 0x01),
+                inb (card->baseaddr + 0x02),
+                inl (card->baseaddr + 0x04),
+                inl (card->baseaddr + 0x0C),
+                inl (card->baseaddr + 0x80),
+                inl (card->baseaddr + 0x84));
+
+       if (count > 0)
+               goto handle_one_block;
+
+out:
+       return userbuf - orig_userbuf;
+}
+
+
 static ssize_t via_dsp_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
        struct via_info *card;
+       int nonblock = (file->f_flags & O_NONBLOCK);
+       int rc;
+
+       DPRINTK ("ENTER, file=%p, buffer=%p, count=%u, ppos=%lu\n",
+                file, buffer, count, ppos ? ((unsigned long)*ppos) : 0);
 
-       DPRINTK ("ENTER\n");
-       
        assert (file != NULL);
        assert (buffer != NULL);
        card = file->private_data;
        assert (card != NULL);
 
-       DPRINTK("EXIT, returning -EINVAL\n");
-       return -EINVAL;
-}
+       if (ppos != &file->f_pos) {
+               DPRINTK ("EXIT, returning -ESPIPE\n");
+               return -ESPIPE;
+       }
 
+       rc = via_syscall_down (card, nonblock);
+       if (rc) goto out;
+
+       if (card->ch_in.is_mapped) {
+               rc = -ENXIO;
+               goto out_up;
+       }
+
+       rc = via_dsp_do_read (card, buffer, count, nonblock);
+
+out_up:
+       up (&card->syscall_sem);
+out:
+       DPRINTK("EXIT, returning %ld\n",(long) rc);
+       return rc;
+}
 
 
-#define sgcount(n) (sgtable[(n)].count & 0x00FFFFFF)
-#define NEXTBUF (atomic_read(&chan->next_buf) % VIA_DMA_BUFFERS)
-#define BUF_IN_USE (atomic_read(&chan->buf_in_use) % VIA_DMA_BUFFERS)
-#define STATE_STOPPED (atomic_read (state) == sgd_stopped)
-#define STATE_STARTED (atomic_read (state) == sgd_in_progress)
 static ssize_t via_dsp_do_write (struct via_info *card,
                                 const char *userbuf, size_t count,
-                                int non_blocking)
+                                int nonblock)
 {
        const char *orig_userbuf = userbuf;
        struct via_channel *chan = &card->ch_out;
        volatile struct via_sgd_table *sgtable = chan->sgtable;
-       atomic_t *state = &chan->state;
        size_t size;
-       int nextbuf, prevbuf, n, realcount;
-       ssize_t rc;
-       
-       while (count > 0) {
-               if (current->need_resched)
-                       schedule ();
-
-               spin_lock_irq (&card->lock);
-               DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
-                        inb (card->baseaddr + 0x00),
-                        inb (card->baseaddr + 0x01),
-                        inb (card->baseaddr + 0x02),
-                        inl (card->baseaddr + 0x04),
-                        inl (card->baseaddr + 0x0C),
-                        inl (card->baseaddr + 0x80),
-                        inl (card->baseaddr + 0x84));
-               spin_unlock_irq (&card->lock);
-               
-               size = (count < VIA_DMA_BUF_SIZE) ? count : VIA_DMA_BUF_SIZE;
+       int n, tmp;
 
-               /* case 1: SGD not active, list is ours for the mangling */
+handle_one_block:
+       /* just to be a nice neighbor */
+       if (current->need_resched)
+               schedule ();
 
-               if (STATE_STOPPED) {
-                       DPRINTK ("case 1\n");
-
-                       if (copy_from_user ((void*)chan->sgbuf[0].cpuaddr,
-                                            userbuf, size))
-                               return -EFAULT;
+       /* grab current channel software pointer.  In the case of
+        * playback, this is pointing to the next buffer that
+        * should receive data from userland.
+        */
+       n = chan->sw_ptr;
 
-                       assert (sgtable[0].addr == cpu_to_le32 (chan->sgbuf[0].handle));
-                       sgtable[0].count = size | VIA_FLAG;
+       /* n_bufs represents the number of buffers remaining
+        * to be filled by userspace.  Sleep until
+        * at least one buffer is available for our use.
+        */
+       tmp = atomic_read (&chan->n_bufs);
+       assert (tmp >= 0);
+       assert (tmp <= VIA_DMA_BUFFERS);
+       while (tmp == 0) {
+               if (nonblock || !chan->is_enabled)
+                       return -EAGAIN;
 
-                       atomic_set (state, sgd_in_progress);
-                       atomic_set (&chan->buf_in_use, 0);
-                       atomic_set (&chan->next_buf, 1);
-                       
-                       count -= size;
-                       userbuf += size;
+               DPRINTK ("Sleeping on block %d, tmp==%d, ir==%d\n", n, tmp, chan->is_record);
+               interruptible_sleep_on (&chan->wait);
 
-                       spin_lock_irq (&card->lock);
-                       sg_begin (chan);
-                       spin_unlock_irq (&card->lock);
+               if (signal_pending (current))
+                       return -ERESTARTSYS;
 
-                       continue;
-               }
+               tmp = atomic_read (&chan->n_bufs);
+       }
 
-               nextbuf = NEXTBUF;
-               if (nextbuf)
-                       prevbuf = nextbuf - 1;
-               else
-                       prevbuf = VIA_DMA_BUFFERS - 1;
-
-               /* case 2: if final buffer is (a) a fragment, and (b) not
-                * currently being consumed by the SGD engine, then append
-                * as much data as possible to the fragment. */
-
-               realcount = sgcount(prevbuf);
-               if (STATE_STARTED && (prevbuf != BUF_IN_USE) &&
-                   (realcount < VIA_DMA_BUF_SIZE)) {
-                       DPRINTK ("case 2\n");
-                       DPRINTK ("st=%d, fb=%d -- nb=%d, pb=%d, n=%d, rc=%d\n",
-                                atomic_read (state),
-                                BUF_IN_USE,
-                                nextbuf,
-                                prevbuf,
-                                prevbuf /* n */,
-                                realcount);
-
-                       n = prevbuf;
-
-                       if ((VIA_DMA_BUF_SIZE - realcount) < size)
-                               size = VIA_DMA_BUF_SIZE - realcount;
-
-                       if (copy_from_user ((void*)(chan->sgbuf[n].cpuaddr +
-                                            realcount),
-                                            userbuf, size))
-                               return -EFAULT;
-
-                       /* slack way to try and prevent races */
-                       if (prevbuf == BUF_IN_USE || !STATE_STARTED)
-                               continue;
-
-                       assert (sgtable[n].addr == cpu_to_le32 (chan->sgbuf[n].handle));
-                       if (n == (VIA_DMA_BUFFERS - 1))
-                               sgtable[n].count = (realcount + size) | VIA_EOL;
-                       else
-                               sgtable[n].count = (realcount + size) | VIA_FLAG;
+       /* Now that we have a buffer we can write to, fill it up
+        * as much as possible with data from userspace.
+        */
+       while ((count > 0) && (chan->slop_len < VIA_DMA_BUF_SIZE)) {
+               size_t slop_left = VIA_DMA_BUF_SIZE - chan->slop_len;
 
-                       count -= size;
-                       userbuf += size;
-                       continue;
-               }
+               size = (count < slop_left) ? count : slop_left;
+               if (copy_from_user (chan->sgbuf[n].cpuaddr + chan->slop_len,
+                                   userbuf, size))
+                       return -EFAULT;
 
-               /* case 3: if there are buffers left, use one
-                * XXX needs more review for possible races */
+               count -= size;
+               chan->slop_len += size;
+               userbuf += size;
+       }
 
-               else if (STATE_STARTED && !via_chan_full (chan)) {
-                       DPRINTK ("case 3\n");
-                       DPRINTK ("st=%d, fb=%d -- nb=%d, pb=%d, n=%d\n",
-                                atomic_read (state),
-                                BUF_IN_USE,
-                                nextbuf,
-                                prevbuf,
-                                nextbuf /* n */);
+       /* If we didn't fill up the buffer with data, stop now.
+        * Put a 'stop' marker in the DMA table too, to tell the
+        * audio hardware to stop if it gets here.
+        */
+       if (chan->slop_len < VIA_DMA_BUF_SIZE) {
+               sgtable[n].count = cpu_to_le32 (chan->slop_len | VIA_EOL | VIA_STOP);
+               goto out;
+       }
 
-                       n = nextbuf;
+       /*
+        * If we get to this point, we have filled a buffer with
+        * audio data, flush the buffer to audio hardware.
+        */
 
-                       if (copy_from_user ((void*)chan->sgbuf[n].cpuaddr,
-                                            userbuf, size))
-                               return -EFAULT;
+       /* Record the true size for the audio hardware to notice */
+       if (n == (VIA_DMA_BUFFERS - 1))
+               sgtable[n].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_EOL);
+       else
+               sgtable[n].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_FLAG);
 
-                       if (n == (VIA_DMA_BUFFERS - 1))
-                               sgtable[n].count = size | VIA_EOL;
-                       else
-                               sgtable[n].count = size | VIA_FLAG;
+       /* advance channel software pointer to point to
+        * the next buffer we will fill with data
+        */
+       if (chan->sw_ptr == (VIA_DMA_BUFFERS - 1))
+               chan->sw_ptr = 0;
+       else
+               chan->sw_ptr++;
 
-                       /* if SGD stopped during data copy or SG table update,
-                        * then loop back to the beginning without updating
-                        * any pointers.
-                        * ie. slack way to prevent race */
-                       if (!STATE_STARTED)
-                               continue;
+       /* mark one less buffer as being available for userspace consumption */
+       assert (atomic_read (&chan->n_bufs) > 0);
+       atomic_dec (&chan->n_bufs);
 
-                       atomic_inc (&chan->next_buf);
+       /* we are at a block boundary, there is no fragment data */
+       chan->slop_len = 0;
 
-                       count -= size;
-                       userbuf += size;
-                       continue;
-               }
+       /* if SGD has not yet been started, start it */
+       via_chan_maybe_start (chan);
 
-               /* case 4, final SGT active case: no free buffers, wait for one */
-
-               else {
-                       DPRINTK ("case 4\n");
-                       DPRINTK ("st=%d, fb=%d -- nb=%d, pb=%d\n",
-                                atomic_read (state),
-                                BUF_IN_USE,
-                                nextbuf,
-                                prevbuf);
-
-                       /* if playback stopped, no need to sleep */
-                       if (!STATE_STARTED)
-                               continue;
-                       
-                       /* if buffer free, no need to sleep */
-                       if (!via_chan_full (chan))
-                               continue;
-                       
-                       if (non_blocking) {
-                               rc = userbuf - orig_userbuf;
-                               if (rc == 0)
-                                       rc = -EAGAIN;
-                               return rc;
-                       }
+       DPRINTK("Flushed block %u, sw_ptr now %u, n_bufs now %d\n",
+               n, chan->sw_ptr, atomic_read (&chan->n_bufs));
 
-                       DPRINTK ("sleeping\n");
-                       interruptible_sleep_on (&chan->wait);
-                       if (signal_pending (current))
-                               return -ERESTARTSYS;
-               }
-       }
+       DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
+                inb (card->baseaddr + 0x00),
+                inb (card->baseaddr + 0x01),
+                inb (card->baseaddr + 0x02),
+                inl (card->baseaddr + 0x04),
+                inl (card->baseaddr + 0x0C),
+                inl (card->baseaddr + 0x80),
+                inl (card->baseaddr + 0x84));
 
-#if 0
-       {
-               u8 r40,r41,r42,r43,r44,r48;
-               pci_read_config_byte (card->pdev, 0x40, &r40);
-               pci_read_config_byte (card->pdev, 0x41, &r41);
-               pci_read_config_byte (card->pdev, 0x42, &r42);
-               pci_read_config_byte (card->pdev, 0x43, &r43);
-               pci_read_config_byte (card->pdev, 0x44, &r44);
-               pci_read_config_byte (card->pdev, 0x48, &r48);
-               DPRINTK("PCI config: %02X %02X %02X %02X %02X %02X\n",
-                       r40,r41,r42,r43,r44,r48);
-       }
-#endif
+       if (count > 0)
+               goto handle_one_block;
 
-       DPRINTK ("EXIT, returning %d\n",
-                userbuf - orig_userbuf);
+out:
        return userbuf - orig_userbuf;
 }
-#undef sgcount
-#undef NEXTBUF
-#undef BUF_IN_USE
-#undef STATE_STOPPED
-#undef STATE_STARTED
 
 
 static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
        struct via_info *card;
        ssize_t rc;
+       int nonblock = (file->f_flags & O_NONBLOCK);
 
        DPRINTK ("ENTER, file=%p, buffer=%p, count=%u, ppos=%lu\n",
                 file, buffer, count, ppos ? ((unsigned long)*ppos) : 0);
-       
+
        assert (file != NULL);
        assert (buffer != NULL);
        card = file->private_data;
@@ -1755,8 +2076,19 @@ static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count
                return -ESPIPE;
        }
 
-       rc = via_dsp_do_write (card, buffer, count, (file->f_flags & O_NONBLOCK));
+       rc = via_syscall_down (card, nonblock);
+       if (rc) goto out;
+
+       if (card->ch_out.is_mapped) {
+               rc = -ENXIO;
+               goto out_up;
+       }
+
+       rc = via_dsp_do_write (card, buffer, count, nonblock);
 
+out_up:
+       up (&card->syscall_sem);
+out:
        DPRINTK("EXIT, returning %ld\n",(long) rc);
        return rc;
 }
@@ -1765,45 +2097,105 @@ static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count
 static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wait)
 {
        struct via_info *card;
-       unsigned int mask = 0;
+       unsigned int mask = 0, rd, wr;
 
        DPRINTK ("ENTER\n");
 
        assert (file != NULL);
-       assert (wait != NULL);
        card = file->private_data;
        assert (card != NULL);
 
-       if ((file->f_mode & FMODE_WRITE) &&
-           (atomic_read (&card->ch_out.state) != sgd_stopped)) {
-               poll_wait(file, &card->ch_out.wait, wait);
-               
-               /* XXX is this correct */
-               if (atomic_read (&card->ch_out.buf_in_use) <
-                   atomic_read (&card->ch_out.next_buf))
-                       mask |= POLLOUT | POLLWRNORM;
+       rd = (file->f_mode & FMODE_READ);
+       wr = (file->f_mode & FMODE_WRITE);
+
+       if (wr && (atomic_read (&card->ch_out.n_bufs) == 0)) {
+               assert (card->ch_out.is_active);
+                poll_wait(file, &card->ch_out.wait, wait);
        }
+        if (rd) {
+               /* XXX is it ok, spec-wise, to start DMA here? */
+               via_chan_maybe_start (&card->ch_in);
+               if (atomic_read (&card->ch_in.n_bufs) == 0)
+                       poll_wait(file, &card->ch_in.wait, wait);
+       }
+
+       if (wr && (atomic_read (&card->ch_out.n_bufs) > 0))
+               mask |= POLLOUT | POLLWRNORM;
+       if (rd && (atomic_read (&card->ch_in.n_bufs) > 0))
+               mask |= POLLIN | POLLRDNORM;
 
        DPRINTK("EXIT, returning %u\n", mask);
        return mask;
 }
 
 
-static int via_dsp_drain_dac (struct via_info *card, int non_block)
+/**
+ *     via_dsp_drain_playback - sleep until all playback samples are flushed
+ *     @card: Private info for specified board
+ *     @chan: Channel to drain
+ *     @nonblock: boolean, non-zero if O_NONBLOCK is set
+ *
+ *     Sleeps until all playback has been flushed to the audio
+ *     hardware.
+ *
+ *     Locking: inside card->syscall_sem
+ */
+
+static int via_dsp_drain_playback (struct via_info *card,
+                                  struct via_channel *chan, int nonblock)
 {
-       DPRINTK ("ENTER, non_block = %d\n", non_block);
+       DPRINTK ("ENTER, nonblock = %d\n", nonblock);
+
+       if (chan->slop_len > 0)
+               via_chan_flush_frag (chan);
+
+       if (atomic_read (&chan->n_bufs) == VIA_DMA_BUFFERS)
+               goto out;
 
-       while (!via_chan_empty (&card->ch_out)) {
-               if (non_block) {
-                       DPRINTK ("EXIT, returning -EBUSY\n");
-                       return -EBUSY;
+       via_chan_maybe_start (chan);
+
+       while (atomic_read (&chan->n_bufs) < VIA_DMA_BUFFERS) {
+               if (nonblock) {
+                       DPRINTK ("EXIT, returning -EAGAIN\n");
+                       return -EAGAIN;
+               }
+
+#ifdef VIA_DEBUG
+               {
+               u8 r40,r41,r42,r43,r44,r48;
+               pci_read_config_byte (card->pdev, 0x40, &r40);
+               pci_read_config_byte (card->pdev, 0x41, &r41);
+               pci_read_config_byte (card->pdev, 0x42, &r42);
+               pci_read_config_byte (card->pdev, 0x43, &r43);
+               pci_read_config_byte (card->pdev, 0x44, &r44);
+               pci_read_config_byte (card->pdev, 0x48, &r48);
+               DPRINTK("PCI config: %02X %02X %02X %02X %02X %02X\n",
+                       r40,r41,r42,r43,r44,r48);
+
+               DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
+                        inb (card->baseaddr + 0x00),
+                        inb (card->baseaddr + 0x01),
+                        inb (card->baseaddr + 0x02),
+                        inl (card->baseaddr + 0x04),
+                        inl (card->baseaddr + 0x0C),
+                        inl (card->baseaddr + 0x80),
+                        inl (card->baseaddr + 0x84));
                }
+
+               if (!chan->is_active)
+                       printk (KERN_ERR "sleeping but not active\n");
+#endif
+
+               DPRINTK ("sleeping, nbufs=%d\n", atomic_read (&chan->n_bufs));
+               interruptible_sleep_on (&chan->wait);
+
                if (signal_pending (current)) {
                        DPRINTK ("EXIT, returning -ERESTARTSYS\n");
                        return -ERESTARTSYS;
                }
+       }
 
-#ifndef NDEBUG
+#ifdef VIA_DEBUG
        {
                u8 r40,r41,r42,r43,r44,r48;
                pci_read_config_byte (card->pdev, 0x40, &r40);
@@ -1815,7 +2207,6 @@ static int via_dsp_drain_dac (struct via_info *card, int non_block)
                DPRINTK("PCI config: %02X %02X %02X %02X %02X %02X\n",
                        r40,r41,r42,r43,r44,r48);
 
-               spin_lock_irq (&card->lock);
                DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
                         inb (card->baseaddr + 0x00),
                         inb (card->baseaddr + 0x01),
@@ -1824,39 +2215,49 @@ static int via_dsp_drain_dac (struct via_info *card, int non_block)
                         inl (card->baseaddr + 0x0C),
                         inl (card->baseaddr + 0x80),
                         inl (card->baseaddr + 0x84));
-               spin_unlock_irq (&card->lock);
-               
+
+               DPRINTK ("final nbufs=%d\n", atomic_read (&chan->n_bufs));
        }
 #endif
 
-               DPRINTK ("sleeping\n");         
-               interruptible_sleep_on (&card->ch_out.wait);
-       }
-       
-       DPRINTK ("EXIT\n");
+out:
+       DPRINTK ("EXIT, returning 0\n");
        return 0;
 }
 
 
+/**
+ *     via_dsp_ioctl_space - get information about channel buffering
+ *     @card: Private info for specified board
+ *     @chan: pointer to channel-specific info
+ *     @arg: user buffer for returned information
+ *
+ *     Handles SNDCTL_DSP_GETISPACE and SNDCTL_DSP_GETOSPACE.
+ *
+ *     Locking: inside card->syscall_sem
+ */
+
 static int via_dsp_ioctl_space (struct via_info *card,
                                struct via_channel *chan,
                                void *arg)
 {
        audio_buf_info info;
-       int n;
 
        info.fragstotal = VIA_DMA_BUFFERS;
        info.fragsize = VIA_DMA_BUF_SIZE;
 
-       /* number of full fragments we can read without blocking */
-       n = atomic_read (&chan->next_buf) - atomic_read (&chan->buf_in_use);
-       info.fragments = VIA_DMA_BUFFERS - n;
+       /* number of full fragments we can read/write without blocking */
+       info.fragments = atomic_read (&chan->n_bufs);
+
+       if ((chan->slop_len > 0) && (info.fragments > 0))
+               info.fragments--;
 
        /* number of bytes that can be read or written immediately
-        * without blocking.  FIXME: we are lazy and ignore partially-full
-        * buffers.
+        * without blocking.
         */
-       info.bytes = info.fragments * VIA_DMA_BUF_SIZE;
+       info.bytes = (info.fragments * VIA_DMA_BUF_SIZE);
+       if (chan->slop_len > 0)
+               info.bytes += VIA_DMA_BUF_SIZE - chan->slop_len;
 
        DPRINTK ("EXIT, returning fragstotal=%d, fragsize=%d, fragments=%d, bytes=%d\n",
                info.fragstotal,
@@ -1868,205 +2269,375 @@ static int via_dsp_ioctl_space (struct via_info *card,
 }
 
 
+/**
+ *     via_dsp_ioctl_ptr - get information about hardware buffer ptr
+ *     @card: Private info for specified board
+ *     @chan: pointer to channel-specific info
+ *     @arg: user buffer for returned information
+ *
+ *     Handles SNDCTL_DSP_GETIPTR and SNDCTL_DSP_GETOPTR.
+ *
+ *     Locking: inside card->syscall_sem
+ */
 
-static int via_dsp_ioctl (struct inode *inode, struct file *file,
-                         unsigned int cmd, unsigned long arg)
+static int via_dsp_ioctl_ptr (struct via_info *card,
+                               struct via_channel *chan,
+                               void *arg)
 {
-       int rc = -EINVAL, rd=0, wr=0, val=0;
-       struct via_info *card;
+       count_info info;
 
-       DPRINTK ("ENTER, cmd = 0x%08X\n", cmd);
+       spin_lock_irq (&card->lock);
 
-       assert (file != NULL);
-       card = file->private_data;
-       assert (card != NULL);
+       info.bytes = chan->bytes;
+       info.blocks = chan->n_irqs;
+       chan->n_irqs = 0;
 
-       if (file->f_mode & FMODE_WRITE)
-               wr = 1;
-       if (file->f_mode & FMODE_READ)
-               rd = 1;
-       
-       switch (cmd) {
+       spin_unlock_irq (&card->lock);
 
-       /* OSS API version.  XXX unverified */          
-       case OSS_GETVERSION:
-               DPRINTK("EXIT, returning SOUND_VERSION\n");
-               return put_user (SOUND_VERSION, (int *)arg);
+       if (chan->is_active) {
+               unsigned long extra;
+               info.ptr = atomic_read (&chan->hw_ptr) * VIA_DMA_BUF_SIZE;
+               extra = VIA_DMA_BUF_SIZE - inl (chan->iobase + VIA_BASE0_PCM_OUT_BLOCK_COUNT);
+               info.ptr += extra;
+               info.bytes += extra;
+       } else {
+               info.ptr = 0;
+       }
+
+       DPRINTK ("EXIT, returning bytes=%d, blocks=%d, ptr=%d\n",
+               info.bytes,
+               info.blocks,
+               info.ptr);
+
+       return copy_to_user (arg, &info, sizeof (info));
+}
+
+
+static int via_dsp_ioctl_trigger (struct via_channel *chan, int val)
+{
+       int enable, do_something;
+
+       if (chan->is_record)
+               enable = (val & PCM_ENABLE_INPUT);
+       else
+               enable = (val & PCM_ENABLE_OUTPUT);
+
+       if (!chan->is_enabled && enable) {
+               do_something = 1;
+       } else if (chan->is_enabled && !enable) {
+               do_something = -1;
+       } else {
+               do_something = 0;
+       }
+
+       DPRINTK ("enable=%d, do_something=%d\n",
+                enable, do_something);
+
+       if (chan->is_active && do_something)
+               return -EINVAL;
+
+       if (do_something == 1) {
+               chan->is_enabled = 1;
+               via_chan_maybe_start (chan);
+               DPRINTK ("Triggering input\n");
+       }
+
+       else if (do_something == -1) {
+               chan->is_enabled = 0;
+               DPRINTK ("Setup input trigger\n");
+       }
+
+       return 0;
+}
+
+
+static int via_dsp_ioctl (struct inode *inode, struct file *file,
+                         unsigned int cmd, unsigned long arg)
+{
+       int rc, rd=0, wr=0, val=0;
+       struct via_info *card;
+       struct via_channel *chan;
+       int nonblock = (file->f_flags & O_NONBLOCK);
+
+       assert (file != NULL);
+       card = file->private_data;
+       assert (card != NULL);
+
+       if (file->f_mode & FMODE_WRITE)
+               wr = 1;
+       if (file->f_mode & FMODE_READ)
+               rd = 1;
+
+       rc = via_syscall_down (card, nonblock);
+       if (rc)
+               return rc;
+       rc = -EINVAL;
+
+       switch (cmd) {
+
+       /* OSS API version.  XXX unverified */
+       case OSS_GETVERSION:
+               DPRINTK("ioctl OSS_GETVERSION, EXIT, returning SOUND_VERSION\n");
+               rc = put_user (SOUND_VERSION, (int *)arg);
+               break;
 
        /* list of supported PCM data formats */
        case SNDCTL_DSP_GETFMTS:
-               DPRINTK("EXIT, returning AFMT U8|S16_LE\n");
-                return put_user (AFMT_U8 | AFMT_S16_LE, (int *)arg);
+               DPRINTK("DSP_GETFMTS, EXIT, returning AFMT U8|S16_LE\n");
+                rc = put_user (AFMT_U8 | AFMT_S16_LE, (int *)arg);
+               break;
 
        /* query or set current channel's PCM data format */
        case SNDCTL_DSP_SETFMT:
-               if (get_user(val, (int *)arg))
-                       return -EFAULT;
+               if (get_user(val, (int *)arg)) {
+                       rc = -EFAULT;
+                       break;
+               }
+               DPRINTK("DSP_SETFMT, val==%d\n", val);
                if (val != AFMT_QUERY) {
                        rc = 0;
 
-                       spin_lock_irq (&card->lock);
                        if (rc == 0 && rd)
                                rc = via_chan_set_fmt (card, &card->ch_in, val);
                        if (rc == 0 && wr)
                                rc = via_chan_set_fmt (card, &card->ch_out, val);
-                       spin_unlock_irq (&card->lock);
 
-                       if (rc <= 0)
-                               return rc ? rc : -EINVAL;
+                       if (rc <= 0) {
+                               if (rc == 0)
+                                       rc = -EINVAL;
+                               break;
+                       }
                        val = rc;
                } else {
-                       spin_lock_irq (&card->lock);
                        if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_16BIT)) ||
                            (wr && (card->ch_out.pcm_fmt & VIA_PCM_FMT_16BIT)))
                                val = AFMT_S16_LE;
                        else
                                val = AFMT_U8;
-                       spin_unlock_irq (&card->lock);
                }
                DPRINTK("SETFMT EXIT, returning %d\n", val);
-                return put_user (val, (int *)arg);
+                rc = put_user (val, (int *)arg);
+               break;
 
        /* query or set number of channels (1=mono, 2=stereo) */
         case SNDCTL_DSP_CHANNELS:
-               if (get_user(val, (int *)arg))
-                       return -EFAULT;
+               if (get_user(val, (int *)arg)) {
+                       rc = -EFAULT;
+                       break;
+               }
+               DPRINTK("DSP_CHANNELS, val==%d\n", val);
                if (val != 0) {
                        rc = 0;
-                       spin_lock_irq (&card->lock);
                        if (rc == 0 && rd)
                                rc = via_chan_set_stereo (card, &card->ch_in, val);
                        if (rc == 0 && wr)
                                rc = via_chan_set_stereo (card, &card->ch_out, val);
-                       spin_unlock_irq (&card->lock);
-                       if (rc <= 0)
-                               return rc ? rc : -EINVAL;
+                       if (rc <= 0) {
+                               if (rc == 0)
+                                       rc = -EINVAL;
+                               break;
+                       }
                        val = rc;
                } else {
-                       spin_lock_irq (&card->lock);
                        if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_STEREO)) ||
                            (wr && (card->ch_out.pcm_fmt & VIA_PCM_FMT_STEREO)))
                                val = 2;
                        else
                                val = 1;
-                       spin_unlock_irq (&card->lock);
                }
                DPRINTK("CHANNELS EXIT, returning %d\n", val);
-                return put_user (val, (int *)arg);
-       
+                rc = put_user (val, (int *)arg);
+               break;
+
        /* enable (val is not zero) or disable (val == 0) stereo */
         case SNDCTL_DSP_STEREO:
-               if (get_user(val, (int *)arg))
-                       return -EFAULT;
+               if (get_user(val, (int *)arg)) {
+                       rc = -EFAULT;
+                       break;
+               }
+               DPRINTK("DSP_STEREO, val==%d\n", val);
                rc = 0;
-               spin_lock_irq (&card->lock);
+
                if (rc == 0 && rd)
                        rc = via_chan_set_stereo (card, &card->ch_in, val ? 2 : 1);
                if (rc == 0 && wr)
                        rc = via_chan_set_stereo (card, &card->ch_out, val ? 2 : 1);
-               spin_unlock_irq (&card->lock);
-               if (rc <= 0)
-                       return rc ? rc : -EINVAL;
+
+               if (rc <= 0) {
+                       if (rc == 0)
+                               rc = -EINVAL;
+                       break;
+               }
                DPRINTK("STEREO EXIT, returning %d\n", val);
-                return 0;
-       
+                rc = 0;
+               break;
+
        /* query or set sampling rate */
         case SNDCTL_DSP_SPEED:
-               if (get_user(val, (int *)arg))
-                       return -EFAULT;
-               if (val < 0)
-                       return -EINVAL;
+               if (get_user(val, (int *)arg)) {
+                       rc = -EFAULT;
+                       break;
+               }
+               DPRINTK("DSP_SPEED, val==%d\n", val);
+               if (val < 0) {
+                       rc = -EINVAL;
+                       break;
+               }
                if (val > 0) {
                        rc = 0;
-                       spin_lock_irq (&card->lock);
+
                        if (rc == 0 && rd)
                                rc = via_chan_set_speed (card, &card->ch_in, val);
                        if (rc == 0 && wr)
                                rc = via_chan_set_speed (card, &card->ch_out, val);
-                       spin_unlock_irq (&card->lock);
-                       if (rc <= 0)
-                               return rc ? rc : -EINVAL;
+
+                       if (rc <= 0) {
+                               if (rc == 0)
+                                       rc = -EINVAL;
+                               break;
+                       }
                        val = rc;
                } else {
-                       spin_lock_irq (&card->lock);
                        if (rd)
                                val = card->ch_in.rate;
                        else if (wr)
                                val = card->ch_out.rate;
                        else
                                val = 0;
-                       spin_unlock_irq (&card->lock);
                }
                DPRINTK("SPEED EXIT, returning %d\n", val);
-                return put_user (val, (int *)arg);
-       
+                rc = put_user (val, (int *)arg);
+               break;
+
        /* wait until all buffers have been played, and then stop device */
        case SNDCTL_DSP_SYNC:
+               DPRINTK ("DSP_SYNC\n");
                if (wr) {
-                       DPRINTK("SYNC EXIT (after calling via_dsp_drain_dac)\n");
-                       return via_dsp_drain_dac (card, file->f_flags & O_NONBLOCK);
+                       DPRINTK("SYNC EXIT (after calling via_dsp_drain_playback)\n");
+                       rc = via_dsp_drain_playback (card, &card->ch_out, nonblock);
                }
                break;
 
        /* stop recording/playback immediately */
         case SNDCTL_DSP_RESET:
-               spin_lock_irq (&card->lock);
+               DPRINTK ("DSP_RESET\n");
                if (rd) {
                        via_chan_clear (&card->ch_in);
-                       via_chan_pcm_fmt (card, &card->ch_in, 1);
+                       via_chan_pcm_fmt (&card->ch_in, 1);
                }
                if (wr) {
                        via_chan_clear (&card->ch_out);
-                       via_chan_pcm_fmt (card, &card->ch_out, 1);
+                       via_chan_pcm_fmt (&card->ch_out, 1);
                }
-               spin_unlock_irq (&card->lock);
-               DPRINTK("RESET EXIT, returning 0\n");
-               return 0;
+
+               rc = 0;
+               break;
 
        /* obtain bitmask of device capabilities, such as mmap, full duplex, etc. */
        case SNDCTL_DSP_GETCAPS:
-               DPRINTK("GETCAPS EXIT\n");
-               return put_user(DSP_CAP_REVISION, (int *)arg);
-               
+               DPRINTK("DSP_GETCAPS\n");
+               rc = put_user(VIA_DSP_CAP, (int *)arg);
+               break;
+
        /* obtain bitmask of device capabilities, such as mmap, full duplex, etc. */
        case SNDCTL_DSP_GETBLKSIZE:
-               DPRINTK("GETBLKSIZE EXIT\n");
-               return put_user(VIA_DMA_BUF_SIZE, (int *)arg);
-               
+               DPRINTK("DSP_GETBLKSIZE\n");
+               rc = put_user(VIA_DMA_BUF_SIZE, (int *)arg);
+               break;
+
        /* obtain information about input buffering */
        case SNDCTL_DSP_GETISPACE:
-               DPRINTK("GETISPACE EXIT\n");
-               return via_dsp_ioctl_space (card, &card->ch_in, (void*) arg);
-       
+               DPRINTK("DSP_GETISPACE\n");
+               if (rd)
+                       rc = via_dsp_ioctl_space (card, &card->ch_in, (void*) arg);
+               break;
+
        /* obtain information about output buffering */
        case SNDCTL_DSP_GETOSPACE:
-               DPRINTK("GETOSPACE EXIT\n");
-               return via_dsp_ioctl_space (card, &card->ch_out, (void*) arg);
+               DPRINTK("DSP_GETOSPACE\n");
+               if (wr)
+                       rc = via_dsp_ioctl_space (card, &card->ch_out, (void*) arg);
+               break;
+
+       /* obtain information about input hardware pointer */
+       case SNDCTL_DSP_GETIPTR:
+               DPRINTK("DSP_GETIPTR\n");
+               if (rd)
+                       rc = via_dsp_ioctl_ptr (card, &card->ch_in, (void*) arg);
+               break;
+
+       /* obtain information about output hardware pointer */
+       case SNDCTL_DSP_GETOPTR:
+               DPRINTK("DSP_GETOPTR\n");
+               if (wr)
+                       rc = via_dsp_ioctl_ptr (card, &card->ch_out, (void*) arg);
+               break;
 
        /* return number of bytes remaining to be played by DMA engine */
        case SNDCTL_DSP_GETODELAY:
                {
-               int n;
-               
-               n = atomic_read (&card->ch_out.next_buf) -
-                   atomic_read (&card->ch_out.buf_in_use);
-               assert (n >= 0);
-
-               if (n == 0)
-                       val = 0;
-               else {
-                       val = (n - 1) * VIA_DMA_BUF_SIZE;
-                       val += inl (card->ch_out.iobase + VIA_BASE0_PCM_OUT_BLOCK_COUNT);
+               DPRINTK("DSP_GETODELAY\n");
+
+               chan = &card->ch_out;
+
+               if (!wr)
+                       break;
+
+               val = VIA_DMA_BUFFERS - atomic_read (&chan->n_bufs);
+
+               if (val > 0) {
+                       val *= VIA_DMA_BUF_SIZE;
+                       val -= VIA_DMA_BUF_SIZE -
+                              inl (chan->iobase + VIA_BASE0_PCM_OUT_BLOCK_COUNT);
                }
-               
+               val += chan->slop_len;
+
+               assert (val <= (VIA_DMA_BUF_SIZE * VIA_DMA_BUFFERS));
+
                DPRINTK("GETODELAY EXIT, val = %d bytes\n", val);
-                return put_user (val, (int *)arg);
+                rc = put_user (val, (int *)arg);
+               break;
                }
 
+       /* handle the quick-start of a channel,
+        * or the notification that a quick-start will
+        * occur in the future
+        */
+       case SNDCTL_DSP_SETTRIGGER:
+               if (get_user(val, (int *)arg)) {
+                       rc = -EFAULT;
+                       break;
+               }
+               DPRINTK("DSP_SETTRIGGER, rd=%d, wr=%d, act=%d/%d, en=%d/%d\n",
+                       rd, wr, card->ch_in.is_active, card->ch_out.is_active,
+                       card->ch_in.is_enabled, card->ch_out.is_enabled);
+
+               rc = 0;
+
+               if (rd)
+                       rc = via_dsp_ioctl_trigger (&card->ch_in, val);
+               if (!rc && wr)
+                       rc = via_dsp_ioctl_trigger (&card->ch_out, val);
+
+               break;
+
+       /* Enable full duplex.  Since we do this as soon as we are opened
+        * with O_RDWR, this is mainly a no-op that always returns success.
+        */
+       case SNDCTL_DSP_SETDUPLEX:
+               DPRINTK("DSP_SETDUPLEX\n");
+               if (!rd || !wr)
+                       break;
+               rc = 0;
+               break;
+
        /* set fragment size.  implemented as a successful no-op for now */
        case SNDCTL_DSP_SETFRAGMENT:
-               if (get_user(val, (int *)arg))
-                       return -EFAULT;
+               if (get_user(val, (int *)arg)) {
+                       rc = -EFAULT;
+                       break;
+               }
+               DPRINTK("DSP_SETFRAGMENT, val==%d\n", val);
 
                DPRINTK ("SNDCTL_DSP_SETFRAGMENT (fragshift==0x%04X (%d), maxfrags==0x%04X (%d))\n",
                         val & 0xFFFF,
@@ -2075,19 +2646,29 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
                         (val >> 16) & 0xFFFF);
 
                /* just to shut up some programs */
-               return 0;
+               rc = 0;
+               break;
 
-       /* inform device of an upcoming pause in input (or output).  not implemented */
+       /* inform device of an upcoming pause in input (or output). */
        case SNDCTL_DSP_POST:
-               DPRINTK("POST EXIT (null ioctl, returning -EINVAL)\n");
-                return -EINVAL;
+               DPRINTK("DSP_POST\n");
+               if (wr) {
+                       if (card->ch_out.slop_len > 0)
+                               via_chan_flush_frag (&card->ch_out);
+                       via_chan_maybe_start (&card->ch_out);
+               }
+
+               rc = 0;
+               break;
 
        /* not implemented */
        default:
-               DPRINTK ("unhandled ioctl\n");
+               DPRINTK ("unhandled ioctl, cmd==%u, arg==%p\n",
+                        cmd, (void*) arg);
                break;
        }
-               
+
+       up (&card->syscall_sem);
        DPRINTK("EXIT, returning %d\n", rc);
        return rc;
 }
@@ -2095,25 +2676,28 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
 
 static int via_dsp_open (struct inode *inode, struct file *file)
 {
-       int open_mode, rc = -EINVAL, minor = MINOR(inode->i_rdev);
-       int got_read_chan = 0, is_busy;
+       int rc, minor = MINOR(inode->i_rdev);
+       int got_read_chan = 0;
        struct via_info *card;
        struct pci_dev *pdev;
+       struct via_channel *chan;
        struct pci_driver *drvr;
-       unsigned long flags;
+       int nonblock = (file->f_flags & O_NONBLOCK);
 
        DPRINTK ("ENTER, minor=%d, file->f_mode=0x%x\n", minor, file->f_mode);
-       
-       if (file->f_mode & FMODE_READ) /* no input ATM */
-               goto err_out;
+
+       if (!(file->f_mode & (FMODE_READ | FMODE_WRITE))) {
+               DPRINTK ("EXIT, returning -EINVAL\n");
+               return -EINVAL;
+       }
 
        card = NULL;
        pci_for_each_dev(pdev) {
                drvr = pci_dev_driver (pdev);
                if (drvr == &via_driver) {
-                       assert (pdev->driver_data != NULL);
-                       
-                       card = pdev->driver_data;       
+                       assert (pci_get_drvdata (pdev) != NULL);
+
+                       card = pci_get_drvdata (pdev);
                        DPRINTK ("dev_dsp = %d, minor = %d, assn = %d\n",
                                 card->dev_dsp, minor,
                                 (card->dev_dsp ^ minor) & ~0xf);
@@ -2122,299 +2706,109 @@ static int via_dsp_open (struct inode *inode, struct file *file)
                                goto match;
                }
        }
-       
+
        DPRINTK ("no matching %s found\n", card ? "minor" : "driver");
-       rc = -ENODEV;
-       goto err_out;
+       return -ENODEV;
 
 match:
-       file->private_data = card;
-
-       /* wait for device to become free */
-       spin_lock_irqsave (&card->lock, flags);
-       open_mode = card->open_mode;
-       if (open_mode & file->f_mode)
-               is_busy = 1;
-       else {
-               is_busy = 0;
-               card->open_mode |= file->f_mode;
-               open_mode = card->open_mode;
-       }
-       spin_unlock_irqrestore (&card->lock, flags);
-       if (is_busy) {
-               rc = -EBUSY;
-               goto err_out;
+       if (nonblock) {
+               if (down_trylock (&card->open_sem)) {
+                       DPRINTK ("EXIT, returning -EAGAIN\n");
+                       return -EAGAIN;
+               }
+       } else {
+               if (down_interruptible (&card->open_sem)) {
+                       DPRINTK ("EXIT, returning -ERESTARTSYS\n");
+                       return -ERESTARTSYS;
+               }
        }
 
-       DPRINTK("open_mode now 0x%x\n", open_mode);
+       file->private_data = card;
+       DPRINTK("file->f_mode == 0x%x\n", file->f_mode);
 
        /* handle input from analog source */
        if (file->f_mode & FMODE_READ) {
-               rc = via_chan_init (card, &card->ch_in, 0x10);
+               chan = &card->ch_in;
+
+               rc = via_chan_init (card, chan);
                if (rc)
-                       goto err_out_clear_mode;
-                       
+                       goto err_out;
+
                got_read_chan = 1;
-               
-               /* why is this forced to 16-bit stereo in all drivers? */
-               card->ch_in.pcm_fmt =
-                       VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO;
 
-               spin_lock_irqsave (&card->lock, flags);
-               via_chan_pcm_fmt (card, &card->ch_out, 0);
-               spin_unlock_irqrestore (&card->lock, flags);
+               /* why is this forced to 16-bit stereo in all drivers? */
+               chan->pcm_fmt = VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO;
 
-               via_set_adc_rate (card, 8000);
+               via_chan_pcm_fmt (chan, 0);
+               via_set_rate (&card->ac97, chan, 44100);
        }
 
        /* handle output to analog source */
        if (file->f_mode & FMODE_WRITE) {
-               rc = via_chan_init (card, &card->ch_out, 0x00);
-               if (rc)
-                       goto err_out_clear_mode;
-               
-               if ((minor & 0xf) == SND_DEV_DSP16)
-                       card->ch_out.pcm_fmt |= VIA_PCM_FMT_16BIT;
-
-               spin_lock_irqsave (&card->lock, flags);
-               via_chan_pcm_fmt (card, &card->ch_out, 0);
-               spin_unlock_irqrestore (&card->lock, flags);
-
-               via_set_dac_rate (card, 8000);
-       }
-
-       DPRINTK ("EXIT, returning 0\n");
-       return 0;
-
-err_out_clear_mode:
-       if (got_read_chan)
-               via_chan_free (card, &card->ch_in);
-       spin_lock_irqsave (&card->lock, flags);
-       card->open_mode &= ~file->f_mode;
-       spin_unlock_irqrestore (&card->lock, flags);
-err_out:
-       DPRINTK("ERROR EXIT, returning %d\n", rc);
-       return rc;
-}
-
-
-static int via_dsp_release(struct inode *inode, struct file *file)
-{
-       struct via_info *card;
-       unsigned long flags;
-
-       DPRINTK ("ENTER\n");
-       
-       assert (file != NULL);
-       card = file->private_data;
-       assert (card != NULL);
-       
-       lock_kernel();
-       if (file->f_mode & FMODE_READ)
-               via_chan_free (card, &card->ch_in);
-
-       if (file->f_mode & FMODE_WRITE) {
-               via_dsp_drain_dac (card, file->f_flags & O_NONBLOCK);
-               via_chan_free (card, &card->ch_out);
-       }
-                       
-       spin_lock_irqsave (&card->lock, flags);
-       card->open_mode &= ~(file->f_mode);
-       spin_unlock_irqrestore (&card->lock, flags);
-
-       wake_up (&card->open_wait);
-       unlock_kernel();
-
-       DPRINTK("EXIT, returning 0\n");
-       return 0;
-}
-
-
-#ifdef VIA_PROC_FS
-
-/****************************************************************
- *
- * /proc/driver/via/info
- *
- *
- */
-
-static int via_info_read_proc (char *page, char **start, off_t off,
-                              int count, int *eof, void *data)
-{
-#define YN(val,bit) (((val) & (bit)) ? "yes" : "no")
-#define ED(val,bit) (((val) & (bit)) ? "enable" : "disable")
-
-       int len = 0;
-       u8 r40, r41, r42, r44;
-       struct via_info *card = data;
-       
-       DPRINTK ("ENTER\n");
-
-       assert (card != NULL);
-       
-       len += sprintf (page+len, VIA_CARD_NAME "\n\n");
-       
-       pci_read_config_byte (card->pdev, 0x40, &r40);
-       pci_read_config_byte (card->pdev, 0x41, &r41);
-       pci_read_config_byte (card->pdev, 0x42, &r42);
-       pci_read_config_byte (card->pdev, 0x44, &r44);
-       
-       len += sprintf (page+len,
-               "Via 82Cxxx PCI registers:\n"
-               "\n"
-               "40  Codec Ready: %s\n"
-               "    Codec Low-power: %s\n"
-               "    Secondary Codec Ready: %s\n"
-               "\n"
-               "41  Interface Enable: %s\n"
-               "    De-Assert Reset: %s\n"
-               "    Force SYNC high: %s\n"
-               "    Force SDO high: %s\n"
-               "    Variable Sample Rate On-Demand Mode: %s\n"
-               "    SGD Read Channel PCM Data Out: %s\n"
-               "    FM Channel PCM Data Out: %s\n"
-               "    SB PCM Data Out: %s\n"
-               "\n"
-               "42  Game port enabled: %s\n"
-               "    SoundBlaster enabled: %s\n"
-               "    FM enabled: %s\n"
-               "    MIDI enabled: %s\n"
-               "\n"    
-               "44  AC-Link Interface Access: %s\n"
-               "    Secondary Codec Support: %s\n"
-                       
-               "\n",
-                       
-               YN (r40, VIA_CR40_AC97_READY),
-               YN (r40, VIA_CR40_AC97_LOW_POWER),
-               YN (r40, VIA_CR40_SECONDARY_READY),
-
-               ED (r41, VIA_CR41_AC97_ENABLE),
-               YN (r41, (1 << 6)),
-               YN (r41, (1 << 5)),
-               YN (r41, (1 << 4)),
-               ED (r41, (1 << 3)),
-               ED (r41, (1 << 2)),
-               ED (r41, (1 << 1)),
-               ED (r41, (1 << 0)),
-
-               YN (r42, VIA_CR42_GAME_ENABLE),
-               YN (r42, VIA_CR42_SB_ENABLE),
-               YN (r42, VIA_CR42_FM_ENABLE),
-               YN (r42, VIA_CR42_MIDI_ENABLE),
-
-               YN (r44, VIA_CR44_AC_LINK_ACCESS),
-               YN (r44, VIA_CR44_SECOND_CODEC_SUPPORT)
-                       
-               );
-
-       DPRINTK("EXIT, returning %d\n", len);
-       return len;
-
-#undef YN
-#undef ED
-}
-
-
-/****************************************************************
- *
- * /proc/driver/via/... setup and cleanup
- *
- *
- */
-
-static int __init via_init_proc (void)
-{
-       DPRINTK ("ENTER\n");
-
-       if (!proc_mkdir ("driver/via", 0))
-               return -EIO;
-
-       DPRINTK ("EXIT, returning 0\n");
-       return 0;
-}
-
-
-static void via_cleanup_proc (void)
-{
-       DPRINTK ("ENTER\n");
-
-       remove_proc_entry ("driver/via", NULL);
-
-       DPRINTK ("EXIT\n");
-}
-
-
-static int __init via_card_init_proc (struct via_info *card)
-{
-       char s[32];
-       int rc;
-       
-       DPRINTK ("ENTER\n");
-
-       sprintf (s, "driver/via/%d", card->card_num);
-       if (!proc_mkdir (s, 0)) {
-               rc = -EIO;
-               goto err_out_none;
-       }
-       
-       sprintf (s, "driver/via/%d/info", card->card_num);
-       if (!create_proc_read_entry (s, 0, 0, via_info_read_proc, card)) {
-               rc = -EIO;
-               goto err_out_dir;
-       }
+               chan = &card->ch_out;
 
-       sprintf (s, "driver/via/%d/ac97", card->card_num);
-       if (!create_proc_read_entry (s, 0, 0, ac97_read_proc, &card->ac97)) {
-               rc = -EIO;
-               goto err_out_info;
+               rc = via_chan_init (card, chan);
+               if (rc)
+                       goto err_out_read_chan;
+
+               if ((minor & 0xf) == SND_DEV_DSP16) {
+                       chan->pcm_fmt |= VIA_PCM_FMT_16BIT;
+                       via_set_rate (&card->ac97, chan, 44100);
+               } else {
+                       via_set_rate (&card->ac97, chan, 8000);
+               }
+
+               via_chan_pcm_fmt (chan, 0);
        }
 
        DPRINTK ("EXIT, returning 0\n");
        return 0;
 
-err_out_info:
-       sprintf (s, "driver/via/%d/info", card->card_num);
-       remove_proc_entry (s, NULL);
-
-err_out_dir:
-       sprintf (s, "driver/via/%d", card->card_num);
-       remove_proc_entry (s, NULL);
-
-err_out_none:
-       DPRINTK ("EXIT, returning %d\n", rc);
+err_out_read_chan:
+       if (got_read_chan)
+               via_chan_free (card, &card->ch_in);
+err_out:
+       up (&card->open_sem);
+       DPRINTK("ERROR EXIT, returning %d\n", rc);
        return rc;
 }
 
 
-static void via_card_cleanup_proc (struct via_info *card)
+static int via_dsp_release(struct inode *inode, struct file *file)
 {
-       char s[32];
+       struct via_info *card;
+       int nonblock = (file->f_flags & O_NONBLOCK);
+       int rc;
 
        DPRINTK ("ENTER\n");
 
-       sprintf (s, "driver/via/%d/ac97", card->card_num);
-       remove_proc_entry (s, NULL);
-
-       sprintf (s, "driver/via/%d/info", card->card_num);
-       remove_proc_entry (s, NULL);
+       assert (file != NULL);
+       card = file->private_data;
+       assert (card != NULL);
 
-       sprintf (s, "driver/via/%d", card->card_num);
-       remove_proc_entry (s, NULL);
+       rc = via_syscall_down (card, nonblock);
+       if (rc) {
+               DPRINTK ("EXIT (syscall_down error), rc=%d\n", rc);
+               return rc;
+       }
 
-       DPRINTK ("EXIT\n");
-}
+       if (file->f_mode & FMODE_WRITE) {
+               rc = via_dsp_drain_playback (card, &card->ch_out, nonblock);
+               if (rc)
+                       printk (KERN_DEBUG "via_audio: ignoring drain playback error %d\n", rc);
 
+               via_chan_free (card, &card->ch_out);
+       }
 
-#else
+       if (file->f_mode & FMODE_READ)
+               via_chan_free (card, &card->ch_in);
 
-static inline int via_init_proc (void) { return 0; }
-static inline void via_cleanup_proc (void) {}
-static inline int via_card_init_proc (struct via_info *card) { return 0; }
-static inline void via_card_cleanup_proc (struct via_info *card) {}
+       up (&card->syscall_sem);
+       up (&card->open_sem);
 
-#endif /* VIA_PROC_FS */
+       DPRINTK("EXIT, returning 0\n");
+       return 0;
+}
 
 
 /****************************************************************
@@ -2430,9 +2824,9 @@ static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id
        struct via_info *card;
        u8 tmp;
        static int printed_version = 0;
-       
+
        DPRINTK ("ENTER\n");
-       
+
        if (printed_version++ == 0)
                printk (KERN_INFO "Via 686a audio driver " VIA_VERSION "\n");
 
@@ -2448,7 +2842,7 @@ static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id
                rc = -EIO;
                goto err_out_none;
        }
-       
+
        card = kmalloc (sizeof (*card), GFP_KERNEL);
        if (!card) {
                printk (KERN_ERR PFX "out of memory, aborting\n");
@@ -2456,15 +2850,21 @@ static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id
                goto err_out_none;
        }
 
-       pdev->driver_data = card;
+       pci_set_drvdata (pdev, card);
 
        memset (card, 0, sizeof (*card));
        card->pdev = pdev;
        card->baseaddr = pci_resource_start (pdev, 0);
        card->card_num = via_num_cards++;
        spin_lock_init (&card->lock);
-       init_waitqueue_head(&card->open_wait);
-       
+       init_MUTEX (&card->syscall_sem);
+       init_MUTEX (&card->open_sem);
+
+       /* we must init these now, in case the intr handler needs them */
+       via_chan_init_defaults (card, &card->ch_out);
+       via_chan_init_defaults (card, &card->ch_in);
+       via_chan_init_defaults (card, &card->ch_fm);
+
        /* if BAR 2 is present, chip is Rev H or later,
         * which means it has a few extra features */
        if (pci_resource_start (pdev, 2) > 0)
@@ -2481,8 +2881,8 @@ static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id
                rc = -ENODEV;
                goto err_out_kfree;
        }
-       
-       /* 
+
+       /*
         * init AC97 mixer and codec
         */
        rc = via_ac97_init (card);
@@ -2499,10 +2899,10 @@ static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id
                printk (KERN_ERR PFX "DSP device init failed, aborting\n");
                goto err_out_have_mixer;
        }
-       
+
        /*
         * per-card /proc info
-        */     
+        */
        rc = via_card_init_proc (card);
        if (rc) {
                printk (KERN_ERR PFX "card-specific /proc init failed, aborting\n");
@@ -2517,10 +2917,11 @@ static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id
                printk (KERN_ERR PFX "interrupt init failed, aborting\n");
                goto err_out_have_proc;
        }
-       
+
        pci_read_config_byte (pdev, 0x3C, &tmp);
        if ((tmp & 0x0F) != pdev->irq) {
                printk (KERN_WARNING PFX "IRQ fixup, 0x3C==0x%02X\n", tmp);
+               udelay (15);
                tmp &= 0xF0;
                tmp |= pdev->irq;
                pci_write_config_byte (pdev, 0x3C, tmp);
@@ -2532,7 +2933,7 @@ static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id
 
        printk (KERN_INFO PFX "board #%d at 0x%04lX, IRQ %d\n",
                card->card_num + 1, card->baseaddr, pdev->irq);
-       
+
        DPRINTK ("EXIT, returning 0\n");
        return 0;
 
@@ -2554,7 +2955,7 @@ err_out_kfree:
 err_out_none:
        release_region (pci_resource_start (pdev, 0), pci_resource_len (pdev, 0));
 err_out:
-       pdev->driver_data = NULL;
+       pci_set_drvdata (pdev, NULL);
        DPRINTK ("EXIT - returning %d\n", rc);
        return rc;
 }
@@ -2563,13 +2964,13 @@ err_out:
 static void __exit via_remove_one (struct pci_dev *pdev)
 {
        struct via_info *card;
-       
+
        DPRINTK ("ENTER\n");
-       
+
        assert (pdev != NULL);
-       card = pdev->driver_data;
+       card = pci_get_drvdata (pdev);
        assert (card != NULL);
-       
+
        via_interrupt_cleanup (card);
        via_card_cleanup_proc (card);
        via_dsp_cleanup (card);
@@ -2582,8 +2983,8 @@ static void __exit via_remove_one (struct pci_dev *pdev)
 #endif
        kfree (card);
 
-       pdev->driver_data = NULL;
-       
+       pci_set_drvdata (pdev, NULL);
+
        pci_set_power_state (pdev, 3); /* ...zzzzzz */
 
        DPRINTK ("EXIT\n");
@@ -2623,11 +3024,11 @@ static int __init init_via82cxxx_audio(void)
        return 0;
 }
 
+
 static void __exit cleanup_via82cxxx_audio(void)
 {
        DPRINTK("ENTER\n");
-       
+
        pci_unregister_driver (&via_driver);
        via_cleanup_proc ();
 
@@ -2637,3 +3038,187 @@ static void __exit cleanup_via82cxxx_audio(void)
 
 module_init(init_via82cxxx_audio);
 module_exit(cleanup_via82cxxx_audio);
+
+MODULE_AUTHOR("Jeff Garzik <jgarzik@mandrakesoft.com>");
+MODULE_DESCRIPTION("DSP audio and mixer driver for Via 82Cxxx audio devices");
+EXPORT_NO_SYMBOLS;
+
+
+
+#ifdef VIA_PROC_FS
+
+/****************************************************************
+ *
+ * /proc/driver/via/info
+ *
+ *
+ */
+
+static int via_info_read_proc (char *page, char **start, off_t off,
+                              int count, int *eof, void *data)
+{
+#define YN(val,bit) (((val) & (bit)) ? "yes" : "no")
+#define ED(val,bit) (((val) & (bit)) ? "enable" : "disable")
+
+       int len = 0;
+       u8 r40, r41, r42, r44;
+       struct via_info *card = data;
+
+       DPRINTK ("ENTER\n");
+
+       assert (card != NULL);
+
+       len += sprintf (page+len, VIA_CARD_NAME "\n\n");
+
+       pci_read_config_byte (card->pdev, 0x40, &r40);
+       pci_read_config_byte (card->pdev, 0x41, &r41);
+       pci_read_config_byte (card->pdev, 0x42, &r42);
+       pci_read_config_byte (card->pdev, 0x44, &r44);
+
+       len += sprintf (page+len,
+               "Via 82Cxxx PCI registers:\n"
+               "\n"
+               "40  Codec Ready: %s\n"
+               "    Codec Low-power: %s\n"
+               "    Secondary Codec Ready: %s\n"
+               "\n"
+               "41  Interface Enable: %s\n"
+               "    De-Assert Reset: %s\n"
+               "    Force SYNC high: %s\n"
+               "    Force SDO high: %s\n"
+               "    Variable Sample Rate On-Demand Mode: %s\n"
+               "    SGD Read Channel PCM Data Out: %s\n"
+               "    FM Channel PCM Data Out: %s\n"
+               "    SB PCM Data Out: %s\n"
+               "\n"
+               "42  Game port enabled: %s\n"
+               "    SoundBlaster enabled: %s\n"
+               "    FM enabled: %s\n"
+               "    MIDI enabled: %s\n"
+               "\n"
+               "44  AC-Link Interface Access: %s\n"
+               "    Secondary Codec Support: %s\n"
+
+               "\n",
+
+               YN (r40, VIA_CR40_AC97_READY),
+               YN (r40, VIA_CR40_AC97_LOW_POWER),
+               YN (r40, VIA_CR40_SECONDARY_READY),
+
+               ED (r41, VIA_CR41_AC97_ENABLE),
+               YN (r41, (1 << 6)),
+               YN (r41, (1 << 5)),
+               YN (r41, (1 << 4)),
+               ED (r41, (1 << 3)),
+               ED (r41, (1 << 2)),
+               ED (r41, (1 << 1)),
+               ED (r41, (1 << 0)),
+
+               YN (r42, VIA_CR42_GAME_ENABLE),
+               YN (r42, VIA_CR42_SB_ENABLE),
+               YN (r42, VIA_CR42_FM_ENABLE),
+               YN (r42, VIA_CR42_MIDI_ENABLE),
+
+               YN (r44, VIA_CR44_AC_LINK_ACCESS),
+               YN (r44, VIA_CR44_SECOND_CODEC_SUPPORT)
+
+               );
+
+       DPRINTK("EXIT, returning %d\n", len);
+       return len;
+
+#undef YN
+#undef ED
+}
+
+
+/****************************************************************
+ *
+ * /proc/driver/via/... setup and cleanup
+ *
+ *
+ */
+
+static int __init via_init_proc (void)
+{
+       DPRINTK ("ENTER\n");
+
+       if (!proc_mkdir ("driver/via", 0))
+               return -EIO;
+
+       DPRINTK ("EXIT, returning 0\n");
+       return 0;
+}
+
+
+static void via_cleanup_proc (void)
+{
+       DPRINTK ("ENTER\n");
+
+       remove_proc_entry ("driver/via", NULL);
+
+       DPRINTK ("EXIT\n");
+}
+
+
+static int __init via_card_init_proc (struct via_info *card)
+{
+       char s[32];
+       int rc;
+
+       DPRINTK ("ENTER\n");
+
+       sprintf (s, "driver/via/%d", card->card_num);
+       if (!proc_mkdir (s, 0)) {
+               rc = -EIO;
+               goto err_out_none;
+       }
+
+       sprintf (s, "driver/via/%d/info", card->card_num);
+       if (!create_proc_read_entry (s, 0, 0, via_info_read_proc, card)) {
+               rc = -EIO;
+               goto err_out_dir;
+       }
+
+       sprintf (s, "driver/via/%d/ac97", card->card_num);
+       if (!create_proc_read_entry (s, 0, 0, ac97_read_proc, &card->ac97)) {
+               rc = -EIO;
+               goto err_out_info;
+       }
+
+       DPRINTK ("EXIT, returning 0\n");
+       return 0;
+
+err_out_info:
+       sprintf (s, "driver/via/%d/info", card->card_num);
+       remove_proc_entry (s, NULL);
+
+err_out_dir:
+       sprintf (s, "driver/via/%d", card->card_num);
+       remove_proc_entry (s, NULL);
+
+err_out_none:
+       DPRINTK ("EXIT, returning %d\n", rc);
+       return rc;
+}
+
+
+static void via_card_cleanup_proc (struct via_info *card)
+{
+       char s[32];
+
+       DPRINTK ("ENTER\n");
+
+       sprintf (s, "driver/via/%d/ac97", card->card_num);
+       remove_proc_entry (s, NULL);
+
+       sprintf (s, "driver/via/%d/info", card->card_num);
+       remove_proc_entry (s, NULL);
+
+       sprintf (s, "driver/via/%d", card->card_num);
+       remove_proc_entry (s, NULL);
+
+       DPRINTK ("EXIT\n");
+}
+
+#endif /* VIA_PROC_FS */
index bf60b916740cb8199c6f79b792d89a336d1128fa..c1441ac7bd34e7c22ea2edfe383d76df1f5c2702 100644 (file)
@@ -21,7 +21,7 @@ export-objs           := usb.o
 # Multipart objects.
 
 list-multi             := usbcore.o
-usbcore-objs           := usb.o usb-debug.o usb-core.o hub.o
+usbcore-objs           := usb.o usb-debug.o hub.o
 
 # Optional parts of multipart objects.
 
index 59e1c4007bc925ea99c0b59b963a14a29ed07e69..9c77b41a0d2f9148418992e274fd92da9722d0bc 100644 (file)
@@ -52,6 +52,7 @@
 
 #define IOCNR_GET_DEVICE_ID    1
 #define LPIOC_GET_DEVICE_ID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len)        /* get device_id string */
+#define LPGETSTATUS            0x060b          /* same as in drivers/char/lp.c */
 
 /*
  * A DEVICE_ID string may include the printer's serial number.
@@ -179,10 +180,17 @@ static int usblp_open(struct inode *inode, struct file *file)
        if (usblp->used)
                goto out;
 
+       /*
+        * TODO: need to implement LP_ABORTOPEN + O_NONBLOCK as in drivers/char/lp.c ???
+        * This is #if 0-ed because we *don't* want to fail an open
+        * just because the printer is off-line.
+        */
+#if 0
        if ((retval = usblp_check_status(usblp, 0))) {
                retval = retval > 1 ? -EIO : -ENOSPC;
                goto out;
        }
+#endif
 
        usblp->used = 1;
        file->private_data = usblp;
@@ -232,30 +240,59 @@ static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait
 static int usblp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
        struct usblp *usblp = file->private_data;
-       int length;
+       int length, err;
+       unsigned char status;
 
-       if ((_IOC_TYPE(cmd) != 'P') || (_IOC_DIR(cmd) != _IOC_READ))
-               return -EINVAL;
+       if (_IOC_TYPE(cmd) == 'P')      /* new-style ioctl number */
+       
+               switch (_IOC_NR(cmd)) {
 
-       switch (_IOC_NR(cmd)) {
+                       case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */
+                               if (_IOC_DIR(cmd) != _IOC_READ)
+                                       return -EINVAL;
 
-               case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */
+                               err = usblp_get_id(usblp, 0, usblp->device_id_string, DEVICE_ID_SIZE - 1);
+                               if (err < 0) {
+                                       dbg ("usblp%d: error = %d reading IEEE-1284 Device ID string",
+                                               usblp->minor, err);
+                                       usblp->device_id_string[0] = usblp->device_id_string[1] = '\0';
+                                       return -EIO;
+                               }
 
-                       length = (usblp->device_id_string[0] << 8) + usblp->device_id_string[1]; /* big-endian */
+                               length = (usblp->device_id_string[0] << 8) + usblp->device_id_string[1]; /* big-endian */
+                               if (length < DEVICE_ID_SIZE)
+                                       usblp->device_id_string[length] = '\0';
+                               else
+                                       usblp->device_id_string[DEVICE_ID_SIZE - 1] = '\0';
 
-                       dbg ("usblp_ioctl GET_DEVICE_ID actlen: %d, size: %d, string: '%s'",
-                               length, _IOC_SIZE(cmd), &usblp->device_id_string[2]);
+                               dbg ("usblp%d Device ID string [%d/max %d]='%s'",
+                                       usblp->minor, length, _IOC_SIZE(cmd), &usblp->device_id_string[2]);
 
-                       if (length > _IOC_SIZE(cmd)) length = _IOC_SIZE(cmd); /* truncate */
+                               if (length > _IOC_SIZE(cmd)) length = _IOC_SIZE(cmd); /* truncate */
 
-                       if (copy_to_user((unsigned char *) arg, usblp->device_id_string, (unsigned long) length))
-                               return -EFAULT;
+                               if (copy_to_user((unsigned char *) arg, usblp->device_id_string, (unsigned long) length))
+                                       return -EFAULT;
 
-                       break;
+                               break;
 
-               default:
-                       return -EINVAL;
-       }
+                       default:
+                               return -EINVAL;
+               }
+       else    /* old-style ioctl value */
+               switch (cmd) {
+
+                       case LPGETSTATUS:
+                               if (usblp_read_status(usblp, &status)) {
+                                       err("usblp%d: failed reading printer status", usblp->minor);
+                                       return -EIO;
+                               }
+                               if (copy_to_user ((unsigned char *)arg, &status, 1))
+                                       return -EFAULT;
+                               break;
+
+                       default:
+                               return -EINVAL;
+               }
 
        return 0;
 }
diff --git a/drivers/usb/usb-core.c b/drivers/usb/usb-core.c
deleted file mode 100644 (file)
index 8e2dd4d..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * driver/usb/usb-core.c
- *
- * (C) Copyright David Waite 1999
- * based on code from usb.c, by Linus Torvalds
- *
- * The purpose of this file is to pull any and all generic modular code from
- * usb.c and put it in a separate file. This way usb.c is kept as a generic
- * library, while this file handles starting drivers, etc.
- *
- */
-
-#include <linux/version.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/usb.h>
-
-/*
- * USB core
- */
-
-int usb_hub_init(void);
-void usb_hub_cleanup(void);
-int usb_major_init(void);
-void usb_major_cleanup(void);
-
-
-/*
- * Cleanup
- */
-
-static void __exit usb_exit(void)
-{
-       usb_major_cleanup();
-       usbdevfs_cleanup();
-       usb_hub_cleanup();
-}
-
-/*
- * Init
- */
-
-static int __init usb_init(void)
-{
-       usb_major_init();
-       usbdevfs_init();
-       usb_hub_init();
-
-       return 0;
-}
-
-module_init(usb_init);
-module_exit(usb_exit);
index df469e7d81ff2fc911d0b856533dbc739152f4ab..bb93ec898db6453914ef8c134794095b4886efa3 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/malloc.h>
 #include <linux/interrupt.h>  /* for in_interrupt() */
 #include <linux/kmod.h>
+#include <linux/init.h>
 
 
 #ifdef CONFIG_USB_DEBUG
@@ -47,6 +48,9 @@ static const int usb_bandwidth_option =
                                0;
 #endif
 
+extern int  usb_hub_init(void);
+extern void usb_hub_cleanup(void);
+
 /*
  * Prototypes for the device driver probing/loading functions
  */
@@ -2029,6 +2033,32 @@ struct list_head *usb_bus_get_list(void)
 }
 #endif
 
+
+/*
+ * Init
+ */
+static int __init usb_init(void)
+{
+       usb_major_init();
+       usbdevfs_init();
+       usb_hub_init();
+
+       return 0;
+}
+
+/*
+ * Cleanup
+ */
+static void __exit usb_exit(void)
+{
+       usb_major_cleanup();
+       usbdevfs_cleanup();
+       usb_hub_cleanup();
+}
+
+module_init(usb_init);
+module_exit(usb_exit);
+
 /*
  * USB may be built into the kernel or be built as modules.
  * If the USB core [and maybe a host controller driver] is built
index 88896352854fd1a2abefcc856281bc1b2e1fa7e0..d988d9e399844638409b91309eb252fd8bfe3ec3 100644 (file)
@@ -129,7 +129,6 @@ static void write_mda_w(unsigned int val, unsigned char reg)
        outb_p(reg+1, mda_index_port); outb_p(val & 0xff, mda_value_port);
 
        spin_unlock_irqrestore(&mda_lock, flags);
-       restore_flags(flags);
 }
 
 static int test_mda_b(unsigned char val, unsigned char reg)
index 10169c62ce43aa0486ff75b3d88f2f5dc8fa4337..3c981f75c5f9f8ae45df72be249b07c727a6b26b 100644 (file)
@@ -434,7 +434,7 @@ static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
                inode->i_nlink--; /* is this nlink == 0? */
                mark_inode_dirty(inode);
                iput (inode);
-               return -EIO;
+               return err;
        }
        de = (struct ext2_dir_entry_2 *) dir_block->b_data;
        de->inode = cpu_to_le32(inode->i_ino);
index 07c393d2e4e40b94f051b6be5bc5951208b2e132..a655468204c88728bdeccf175ccf4e746887b070 100644 (file)
@@ -38,4 +38,6 @@ static inline void ptep_mkdirty(pte_t *ptep)
        set_pte(ptep, pte_mkdirty(old_pte));
 }
 
+#define pte_same(A,B)  (pte_val(A) == pte_val(B))
+
 #endif /* _ASM_GENERIC_PGTABLE_H */
index c60710120160f30b2af4bb9bfa0d6bbb395d67d9..4b95d09cd68d4864f4788346d24f5f33a2af815f 100644 (file)
@@ -94,7 +94,6 @@ static void __init check_fpu(void)
                printk(KERN_INFO "Enabling unmasked SIMD FPU exception support... ");
                set_in_cr4(X86_CR4_OSXMMEXCPT);
                printk("done.\n");
-               load_mxcsr(0x1f80);
        }
 #endif
 
@@ -166,6 +165,7 @@ __asm__(".align 4\nvide: ret");
 static void __init check_amd_k6(void)
 {
        if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
+           boot_cpu_data.x86 == 5 &&
            boot_cpu_data.x86_model == 6 &&
            boot_cpu_data.x86_mask == 1)
        {
index de59b793a5c66a5d26bd42590ceabf13afe11de3..55ffacff16a26602c6405aa764ef0072345ace6d 100644 (file)
@@ -93,7 +93,7 @@ typedef struct user_fxsr_struct elf_fpxregset_t;
    For the moment, we have only optimizations for the Intel generations,
    but that could change... */
 
-#define ELF_PLATFORM  ("i386\0i486\0i586\0i686"+((boot_cpu_data.x86-3)*5))
+#define ELF_PLATFORM  ("i386\0i486\0i586\0i686"+(((boot_cpu_data.x86>6?6:boot_cpu_data.x86)-3)*5))
 
 #ifdef __KERNEL__
 #define SET_PERSONALITY(ex, ibcs2) set_personality((ibcs2)?PER_SVR4:PER_LINUX)
index e41ca1da0ed60ccc5ec71c91e579244649100732..f8ebabe1b7d77cdddf70023336676c81071575f0 100644 (file)
@@ -16,6 +16,7 @@
 #include <asm/sigcontext.h>
 #include <asm/user.h>
 
+extern void init_fpu(void);
 /*
  * FPU lazy state save handling...
  */
index dcd555159c7dcbc9edbab552b48ed4348d244a97..ddc06ea014a6bc59c3045e42f12898144477a91f 100644 (file)
@@ -48,8 +48,8 @@ struct user_i387_struct {
        long    twd;
        long    fip;
        long    fcs;
-       long    fdp;
-       long    fds;
+       long    foo;
+       long    fos;
        long    st_space[20];   /* 8*10 bytes for each FP-reg = 80 bytes */
 };
 
index 202ebe6d9f31bb42483e79194fbf23ff988c7ba7..e3fcd317a2290869195d4ffb3d14efa3795b5147 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: pgtable.h,v 1.103 2000/10/16 14:32:49 anton Exp $ */
+/* $Id: pgtable.h,v 1.104 2000/10/19 00:50:16 davem Exp $ */
 #ifndef _SPARC_PGTABLE_H
 #define _SPARC_PGTABLE_H
 
index 1b5e4da9c4a0952285d3660358faa1f3138d677a..e16de8fb2bace794492ebae42533e5d7e984d861 100644 (file)
-/* $Id: envctrl.h,v 1.1 1998/05/16 17:26:07 ecd Exp $
+/* $Id: envctrl.h,v 1.2 2000/10/17 16:20:36 davem Exp $
  *
  * envctrl.h: Definitions for access to the i2c environment
  *            monitoring on Ultrasparc systems.
  *
  * Copyright (C) 1998  Eddie C. Dost  (ecd@skynet.be)
+ * Copyright (C) 2000  Vinh Truong  (vinh.truong@eng.sun.com)
+ * VT - Add all ioctl commands and environment status definitions 
+ * VT - Add application note 
  */
-
 #ifndef _SPARC64_ENVCTRL_H
 #define _SPARC64_ENVCTRL_H 1
 
 #include <linux/ioctl.h>
 
-#define I2CIOCSADR _IOW('p', 0x40, int)
-#define I2CIOCGADR _IOR('p', 0x41, int)
+/* Application note:
+ *
+ * The driver supports 4 operations: open(), close(), ioctl(), read()
+ * The device name is /dev/envctrl.
+ * Below is sample usage:
+ *
+ *     fd = open("/dev/envtrl", O_RDONLY);
+ *     if (ioctl(fd, ENVCTRL_READ_SHUTDOWN_TEMPERATURE, 0) < 0)
+ *             printf("error\n");
+ *     ret = read(fd, buf, 10);
+ *     close(fd);
+ *
+ * Notice in the case of cpu voltage and temperature, the default is
+ * cpu0.  If we need to know the info of cpu1, cpu2, cpu3, we need to
+ * pass in cpu number in ioctl() last parameter.  For example, to
+ * get the voltage of cpu2:
+ *
+ *     ioctlbuf[0] = 2;
+ *     if (ioctl(fd, ENVCTRL_READ_CPU_VOLTAGE, ioctlbuf) < 0)
+ *             printf("error\n");
+ *     ret = read(fd, buf, 10);
+ *
+ * All the return values are in ascii.  So check read return value
+ * and do appropriate conversions in your application.
+ */
+
+/* IOCTL commands */
+
+/* Note: these commands reflect possible monitor features.
+ * Some boards choose to support some of the features only.
+ */
+#define ENVCTRL_RD_CPU_TEMPERATURE     _IOR('p', 0x40, int)
+#define ENVCTRL_RD_CPU_VOLTAGE         _IOR('p', 0x41, int)
+#define ENVCTRL_RD_FAN_STATUS          _IOR('p', 0x42, int)
+#define ENVCTRL_RD_WARNING_TEMPERATURE _IOR('p', 0x43, int)
+#define ENVCTRL_RD_SHUTDOWN_TEMPERATURE        _IOR('p', 0x44, int)
+#define ENVCTRL_RD_VOLTAGE_STATUS      _IOR('p', 0x45, int)
+#define ENVCTRL_RD_SCSI_TEMPERATURE    _IOR('p', 0x46, int)
+#define ENVCTRL_RD_ETHERNET_TEMPERATURE        _IOR('p', 0x47, int)
+#define ENVCTRL_RD_MTHRBD_TEMPERATURE  _IOR('p', 0x48, int)
+
+/* Read return values for a voltage status request. */
+#define ENVCTRL_VOLTAGE_POWERSUPPLY_GOOD       0x01
+#define ENVCTRL_VOLTAGE_BAD                    0x02
+#define ENVCTRL_POWERSUPPLY_BAD                        0x03
+#define ENVCTRL_VOLTAGE_POWERSUPPLY_BAD                0x04
+
+/* Read return values for a fan status request.
+ * A failure match means either the fan fails or
+ * the fan is not connected.  Some boards have optional
+ * connectors to connect extra fans.
+ *
+ * There are maximum 8 monitor fans.  Some are cpu fans
+ * some are system fans.  The mask below only indicates
+ * fan by order number.
+ * Below is a sample application:
+ *
+ *     if (ioctl(fd, ENVCTRL_READ_FAN_STATUS, 0) < 0) {
+ *             printf("ioctl fan failed\n");
+ *     }
+ *     if (read(fd, rslt, 1) <= 0) {
+ *             printf("error or fan not monitored\n");
+ *     } else {
+ *             if (rslt[0] == ENVCTRL_ALL_FANS_GOOD) {
+ *                     printf("all fans good\n");
+ *     } else if (rslt[0] == ENVCTRL_ALL_FANS_BAD) {
+ *             printf("all fans bad\n");
+ *     } else {
+ *             if (rslt[0] & ENVCTRL_FAN0_FAILURE_MASK) {
+ *                     printf("fan 0 failed or not connected\n");
+ *     }
+ *     ......
+ */  
+
+#define ENVCTRL_ALL_FANS_GOOD                  0x00
+#define ENVCTRL_FAN0_FAILURE_MASK              0x01
+#define ENVCTRL_FAN1_FAILURE_MASK              0x02
+#define ENVCTRL_FAN2_FAILURE_MASK              0x04
+#define ENVCTRL_FAN3_FAILURE_MASK              0x08
+#define ENVCTRL_FAN4_FAILURE_MASK              0x10
+#define ENVCTRL_FAN5_FAILURE_MASK              0x20
+#define ENVCTRL_FAN6_FAILURE_MASK              0x40
+#define ENVCTRL_FAN7_FAILURE_MASK              0x80
+#define ENVCTRL_ALL_FANS_BAD                   0xFF
 
 #endif /* !(_SPARC64_ENVCTRL_H) */
index 6eb90b44c70ad02184217d0cc957f9c1eb877b42..62c4c4ef4a2e47f00d0edd125ec869d76df34797 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: pgtable.h,v 1.131 2000/08/11 03:00:14 davem Exp $
+/* $Id: pgtable.h,v 1.132 2000/10/19 00:50:16 davem Exp $
  * pgtable.h: SpitFire page table operations.
  *
  * Copyright 1996,1997 David S. Miller (davem@caip.rutgers.edu)
index f67d6be5261b4696dca8b9f1888c0256a4e1b650..59355eb8aa49b4eb815af2cd68bb0e684f0a2235 100644 (file)
@@ -95,6 +95,7 @@ struct vm_area_struct {
 
 #define VM_DONTCOPY    0x00020000      /* Do not copy this vma on fork */
 #define VM_DONTEXPAND  0x00040000      /* Cannot expand with mremap() */
+#define VM_RESERVED    0x00080000      /* Don't unmap it from swap_out */
 
 #define VM_STACK_FLAGS 0x00000177
 
index d6d8d841de42976113856988b7a06b747b567432..07bc8b261fadc29cf317b5239d2a0eaa745302b9 100644 (file)
@@ -274,6 +274,7 @@ enum
        NET_TCP_RMEM=85,
        NET_TCP_APP_WIN=86,
        NET_TCP_ADV_WIN_SCALE=87,
+       NET_IPV4_NONLOCAL_BIND=88,
 };
 
 enum {
index 2a1248effe7f3de1ce0637cf664cdd417e9269a5..d2b4e5a000932b6d8d2fad80417e0402130ac285 100644 (file)
@@ -135,6 +135,12 @@ void invalidate_inode_pages(struct inode * inode)
                if (TryLockPage(page))
                        continue;
 
+               /* Neither can we invalidate something in use.. */
+               if (page_count(page) != 1) {
+                       UnlockPage(page);
+                       continue;
+               }
+
                __lru_cache_del(page);
                __remove_inode_page(page);
                UnlockPage(page);
@@ -156,6 +162,7 @@ static inline void truncate_partial_page(struct page *page, unsigned partial)
 
 static inline void truncate_complete_page(struct page *page)
 {
+       /* Leave it on the LRU if it gets converted into anonymous buffers */
        if (!page->buffers || block_flushpage(page, 0))
                lru_cache_del(page);
 
@@ -167,6 +174,7 @@ static inline void truncate_complete_page(struct page *page)
         * all sorts of fun problems ...  
         */
        ClearPageDirty(page);
+       ClearPageUptodate(page);
        remove_inode_page(page);
        page_cache_release(page);
 }
index ccbe6de9ec540411e15b575f92b7d09a16ca6d8e..2edeb618e7f4149e43a5d0e593589bcef118911d 100644 (file)
@@ -87,6 +87,13 @@ static int try_to_swap_out(struct mm_struct * mm, struct vm_area_struct* vma, un
        if (TryLockPage(page))
                goto out_failed;
 
+       /* From this point on, the odds are that we're going to
+        * nuke this pte, so read and clear the pte.  This hook
+        * is needed on CPUs which update the accessed and dirty
+        * bits in hardware.
+        */
+       pte = ptep_get_and_clear(page_table);
+
        /*
         * Is the page already in the swap cache? If so, then
         * we can just drop our reference to it without doing
@@ -98,10 +105,6 @@ static int try_to_swap_out(struct mm_struct * mm, struct vm_area_struct* vma, un
        if (PageSwapCache(page)) {
                entry.val = page->index;
                swap_duplicate(entry);
-               if (pte_dirty(pte))
-                       BUG();
-               if (pte_write(pte))
-                       BUG();
                set_pte(page_table, swp_entry_to_pte(entry));
 drop_pte:
                UnlockPage(page);
@@ -112,13 +115,6 @@ drop_pte:
                goto out_failed;
        }
 
-       /* From this point on, the odds are that we're going to
-        * nuke this pte, so read and clear the pte.  This hook
-        * is needed on CPUs which update the accessed and dirty
-        * bits in hardware.
-        */
-       pte = ptep_get_and_clear(page_table);
-
        /*
         * Is it a clean page? Then it must be recoverable
         * by just paging it in again, and we can just drop
@@ -318,7 +314,7 @@ static int swap_out_vma(struct mm_struct * mm, struct vm_area_struct * vma, unsi
        unsigned long end;
 
        /* Don't swap out areas which are locked down */
-       if (vma->vm_flags & VM_LOCKED)
+       if (vma->vm_flags & (VM_LOCKED|VM_RESERVED))
                return 0;
 
        pgdir = pgd_offset(mm, address);
index 544800d7e9905bf6c3a62d6cf20ecf98651131c9..db4f5d626aa3e8265ade188c75e259cd40cc29dd 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             PF_INET protocol family socket handler.
  *
- * Version:    $Id: af_inet.c,v 1.116 2000/10/15 01:34:45 davem Exp $
+ * Version:    $Id: af_inet.c,v 1.118 2000/10/19 15:51:02 davem Exp $
  *
  * Authors:    Ross Biro, <bir7@leland.Stanford.Edu>
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -447,6 +447,9 @@ int inet_release(struct socket *sock)
        return(0);
 }
 
+/* It is off by default, see below. */
+int sysctl_ip_nonlocal_bind;
+
 static int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
        struct sockaddr_in *addr=(struct sockaddr_in *)uaddr;
@@ -464,6 +467,20 @@ static int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 
        chk_addr_ret = inet_addr_type(addr->sin_addr.s_addr);
 
+       /* Not specified by any standard per-se, however it breaks too
+        * many applications when removed.  It is unfortunate since
+        * allowing applications to make a non-local bind solves
+        * several problems with systems using dynamic addressing.
+        * (ie. your servers still start up even if your ISDN link
+        *  is temporarily down)
+        */
+       if (sysctl_ip_nonlocal_bind == 0 &&
+           addr->sin_addr.s_addr != INADDR_ANY &&
+           chk_addr_ret != RTN_LOCAL &&
+           chk_addr_ret != RTN_MULTICAST &&
+           chk_addr_ret != RTN_BROADCAST)
+               return -EADDRNOTAVAIL;
+
        snum = ntohs(addr->sin_port);
        if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
                return -EACCES;
@@ -753,14 +770,15 @@ int inet_shutdown(struct socket *sock, int how)
        }
 
        switch (sk->state) {
-       default:        
+       case TCP_CLOSE:
+               err = -ENOTCONN;
+               /* Hack to wake up other listeners, who can poll for
+                  POLLHUP, even on eg. unconnected UDP sockets -- RR */
+       default:
                sk->shutdown |= how;
                if (sk->prot->shutdown)
                        sk->prot->shutdown(sk, how);
                break;
-       case TCP_CLOSE:
-               err = -ENOTCONN;
-               break;
 
        /* Remaining two branches are temporary solution for missing
         * close() in multithreaded environment. It is _not_ a good idea,
index 81f20361a56697d49471c948aa561b3cd3cb10e7..9387778ee7da7eb89b81b9c30af4dfef5a84b6b0 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             RAW - implementation of IP "raw" sockets.
  *
- * Version:    $Id: raw.c,v 1.53 2000/08/09 11:59:04 davem Exp $
+ * Version:    $Id: raw.c,v 1.54 2000/10/18 18:04:23 davem Exp $
  *
  * Authors:    Ross Biro, <bir7@leland.Stanford.Edu>
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -653,7 +653,7 @@ int raw_get_info(char *buffer, char **start, off_t offset, int length)
                        if (sk->family != PF_INET)
                                continue;
                        pos += 128;
-                       if (pos < offset)
+                       if (pos <= offset)
                                continue;
                        get_raw_sock(sk, tmpbuf, i);
                        len += sprintf(buffer+len, "%-127s\n", tmpbuf);
index d9f05c671f919fd7033346cdcca5a804d1317ec4..68d536e108bf65de89144d6d0b182196c5b6735d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * sysctl_net_ipv4.c: sysctl interface to net IPV4 subsystem.
  *
- * $Id: sysctl_net_ipv4.c,v 1.46 2000/09/16 09:38:30 davem Exp $
+ * $Id: sysctl_net_ipv4.c,v 1.47 2000/10/19 15:51:02 davem Exp $
  *
  * Begun April 1, 1996, Mike Shaver.
  * Added /proc/sys/net/ipv4 directory entry (empty =) ). [MS]
@@ -15,6 +15,9 @@
 #include <net/route.h>
 #include <net/tcp.h>
 
+/* From af_inet.c */
+extern int sysctl_ip_nonlocal_bind;
+
 /* From icmp.c */
 extern int sysctl_icmp_echo_ignore_all;
 extern int sysctl_icmp_echo_ignore_broadcasts;
@@ -110,6 +113,9 @@ ctl_table ipv4_table[] = {
         {NET_IPV4_NO_PMTU_DISC, "ip_no_pmtu_disc",
          &ipv4_config.no_pmtu_disc, sizeof(int), 0644, NULL,
          &proc_dointvec},
+       {NET_IPV4_NONLOCAL_BIND, "ip_nonlocal_bind",
+        &sysctl_ip_nonlocal_bind, sizeof(int), 0644, NULL,
+        &proc_dointvec},
        {NET_IPV4_TCP_SYN_RETRIES, "tcp_syn_retries",
         &sysctl_tcp_syn_retries, sizeof(int), 0644, NULL, &proc_dointvec},
        {NET_TCP_SYNACK_RETRIES, "tcp_synack_retries",
index 082848dda1fae0cc670ac82e11c83b394aa84bb6..1a0f278b423979dfb086acd189d5c215348a84c2 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:    $Id: tcp_ipv4.c,v 1.217 2000/10/15 13:15:19 davem Exp $
+ * Version:    $Id: tcp_ipv4.c,v 1.218 2000/10/18 18:04:22 davem Exp $
  *
  *             IPv4 specific functions
  *
@@ -2097,7 +2097,7 @@ skip_listen:
                                                        continue;
 
                                                pos += TMPSZ;
-                                               if (pos < offset)
+                                               if (pos <= offset)
                                                        continue;
                                                get_openreq(sk, req, tmpbuf, num, uid);
                                                len += sprintf(buffer+len, "%-*s\n", TMPSZ-1, tmpbuf);
@@ -2129,7 +2129,7 @@ skip_listen:
                        if (!TCP_INET_FAMILY(sk->family))
                                continue;
                        pos += TMPSZ;
-                       if (pos < offset)
+                       if (pos <= offset)
                                continue;
                        get_tcp_sock(sk, tmpbuf, num);
                        len += sprintf(buffer+len, "%-*s\n", TMPSZ-1, tmpbuf);
@@ -2144,7 +2144,7 @@ skip_listen:
                        if (!TCP_INET_FAMILY(tw->family))
                                continue;
                        pos += TMPSZ;
-                       if (pos < offset)
+                       if (pos <= offset)
                                continue;
                        get_timewait_sock(tw, tmpbuf, num);
                        len += sprintf(buffer+len, "%-*s\n", TMPSZ-1, tmpbuf);
index 970fbfdde5ab9b720f11f2064d62a65a4ffc7a9c..e098b9ff25cca6f4db5d33a74b12026094c999a5 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             The User Datagram Protocol (UDP).
  *
- * Version:    $Id: udp.c,v 1.89 2000/10/03 07:29:01 anton Exp $
+ * Version:    $Id: udp.c,v 1.90 2000/10/18 18:04:22 davem Exp $
  *
  * Authors:    Ross Biro, <bir7@leland.Stanford.Edu>
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -1028,7 +1028,7 @@ int udp_get_info(char *buffer, char **start, off_t offset, int length)
                        if (sk->family != PF_INET)
                                continue;
                        pos += 128;
-                       if (pos < offset)
+                       if (pos <= offset)
                                continue;
                        get_udp_sock(sk, tmpbuf, i);
                        len += sprintf(buffer+len, "%-127s\n", tmpbuf);
index 2528e7c7ba62b89348425d9d08649d3e2b487d77..c243a6ec915ebde34952256b2b80b39ae2518254 100644 (file)
@@ -7,7 +7,7 @@
  *
  *     Adapted from linux/net/ipv4/af_inet.c
  *
- *     $Id: af_inet6.c,v 1.59 2000/10/15 01:34:45 davem Exp $
+ *     $Id: af_inet6.c,v 1.60 2000/10/19 01:05:34 davem Exp $
  *
  *     Fixes:
  *     Hideaki YOSHIFUJI       :       sin6_scope_id support
index 0b7e0025236ea8142ce47aaeb46049908dc4d9bc..43ef2e87d5daf4ac482862f0122b4008b3cb048e 100644 (file)
@@ -7,7 +7,7 @@
  *
  *     Adapted from linux/net/ipv4/raw.c
  *
- *     $Id: raw.c,v 1.40 2000/08/09 11:59:04 davem Exp $
+ *     $Id: raw.c,v 1.41 2000/10/18 18:04:23 davem Exp $
  *
  *     Fixes:
  *     Hideaki YOSHIFUJI       :       sin6_scope_id support
@@ -793,7 +793,7 @@ int raw6_get_info(char *buffer, char **start, off_t offset, int length)
                        if (sk->family != PF_INET6)
                                continue;
                        pos += LINE_LEN+1;
-                       if (pos < offset)
+                       if (pos <= offset)
                                continue;
                        get_raw6_sock(sk, tmpbuf, i);
                        len += sprintf(buffer+len, LINE_FMT, tmpbuf);
index 952e2476516aa5ac0f605312f113953363c9f411..b830884e88b6081733b1f60285d0a7aa84d130db 100644 (file)
@@ -5,7 +5,7 @@
  *     Authors:
  *     Pedro Roque             <roque@di.fc.ul.pt>     
  *
- *     $Id: tcp_ipv6.c,v 1.125 2000/08/09 11:59:04 davem Exp $
+ *     $Id: tcp_ipv6.c,v 1.126 2000/10/18 18:04:23 davem Exp $
  *
  *     Based on: 
  *     linux/net/ipv4/tcp.c
@@ -2009,7 +2009,7 @@ int tcp6_get_info(char *buffer, char **start, off_t offset, int length)
                                                if (req->class->family != PF_INET6)
                                                        continue;
                                                pos += LINE_LEN+1;
-                                               if (pos < offset)
+                                               if (pos <= offset)
                                                        continue;
                                                get_openreq6(sk, req, tmpbuf, num, uid);
                                                len += sprintf(buffer+len, LINE_FMT, tmpbuf);
@@ -2041,7 +2041,7 @@ int tcp6_get_info(char *buffer, char **start, off_t offset, int length)
                        if (sk->family != PF_INET6)
                                continue;
                        pos += LINE_LEN+1;
-                       if (pos < offset)
+                       if (pos <= offset)
                                continue;
                        get_tcp6_sock(sk, tmpbuf, num);
                        len += sprintf(buffer+len, LINE_FMT, tmpbuf);
@@ -2056,7 +2056,7 @@ int tcp6_get_info(char *buffer, char **start, off_t offset, int length)
                        if (tw->family != PF_INET6)
                                continue;
                        pos += LINE_LEN+1;
-                       if (pos < offset)
+                       if (pos <= offset)
                                continue;
                        get_timewait6_sock(tw, tmpbuf, num);
                        len += sprintf(buffer+len, LINE_FMT, tmpbuf);
index 4d95aa40be8c6f69c8d43c5176a1b8630a5df9a6..187db4414a1172717f7a94894e0b677df9e7c726 100644 (file)
@@ -7,7 +7,7 @@
  *
  *     Based on linux/ipv4/udp.c
  *
- *     $Id: udp.c,v 1.57 2000/09/18 05:59:48 davem Exp $
+ *     $Id: udp.c,v 1.58 2000/10/18 18:04:23 davem Exp $
  *
  *     Fixes:
  *     Hideaki YOSHIFUJI       :       sin6_scope_id support
@@ -956,7 +956,7 @@ int udp6_get_info(char *buffer, char **start, off_t offset, int length)
                        if (sk->family != PF_INET6)
                                continue;
                        pos += LINE_LEN+1;
-                       if (pos < offset)
+                       if (pos <= offset)
                                continue;
                        get_udp6_sock(sk, tmpbuf, i);
                        len += sprintf(buffer+len, LINE_FMT, tmpbuf);
index 906bcffbe303ac1abc7cadb51a65b033739899b5..31a3409330539ebfd146fa77d8207c34c6b2875c 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             PACKET - implements raw packet sockets.
  *
- * Version:    $Id: af_packet.c,v 1.44 2000/10/15 01:34:47 davem Exp $
+ * Version:    $Id: af_packet.c,v 1.45 2000/10/19 01:05:35 davem Exp $
  *
  * Authors:    Ross Biro, <bir7@leland.Stanford.Edu>
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
index 62e8b1b853283e9e41c16e9380ff059ddfd8ecbb..7603a9221e1f011423a3db6468747d3cc696227e 100644 (file)
@@ -8,7 +8,7 @@
  *             as published by the Free Software Foundation; either version
  *             2 of the License, or (at your option) any later version.
  *
- * Version:    $Id: af_unix.c,v 1.106 2000/10/15 01:34:48 davem Exp $
+ * Version:    $Id: af_unix.c,v 1.107 2000/10/19 01:05:36 davem Exp $
  *
  * Fixes:
  *             Linus Torvalds  :       Assorted bug cures.