Index: arch/arm/kernel/head-armv.S
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/arch/arm/kernel/head-armv.S,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- arch/arm/kernel/head-armv.S	20 Oct 2004 22:31:25 -0000	1.3
+++ arch/arm/kernel/head-armv.S	7 Jul 2006 18:10:43 -0000	1.4
@@ -184,19 +184,6 @@
 		mov32	r13, 0x10000
 		/* End of code for bootloader */
 
-		/*
-		 * Pointer to the Syscon base
-		 */
-		mov32	r0, SYSCON_BASE
-
-		/*
-		 * Setup the UARTBAUD bit in the PWRCNT register. This
-		 * enables the 14.7456MHz clock to the UARTs.  Everything
-		 * else in this register (DMA, USB, IRDA) is zeroed.
-		 */
-		mov32 	r1, 0x20000000
-		str	r1, [r0, #0x04]
- 
 #endif	
 		/* 
 		 * TODO - RedBoot does not set 'r1' with our architecture
Index: arch/arm/kernel/irq.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/arch/arm/kernel/irq.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- arch/arm/kernel/irq.c	30 Jun 2005 22:11:31 -0000	1.3
+++ arch/arm/kernel/irq.c	13 Jan 2009 19:28:09 -0000	1.4
@@ -30,8 +30,12 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/irq.h>
+#include <linux/poll.h>
 
 #include <asm/irq.h>
+#include <asm/poll.h>
 #include <asm/system.h>
 #include <asm/mach/irq.h>
 #include <asm/io.h>
@@ -40,6 +44,20 @@
 
 #include <asm/arch/irq.h>	/* pick up fixup_irq definition */
 
+static struct proc_dir_entry *root_irq_dir;
+
+#undef MAX_NAMELEN
+ 
+#define MAX_NAMELEN 10
+ 
+struct irq_proc {
+	unsigned long irq;
+	wait_queue_head_t q;
+	atomic_t count;
+	char devname[TASK_COMM_LEN];
+};
+
+
 /*
  * Maximum IRQ count.  Currently, this is arbitary.  However, it should
  * not be set too low to prevent false triggering.  Conversely, if it
@@ -665,8 +683,180 @@
 	return irq_found;
 }
 
+static void irq_proc_irq_handler(int irq, void *vidp, struct pt_regs *regs)
+{
+	struct irq_proc *idp = (struct irq_proc *)vidp;
+
+	BUG_ON(idp->irq != irq);
+	disable_irq_nosync(irq);
+	atomic_inc(&idp->count);
+	wake_up(&idp->q);
+	return ;
+}
+
+
+static int irq_proc_open(struct inode *inop, struct file *filp)
+{
+	struct irq_proc *ip;
+	struct proc_dir_entry *ent = PDE(inop);
+	int error;
+
+	ip = kmalloc(sizeof *ip, GFP_KERNEL);
+	if (ip == NULL)
+		return -ENOMEM;
+
+	memset(ip, 0, sizeof(*ip));
+	strcpy(ip->devname, current->comm);
+	init_waitqueue_head(&ip->q);
+	atomic_set(&ip->count, 0);
+	ip->irq = (unsigned long)ent->data;
+
+	error = request_irq(ip->irq,
+		irq_proc_irq_handler,
+		SA_INTERRUPT,
+		ip->devname,
+		ip);
+	if (error < 0) {
+		kfree(ip);
+		return error;
+	}
+	filp->private_data = (void *)ip;
+
+	return 0;
+}
+
+static int irq_proc_release(struct inode *inop, struct file *filp)
+{
+	struct irq_proc *ip = (struct irq_proc *)filp->private_data;
+
+	free_irq(ip->irq, ip);
+	filp->private_data = NULL;
+	kfree(ip);
+	return 0;
+}
+
+static unsigned int irq_proc_poll(struct file *filp, struct poll_table_struct *wait)
+{
+	struct irq_proc *ip = (struct irq_proc *)filp->private_data;
+
+//EWD: we get a warning for the following line(irq_desc_t vs irq_desc)
+	irq_desc_t *idp = irq_desc + ip->irq;
+
+	if (atomic_read(&ip->count) > 0)
+		return POLLIN | POLLRDNORM; /* readable */
+
+	/* if interrupts disabled and we don't have one to process... */
+	if (idp->status & IRQ_DISABLED)
+		enable_irq(ip->irq);
+
+	poll_wait(filp, &ip->q, wait);
+
+	if (atomic_read(&ip->count) > 0)
+		return POLLIN | POLLRDNORM; /* readable */
+
+	return 0;
+}
+
+
+/*
+ * Signal to userspace an interrupt has occured.
+ */
+static ssize_t irq_proc_read(struct file *filp, char  /*__user*/ *bufp, size_t len, loff_t *ppos)
+{
+	struct irq_proc *ip = (struct irq_proc *)filp->private_data;
+//EWD: we get a warning for the following line(irq_desc_t vs irq_desc)
+	irq_desc_t *idp = irq_desc + ip->irq;
+	int pending;
+
+	DECLARE_WAIT_QUEUE_HEAD(queue);
+	DECLARE_WAITQUEUE(wait, current);
+
+	if (len < sizeof(int))
+		return -EINVAL;
+
+	pending = atomic_read(&ip->count);
+	if (pending == 0) {
+		if (idp->status & IRQ_DISABLED)
+			enable_irq(ip->irq);
+			if (filp->f_flags & O_NONBLOCK)
+				return -EWOULDBLOCK;
+	}	
+
+	while (pending == 0) {
+		add_wait_queue(&queue, &wait);
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		pending = atomic_read(&ip->count);
+		if (pending == 0)
+			schedule();
+		remove_wait_queue(&queue, &wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		set_current_state(TASK_RUNNING);
+	}
+
+	if (copy_to_user(bufp, &pending, sizeof pending))
+		return -EFAULT;
+
+	*ppos += sizeof pending;
+
+	atomic_sub(pending, &ip->count);
+	return sizeof pending;
+}
+
+
+static struct file_operations irq_proc_file_operations = {
+	.read = irq_proc_read,
+	.open = irq_proc_open,
+	.release = irq_proc_release,
+	.poll = irq_proc_poll,
+};
+
+#define MAX_NAMELEN 10
+void register_irq_proc(unsigned int irq)
+{
+	struct proc_dir_entry *entry;
+	char name [MAX_NAMELEN];
+
+	if (!root_irq_dir)
+		return;
+
+	memset(name, 0, MAX_NAMELEN);
+	sprintf(name, "%d", irq);
+
+	if (!irq_desc[irq].dir) {
+		/* create /proc/irq/1234 */
+		irq_desc[irq].dir = proc_mkdir(name, root_irq_dir);
+
+		/*
+		* Create handles for user-mode interrupt handlers
+		* if the kernel hasn't already grabbed the IRQ
+		*/
+		entry = create_proc_entry("irq", 0600, irq_desc[irq].dir);
+		if (entry) {
+			entry->data = (void *)(unsigned long)irq;
+			entry->read_proc = NULL;
+			entry->write_proc = NULL;
+			entry->proc_fops = &irq_proc_file_operations;
+		}
+	}
+}
+
+
 void __init init_irq_proc(void)
 {
+	int i;
+
+	/* create /proc/irq */
+	root_irq_dir = proc_mkdir("irq", NULL);
+	if (!root_irq_dir)
+		return;
+
+	/*
+	* Create entries for all existing IRQs.
+	*/
+	for (i = 0; i < NR_IRQS; i++)
+		register_irq_proc(i);
 }
 
 void __init init_IRQ(void)
Index: arch/arm/mach-ep93xx/dma_ep93xx.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/arch/arm/mach-ep93xx/dma_ep93xx.c,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- arch/arm/mach-ep93xx/dma_ep93xx.c	22 Jul 2004 19:48:59 -0000	1.1.1.1
+++ arch/arm/mach-ep93xx/dma_ep93xx.c	21 Feb 2007 16:59:36 -0000	1.3
@@ -1,3533 +1,3600 @@
-/******************************************************************************
- * arch/arm/mach-ep9312/dma_ep93xx.c
- *
- * Support functions for the ep93xx internal DMA channels.
- * (see also Documentation/arm/ep93xx/dma.txt)
- *
- * Copyright (C) 2003  Cirrus Logic
- *
- * A large portion of this file is based on the dma api implemented by
- * Nicolas Pitre, dma-sa1100.c, copyrighted 2000.
- * 
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- ****************************************************************************/
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/delay.h>
-
-#include <asm/system.h>
-#include <asm/irq.h>
-#include <asm/hardware.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/mach/dma.h>
-#include "dma_ep93xx.h"
-
-/*****************************************************************************
- *
- * Debugging macros
- *
- ****************************************************************************/
-#undef DEBUG
-/*#define DEBUG   1 */
-#ifdef DEBUG
-#define DPRINTK( x... )  printk( ##x )
-#else
-#define DPRINTK( x... )
-#endif
-
-/*****************************************************************************
- *
- * static global variables
- *
- ****************************************************************************/
-ep93xx_dma_t dma_chan[MAX_EP93XX_DMA_CHANNELS];
-
-/*
- *  Maximum physical DMA buffer size
- */
-#define MAX_DMA_SIZE        0xffff
-#define MAX_DMA_ORDER       12
-
-/*
- *  lock used to protect the list of dma channels while searching for a free
- *  channel during dma_request.
- */
-static spinlock_t dma_list_lock;
-
-/*****************************************************************************
- *
- *  Internal DMA processing functions.
- *
- ****************************************************************************/
-/*****************************************************************************
- *
- *  get_dma_channel_from_handle()
- *
- *  If Handle is valid, returns the DMA channel # (0 to 9 for channels 1-10)
- *  If Handle is not valid, returns -1.
- *
- ****************************************************************************/
-static int
-dma_get_channel_from_handle(int handle)
-{
-    int channel;  
-    
-    /*
-     *  Get the DMA channel # from the handle.
-     */
-    channel = ((int)handle & DMA_HANDLE_SPECIFIER_MASK) >> 28;
-    
-    /*
-     *  See if this is a valid handle.
-     */
-    if(dma_chan[channel].last_valid_handle != (int)handle)
-    {
-        DPRINTK("DMA ERROR - invalid handle 0x%x \n", handle);
-        /*
-         *  Fail, invalid handle
-         */ 
-        return(-1);
-    }
-    
-    /*
-     *  See if this instance is still open
-     */    
-    if(!dma_chan[channel].ref_count )
-    {
-        /*
-         *  Fail, instance closed
-         */ 
-        return(-1);
-    } 
-
-    return(channel);
-}
-
-/*****************************************************************************
- *
- * dma_m2m_irq_handler
- *
- * TODO: need to add support for M2M interrupt handling!!!
- *
- ****************************************************************************/
-static void
-dma_m2m_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
-{
-    ep93xx_dma_t *dma = (ep93xx_dma_t *)dev_id;
-    //go DMAM2M volatile * dma_regs = (DMAM2M *)dma->reg_base;
-    unsigned int M2M_reg_base = dma->reg_base;
-    ep93xx_dma_dev_t dma_int = UNDEF_INT;
-    unsigned int read_back, loop, uiCONTROL, uiINTERRUPT;
-    
-    /*
-     *  Determine what kind of dma interrupt this is.
-     */
-    //go if(dma_regs->INTERRUPT.Field.STALLINT)
-    //go else if(dma_regs->INTERRUPT.Field.NFBINT)
-    //go else if(dma_regs->INTERRUPT.Field.DONEINT)
-
-    if( inl(M2M_reg_base+M2M_OFFSET_INTERRUPT) & INTERRUPT_M2M_STALLINT )
-        dma_int = STALL;
-    else if( inl(M2M_reg_base+M2M_OFFSET_INTERRUPT) & INTERRUPT_M2M_NFBINT )
-        dma_int = NFB;
-    else if( inl(M2M_reg_base+M2M_OFFSET_INTERRUPT) & INTERRUPT_M2M_DONEINT )
-        dma_int = DONE;
-        
-    DPRINTK("IRQ: b=%#x st=%#x\n", (int)dma->current_buffer->source, dma_int);
-    
-    /*
-     * Stall Interrupt: The Channel is stalled, meaning nothing is 
-     * programmed to transfer right now.  So, we're back to the 
-     * beginnning.  If there's a buffer to transfer, program it into
-     * max and base 0 registers.
-     */
-    if(dma_int == STALL)
-    {            
-        DPRINTK("1  ");
-        
-        if(dma->total_buffers)
-        {
-            /*
-             *  The current_buffer has already been tranfered, so add the 
-             *  byte count to the total_bytes field.
-             */
-            dma->total_bytes = dma->total_bytes +
-                dma->buffer_queue[dma->current_buffer].size;
-        
-            /*
-             *  Mark the current_buffer as used.
-             */
-            dma->buffer_queue[dma->current_buffer].used = TRUE;
-    
-            /*
-             *  Increment the used buffer counter
-             */
-            dma->used_buffers++;
-            
-            DPRINTK("#%d", dma->current_buffer);
-            
-            /*
-             *  Increment the current_buffer
-             */
-            dma->current_buffer = (dma->current_buffer + 1) %
-                                  MAX_EP93XX_DMA_BUFFERS;
-
-            /*
-             *  check if there's a new buffer to transfer.  
-             */
-            if(dma->new_buffers && dma->xfer_enable)
-            {   
-                /*
-                 *  We have a new buffer to transfer so program in the buffer
-                 *  values.  Since a STALL interrupt was triggered, we
-                 *  program the buffer descriptor 0
-                 *  
-                 *  Set the SAR_BASE/DAR_BASE/BCR registers with values from the
-                 *  next buffer in the queue.
-                 */
-                //go dma_regs->SAR_BASE0.Field.SAR_BASE0 = 
-                //go  dma->buffer_queue[dma->current_buffer].source;
-                outl( dma->buffer_queue[dma->current_buffer].source,
-                      M2M_reg_base + M2M_OFFSET_SAR_BASE0 );
-
-                //go dma_regs->DAR_BASE0.Field.DAR_BASE0 = 
-                //go  dma->buffer_queue[dma->current_buffer].dest;
-                outl( dma->buffer_queue[dma->current_buffer].dest,
-                      M2M_reg_base + M2M_OFFSET_DAR_BASE0 );
-                      
-                //go dma_regs->BCR0.Field.BCR0 =
-                //go  dma->buffer_queue[dma->current_buffer].size;
-                outl( dma->buffer_queue[dma->current_buffer].size,
-                      M2M_reg_base + M2M_OFFSET_BCR0 );
-                
-                /*
-                 *  Decrement the new buffer counter
-                 */
-                dma->new_buffers--;
-                
-                /*
-                 *  If there's a second new buffer, we program the second
-                 *  buffer descriptor.
-                 */
-                if(dma->new_buffers)
-                {
-                    DPRINTK("A  ");
-                    
-                    /*  
-                     *  Set the SAR_BASE/DAR_BASE/BCR registers with values from the
-                     *  next buffer in the queue.
-                     */
-                    //go dma_regs->SAR_BASE1.Field.SAR_BASE1 = 
-                    //go  dma->buffer_queue[(dma->current_buffer + 1) %
-                    //go  MAX_EP93XX_DMA_BUFFERS].source;
-                    
-                    outl( dma->buffer_queue[(dma->current_buffer + 1) %
-                          MAX_EP93XX_DMA_BUFFERS].source,
-                          M2M_reg_base+M2M_OFFSET_SAR_BASE1 );
-                        
-                    //go dma_regs->DAR_BASE0.Field.DAR_BASE0 = 
-                    //go  dma->buffer_queue[(dma->current_buffer + 1) %
-                    //go  MAX_EP93XX_DMA_BUFFERS].dest;
-                
-                    outl( dma->buffer_queue[(dma->current_buffer + 1) %
-                          MAX_EP93XX_DMA_BUFFERS].dest,
-                          M2M_reg_base+M2M_OFFSET_DAR_BASE0 );
-
-                    //go dma_regs->BCR0.Field.BCR0 =
-                    //go  dma->buffer_queue[(dma->current_buffer + 1) %
-                    //go  MAX_EP93XX_DMA_BUFFERS].size;
-                                    
-                    outl( dma->buffer_queue[(dma->current_buffer + 1) %
-                          MAX_EP93XX_DMA_BUFFERS].size,
-                          M2M_reg_base+M2M_OFFSET_BCR0 );
-
-                    /*
-                     *  Decrement the new buffer counter
-                     */
-                    dma->new_buffers--;
-                    
-                    /*
-                     *  Enable the NFB Interrupt.
-                     */
-                    //go dma_regs->CONTROL.Field.NFBINTEN = 1;
-                    uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-                    uiCONTROL |= CONTROL_M2M_NFBINTEN;
-                    outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-                }
-            }
-            /*
-             *  Read my lips.  No new buffers.
-             */
-            else
-            {
-                DPRINTK("2 \n");
-                /*
-                 *  There's a chance we setup both buffer descriptors, but
-                 *  didn't service the NFB quickly enough, causing the channel
-                 *  to transfer both buffers, then enter the stall state.
-                 *  So, we need to be able to process the second buffer.
-                 */
-                if((dma->used_buffers + dma->new_buffers) < dma->total_buffers)
-                {
-                    DPRINTK("3 ");
-                    
-                    /*
-                     *  The current_buffer has already been tranfered, so add the 
-                     *  byte count to the total_bytes field.
-                     */
-                    dma->total_bytes = dma->total_bytes +
-                        dma->buffer_queue[dma->current_buffer].size;
-        
-                    /*
-                     *  Mark the current_buffer as used.
-                     */
-                    dma->buffer_queue[dma->current_buffer].used = TRUE;
-    
-                    /*
-                     *  Increment the used buffer counter
-                     */
-                    dma->used_buffers++;
-                    
-                    DPRINTK("#%d", dma->current_buffer);
-                    
-                    /*
-                     *  Increment the current buffer pointer.
-                     */
-                    dma->current_buffer = (dma->current_buffer + 1) %
-                                          MAX_EP93XX_DMA_BUFFERS;
-                    
-                }
-                
-                /*
-                 *  No new buffers to transfer, so disable the channel.
-                 */
-                //go dma_regs->CONTROL.Field.ENABLE = 0;
-                uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-                uiCONTROL &= ~CONTROL_M2M_ENABLE;
-                outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-
-                /*
-                 *  Indicate that this channel is in the pause by starvation
-                 *  state by setting the pause bit to true.
-                 */
-                dma->pause = TRUE;
-                
-                DPRINTK("End of stall handling. \n");
-                DPRINTK("STATUS - 0x%x \n",     inl(M2M_reg_base+M2M_OFFSET_STATUS) );
-                DPRINTK("CONTROL - 0x%x \n",    inl(M2M_reg_base+M2M_CONTROL.Value) );
-                DPRINTK("REMAIN - 0x%x \n",     inl(M2M_reg_base+M2M_REMAIN.Value) );
-                DPRINTK("PPALLOC - 0x%x \n",    inl(M2M_reg_base+M2M_PPALLOC.Value) );
-                DPRINTK("BASE0 - 0x%x \n",      inl(M2M_reg_base+M2M_BASE0.Value) );
-                DPRINTK("MAXCNT0 - 0x%x \n",    inl(M2M_reg_base+M2M_MAXCNT0.Value) );
-                DPRINTK("CURRENT0 - 0x%x \n",   inl(M2M_reg_base+M2M_CURRENT0.Value) );
-                DPRINTK("BASE1 - 0x%x \n",      inl(M2M_reg_base+M2M_BASE1.Value) );
-                DPRINTK("MAXCNT1 - 0x%x \n",    inl(M2M_reg_base+M2M_MAXCNT1.Value) );
-                DPRINTK("CURRENT1 - 0x%x \n",   inl(M2M_reg_base+M2M_CURRENT1.Value) );
-    
-                DPRINTK("Buffer    buf_id     source       size       last       used \n");
-                for(loop = 0; loop < 32; loop ++)
-                {
-                DPRINTK("%d        0x%x        0x%x         0x%x        %d         %d \n",
-                        loop, dma->buffer_queue[loop].buf_id, dma->buffer_queue[loop].source, 
-                        dma->buffer_queue[loop].size,
-                        dma->buffer_queue[loop].last, dma->buffer_queue[loop].used);
-                }
-                DPRINTK("pause     0x%x        0x%x         0x%x        %d         %d \n",
-                        dma->pause_buf.buf_id, dma->pause_buf.source, dma->pause_buf.size,
-                        dma->pause_buf.last, dma->pause_buf.used);
-    
-                DPRINTK("Pause - %d \n", dma->pause);
-                DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
-                DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
-                DPRINTK("total buffer - %d \n", dma->total_buffers);
-                DPRINTK("new buffers - %d \n", dma->new_buffers);
-                DPRINTK("current buffer - %d \n", dma->current_buffer);
-                DPRINTK("last buffer - %d \n", dma->last_buffer);
-                DPRINTK("used buffers - %d \n", dma->used_buffers);
-                DPRINTK("callback addr - 0x%x \n", dma->callback);
-            }
-        }
-        /*
-         *  Total number of buffers in the queue is 0.
-         */
-        else
-        {
-            /*
-             *  No buffers to transfer, or old buffers to mark as used, 
-             *  so Disable the channel
-             */
-            //go dma_regs->CONTROL.Field.ENABLE = 0;
-            uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-            uiCONTROL &= ~CONTROL_M2M_ENABLE;
-            outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-
-            /*
-             *  Must read the control register back after a write.
-             */
-            //go read_back = dma_regs->CONTROL.Value;
-            read_back = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-
-            /*
-             *  Indicate that this channel is in the pause by
-             *  starvation state by setting the pause bit to true.
-             */
-            dma->pause = TRUE;
-        } 
-    }
-    /*
-     *  Next Frame Buffer Interrupt.  If there's a new buffer program it  
-     *  Check if this is the last buffer in the transfer,
-     *  and if it is, disable the NFB int to prevent being
-     *  interrupted for another buffer when we know there won't be 
-     *  another.
-     */
-    if(dma_int == NFB)
-    {
-        DPRINTK("5  ");
-        
-        if(dma->total_buffers)
-        {
-            DPRINTK("6  ");
-            /*
-             *  The iCurrentBuffer has already been transfered.  so add the 
-             *  byte count from the current buffer to the total byte count.
-             */
-            dma->total_bytes = dma->total_bytes +
-                               dma->buffer_queue[dma->current_buffer].size;
-
-            /*
-             *  Mark the Current Buffer as used.
-             */
-            dma->buffer_queue[dma->current_buffer].used = TRUE;
-
-            /* 
-             *  Increment the used buffer counter
-             */
-            dma->used_buffers++;
-            
-            DPRINTK("#%d", dma->current_buffer);
-            
-            if((dma->buffer_queue[
-                (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].last) ||
-               (dma->new_buffers == 0) || (dma->xfer_enable == FALSE))
-            {
-                DPRINTK("7  "); 
-                
-                /*
-                 *  This is the last Buffer in this transaction, so disable
-                 *  the NFB interrupt.  We shouldn't get an NFB int when the
-                 *  FSM moves to the ON state where it would typically get the
-                 *  NFB int indicating a new buffer can be programmed.
-                 *  Instead, once in the ON state, the DMA will just proceed
-                 *  to complet the transfer of the current buffer, move the
-                 *  FSB directly to the STALL state where a STALL interrupt
-                 *  will be generated.
-                 */
-                //go dma_regs->CONTROL.Field.NFBINTEN = 0;
-                uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-                uiCONTROL &= ~CONTROL_M2M_NFBINTEN ;
-                outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-
-                /*
-                 *  The current buffer has been transferred, so increment
-                 *  the current buffer counter to reflect this.
-                 */
-                dma->current_buffer = (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS;
-                
-                DPRINTK("End of NFB handling. \n");
-                DPRINTK("STATUS - 0x%x \n",     inl(M2M_reg_base+M2M_OFFSET_STATUS) );
-                DPRINTK("CONTROL - 0x%x \n",    inl(M2M_reg_base+M2M_OFFSET_CONTROL) );
-                DPRINTK("REMAIN - 0x%x \n",     inl(M2M_reg_base+M2M_OFFSET_REMAIN) );
-                DPRINTK("PPALLOC - 0x%x \n",    inl(M2M_reg_base+M2M_OFFSET_PPALLOC) );
-                DPRINTK("BASE0 - 0x%x \n",      inl(M2M_reg_base+M2M_OFFSET_BASE0) );
-                DPRINTK("MAXCNT0 - 0x%x \n",    inl(M2M_reg_base+M2M_OFFSET_MAXCNT0) );
-                DPRINTK("CURRENT0 - 0x%x \n",   inl(M2M_reg_base+M2M_OFFSET_CURRENT0) );
-                DPRINTK("BASE1 - 0x%x \n",      inl(M2M_reg_base+M2M_OFFSET_BASE1) );
-                DPRINTK("MAXCNT1 - 0x%x \n",    inl(M2M_reg_base+M2M_OFFSET_MAXCNT1) );
-                DPRINTK("CURRENT1 - 0x%x \n",   inl(M2M_reg_base+M2M_OFFSET_CURRENT1) );
-    
-                DPRINTK("Buffer    buf_id     source       size       last       used \n");
-                for(loop = 0; loop < 32; loop ++)
-                {
-                DPRINTK("%d        0x%x        0x%x         0x%x        %d         %d \n",
-                        loop, dma->buffer_queue[loop].buf_id, dma->buffer_queue[loop].source, 
-                        dma->buffer_queue[loop].size,
-                        dma->buffer_queue[loop].last, dma->buffer_queue[loop].used);
-                }
-                DPRINTK("pause     0x%x        0x%x         0x%x        %d         %d \n",
-                        dma->pause_buf.buf_id, dma->pause_buf.source, dma->pause_buf.size,
-                        dma->pause_buf.last, dma->pause_buf.used);
-    
-                DPRINTK("Pause - %d \n", dma->pause);
-                DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
-                DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
-                DPRINTK("total buffer - %d \n", dma->total_buffers);
-                DPRINTK("new buffers - %d \n", dma->new_buffers);
-                DPRINTK("current buffer - %d \n", dma->current_buffer);
-                DPRINTK("last buffer - %d \n", dma->last_buffer);
-                DPRINTK("used buffers - %d \n", dma->used_buffers);
-                DPRINTK("callback addr - 0x%x \n", dma->callback);
-                
-            }
-            else if(dma->new_buffers)
-            {
-                DPRINTK("8  ");
-                /* 
-                 *  we have a new buffer, so increment the current buffer to
-                 *  point to the next buffer, which is already programmed into
-                 *  the DMA. Next time around, it'll be pointing to the
-                 *  current buffer.
-                 */
-                dma->current_buffer = (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS;
-        
-                /*
-                 *  we know we have a new buffer to program as the next
-                 *  buffer, so check which set of SAR_BASE/DAR_BASE/BCR registers
-                 *  to program.  
-                 */
-                //go if(dma_regs->STATUS.Field.NB)
-                if( inl(M2M_reg_base+M2M_OFFSET_STATUS) & STATUS_M2M_NB )
-                {
-                    /*
-                     *  Set the SAR_BASE1/DAR_BASE1/BCR1 registers with values from the
-                     *  next buffer in the queue.
-                     */
-                    //go dma_regs->SAR_BASE1.Field.SAR_BASE1 = dma->buffer_queue[
-                    //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source;
-
-                    outl( dma->buffer_queue[(dma->current_buffer + 1) % 
-                            MAX_EP93XX_DMA_BUFFERS].source,
-                         M2M_reg_base+M2M_OFFSET_SAR_BASE1 );
-                         
-                    //go dma_regs->DAR_BASE1.Field.DAR_BASE1 = dma->buffer_queue[
-                    //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].dest;
-                    
-                    outl( dma->buffer_queue[(dma->current_buffer + 1) % 
-                            MAX_EP93XX_DMA_BUFFERS].dest,
-                         M2M_reg_base+M2M_OFFSET_DAR_BASE1 );
-                         
-                    //go dma_regs->BCR1.Field.BCR1 = dma->buffer_queue[
-                    //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size;
-                    
-                    outl( dma->buffer_queue[(dma->current_buffer + 1) % 
-                            MAX_EP93XX_DMA_BUFFERS].size,
-                         M2M_reg_base+M2M_OFFSET_BCR1 );
-                }
-                else
-                {
-                    /*
-                     *  Set the SAR_BASE0/DAR_BASE0/BCR0 registers with values from the
-                     *  next buffer in the queue.
-                     */
-                    //go dma_regs->SAR_BASE0.Field.SAR_BASE0 = dma->buffer_queue[
-                    //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source;
-
-                    outl( dma->buffer_queue[(dma->current_buffer + 1) % 
-                            MAX_EP93XX_DMA_BUFFERS].source,
-                         M2M_reg_base+M2M_OFFSET_SAR_BASE0 );
-                         
-                    //go dma_regs->DAR_BASE0.Field.DAR_BASE0 = dma->buffer_queue[
-                    //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].dest;
-                    
-                    outl( dma->buffer_queue[(dma->current_buffer + 1) % 
-                            MAX_EP93XX_DMA_BUFFERS].dest,
-                         M2M_reg_base+M2M_OFFSET_DAR_BASE0 );
-                         
-                    //go dma_regs->BCR0.Field.BCR0 = dma->buffer_queue[
-                    //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size;
-
-                    outl( dma->buffer_queue[(dma->current_buffer + 1) % 
-                            MAX_EP93XX_DMA_BUFFERS].size,
-                         M2M_reg_base+M2M_OFFSET_BCR0 );
-                }
-                
-                /*
-                 *  Decrement the new buffers counter
-                 */
-                dma->new_buffers--;
-            }
-        }
-        /*
-         *  Total number of buffers is 0 - really we should never get here,
-         *  but just in case.
-         */ 
-        else
-        {
-            DPRINTK("9 \n");
-            
-            /*
-             *  No new buffers to transfer, so Disable the channel
-             */
-            //go dma_regs->CONTROL.Field.ENABLE = 0;
-            uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-            uiCONTROL &= ~CONTROL_M2M_ENABLE;
-            outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-            
-            /*
-             *  Indicate that the channel is paused by starvation.
-             */
-            dma->pause = 1;
-        }        
-    }
-                
-    /* 
-     *  Done interrupt generated, indicating that the transfer is complete.
-     */
-    if(dma_int == DONE)
-    {
-        /*
-         *  Clear the interrupt.
-         */
-        //go dma_regs->INTERRUPT.Field.DONEINT = 0;
-        uiINTERRUPT = inl(M2M_reg_base+M2M_OFFSET_INTERRUPT);
-        uiINTERRUPT &= ~INTERRUPT_M2M_DONEINT;
-        outl( uiINTERRUPT, M2M_reg_base+M2M_OFFSET_INTERRUPT );
-        
-        /*
-         *  Disable the channel.
-         */
-        //go dma_regs->CONTROL.Field.ENABLE = 0;
-        uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-        uiCONTROL &= ~CONTROL_M2M_ENABLE;
-        outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-        
-        /*
-         *  Indicate that we're in the stopped mode.
-         */
-        dma->pause = 1;
-        dma->xfer_enable = 0;
-    }
-
-    /*
-     *  Make sure the interrupt was valid, and if it was, then check
-     *  if a callback function was installed for this DMA channel.  If a
-     *  callback was installed call it.
-     */  
-    if((dma_int != UNDEF_INT) && dma->callback)
-    {
-        dma->callback(dma_int, dma->device, dma->user_data);
-    }    
-}
-
-/*****************************************************************************
- *
- * dma_m2p_irq_handler
- *
- * 
- *
- ****************************************************************************/
-static void
-dma_m2p_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
-{
-    ep93xx_dma_t *dma = (ep93xx_dma_t *) dev_id;
-    //go DMAM2P volatile * dma_regs = (DMAM2P *)dma->reg_base;
-    unsigned int M2P_reg_base = dma->reg_base;
-    unsigned int read_back;
-    ep93xx_dma_dev_t dma_int = UNDEF_INT;
-    unsigned int loop, uiCONTROL, uiINTERRUPT;
-    
-    /*
-     *  Determine what kind of dma interrupt this is.
-     */
-    //go if(dma_regs->INTERRUPT.Field.STALLINT)
-    //go else if(dma_regs->INTERRUPT.Field.NFBINT)
-    //go else if(dma_regs->INTERRUPT.Field.CHERRORINT)
-
-    if( inl(M2P_reg_base+M2P_OFFSET_INTERRUPT) & INTERRUPT_M2P_STALLINT )
-        dma_int = STALL;
-    else if( inl(M2P_reg_base+M2P_OFFSET_INTERRUPT) & INTERRUPT_M2P_NFBINT )
-        dma_int = NFB;
-    else if( inl(M2P_reg_base+M2P_OFFSET_INTERRUPT) & INTERRUPT_M2P_CHERRORINT )
-        dma_int = CHERROR;
-
-    /*
-     *  Stall Interrupt: The Channel is stalled, meaning nothing is 
-     *  programmed to transfer right now.  So, we're back to the 
-     *  beginnning.  If there's a buffer to transfer, program it into
-     *  max and base 0 registers.
-     */
-    if(dma_int == STALL)
-    {            
-        DPRINTK("1  ");
-        
-        if(dma->total_buffers)
-        {
-            /*
-             *  The current_buffer has already been tranfered, so add the 
-             *  byte count to the total_bytes field.
-             */
-            dma->total_bytes = dma->total_bytes +
-                dma->buffer_queue[dma->current_buffer].size;
-        
-            /*
-             *  Mark the current_buffer as used.
-             */
-            dma->buffer_queue[dma->current_buffer].used = TRUE;
-    
-            /*
-             *  Increment the used buffer counter
-             */
-            dma->used_buffers++;
-            
-            DPRINTK("#%d", dma->current_buffer);
-            
-            /*
-             *  Increment the current_buffer
-             */
-            dma->current_buffer = (dma->current_buffer + 1) %
-                                  MAX_EP93XX_DMA_BUFFERS;
-
-            /*
-             *  check if there's a new buffer to transfer.  
-             */
-            if(dma->new_buffers && dma->xfer_enable)
-            {   
-                /*
-                 *  We have a new buffer to transfer so program in the buffer
-                 *  values.  Since a STALL interrupt was triggered, we
-                 *  program the base0 and maxcnt0
-                 *  
-                 *  Set the MAXCNT0 register with the buffer size
-                 */
-                //go dma_regs->MAXCNT0.Field.MAXCNT0 = 
-                //go  dma->buffer_queue[dma->current_buffer].size;
-                outl( dma->buffer_queue[dma->current_buffer].size,
-                    M2P_reg_base+M2P_OFFSET_MAXCNT0 );
-
-                /*
-                 *  Set the BASE0 register with the buffer base address
-                 */
-                //go dma_regs->BASE0.Field.BASE0 = 
-                //go  dma->buffer_queue[dma->current_buffer].source;
-                outl( dma->buffer_queue[dma->current_buffer].source,
-                    M2P_reg_base+M2P_OFFSET_BASE0 );
-                
-                /*
-                 *  Decrement the new buffer counter
-                 */
-                dma->new_buffers--;
-                
-                if(dma->new_buffers)
-                {
-                    DPRINTK("A  ");
-                    /*  
-                     *  Set the MAXCNT1 register with the buffer size
-                     */
-                    //go dma_regs->MAXCNT1.Field.MAXCNT1 = 
-                    //go  dma->buffer_queue[(dma->current_buffer + 1) %
-                    //go              MAX_EP93XX_DMA_BUFFERS].size;
-                    outl( dma->buffer_queue[(dma->current_buffer + 1) %
-                                      MAX_EP93XX_DMA_BUFFERS].size,
-                          M2P_reg_base+M2P_OFFSET_MAXCNT1 );
-                          
-                    /*
-                     *  Set the BASE1 register with the buffer base address
-                     */
-                    //go dma_regs->BASE1.Field.BASE1 = 
-                    //go  dma->buffer_queue[dma->current_buffer + 1 %
-                    //go              MAX_EP93XX_DMA_BUFFERS].source;
-                    outl( dma->buffer_queue[dma->current_buffer + 1 %
-                                      MAX_EP93XX_DMA_BUFFERS].source,
-                        M2P_reg_base+M2P_OFFSET_BASE1 );
-                        
-                    /*
-                     *  Decrement the new buffer counter
-                     */
-                    dma->new_buffers--;
-                    
-                    /*
-                     *  Enable the NFB Interrupt.
-                     */
-                    //go dma_regs->CONTROL.Field.NFBINTEN = 1;
-                    uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
-                    uiCONTROL |= CONTROL_M2P_NFBINTEN;
-                    outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
-                }
-            }
-            /*
-             *  No new buffers.
-             */
-            else
-            {
-                DPRINTK("2 \n");
-                /*
-                 *  There's a chance we setup both buffer descriptors, but
-                 *  didn't service the NFB quickly enough, causing the channel
-                 *  to transfer both buffers, then enter the stall state.
-                 *  So, we need to be able to process the second buffer.
-                 */
-                if((dma->used_buffers + dma->new_buffers) < dma->total_buffers)
-                {
-                    DPRINTK("3 ");
-                    
-                    /*
-                     *  The current_buffer has already been tranfered, so add the 
-                     *  byte count to the total_bytes field.
-                     */
-                    dma->total_bytes = dma->total_bytes +
-                        dma->buffer_queue[dma->current_buffer].size;
-        
-                    /*
-                     *  Mark the current_buffer as used.
-                     */
-                    dma->buffer_queue[dma->current_buffer].used = TRUE;
-    
-                    /*
-                     *  Increment the used buffer counter
-                     */
-                    dma->used_buffers++;
-                    
-                    DPRINTK("#%d", dma->current_buffer);
-                    
-                    /*
-                     *  Increment the current buffer pointer.
-                     */
-                    dma->current_buffer = (dma->current_buffer + 1) %
-                                          MAX_EP93XX_DMA_BUFFERS;
-                    
-                }
-                
-                /*
-                 *  No new buffers to transfer, so disable the channel.
-                 */
-                //go dma_regs->CONTROL.Field.ENABLE = 0;
-                uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
-                uiCONTROL &= ~CONTROL_M2P_ENABLE;
-                outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
-
-                /*
-                 *  Indicate that this channel is in the pause by starvation
-                 *  state by setting the pause bit to true.
-                 */
-                dma->pause = TRUE;
-                
-                DPRINTK("STATUS - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_STATUS) );
-                DPRINTK("CONTROL - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_CONTROL) );
-                DPRINTK("REMAIN - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_REMAIN) );
-                DPRINTK("PPALLOC - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_PPALLOC) );
-                DPRINTK("BASE0 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE0) );
-                DPRINTK("MAXCNT0 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT0) );
-                DPRINTK("CURRENT0 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT0) );
-                DPRINTK("BASE1 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE1) );
-                DPRINTK("MAXCNT1 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT1) );
-                DPRINTK("CURRENT1 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
-    
-                DPRINTK("Buffer    buf_id     source       size       last       used \n");
-                for(loop = 0; loop < 32; loop ++)
-                {
-                DPRINTK("%d        0x%x        0x%x         0x%x        %d         %d \n",
-                        loop, dma->buffer_queue[loop].buf_id, dma->buffer_queue[loop].source, 
-                        dma->buffer_queue[loop].size,
-                        dma->buffer_queue[loop].last, dma->buffer_queue[loop].used);
-                }
-                DPRINTK("pause     0x%x        0x%x         0x%x        %d         %d \n",
-                        dma->pause_buf.buf_id, dma->pause_buf.source, dma->pause_buf.size,
-                        dma->pause_buf.last, dma->pause_buf.used);
-    
-                DPRINTK("Pause - %d \n", dma->pause);
-                DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
-                DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
-                DPRINTK("total buffer - %d \n", dma->total_buffers);
-                DPRINTK("new buffers - %d \n", dma->new_buffers);
-                DPRINTK("current buffer - %d \n", dma->current_buffer);
-                DPRINTK("last buffer - %d \n", dma->last_buffer);
-                DPRINTK("used buffers - %d \n", dma->used_buffers);
-                DPRINTK("callback addr - 0x%x \n", dma->callback);
-            }
-        }
-        /*
-         *  Total number of buffers in the queue is 0.
-         */
-        else
-        {
-            /*
-             *  No buffers to transfer, or old buffers to mark as used, 
-             *  so Disable the channel
-             */
-            //go dma_regs->CONTROL.Field.ENABLE = 0;
-            uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
-            uiCONTROL &= ~CONTROL_M2P_ENABLE;
-            outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
-
-            /*
-             *  Must read the control register back after a write.
-             */
-            //go read_back = dma_regs->CONTROL.Value;
-            read_back = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
-            
-            /*
-             *  Indicate that this channel is in the pause by
-             *  starvation state by setting the pause bit to true.
-             */
-            dma->pause = TRUE;
-        } 
-    }
-    /*
-     *  Next Frame Buffer Interrupt.  If there's a new buffer program it  
-     *  Check if this is the last buffer in the transfer,
-     *  and if it is, disable the NFB int to prevent being
-     *  interrupted for another buffer when we know there won't be 
-     *  another.
-     */
-    if(dma_int == NFB)
-    {
-        DPRINTK("5  ");
-        
-        if(dma->total_buffers)
-        {
-            DPRINTK("6  ");
-            /*
-             *  The iCurrentBuffer has already been transfered.  so add the 
-             *  byte count from the current buffer to the total byte count.
-             */
-            dma->total_bytes = dma->total_bytes +
-                               dma->buffer_queue[dma->current_buffer].size;
-
-            /*
-             *  Mark the Current Buffer as used.
-             */
-            dma->buffer_queue[dma->current_buffer].used = TRUE;
-
-            /* 
-             *  Increment the used buffer counter
-             */
-            dma->used_buffers++;
-            
-            DPRINTK("#%d", dma->current_buffer);
-            
-
-            if((dma->buffer_queue[
-                (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].last) ||
-               (dma->new_buffers == 0) || (dma->xfer_enable == FALSE))
-            {
-                DPRINTK("7  "); 
-                
-                /*
-                 *  This is the last Buffer in this transaction, so disable
-                 *  the NFB interrupt.  We shouldn't get an NFB int when the
-                 *  FSM moves to the ON state where it would typically get the
-                 *  NFB int indicating a new buffer can be programmed.
-                 *  Instead, once in the ON state, the DMA will just proceed
-                 *  to complet the transfer of the current buffer, move the
-                 *  FSB directly to the STALL state where a STALL interrupt
-                 *  will be generated.
-                 */
-                //go dma_regs->CONTROL.Field.NFBINTEN = 0;
-                uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
-                uiCONTROL &= ~CONTROL_M2P_NFBINTEN;
-                outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
-
-                /*
-                 *  The current buffer has been transferred, so increment
-                 *  the current buffer counter to reflect this.
-                 */
-                dma->current_buffer = (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS;
-                
-                DPRINTK("End of NFB handling. \n");
-                DPRINTK("STATUS - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_STATUS) );
-                DPRINTK("CONTROL - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_CONTROL) );
-                DPRINTK("REMAIN - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_REMAIN) );
-                DPRINTK("PPALLOC - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_PPALLOC) );
-                DPRINTK("BASE0 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE0) );
-                DPRINTK("MAXCNT0 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT0) );
-                DPRINTK("CURRENT0 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT0) );
-                DPRINTK("BASE1 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE1) );
-                DPRINTK("MAXCNT1 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT1) );
-                DPRINTK("CURRENT1 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
-    
-                DPRINTK("Buffer    buf_id     source       size       last       used \n");
-                for(loop = 0; loop < 32; loop ++)
-                {
-                DPRINTK("%d        0x%x        0x%x         0x%x        %d         %d \n",
-                        loop, dma->buffer_queue[loop].buf_id, dma->buffer_queue[loop].source, 
-                        dma->buffer_queue[loop].size,
-                        dma->buffer_queue[loop].last, dma->buffer_queue[loop].used);
-                }
-                DPRINTK("pause     0x%x        0x%x         0x%x        %d         %d \n",
-                        dma->pause_buf.buf_id, dma->pause_buf.source, dma->pause_buf.size,
-                        dma->pause_buf.last, dma->pause_buf.used);
-    
-                DPRINTK("Pause - %d \n", dma->pause);
-                DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
-                DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
-                DPRINTK("total buffer - %d \n", dma->total_buffers);
-                DPRINTK("new buffers - %d \n", dma->new_buffers);
-                DPRINTK("current buffer - %d \n", dma->current_buffer);
-                DPRINTK("last buffer - %d \n", dma->last_buffer);
-                DPRINTK("used buffers - %d \n", dma->used_buffers);
-                DPRINTK("callback addr - 0x%x \n", dma->callback);
-                
-            }
-            else if(dma->new_buffers)
-            {
-                DPRINTK("8  ");
-                /* 
-                 *  we have a new buffer, so increment the current buffer to
-                 *  point to the next buffer, which is already programmed into
-                 *  the DMA. Next time around, it'll be pointing to the
-                 *  current buffer.
-                 */
-                dma->current_buffer = (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS;
-        
-                /*
-                 *  we know we have a new buffer to program as the next
-                 *  buffer, so check which set of MAXCNT and BASE registers
-                 *  to program.  
-                 */
-                //go if(dma_regs->STATUS.Field.NEXTBUFFER)
-                if( inl(M2P_reg_base+M2P_OFFSET_STATUS) & STATUS_M2P_NEXTBUFFER )
-                {
-                    /*
-                     *  Set the MAXCNT1 register with the buffer size
-                     */
-                    //go dma_regs->MAXCNT1.Field.MAXCNT1 = dma->buffer_queue[
-                    //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size;
-                    outl( dma->buffer_queue[
-                             (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size,
-                        M2P_reg_base+M2P_OFFSET_MAXCNT1 );
-
-                    /*
-                     *  Set the BASE1 register with the buffer base address
-                     */
-                    //go dma_regs->BASE1.Field.BASE1 = dma->buffer_queue[
-                    //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source;
-                    outl( dma->buffer_queue[
-                             (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source,
-                            M2P_reg_base+M2P_OFFSET_BASE1 );
-                }
-                else
-                {
-                    /*
-                     *  Set the MAXCNT0 register with the buffer size
-                     */
-                    //go dma_regs->MAXCNT0.Field.MAXCNT0 = dma->buffer_queue[
-                    //go    (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size;
-                    outl( dma->buffer_queue[
-                            (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size,
-                            M2P_reg_base+M2P_OFFSET_MAXCNT0 );
-                            
-                    /*
-                     *  Set the BASE0 register with the buffer base address
-                     */
-                    //go dma_regs->BASE0.Field.BASE0 = dma->buffer_queue[
-                    //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source;
-                    outl( dma->buffer_queue[
-                             (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source,
-                            M2P_reg_base+M2P_OFFSET_BASE0 );
-                }
-                
-                /*
-                 *  Decrement the new buffers counter
-                 */
-                dma->new_buffers--;
-            }
-        }
-        /*
-         *  Total number of buffers is 0 - really we should never get here,
-         *  but just in case.
-         */ 
-        else
-        {
-            DPRINTK("9 \n");
-            
-            /*
-             *  No new buffers to transfer, so Disable the channel
-             */
-            //go dma_regs->CONTROL.Field.ENABLE = 0;
-            uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
-            uiCONTROL &= ~CONTROL_M2P_ENABLE;
-            outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
-        }        
-    }
-    /* 
-     *  Channel Error Interrupt, or perhipheral interrupt, specific to the
-     *  memory to/from peripheral channels.
-     */
-    if(dma_int == CHERROR)
-    {
-        /*
-         *  just clear the interrupt, it's really up to the peripheral
-         *  driver to determine if any further action is necessary.
-         */
-        //go dma_regs->INTERRUPT.Field.CHERRORINT = 0;     
-        uiINTERRUPT = inl(M2P_reg_base+M2P_OFFSET_INTERRUPT);
-        uiINTERRUPT &= ~INTERRUPT_M2P_CHERRORINT;
-        outl( uiINTERRUPT, M2P_reg_base+M2P_OFFSET_INTERRUPT );
-    }
-    
-
-    /*
-     *  Make sure the interrupt was valid, and if it was, then check
-     *  if a callback function was installed for this DMA channel.  If a
-     *  callback was installed call it.
-     */  
-    if((dma_int != UNDEF_INT) && dma->callback)
-    {
-        dma->callback(dma_int, dma->device, dma->user_data);
-    }    
-}
-
-/*****************************************************************************
- *
- * ep9312_dma_open_m2p(int device)
- *
- * Description: This function will attempt to open a M2P/P2M DMA channel.
- *              If the open is successful, the channel number is returned,
- *              otherwise a negative number is returned.
- *
- * Parameters:
- *  device:     device for which the dma channel is requested.
- *
- ****************************************************************************/
-static int
-dma_open_m2p(int device)
-{
-    int channel = -1;
-    unsigned int loop;
-    //go DMAM2P * regs;
-    unsigned int M2P_reg_base;
-	unsigned int uiPWRCNT;
-    
-    DPRINTK("DMA Open M2P with hw dev %d\n", device);
-    
-
-    /*
-     *  Lock the dma channel list.
-     */ 
-    spin_lock(dma_list_lock);
-    DPRINTK("1\n");
-    /*
-     * Verify that the device requesting DMA isn't already using a DMA channel
-     */
-    if (device >= 10)
-    {
-        loop = 1;         // Rx transfer requested
-    }
-    else
-    {
-        loop = 0;         // Tx transfer requested
-    }
-    
-    for(; loop < 10; loop = loop + 2)
-    {
-        /*
-         *  Before checking for a matching device, check that the
-         *  channel is in use, otherwise the device field is 
-         *  invalid.
-         */ 
-        if(dma_chan[loop].ref_count)
-        {
-            if(device == dma_chan[loop].device)
-            {
-                DPRINTK("DMA Open M2P - Error\n");
-                /*
-                 *  Fail.
-                 */
-                return(-1);
-            }
-        }
-    }
-
-    /*
-     *  Get a DMA channel instance for the given hardware device.
-     *  If this is a TX look for even numbered channels, else look for
-     *  odd numbered channels
-     */ 
-    if(device >= 10)
-    {
-        loop = 1;         /* Rx transfer requested */
-    }
-    else
-    {
-        loop = 0;         /* Tx transfer requested */
-    }
-    
-    for(; loop < 10; loop = loop + 2)
-    {
-        if(!dma_chan[loop].ref_count)
-        {
-            /*
-             *  Capture the channel and increment the reference count.
-             */ 
-            channel = loop;
-            dma_chan[channel].ref_count++;
-            break;          
-        }
-    }
-    
-    /*
-     *  Unlock the dma channel list.
-     */ 
-    spin_unlock(dma_list_lock);
-
-    /*
-     *  See if we got a valid channel.
-     */
-    if(channel < 0)
-    {
-        /*
-         *  Fail.
-         */
-        return(-1);
-    }
-    
-    /*
-     *  Point regs to the correct dma channel register base.
-     */ 
-    //go regs = (DMAM2P *)(dma_chan[channel].reg_base);
-    M2P_reg_base = dma_chan[channel].reg_base;
-
-    /*
-     *  Turn on the clock for the specified DMA channel
-     *  TODO: need to use the correct register name for the
-     *  power control register.
-     */
-	uiPWRCNT = inl(SYSCON_PWRCNT);
-    switch(channel)
-    {
-        case 0:
-        {
-            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH0;
-            break;
-        }
-
-        case 1:
-        {
-            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH1;
-            break;
-        }
-
-        case 2:
-        {
-            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH2;
-            break;
-        }
-
-        case 3:
-        {
-            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH3;
-            break;
-        }
-
-        case 4:
-        {
-            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH4;
-            break;
-        }
-
-        case 5:
-        {
-            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH5;
-            break;
-        }
-
-        case 6:
-        {
-            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH6;
-            break;
-        }
-
-        case 7:
-        {
-            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH7;
-            break;
-        }
-        case 8:
-        {
-            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH8;
-            break;
-        }
-        case 9:
-        {
-            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH9;
-            break;
-        }
-
-        default:
-        {
-            return(-1);
-        }
-    }
-	outl( uiPWRCNT, SYSCON_PWRCNT );
-	
-    /*DPRINTK("DMA Open - power control: 0x%x \n", inl(SYSCON_PWRCNT) ); */
-
-    /*
-     *  Clear out the control register before any further setup.
-     */
-    //go regs->CONTROL.Value = 0;
-    outl( 0, M2P_reg_base+M2P_OFFSET_CONTROL );
-
-    /* 
-     *  Setup the peripheral port value in the DMA channel registers.
-     */
-    if(device < 10)
-    {
-        //go regs->PPALLOC.Value = (unsigned int)device;
-        outl( (unsigned int)device, M2P_reg_base+M2P_OFFSET_PPALLOC );
-    }
-    else
-    {
-        //go regs->PPALLOC.Value = (unsigned int)(device - 10);
-        outl( (unsigned int)(device - 10), M2P_reg_base+M2P_OFFSET_PPALLOC );
-    }
-
-    /*
-     *  Let's hold on to the value of the Hw device for comparison later.
-     */ 
-    dma_chan[channel].device = device;
-
-    /*
-     *  Success.
-     */ 
-    return(channel);
-}
-
-/*****************************************************************************
- *
- * dma_open_m2m(int device)
- *
- * Description: This function will attempt to open a M2M DMA channel.
- *              If the open is successful, the channel number is returned,
- *              otherwise a negative number is returned.
- *
- * Parameters:
- *  device:     device for which the dma channel is requested.
- *
- ****************************************************************************/
-static int
-dma_open_m2m(int device)
-{
-    int channel = -1;
-    unsigned int loop;
-    //go DMAM2M * regs;
-	unsigned int M2M_reg_base;
-    unsigned int uiPWRCNT, uiCONTROL;
-    
-    DPRINTK("DMA Open M2M with hw dev %d\n", device);
-    
-
-    /*
-     *  Lock the dma channel list.
-     */ 
-    spin_lock(dma_list_lock);
-    DPRINTK("1\n");
-        
-    /*
-     *  Check if this device is already allocated a channel.
-     *  TODO: can one M2M device be allocated multiple channels?
-     */
-    for(loop = DMA_MEMORY; loop < UNDEF; loop++)
-    {
-        /*
-         *  Before checking for a matching device, check that the
-         *  channel is in use, otherwise the device field is 
-         *  invalid.
-         */ 
-        if(dma_chan[loop].ref_count)
-        {
-            if(device == dma_chan[loop].device)
-            {
-                DPRINTK("Error - dma_open_m2m - already allocated channel\n");
-                
-                /*
-                 *  Fail.
-                 */
-                return(-1);
-            }
-        }
-    }
-
-    /*
-     *  Get a DMA channel instance for the given hardware device.
-     */ 
-    for(loop = 10; loop < 12; loop++)
-    {
-        if(!dma_chan[loop].ref_count)
-        {
-            /*
-             *  Capture the channel and increment the reference count.
-             */ 
-            channel = loop;
-            dma_chan[channel].ref_count++;
-            break;          
-        }
-    }
-    
-    /*
-     *  Unlock the dma channel list.
-     */ 
-    spin_unlock(dma_list_lock);
-
-    /*
-     *  See if we got a valid channel.
-     */
-    if(channel < 0)
-    {
-        /*
-         *  Fail.
-         */
-        return(-1);
-    }
-    
-    /*
-     *  Point regs to the correct dma channel register base.
-     */
-    // regs = (DMAM2M *)(dma_chan[channel].reg_base);
-    M2M_reg_base = dma_chan[channel].reg_base;
-
-    /*
-     *  Turn on the clock for the specified DMA channel
-     *  TODO: need to use the correct register name for the
-     *  power control register.
-     */
-	uiPWRCNT = inl(SYSCON_PWRCNT);
-    switch(channel)
-    {
-        case 10:
-        {
-            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2MCH0;
-            break;
-        }
-
-        case 11:
-        {
-            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2MCH1;
-            break;
-        }
-
-        default:
-        {
-            return(-1);
-        }
-    }
-	outl( uiPWRCNT, SYSCON_PWRCNT );
-    
-    DPRINTK("DMA Open - power control: 0x%x \n", inl(SYSCON_PWRCNT) );
-
-    /*
-     *  Clear out the control register before any further setup.
-     */
-    //go regs->CONTROL.Value = 0;
-    outl( 0, M2M_reg_base+M2M_OFFSET_CONTROL );
-    
-    /* 
-     *  Setup the transfer mode and the request source selection within
-     *  the DMA M2M channel registers.
-     */
-    switch(device)
-    {
-        case DMA_MEMORY:
-        {
-            //go regs->CONTROL.Field.TM = 0;
-            //go regs->CONTROL.Field.RSS = 0;
-            
-            // Clear TM field, set RSS field to 0
-            uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-            uiCONTROL &= ~(CONTROL_M2M_TM_MASK | CONTROL_M2M_RSS_MASK);
-            outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-            
-            break;
-        }
-
-        case DMA_IDE:
-        {
-            //go regs->CONTROL.Field.RSS = 3;
-            //go regs->CONTROL.Field.NO_HDSK = 1;
-            //go regs->CONTROL.Field.PW = 01;
-            
-            // Set RSS field to 3, Set NO_HDSK, Set PW field to 1
-            uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-            uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_PW_MASK);
-            uiCONTROL |= (3<<CONTROL_M2M_RSS_SHIFT) |
-                         CONTROL_M2M_NO_HDSK | 
-                         (1<<CONTROL_M2M_PW_SHIFT);
-            outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-
-            break;
-        }
-
-        case DMARx_SSP:
-        {
-            //go regs->CONTROL.Field.TM = 2;
-            //go regs->CONTROL.Field.RSS = 1;
-            //go regs->CONTROL.Field.NO_HDSK = 1;
-
-            // Set RSS field to 1, Set NO_HDSK, Set TM field to 2
-            uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-            uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_TM_MASK);
-            uiCONTROL |= (1<<CONTROL_M2M_RSS_SHIFT) |
-                         CONTROL_M2M_NO_HDSK | 
-                         (2<<CONTROL_M2M_TM_SHIFT);
-            outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-
-            break;
-        }
-
-        case DMATx_SSP:
-        {
-            //go regs->CONTROL.Field.TM = 1;
-            //go regs->CONTROL.Field.RSS = 2;
-            //go regs->CONTROL.Field.NO_HDSK = 1;
-
-            // Set RSS field to 2, Set NO_HDSK, Set TM field to 1
-            uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-            uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_TM_MASK);
-            uiCONTROL |= (2<<CONTROL_M2M_RSS_SHIFT) |
-                         CONTROL_M2M_NO_HDSK | 
-                         (1<<CONTROL_M2M_TM_SHIFT);
-            outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-
-            break;
-        }
-
-        case DMATx_EXT_DREQ:
-        {
-            //go regs->CONTROL.Field.TM = 1;
-            //go regs->CONTROL.Field.RSS = 0;
-            
-            // Set TM field to 2, set RSS field to 0
-            uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-            uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_TM_MASK);
-            uiCONTROL |= 1<<CONTROL_M2M_TM_SHIFT;
-            outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-
-            break;
-        }
-
-        case DMARx_EXT_DREQ:
-        {
-            //go regs->CONTROL.Field.TM = 2;
-            //go regs->CONTROL.Field.RSS = 0;
-
-            // Set TM field to 2, set RSS field to 0
-            uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-            uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_TM_MASK);
-            uiCONTROL |= 2<<CONTROL_M2M_TM_SHIFT;
-            outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-
-            break;
-        }
-
-        default:
-        {
-            return -1;
-        }
-    }
-
-    /*
-     *  Let's hold on to the value of the Hw device for comparison later.
-     */ 
-    dma_chan[channel].device = device;
-
-    /*
-     *  Success.
-     */ 
-    return(channel);
-}
-
-/*****************************************************************************
- *
- *  int dma_config_m2m(ep93xx_dma_t * dma, unsigned int flags_m2m,
- *               dma_callback callback, unsigned int user_data)
- *
- *  Description: Configure the DMA channel and install a callback function. 
- *               This function will have to be called for every transfer
- *
- *  dma:        Pointer to the dma instance data for the M2M channel to
- *              configure.
- *  flags_m2m   Flags used to configure an M2M dma channel and determine
- *              if a callback function and user_data information are included
- *              in this call.
- *  callback    function pointer which is called near the end of the 
- *              dma channel's irq handler.
- *  user_data   defined by the calling driver.
- *
- ****************************************************************************/
-static int
-dma_config_m2m(ep93xx_dma_t * dma, unsigned int flags_m2m,
-               dma_callback callback, unsigned int user_data)
-{
-    unsigned int flags;
-    unsigned int M2M_reg_base, uiCONTROL;
-    //go DMAM2M * regs;
-    
-    
-    /*
-     *  Make sure the channel is disabled before configuring the channel.
-     *
-     *  TODO: Is this correct??
-     */
-    if(!dma->pause || (!dma->pause && dma->xfer_enable))
-    {
-        /*
-         *  DMA channel is not paused, so we can't configure it.
-         */
-        DPRINTK("DMA channel not paused, so can't configure! \n");
-        return(-1);
-    }
-    
-    /*
-     *  Mask interrupts.
-     */
-    local_irq_save(flags);
-    
-    /*
-     *  Setup a pointer into the dma channel's register set.
-     */                                                                      
-    //go regs = (DMAM2M *)dma->reg_base;
-    M2M_reg_base = dma->reg_base;
-        
-    /*
-     *  By default we enable the stall interrupt.
-     */
-    //go regs->CONTROL.Field.STALLINTEN = 1;
-    uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-    uiCONTROL |= CONTROL_M2M_STALLINTEN;
-    outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-    
-    /*
-     *  By default we enable the done interrupt.
-     */
-    //go regs->CONTROL.Field.DONEINTEN = 1;
-    uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-    uiCONTROL |= CONTROL_M2M_DONEINTEN;
-    outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-    
-    /*
-     *  Set up the transfer control fields based on values passed in
-     *  the flags_m2m field.
-     */
-    //go regs->CONTROL.Field.DAH = flags_m2m & DESTINATION_HOLD;
-    //go regs->CONTROL.Field.SAH = flags_m2m & SOURCE_HOLD;
-    //go regs->CONTROL.Field.TM = flags_m2m & TRANSFER_MODE_MASK;
-    //go regs->CONTROL.Field.PWSC = flags_m2m & WAIT_STATES_MASK;
-    uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-    
-    if( flags_m2m & DESTINATION_HOLD )
-        uiCONTROL |= CONTROL_M2M_DAH;
-    else
-        uiCONTROL &= ~CONTROL_M2M_DAH;
-        
-    if( flags_m2m & SOURCE_HOLD )
-        uiCONTROL |= CONTROL_M2M_SAH;
-    else
-        uiCONTROL &= ~CONTROL_M2M_SAH;
-        
-    uiCONTROL &= ~CONTROL_M2M_TM_MASK;
-    uiCONTROL |= ((flags_m2m & TRANSFER_MODE_MASK)<<CONTROL_M2M_TM_SHIFT ) & 
-        CONTROL_M2M_TM_MASK;
-
-    uiCONTROL &= ~CONTROL_M2M_PWSC_MASK;
-    uiCONTROL |= ((flags_m2m & WAIT_STATES_MASK)<<CONTROL_M2M_PWSC_SHIFT) & 
-            CONTROL_M2M_PWSC_MASK;
-
-    outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-    
-        
-    /*
-     *  Save the callback function in the dma instance for this channel.
-     */
-    dma->callback = callback;
-    
-    /*
-     *  Save the user data in the the dma instance for this channel.
-     */ 
-    dma->user_data = user_data;
-    
-    /*
-     *  Put the dma instance into the pause state by setting the 
-     *  pause bit to true.
-     */
-    dma->pause = TRUE;
-    
-    local_irq_restore(flags);
-        
-    /*
-     *  Success.
-     */
-    return(0);
-}
-
-/*****************************************************************************
- *
- *  int dma_start(int handle, unsigned int channels, unsigned int * handles)
- *
- *  Description: Initiate a transfer on up to 3 channels.
- *
- *  handle:     handle for the channel to initiate transfer on.  
- *  channels:   number of channels to initiate transfers on.
- *  handles:    pointer to an array of handles, one for each channel which
- *               is to be started.
- *
- ****************************************************************************/
-static int
-dma_start_m2m(int channel, ep93xx_dma_t * dma)
-{
-    unsigned int flags;
-    //go DMAM2M * regs = (DMAM2M *)dma->reg_base;
-    unsigned int M2M_reg_base = dma->reg_base;
-    unsigned int uiCONTROL;
-        
-    /*
-     *  Make sure the channel has at least one buffer in the queue.
-     */
-    if(dma->new_buffers < 1)
-    {
-        DPRINTK("DMA Start: Channel starved.\n");
-            
-        /*
-         *  This channel does not have enough buffers queued up,
-         *  so enter the pause by starvation state.
-         */
-        dma->xfer_enable = TRUE;
-        dma->pause = TRUE;
-         
-        /*
-         *  Success.
-         */  
-        return(0);
-    }
-    
-    /*
-     *  Mask interrupts while we get this started.
-     */
-    local_irq_save(flags);
-    
-    /*
-     *  Set up one or both buffer descriptors with values from the next one or
-     *  two buffers in the queue.  By default enable the next frame buffer
-     *  interrupt on the channel.
-     */
-    //go regs->CONTROL.Field.NFBINTEN = 1;
-    uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-    uiCONTROL |= CONTROL_M2M_NFBINTEN;
-    outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-    
-    /*
-     *  Update the dma channel instance transfer state.
-     */
-    dma->xfer_enable = TRUE;
-    dma->pause = FALSE;
-        
-    /*
-     *  Decrement the new buffers counter.
-     */
-    dma->new_buffers--;
-    
-    /*
-     *  Program up the first buffer descriptor with a sourc and destination
-     *  and a byte count.
-     */
-    //go regs->SAR_BASE0.Field.SAR_BASE0 = dma->buffer_queue[dma->current_buffer].source;
-    outl( dma->buffer_queue[dma->current_buffer].source,
-            M2M_reg_base+M2M_OFFSET_SAR_BASE0 );
-    
-    //go regs->DAR_BASE0.Field.DAR_BASE0 = dma->buffer_queue[dma->current_buffer].dest;
-    outl( dma->buffer_queue[dma->current_buffer].dest,
-            M2M_reg_base+M2M_OFFSET_DAR_BASE0 );
-
-    //go regs->BCR0.Field.BCR0 = dma->buffer_queue[dma->current_buffer].size;
-    outl( dma->buffer_queue[dma->current_buffer].size,
-            M2M_reg_base+M2M_OFFSET_BCR0 );
-    
-    /*
-     *  Now we enable the channel.  This initiates the transfer.
-     */
-    //go regs->CONTROL.Field.ENABLE = 1;
-    uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-    uiCONTROL |= CONTROL_M2M_ENABLE;
-    outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-    
-    /*
-     *  Before restoring irqs set up the second buffer descriptor
-     *  with a second buffer if we have a second buffer.
-     */ 
-    if(dma->new_buffers)
-    {
-        //go regs->SAR_BASE1.Field.SAR_BASE1 = dma->buffer_queue[
-        //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source;
-        outl( dma->buffer_queue[(dma->current_buffer + 1) % 
-                MAX_EP93XX_DMA_BUFFERS].source,
-                M2M_reg_base+M2M_OFFSET_SAR_BASE1 );
-        
-        //go regs->DAR_BASE1.Field.DAR_BASE1 = dma->buffer_queue[
-        //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].dest;
-        outl( dma->buffer_queue[(dma->current_buffer + 1) % 
-                MAX_EP93XX_DMA_BUFFERS].dest,
-                M2M_reg_base+M2M_OFFSET_DAR_BASE1 );
-        
-        //go regs->BCR1.Field.BCR1 = dma->buffer_queue[
-        //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size;
-        outl( dma->buffer_queue[(dma->current_buffer + 1) % 
-                MAX_EP93XX_DMA_BUFFERS].size,
-                M2M_reg_base+M2M_OFFSET_BCR1 );
-    }
-    
-    /*
-     *  If this is a memory to memory transfer, we need to s/w trigger the
-     *  transfer by setting the start bit within the control register.
-     */
-    if(dma->device == DMA_MEMORY)
-    {
-        //go regs->CONTROL.Field.START = 1;
-        uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-        uiCONTROL |= CONTROL_M2M_START;
-        outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-    }
-    
-    
-/*    
-    DPRINTK("DMA - It's been started!!");
-    DPRINTK("STATUS - 0x%x \n",     inl(M2M_reg_base+M2M_OFFSET_STATUS) );
-    DPRINTK("CONTROL - 0x%x \n",    inl(M2M_reg_base+M2M_CONTROL.Value) );
-    DPRINTK("REMAIN - 0x%x \n",     inl(M2M_reg_base+M2M_REMAIN.Value) );
-    DPRINTK("PPALLOC - 0x%x \n",    inl(M2M_reg_base+M2M_PPALLOC.Value) );
-    DPRINTK("BASE0 - 0x%x \n",      inl(M2M_reg_base+M2M_BASE0.Value) );
-    DPRINTK("MAXCNT0 - 0x%x \n",    inl(M2M_reg_base+M2M_MAXCNT0.Value) );
-    DPRINTK("CURRENT0 - 0x%x \n",   inl(M2M_reg_base+M2M_CURRENT0.Value) );
-    DPRINTK("BASE1 - 0x%x \n",      inl(M2M_reg_base+M2M_BASE1.Value) );
-    DPRINTK("MAXCNT1 - 0x%x \n",    inl(M2M_reg_base+M2M_MAXCNT1.Value) );
-    DPRINTK("CURRENT1 - 0x%x \n",   inl(M2M_reg_base+M2M_CURRENT1.Value) );
-    
-    DPRINTK("Pause - %d \n", dma_pointers[0]->pause);
-    DPRINTK("xfer_enable - %d \n", dma_pointers[0]->xfer_enable);
-    DPRINTK("total bytes - 0x%x \n", dma_pointers[0]->total_bytes);
-    DPRINTK("total buffer - %d \n", dma_pointers[0]->total_buffers);
-    DPRINTK("new buffers - %d \n", dma_pointers[0]->new_buffers);
-    DPRINTK("current buffer - %d \n", dma_pointers[0]->current_buffer);
-    DPRINTK("last buffer - %d \n", dma_pointers[0]->last_buffer);
-    DPRINTK("used buffers - %d \n", dma_pointers[0]->used_buffers);
-*/    
-    /*
-     *  Unmask irqs
-     */
-    local_irq_restore(flags);
-    
-    /*
-     *  Success.
-     */
-    return(0);
-}
-
-/*****************************************************************************
- *
- *  DMA interface functions
- *
- ****************************************************************************/
-
-/*****************************************************************************
- *
- *  int dma_init(int handle, unsigned int flags_m2p, unsigned int flags_m2m,
- *               dma_callback callback, unsigned int user_data)
- *
- *  Description: Configure the DMA channel and install a callback function.
- *
- *  handle:     Handle unique the each instance of the dma interface, used
- *              to verify this call.
- *  flags_m2p   Flags used to configure an M2P/P2M dma channel and determine
- *              if a callback function and user_data information are included
- *              in this call. This field should be NULL if handle represents
- *              an M2M channel.
- *  flags_m2m   Flags used to configure an M2M dma channel and determine
- *              if a callback function and user_data information are included
- *              in this call. This field should be NULL if handle represents
- *              an M2P/P2M channel.
- *  callback    function pointer which is called near the end of the 
- *              dma channel's irq handler.
- *  user_data   defined by the calling driver.
- *
- ****************************************************************************/
-int
-ep93xx_dma_config(int handle, unsigned int flags_m2p, unsigned int flags_m2m,
-                  dma_callback callback, unsigned int user_data)
-{
-    int  channel;
-    ep93xx_dma_t * dma;
-    //go DMAM2P * dma_regs;
-    int flags;
-    unsigned int M2P_reg_base, uiCONTROL;
-    
-    /*
-     *  Get the DMA hw channel # from the handle.
-     */
-    channel = dma_get_channel_from_handle(handle);
-    
-    /*
-     *  See if this is a valid handle.
-     */
-    if(channel < 0)
-    {
-        printk(KERN_ERR
-            "DMA Config: Invalid dma handle.\n");
-        
-        /*
-         *  Fail.
-         */        
-        return(-EINVAL);                                                  
-    }
-    
-    DPRINTK("DMA Config \n");
-    
-    dma = &dma_chan[channel];                                                                     
-    
-    local_irq_save(flags);
-    
-    /*                                                                    
-     *  Check if the channel is currently transferring.                   
-     */
-    if(dma->xfer_enable)
-    {                                                                     
-        local_irq_restore(flags);
-        
-        /*                                                                
-         *  Already transferring, invalid command.                        
-         */                                                               
-        return(-EINVAL);                                                  
-    }
-    
-    
-    /*
-     *  Check if this is an m2m function.
-     */ 
-    if(channel >= 10)
-    {
-        local_irq_restore(flags);
-        
-        /*
-         *  Call another function to handle m2m config.
-         */ 
-        return(dma_config_m2m(dma, flags_m2m, callback, user_data)); 
-    }
-    
-    /*
-     *  Setup a pointer into the dma channel's register set.
-     */                                                                      
-    //go dma_regs = (DMAM2P *)dma->reg_base;
-    M2P_reg_base = dma->reg_base;
-                                                                              
-    /*
-     *  By default we enable the stall interrupt.
-     */
-    //go dma_regs->CONTROL.Field.STALLINTEN = 1;
-    uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
-    uiCONTROL |= CONTROL_M2P_STALLINTEN;
-    outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
-    
-    /*
-     *  Configure the channel for an error from the peripheral.
-     */
-    //go dma_regs->CONTROL.Field.CHERRORINTEN =
-    //go    flags_m2p && CHANNEL_ERROR_INT_ENABLE;
-    uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
-    if( flags_m2p && CHANNEL_ERROR_INT_ENABLE )
-        uiCONTROL |= CONTROL_M2P_CHERRORINTEN;
-    else
-        uiCONTROL &= ~CONTROL_M2P_CHERRORINTEN;
-    outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
-        
-        
-    //go dma_regs->CONTROL.Field.ABRT = flags_m2p && ABORT;
-    uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
-    if( flags_m2p && ABORT )
-        uiCONTROL |= CONTROL_M2P_ABRT;
-    else
-        uiCONTROL &= ~CONTROL_M2P_ABRT;
-    outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
-    
-    
-    //go dma_regs->CONTROL.Field.ICE = flags_m2p && IGNORE_CHANNEL_ERROR;
-    uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
-    if( flags_m2p && IGNORE_CHANNEL_ERROR )
-        uiCONTROL |= CONTROL_M2P_ICE;
-    else
-        uiCONTROL &= ~CONTROL_M2P_ICE;
-    outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
-    
-    /*
-     *  Save the callback function in the dma instance for this channel.
-     */
-    dma->callback = callback;
-    
-    /*
-     *  Save the user data in the the dma instance for this channel.
-     */ 
-    dma->user_data = user_data;
-    
-    /*
-     *  Put the dma instance into the pause state by setting the 
-     *  pause bit to true.
-     */
-    dma->pause = TRUE;
-    
-    local_irq_restore(flags);
-        
-    /*
-     *  Success.
-     */                                                                                                        
-    return(0);
-}
-
-/*****************************************************************************
- *
- *  int dma_start(int handle, unsigned int channels, unsigned int * handles)
- *
- *  Description: Initiate a transfer on up to 3 channels.
- *
- *  handle:     handle for the channel to initiate transfer on.  
- *  channels:   number of channels to initiate transfers on.
- *  handles:    pointer to an array of handles, one for each channel which
- *               is to be started.
- *
- ****************************************************************************/
-int
-ep93xx_dma_start(int handle, unsigned int channels, unsigned int * handles)
-{
-    ep93xx_dma_t * dma_pointers[3];
-    //go DMAM2P * regs[3];
-    unsigned int M2P_reg_bases[3];
-    unsigned int loop, flags, uiCONTROL;
-    int  channel;    
-    
-    /*
-     *  Get the DMA hw channel # from the handle.
-     */
-    channel = dma_get_channel_from_handle(handle);
-    
-    /*
-     *  See if this is a valid handle.
-     */
-    if(channel < 0)
-    {
-        printk(KERN_ERR "DMA Start: Invalid dma handle.\n");
-        
-        /*
-         *  Fail.
-         */        
-        return(-EINVAL);
-    }
-    
-    if(channels < 1)
-    {
-        printk(KERN_ERR "DMA Start: Invalid parameter.\n");
-        
-        return(-EINVAL);
-    }
-    
-    DPRINTK("DMA Start \n");
-    
-    /*
-     *  Mask off registers.
-     */     
-    local_irq_save(flags);
-    
-    /*
-     *  Check if this is a start multiple.
-     */
-    if(channels > 1)
-    {
-#if 0
-        for(loop = 0; loop < channels; loop++)
-        {
-            /*
-             *  Get the DMA hw channel # from the handle.
-             */
-            channel = dma_get_channel_from_handle(handle);
-    
-            /*
-             *  See if this is a valid handle.
-             */
-            if(channel < 0)
-            {
-                printk(KERN_ERR "DMA Start: Invalid dma handle.\n");
-        
-                /*
-                 *  Fail.
-                 */        
-                return(-EINVAL);
-            }
-            
-            /*
-             *  Check if this channel is already started.
-             */
-            if(dma_chan[channel].xfer_enable)
-            {
-                printk(KERN_ERR
-                    "DMA Start: Invalid command for channel %d.\n", channel);
-                
-                /*
-                 *  This channel is already transferring, so return an error.
-                 */ 
-                return(-EINVAL);
-            }
-            
-            /*
-             *  Make sure the channel has at least two buffers in the queue.
-             */
-            if(dma_chan[channel].new_buffers < 2)
-            {
-                printk(KERN_ERR
-                    "DMA Start: Invalid command for channel %d.\n", channel);
-                    
-                /*
-                 *  This channel does not have enough buffers queued up,
-                 *  so return an error.
-                 */ 
-                return(-EINVAL);
-            }
-            
-            /*
-             *  Set up a pointer into the dma instance data for this channel.
-             */
-            dma_pointers[loop] = &dma_chan[channel];
-            
-            /*
-             *  Set up a pointer to the register set for this channel.
-             */
-            //go regs[loop] = (DMAM2P *)dma_pointers[loop]->reg_base;
-            M2P_reg_bases[loop] = dma_pointers[loop]->reg_base;
-             
-        }
-#endif
-        DPRINTK("DMA ERROR: Start, multiple start not supported yet \n");
-        return(-1);
-    }
-    /*
-     *  Only one channel
-     */
-    else
-    {
-        /*
-         *  Check if this channel is already transferring.
-         */
-        if(dma_chan[channel].xfer_enable && !dma_chan[channel].pause)
-        {
-            printk(KERN_ERR
-                "DMA Start: Invalid command for channel %d.\n", channel);
-            DPRINTK("DMA Start: Invalid command for channel %d.\n", channel);
-            
-            
-            /*
-             *  This channel is already transferring, so return an error.
-             */ 
-            return(-EINVAL);
-        }
-        
-        /*
-         *  If this is an M2M channel, call a different function.
-         */ 
-        if(channel >= 10)
-        {
-            /*
-             *  Call the m2m start function.  Only start one channel.
-             */
-            return(dma_start_m2m(channel, &dma_chan[channel]));
-        }
-        
-        /*
-         *  Make sure the channel has at least one buffer in the queue.
-         */
-        if(dma_chan[channel].new_buffers < 1)
-        {
-            DPRINTK("DMA Start: Channel starved.\n");
-                
-            /*
-             *  This channel does not have enough buffers queued up,
-             *  so enter the pause by starvation state.
-             */
-            dma_chan[channel].xfer_enable = TRUE;
-            dma_chan[channel].pause = TRUE;
-             
-            /*
-             *  Success.
-             */  
-            return(0);
-        }
-        
-        /*
-         *  Set up a dma instance pointer for this dma channel.
-         */ 
-        dma_pointers[0] = &dma_chan[channel];
-        
-        /*
-         * Set up a pointer to the register set for this channel.
-         */
-        //go regs[0] = (DMAM2P *)dma_pointers[0]->reg_base;
-        M2P_reg_bases[0] = dma_pointers[0]->reg_base; 
-    }
-    
-    /*
-     *  Setup both MAXCNT registers with values from the next two buffers
-     *  in the queue, and enable the next frame buffer interrupt on the channel.
-     */
-    for(loop = 0; loop < channels; loop++)
-    {
-        /*
-         *  By default we enable the next frame buffer interrupt.
-         */
-        //go regs[loop]->CONTROL.Field.NFBINTEN = 1;
-        uiCONTROL = inl(M2P_reg_bases[loop]+M2P_OFFSET_CONTROL);
-        uiCONTROL |= CONTROL_M2P_NFBINTEN;
-        outl( uiCONTROL, M2P_reg_bases[loop]+M2P_OFFSET_CONTROL );
-        
-        /*
-         *  Check if we need to restore a paused transfer.
-         *  TODO: should check if buf_id == -1 or not.
-         */ 
-        if(dma_pointers[loop]->pause_buf.buf_id != -1)
-        {
-            //go regs[loop]->MAXCNT0.Field.MAXCNT0 =
-            //go     dma_pointers[loop]->pause_buf.size;
-            outl( dma_pointers[loop]->pause_buf.size, 
-                M2P_reg_bases[loop]+M2P_OFFSET_MAXCNT0 );
-        }
-        else
-        {
-            //go regs[loop]->MAXCNT0.Field.MAXCNT0 =
-            //go     dma_pointers[loop]->buffer_queue[dma_pointers[loop]->current_buffer].size;
-            outl( dma_pointers[loop]->buffer_queue[dma_pointers[loop]->current_buffer].size,
-                M2P_reg_bases[loop]+M2P_OFFSET_MAXCNT0 );
-        }
-    }
-    
-    for(loop = 0; loop < channels; loop++)
-    {
-        /*
-         *  Enable the specified dma channels.
-         */ 
-        //go regs[loop]->CONTROL.Field.ENABLE = 1;
-        uiCONTROL = inl(M2P_reg_bases[loop]+M2P_OFFSET_CONTROL);
-        uiCONTROL |= CONTROL_M2P_ENABLE;
-        outl( uiCONTROL, M2P_reg_bases[loop]+M2P_OFFSET_CONTROL );
-        
-        /*
-         *  Update the dma channel instance transfer state.
-         */
-        dma_pointers[loop]->xfer_enable = TRUE;
-        dma_pointers[loop]->pause = FALSE;
-                
-        if(dma_pointers[loop]->pause_buf.buf_id == -1)
-        {
-            dma_pointers[loop]->new_buffers--;
-        }
-    }
-        
-    /*
-     *  Program up the BASE0 registers for all specified channels, this
-     *  will initiate transfers on all specified channels.
-     */
-    for(loop = 0; loop < channels; loop++)
-    {   
-        /*
-         *  Check if we need to restore a paused transfer.
-         */ 
-        if(dma_pointers[loop]->pause_buf.buf_id != -1)
-        {
-            //go regs[loop]->BASE0.Field.BASE0 = dma_pointers[loop]->pause_buf.source;
-            outl( dma_pointers[loop]->pause_buf.source,
-                M2P_reg_bases[loop]+M2P_OFFSET_BASE0 );
-            
-            /*
-             *  Set the pause buffer to NULL
-             */ 
-            dma_pointers[loop]->pause_buf.buf_id = -1;
-            dma_pointers[loop]->pause_buf.size = 0;
-        }
-        else
-        {
-            //go regs[loop]->BASE0.Field.BASE0 =
-            //go     dma_pointers[loop]->buffer_queue[
-            //go     dma_pointers[loop]->current_buffer].source;
-            outl( dma_pointers[loop]->buffer_queue[
-                  dma_pointers[loop]->current_buffer].source,
-                M2P_reg_bases[loop]+M2P_OFFSET_BASE0 );
-        }
-    }
-    
-    /*
-     *  Before restoring irqs setup the second MAXCNT/BASE
-     *  register with a second buffer.
-     */ 
-    for(loop = 0; loop < channels; loop++)
-    {                
-        if(dma_pointers[loop]->new_buffers)
-        {
-            //go regs[loop]->MAXCNT1.Field.MAXCNT1 =
-            //go     dma_pointers[loop]->buffer_queue[
-            //go     (dma_pointers[loop]->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size;
-            outl( dma_pointers[loop]->buffer_queue[
-                  (dma_pointers[loop]->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size,
-                M2P_reg_bases[loop]+M2P_OFFSET_MAXCNT1 );
-            
-            //go regs[loop]->BASE1.Field.BASE1 =
-            //go     dma_pointers[loop]->buffer_queue[
-            //go     (dma_pointers[loop]->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source;
-            outl( dma_pointers[loop]->buffer_queue[
-                  (dma_pointers[loop]->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source,
-                M2P_reg_bases[loop]+M2P_OFFSET_BASE1 );
-        }
-    }
-    
-/*    
-    DPRINTK("DMA - It's been started!!");
-    DPRINTK("STATUS - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_STATUS) );
-    DPRINTK("CONTROL - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_CONTROL) );
-    DPRINTK("REMAIN - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_REMAIN) );
-    DPRINTK("PPALLOC - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_PPALLOC) );
-    DPRINTK("BASE0 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE0) );
-    DPRINTK("MAXCNT0 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT0) );
-    DPRINTK("CURRENT0 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT0) );
-    DPRINTK("BASE1 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE1) );
-    DPRINTK("MAXCNT1 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT1) );
-    DPRINTK("CURRENT1 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
-    
-    DPRINTK("Pause - %d \n", dma_pointers[0]->pause);
-    DPRINTK("xfer_enable - %d \n", dma_pointers[0]->xfer_enable);
-    DPRINTK("total bytes - 0x%x \n", dma_pointers[0]->total_bytes);
-    DPRINTK("total buffer - %d \n", dma_pointers[0]->total_buffers);
-    DPRINTK("new buffers - %d \n", dma_pointers[0]->new_buffers);
-    DPRINTK("current buffer - %d \n", dma_pointers[0]->current_buffer);
-    DPRINTK("last buffer - %d \n", dma_pointers[0]->last_buffer);
-    DPRINTK("used buffers - %d \n", dma_pointers[0]->used_buffers);
-*/    
-    /*
-     *  Unmask irqs
-     */
-    local_irq_restore(flags);
-    
-    /*
-     *  Success.
-     */
-    return(0);
-}
- 
-/*****************************************************************************
- *
- *  int ep93xx_dma_add_buffer(int handle, unsigned int * address,
- *                         unsigned int size, unsigned int last)
- *
- *  Description: Add a buffer entry to the DMA buffer queue.
- *
- *  handle:     handle for the channel to add this buffer to.  
- *  address:    Pointer to an integer which is the start address of the
- *              buffer which is to be added to the queue.
- *  size:       size of the buffer in bytes.
- *  last:       1 if this is the last buffer in this stream, 0 otherwise.
- *
- ****************************************************************************/
-int
-ep93xx_dma_add_buffer(int handle, unsigned int source, unsigned int dest,
-                      unsigned int size, unsigned int last,
-                      unsigned int buf_id)
-{
-    unsigned int flags;
-    ep93xx_dma_t * dma;
-    int  channel;
-
-    /*
-     *  Get the DMA hw channel # from the handle.
-     */
-    channel = dma_get_channel_from_handle(handle);
-    
-    /*
-     *  See if this is a valid handle.
-     */
-    if(channel < 0)
-    {
-        printk(KERN_ERR
-            "DMA Add Buffer: Invalid dma handle.\n");
-        
-        /*
-         *  Fail.
-         */        
-        return(-EINVAL);
-    }
-    
-    /*
-     *  Get a pointer to the dma instance.
-     */
-    dma = &dma_chan[channel];
-
-    /*
-     *  Mask interrupts and hold on to the original state.
-     */ 
-    local_irq_save(flags);
-    
-    /*
-     *  If the buffer queue is full, last_buffer is the same as current_buffer and 
-     *  we're not tranfering, or last_buffer is pointing to a used buffer, then exit.
-     *  TODO: do I need to do any more checks? 
-     */
-    if(dma->total_buffers >= MAX_EP93XX_DMA_BUFFERS)
-    {
-        /*
-         *  Restore the state of the irqs
-         */ 
-        local_irq_restore(flags);
-
-        /*
-         *  Fail.
-         */ 
-        return(-1);
-    }
-
-    /*
-     *  Add this buffer to the queue
-     */
-    dma->buffer_queue[dma->last_buffer].source = source;
-    dma->buffer_queue[dma->last_buffer].dest = dest;
-    dma->buffer_queue[dma->last_buffer].size = size;
-    dma->buffer_queue[dma->last_buffer].last = last;
-    dma->buffer_queue[dma->last_buffer].buf_id = buf_id;
-    
-    /*
-     *  Reset the used field of the buffer structure.
-     */
-    dma->buffer_queue[dma->last_buffer].used = FALSE;
-    
-    /*
-     *  Increment the End Item Pointer.
-     */
-    dma->last_buffer = (dma->last_buffer + 1) % MAX_EP93XX_DMA_BUFFERS;
-
-    /*
-     *  Increment the new buffers counter and the total buffers counter
-     */
-    dma->new_buffers++;
-    dma->total_buffers++;
-                              
-    /*
-     *  restore the interrupt state.
-     */
-    local_irq_restore(flags);
-    
-    /*
-     *  Check if the channel was starved into a stopped state.
-     */
-    if(dma->pause && dma->xfer_enable)
-    {
-        if(dma->new_buffers >= 1)
-        {
-            DPRINTK("DMA - calling start from add after starve. \n");
-            
-            /*
-             *  The channel was starved into a stopped state, and we've got
-             *  2 new buffers, so start tranferring again.
-             */
-            ep93xx_dma_start(handle, 1, 0);
-        }
-    }
-    
-    /*
-     *  Success.
-     */ 
-    return(0);
-}
-
-/*****************************************************************************
- *
- *  int ep93xx_dma_remove_buffer(int handle, unsigned int * address,
- *                                unsigned int * size)
- *
- *  Description: Remove a buffer entry from the DMA buffer queue. If
- *               buffer was removed successfully, return 0, otherwise
- *               return -1.
- *
- *  handle:     handle for the channel to remove a buffer from. 
- *  address:    Pointer to an integer which is filled in with the start
- *              address of the removed buffer.
- *  size:       Pointer to an integer which is filled in with the size in
- *              bytes of the removed buffer.
- *
- ****************************************************************************/
-int
-ep93xx_dma_remove_buffer(int handle, unsigned int * buf_id)
-{
-    unsigned int test;
-    unsigned int loop;
-    int return_val = -1;
-    unsigned int flags;
-    ep93xx_dma_t *dma;
-    int  channel;
-
-    /*
-     *  Get the DMA hw channel # from the handle.
-     */
-    channel = dma_get_channel_from_handle(handle);
-    
-    /*
-     *  See if this is a valid handle.
-     */
-    if(channel < 0)
-    {
-        printk(KERN_ERR
-            "DMA Remove Buffer: Invalid dma handle.\n");
-        
-        /*
-         *  Fail.
-         */        
-        return(-EINVAL);
-    }
-    
-    dma = &dma_chan[channel];
-
-    /*
-     *  Mask interrupts and hold on to the original state.
-     */ 
-    local_irq_save(flags);
-    
-    /*
-     *  Make sure there are used buffers to be returned.
-     */
-    if(dma->used_buffers)
-    {
-        test = dma->last_buffer;
-        
-        for(loop = 0; loop < MAX_EP93XX_DMA_BUFFERS; loop++)
-        {
-            if(dma->buffer_queue[test].used && (dma->buffer_queue[test].buf_id != -1))
-            {                   
-                /*DPRINTK("buffer %d used \n", test); */
-                
-                /*
-                 *  This is a used buffer, fill in the buf_id pointer
-                 *  with the buf_id for this buffer.
-                 */
-                *buf_id = dma->buffer_queue[test].buf_id;
-
-                /*
-                 *  Reset this buffer structure
-                 */
-                dma->buffer_queue[test].buf_id = -1;
-
-                /*
-                 *  Decrement the used buffer counter, and the total buffer counter.
-                 */
-                dma->used_buffers--;
-                dma->total_buffers--;
-
-                /*
-                 *  Successful removal of a buffer, so set the return
-                 *  value to 0, then exit this loop.
-                 */ 
-                return_val = 0;
-                break;
-            }
-
-            /*
-             *  This buffer isn't used, let's see if the next one is.
-             */
-            test = (test + 1) % MAX_EP93XX_DMA_BUFFERS;
-        }
-    }
-    
-    /*
-     *  Restore interrupts.
-     */   
-    local_irq_restore(flags);
-    
-    /*
-     *  Success.
-     */ 
-    return(return_val);
-}
-
-/*****************************************************************************
- *
- *  int ep93xx_dma_pause(int handle, unsigned int channels,
- *                       unsigned int * handles)
- *
- *  Description: Disable any ongoing transfer for the given channel, retaining
- *               the state of the current buffer transaction so that upon
- *               resume, the dma will continue where it left off.
- *
- *  handle:     Handle for the channel to be paused.  If this is a pause for
- *              for multiple channels, handle is a valid handle for one of
- *              the channels to be paused.
- *  channels:   number of channel to pause transfers on.
- *  handles:    Pointer to an array of handles, one for each channel which
- *              to be paused.  If this pause is intended only for one
- *              channel, this field should be set to NULL.
- *
- ****************************************************************************/
-int
-ep93xx_dma_pause(int handle, unsigned int channels, unsigned int * handles)
-{
-    unsigned int flags;
-    ep93xx_dma_t * dma;
-    int channel;
-    //go DMAM2P * regs_m2p;
-    //go DMAM2M * regs_m2m;
-    unsigned int M2M_reg_base, M2P_reg_base;
-    
-
-    /*
-     *  Get the DMA hw channel # from the handle.
-     */
-    channel = dma_get_channel_from_handle(handle);
-    
-    /*
-     *  See if this is a valid handle.
-     */
-    if(channel < 0)
-    {
-        printk(KERN_ERR
-            "DMA Pause: Invalid dma handle.\n");
-        
-        /*
-         *  Fail.
-         */        
-        return(-EINVAL);
-    }
-    
-    DPRINTK("DMA %d: pause \n", channel);
-    
-    /*
-     *  Set up a pointer to the dma instance data.
-     */
-    dma = &dma_chan[channel];    
-    
-    /*
-     *  Set a pointer to the dma channel registers.
-     */
-    if(channel < 10)
-    {
-        //go regs_m2p = dma->reg_base;
-        M2P_reg_base = dma->reg_base;
-    }
-    else
-    {
-        //go regs_m2m = dma->reg_base;
-        M2M_reg_base = dma->reg_base;
-    }
-    
-    /*
-     *  Mask interrupts and hold on to the original state.
-     */ 
-    local_irq_save(flags);
-    
-    /*
-     *  Check if we're already paused.
-     */ 
-    if(dma->pause)
-    {
-        /*
-         *  We're paused, but are we stopped?
-         */
-        if(dma->xfer_enable)
-        {
-            /*
-             *  Put the channel in the stopped state.
-             */
-            dma->xfer_enable = FALSE;
-        }
-        
-        DPRINTK("DMA Pause - already paused.");
-    }
-    else
-    {
-        /*
-         *  Put the channel into the stopped state.
-         */
-        dma->xfer_enable = FALSE;
-        dma->pause = TRUE;
-    }
-    
-    /*
-     *  restore interrupts.
-     */ 
-    local_irq_restore(flags);
-   
-    /*
-     *  Already paused, so exit.
-     */ 
-    return(0);
-        
-#if 0    
-    
-    /*
-     *  Disable this DMA channel.
-     */
-    if(channel < 10)
-    {
-        /*
-         *  Before disabling the channel, get the current state.
-         */
-        //go state = regs_m2p->STATUS.Field.CURRENT;
-        state = inl(M2P_reg_base+M2P_OFFSET_STATUS) & STATUS_M2P_CURRENT_MASK;
-        
-        //go nfb = regs_m2p->STATUS.Field.NEXTBUFFER;
-        nfb = inl(M2P_reg_base+M2P_OFFSET_STATUS) & STATUS_M2P_NEXTBUFFER;
-        
-        /*
-         *  Disable the channel
-         */ 
-        //go regs_m2p->CONTROL.Field.ENABLE = 0;
-        uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
-        uiCONTROL &= ~CONTROL_M2P_ENABLE;
-        outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
-    }
-    else
-    {
-        //go regs_m2m->CONTROL.Field.ENABLE = 0;
-        uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-        uiCONTROL &= ~CONTROL_M2M_ENABLE;
-        outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-    }
-    
-    /*
-    local_irq_restore(flags);
-
-    mdelay(1);
-    i2s->i2s_tx0_EN.Value = 0;
-
-    local_irq_save(flags);
-    */
-    
-    
-
-    /*
-     *  Indicate that the channel stopped transferring, and set the
-     *  pause field.
-     */
-    dma->xfer_enable = FALSE;
-    dma->pause = TRUE;
-    
-    /*
-     *  Save the state of the buffer being transferred.
-     */
-    if(channel < 10)
-    {
-        if(state == 2)
-        {
-            if(nfb)
-            {
-                /*
-                 *  Active buffer descriptor is 0
-                 */
-                //go dma->pause_buf.source = regs_m2p->CURRENT0.Field.CURRENT0;
-                dma->pause_buf.source = 
-                    inl( M2P_reg_base+M2P_OFFSET_CURRENT0 );
-                
-                /*
-                 *  state is now the buffer descriptor index.
-                 */
-                state = 0;
-            }
-            else
-            {
-                /*
-                 *  Active buffer descriptor is 1
-                 */
-                //go dma->pause_buf.source = regs_m2p->CURRENT1.Field.CURRENT1;
-                dma->pause_buf.source = 
-                    inl( M2P_reg_base+M2P_OFFSET_CURRENT1 );
-                
-                /*
-                 *  state is now the buffer descriptor index.
-                 */
-                state = 1;
-            }
-        }
-        else if(state == 3)
-        {
-            if(nfb)
-            {
-                /*
-                 *  Active buffer descriptor is 1
-                 */
-                //go dma->pause_buf.source = regs_m2p->CURRENT1.Field.CURRENT1;
-                dma->pause_buf.source = 
-                    inl( M2P_reg_base+M2P_OFFSET_CURRENT1 );
-            
-                /*
-                 *  state is now the buffer descriptor index.
-                 */
-                state = 1;
-            }
-            else
-            {
-                /*
-                 *  Active buffer descriptor is 0
-                 */
-                //go dma->pause_buf.source = regs_m2p->CURRENT0.Field.CURRENT0;
-                dma->pause_buf.source = 
-                    inl( M2P_reg_base+M2P_OFFSET_CURRENT0 );
-            
-                /*
-                 *  state is now the buffer descriptor index.
-                 */
-                state = 0;
-            }    
-        }
-        
-        /*
-         *  Active buffer descriptor is 0
-         */
-        //go dma->pause_buf.size = regs_m2p->REMAIN.Field.REMAIN;
-        dma->pause_buf.size = 
-            inl( M2P_reg_base+M2P_OFFSET_REMAIN ) & M2P_MASK_REMAIN;
-        
-        /*
-         *  Set the pause buffer buf_id field to 1 to indicate that this 
-         *  buffer has valid data.
-         */
-        dma->pause_buf.buf_id = 1;
-        
-        /*
-         *  Reset the nfb variable, it now represents whether or not we need
-         *  to sync up the dma channel instance.
-         */ 
-        nfb = 0; 
-        
-        /*
-         *  Check if the active buffer is the current buffer.  the channel
-         *  may have started transferring the next buffer, which would mean
-         *  we have to sync up the dma instance data.
-         */ 
-        //go if(((state == 0) && (dma->buffer_queue[dma->current_buffer].source !=
-        //go                   regs_m2p->BASE0.Field.BASE0)) ||
-        //go    ((state == 1) && (dma->buffer_queue[dma->current_buffer].source !=
-        //go                      regs_m2p->BASE1.Field.BASE1)))
-        
-        if(((state == 0) && (dma->buffer_queue[dma->current_buffer].source !=
-                          (inl(M2P_reg_base+M2P_OFFSET_BASE0) & M2P_MASK_BASE0))) ||
-           ((state == 1) && (dma->buffer_queue[dma->current_buffer].source !=
-                          (inl(M2P_reg_base+M2P_OFFSET_BASE1) & M2P_MASK_BASE1))))
-        {
-            DPRINTK("Need to sync up \n");
-            /*
-             *  We need to sync up the dma channel instance data.
-             */
-            nfb = 1;
-        }
-        
-        if(nfb)
-        {
-            /*
-             *  The current buffer is already used at this point.
-             */
-            dma->buffer_queue[dma->current_buffer].used = TRUE;
-            
-            /*
-             *  increment the number of used buffers.
-             */
-            dma->used_buffers++;
-            
-            DPRINTK("#%d", dma->current_buffer);
-            
-            
-            /*
-             *  Increment the current buffer position.
-             */
-            dma->current_buffer = (dma->current_buffer + 1) %
-                                   MAX_EP93XX_DMA_BUFFERS;
-        }
-    }
-    /*
-     *  Memory to memory channel.
-     */
-    else
-    {
-    }
-      
-    DPRINTK("STATUS - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_STATUS) );
-    DPRINTK("CONTROL - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_CONTROL) );
-    DPRINTK("REMAIN - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_REMAIN) );
-    DPRINTK("PPALLOC - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_PPALLOC) );
-    DPRINTK("BASE0 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE0) );
-    DPRINTK("MAXCNT0 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT0) );
-    DPRINTK("CURRENT0 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT0) );
-    DPRINTK("BASE1 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE1) );
-    DPRINTK("MAXCNT1 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT1) );
-    DPRINTK("CURRENT1 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
-    
-    DPRINTK("Buffer    buf_id     source       size       last       used \n");
-    for(loop = 0; loop < 32; loop ++)
-    {
-    DPRINTK("%d        0x%x       0x%x         0x%x        %d         %d \n",
-            loop, dma->buffer_queue[loop].buf_id, dma->buffer_queue[loop].source, dma->buffer_queue[loop].size,
-            dma->buffer_queue[loop].last, dma->buffer_queue[loop].used);
-    }
-    DPRINTK("pause     0x%x       0x%x         0x%x        %d         %d \n",
-            dma->pause_buf.buf_id, dma->pause_buf.source, dma->pause_buf.size,
-            dma->pause_buf.last, dma->pause_buf.used);
-    
-    DPRINTK("Pause - %d \n", dma->pause);
-    DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
-    DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
-    DPRINTK("total buffer - %d \n", dma->total_buffers);
-    DPRINTK("new buffers - %d \n", dma->new_buffers);
-    DPRINTK("current buffer - %d \n", dma->current_buffer);
-    DPRINTK("last buffer - %d \n", dma->last_buffer);
-    DPRINTK("used buffers - %d \n", dma->used_buffers);
-    DPRINTK("nfb - %d \n", nfb);
-    DPRINTK("state - %d \n", state);
-    
-    
-    
-
-    /*
-     *  restore interrupts.
-     */
-    local_irq_restore(flags);
-       
-        
-    /*
-     * Success.
-     */
-    return(0);
-#endif
-}
-
-/*****************************************************************************
- *
- *  void ep93xx_dma_flush(int handle)
- *
- *  Description: Flushes all queued buffers and transfers in progress
- *               for the given channel.  Return the buffer entries
- *               to the calling function.
- * 
- *  handle:     handle for the channel for which the flush is intended.
- *
- ****************************************************************************/
-int
-ep93xx_dma_flush(int handle)
-{
-    unsigned int loop, flags;
-    ep93xx_dma_t * dma;
-    int  channel;
-    //go DMAM2P * dma_regs;
-    unsigned int M2P_reg_base;
-
-    /*
-     *  Get the DMA hw channel # from the handle.
-     */
-    channel = dma_get_channel_from_handle(handle);
-    
-    /*
-     *  See if this is a valid handle.
-     */
-    if(channel < 0)
-    {
-        printk(KERN_ERR
-            "DMA Flush: Invalid dma handle.\n");
-        
-        /*
-         *  Fail.
-         */        
-        return(-EINVAL);
-    }
-    
-    DPRINTK("DMA %d: flush \n", channel);
-    
-    /*
-     *  Set up a pointer to the dma instance data for this channel
-     */    
-    dma = &dma_chan[channel];
-
-    /*
-     *  Mask interrupts and hold on to the original state.
-     */ 
-    local_irq_save(flags);
-    
-    for(loop = 0; loop < MAX_EP93XX_DMA_BUFFERS; loop++)
-    {
-        dma->buffer_queue[loop].buf_id = -1;
-    }
-    
-    /*
-     *  Set the Current and Last item to zero.
-     */
-    dma->current_buffer = 0;
-    dma->last_buffer = 0;
-
-    /*
-     *  Reset the Buffer counters
-     */
-    dma->used_buffers = 0;
-    dma->new_buffers = 0;
-    dma->total_buffers = 0;
-
-    /*
-     *  reset the Total bytes counter.
-     */
-    dma->total_bytes = 0;
-    
-    //go dma_regs = (DMAM2P *)(dma_chan[channel].reg_base);
-    M2P_reg_base = dma_chan[channel].reg_base;
-
-    /*
-     *  restore interrupts.
-     */
-    local_irq_restore(flags);
-
-    /*
-     *  Success.
-     */
-    return(0);
-}
-
-/*****************************************************************************
- *
- *  int ep93xx_dma_queue_full(int handle)
- *
- *  Description: Query to determine if the DMA queue of buffers for
- *              a given channel is full.  
- *              0 = queue is full
- *              1 = queue is not full
- *
- *  handle:     handle for the channel to query.
- *
- ****************************************************************************/
-int
-ep93xx_dma_queue_full(int handle)
-{
-    int list_full = 0;
-    unsigned int flags;
-    int  channel;
-
-    /*
-     *  Get the DMA hw channel # from the handle.
-     */
-    channel = dma_get_channel_from_handle(handle);
-    
-    /*
-     *  See if this is a valid handle.
-     */
-    if(channel < 0)
-    {
-        printk(KERN_ERR
-            "DMA Queue Full: Invalid dma handle.\n");
-        
-        /*
-         *  Fail.
-         */        
-        return(-EINVAL);
-    }
-    
-    DPRINTK("DMA %d: queue full \n", channel);
-
-    /*
-     *  Mask interrupts and hold on to the original state.
-     */ 
-    local_irq_save(flags);
-    
-    /*
-     *  If the last item is equal to the used item then 
-     *  the queue is full.
-     */
-    if(dma_chan[channel].total_buffers < MAX_EP93XX_DMA_BUFFERS)  
-    { 
-        list_full =  FALSE;
-    }
-    else
-    {
-        list_full = TRUE;
-    }
-    
-    /*
-     *  restore interrupts.
-     */
-    local_irq_restore(flags);
-        
-    return(list_full);
-}
-
-/*****************************************************************************
- *
- *  int ep93xx_dma_get_position(int handle, unsigned int * buf_id,
-                                unsigned int * total)
- *
- *  Description:    Takes two integer pointers and fills them with the start
- *                  and current address of the buffer currently transferring
- *                  on the specified DMA channel.
- *
- *  handle:         handle for the channel to query.
- *  buf_id:         pointer which is filled in with the buffer id for the
- *                  current buffer transferring on the dma channel.
- *                  buffer being transferred.
- *  total  :        pointer which is filled in with the total bytes
- *                  transferred on the channel.
- ****************************************************************************/
-int
-ep93xx_dma_get_position(int handle, unsigned int * buf_id,
-                        unsigned int * total)
-{
-    int  channel;
-    ep93xx_dma_t * dma;
-
-    /*
-     *  Get the DMA hw channel # from the handle.
-     */
-    channel = dma_get_channel_from_handle(handle);
-    
-    /*
-     *  See if this is a valid handle.
-     */
-    if(channel < 0)
-    {
-        printk(KERN_ERR "DMA Get Position: Invalid dma handle.\n");
-        
-        /*
-         *  Fail.
-         */        
-        return(-EINVAL);
-    }
-    
-    dma = &dma_chan[channel];
-    
-    /*
-     *  TODO: should irqs be disabled here?
-     */
-    
-    *buf_id = dma->buffer_queue[dma->current_buffer].buf_id;
-    
-    *total = dma->total_bytes;
-    
-    DPRINTK("DMA buf_id %d, total %d\n", *buf_id, *total);
-    
-    /*
-     *  Success.
-     */ 
-    return(0);
-}
-
-/*****************************************************************************
- *
- *  int ep93xx_dma_get_total(int handle)
- *
- *  Description:    Returns the total number of bytes transferred on the
- *                  specified channel since the channel was requested.
- *
- *  handle:     handle for the channel to query.
- *
- ****************************************************************************/
-int
-ep93xx_dma_get_total(int handle)
-{
-    int  channel;
-
-    /*
-     *  Get the DMA hw channel # from the handle.
-     */
-    channel = dma_get_channel_from_handle(handle);
-    
-    /*
-     *  See if this is a valid handle.
-     */
-    if(channel < 0)
-    {
-        printk(KERN_ERR "DMA Get Total: Invalid dma handle.\n");
-        
-        /*
-         *  Fail.
-         */        
-        return(-EINVAL);
-    }
-    
-    DPRINTK("DMA %d: total: %d \n", channel, dma_chan[channel].total_bytes);
-    
-    /*
-     *  Return the total number of bytes transferred on this channel since
-     *  it was requested.
-     */
-    return(dma_chan[channel].total_bytes);
-}
-
-/*****************************************************************************
- * ep93xx_dma_request
- *
- * Description: This function will allocate a DMA channel for a particular
- * hardware peripheral.  Before initiating a transfer on the allocated
- * channel, the channel must be set up and buffers have to queued up.
- *
- *  handle:     pointer to an integer which is filled in with a unique
- *              handle for this instance of the dma interface.
- *  device_id   string with the device name, primarily used by /proc.
- *  device      hardware device ID for which the requested dma channel will
- *              transfer data.
- * 
- ****************************************************************************/
-int
-ep93xx_dma_request(int * handle, const char *device_id,
-                   ep93xx_dma_dev_t device)
-{
-    ep93xx_dma_t *dma = NULL;
-    int channel, error = 0;
-    unsigned int loop;
-    //go DMAM2P * dma_regs;
-    unsigned int M2P_reg_base;
-    
-    /*
-     *  Check if the device requesting a DMA channel is a valid device.
-     */      
-    if((device >= UNDEF) || (device < 0))
-    {
-        /*
-         *  Invalid device, so return an error.
-         */
-        return(-ENODEV);
-    }
-    
-    /* 
-     *  We've got a valid hardware device requesting a DMA channel.
-     *  Now check if the device should open an M2P or M2M channel
-     */
-    if(device < 20)
-    {
-        channel = dma_open_m2p(device);
-    }
-    else
-    {
-        channel = dma_open_m2m(device);
-    }
-
-    /*
-     *  Check if we successfully opened a DMA channel
-     */
-    if(channel < 0)
-    {
-        printk(KERN_ERR
-               "%s: Could not open dma channel for this device.\n",
-               device_id);
-        
-        /*
-         *  Fail.
-         */        
-        return(-EBUSY);
-    }
-    
-    dma = &dma_chan[channel];
-
-    /*
-     *  Request the appropriate IRQ for the specified channel
-     */ 
-    if(channel < 10)
-    {
-        error = request_irq(dma->irq, dma_m2p_irq_handler, SA_INTERRUPT,
-                            device_id, (void *) dma);
-    }
-    else
-    {
-        error = request_irq(dma->irq, dma_m2m_irq_handler, SA_INTERRUPT,
-                            device_id, (void *) dma);
-    }
-    
-    /*
-     *  Check for any errors during the irq request
-     */ 
-    if(error)
-    {
-        printk(KERN_ERR
-               "%s: unable to request IRQ %d for DMA channel\n",
-               device_id, dma->irq);
-        /*
-         *  Fail.
-         */
-        return(error);
-    }
-    
-    /*
-     *  Generate a valid handle and exit.
-     *
-     *  Increment the last valid handle.
-     *  Check for wraparound (unlikely, but we like to be complete).
-     */
-    dma->last_valid_handle++;
-    
-    if( (dma->last_valid_handle & DMA_HANDLE_SPECIFIER_MASK) !=
-        (channel << 28) )
-    {
-        /*
-         *  If we wrapped around start over.
-         */
-        dma->last_valid_handle = (channel << 28) + 1;
-    }
-    
-    /*
-     *  Fill in the handle pointer with a valid handle for 
-     *  this dma channel instance.
-     */
-    *handle = dma->last_valid_handle;
-    
-    DPRINTK("Handle for channel %d: 0x%x\n", channel, *handle);
-    
-    /*
-     * Save the device ID and device name. 
-     */
-    dma->device = device;
-    dma->device_id = device_id;
-    
-    /*
-     *  Init all fields within the dma instance.
-     */
-    for(loop = 0; loop < MAX_EP93XX_DMA_BUFFERS; loop++)
-    {
-        dma->buffer_queue[loop].buf_id = -1;
-    }
-    
-    /*
-     *  Initialize all buffer queue variables.
-     */
-    dma->current_buffer = 0;
-    dma->last_buffer = 0;
-
-    dma->new_buffers = 0;
-    dma->used_buffers = 0;
-    dma->total_buffers = 0;
-
-    /*
-     *  Initialize the total bytes variable
-     */
-    dma->total_bytes = 0;
-
-    /*
-     *  Initialize the transfer and pause state variables to 0.
-     */
-    dma->xfer_enable = 0;
-    
-    dma->pause = 0;
-    
-    /*
-     *  Initialize the pause buffer structure.
-     */
-    dma->pause_buf.buf_id = -1;
-    
-    /*
-     *  Initialize the callback function and user data fields.
-     */
-    dma->callback = NULL;
-
-    /*
-     * User data used as a parameter for the Callback function.  The user
-     * sets up the data and sends it with the callback function.
-     */
-    dma->user_data = 0;
-    
-    //go dma_regs = (DMAM2P *)dma_chan[channel].reg_base;
-    M2P_reg_base = dma_chan[channel].reg_base;
-    
-    /*
-     *  Debugging message.
-     */
-    DPRINTK("Successfully requested dma channel %d\n", channel);
-    DPRINTK("STATUS - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_STATUS) );
-    DPRINTK("CONTROL - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_CONTROL) );
-    DPRINTK("REMAIN - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_REMAIN) );
-    DPRINTK("PPALLOC - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_PPALLOC) );
-    DPRINTK("BASE0 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE0) );
-    DPRINTK("MAXCNT0 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT0) );
-    DPRINTK("CURRENT0 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT0) );
-    DPRINTK("BASE1 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE1) );
-    DPRINTK("MAXCNT1 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT1) );
-    DPRINTK("CURRENT1 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
-    
-    DPRINTK("Buffer    source       size       last       used \n");
-    for(loop = 0; loop < 5; loop ++)
-    {
-    DPRINTK("%d        0x%x         0x%x        %d         %d \n",
-            loop, dma->buffer_queue[loop].source, dma->buffer_queue[loop].size,
-            dma->buffer_queue[loop].last, dma->buffer_queue[loop].used);
-    }
-    DPRINTK("pause     0x%x         0x%x        %d         %d \n",
-            dma->pause_buf.source, dma->pause_buf.size,
-            dma->pause_buf.last, dma->pause_buf.used);
-    
-    DPRINTK("Pause - %d \n", dma->pause);
-    DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
-    DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
-    DPRINTK("total buffer - %d \n", dma->total_buffers);
-    DPRINTK("new buffers - %d \n", dma->new_buffers);
-    DPRINTK("current buffer - %d \n", dma->current_buffer);
-    DPRINTK("last buffer - %d \n", dma->last_buffer);
-    DPRINTK("used buffers - %d \n", dma->used_buffers);
-    
-    DPRINTK("CURRENT1 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
-    DPRINTK("VIC0IRQSTATUS - 0x%x, VIC0INTENABLE - 0x%x \n",
-            *(unsigned int *)(VIC0IRQSTATUS),
-            *(unsigned int *)(VIC0INTENABLE));
-    
-    /*
-     *  Success.
-     */
-    return(0);
-}
-
-/*****************************************************************************
- *
- * ep93xx_dma_free
- *
- * Description: This function will free the dma channel for future requests.
- *
- *  handle:     handle for the channel to be freed.
- * 
- ****************************************************************************/
-int
-ep93xx_dma_free(int handle)
-{
-    ep93xx_dma_t *dma;
-    //go DMAM2P * regs_m2p;
-    //go DMAM2M * regs_m2m;
-    unsigned int M2M_reg_base, M2P_reg_base, uiCONTROL;    
-    int channel;
-    
-    /*
-     *  Get the DMA hw channel # from the handle.
-     */
-    channel = dma_get_channel_from_handle(handle);
-    
-    /*
-     *  See if this is a valid handle.
-     */
-    if(channel < 0)
-    {
-        printk(KERN_ERR "DMA Free: Invalid dma handle.\n");
-        
-        /*
-         *  Fail.
-         */        
-        return(-EINVAL);
-    }
-    
-    /*
-     *  Get a pointer to the dma instance.
-     */
-    dma = &dma_chan[channel];
-    
-    /*
-     *  Disable the dma channel
-     */
-    if(channel < 10)
-    {
-        /*
-         *  M2P channel
-         */
-        //go regs_m2p = dma->reg_base; 
-        M2P_reg_base = dma->reg_base;
-        
-        //go regs_m2p->CONTROL.Field.ENABLE = 0;
-        uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
-        uiCONTROL &= ~CONTROL_M2P_ENABLE;
-        outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
-    }
-    else
-    {
-        /*
-         *  M2M channel
-         */
-        //go regs_m2m = dma->reg_base; 
-        M2M_reg_base = dma->reg_base;
-        
-        //go regs_m2m->CONTROL.Field.ENABLE = 0;
-        uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
-        uiCONTROL &= ~CONTROL_M2M_ENABLE;
-        outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
-    }
-
-    /*
-     *  Free the interrupt servicing this dma channel
-     */ 
-    free_irq(dma->irq, (void *) dma);
-    
-    /*
-     *  Decrement the reference count for this instance of the dma interface
-     */ 
-    dma->ref_count--;
-    
-    /*
-     *  Set the transfer and pause state variables to 0
-     *  (unititialized state).
-     */
-    dma->xfer_enable = 0;
-    dma->pause = 0;
-    
-    /*
-     *  Debugging message.
-     */
-    DPRINTK("Successfully freed dma channel %d\n", channel);
-    
-    /*
-     *  Success.
-     */
-    return(0);
-}
-
-/*****************************************************************************
- *
- * ep93xx_dma_init(void)
- *
- * Description: This function is called during system initialization to 
- * setup the interrupt number and register set base address for each DMA
- * channel.
- * 
- ****************************************************************************/
-static int __init
-ep93xx_dma_init(void)
-{
-    int channel;
-        
-    /*
-     * Init some values in each dma instance.
-     */
-    for(channel = 0; channel < MAX_EP93XX_DMA_CHANNELS; channel++)
-    {
-        /*
-         *  IRQ for the specified dma channel.
-         */
-        dma_chan[channel].irq = IRQ_DMAM2P0 + channel;
-        
-        /*
-         *  Initial value of the dma channel handle.
-         */
-        dma_chan[channel].last_valid_handle = channel << 28;
-            
-        /*
-         *  Give the instance a pointer to the dma channel register
-         *  base.
-         */
-        if(channel < 10)
-        {
-            dma_chan[channel].reg_base = DMAM2PChannelBase[channel];
-        }
-        else
-        {
-            dma_chan[channel].reg_base = DMAM2MChannelBase[channel - 10];
-        }
-        
-        /*
-         *  Initialize the reference count for this channel.
-         */
-        dma_chan[channel].ref_count = 0;
-    }
-    
-    DPRINTK("DMA Interface intitialization complete\n");
-        
-    /*
-     * Success
-     */
-    return 0;
-}
-
-__initcall(ep93xx_dma_init);
-
-
+/******************************************************************************
+ * arch/arm/mach-ep9312/dma_ep93xx.c
+ *
+ * Support functions for the ep93xx internal DMA channels.
+ * (see also Documentation/arm/ep93xx/dma.txt)
+ *
+ * Copyright (C) 2003  Cirrus Logic
+ *
+ * A large portion of this file is based on the dma api implemented by
+ * Nicolas Pitre, dma-sa1100.c, copyrighted 2000.
+ * 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ ****************************************************************************/
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/mach/dma.h>
+#include "dma_ep93xx.h"
+
+/*****************************************************************************
+ *
+ * Debugging macros
+ *
+ ****************************************************************************/
+#undef DEBUG
+/*#define DEBUG   1 */
+#ifdef DEBUG
+#define DPRINTK( x... )  printk( ##x )
+#else
+#define DPRINTK( x... )
+#endif
+
+/*****************************************************************************
+ *
+ * static global variables
+ *
+ ****************************************************************************/
+ep93xx_dma_t dma_chan[MAX_EP93XX_DMA_CHANNELS];
+
+/*
+ *  Maximum physical DMA buffer size
+ */
+#define MAX_DMA_SIZE        0xffff
+#define MAX_DMA_ORDER       12
+
+/*
+ *  lock used to protect the list of dma channels while searching for a free
+ *  channel during dma_request.
+ */
+static spinlock_t dma_list_lock;
+
+/*****************************************************************************
+ *
+ *  Internal DMA processing functions.
+ *
+ ****************************************************************************/
+/*****************************************************************************
+ *
+ *  get_dma_channel_from_handle()
+ *
+ *  If Handle is valid, returns the DMA channel # (0 to 9 for channels 1-10)
+ *  If Handle is not valid, returns -1.
+ *
+ ****************************************************************************/
+static int
+dma_get_channel_from_handle(int handle)
+{
+    int channel;  
+    
+    /*
+     *  Get the DMA channel # from the handle.
+     */
+    channel = ((int)handle & DMA_HANDLE_SPECIFIER_MASK) >> 28;
+    
+    /*
+     *  See if this is a valid handle.
+     */
+    if(dma_chan[channel].last_valid_handle != (int)handle)
+    {
+        DPRINTK("DMA ERROR - invalid handle 0x%x \n", handle);
+        /*
+         *  Fail, invalid handle
+         */ 
+        return(-1);
+    }
+    
+    /*
+     *  See if this instance is still open
+     */    
+    if(!dma_chan[channel].ref_count )
+    {
+        /*
+         *  Fail, instance closed
+         */ 
+        return(-1);
+    } 
+
+    return(channel);
+}
+
+/*****************************************************************************
+ *
+ * dma_m2m_irq_handler
+ *
+ * TODO: need to add support for M2M interrupt handling!!!
+ *
+ ****************************************************************************/
+static void
+dma_m2m_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+    ep93xx_dma_t *dma = (ep93xx_dma_t *)dev_id;
+    //go DMAM2M volatile * dma_regs = (DMAM2M *)dma->reg_base;
+    unsigned int M2M_reg_base = dma->reg_base;
+    ep93xx_dma_dev_t dma_int = UNDEF_INT;
+    unsigned int read_back, loop, uiCONTROL, uiINTERRUPT;
+    
+    /*
+     *  Determine what kind of dma interrupt this is.
+     */
+    //go if(dma_regs->INTERRUPT.Field.STALLINT)
+    //go else if(dma_regs->INTERRUPT.Field.NFBINT)
+    //go else if(dma_regs->INTERRUPT.Field.DONEINT)
+
+    if( inl(M2M_reg_base+M2M_OFFSET_INTERRUPT) & INTERRUPT_M2M_STALLINT )
+        dma_int = STALL;
+    else if( inl(M2M_reg_base+M2M_OFFSET_INTERRUPT) & INTERRUPT_M2M_NFBINT )
+        dma_int = NFB;
+    else if( inl(M2M_reg_base+M2M_OFFSET_INTERRUPT) & INTERRUPT_M2M_DONEINT )
+        dma_int = DONE;
+        
+    DPRINTK("IRQ: b=%#x st=%#x\n", (int)dma->current_buffer->source, dma_int);
+    
+    /*
+     * Stall Interrupt: The Channel is stalled, meaning nothing is 
+     * programmed to transfer right now.  So, we're back to the 
+     * beginnning.  If there's a buffer to transfer, program it into
+     * max and base 0 registers.
+     */
+    if(dma_int == STALL)
+    {            
+        DPRINTK("1  ");
+        
+        if(dma->total_buffers)
+        {
+            /*
+             *  The current_buffer has already been tranfered, so add the 
+             *  byte count to the total_bytes field.
+             */
+            dma->total_bytes = dma->total_bytes +
+                dma->buffer_queue[dma->current_buffer].size;
+        
+            /*
+             *  Mark the current_buffer as used.
+             */
+            dma->buffer_queue[dma->current_buffer].used = TRUE;
+    
+            /*
+             *  Increment the used buffer counter
+             */
+            dma->used_buffers++;
+            
+            DPRINTK("#%d", dma->current_buffer);
+            
+            /*
+             *  Increment the current_buffer
+             */
+            dma->current_buffer = (dma->current_buffer + 1) %
+                                  MAX_EP93XX_DMA_BUFFERS;
+
+            /*
+             *  check if there's a new buffer to transfer.  
+             */
+            if(dma->new_buffers && dma->xfer_enable)
+            {   
+                /*
+                 *  We have a new buffer to transfer so program in the buffer
+                 *  values.  Since a STALL interrupt was triggered, we
+                 *  program the buffer descriptor 0
+                 *  
+                 *  Set the SAR_BASE/DAR_BASE/BCR registers with values from the
+                 *  next buffer in the queue.
+                 */
+                //go dma_regs->SAR_BASE0.Field.SAR_BASE0 = 
+                //go  dma->buffer_queue[dma->current_buffer].source;
+                outl( dma->buffer_queue[dma->current_buffer].source,
+                      M2M_reg_base + M2M_OFFSET_SAR_BASE0 );
+
+                //go dma_regs->DAR_BASE0.Field.DAR_BASE0 = 
+                //go  dma->buffer_queue[dma->current_buffer].dest;
+                outl( dma->buffer_queue[dma->current_buffer].dest,
+                      M2M_reg_base + M2M_OFFSET_DAR_BASE0 );
+                      
+                //go dma_regs->BCR0.Field.BCR0 =
+                //go  dma->buffer_queue[dma->current_buffer].size;
+                outl( dma->buffer_queue[dma->current_buffer].size,
+                      M2M_reg_base + M2M_OFFSET_BCR0 );
+                
+                /*
+                 *  Decrement the new buffer counter
+                 */
+                dma->new_buffers--;
+                
+                /*
+                 *  If there's a second new buffer, we program the second
+                 *  buffer descriptor.
+                 */
+                if(dma->new_buffers)
+                {
+                    DPRINTK("A  ");
+                    
+                    /*  
+                     *  Set the SAR_BASE/DAR_BASE/BCR registers with values from the
+                     *  next buffer in the queue.
+                     */
+                    //go dma_regs->SAR_BASE1.Field.SAR_BASE1 = 
+                    //go  dma->buffer_queue[(dma->current_buffer + 1) %
+                    //go  MAX_EP93XX_DMA_BUFFERS].source;
+                    
+                    outl( dma->buffer_queue[(dma->current_buffer + 1) %
+                          MAX_EP93XX_DMA_BUFFERS].source,
+                          M2M_reg_base+M2M_OFFSET_SAR_BASE1 );
+                        
+                    //go dma_regs->DAR_BASE0.Field.DAR_BASE0 = 
+                    //go  dma->buffer_queue[(dma->current_buffer + 1) %
+                    //go  MAX_EP93XX_DMA_BUFFERS].dest;
+                
+                    outl( dma->buffer_queue[(dma->current_buffer + 1) %
+                          MAX_EP93XX_DMA_BUFFERS].dest,
+                          M2M_reg_base+M2M_OFFSET_DAR_BASE0 );
+
+                    //go dma_regs->BCR0.Field.BCR0 =
+                    //go  dma->buffer_queue[(dma->current_buffer + 1) %
+                    //go  MAX_EP93XX_DMA_BUFFERS].size;
+                                    
+                    outl( dma->buffer_queue[(dma->current_buffer + 1) %
+                          MAX_EP93XX_DMA_BUFFERS].size,
+                          M2M_reg_base+M2M_OFFSET_BCR0 );
+
+                    /*
+                     *  Decrement the new buffer counter
+                     */
+                    dma->new_buffers--;
+                    
+                    /*
+                     *  Enable the NFB Interrupt.
+                     */
+                    //go dma_regs->CONTROL.Field.NFBINTEN = 1;
+                    uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+                    uiCONTROL |= CONTROL_M2M_NFBINTEN;
+                    outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+                }
+            }
+            /*
+             *  Read my lips.  No new buffers.
+             */
+            else
+            {
+                DPRINTK("2 \n");
+                /*
+                 *  There's a chance we setup both buffer descriptors, but
+                 *  didn't service the NFB quickly enough, causing the channel
+                 *  to transfer both buffers, then enter the stall state.
+                 *  So, we need to be able to process the second buffer.
+                 */
+                if((dma->used_buffers + dma->new_buffers) < dma->total_buffers)
+                {
+                    DPRINTK("3 ");
+                    
+                    /*
+                     *  The current_buffer has already been tranfered, so add the 
+                     *  byte count to the total_bytes field.
+                     */
+                    dma->total_bytes = dma->total_bytes +
+                        dma->buffer_queue[dma->current_buffer].size;
+        
+                    /*
+                     *  Mark the current_buffer as used.
+                     */
+                    dma->buffer_queue[dma->current_buffer].used = TRUE;
+    
+                    /*
+                     *  Increment the used buffer counter
+                     */
+                    dma->used_buffers++;
+                    
+                    DPRINTK("#%d", dma->current_buffer);
+                    
+                    /*
+                     *  Increment the current buffer pointer.
+                     */
+                    dma->current_buffer = (dma->current_buffer + 1) %
+                                          MAX_EP93XX_DMA_BUFFERS;
+                    
+                }
+                
+                /*
+                 *  No new buffers to transfer, so disable the channel.
+                 */
+                //go dma_regs->CONTROL.Field.ENABLE = 0;
+                uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+                uiCONTROL &= ~CONTROL_M2M_ENABLE;
+                outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+
+                /*
+                 *  Indicate that this channel is in the pause by starvation
+                 *  state by setting the pause bit to true.
+                 */
+                dma->pause = TRUE;
+                
+                DPRINTK("End of stall handling. \n");
+                DPRINTK("STATUS - 0x%x \n",     inl(M2M_reg_base+M2M_OFFSET_STATUS) );
+                DPRINTK("CONTROL - 0x%x \n",    inl(M2M_reg_base+M2M_CONTROL.Value) );
+                DPRINTK("REMAIN - 0x%x \n",     inl(M2M_reg_base+M2M_REMAIN.Value) );
+                DPRINTK("PPALLOC - 0x%x \n",    inl(M2M_reg_base+M2M_PPALLOC.Value) );
+                DPRINTK("BASE0 - 0x%x \n",      inl(M2M_reg_base+M2M_BASE0.Value) );
+                DPRINTK("MAXCNT0 - 0x%x \n",    inl(M2M_reg_base+M2M_MAXCNT0.Value) );
+                DPRINTK("CURRENT0 - 0x%x \n",   inl(M2M_reg_base+M2M_CURRENT0.Value) );
+                DPRINTK("BASE1 - 0x%x \n",      inl(M2M_reg_base+M2M_BASE1.Value) );
+                DPRINTK("MAXCNT1 - 0x%x \n",    inl(M2M_reg_base+M2M_MAXCNT1.Value) );
+                DPRINTK("CURRENT1 - 0x%x \n",   inl(M2M_reg_base+M2M_CURRENT1.Value) );
+    
+                DPRINTK("Buffer    buf_id     source       size       last       used \n");
+                for(loop = 0; loop < 32; loop ++)
+                {
+                DPRINTK("%d        0x%x        0x%x         0x%x        %d         %d \n",
+                        loop, dma->buffer_queue[loop].buf_id, dma->buffer_queue[loop].source, 
+                        dma->buffer_queue[loop].size,
+                        dma->buffer_queue[loop].last, dma->buffer_queue[loop].used);
+                }
+                DPRINTK("pause     0x%x        0x%x         0x%x        %d         %d \n",
+                        dma->pause_buf.buf_id, dma->pause_buf.source, dma->pause_buf.size,
+                        dma->pause_buf.last, dma->pause_buf.used);
+    
+                DPRINTK("Pause - %d \n", dma->pause);
+                DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
+                DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
+                DPRINTK("total buffer - %d \n", dma->total_buffers);
+                DPRINTK("new buffers - %d \n", dma->new_buffers);
+                DPRINTK("current buffer - %d \n", dma->current_buffer);
+                DPRINTK("last buffer - %d \n", dma->last_buffer);
+                DPRINTK("used buffers - %d \n", dma->used_buffers);
+                DPRINTK("callback addr - 0x%x \n", dma->callback);
+            }
+        }
+        /*
+         *  Total number of buffers in the queue is 0.
+         */
+        else
+        {
+            /*
+             *  No buffers to transfer, or old buffers to mark as used, 
+             *  so Disable the channel
+             */
+            //go dma_regs->CONTROL.Field.ENABLE = 0;
+            uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+            uiCONTROL &= ~CONTROL_M2M_ENABLE;
+            outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+
+            /*
+             *  Must read the control register back after a write.
+             */
+            //go read_back = dma_regs->CONTROL.Value;
+            read_back = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+
+            /*
+             *  Indicate that this channel is in the pause by
+             *  starvation state by setting the pause bit to true.
+             */
+            dma->pause = TRUE;
+        } 
+    }
+    /*
+     *  Next Frame Buffer Interrupt.  If there's a new buffer program it  
+     *  Check if this is the last buffer in the transfer,
+     *  and if it is, disable the NFB int to prevent being
+     *  interrupted for another buffer when we know there won't be 
+     *  another.
+     */
+    if(dma_int == NFB)
+    {
+        DPRINTK("5  ");
+        
+        if(dma->total_buffers)
+        {
+            DPRINTK("6  ");
+            /*
+             *  The iCurrentBuffer has already been transfered.  so add the 
+             *  byte count from the current buffer to the total byte count.
+             */
+            dma->total_bytes = dma->total_bytes +
+                               dma->buffer_queue[dma->current_buffer].size;
+
+            /*
+             *  Mark the Current Buffer as used.
+             */
+            dma->buffer_queue[dma->current_buffer].used = TRUE;
+
+            /* 
+             *  Increment the used buffer counter
+             */
+            dma->used_buffers++;
+            
+            DPRINTK("#%d", dma->current_buffer);
+            
+            if((dma->buffer_queue[
+                (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].last) ||
+               (dma->new_buffers == 0) || (dma->xfer_enable == FALSE))
+            {
+                DPRINTK("7  "); 
+                
+                /*
+                 *  This is the last Buffer in this transaction, so disable
+                 *  the NFB interrupt.  We shouldn't get an NFB int when the
+                 *  FSM moves to the ON state where it would typically get the
+                 *  NFB int indicating a new buffer can be programmed.
+                 *  Instead, once in the ON state, the DMA will just proceed
+                 *  to complet the transfer of the current buffer, move the
+                 *  FSB directly to the STALL state where a STALL interrupt
+                 *  will be generated.
+                 */
+                //go dma_regs->CONTROL.Field.NFBINTEN = 0;
+                uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+                uiCONTROL &= ~CONTROL_M2M_NFBINTEN ;
+                outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+
+                /*
+                 *  The current buffer has been transferred, so increment
+                 *  the current buffer counter to reflect this.
+                 */
+                dma->current_buffer = (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS;
+                
+                DPRINTK("End of NFB handling. \n");
+                DPRINTK("STATUS - 0x%x \n",     inl(M2M_reg_base+M2M_OFFSET_STATUS) );
+                DPRINTK("CONTROL - 0x%x \n",    inl(M2M_reg_base+M2M_OFFSET_CONTROL) );
+                DPRINTK("REMAIN - 0x%x \n",     inl(M2M_reg_base+M2M_OFFSET_REMAIN) );
+                DPRINTK("PPALLOC - 0x%x \n",    inl(M2M_reg_base+M2M_OFFSET_PPALLOC) );
+                DPRINTK("BASE0 - 0x%x \n",      inl(M2M_reg_base+M2M_OFFSET_BASE0) );
+                DPRINTK("MAXCNT0 - 0x%x \n",    inl(M2M_reg_base+M2M_OFFSET_MAXCNT0) );
+                DPRINTK("CURRENT0 - 0x%x \n",   inl(M2M_reg_base+M2M_OFFSET_CURRENT0) );
+                DPRINTK("BASE1 - 0x%x \n",      inl(M2M_reg_base+M2M_OFFSET_BASE1) );
+                DPRINTK("MAXCNT1 - 0x%x \n",    inl(M2M_reg_base+M2M_OFFSET_MAXCNT1) );
+                DPRINTK("CURRENT1 - 0x%x \n",   inl(M2M_reg_base+M2M_OFFSET_CURRENT1) );
+    
+                DPRINTK("Buffer    buf_id     source       size       last       used \n");
+                for(loop = 0; loop < 32; loop ++)
+                {
+                DPRINTK("%d        0x%x        0x%x         0x%x        %d         %d \n",
+                        loop, dma->buffer_queue[loop].buf_id, dma->buffer_queue[loop].source, 
+                        dma->buffer_queue[loop].size,
+                        dma->buffer_queue[loop].last, dma->buffer_queue[loop].used);
+                }
+                DPRINTK("pause     0x%x        0x%x         0x%x        %d         %d \n",
+                        dma->pause_buf.buf_id, dma->pause_buf.source, dma->pause_buf.size,
+                        dma->pause_buf.last, dma->pause_buf.used);
+    
+                DPRINTK("Pause - %d \n", dma->pause);
+                DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
+                DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
+                DPRINTK("total buffer - %d \n", dma->total_buffers);
+                DPRINTK("new buffers - %d \n", dma->new_buffers);
+                DPRINTK("current buffer - %d \n", dma->current_buffer);
+                DPRINTK("last buffer - %d \n", dma->last_buffer);
+                DPRINTK("used buffers - %d \n", dma->used_buffers);
+                DPRINTK("callback addr - 0x%x \n", dma->callback);
+                
+            }
+            else if(dma->new_buffers)
+            {
+                DPRINTK("8  ");
+                /* 
+                 *  we have a new buffer, so increment the current buffer to
+                 *  point to the next buffer, which is already programmed into
+                 *  the DMA. Next time around, it'll be pointing to the
+                 *  current buffer.
+                 */
+                dma->current_buffer = (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS;
+        
+                /*
+                 *  we know we have a new buffer to program as the next
+                 *  buffer, so check which set of SAR_BASE/DAR_BASE/BCR registers
+                 *  to program.  
+                 */
+                //go if(dma_regs->STATUS.Field.NB)
+                if( inl(M2M_reg_base+M2M_OFFSET_STATUS) & STATUS_M2M_NB )
+                {
+                    /*
+                     *  Set the SAR_BASE1/DAR_BASE1/BCR1 registers with values from the
+                     *  next buffer in the queue.
+                     */
+                    //go dma_regs->SAR_BASE1.Field.SAR_BASE1 = dma->buffer_queue[
+                    //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source;
+
+                    outl( dma->buffer_queue[(dma->current_buffer + 1) % 
+                            MAX_EP93XX_DMA_BUFFERS].source,
+                         M2M_reg_base+M2M_OFFSET_SAR_BASE1 );
+                         
+                    //go dma_regs->DAR_BASE1.Field.DAR_BASE1 = dma->buffer_queue[
+                    //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].dest;
+                    
+                    outl( dma->buffer_queue[(dma->current_buffer + 1) % 
+                            MAX_EP93XX_DMA_BUFFERS].dest,
+                         M2M_reg_base+M2M_OFFSET_DAR_BASE1 );
+                         
+                    //go dma_regs->BCR1.Field.BCR1 = dma->buffer_queue[
+                    //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size;
+                    
+                    outl( dma->buffer_queue[(dma->current_buffer + 1) % 
+                            MAX_EP93XX_DMA_BUFFERS].size,
+                         M2M_reg_base+M2M_OFFSET_BCR1 );
+                }
+                else
+                {
+                    /*
+                     *  Set the SAR_BASE0/DAR_BASE0/BCR0 registers with values from the
+                     *  next buffer in the queue.
+                     */
+                    //go dma_regs->SAR_BASE0.Field.SAR_BASE0 = dma->buffer_queue[
+                    //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source;
+
+                    outl( dma->buffer_queue[(dma->current_buffer + 1) % 
+                            MAX_EP93XX_DMA_BUFFERS].source,
+                         M2M_reg_base+M2M_OFFSET_SAR_BASE0 );
+                         
+                    //go dma_regs->DAR_BASE0.Field.DAR_BASE0 = dma->buffer_queue[
+                    //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].dest;
+                    
+                    outl( dma->buffer_queue[(dma->current_buffer + 1) % 
+                            MAX_EP93XX_DMA_BUFFERS].dest,
+                         M2M_reg_base+M2M_OFFSET_DAR_BASE0 );
+                         
+                    //go dma_regs->BCR0.Field.BCR0 = dma->buffer_queue[
+                    //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size;
+
+                    outl( dma->buffer_queue[(dma->current_buffer + 1) % 
+                            MAX_EP93XX_DMA_BUFFERS].size,
+                         M2M_reg_base+M2M_OFFSET_BCR0 );
+                }
+                
+                /*
+                 *  Decrement the new buffers counter
+                 */
+                dma->new_buffers--;
+            }
+        }
+        /*
+         *  Total number of buffers is 0 - really we should never get here,
+         *  but just in case.
+         */ 
+        else
+        {
+            DPRINTK("9 \n");
+            
+            /*
+             *  No new buffers to transfer, so Disable the channel
+             */
+            //go dma_regs->CONTROL.Field.ENABLE = 0;
+            uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+            uiCONTROL &= ~CONTROL_M2M_ENABLE;
+            outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+            
+            /*
+             *  Indicate that the channel is paused by starvation.
+             */
+            dma->pause = 1;
+        }        
+    }
+                
+    /* 
+     *  Done interrupt generated, indicating that the transfer is complete.
+     */
+    if(dma_int == DONE)
+    {
+        /*
+         *  Clear the interrupt.
+         */
+        //go dma_regs->INTERRUPT.Field.DONEINT = 0;
+        uiINTERRUPT = inl(M2M_reg_base+M2M_OFFSET_INTERRUPT);
+        uiINTERRUPT &= ~INTERRUPT_M2M_DONEINT;
+        outl( uiINTERRUPT, M2M_reg_base+M2M_OFFSET_INTERRUPT );
+        
+        /*
+         *  Disable the channel.
+         */
+        //go dma_regs->CONTROL.Field.ENABLE = 0;
+        uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+        uiCONTROL &= ~CONTROL_M2M_ENABLE;
+        outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+        
+        /*
+         *  Indicate that we're in the stopped mode.
+         */
+        dma->pause = 1;
+        dma->xfer_enable = 0;
+    }
+
+    /*
+     *  Make sure the interrupt was valid, and if it was, then check
+     *  if a callback function was installed for this DMA channel.  If a
+     *  callback was installed call it.
+     */  
+    if((dma_int != UNDEF_INT) && dma->callback)
+    {
+        dma->callback(dma_int, dma->device, dma->user_data);
+    }    
+}
+
+/*****************************************************************************
+ *
+ * dma_m2p_irq_handler
+ *
+ * 
+ *
+ ****************************************************************************/
+static void
+dma_m2p_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+    ep93xx_dma_t *dma = (ep93xx_dma_t *) dev_id;
+    //go DMAM2P volatile * dma_regs = (DMAM2P *)dma->reg_base;
+    unsigned int M2P_reg_base = dma->reg_base;
+    unsigned int read_back;
+    ep93xx_dma_dev_t dma_int = UNDEF_INT;
+    unsigned int loop, uiCONTROL, uiINTERRUPT;
+    
+    /*
+     *  Determine what kind of dma interrupt this is.
+     */
+    //go if(dma_regs->INTERRUPT.Field.STALLINT)
+    //go else if(dma_regs->INTERRUPT.Field.NFBINT)
+    //go else if(dma_regs->INTERRUPT.Field.CHERRORINT)
+
+    if( inl(M2P_reg_base+M2P_OFFSET_INTERRUPT) & INTERRUPT_M2P_STALLINT )
+        dma_int = STALL;
+    else if( inl(M2P_reg_base+M2P_OFFSET_INTERRUPT) & INTERRUPT_M2P_NFBINT )
+        dma_int = NFB;
+    else if( inl(M2P_reg_base+M2P_OFFSET_INTERRUPT) & INTERRUPT_M2P_CHERRORINT )
+        dma_int = CHERROR;
+
+    /*
+     *  Stall Interrupt: The Channel is stalled, meaning nothing is 
+     *  programmed to transfer right now.  So, we're back to the 
+     *  beginnning.  If there's a buffer to transfer, program it into
+     *  max and base 0 registers.
+     */
+    if(dma_int == STALL)
+    {            
+        DPRINTK("1  ");
+        
+        if(dma->total_buffers)
+        {
+            /*
+             *  The current_buffer has already been tranfered, so add the 
+             *  byte count to the total_bytes field.
+             */
+            dma->total_bytes = dma->total_bytes +
+                dma->buffer_queue[dma->current_buffer].size;
+        
+            /*
+             *  Mark the current_buffer as used.
+             */
+            dma->buffer_queue[dma->current_buffer].used = TRUE;
+    
+            /*
+             *  Increment the used buffer counter
+             */
+            dma->used_buffers++;
+            
+            DPRINTK("#%d", dma->current_buffer);
+            
+            /*
+             *  Increment the current_buffer
+             */
+            dma->current_buffer = (dma->current_buffer + 1) %
+                                  MAX_EP93XX_DMA_BUFFERS;
+
+            /*
+             *  check if there's a new buffer to transfer.  
+             */
+            if(dma->new_buffers && dma->xfer_enable)
+            {   
+                /*
+                 *  We have a new buffer to transfer so program in the buffer
+                 *  values.  Since a STALL interrupt was triggered, we
+                 *  program the base0 and maxcnt0
+                 *  
+                 *  Set the MAXCNT0 register with the buffer size
+                 */
+                //go dma_regs->MAXCNT0.Field.MAXCNT0 = 
+                //go  dma->buffer_queue[dma->current_buffer].size;
+                outl( dma->buffer_queue[dma->current_buffer].size,
+                    M2P_reg_base+M2P_OFFSET_MAXCNT0 );
+
+                /*
+                 *  Set the BASE0 register with the buffer base address
+                 */
+                //go dma_regs->BASE0.Field.BASE0 = 
+                //go  dma->buffer_queue[dma->current_buffer].source;
+                outl( dma->buffer_queue[dma->current_buffer].source,
+                    M2P_reg_base+M2P_OFFSET_BASE0 );
+                
+                /*
+                 *  Decrement the new buffer counter
+                 */
+                dma->new_buffers--;
+                
+                if(dma->new_buffers)
+                {
+                    DPRINTK("A  ");
+                    /*  
+                     *  Set the MAXCNT1 register with the buffer size
+                     */
+                    //go dma_regs->MAXCNT1.Field.MAXCNT1 = 
+                    //go  dma->buffer_queue[(dma->current_buffer + 1) %
+                    //go              MAX_EP93XX_DMA_BUFFERS].size;
+                    outl( dma->buffer_queue[(dma->current_buffer + 1) %
+                                      MAX_EP93XX_DMA_BUFFERS].size,
+                          M2P_reg_base+M2P_OFFSET_MAXCNT1 );
+                          
+                    /*
+                     *  Set the BASE1 register with the buffer base address
+                     */
+                    //go dma_regs->BASE1.Field.BASE1 = 
+                    //go  dma->buffer_queue[dma->current_buffer + 1 %
+                    //go              MAX_EP93XX_DMA_BUFFERS].source;
+                    outl( dma->buffer_queue[dma->current_buffer + 1 %
+                                      MAX_EP93XX_DMA_BUFFERS].source,
+                        M2P_reg_base+M2P_OFFSET_BASE1 );
+                        
+                    /*
+                     *  Decrement the new buffer counter
+                     */
+                    dma->new_buffers--;
+                    
+                    /*
+                     *  Enable the NFB Interrupt.
+                     */
+                    //go dma_regs->CONTROL.Field.NFBINTEN = 1;
+                    uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
+                    uiCONTROL |= CONTROL_M2P_NFBINTEN;
+                    outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
+                }
+            }
+            /*
+             *  No new buffers.
+             */
+            else
+            {
+                DPRINTK("2 \n");
+                /*
+                 *  There's a chance we setup both buffer descriptors, but
+                 *  didn't service the NFB quickly enough, causing the channel
+                 *  to transfer both buffers, then enter the stall state.
+                 *  So, we need to be able to process the second buffer.
+                 */
+                if((dma->used_buffers + dma->new_buffers) < dma->total_buffers)
+                {
+                    DPRINTK("3 ");
+                    
+                    /*
+                     *  The current_buffer has already been tranfered, so add the 
+                     *  byte count to the total_bytes field.
+                     */
+                    dma->total_bytes = dma->total_bytes +
+                        dma->buffer_queue[dma->current_buffer].size;
+        
+                    /*
+                     *  Mark the current_buffer as used.
+                     */
+                    dma->buffer_queue[dma->current_buffer].used = TRUE;
+    
+                    /*
+                     *  Increment the used buffer counter
+                     */
+                    dma->used_buffers++;
+                    
+                    DPRINTK("#%d", dma->current_buffer);
+                    
+                    /*
+                     *  Increment the current buffer pointer.
+                     */
+                    dma->current_buffer = (dma->current_buffer + 1) %
+                                          MAX_EP93XX_DMA_BUFFERS;
+                    
+                }
+                
+                /*
+                 *  No new buffers to transfer, so disable the channel.
+                 */
+                //go dma_regs->CONTROL.Field.ENABLE = 0;
+                uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
+                uiCONTROL &= ~CONTROL_M2P_ENABLE;
+                outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
+
+                /*
+                 *  Indicate that this channel is in the pause by starvation
+                 *  state by setting the pause bit to true.
+                 */
+                dma->pause = TRUE;
+                
+                DPRINTK("STATUS - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_STATUS) );
+                DPRINTK("CONTROL - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_CONTROL) );
+                DPRINTK("REMAIN - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_REMAIN) );
+                DPRINTK("PPALLOC - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_PPALLOC) );
+                DPRINTK("BASE0 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE0) );
+                DPRINTK("MAXCNT0 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT0) );
+                DPRINTK("CURRENT0 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT0) );
+                DPRINTK("BASE1 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE1) );
+                DPRINTK("MAXCNT1 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT1) );
+                DPRINTK("CURRENT1 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
+    
+                DPRINTK("Buffer    buf_id     source       size       last       used \n");
+                for(loop = 0; loop < 32; loop ++)
+                {
+                DPRINTK("%d        0x%x        0x%x         0x%x        %d         %d \n",
+                        loop, dma->buffer_queue[loop].buf_id, dma->buffer_queue[loop].source, 
+                        dma->buffer_queue[loop].size,
+                        dma->buffer_queue[loop].last, dma->buffer_queue[loop].used);
+                }
+                DPRINTK("pause     0x%x        0x%x         0x%x        %d         %d \n",
+                        dma->pause_buf.buf_id, dma->pause_buf.source, dma->pause_buf.size,
+                        dma->pause_buf.last, dma->pause_buf.used);
+    
+                DPRINTK("Pause - %d \n", dma->pause);
+                DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
+                DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
+                DPRINTK("total buffer - %d \n", dma->total_buffers);
+                DPRINTK("new buffers - %d \n", dma->new_buffers);
+                DPRINTK("current buffer - %d \n", dma->current_buffer);
+                DPRINTK("last buffer - %d \n", dma->last_buffer);
+                DPRINTK("used buffers - %d \n", dma->used_buffers);
+                DPRINTK("callback addr - 0x%x \n", dma->callback);
+            }
+        }
+        /*
+         *  Total number of buffers in the queue is 0.
+         */
+        else
+        {
+            /*
+             *  No buffers to transfer, or old buffers to mark as used, 
+             *  so Disable the channel
+             */
+            //go dma_regs->CONTROL.Field.ENABLE = 0;
+            uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
+            uiCONTROL &= ~CONTROL_M2P_ENABLE;
+            outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
+
+            /*
+             *  Must read the control register back after a write.
+             */
+            //go read_back = dma_regs->CONTROL.Value;
+            read_back = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
+            
+            /*
+             *  Indicate that this channel is in the pause by
+             *  starvation state by setting the pause bit to true.
+             */
+            dma->pause = TRUE;
+        } 
+    }
+    /*
+     *  Next Frame Buffer Interrupt.  If there's a new buffer program it  
+     *  Check if this is the last buffer in the transfer,
+     *  and if it is, disable the NFB int to prevent being
+     *  interrupted for another buffer when we know there won't be 
+     *  another.
+     */
+    if(dma_int == NFB)
+    {
+        DPRINTK("5  ");
+        
+        if(dma->total_buffers)
+        {
+            DPRINTK("6  ");
+            /*
+             *  The iCurrentBuffer has already been transfered.  so add the 
+             *  byte count from the current buffer to the total byte count.
+             */
+            dma->total_bytes = dma->total_bytes +
+                               dma->buffer_queue[dma->current_buffer].size;
+
+            /*
+             *  Mark the Current Buffer as used.
+             */
+            dma->buffer_queue[dma->current_buffer].used = TRUE;
+
+            /* 
+             *  Increment the used buffer counter
+             */
+            dma->used_buffers++;
+            
+            DPRINTK("#%d", dma->current_buffer);
+            
+
+            if((dma->buffer_queue[
+                (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].last) ||
+               (dma->new_buffers == 0) || (dma->xfer_enable == FALSE))
+            {
+                DPRINTK("7  "); 
+                
+                /*
+                 *  This is the last Buffer in this transaction, so disable
+                 *  the NFB interrupt.  We shouldn't get an NFB int when the
+                 *  FSM moves to the ON state where it would typically get the
+                 *  NFB int indicating a new buffer can be programmed.
+                 *  Instead, once in the ON state, the DMA will just proceed
+                 *  to complet the transfer of the current buffer, move the
+                 *  FSB directly to the STALL state where a STALL interrupt
+                 *  will be generated.
+                 */
+                //go dma_regs->CONTROL.Field.NFBINTEN = 0;
+                uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
+                uiCONTROL &= ~CONTROL_M2P_NFBINTEN;
+                outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
+
+                /*
+                 *  The current buffer has been transferred, so increment
+                 *  the current buffer counter to reflect this.
+                 */
+                dma->current_buffer = (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS;
+                
+                DPRINTK("End of NFB handling. \n");
+                DPRINTK("STATUS - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_STATUS) );
+                DPRINTK("CONTROL - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_CONTROL) );
+                DPRINTK("REMAIN - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_REMAIN) );
+                DPRINTK("PPALLOC - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_PPALLOC) );
+                DPRINTK("BASE0 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE0) );
+                DPRINTK("MAXCNT0 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT0) );
+                DPRINTK("CURRENT0 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT0) );
+                DPRINTK("BASE1 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE1) );
+                DPRINTK("MAXCNT1 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT1) );
+                DPRINTK("CURRENT1 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
+    
+                DPRINTK("Buffer    buf_id     source       size       last       used \n");
+                for(loop = 0; loop < 32; loop ++)
+                {
+                DPRINTK("%d        0x%x        0x%x         0x%x        %d         %d \n",
+                        loop, dma->buffer_queue[loop].buf_id, dma->buffer_queue[loop].source, 
+                        dma->buffer_queue[loop].size,
+                        dma->buffer_queue[loop].last, dma->buffer_queue[loop].used);
+                }
+                DPRINTK("pause     0x%x        0x%x         0x%x        %d         %d \n",
+                        dma->pause_buf.buf_id, dma->pause_buf.source, dma->pause_buf.size,
+                        dma->pause_buf.last, dma->pause_buf.used);
+    
+                DPRINTK("Pause - %d \n", dma->pause);
+                DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
+                DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
+                DPRINTK("total buffer - %d \n", dma->total_buffers);
+                DPRINTK("new buffers - %d \n", dma->new_buffers);
+                DPRINTK("current buffer - %d \n", dma->current_buffer);
+                DPRINTK("last buffer - %d \n", dma->last_buffer);
+                DPRINTK("used buffers - %d \n", dma->used_buffers);
+                DPRINTK("callback addr - 0x%x \n", dma->callback);
+                
+            }
+            else if(dma->new_buffers)
+            {
+                DPRINTK("8  ");
+                /* 
+                 *  we have a new buffer, so increment the current buffer to
+                 *  point to the next buffer, which is already programmed into
+                 *  the DMA. Next time around, it'll be pointing to the
+                 *  current buffer.
+                 */
+                dma->current_buffer = (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS;
+        
+                /*
+                 *  we know we have a new buffer to program as the next
+                 *  buffer, so check which set of MAXCNT and BASE registers
+                 *  to program.  
+                 */
+                //go if(dma_regs->STATUS.Field.NEXTBUFFER)
+                if( inl(M2P_reg_base+M2P_OFFSET_STATUS) & STATUS_M2P_NEXTBUFFER )
+                {
+                    /*
+                     *  Set the MAXCNT1 register with the buffer size
+                     */
+                    //go dma_regs->MAXCNT1.Field.MAXCNT1 = dma->buffer_queue[
+                    //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size;
+                    outl( dma->buffer_queue[
+                             (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size,
+                        M2P_reg_base+M2P_OFFSET_MAXCNT1 );
+
+                    /*
+                     *  Set the BASE1 register with the buffer base address
+                     */
+                    //go dma_regs->BASE1.Field.BASE1 = dma->buffer_queue[
+                    //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source;
+                    outl( dma->buffer_queue[
+                             (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source,
+                            M2P_reg_base+M2P_OFFSET_BASE1 );
+                }
+                else
+                {
+                    /*
+                     *  Set the MAXCNT0 register with the buffer size
+                     */
+                    //go dma_regs->MAXCNT0.Field.MAXCNT0 = dma->buffer_queue[
+                    //go    (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size;
+                    outl( dma->buffer_queue[
+                            (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size,
+                            M2P_reg_base+M2P_OFFSET_MAXCNT0 );
+                            
+                    /*
+                     *  Set the BASE0 register with the buffer base address
+                     */
+                    //go dma_regs->BASE0.Field.BASE0 = dma->buffer_queue[
+                    //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source;
+                    outl( dma->buffer_queue[
+                             (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source,
+                            M2P_reg_base+M2P_OFFSET_BASE0 );
+                }
+                
+                /*
+                 *  Decrement the new buffers counter
+                 */
+                dma->new_buffers--;
+            }
+        }
+        /*
+         *  Total number of buffers is 0 - really we should never get here,
+         *  but just in case.
+         */ 
+        else
+        {
+            DPRINTK("9 \n");
+            
+            /*
+             *  No new buffers to transfer, so Disable the channel
+             */
+            //go dma_regs->CONTROL.Field.ENABLE = 0;
+            uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
+            uiCONTROL &= ~CONTROL_M2P_ENABLE;
+            outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
+        }        
+    }
+    /* 
+     *  Channel Error Interrupt, or perhipheral interrupt, specific to the
+     *  memory to/from peripheral channels.
+     */
+    if(dma_int == CHERROR)
+    {
+        /*
+         *  just clear the interrupt, it's really up to the peripheral
+         *  driver to determine if any further action is necessary.
+         */
+        //go dma_regs->INTERRUPT.Field.CHERRORINT = 0;     
+        uiINTERRUPT = inl(M2P_reg_base+M2P_OFFSET_INTERRUPT);
+        uiINTERRUPT &= ~INTERRUPT_M2P_CHERRORINT;
+        outl( uiINTERRUPT, M2P_reg_base+M2P_OFFSET_INTERRUPT );
+    }
+    
+
+    /*
+     *  Make sure the interrupt was valid, and if it was, then check
+     *  if a callback function was installed for this DMA channel.  If a
+     *  callback was installed call it.
+     */  
+    if((dma_int != UNDEF_INT) && dma->callback)
+    {
+        dma->callback(dma_int, dma->device, dma->user_data);
+    }    
+}
+
+/*****************************************************************************
+ *
+ * ep9312_dma_open_m2p(int device)
+ *
+ * Description: This function will attempt to open a M2P/P2M DMA channel.
+ *              If the open is successful, the channel number is returned,
+ *              otherwise a negative number is returned.
+ *
+ * Parameters:
+ *  device:     device for which the dma channel is requested.
+ *
+ ****************************************************************************/
+static int
+dma_open_m2p(int device)
+{
+    int channel = -1;
+    unsigned int loop;
+    //go DMAM2P * regs;
+    unsigned int M2P_reg_base;
+	unsigned int uiPWRCNT;
+    
+    DPRINTK("DMA Open M2P with hw dev %d\n", device);
+    
+
+    /*
+     *  Lock the dma channel list.
+     */ 
+    spin_lock(dma_list_lock);
+    DPRINTK("1\n");
+    /*
+     * Verify that the device requesting DMA isn't already using a DMA channel
+     */
+    if (device >= 10)
+    {
+        loop = 1;         // Rx transfer requested
+    }
+    else
+    {
+        loop = 0;         // Tx transfer requested
+    }
+    
+    for(; loop < 10; loop = loop + 2)
+    {
+        /*
+         *  Before checking for a matching device, check that the
+         *  channel is in use, otherwise the device field is 
+         *  invalid.
+         */ 
+        if(dma_chan[loop].ref_count)
+        {
+            if(device == dma_chan[loop].device)
+            {
+                DPRINTK("DMA Open M2P - Error\n");
+                /*
+                 *  Fail.
+                 */
+                return(-1);
+            }
+        }
+    }
+
+    /*
+     *  Get a DMA channel instance for the given hardware device.
+     *  If this is a TX look for even numbered channels, else look for
+     *  odd numbered channels
+     */ 
+    if(device >= 10)
+    {
+        loop = 1;         /* Rx transfer requested */
+    }
+    else
+    {
+        loop = 0;         /* Tx transfer requested */
+    }
+    
+    for(; loop < 10; loop = loop + 2)
+    {
+        if(!dma_chan[loop].ref_count)
+        {
+            /*
+             *  Capture the channel and increment the reference count.
+             */ 
+            channel = loop;
+            dma_chan[channel].ref_count++;
+            break;          
+        }
+    }
+    
+    /*
+     *  Unlock the dma channel list.
+     */ 
+    spin_unlock(dma_list_lock);
+
+    /*
+     *  See if we got a valid channel.
+     */
+    if(channel < 0)
+    {
+        /*
+         *  Fail.
+         */
+        return(-1);
+    }
+    
+    /*
+     *  Point regs to the correct dma channel register base.
+     */ 
+    //go regs = (DMAM2P *)(dma_chan[channel].reg_base);
+    M2P_reg_base = dma_chan[channel].reg_base;
+
+    /*
+     *  Turn on the clock for the specified DMA channel
+     *  TODO: need to use the correct register name for the
+     *  power control register.
+     */
+	uiPWRCNT = inl(SYSCON_PWRCNT);
+    switch(channel)
+    {
+        case 0:
+        {
+            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH0;
+            break;
+        }
+
+        case 1:
+        {
+            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH1;
+            break;
+        }
+
+        case 2:
+        {
+            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH2;
+            break;
+        }
+
+        case 3:
+        {
+            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH3;
+            break;
+        }
+
+        case 4:
+        {
+            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH4;
+            break;
+        }
+
+        case 5:
+        {
+            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH5;
+            break;
+        }
+
+        case 6:
+        {
+            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH6;
+            break;
+        }
+
+        case 7:
+        {
+            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH7;
+            break;
+        }
+        case 8:
+        {
+            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH8;
+            break;
+        }
+        case 9:
+        {
+            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH9;
+            break;
+        }
+
+        default:
+        {
+            return(-1);
+        }
+    }
+	outl( uiPWRCNT, SYSCON_PWRCNT );
+	
+    /*DPRINTK("DMA Open - power control: 0x%x \n", inl(SYSCON_PWRCNT) ); */
+
+    /*
+     *  Clear out the control register before any further setup.
+     */
+    //go regs->CONTROL.Value = 0;
+    outl( 0, M2P_reg_base+M2P_OFFSET_CONTROL );
+
+    /* 
+     *  Setup the peripheral port value in the DMA channel registers.
+     */
+    if(device < 10)
+    {
+        //go regs->PPALLOC.Value = (unsigned int)device;
+        outl( (unsigned int)device, M2P_reg_base+M2P_OFFSET_PPALLOC );
+    }
+    else
+    {
+        //go regs->PPALLOC.Value = (unsigned int)(device - 10);
+        outl( (unsigned int)(device - 10), M2P_reg_base+M2P_OFFSET_PPALLOC );
+    }
+
+    /*
+     *  Let's hold on to the value of the Hw device for comparison later.
+     */ 
+    dma_chan[channel].device = device;
+
+    /*
+     *  Success.
+     */ 
+    return(channel);
+}
+
+/*****************************************************************************
+ *
+ * dma_open_m2m(int device)
+ *
+ * Description: This function will attempt to open a M2M DMA channel.
+ *              If the open is successful, the channel number is returned,
+ *              otherwise a negative number is returned.
+ *
+ * Parameters:
+ *  device:     device for which the dma channel is requested.
+ *
+ ****************************************************************************/
+static int
+dma_open_m2m(int device)
+{
+    int channel = -1;
+    unsigned int loop;
+    //go DMAM2M * regs;
+	unsigned int M2M_reg_base;
+    unsigned int uiPWRCNT, uiCONTROL;
+    
+    DPRINTK("DMA Open M2M with hw dev %d\n", device);
+    
+
+    /*
+     *  Lock the dma channel list.
+     */ 
+    spin_lock(dma_list_lock);
+    DPRINTK("1\n");
+        
+    /*
+     *  Check if this device is already allocated a channel.
+     *  TODO: can one M2M device be allocated multiple channels?
+     */
+    for(loop = DMA_MEMORY; loop < UNDEF; loop++)
+    {
+        /*
+         *  Before checking for a matching device, check that the
+         *  channel is in use, otherwise the device field is 
+         *  invalid.
+         */ 
+        if(dma_chan[loop].ref_count)
+        {
+            if(device == dma_chan[loop].device)
+            {
+                DPRINTK("Error - dma_open_m2m - already allocated channel\n");
+                
+                /*
+                 *  Fail.
+                 */
+                return(-1);
+            }
+        }
+    }
+
+    /*
+     *  Get a DMA channel instance for the given hardware device.
+     */ 
+    for(loop = 10; loop < 12; loop++)
+    {
+        if(!dma_chan[loop].ref_count)
+        {
+            /*
+             *  Capture the channel and increment the reference count.
+             */ 
+            channel = loop;
+            dma_chan[channel].ref_count++;
+            break;          
+        }
+    }
+    
+    /*
+     *  Unlock the dma channel list.
+     */ 
+    spin_unlock(dma_list_lock);
+
+    /*
+     *  See if we got a valid channel.
+     */
+    if(channel < 0)
+    {
+        /*
+         *  Fail.
+         */
+        return(-1);
+    }
+    
+    /*
+     *  Point regs to the correct dma channel register base.
+     */
+    // regs = (DMAM2M *)(dma_chan[channel].reg_base);
+    M2M_reg_base = dma_chan[channel].reg_base;
+
+    /*
+     *  Turn on the clock for the specified DMA channel
+     *  TODO: need to use the correct register name for the
+     *  power control register.
+     */
+	uiPWRCNT = inl(SYSCON_PWRCNT);
+    switch(channel)
+    {
+        case 10:
+        {
+            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2MCH0;
+            break;
+        }
+
+        case 11:
+        {
+            uiPWRCNT |= SYSCON_PWRCNT_DMA_M2MCH1;
+            break;
+        }
+
+        default:
+        {
+            return(-1);
+        }
+    }
+	outl( uiPWRCNT, SYSCON_PWRCNT );
+    
+    DPRINTK("DMA Open - power control: 0x%x \n", inl(SYSCON_PWRCNT) );
+
+    /*
+     *  Clear out the control register before any further setup.
+     */
+    //go regs->CONTROL.Value = 0;
+    outl( 0, M2M_reg_base+M2M_OFFSET_CONTROL );
+    
+    /* 
+     *  Setup the transfer mode and the request source selection within
+     *  the DMA M2M channel registers.
+     */
+    switch(device)
+    {
+        case DMA_MEMORY:
+        {
+            //go regs->CONTROL.Field.TM = 0;
+            //go regs->CONTROL.Field.RSS = 0;
+            
+            // Clear TM field, set RSS field to 0
+            uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+            uiCONTROL &= ~(CONTROL_M2M_TM_MASK | CONTROL_M2M_RSS_MASK);
+            outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+            
+            break;
+        }
+
+        case DMA_IDE:
+        {
+            //go regs->CONTROL.Field.RSS = 3;
+            //go regs->CONTROL.Field.NO_HDSK = 1;
+            //go regs->CONTROL.Field.PW = 01;
+            
+            // Set RSS field to 3, Set NO_HDSK, Set PW field to 1
+            uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+            uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_PW_MASK);
+            uiCONTROL |= (3<<CONTROL_M2M_RSS_SHIFT) |
+                         CONTROL_M2M_NO_HDSK | 
+                         (1<<CONTROL_M2M_PW_SHIFT);
+            outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+
+            break;
+        }
+
+        case DMARx_SSP:
+        {
+            //go regs->CONTROL.Field.TM = 2;
+            //go regs->CONTROL.Field.RSS = 1;
+            //go regs->CONTROL.Field.NO_HDSK = 1;
+
+            // Set RSS field to 1, Set NO_HDSK, Set TM field to 2
+            uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+            uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_TM_MASK);
+            uiCONTROL |= (1<<CONTROL_M2M_RSS_SHIFT) |
+                         CONTROL_M2M_NO_HDSK | 
+                         (2<<CONTROL_M2M_TM_SHIFT);
+            outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+
+            break;
+        }
+
+        case DMATx_SSP:
+        {
+            //go regs->CONTROL.Field.TM = 1;
+            //go regs->CONTROL.Field.RSS = 2;
+            //go regs->CONTROL.Field.NO_HDSK = 1;
+
+            // Set RSS field to 2, Set NO_HDSK, Set TM field to 1
+            uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+            uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_TM_MASK);
+            uiCONTROL |= (2<<CONTROL_M2M_RSS_SHIFT) |
+                         CONTROL_M2M_NO_HDSK | 
+                         (1<<CONTROL_M2M_TM_SHIFT);
+            outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+
+            break;
+        }
+
+        case DMATx_EXT_DREQ:
+        {
+            //go regs->CONTROL.Field.TM = 1;
+            //go regs->CONTROL.Field.RSS = 0;
+            
+            // Set TM field to 2, set RSS field to 0
+            uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+            uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_TM_MASK);
+            uiCONTROL |= 1<<CONTROL_M2M_TM_SHIFT;
+            outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+
+            break;
+        }
+
+        case DMARx_EXT_DREQ:
+        {
+            //go regs->CONTROL.Field.TM = 2;
+            //go regs->CONTROL.Field.RSS = 0;
+
+            // Set TM field to 2, set RSS field to 0
+            uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+            uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_TM_MASK);
+            uiCONTROL |= 2<<CONTROL_M2M_TM_SHIFT;
+            outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+
+            break;
+        }
+
+        default:
+        {
+            return -1;
+        }
+    }
+
+    /*
+     *  Let's hold on to the value of the Hw device for comparison later.
+     */ 
+    dma_chan[channel].device = device;
+
+    /*
+     *  Success.
+     */ 
+    return(channel);
+}
+
+/*****************************************************************************
+ *
+ *  int dma_config_m2m(ep93xx_dma_t * dma, unsigned int flags_m2m,
+ *               dma_callback callback, unsigned int user_data)
+ *
+ *  Description: Configure the DMA channel and install a callback function. 
+ *               This function will have to be called for every transfer
+ *
+ *  dma:        Pointer to the dma instance data for the M2M channel to
+ *              configure.
+ *  flags_m2m   Flags used to configure an M2M dma channel and determine
+ *              if a callback function and user_data information are included
+ *              in this call.
+ *  callback    function pointer which is called near the end of the 
+ *              dma channel's irq handler.
+ *  user_data   defined by the calling driver.
+ *
+ ****************************************************************************/
+static int
+dma_config_m2m(ep93xx_dma_t * dma, unsigned int flags_m2m,
+               dma_callback callback, unsigned int user_data)
+{
+    unsigned int flags;
+    unsigned int M2M_reg_base, uiCONTROL;
+    //go DMAM2M * regs;
+    
+    
+    /*
+     *  Make sure the channel is disabled before configuring the channel.
+     *
+     *  TODO: Is this correct??
+     */
+    if(!dma->pause || (!dma->pause && dma->xfer_enable))
+    {
+        /*
+         *  DMA channel is not paused, so we can't configure it.
+         */
+        DPRINTK("DMA channel not paused, so can't configure! \n");
+        return(-1);
+    }
+    
+    /*
+     *  Mask interrupts.
+     */
+    local_irq_save(flags);
+    
+    /*
+     *  Setup a pointer into the dma channel's register set.
+     */                                                                      
+    //go regs = (DMAM2M *)dma->reg_base;
+    M2M_reg_base = dma->reg_base;
+        
+    /*
+     *  By default we enable the stall interrupt.
+     */
+    //go regs->CONTROL.Field.STALLINTEN = 1;
+    uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+    uiCONTROL |= CONTROL_M2M_STALLINTEN;
+    outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+    
+    /*
+     *  By default we enable the done interrupt.
+     */
+    //go regs->CONTROL.Field.DONEINTEN = 1;
+    uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+    uiCONTROL |= CONTROL_M2M_DONEINTEN;
+    outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+    
+    /*
+     *  Set up the transfer control fields based on values passed in
+     *  the flags_m2m field.
+     */
+    //go regs->CONTROL.Field.DAH = flags_m2m & DESTINATION_HOLD;
+    //go regs->CONTROL.Field.SAH = flags_m2m & SOURCE_HOLD;
+    //go regs->CONTROL.Field.TM = flags_m2m & TRANSFER_MODE_MASK;
+    //go regs->CONTROL.Field.PWSC = flags_m2m & WAIT_STATES_MASK;
+    uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+    
+    if( flags_m2m & DESTINATION_HOLD )
+        uiCONTROL |= CONTROL_M2M_DAH;
+    else
+        uiCONTROL &= ~CONTROL_M2M_DAH;
+        
+    if( flags_m2m & SOURCE_HOLD )
+        uiCONTROL |= CONTROL_M2M_SAH;
+    else
+        uiCONTROL &= ~CONTROL_M2M_SAH;
+        
+    uiCONTROL &= ~CONTROL_M2M_TM_MASK;
+    uiCONTROL |= ((flags_m2m & TRANSFER_MODE_MASK)<<CONTROL_M2M_TM_SHIFT ) & 
+        CONTROL_M2M_TM_MASK;
+
+    uiCONTROL &= ~CONTROL_M2M_PWSC_MASK;
+    uiCONTROL |= ((flags_m2m & WAIT_STATES_MASK)<<CONTROL_M2M_PWSC_SHIFT) & 
+            CONTROL_M2M_PWSC_MASK;
+
+    outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+    
+        
+    /*
+     *  Save the callback function in the dma instance for this channel.
+     */
+    dma->callback = callback;
+    
+    /*
+     *  Save the user data in the the dma instance for this channel.
+     */ 
+    dma->user_data = user_data;
+    
+    /*
+     *  Put the dma instance into the pause state by setting the 
+     *  pause bit to true.
+     */
+    dma->pause = TRUE;
+    
+    local_irq_restore(flags);
+        
+    /*
+     *  Success.
+     */
+    return(0);
+}
+
+/*****************************************************************************
+ *
+ *  int dma_start(int handle, unsigned int channels, unsigned int * handles)
+ *
+ *  Description: Initiate a transfer on up to 3 channels.
+ *
+ *  handle:     handle for the channel to initiate transfer on.  
+ *  channels:   number of channels to initiate transfers on.
+ *  handles:    pointer to an array of handles, one for each channel which
+ *               is to be started.
+ *
+ ****************************************************************************/
+static int
+dma_start_m2m(int channel, ep93xx_dma_t * dma)
+{
+    unsigned int flags;
+    //go DMAM2M * regs = (DMAM2M *)dma->reg_base;
+    unsigned int M2M_reg_base = dma->reg_base;
+    unsigned int uiCONTROL;
+        
+    /*
+     *  Make sure the channel has at least one buffer in the queue.
+     */
+    if(dma->new_buffers < 1)
+    {
+        DPRINTK("DMA Start: Channel starved.\n");
+            
+        /*
+         *  This channel does not have enough buffers queued up,
+         *  so enter the pause by starvation state.
+         */
+        dma->xfer_enable = TRUE;
+        dma->pause = TRUE;
+         
+        /*
+         *  Success.
+         */  
+        return(0);
+    }
+    
+    /*
+     *  Mask interrupts while we get this started.
+     */
+    local_irq_save(flags);
+    
+    /*
+     *  Set up one or both buffer descriptors with values from the next one or
+     *  two buffers in the queue.  By default enable the next frame buffer
+     *  interrupt on the channel.
+     */
+    //go regs->CONTROL.Field.NFBINTEN = 1;
+    uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+    uiCONTROL |= CONTROL_M2M_NFBINTEN;
+    outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+    
+    /*
+     *  Update the dma channel instance transfer state.
+     */
+    dma->xfer_enable = TRUE;
+    dma->pause = FALSE;
+        
+    /*
+     *  Decrement the new buffers counter.
+     */
+    dma->new_buffers--;
+    
+    /*
+     *  Program up the first buffer descriptor with a sourc and destination
+     *  and a byte count.
+     */
+    //go regs->SAR_BASE0.Field.SAR_BASE0 = dma->buffer_queue[dma->current_buffer].source;
+    outl( dma->buffer_queue[dma->current_buffer].source,
+            M2M_reg_base+M2M_OFFSET_SAR_BASE0 );
+    
+    //go regs->DAR_BASE0.Field.DAR_BASE0 = dma->buffer_queue[dma->current_buffer].dest;
+    outl( dma->buffer_queue[dma->current_buffer].dest,
+            M2M_reg_base+M2M_OFFSET_DAR_BASE0 );
+
+    //go regs->BCR0.Field.BCR0 = dma->buffer_queue[dma->current_buffer].size;
+    outl( dma->buffer_queue[dma->current_buffer].size,
+            M2M_reg_base+M2M_OFFSET_BCR0 );
+    
+    /*
+     *  Now we enable the channel.  This initiates the transfer.
+     */
+    //go regs->CONTROL.Field.ENABLE = 1;
+    uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+    uiCONTROL |= CONTROL_M2M_ENABLE;
+    outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+    
+    /*
+     *  Before restoring irqs set up the second buffer descriptor
+     *  with a second buffer if we have a second buffer.
+     */ 
+    if(dma->new_buffers)
+    {
+        //go regs->SAR_BASE1.Field.SAR_BASE1 = dma->buffer_queue[
+        //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source;
+        outl( dma->buffer_queue[(dma->current_buffer + 1) % 
+                MAX_EP93XX_DMA_BUFFERS].source,
+                M2M_reg_base+M2M_OFFSET_SAR_BASE1 );
+        
+        //go regs->DAR_BASE1.Field.DAR_BASE1 = dma->buffer_queue[
+        //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].dest;
+        outl( dma->buffer_queue[(dma->current_buffer + 1) % 
+                MAX_EP93XX_DMA_BUFFERS].dest,
+                M2M_reg_base+M2M_OFFSET_DAR_BASE1 );
+        
+        //go regs->BCR1.Field.BCR1 = dma->buffer_queue[
+        //go     (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size;
+        outl( dma->buffer_queue[(dma->current_buffer + 1) % 
+                MAX_EP93XX_DMA_BUFFERS].size,
+                M2M_reg_base+M2M_OFFSET_BCR1 );
+    }
+    
+    /*
+     *  If this is a memory to memory transfer, we need to s/w trigger the
+     *  transfer by setting the start bit within the control register.
+     */
+    if(dma->device == DMA_MEMORY)
+    {
+        //go regs->CONTROL.Field.START = 1;
+        uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+        uiCONTROL |= CONTROL_M2M_START;
+        outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+    }
+    
+    
+/*    
+    DPRINTK("DMA - It's been started!!");
+    DPRINTK("STATUS - 0x%x \n",     inl(M2M_reg_base+M2M_OFFSET_STATUS) );
+    DPRINTK("CONTROL - 0x%x \n",    inl(M2M_reg_base+M2M_CONTROL.Value) );
+    DPRINTK("REMAIN - 0x%x \n",     inl(M2M_reg_base+M2M_REMAIN.Value) );
+    DPRINTK("PPALLOC - 0x%x \n",    inl(M2M_reg_base+M2M_PPALLOC.Value) );
+    DPRINTK("BASE0 - 0x%x \n",      inl(M2M_reg_base+M2M_BASE0.Value) );
+    DPRINTK("MAXCNT0 - 0x%x \n",    inl(M2M_reg_base+M2M_MAXCNT0.Value) );
+    DPRINTK("CURRENT0 - 0x%x \n",   inl(M2M_reg_base+M2M_CURRENT0.Value) );
+    DPRINTK("BASE1 - 0x%x \n",      inl(M2M_reg_base+M2M_BASE1.Value) );
+    DPRINTK("MAXCNT1 - 0x%x \n",    inl(M2M_reg_base+M2M_MAXCNT1.Value) );
+    DPRINTK("CURRENT1 - 0x%x \n",   inl(M2M_reg_base+M2M_CURRENT1.Value) );
+    
+    DPRINTK("Pause - %d \n", dma_pointers[0]->pause);
+    DPRINTK("xfer_enable - %d \n", dma_pointers[0]->xfer_enable);
+    DPRINTK("total bytes - 0x%x \n", dma_pointers[0]->total_bytes);
+    DPRINTK("total buffer - %d \n", dma_pointers[0]->total_buffers);
+    DPRINTK("new buffers - %d \n", dma_pointers[0]->new_buffers);
+    DPRINTK("current buffer - %d \n", dma_pointers[0]->current_buffer);
+    DPRINTK("last buffer - %d \n", dma_pointers[0]->last_buffer);
+    DPRINTK("used buffers - %d \n", dma_pointers[0]->used_buffers);
+*/    
+    /*
+     *  Unmask irqs
+     */
+    local_irq_restore(flags);
+    
+    /*
+     *  Success.
+     */
+    return(0);
+}
+
+/*****************************************************************************
+ *
+ *  DMA interface functions
+ *
+ ****************************************************************************/
+
+/*****************************************************************************
+ *
+ *  int dma_init(int handle, unsigned int flags_m2p, unsigned int flags_m2m,
+ *               dma_callback callback, unsigned int user_data)
+ *
+ *  Description: Configure the DMA channel and install a callback function.
+ *
+ *  handle:     Handle unique the each instance of the dma interface, used
+ *              to verify this call.
+ *  flags_m2p   Flags used to configure an M2P/P2M dma channel and determine
+ *              if a callback function and user_data information are included
+ *              in this call. This field should be NULL if handle represents
+ *              an M2M channel.
+ *  flags_m2m   Flags used to configure an M2M dma channel and determine
+ *              if a callback function and user_data information are included
+ *              in this call. This field should be NULL if handle represents
+ *              an M2P/P2M channel.
+ *  callback    function pointer which is called near the end of the 
+ *              dma channel's irq handler.
+ *  user_data   defined by the calling driver.
+ *
+ ****************************************************************************/
+int
+ep93xx_dma_config(int handle, unsigned int flags_m2p, unsigned int flags_m2m,
+                  dma_callback callback, unsigned int user_data)
+{
+    int  channel;
+    ep93xx_dma_t * dma;
+    //go DMAM2P * dma_regs;
+    int flags;
+    unsigned int M2P_reg_base, uiCONTROL;
+    
+    /*
+     *  Get the DMA hw channel # from the handle.
+     */
+    channel = dma_get_channel_from_handle(handle);
+    
+    /*
+     *  See if this is a valid handle.
+     */
+    if(channel < 0)
+    {
+        printk(KERN_ERR
+            "DMA Config: Invalid dma handle.\n");
+        
+        /*
+         *  Fail.
+         */        
+        return(-EINVAL);                                                  
+    }
+    
+    DPRINTK("DMA Config \n");
+    
+    dma = &dma_chan[channel];                                                                     
+    
+    local_irq_save(flags);
+    
+    /*                                                                    
+     *  Check if the channel is currently transferring.                   
+     */
+    if(dma->xfer_enable)
+    {                                                                     
+        local_irq_restore(flags);
+        
+	printk("channel is currently transferring\n");
+        /*                                                                
+         *  Already transferring, invalid command.                        
+         */                                                               
+        return(-EINVAL);                                                  
+    }
+    
+    
+    /*
+     *  Check if this is an m2m function.
+     */ 
+    if(channel >= 10)
+    {
+        local_irq_restore(flags);
+        
+        /*
+         *  Call another function to handle m2m config.
+         */ 
+        return(dma_config_m2m(dma, flags_m2m, callback, user_data)); 
+    }
+    
+    /*
+     *  Setup a pointer into the dma channel's register set.
+     */                                                                      
+    //go dma_regs = (DMAM2P *)dma->reg_base;
+    M2P_reg_base = dma->reg_base;
+                                                                              
+    /*
+     *  By default we enable the stall interrupt.
+     */
+    //go dma_regs->CONTROL.Field.STALLINTEN = 1;
+    uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
+    uiCONTROL |= CONTROL_M2P_STALLINTEN;
+    outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
+    
+    /*
+     *  Configure the channel for an error from the peripheral.
+     */
+    //go dma_regs->CONTROL.Field.CHERRORINTEN =
+    //go    flags_m2p && CHANNEL_ERROR_INT_ENABLE;
+    uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
+    if( flags_m2p && CHANNEL_ERROR_INT_ENABLE )
+        uiCONTROL |= CONTROL_M2P_CHERRORINTEN;
+    else
+        uiCONTROL &= ~CONTROL_M2P_CHERRORINTEN;
+    outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
+        
+        
+    //go dma_regs->CONTROL.Field.ABRT = flags_m2p && CHANNEL_ABORT;
+    uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
+    if( flags_m2p && CHANNEL_ABORT )
+        uiCONTROL |= CONTROL_M2P_ABRT;
+    else
+        uiCONTROL &= ~CONTROL_M2P_ABRT;
+    outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
+    
+    
+    //go dma_regs->CONTROL.Field.ICE = flags_m2p && IGNORE_CHANNEL_ERROR;
+    uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
+    if( flags_m2p && IGNORE_CHANNEL_ERROR )
+        uiCONTROL |= CONTROL_M2P_ICE;
+    else
+        uiCONTROL &= ~CONTROL_M2P_ICE;
+    outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
+    
+    /*
+     *  Save the callback function in the dma instance for this channel.
+     */
+    dma->callback = callback;
+    
+    /*
+     *  Save the user data in the the dma instance for this channel.
+     */ 
+    dma->user_data = user_data;
+    
+    /*
+     *  Put the dma instance into the pause state by setting the 
+     *  pause bit to true.
+     */
+    dma->pause = TRUE;
+    
+    local_irq_restore(flags);
+        
+    /*
+     *  Success.
+     */                                                                                                        
+    return(0);
+}
+
+/*****************************************************************************
+ *
+ *  int dma_start(int handle, unsigned int channels, unsigned int * handles)
+ *
+ *  Description: Initiate a transfer on up to 3 channels.
+ *
+ *  handle:     handle for the channel to initiate transfer on.  
+ *  channels:   number of channels to initiate transfers on.
+ *  handles:    pointer to an array of handles, one for each channel which
+ *               is to be started.
+ *
+ ****************************************************************************/
+int
+ep93xx_dma_start(int handle, unsigned int channels, unsigned int * handles)
+{
+    ep93xx_dma_t * dma_pointers[3];
+    //go DMAM2P * regs[3];
+    unsigned int M2P_reg_bases[3];
+    unsigned int loop, flags, uiCONTROL;
+    int  channel;    
+    
+    /*
+     *  Get the DMA hw channel # from the handle.
+     */
+    channel = dma_get_channel_from_handle(handle);
+    
+    /*
+     *  See if this is a valid handle.
+     */
+    if(channel < 0)
+    {
+        printk(KERN_ERR "DMA Start: Invalid dma handle.\n");
+        
+        /*
+         *  Fail.
+         */        
+        return(-EINVAL);
+    }
+    
+    if(channels < 1)
+    {
+        printk(KERN_ERR "DMA Start: Invalid parameter.\n");
+        
+        return(-EINVAL);
+    }
+    
+    DPRINTK("DMA Start \n");
+    
+    /*
+     *  Mask off registers.
+     */     
+    local_irq_save(flags);
+    
+    /*
+     *  Check if this is a start multiple.
+     */
+    if(channels > 1)
+    {
+#if 0
+        for(loop = 0; loop < channels; loop++)
+        {
+            /*
+             *  Get the DMA hw channel # from the handle.
+             */
+            channel = dma_get_channel_from_handle(handle);
+    
+            /*
+             *  See if this is a valid handle.
+             */
+            if(channel < 0)
+            {
+                printk(KERN_ERR "DMA Start: Invalid dma handle.\n");
+        
+                /*
+                 *  Fail.
+                 */        
+                return(-EINVAL);
+            }
+            
+            /*
+             *  Check if this channel is already started.
+             */
+            if(dma_chan[channel].xfer_enable)
+            {
+                printk(KERN_ERR
+                    "DMA Start: Invalid command for channel %d.\n", channel);
+                
+                /*
+                 *  This channel is already transferring, so return an error.
+                 */ 
+                return(-EINVAL);
+            }
+            
+            /*
+             *  Make sure the channel has at least two buffers in the queue.
+             */
+            if(dma_chan[channel].new_buffers < 2)
+            {
+                printk(KERN_ERR
+                    "DMA Start: Invalid command for channel %d.\n", channel);
+                    
+                /*
+                 *  This channel does not have enough buffers queued up,
+                 *  so return an error.
+                 */ 
+                return(-EINVAL);
+            }
+            
+            /*
+             *  Set up a pointer into the dma instance data for this channel.
+             */
+            dma_pointers[loop] = &dma_chan[channel];
+            
+            /*
+             *  Set up a pointer to the register set for this channel.
+             */
+            //go regs[loop] = (DMAM2P *)dma_pointers[loop]->reg_base;
+            M2P_reg_bases[loop] = dma_pointers[loop]->reg_base;
+             
+        }
+#endif
+        DPRINTK("DMA ERROR: Start, multiple start not supported yet \n");
+        return(-1);
+    }
+    /*
+     *  Only one channel
+     */
+    else
+    {
+        /*
+         *  Check if this channel is already transferring.
+         */
+        if(dma_chan[channel].xfer_enable && !dma_chan[channel].pause)
+        {
+            printk(KERN_ERR
+                "DMA Start: Invalid command for channel %d.\n", channel);
+            DPRINTK("DMA Start: Invalid command for channel %d.\n", channel);
+            
+            
+            /*
+             *  This channel is already transferring, so return an error.
+             */ 
+            return(-EINVAL);
+        }
+        
+        /*
+         *  If this is an M2M channel, call a different function.
+         */ 
+        if(channel >= 10)
+        {
+            /*
+             *  Call the m2m start function.  Only start one channel.
+             */
+            return(dma_start_m2m(channel, &dma_chan[channel]));
+        }
+        
+        /*
+         *  Make sure the channel has at least one buffer in the queue.
+         */
+        if(dma_chan[channel].new_buffers < 1)
+        {
+            DPRINTK("DMA Start: Channel starved.\n");
+                
+            /*
+             *  This channel does not have enough buffers queued up,
+             *  so enter the pause by starvation state.
+             */
+            dma_chan[channel].xfer_enable = TRUE;
+            dma_chan[channel].pause = TRUE;
+             
+            /*
+             *  Success.
+             */  
+            return(0);
+        }
+        
+        /*
+         *  Set up a dma instance pointer for this dma channel.
+         */ 
+        dma_pointers[0] = &dma_chan[channel];
+        
+        /*
+         * Set up a pointer to the register set for this channel.
+         */
+        //go regs[0] = (DMAM2P *)dma_pointers[0]->reg_base;
+        M2P_reg_bases[0] = dma_pointers[0]->reg_base; 
+    }
+    
+    /*
+     *  Setup both MAXCNT registers with values from the next two buffers
+     *  in the queue, and enable the next frame buffer interrupt on the channel.
+     */
+    for(loop = 0; loop < channels; loop++)
+    {
+        /*
+         *  By default we enable the next frame buffer interrupt.
+         */
+        //go regs[loop]->CONTROL.Field.NFBINTEN = 1;
+        uiCONTROL = inl(M2P_reg_bases[loop]+M2P_OFFSET_CONTROL);
+        uiCONTROL |= CONTROL_M2P_NFBINTEN;
+        outl( uiCONTROL, M2P_reg_bases[loop]+M2P_OFFSET_CONTROL );
+        
+        /*
+         *  Check if we need to restore a paused transfer.
+         *  TODO: should check if buf_id == -1 or not.
+         */ 
+        if(dma_pointers[loop]->pause_buf.buf_id != -1)
+        {
+            //go regs[loop]->MAXCNT0.Field.MAXCNT0 =
+            //go     dma_pointers[loop]->pause_buf.size;
+            outl( dma_pointers[loop]->pause_buf.size, 
+                M2P_reg_bases[loop]+M2P_OFFSET_MAXCNT0 );
+        }
+        else
+        {
+            //go regs[loop]->MAXCNT0.Field.MAXCNT0 =
+            //go     dma_pointers[loop]->buffer_queue[dma_pointers[loop]->current_buffer].size;
+            outl( dma_pointers[loop]->buffer_queue[dma_pointers[loop]->current_buffer].size,
+                M2P_reg_bases[loop]+M2P_OFFSET_MAXCNT0 );
+        }
+    }
+    
+    for(loop = 0; loop < channels; loop++)
+    {
+        /*
+         *  Enable the specified dma channels.
+         */ 
+        //go regs[loop]->CONTROL.Field.ENABLE = 1;
+        uiCONTROL = inl(M2P_reg_bases[loop]+M2P_OFFSET_CONTROL);
+        uiCONTROL |= CONTROL_M2P_ENABLE;
+        outl( uiCONTROL, M2P_reg_bases[loop]+M2P_OFFSET_CONTROL );
+        
+        /*
+         *  Update the dma channel instance transfer state.
+         */
+        dma_pointers[loop]->xfer_enable = TRUE;
+        dma_pointers[loop]->pause = FALSE;
+                
+        if(dma_pointers[loop]->pause_buf.buf_id == -1)
+        {
+            dma_pointers[loop]->new_buffers--;
+        }
+    }
+        
+    /*
+     *  Program up the BASE0 registers for all specified channels, this
+     *  will initiate transfers on all specified channels.
+     */
+    for(loop = 0; loop < channels; loop++)
+    {   
+        /*
+         *  Check if we need to restore a paused transfer.
+         */ 
+        if(dma_pointers[loop]->pause_buf.buf_id != -1)
+        {
+            //go regs[loop]->BASE0.Field.BASE0 = dma_pointers[loop]->pause_buf.source;
+            outl( dma_pointers[loop]->pause_buf.source,
+                M2P_reg_bases[loop]+M2P_OFFSET_BASE0 );
+            
+            /*
+             *  Set the pause buffer to NULL
+             */ 
+            dma_pointers[loop]->pause_buf.buf_id = -1;
+            dma_pointers[loop]->pause_buf.size = 0;
+        }
+        else
+        {
+            //go regs[loop]->BASE0.Field.BASE0 =
+            //go     dma_pointers[loop]->buffer_queue[
+            //go     dma_pointers[loop]->current_buffer].source;
+            outl( dma_pointers[loop]->buffer_queue[
+                  dma_pointers[loop]->current_buffer].source,
+                M2P_reg_bases[loop]+M2P_OFFSET_BASE0 );
+        }
+    }
+    
+    /*
+     *  Before restoring irqs setup the second MAXCNT/BASE
+     *  register with a second buffer.
+     */ 
+    for(loop = 0; loop < channels; loop++)
+    {                
+        if(dma_pointers[loop]->new_buffers)
+        {
+            //go regs[loop]->MAXCNT1.Field.MAXCNT1 =
+            //go     dma_pointers[loop]->buffer_queue[
+            //go     (dma_pointers[loop]->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size;
+            outl( dma_pointers[loop]->buffer_queue[
+                  (dma_pointers[loop]->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size,
+                M2P_reg_bases[loop]+M2P_OFFSET_MAXCNT1 );
+            
+            //go regs[loop]->BASE1.Field.BASE1 =
+            //go     dma_pointers[loop]->buffer_queue[
+            //go     (dma_pointers[loop]->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source;
+            outl( dma_pointers[loop]->buffer_queue[
+                  (dma_pointers[loop]->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source,
+                M2P_reg_bases[loop]+M2P_OFFSET_BASE1 );
+        }
+    }
+    
+/*    
+    DPRINTK("DMA - It's been started!!");
+    DPRINTK("STATUS - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_STATUS) );
+    DPRINTK("CONTROL - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_CONTROL) );
+    DPRINTK("REMAIN - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_REMAIN) );
+    DPRINTK("PPALLOC - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_PPALLOC) );
+    DPRINTK("BASE0 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE0) );
+    DPRINTK("MAXCNT0 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT0) );
+    DPRINTK("CURRENT0 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT0) );
+    DPRINTK("BASE1 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE1) );
+    DPRINTK("MAXCNT1 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT1) );
+    DPRINTK("CURRENT1 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
+    
+    DPRINTK("Pause - %d \n", dma_pointers[0]->pause);
+    DPRINTK("xfer_enable - %d \n", dma_pointers[0]->xfer_enable);
+    DPRINTK("total bytes - 0x%x \n", dma_pointers[0]->total_bytes);
+    DPRINTK("total buffer - %d \n", dma_pointers[0]->total_buffers);
+    DPRINTK("new buffers - %d \n", dma_pointers[0]->new_buffers);
+    DPRINTK("current buffer - %d \n", dma_pointers[0]->current_buffer);
+    DPRINTK("last buffer - %d \n", dma_pointers[0]->last_buffer);
+    DPRINTK("used buffers - %d \n", dma_pointers[0]->used_buffers);
+*/    
+    /*
+     *  Unmask irqs
+     */
+    local_irq_restore(flags);
+    
+    /*
+     *  Success.
+     */
+    return(0);
+}
+ 
+/*****************************************************************************
+ *
+ *  int ep93xx_dma_add_buffer(int handle, unsigned int * address,
+ *                         unsigned int size, unsigned int last)
+ *
+ *  Description: Add a buffer entry to the DMA buffer queue.
+ *
+ *  handle:     handle for the channel to add this buffer to.  
+ *  address:    Pointer to an integer which is the start address of the
+ *              buffer which is to be added to the queue.
+ *  size:       size of the buffer in bytes.
+ *  last:       1 if this is the last buffer in this stream, 0 otherwise.
+ *
+ ****************************************************************************/
+int
+ep93xx_dma_add_buffer(int handle, unsigned int source, unsigned int dest,
+                      unsigned int size, unsigned int last,
+                      unsigned int buf_id)
+{
+    unsigned int flags;
+    ep93xx_dma_t * dma;
+    int  channel;
+
+    /*
+     *  Get the DMA hw channel # from the handle.
+     */
+    channel = dma_get_channel_from_handle(handle);
+    
+    /*
+     *  See if this is a valid handle.
+     */
+    if(channel < 0)
+    {
+        printk(KERN_ERR
+            "DMA Add Buffer: Invalid dma handle.\n");
+        
+        /*
+         *  Fail.
+         */        
+        return(-EINVAL);
+    }
+    
+    /*
+     *  Get a pointer to the dma instance.
+     */
+    dma = &dma_chan[channel];
+
+    /*
+     *  Mask interrupts and hold on to the original state.
+     */ 
+    local_irq_save(flags);
+    
+    /*
+     *  If the buffer queue is full, last_buffer is the same as current_buffer and 
+     *  we're not tranfering, or last_buffer is pointing to a used buffer, then exit.
+     *  TODO: do I need to do any more checks? 
+     */
+    if(dma->total_buffers >= MAX_EP93XX_DMA_BUFFERS)
+    {
+        /*
+         *  Restore the state of the irqs
+         */ 
+        local_irq_restore(flags);
+
+        /*
+         *  Fail.
+         */ 
+        return(-1);
+    }
+
+    /*
+     *  Add this buffer to the queue
+     */
+    dma->buffer_queue[dma->last_buffer].source = source;
+    dma->buffer_queue[dma->last_buffer].dest = dest;
+    dma->buffer_queue[dma->last_buffer].size = size;
+    dma->buffer_queue[dma->last_buffer].last = last;
+    dma->buffer_queue[dma->last_buffer].buf_id = buf_id;
+    
+    /*
+     *  Reset the used field of the buffer structure.
+     */
+    dma->buffer_queue[dma->last_buffer].used = FALSE;
+    
+    /*
+     *  Increment the End Item Pointer.
+     */
+    dma->last_buffer = (dma->last_buffer + 1) % MAX_EP93XX_DMA_BUFFERS;
+
+    /*
+     *  Increment the new buffers counter and the total buffers counter
+     */
+    dma->new_buffers++;
+    dma->total_buffers++;
+                              
+    /*
+     *  restore the interrupt state.
+     */
+    local_irq_restore(flags);
+    
+    /*
+     *  Check if the channel was starved into a stopped state.
+     */
+    if(dma->pause && dma->xfer_enable)
+    {
+        if(dma->new_buffers >= 1)
+        {
+            DPRINTK("DMA - calling start from add after starve. \n");
+            
+            /*
+             *  The channel was starved into a stopped state, and we've got
+             *  2 new buffers, so start tranferring again.
+             */
+            ep93xx_dma_start(handle, 1, 0);
+        }
+    }
+    
+    /*
+     *  Success.
+     */ 
+    return(0);
+}
+
+/*****************************************************************************
+ *
+ *  int ep93xx_dma_remove_buffer(int handle, unsigned int * address,
+ *                                unsigned int * size)
+ *
+ *  Description: Remove a buffer entry from the DMA buffer queue. If
+ *               buffer was removed successfully, return 0, otherwise
+ *               return -1.
+ *
+ *  handle:     handle for the channel to remove a buffer from. 
+ *  address:    Pointer to an integer which is filled in with the start
+ *              address of the removed buffer.
+ *  size:       Pointer to an integer which is filled in with the size in
+ *              bytes of the removed buffer.
+ *
+ ****************************************************************************/
+int
+ep93xx_dma_remove_buffer(int handle, unsigned int * buf_id)
+{
+    unsigned int test;
+    unsigned int loop;
+    int return_val = -1;
+    unsigned int flags;
+    ep93xx_dma_t *dma;
+    int  channel;
+
+    /*
+     *  Get the DMA hw channel # from the handle.
+     */
+    channel = dma_get_channel_from_handle(handle);
+    
+    /*
+     *  See if this is a valid handle.
+     */
+    if(channel < 0)
+    {
+        printk(KERN_ERR
+            "DMA Remove Buffer: Invalid dma handle.\n");
+        
+        /*
+         *  Fail.
+         */        
+        return(-EINVAL);
+    }
+    
+    dma = &dma_chan[channel];
+
+    /*
+     *  Mask interrupts and hold on to the original state.
+     */ 
+    local_irq_save(flags);
+    
+    /*
+     *  Make sure there are used buffers to be returned.
+     */
+    if(dma->used_buffers)
+    {
+        test = dma->last_buffer;
+        
+        for(loop = 0; loop < MAX_EP93XX_DMA_BUFFERS; loop++)
+        {
+            if(dma->buffer_queue[test].used && (dma->buffer_queue[test].buf_id != -1))
+            {                   
+                /*DPRINTK("buffer %d used \n", test); */
+                
+                /*
+                 *  This is a used buffer, fill in the buf_id pointer
+                 *  with the buf_id for this buffer.
+                 */
+                *buf_id = dma->buffer_queue[test].buf_id;
+
+                /*
+                 *  Reset this buffer structure
+                 */
+                dma->buffer_queue[test].buf_id = -1;
+
+                /*
+                 *  Decrement the used buffer counter, and the total buffer counter.
+                 */
+                dma->used_buffers--;
+                dma->total_buffers--;
+
+                /*
+                 *  Successful removal of a buffer, so set the return
+                 *  value to 0, then exit this loop.
+                 */ 
+                return_val = 0;
+                break;
+            }
+
+            /*
+             *  This buffer isn't used, let's see if the next one is.
+             */
+            test = (test + 1) % MAX_EP93XX_DMA_BUFFERS;
+        }
+    }
+    
+    /*
+     *  Restore interrupts.
+     */   
+    local_irq_restore(flags);
+    
+    /*
+     *  Success.
+     */ 
+    return(return_val);
+}
+
+/*****************************************************************************
+ *
+ *  int ep93xx_dma_pause(int handle, unsigned int channels,
+ *                       unsigned int * handles)
+ *
+ *  Description: Disable any ongoing transfer for the given channel, retaining
+ *               the state of the current buffer transaction so that upon
+ *               resume, the dma will continue where it left off.
+ *
+ *  handle:     Handle for the channel to be paused.  If this is a pause for
+ *              for multiple channels, handle is a valid handle for one of
+ *              the channels to be paused.
+ *  channels:   number of channel to pause transfers on.
+ *  handles:    Pointer to an array of handles, one for each channel which
+ *              to be paused.  If this pause is intended only for one
+ *              channel, this field should be set to NULL.
+ *
+ ****************************************************************************/
+int
+ep93xx_dma_pause(int handle, unsigned int channels, unsigned int * handles)
+{
+    unsigned int flags;
+    ep93xx_dma_t * dma;
+    int channel;
+    //go DMAM2P * regs_m2p;
+    //go DMAM2M * regs_m2m;
+    unsigned int M2M_reg_base, M2P_reg_base;
+    
+
+    /*
+     *  Get the DMA hw channel # from the handle.
+     */
+    channel = dma_get_channel_from_handle(handle);
+    
+    /*
+     *  See if this is a valid handle.
+     */
+    if(channel < 0)
+    {
+        printk(KERN_ERR
+            "DMA Pause: Invalid dma handle.\n");
+        
+        /*
+         *  Fail.
+         */        
+        return(-EINVAL);
+    }
+    
+    DPRINTK("DMA %d: pause \n", channel);
+    
+    /*
+     *  Set up a pointer to the dma instance data.
+     */
+    dma = &dma_chan[channel];    
+    
+    /*
+     *  Set a pointer to the dma channel registers.
+     */
+    if(channel < 10)
+    {
+        //go regs_m2p = dma->reg_base;
+        M2P_reg_base = dma->reg_base;
+    }
+    else
+    {
+        //go regs_m2m = dma->reg_base;
+        M2M_reg_base = dma->reg_base;
+    }
+    
+    /*
+     *  Mask interrupts and hold on to the original state.
+     */ 
+    local_irq_save(flags);
+    
+    /*
+     *  Check if we're already paused.
+     */ 
+    if(dma->pause)
+    {
+        /*
+         *  We're paused, but are we stopped?
+         */
+        if(dma->xfer_enable)
+        {
+            /*
+             *  Put the channel in the stopped state.
+             */
+            dma->xfer_enable = FALSE;
+        }
+        
+        DPRINTK("DMA Pause - already paused.");
+    }
+    else
+    {
+        /*
+         *  Put the channel into the stopped state.
+         */
+        dma->xfer_enable = FALSE;
+        dma->pause = TRUE;
+    }
+    
+    /*
+     *  restore interrupts.
+     */ 
+    local_irq_restore(flags);
+   
+    /*
+     *  Already paused, so exit.
+     */ 
+    return(0);
+        
+#if 0    
+    
+    /*
+     *  Disable this DMA channel.
+     */
+    if(channel < 10)
+    {
+        /*
+         *  Before disabling the channel, get the current state.
+         */
+        //go state = regs_m2p->STATUS.Field.CURRENT;
+        state = inl(M2P_reg_base+M2P_OFFSET_STATUS) & STATUS_M2P_CURRENT_MASK;
+        
+        //go nfb = regs_m2p->STATUS.Field.NEXTBUFFER;
+        nfb = inl(M2P_reg_base+M2P_OFFSET_STATUS) & STATUS_M2P_NEXTBUFFER;
+        
+        /*
+         *  Disable the channel
+         */ 
+        //go regs_m2p->CONTROL.Field.ENABLE = 0;
+        uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
+        uiCONTROL &= ~CONTROL_M2P_ENABLE;
+        outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
+    }
+    else
+    {
+        //go regs_m2m->CONTROL.Field.ENABLE = 0;
+        uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+        uiCONTROL &= ~CONTROL_M2M_ENABLE;
+        outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+    }
+    
+    /*
+    local_irq_restore(flags);
+
+    mdelay(1);
+    i2s->i2s_tx0_EN.Value = 0;
+
+    local_irq_save(flags);
+    */
+    
+    
+
+    /*
+     *  Indicate that the channel stopped transferring, and set the
+     *  pause field.
+     */
+    dma->xfer_enable = FALSE;
+    dma->pause = TRUE;
+    
+    /*
+     *  Save the state of the buffer being transferred.
+     */
+    if(channel < 10)
+    {
+        if(state == 2)
+        {
+            if(nfb)
+            {
+                /*
+                 *  Active buffer descriptor is 0
+                 */
+                //go dma->pause_buf.source = regs_m2p->CURRENT0.Field.CURRENT0;
+                dma->pause_buf.source = 
+                    inl( M2P_reg_base+M2P_OFFSET_CURRENT0 );
+                
+                /*
+                 *  state is now the buffer descriptor index.
+                 */
+                state = 0;
+            }
+            else
+            {
+                /*
+                 *  Active buffer descriptor is 1
+                 */
+                //go dma->pause_buf.source = regs_m2p->CURRENT1.Field.CURRENT1;
+                dma->pause_buf.source = 
+                    inl( M2P_reg_base+M2P_OFFSET_CURRENT1 );
+                
+                /*
+                 *  state is now the buffer descriptor index.
+                 */
+                state = 1;
+            }
+        }
+        else if(state == 3)
+        {
+            if(nfb)
+            {
+                /*
+                 *  Active buffer descriptor is 1
+                 */
+                //go dma->pause_buf.source = regs_m2p->CURRENT1.Field.CURRENT1;
+                dma->pause_buf.source = 
+                    inl( M2P_reg_base+M2P_OFFSET_CURRENT1 );
+            
+                /*
+                 *  state is now the buffer descriptor index.
+                 */
+                state = 1;
+            }
+            else
+            {
+                /*
+                 *  Active buffer descriptor is 0
+                 */
+                //go dma->pause_buf.source = regs_m2p->CURRENT0.Field.CURRENT0;
+                dma->pause_buf.source = 
+                    inl( M2P_reg_base+M2P_OFFSET_CURRENT0 );
+            
+                /*
+                 *  state is now the buffer descriptor index.
+                 */
+                state = 0;
+            }    
+        }
+        
+        /*
+         *  Active buffer descriptor is 0
+         */
+        //go dma->pause_buf.size = regs_m2p->REMAIN.Field.REMAIN;
+        dma->pause_buf.size = 
+            inl( M2P_reg_base+M2P_OFFSET_REMAIN ) & M2P_MASK_REMAIN;
+        
+        /*
+         *  Set the pause buffer buf_id field to 1 to indicate that this 
+         *  buffer has valid data.
+         */
+        dma->pause_buf.buf_id = 1;
+        
+        /*
+         *  Reset the nfb variable, it now represents whether or not we need
+         *  to sync up the dma channel instance.
+         */ 
+        nfb = 0; 
+        
+        /*
+         *  Check if the active buffer is the current buffer.  the channel
+         *  may have started transferring the next buffer, which would mean
+         *  we have to sync up the dma instance data.
+         */ 
+        //go if(((state == 0) && (dma->buffer_queue[dma->current_buffer].source !=
+        //go                   regs_m2p->BASE0.Field.BASE0)) ||
+        //go    ((state == 1) && (dma->buffer_queue[dma->current_buffer].source !=
+        //go                      regs_m2p->BASE1.Field.BASE1)))
+        
+        if(((state == 0) && (dma->buffer_queue[dma->current_buffer].source !=
+                          (inl(M2P_reg_base+M2P_OFFSET_BASE0) & M2P_MASK_BASE0))) ||
+           ((state == 1) && (dma->buffer_queue[dma->current_buffer].source !=
+                          (inl(M2P_reg_base+M2P_OFFSET_BASE1) & M2P_MASK_BASE1))))
+        {
+            DPRINTK("Need to sync up \n");
+            /*
+             *  We need to sync up the dma channel instance data.
+             */
+            nfb = 1;
+        }
+        
+        if(nfb)
+        {
+            /*
+             *  The current buffer is already used at this point.
+             */
+            dma->buffer_queue[dma->current_buffer].used = TRUE;
+            
+            /*
+             *  increment the number of used buffers.
+             */
+            dma->used_buffers++;
+            
+            DPRINTK("#%d", dma->current_buffer);
+            
+            
+            /*
+             *  Increment the current buffer position.
+             */
+            dma->current_buffer = (dma->current_buffer + 1) %
+                                   MAX_EP93XX_DMA_BUFFERS;
+        }
+    }
+    /*
+     *  Memory to memory channel.
+     */
+    else
+    {
+    }
+      
+    DPRINTK("STATUS - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_STATUS) );
+    DPRINTK("CONTROL - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_CONTROL) );
+    DPRINTK("REMAIN - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_REMAIN) );
+    DPRINTK("PPALLOC - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_PPALLOC) );
+    DPRINTK("BASE0 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE0) );
+    DPRINTK("MAXCNT0 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT0) );
+    DPRINTK("CURRENT0 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT0) );
+    DPRINTK("BASE1 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE1) );
+    DPRINTK("MAXCNT1 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT1) );
+    DPRINTK("CURRENT1 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
+    
+    DPRINTK("Buffer    buf_id     source       size       last       used \n");
+    for(loop = 0; loop < 32; loop ++)
+    {
+    DPRINTK("%d        0x%x       0x%x         0x%x        %d         %d \n",
+            loop, dma->buffer_queue[loop].buf_id, dma->buffer_queue[loop].source, dma->buffer_queue[loop].size,
+            dma->buffer_queue[loop].last, dma->buffer_queue[loop].used);
+    }
+    DPRINTK("pause     0x%x       0x%x         0x%x        %d         %d \n",
+            dma->pause_buf.buf_id, dma->pause_buf.source, dma->pause_buf.size,
+            dma->pause_buf.last, dma->pause_buf.used);
+    
+    DPRINTK("Pause - %d \n", dma->pause);
+    DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
+    DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
+    DPRINTK("total buffer - %d \n", dma->total_buffers);
+    DPRINTK("new buffers - %d \n", dma->new_buffers);
+    DPRINTK("current buffer - %d \n", dma->current_buffer);
+    DPRINTK("last buffer - %d \n", dma->last_buffer);
+    DPRINTK("used buffers - %d \n", dma->used_buffers);
+    DPRINTK("nfb - %d \n", nfb);
+    DPRINTK("state - %d \n", state);
+    
+    
+    
+
+    /*
+     *  restore interrupts.
+     */
+    local_irq_restore(flags);
+       
+        
+    /*
+     * Success.
+     */
+    return(0);
+#endif
+}
+
+/*****************************************************************************
+ *
+ *  void ep93xx_dma_flush(int handle)
+ *
+ *  Description: Flushes all queued buffers and transfers in progress
+ *               for the given channel.  Return the buffer entries
+ *               to the calling function.
+ * 
+ *  handle:     handle for the channel for which the flush is intended.
+ *
+ ****************************************************************************/
+int
+ep93xx_dma_flush(int handle)
+{
+    unsigned int loop, flags;
+    ep93xx_dma_t * dma;
+    int  channel;
+    //go DMAM2P * dma_regs;
+    unsigned int M2P_reg_base;
+
+    /*
+     *  Get the DMA hw channel # from the handle.
+     */
+    channel = dma_get_channel_from_handle(handle);
+    
+    /*
+     *  See if this is a valid handle.
+     */
+    if(channel < 0)
+    {
+        printk(KERN_ERR
+            "DMA Flush: Invalid dma handle.\n");
+        
+        /*
+         *  Fail.
+         */        
+        return(-EINVAL);
+    }
+    
+    DPRINTK("DMA %d: flush \n", channel);
+    
+    /*
+     *  Set up a pointer to the dma instance data for this channel
+     */    
+    dma = &dma_chan[channel];
+
+    /*
+     *  Mask interrupts and hold on to the original state.
+     */ 
+    local_irq_save(flags);
+    
+    for(loop = 0; loop < MAX_EP93XX_DMA_BUFFERS; loop++)
+    {
+        dma->buffer_queue[loop].buf_id = -1;
+    }
+    
+    /*
+     *  Set the Current and Last item to zero.
+     */
+    dma->current_buffer = 0;
+    dma->last_buffer = 0;
+
+    /*
+     *  Reset the Buffer counters
+     */
+    dma->used_buffers = 0;
+    dma->new_buffers = 0;
+    dma->total_buffers = 0;
+
+    /*
+     *  reset the Total bytes counter.
+     */
+    dma->total_bytes = 0;
+    
+    //go dma_regs = (DMAM2P *)(dma_chan[channel].reg_base);
+    M2P_reg_base = dma_chan[channel].reg_base;
+
+    /*
+     *  restore interrupts.
+     */
+    local_irq_restore(flags);
+
+    /*
+     *  Success.
+     */
+    return(0);
+}
+
+/*****************************************************************************
+ *
+ *  int ep93xx_dma_queue_full(int handle)
+ *
+ *  Description: Query to determine if the DMA queue of buffers for
+ *              a given channel is full.  
+ *              0 = queue is full
+ *              1 = queue is not full
+ *
+ *  handle:     handle for the channel to query.
+ *
+ ****************************************************************************/
+int
+ep93xx_dma_queue_full(int handle)
+{
+    int list_full = 0;
+    unsigned int flags;
+    int  channel;
+
+    /*
+     *  Get the DMA hw channel # from the handle.
+     */
+    channel = dma_get_channel_from_handle(handle);
+    
+    /*
+     *  See if this is a valid handle.
+     */
+    if(channel < 0)
+    {
+        printk(KERN_ERR
+            "DMA Queue Full: Invalid dma handle.\n");
+        
+        /*
+         *  Fail.
+         */        
+        return(-EINVAL);
+    }
+    
+    DPRINTK("DMA %d: queue full \n", channel);
+
+    /*
+     *  Mask interrupts and hold on to the original state.
+     */ 
+    local_irq_save(flags);
+    
+    /*
+     *  If the last item is equal to the used item then 
+     *  the queue is full.
+     */
+    if(dma_chan[channel].total_buffers < MAX_EP93XX_DMA_BUFFERS)  
+    { 
+        list_full =  FALSE;
+    }
+    else
+    {
+        list_full = TRUE;
+    }
+    
+    /*
+     *  restore interrupts.
+     */
+    local_irq_restore(flags);
+        
+    return(list_full);
+}
+
+/*****************************************************************************
+ *
+ *  int ep93xx_dma_get_position(int handle, unsigned int * buf_id,
+                                unsigned int * total, unsigned int * current_frac)
+ *
+ *  Description:    Takes two integer pointers and fills them with the start
+ *                  and current address of the buffer currently transferring
+ *                  on the specified DMA channel.
+ *
+ *  handle:         handle for the channel to query.
+ *  buf_id:         pointer which is filled in with the buffer id for the
+ *                  current buffer transferring on the dma channel.
+ *                  buffer being transferred.
+ *  total  :        pointer which is filled in with the total bytes
+ *                  transferred on the channel.
+ ****************************************************************************/
+int
+ep93xx_dma_get_position(int handle, unsigned int * buf_id,
+                        unsigned int * total, unsigned int * current_frac)
+{
+    int  channel;
+    ep93xx_dma_t * dma;
+
+    /*
+     *  Get the DMA hw channel # from the handle.
+     */
+    channel = dma_get_channel_from_handle(handle);
+    
+    /*
+     *  See if this is a valid handle.
+     */
+    if(channel < 0)
+    {
+        printk(KERN_ERR "DMA Get Position: Invalid dma handle.\n");
+        
+        /*
+         *  Fail.
+         */        
+        return(-EINVAL);
+    }
+    
+    dma = &dma_chan[channel];
+    
+    /*
+     *  TODO: should irqs be disabled here?
+     */
+    
+    *buf_id = dma->buffer_queue[dma->current_buffer].buf_id;
+    
+    *total = dma->total_bytes;
+    
+    DPRINTK("DMA buf_id %d, total %d\n", *buf_id, *total);
+    
+    /*
+     *  Success.
+     */ 
+    return(0);
+}
+
+/*****************************************************************************
+ *
+ *  int ep93xx_dma_get_total(int handle)
+ *
+ *  Description:    Returns the total number of bytes transferred on the
+ *                  specified channel since the channel was requested.
+ *
+ *  handle:     handle for the channel to query.
+ *
+ ****************************************************************************/
+int
+ep93xx_dma_get_total(int handle)
+{
+    int  channel;
+
+    /*
+     *  Get the DMA hw channel # from the handle.
+     */
+    channel = dma_get_channel_from_handle(handle);
+    
+    /*
+     *  See if this is a valid handle.
+     */
+    if(channel < 0)
+    {
+        printk(KERN_ERR "DMA Get Total: Invalid dma handle.\n");
+        
+        /*
+         *  Fail.
+         */        
+        return(-EINVAL);
+    }
+    
+    DPRINTK("DMA %d: total: %d \n", channel, dma_chan[channel].total_bytes);
+    
+    /*
+     *  Return the total number of bytes transferred on this channel since
+     *  it was requested.
+     */
+    return(dma_chan[channel].total_bytes);
+}
+
+/*****************************************************************************
+ *
+ *  int ep93xx_dma_is_done(int handle)
+ *
+ *  Description:	Determines if the specified channel is done
+ *			transferring the requested data.
+ *
+ *  handle:	 handle for the channel to query.
+ *
+ ****************************************************************************/
+int
+ep93xx_dma_is_done(int handle)
+{
+	ep93xx_dma_t *dma;
+	int channel;
+
+	/*
+	 *  Get the DMA hw channel # from the handle.
+	 */
+	channel = dma_get_channel_from_handle(handle);
+
+	/*
+	 *  See if this is a valid handle.
+	 */
+	if (channel < 0) {
+		printk(KERN_ERR "ep93xx_dma_is_done: Invalid dma handle.\n");
+		return(-EINVAL);
+	}
+
+        /*
+         * Get a pointer to the DMA channel state structure.
+         */
+        dma = &dma_chan[channel];
+
+        /*
+         * See if there are any buffers remaining to be provided to the HW.
+         */
+        if (dma->new_buffers)
+            return 0;
+
+        /*
+         * See if this is a M2P or M2M channel.
+         */
+        if (channel < 10) {
+            /*
+             * If the bytes remaining register of the HW is not zero, then
+             * there is more work to be done.
+             */
+            if (inl(dma->reg_base + M2P_OFFSET_REMAIN) != 0)
+                return 0;
+        } else {
+            /*
+             * If either byte count register in the HW is not zero, then there
+             * is more work to be done.
+             */
+            if ((inl(dma->reg_base + M2M_OFFSET_BCR0) != 0) ||
+                (inl(dma->reg_base + M2M_OFFSET_BCR1) != 0))
+                return 0;
+        }
+
+        /*
+         * The DMA is complete.
+         */
+        return 1;
+}
+
+/*****************************************************************************
+ * ep93xx_dma_request
+ *
+ * Description: This function will allocate a DMA channel for a particular
+ * hardware peripheral.  Before initiating a transfer on the allocated
+ * channel, the channel must be set up and buffers have to queued up.
+ *
+ *  handle:     pointer to an integer which is filled in with a unique
+ *              handle for this instance of the dma interface.
+ *  device_id   string with the device name, primarily used by /proc.
+ *  device      hardware device ID for which the requested dma channel will
+ *              transfer data.
+ * 
+ ****************************************************************************/
+int
+ep93xx_dma_request(int * handle, const char *device_id,
+                   ep93xx_dma_dev_t device)
+{
+    ep93xx_dma_t *dma = NULL;
+    int channel, error = 0;
+    unsigned int loop;
+    //go DMAM2P * dma_regs;
+    unsigned int M2P_reg_base;
+    
+    /*
+     *  Check if the device requesting a DMA channel is a valid device.
+     */      
+    if((device >= UNDEF) || (device < 0))
+    {
+        /*
+         *  Invalid device, so return an error.
+         */
+        return(-ENODEV);
+    }
+    
+    /* 
+     *  We've got a valid hardware device requesting a DMA channel.
+     *  Now check if the device should open an M2P or M2M channel
+     */
+    if(device < 20)
+    {
+        channel = dma_open_m2p(device);
+    }
+    else
+    {
+        channel = dma_open_m2m(device);
+    }
+
+    /*
+     *  Check if we successfully opened a DMA channel
+     */
+    if(channel < 0)
+    {
+        printk(KERN_ERR
+               "%s: Could not open dma channel for this device.\n",
+               device_id);
+        
+        /*
+         *  Fail.
+         */        
+        return(-EBUSY);
+    }
+    
+    dma = &dma_chan[channel];
+
+    /*
+     *  Request the appropriate IRQ for the specified channel
+     */ 
+    if(channel < 10)
+    {
+        error = request_irq(dma->irq, dma_m2p_irq_handler, SA_INTERRUPT,
+                            device_id, (void *) dma);
+    }
+    else
+    {
+        error = request_irq(dma->irq, dma_m2m_irq_handler, SA_INTERRUPT,
+                            device_id, (void *) dma);
+    }
+    
+    /*
+     *  Check for any errors during the irq request
+     */ 
+    if(error)
+    {
+        printk(KERN_ERR
+               "%s: unable to request IRQ %d for DMA channel\n",
+               device_id, dma->irq);
+        /*
+         *  Fail.
+         */
+        return(error);
+    }
+    
+    /*
+     *  Generate a valid handle and exit.
+     *
+     *  Increment the last valid handle.
+     *  Check for wraparound (unlikely, but we like to be complete).
+     */
+    dma->last_valid_handle++;
+    
+    if( (dma->last_valid_handle & DMA_HANDLE_SPECIFIER_MASK) !=
+        (channel << 28) )
+    {
+        /*
+         *  If we wrapped around start over.
+         */
+        dma->last_valid_handle = (channel << 28) + 1;
+    }
+    
+    /*
+     *  Fill in the handle pointer with a valid handle for 
+     *  this dma channel instance.
+     */
+    *handle = dma->last_valid_handle;
+    
+    DPRINTK("Handle for channel %d: 0x%x\n", channel, *handle);
+    
+    /*
+     * Save the device ID and device name. 
+     */
+    dma->device = device;
+    dma->device_id = device_id;
+    
+    /*
+     *  Init all fields within the dma instance.
+     */
+    for(loop = 0; loop < MAX_EP93XX_DMA_BUFFERS; loop++)
+    {
+        dma->buffer_queue[loop].buf_id = -1;
+    }
+    
+    /*
+     *  Initialize all buffer queue variables.
+     */
+    dma->current_buffer = 0;
+    dma->last_buffer = 0;
+
+    dma->new_buffers = 0;
+    dma->used_buffers = 0;
+    dma->total_buffers = 0;
+
+    /*
+     *  Initialize the total bytes variable
+     */
+    dma->total_bytes = 0;
+
+    /*
+     *  Initialize the transfer and pause state variables to 0.
+     */
+    dma->xfer_enable = 0;
+    
+    dma->pause = 0;
+    
+    /*
+     *  Initialize the pause buffer structure.
+     */
+    dma->pause_buf.buf_id = -1;
+    
+    /*
+     *  Initialize the callback function and user data fields.
+     */
+    dma->callback = NULL;
+
+    /*
+     * User data used as a parameter for the Callback function.  The user
+     * sets up the data and sends it with the callback function.
+     */
+    dma->user_data = 0;
+    
+    //go dma_regs = (DMAM2P *)dma_chan[channel].reg_base;
+    M2P_reg_base = dma_chan[channel].reg_base;
+    
+    /*
+     *  Debugging message.
+     */
+    DPRINTK("Successfully requested dma channel %d\n", channel);
+    DPRINTK("STATUS - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_STATUS) );
+    DPRINTK("CONTROL - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_CONTROL) );
+    DPRINTK("REMAIN - 0x%x \n",     inl(M2P_reg_base+M2P_OFFSET_REMAIN) );
+    DPRINTK("PPALLOC - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_PPALLOC) );
+    DPRINTK("BASE0 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE0) );
+    DPRINTK("MAXCNT0 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT0) );
+    DPRINTK("CURRENT0 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT0) );
+    DPRINTK("BASE1 - 0x%x \n",      inl(M2P_reg_base+M2P_OFFSET_BASE1) );
+    DPRINTK("MAXCNT1 - 0x%x \n",    inl(M2P_reg_base+M2P_OFFSET_MAXCNT1) );
+    DPRINTK("CURRENT1 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
+    
+    DPRINTK("Buffer    source       size       last       used \n");
+    for(loop = 0; loop < 5; loop ++)
+    {
+    DPRINTK("%d        0x%x         0x%x        %d         %d \n",
+            loop, dma->buffer_queue[loop].source, dma->buffer_queue[loop].size,
+            dma->buffer_queue[loop].last, dma->buffer_queue[loop].used);
+    }
+    DPRINTK("pause     0x%x         0x%x        %d         %d \n",
+            dma->pause_buf.source, dma->pause_buf.size,
+            dma->pause_buf.last, dma->pause_buf.used);
+    
+    DPRINTK("Pause - %d \n", dma->pause);
+    DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
+    DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
+    DPRINTK("total buffer - %d \n", dma->total_buffers);
+    DPRINTK("new buffers - %d \n", dma->new_buffers);
+    DPRINTK("current buffer - %d \n", dma->current_buffer);
+    DPRINTK("last buffer - %d \n", dma->last_buffer);
+    DPRINTK("used buffers - %d \n", dma->used_buffers);
+    
+    DPRINTK("CURRENT1 - 0x%x \n",   inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
+    DPRINTK("VIC0IRQSTATUS - 0x%x, VIC0INTENABLE - 0x%x \n",
+            *(unsigned int *)(VIC0IRQSTATUS),
+            *(unsigned int *)(VIC0INTENABLE));
+    
+    /*
+     *  Success.
+     */
+    return(0);
+}
+
+/*****************************************************************************
+ *
+ * ep93xx_dma_free
+ *
+ * Description: This function will free the dma channel for future requests.
+ *
+ *  handle:     handle for the channel to be freed.
+ * 
+ ****************************************************************************/
+int
+ep93xx_dma_free(int handle)
+{
+    ep93xx_dma_t *dma;
+    //go DMAM2P * regs_m2p;
+    //go DMAM2M * regs_m2m;
+    unsigned int M2M_reg_base, M2P_reg_base, uiCONTROL;    
+    int channel;
+    
+    /*
+     *  Get the DMA hw channel # from the handle.
+     */
+    channel = dma_get_channel_from_handle(handle);
+    
+    /*
+     *  See if this is a valid handle.
+     */
+    if(channel < 0)
+    {
+        printk(KERN_ERR "DMA Free: Invalid dma handle.\n");
+        
+        /*
+         *  Fail.
+         */        
+        return(-EINVAL);
+    }
+    
+    /*
+     *  Get a pointer to the dma instance.
+     */
+    dma = &dma_chan[channel];
+    
+    /*
+     *  Disable the dma channel
+     */
+    if(channel < 10)
+    {
+        /*
+         *  M2P channel
+         */
+        //go regs_m2p = dma->reg_base; 
+        M2P_reg_base = dma->reg_base;
+        
+        //go regs_m2p->CONTROL.Field.ENABLE = 0;
+        uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
+        uiCONTROL &= ~CONTROL_M2P_ENABLE;
+        outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
+    }
+    else
+    {
+        /*
+         *  M2M channel
+         */
+        //go regs_m2m = dma->reg_base; 
+        M2M_reg_base = dma->reg_base;
+        
+        //go regs_m2m->CONTROL.Field.ENABLE = 0;
+        uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
+        uiCONTROL &= ~CONTROL_M2M_ENABLE;
+        outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
+    }
+
+    /*
+     *  Free the interrupt servicing this dma channel
+     */ 
+    free_irq(dma->irq, (void *) dma);
+    
+    /*
+     *  Decrement the reference count for this instance of the dma interface
+     */ 
+    dma->ref_count--;
+    
+    /*
+     *  Set the transfer and pause state variables to 0
+     *  (unititialized state).
+     */
+    dma->xfer_enable = 0;
+    dma->pause = 0;
+    
+    /*
+     *  Debugging message.
+     */
+    DPRINTK("Successfully freed dma channel %d\n", channel);
+    
+    /*
+     *  Success.
+     */
+    return(0);
+}
+
+/*****************************************************************************
+ *
+ * ep93xx_dma_init(void)
+ *
+ * Description: This function is called during system initialization to 
+ * setup the interrupt number and register set base address for each DMA
+ * channel.
+ * 
+ ****************************************************************************/
+static int __init
+ep93xx_dma_init(void)
+{
+    int channel;
+        
+    /*
+     * Init some values in each dma instance.
+     */
+    for(channel = 0; channel < MAX_EP93XX_DMA_CHANNELS; channel++)
+    {
+        /*
+         *  IRQ for the specified dma channel.
+         */
+        dma_chan[channel].irq = IRQ_DMAM2P0 + channel;
+        
+        /*
+         *  Initial value of the dma channel handle.
+         */
+        dma_chan[channel].last_valid_handle = channel << 28;
+            
+        /*
+         *  Give the instance a pointer to the dma channel register
+         *  base.
+         */
+        if(channel < 10)
+        {
+            dma_chan[channel].reg_base = DMAM2PChannelBase[channel];
+        }
+        else
+        {
+            dma_chan[channel].reg_base = DMAM2MChannelBase[channel - 10];
+        }
+        
+        /*
+         *  Initialize the reference count for this channel.
+         */
+        dma_chan[channel].ref_count = 0;
+    }
+    
+    DPRINTK("DMA Interface intitialization complete\n");
+        
+    /*
+     * Success
+     */
+    return 0;
+}
+
+__initcall(ep93xx_dma_init);
+
+
Index: arch/arm/mach-ep93xx/dma_ep93xx.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/arch/arm/mach-ep93xx/dma_ep93xx.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- arch/arm/mach-ep93xx/dma_ep93xx.h	22 Jul 2004 19:48:59 -0000	1.1.1.1
+++ arch/arm/mach-ep93xx/dma_ep93xx.h	29 Sep 2006 22:25:11 -0000	1.2
@@ -1,195 +1,195 @@
-/*****************************************************************************
- *
- * arch/arm/mach-ep93xx/dma_ep93xx.h
- *
- * DESCRIPTION:    93XX DMA controller API private defintions.
- *
- * Copyright Cirrus Logic Corporation, 2003.  All rights reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- ****************************************************************************/
-#ifndef _EP93XX_DMA_H_
-#define _EP93XX_DMA_H_
-
-#define MAX_EP93XX_DMA_BUFFERS      32
-
-#ifndef TRUE
-#define TRUE                        1
-#endif
-
-#ifndef FALSE
-#define FALSE                       0
-#endif
-
-#ifndef NULL
-#define NULL                        0
-#endif
-
-/*****************************************************************************
- *
- * DMA buffer structure type.
- *
- ****************************************************************************/
-typedef struct ep93xx_dma_buffer_s
-{
-    unsigned int    source;     /* buffer physical source address.          */
-    unsigned int    dest;       /* buffer physical destination address,     */
-                                /* only used with the 2 M2M channels.       */
-    unsigned int    size;       /* buffer size in bytes                     */
-    unsigned int    last;       /* 1 if this is the last buffer             */
-                                /* in this transaction.  If 1,              */
-                                /* disable the NFBint so we aren't          */
-                                /* interrupted for another buffer           */
-                                /* when we know there won't be another.     */
-    unsigned int    used;       /* This field is set to 1 by the DMA        */
-                                /* interface after the buffer is transferred*/
-    int    buf_id;              /* unique identifyer specified by the       */
-                                /* the driver which requested the dma       */
-} ep93xx_dma_buffer_t;
-
-typedef ep93xx_dma_buffer_t * ep93xx_dma_buffer_p;
-
-/*****************************************************************************
- *
- * Instance definition for the DMA interface.
- *
- ****************************************************************************/
-typedef struct ep7312_dma_s
-{
-    /*
-     *  This 1 when the instance is in use, and 0 when it's not.
-     */
-    unsigned int ref_count;
-
-    /*
-     * This is the last valid handle for this instance.  When giving out a
-     * new handle this will be incremented and given out.
-     */
-    int last_valid_handle;
-
-    /*
-     * device specifies one of the 20 DMA hardware ports this 
-     * DMA channel will service.
-     */
-    ep93xx_dma_dev_t device;
-
-    /*
-     * DMABufferQueue is the queue of buffer structure pointers which the
-     * dma channel will use to setup transfers.
-     */
-    ep93xx_dma_buffer_t buffer_queue[MAX_EP93XX_DMA_BUFFERS];
-
-    /*
-     * currnt_buffer : This is the buffer currently being transfered on 
-     *                 this channel.
-     * last_buffer : This is the last buffer for this transfer.
-     * Note: current_buffer + 1 is already programmed into the dma
-     *       channel as the next buffer to transfer. Don't write
-     *       over either entry.
-     */
-    int current_buffer;
-    int last_buffer;
-
-    /*
-     * The following 3 fields are buffer counters.
-     *
-     * iNewBuffers: Buffers in the queue which have not been transfered.
-     * iUsedBuffers: Buffers in the queue which have have been tranferred,
-     *               and are waiting to be returned.
-     * iTotalBuffers: Total number of buffers in the queue.
-     */  
-    int new_buffers;
-    int used_buffers;
-    int total_buffers;
-
-    /*
-     * uiTotalBytes has the total bytes transfered on the channel since the
-     * last flush.  This value does not include the bytes tranfered in the
-     * current buffer.  A byte count is only added after a complete buffer
-     * is tranfered. 
-     */
-    unsigned int total_bytes;
-
-    /*
-     *  Interrupt number for this channel
-     */
-    unsigned int irq;
-
-    /*
-     * Indicates whether or not the channel is currently enabled to transfer
-     * data.
-     */
-    unsigned int xfer_enable;
-    
-    /*
-     * pause indicates if the dma channel was paused by calling the pause
-     * ioctl.
-     */
-    unsigned int pause;
-    
-    /*
-     *  buffer structure used during a pause to capture the current
-     *  address and remaining bytes for the buffer actively being transferred
-     *  on the channel. This buffer will be used to reprogram the dma 
-     *  channel upon a resume.
-     */
-    ep93xx_dma_buffer_t pause_buf;
-    
-    /*
-     * DMACallback is a function pointer which the calling application can 
-     * use install a function to.  this fuction can be used to notify the 
-     * calling application of an interrupt.
-     */
-    dma_callback callback;
-
-    /*
-     * User data used as a parameter for the Callback function.  The user
-     * sets up the data and sends it with the callback function.
-     */
-    unsigned int user_data;
-    
-    /*
-     * A string representation of the device attached to the channel.
-     */
-    const char * device_id;
-    
-    /*
-     * The register base address for this dma channel.
-     */
-    unsigned int reg_base;
-    
-} ep93xx_dma_t;
-
-/*****************************************************************************
- *
- * DMA macros
- *
- ****************************************************************************/
-#define DMA_HANDLE_SPECIFIER_MASK   0xF0000000
-#define DMA_CH0_HANDLE_SPECIFIER    0x00000000
-#define DMA_CH1_HANDLE_SPECIFIER    0x10000000
-#define DMA_CH2_HANDLE_SPECIFIER    0x20000000
-#define DMA_CH3_HANDLE_SPECIFIER    0x30000000
-#define DMA_CH4_HANDLE_SPECIFIER    0x40000000
-#define DMA_CH5_HANDLE_SPECIFIER    0x50000000
-#define DMA_CH6_HANDLE_SPECIFIER    0x60000000
-#define DMA_CH7_HANDLE_SPECIFIER    0x70000000
-#define DMA_CH8_HANDLE_SPECIFIER    0x80000000
-#define DMA_CH9_HANDLE_SPECIFIER    0x90000000
-#define DMA_CH10_HANDLE_SPECIFIER   0xA0000000
-#define DMA_CH11_HANDLE_SPECIFIER   0xB0000000
-
-#endif // _DMADRV_H_
+/*****************************************************************************
+ *
+ * arch/arm/mach-ep93xx/dma_ep93xx.h
+ *
+ * DESCRIPTION:    93XX DMA controller API private defintions.
+ *
+ * Copyright Cirrus Logic Corporation, 2003.  All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ ****************************************************************************/
+#ifndef _EP93XX_DMA_H_
+#define _EP93XX_DMA_H_
+
+#define MAX_EP93XX_DMA_BUFFERS      32
+
+#ifndef TRUE
+#define TRUE                        1
+#endif
+
+#ifndef FALSE
+#define FALSE                       0
+#endif
+
+#ifndef NULL
+#define NULL                        0
+#endif
+
+/*****************************************************************************
+ *
+ * DMA buffer structure type.
+ *
+ ****************************************************************************/
+typedef struct ep93xx_dma_buffer_s
+{
+    unsigned int    source;     /* buffer physical source address.          */
+    unsigned int    dest;       /* buffer physical destination address,     */
+                                /* only used with the 2 M2M channels.       */
+    unsigned int    size;       /* buffer size in bytes                     */
+    unsigned int    last;       /* 1 if this is the last buffer             */
+                                /* in this transaction.  If 1,              */
+                                /* disable the NFBint so we aren't          */
+                                /* interrupted for another buffer           */
+                                /* when we know there won't be another.     */
+    unsigned int    used;       /* This field is set to 1 by the DMA        */
+                                /* interface after the buffer is transferred*/
+    int    buf_id;              /* unique identifyer specified by the       */
+                                /* the driver which requested the dma       */
+} ep93xx_dma_buffer_t;
+
+typedef ep93xx_dma_buffer_t * ep93xx_dma_buffer_p;
+
+/*****************************************************************************
+ *
+ * Instance definition for the DMA interface.
+ *
+ ****************************************************************************/
+typedef struct ep9312_dma_s
+{
+    /*
+     *  This 1 when the instance is in use, and 0 when it's not.
+     */
+    unsigned int ref_count;
+
+    /*
+     * This is the last valid handle for this instance.  When giving out a
+     * new handle this will be incremented and given out.
+     */
+    int last_valid_handle;
+
+    /*
+     * device specifies one of the 20 DMA hardware ports this 
+     * DMA channel will service.
+     */
+    ep93xx_dma_dev_t device;
+
+    /*
+     * DMABufferQueue is the queue of buffer structure pointers which the
+     * dma channel will use to setup transfers.
+     */
+    ep93xx_dma_buffer_t buffer_queue[MAX_EP93XX_DMA_BUFFERS];
+
+    /*
+     * currnt_buffer : This is the buffer currently being transfered on 
+     *                 this channel.
+     * last_buffer : This is the last buffer for this transfer.
+     * Note: current_buffer + 1 is already programmed into the dma
+     *       channel as the next buffer to transfer. Don't write
+     *       over either entry.
+     */
+    int current_buffer;
+    int last_buffer;
+
+    /*
+     * The following 3 fields are buffer counters.
+     *
+     * iNewBuffers: Buffers in the queue which have not been transfered.
+     * iUsedBuffers: Buffers in the queue which have have been tranferred,
+     *               and are waiting to be returned.
+     * iTotalBuffers: Total number of buffers in the queue.
+     */  
+    int new_buffers;
+    int used_buffers;
+    int total_buffers;
+
+    /*
+     * uiTotalBytes has the total bytes transfered on the channel since the
+     * last flush.  This value does not include the bytes tranfered in the
+     * current buffer.  A byte count is only added after a complete buffer
+     * is tranfered. 
+     */
+    unsigned int total_bytes;
+
+    /*
+     *  Interrupt number for this channel
+     */
+    unsigned int irq;
+
+    /*
+     * Indicates whether or not the channel is currently enabled to transfer
+     * data.
+     */
+    unsigned int xfer_enable;
+    
+    /*
+     * pause indicates if the dma channel was paused by calling the pause
+     * ioctl.
+     */
+    unsigned int pause;
+    
+    /*
+     *  buffer structure used during a pause to capture the current
+     *  address and remaining bytes for the buffer actively being transferred
+     *  on the channel. This buffer will be used to reprogram the dma 
+     *  channel upon a resume.
+     */
+    ep93xx_dma_buffer_t pause_buf;
+    
+    /*
+     * DMACallback is a function pointer which the calling application can 
+     * use install a function to.  this fuction can be used to notify the 
+     * calling application of an interrupt.
+     */
+    dma_callback callback;
+
+    /*
+     * User data used as a parameter for the Callback function.  The user
+     * sets up the data and sends it with the callback function.
+     */
+    unsigned int user_data;
+    
+    /*
+     * A string representation of the device attached to the channel.
+     */
+    const char * device_id;
+    
+    /*
+     * The register base address for this dma channel.
+     */
+    unsigned int reg_base;
+    
+} ep93xx_dma_t;
+
+/*****************************************************************************
+ *
+ * DMA macros
+ *
+ ****************************************************************************/
+#define DMA_HANDLE_SPECIFIER_MASK   0xF0000000
+#define DMA_CH0_HANDLE_SPECIFIER    0x00000000
+#define DMA_CH1_HANDLE_SPECIFIER    0x10000000
+#define DMA_CH2_HANDLE_SPECIFIER    0x20000000
+#define DMA_CH3_HANDLE_SPECIFIER    0x30000000
+#define DMA_CH4_HANDLE_SPECIFIER    0x40000000
+#define DMA_CH5_HANDLE_SPECIFIER    0x50000000
+#define DMA_CH6_HANDLE_SPECIFIER    0x60000000
+#define DMA_CH7_HANDLE_SPECIFIER    0x70000000
+#define DMA_CH8_HANDLE_SPECIFIER    0x80000000
+#define DMA_CH9_HANDLE_SPECIFIER    0x90000000
+#define DMA_CH10_HANDLE_SPECIFIER   0xA0000000
+#define DMA_CH11_HANDLE_SPECIFIER   0xB0000000
+
+#endif // _DMADRV_H_
Index: arch/arm/mach-ep93xx/mm.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/arch/arm/mach-ep93xx/mm.c,v
retrieving revision 1.10
retrieving revision 1.13
diff -u -r1.10 -r1.13
--- arch/arm/mach-ep93xx/mm.c	17 Mar 2006 22:28:56 -0000	1.10
+++ arch/arm/mach-ep93xx/mm.c	24 Jan 2007 00:36:06 -0000	1.13
@@ -62,9 +62,10 @@
 	unsigned char stat;
 
 	iotable_init(ep93xx_io_desc);
-	stat = inb(TS7XXX_STATUS);
-	if ((stat & 0x1) != 0x1) console_loglevel = 4;
-	outw(0x4, TS7XXX_ENIO);
+//	stat = inb(TS7XXX_STATUS);
+//	if ((stat & 0x1) != 0x1) console_loglevel = 4;
+	if (!inb(TS7XXX_STATUS2) & 0x4) outw(0x4, TS7XXX_ENIO);
 	outw(0x5, TS7XXX_WATCHDOG_FEED);
 	outw(0x0, TS7XXX_WATCHDOG_CTRL);
+	outl(0x20000000, SYSCON_PWRCNT);
 }
Index: drivers/block/Config.in
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/block/Config.in,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- drivers/block/Config.in	3 Feb 2006 20:35:37 -0000	1.3
+++ drivers/block/Config.in	16 Jan 2009 20:34:27 -0000	1.4
@@ -5,6 +5,8 @@
 comment 'Block devices'
 
 tristate 'SD card support' CONFIG_BLK_DEV_TS_SD
+dep_mbool 'Prosoft 8000 SD card support' CONFIG_PROSOFT_SD $CONFIG_BLK_DEV_TS_SD
+
 tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD
 if [ "$CONFIG_AMIGA" = "y" ]; then
    tristate 'Amiga floppy support' CONFIG_AMIGA_FLOPPY
Index: drivers/char/Config.in
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/char/Config.in,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- drivers/char/Config.in	20 Oct 2005 21:21:55 -0000	1.6
+++ drivers/char/Config.in	16 Jan 2009 19:55:40 -0000	1.7
@@ -333,6 +333,7 @@
 tristate 'Enhanced Real Time Clock Support' CONFIG_RTC
 if [ "$CONFIG_ARCH_EP9301" = "y" ]; then
    bool 'TS-5620 Real Time Clock board' CONFIG_TS_RTC
+   bool 'Prosoft 8000 Real Time Clock board' CONFIG_PROSOFT_RTC
 fi
 if [ "$CONFIG_IA64" = "y" ]; then
    bool 'EFI Real Time Clock Services' CONFIG_EFI_RTC
Index: drivers/char/n_tty.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/char/n_tty.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- drivers/char/n_tty.c	4 Aug 2004 01:28:44 -0000	1.2
+++ drivers/char/n_tty.c	7 Mar 2008 19:32:51 -0000	1.3
@@ -962,7 +962,7 @@
 	ssize_t retval = 0;
 	ssize_t size;
 	long timeout;
-	unsigned long flags;
+	unsigned long flags,flags2;
 
 do_it_again:
 
@@ -1021,6 +1021,7 @@
 	}
 
 	add_wait_queue(&tty->read_wait, &wait);
+	save_flags_cli(flags2);
 	set_bit(TTY_DONT_FLIP, &tty->flags);
 	while (nr) {
 		/* First test for status change. */
@@ -1061,7 +1062,9 @@
 				break;
 			}
 			clear_bit(TTY_DONT_FLIP, &tty->flags);
+			restore_flags(flags2);
 			timeout = schedule_timeout(timeout);
+			save_flags_cli(flags2);
 			set_bit(TTY_DONT_FLIP, &tty->flags);
 			continue;
 		}
@@ -1128,6 +1131,7 @@
 			timeout = time;
 	}
 	clear_bit(TTY_DONT_FLIP, &tty->flags);
+	restore_flags(flags2);
 	up(&tty->atomic_read);
 	remove_wait_queue(&tty->read_wait, &wait);
 
Index: drivers/char/rtc.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/char/rtc.c,v
retrieving revision 1.3
retrieving revision 1.9
diff -u -r1.3 -r1.9
--- drivers/char/rtc.c	24 Aug 2004 20:17:59 -0000	1.3
+++ drivers/char/rtc.c	16 Jan 2009 19:55:40 -0000	1.9
@@ -67,6 +67,7 @@
 #include <linux/proc_fs.h>
 #include <linux/spinlock.h>
 #include <linux/sysctl.h>
+#include <linux/delay.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
@@ -79,6 +80,8 @@
 #include <asm/isa.h>
 #endif
 
+
+
 static unsigned long rtc_port;
 static int rtc_irq = PCI_IRQ_NONE;
 #endif
@@ -87,6 +90,260 @@
 static int rtc_has_irq = 1;
 #endif
 
+int cmos_method_old = 1, cmos_method_boc = 0;
+
+#define TS_MODEL_REG 0x2000000
+#define TS_MODEL_7400 4
+
+//Prosoft RTC support
+volatile static unsigned char *ts7350_rtc = NULL;
+volatile static unsigned short *ts7350_pc104 = NULL;
+
+#define TS7350_RTC_BASE                 0x600ff000
+#define TS7350_RTC_SIZE                 0x00001000
+#define TS7350_RTC_INDEX                0x00000870      //0x600ff870
+#define TS7350_RTC_DATA                 0x00000871      //0x600ff871
+#define TS7350_PC104_BASE               0x600ff000
+#define TS7350_PC104_SIZE               0x00000100
+#define TS7350_PC104_A_LOW              0x0000004c      //0x600ff098
+#define TS7350_PC104_A_UPPER            0x0000004d      //0x600ff09a
+#define TS7350_PC104_B_LOW              0x0000004e      //0x600ff09c
+#define TS7350_PC104_B_UPPER            0x0000004f      //0x600ff09e
+#define TS7350_PC104_ISACONF            0x0000006a      //0x600ff0d4
+
+#define RESTORE_PC104 config_pc104(1)
+#define SETUP_PC104 config_pc104(0)
+
+static inline void config_pc104(int restore)
+{
+        static unsigned short a_lower, a_upper, b_lower, b_upper, isaconf;
+
+        if (restore) {
+
+                //save PC/104 status regs
+                ts7350_pc104[TS7350_PC104_A_LOW] = a_lower;
+                ts7350_pc104[TS7350_PC104_A_UPPER] = a_upper;
+                ts7350_pc104[TS7350_PC104_B_LOW] = b_lower;
+                ts7350_pc104[TS7350_PC104_B_UPPER] = b_upper;
+                ts7350_pc104[TS7350_PC104_B_UPPER] = isaconf;
+
+        } else {
+
+                if (!ts7350_rtc)
+                        ts7350_rtc = (unsigned char *)
+                            __ioremap(TS7350_RTC_BASE, TS7350_RTC_SIZE, 0);
+
+                if (!ts7350_pc104)
+                        ts7350_pc104 = (unsigned short *)
+                            __ioremap(TS7350_PC104_BASE, TS7350_PC104_SIZE, 0);
+
+                //save PC/104 status regs
+                a_lower = ts7350_pc104[TS7350_PC104_A_LOW];
+                a_upper = ts7350_pc104[TS7350_PC104_A_UPPER];
+                b_lower = ts7350_pc104[TS7350_PC104_B_LOW];
+                b_upper = ts7350_pc104[TS7350_PC104_B_UPPER];
+                isaconf = ts7350_pc104[TS7350_PC104_ISACONF];
+
+                //configure PC/104 connector for PC/104 bus
+                //we need to do this to talk to the RTC
+                ts7350_pc104[TS7350_PC104_A_LOW] = 0x5555;
+                ts7350_pc104[TS7350_PC104_A_UPPER] = 0x5555;
+                ts7350_pc104[TS7350_PC104_B_LOW] = 0x5555;
+                ts7350_pc104[TS7350_PC104_B_UPPER] = 0x5555;
+
+                //setup PC/104 setup time and hold time
+                ts7350_pc104[TS7350_PC104_ISACONF] = 0x1ef;
+
+                //ensure RTC battery is enabled
+                ts7350_rtc[TS7350_RTC_INDEX] = 0xa;
+                ts7350_rtc[TS7350_RTC_DATA] = 0x20;
+        }
+}
+
+static unsigned char ts7350_rtc_readbyte(unsigned long addr)
+{
+        unsigned char value;
+
+        SETUP_PC104;
+        ts7350_rtc[TS7350_RTC_INDEX] = addr;
+        value = ts7350_rtc[TS7350_RTC_DATA];
+        RESTORE_PC104;
+
+        return value;
+}
+
+static void ts7350_rtc_writebyte(unsigned char value, unsigned long addr)
+{
+        SETUP_PC104;
+        ts7350_rtc[TS7350_RTC_INDEX] = addr;
+        ts7350_rtc[TS7350_RTC_DATA] = value;
+        RESTORE_PC104;
+}
+
+static inline int get_ts_model(void) {
+  return (inw(TS7XXX_IO16_BASE + TS_MODEL_REG)) & 7;
+}
+
+static inline unsigned char CMOSREAD_OLD(int addr) {
+  return CMOS_READ(addr);
+}
+
+static inline unsigned char CMOSWRITE_OLD(unsigned char byte,int addr) {
+  return CMOS_WRITE(byte,addr);
+}
+
+#undef CMOS_READ
+#undef CMOS_WRITE
+
+
+
+static inline unsigned char CMOSREAD_BOC(int addr) {
+  outb_p((addr), BOC_RTCIDX);
+  return inb_p(BOC_RTCDATA);
+}
+
+static inline unsigned char CMOSWRITE_BOC(unsigned char val,int addr) {
+  outb_p((addr), BOC_RTCIDX);
+  outb_p((val), BOC_RTCDATA);
+}
+
+
+void setRTCdata(unsigned char ch) {
+  volatile unsigned char *dr = (void *)IO_ADDRESS(0x80840000);
+  volatile unsigned char *ddr = (void *)IO_ADDRESS(0x80840010);
+
+  *ddr = 0xFF;
+  *dr = ch;
+  //printk("DR wrote %02X, read %02X (DDR=%02X)\n",ch,*dr,*ddr);
+}
+
+unsigned char getRTCdata(void) {
+  volatile unsigned char *dr = (void *)IO_ADDRESS(0x80840000);
+  volatile unsigned char *ddr = (void *)IO_ADDRESS(0x80840010);
+
+  *ddr = 0x00;
+  return *dr;
+}
+
+void setRTC_RD(int en) {
+  volatile unsigned char *dr = (void *)IO_ADDRESS(0x80840040);
+  volatile unsigned char *ddr = (void *)IO_ADDRESS(0x80840044);
+
+  *ddr = *ddr | 8;
+  *dr = !en ? (*dr | 8) : (*dr & 0xF7);
+}
+
+void setRTC_CS(int en) {
+  volatile unsigned char *dr = (void *)IO_ADDRESS(0x80840040);
+  volatile unsigned char *ddr = (void *)IO_ADDRESS(0x80840044);
+
+  *ddr = *ddr | 32;
+  *dr = !en ? (*dr | 32) : (*dr & 0xDF);
+}
+
+void setRTC_ALE(int en) {
+  volatile unsigned char *dr = (void *)IO_ADDRESS(0x80840040);
+  volatile unsigned char *ddr = (void *)IO_ADDRESS(0x80840044);
+
+  *ddr = *ddr | 16;
+  *dr = en ? (*dr | 16) : (*dr & 0xEF);
+}
+
+void setRTC_WR(int en) {
+  volatile unsigned char *dr = (void *)IO_ADDRESS(0x80840030);
+  volatile unsigned char *ddr = (void *)IO_ADDRESS(0x80840034);
+
+  *ddr = *ddr | 8;
+  *dr = !en ? (*dr | 8) : (*dr & 0xF7);
+}
+
+static inline unsigned char CMOSREAD_NEW(int addr) {
+  unsigned char ch;
+
+  setRTC_RD(0);
+  udelay(1);
+  setRTC_WR(0);
+  udelay(1);
+  setRTC_ALE(0);
+  udelay(1);
+  setRTC_CS(1);
+  udelay(1);
+  setRTCdata(addr);
+  udelay(1);
+  setRTC_ALE(1);
+  udelay(1);
+  setRTC_ALE(0);
+  udelay(1);
+  getRTCdata(); // turn bus around
+  udelay(1);
+  setRTC_RD(1);
+  udelay(1);
+  ch = getRTCdata();
+  udelay(1);
+  setRTC_RD(0);
+  udelay(1);
+  setRTC_CS(0);
+  //printk("CMOS_READ @%X -> %X\n",addr,ch);
+  return ch;
+}
+
+static inline unsigned char CMOSWRITE_NEW(unsigned char ch,int addr) {
+  //printk("CMOS_WRITE @%X <- %X\n",addr,ch);
+  setRTC_RD(0);
+  udelay(1);
+  setRTC_WR(0);
+  udelay(1);
+  setRTC_ALE(0);
+  udelay(1);
+  setRTC_CS(1);
+  udelay(1);
+  setRTC_ALE(1);
+  udelay(1);
+  setRTCdata(addr);
+  udelay(1);
+  setRTC_ALE(0);
+  udelay(1);
+  setRTC_WR(1);
+  udelay(1);
+  setRTCdata(ch);
+  udelay(1);
+  setRTC_WR(0);
+  udelay(1);
+  setRTC_CS(0);
+  return ch;
+}
+
+static inline unsigned char CMOS_READ(int addr) {
+#ifndef CONFIG_PROSOFT_RTC
+  return (cmos_method_boc) ? CMOSREAD_BOC(addr) :
+    (cmos_method_old) ? CMOSREAD_OLD(addr) : CMOSREAD_NEW(addr);
+#else
+return ts7350_rtc_readbyte((unsigned long) addr);
+#endif
+}
+
+static inline unsigned char CMOS_WRITE(unsigned char byte,int addr) {
+#ifndef CONFIG_PROSOFT_RTC
+  return  (cmos_method_boc) ? CMOSWRITE_BOC(byte,addr) :
+    (cmos_method_old) ? CMOSWRITE_OLD(byte,addr):CMOSWRITE_NEW(byte,addr);
+#else
+	ts7350_rtc_writebyte(byte, (unsigned long) addr);
+#endif
+}
+
+void CMOS_INIT(void) {
+#ifndef CONFIG_PROSOFT_RTC
+
+  volatile unsigned *dr = (void *)IO_ADDRESS(0x809300C0);
+  
+  *dr = 0xAA;
+  dr = (void *)IO_ADDRESS(0x80930080);
+  *dr = 0x08140d00;
+#endif
+
+  CMOS_WRITE(0x20,0xa);
+}
+
 /*
  *	We sponge a minor off of the misc major. No need slurping
  *	up another valuable major dev number for this. If you add
@@ -725,6 +982,11 @@
 	void *r;
 #endif
 
+	if (get_ts_model() == 4) {
+	  printk("Using DIO to access RTC\n");
+	  cmos_method_old = 0;
+	}
+
 #ifdef __sparc__
 	for_each_ebus(ebus) {
 		for_each_ebusdev(edev, ebus) {
@@ -775,14 +1037,30 @@
 	{
 		unsigned char i;
 
+		CMOS_INIT();
 		CMOS_WRITE(0xaa, 0x7f);
 		i = CMOS_READ(0x7f) ^ 0xaa;
 		CMOS_WRITE(0x55, 0x7e);
 		i |= CMOS_READ(0x7e) ^ 0x55;
 		if (i != 0) {
-			printk("rtc: no TS-5620 RTC detected.\n");
-			return -EIO;
+		  if (cmos_method_old) {
+		    cmos_method_boc = 1;
+		    CMOS_INIT(); // try the above again with BOC access method
+		    CMOS_WRITE(0xaa, 0x7f);
+		    i = CMOS_READ(0x7f) ^ 0xaa;
+		    CMOS_WRITE(0x55, 0x7e);
+		    i |= CMOS_READ(0x7e) ^ 0x55;
+		  }
+		  if (i != 0) {
+		    cmos_method_boc = 0;
+		    printk("rtc: no TS-5620 RTC detected.\n");
+		    return -EIO;
+		  }
 		}
+		while((CMOS_READ(0xa) & 0x80) != 0);
+		// BCD mode, 24 hr mode
+		CMOS_WRITE(0x2, 0xb);
+
 	}
 #endif
 
Index: drivers/i2c/Config.in
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/i2c/Config.in,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- drivers/i2c/Config.in	4 Aug 2004 01:28:53 -0000	1.2
+++ drivers/i2c/Config.in	21 Feb 2007 00:01:10 -0000	1.3
@@ -12,6 +12,7 @@
       dep_tristate '  Philips style parallel port adapter' CONFIG_I2C_PHILIPSPAR $CONFIG_I2C_ALGOBIT $CONFIG_PARPORT
       dep_tristate '  ELV adapter' CONFIG_I2C_ELV $CONFIG_I2C_ALGOBIT
       dep_tristate '  Velleman K8000 adapter' CONFIG_I2C_VELLEMAN $CONFIG_I2C_ALGOBIT
+      dep_tristate '  EP93xx I2C using GPIO pins' CONFIG_EP93XX_I2C $CONFIG_I2C_ALGOBIT
       dep_tristate '  NatSemi SCx200 I2C using GPIO pins' CONFIG_SCx200_I2C $CONFIG_SCx200_GPIO $CONFIG_I2C_ALGOBIT
       if [ "$CONFIG_SCx200_I2C" != "n" ]; then
          int  '    GPIO pin used for SCL' CONFIG_SCx200_I2C_SCL 12
Index: drivers/i2c/Makefile
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/i2c/Makefile,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- drivers/i2c/Makefile	4 Aug 2004 01:28:53 -0000	1.2
+++ drivers/i2c/Makefile	21 Feb 2007 00:01:10 -0000	1.3
@@ -6,7 +6,7 @@
 
 export-objs	:= i2c-core.o i2c-algo-bit.o i2c-algo-pcf.o \
 		   i2c-algo-ite.o i2c-algo-sibyte.o i2c-algo-sgi.o \
-		   i2c-proc.o
+		   i2c-proc.o i2c-ep93xx.o
 
 # Init order: core, chardev, bit adapters, pcf adapters
 
@@ -21,6 +21,7 @@
 obj-$(CONFIG_I2C_GUIDE)		+= i2c-guide.o
 obj-$(CONFIG_I2C_FRODO)		+= i2c-frodo.o
 obj-$(CONFIG_I2C_OMAHA)		+= i2c-omaha.o
+obj-$(CONFIG_EP93XX_I2C)	+= i2c-ep93xx.o
 
 # PCF adapters
 obj-$(CONFIG_I2C_ALGOPCF)	+= i2c-algo-pcf.o
Index: drivers/misc/ts_sbcinfo.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/misc/ts_sbcinfo.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- drivers/misc/ts_sbcinfo.c	13 Apr 2006 00:56:18 -0000	1.6
+++ drivers/misc/ts_sbcinfo.c	7 Dec 2006 23:03:46 -0000	1.7
@@ -58,6 +58,9 @@
 		case TS7300:
 			sbc->board_id = 7300;
 			break;
+		case TS7400:
+			sbc->board_id = 7400;
+			break;
 		default:
 			sbc->board_id = 0;
 			break;
Index: drivers/mtd/nand/nand.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/mtd/nand/nand.c,v
retrieving revision 1.2
retrieving revision 1.4
diff -u -r1.2 -r1.4
--- drivers/mtd/nand/nand.c	4 Aug 2004 01:29:06 -0000	1.2
+++ drivers/mtd/nand/nand.c	29 Sep 2006 22:26:17 -0000	1.4
@@ -115,7 +115,7 @@
  *  12-05-2000  tglx: Dave Ellis (DGE@sixnetio) provided the fix for
  *		WRITE_VERIFY long time ago. Thanks for remembering me.	
  *	
- * $Id: nand.c,v 1.2 2004/08/04 01:29:06 joff Exp $
+ * $Id: nand.c,v 1.4 2006/09/29 22:26:17 joff Exp $
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -316,9 +316,10 @@
 static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
 {
 
-	unsigned long	timeo = jiffies;
+	unsigned long	start, timeo;
 	int	status;
 	
+	start = timeo = jiffies;
 	if (state == FL_ERASING)
 		 timeo += (HZ * 400) / 1000;
 	else
@@ -341,7 +342,7 @@
 			break;
 						
 		spin_unlock_bh (&this->chip_lock);
-		yield ();
+		if ((jiffies - start) > ((HZ * 20) / 1000)) yield ();
 		spin_lock_bh (&this->chip_lock);
 	}
 	status = (int) readb (this->IO_ADDR_R);
@@ -1142,13 +1143,14 @@
 
 	while (len) {
 		/* Check if we have a bad block, we do not erase bad blocks ! */
+		/* Allow us to erase bad blocks
 		this->cmdfunc (mtd, NAND_CMD_READOOB, NAND_BADBLOCK_POS, page);
 		if (readb (this->IO_ADDR_R) != 0xff) {
 			printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page);
 			instr->state = MTD_ERASE_FAILED;
 			goto erase_exit;
 		}
-
+		*/
 		/* Send commands to erase a page */
 		this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
 		this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
Index: drivers/mtd/nand/nand_base.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/mtd/nand/nand_base.c,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- drivers/mtd/nand/nand_base.c	16 Nov 2004 17:16:52 -0000	1.2
+++ drivers/mtd/nand/nand_base.c	29 Sep 2006 22:26:17 -0000	1.5
@@ -41,7 +41,7 @@
  *	The AG-AND chips have nice features for speed improvement,
  *	which are not supported yet. Read / program 4 pages in one go.
  *
- * $Id: nand_base.c,v 1.2 2004/11/16 17:16:52 joff Exp $
+ * $Id: nand_base.c,v 1.5 2006/09/29 22:26:17 joff Exp $
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -277,8 +277,9 @@
 	int i;
 	struct nand_chip *this = mtd->priv;
 
-	for (i=0; i<len; i++)
+	for (i=0; i<len; i++) {
 		writeb(buf[i], this->IO_ADDR_W);
+	}
 }
 
 /**
@@ -291,11 +292,12 @@
  */
 static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
 {
-	int i;
+        int i;
 	struct nand_chip *this = mtd->priv;
 
-	for (i=0; i<len; i++)
-		buf[i] = readb(this->IO_ADDR_R);
+	for (i=0; i<len; i++) {
+	  buf[i] = readb(this->IO_ADDR_R);
+	}
 }
 
 /**
@@ -422,7 +424,6 @@
 		/* Deselect and wake up anyone waiting on the device */
 		nand_release_device(mtd);
 	}	
-	
 	return res;
 }
 
@@ -756,9 +757,10 @@
 static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
 {
 
-	unsigned long	timeo = jiffies;
+	unsigned long	start, timeo;
 	int	status;
 	
+	start = timeo = jiffies;
 	if (state == FL_ERASING)
 		 timeo += (HZ * 400) / 1000;
 	else
@@ -785,7 +787,12 @@
 			if (this->read_byte(mtd) & NAND_STATUS_READY)
 				break;
 		}
-		yield ();
+		/* XXX: Only yield when we've polled for more than 10 ms.
+		 * This prevents drastic slowdown in write speed if first poll
+		 * is false.  --joff
+		 */
+		if ((jiffies - start) > ((HZ * 10)/ 1000))
+			yield ();
 	}
 	status = (int) this->read_byte(mtd);
 	return status;
@@ -806,6 +813,7 @@
  *
  * Cached programming is not supported yet.
  */
+
 static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, 
 	u_char *oob_buf,  struct nand_oobinfo *oobsel, int cached)
 {
@@ -815,7 +823,6 @@
 	int  	*oob_config = oobsel->eccpos;
 	int	datidx = 0, eccidx = 0, eccsteps = this->eccsteps;
 	int	eccbytes = 0;
-	
 	/* FIXME: Enable cached programming */
 	cached = 0;
 	
@@ -834,8 +841,9 @@
 	case NAND_ECC_SOFT:
 		for (; eccsteps; eccsteps--) {
 			this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);
-			for (i = 0; i < 3; i++, eccidx++)
+			for (i = 0; i < 3; i++, eccidx++) {
 				oob_buf[oob_config[eccidx]] = ecc_code[i];
+			}
 			datidx += this->eccsize;
 		}
 		this->write_buf(mtd, this->data_poi, mtd->oobblock);
@@ -847,8 +855,10 @@
 			this->enable_hwecc(mtd, NAND_ECC_WRITE);
 			this->write_buf(mtd, &this->data_poi[datidx], this->eccsize);
 			this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);
-			for (i = 0; i < eccbytes; i++, eccidx++)
+
+			for (i = 0; i < eccbytes; i++, eccidx++) 
 				oob_buf[oob_config[eccidx]] = ecc_code[i];
+
 			/* If the hardware ecc provides syndromes then
 			 * the ecc code must be written immidiately after
 			 * the data bytes (words) */
@@ -1040,7 +1050,6 @@
 	int	compareecc = 1;
 	int	oobreadlen;
 
-
 	DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
 
 	/* Do not allow reads past end of device */
@@ -1135,9 +1144,13 @@
 		}
 			
 		case NAND_ECC_SOFT:	/* Software ECC 3/256: Read in a page + oob data */
+		  
 			this->read_buf(mtd, data_poi, end);
-			for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) 
+
+			for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) {
 				this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
+			}
+			
 			break;	
 
 		default:
@@ -1175,8 +1188,9 @@
 			goto readoob;	
 		
 		/* Pick the ECC bytes out of the oob data */
-		for (j = 0; j < oobsel->eccbytes; j++)
+		for (j = 0; j < oobsel->eccbytes; j++) {
 			ecc_code[j] = oob_data[oob_config[j]];
+		}
 
 		/* correct data, if neccecary */
 		for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) {
@@ -1271,7 +1285,7 @@
 	 * fs driver will take care of that, because
 	 * retlen == desired len and result == -EBADMSG
 	 */
-	*retlen = read;
+	*retlen = ecc_failed ? -1 : read;
 	return ecc_failed ? -EBADMSG : 0;
 }
 
@@ -2051,11 +2065,13 @@
 
 	while (len) {
 		/* Check if we have a bad block, we do not erase bad blocks ! */
+		/* Allow us to erase bad blocks
 		if (nand_block_checkbad(mtd, ((loff_t) page) << this->page_shift, 0, allowbbt)) {
 			printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page);
 			instr->state = MTD_ERASE_FAILED;
 			goto erase_exit;
 		}
+		*/
 		
 		/* Invalidate the page cache, if we erase the block which contains 
 		   the current cached page */
Index: drivers/mtd/nand/ts7250.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/mtd/nand/ts7250.c,v
retrieving revision 1.4
retrieving revision 1.8
diff -u -r1.4 -r1.8
--- drivers/mtd/nand/ts7250.c	30 Dec 2004 22:02:07 -0000	1.4
+++ drivers/mtd/nand/ts7250.c	16 Mar 2007 17:13:19 -0000	1.8
@@ -9,7 +9,7 @@
  *  Derived from drivers/mtd/nand/autcpu12.c
  *       Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
  *
- * $Id: ts7250.c,v 1.4 2004/12/30 22:02:07 joff Exp $
+ * $Id: ts7250.c,v 1.8 2007/03/16 17:13:19 eddie Exp $
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -30,6 +30,31 @@
 #include <asm/arch/hardware.h>
 #include <asm/sizes.h>
 
+struct partition_info { // little-endian format
+  unsigned int bootable:8; // 0x80 = active, 0x00 = don't use for booting
+  unsigned int starting_head:8;
+  unsigned int starting_csect:8;
+  unsigned int starting_xcyl:8;
+  // MSB bits 0-5 are sector
+  unsigned int system_id:8; // format of partition
+  unsigned int ending_head:8;
+  unsigned int ending_csect:8;
+  unsigned int ending_xcyl:8;
+  unsigned int relative_sector:32; // sector offset from start of disk to start of volume
+  unsigned int total_sectors:32;
+} __attribute__((packed));
+
+struct partition_table {
+  struct partition_info part[4];
+} __attribute__((__packed__));
+
+struct MBR {
+  char bootcode[446];
+  struct partition_table pt;
+  unsigned int sig55:8;
+  unsigned int sigAA:8;
+} __attribute__((__packed__));
+
 /*
  * MTD structure for TS7250 board
  */
@@ -48,31 +73,48 @@
  * Define static partitions for flash device
  */
 static struct mtd_partition partition_info32[] = {
-	{ .name= "TS-BOOTROM",
-		  .offset= 0,
-		  .size= 0x4000 },
-	{ .name= "Linux",
-		  .offset= 0x4000,
-		  .size= 0x1d00000 },
-	{ .name= "RedBoot",
-		  .offset= 0x1d04000,
-		  .size= 0x2fc000 },
+        { .name= "TS-BOOTROM",
+                  .offset= 0,
+                  .size= 0x4000 },
+        { .name= "Linux",
+                  .offset= 0x4000,
+                  .size= 0x1d00000 },
+        { .name= "RedBoot",
+                  .offset= 0x1d04000,
+                  .size= 0x2fc000 },
 };
 /*
  * Define static partitions for flash device
  */
 static struct mtd_partition partition_info128[] = {
-	{ .name= "TS-BOOTROM",
+        { .name= "TS-BOOTROM",
+                  .offset= 0,
+                  .size= 0x4000 },
+        { .name= "Linux",
+                  .offset= 0x4000,
+                  .size= 0x7d00000 },
+        { .name= "RedBoot",
+                  .offset= 0x7d04000,
+                  .size= 0x2fc000 },
+};
+
+static struct mtd_partition partition_mbr[] = {
+	{ .name= "whole chip",
 		  .offset= 0,
-		  .size= 0x4000 },
-	{ .name= "Linux",
+		  .size= 0x2000000 },
+	{ .name= "kernel",
 		  .offset= 0x4000,
-		  .size= 0x7d00000 },
-	{ .name= "RedBoot",
-		  .offset= 0x7d04000,
-		  .size= 0x2fc000 },
+		  .size= 0x200000 },
+	{ .name= "initrd",
+		  .offset= 0x204000,
+		  .size= 0x200000 },
+	{ .name= "rootfs",
+		  .offset= 0x404000,
+		  .size= 0x2000000 - 0x404000 },
+	{ .name= "part4",
+		  .offset= 0x2000000,
+		  .size= 0 },
 };
-#define NUM_PARTITIONS 3
 
 extern int parse_cmdline_partitions(struct mtd_info *master, 
 				    struct mtd_partition **pparts,
@@ -110,6 +152,41 @@
 	}
 }
 
+static void ts7250_enable_hwecc(struct mtd_info *mtd,int dir) {
+  volatile int *ts7x = (void *)IO_ADDRESS(TS7XXX_NAND_FLASH_CTRL);
+  int dummy;
+
+  dummy = *ts7x;
+  //printk("enable_hwecc\n");
+}
+
+static int ts7250_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) {
+     volatile int *ts7x = (void *)IO_ADDRESS(TS7XXX_NAND_FLASH_CTRL);
+     int dummy;
+
+     dummy =  0xFFFFFF & ((*ts7x >> 6) | 3);
+     //printk("Hardware ECC: %06X ",dummy);
+     ecc_code[0] = (dummy >> 16) & 0xFF;
+     ecc_code[1] = (dummy >> 8) & 0xFF;
+     ecc_code[2] = (dummy >> 0) & 0xFF;
+     //printk("(%02X %02X %02X)\n",ecc_code[0],ecc_code[1],ecc_code[2]);
+     return dummy;
+}
+
+static int detect_hwecc(struct nand_chip *this) {
+  volatile int *ts7x = (void *)IO_ADDRESS(TS7XXX_NAND_FLASH_CTRL);
+  int dummy;
+
+  dummy = *ts7x;
+  dummy = *ts7x;
+  if ((dummy >> 8) != -1) {
+    return 0;
+  }
+  writeb(0x1, this->IO_ADDR_W);
+  dummy = *ts7x;
+   return ((dummy >> 8) != -1);
+}
+
 /*
  *	read device ready pin
  */
@@ -118,6 +195,7 @@
 	return readb(ts7250_nandbusy) & 0x20;
 }
 
+extern int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
 /*
  * Main initialization routine
  */
@@ -127,6 +205,11 @@
 	const char *part_type = 0;
 	int mtd_parts_nb = 0;
 	struct mtd_partition *mtd_parts = 0;
+	char buf[512];
+	int i,got = 0,parts=0,hwecc = 0;
+	unsigned ofs=0;
+	volatile unsigned short *tsmodel;
+	struct MBR *mbr = (struct MBR *)buf;
 
 	if (readb(IO_ADDRESS(TS7XXX_MODEL)) == 0x0)  /* is TS-7200 */
 		return -ENXIO;
@@ -153,28 +236,78 @@
 	
 	/* insert callbacks */
 	if (readb(IO_ADDRESS(TS7XXX_STATUS2)) & 0x4) { 
-		this->IO_ADDR_R = IO_ADDRESS(TS7XXX_NAND_FLASH2_DATA);
-		this->IO_ADDR_W = IO_ADDRESS(TS7XXX_NAND_FLASH2_DATA);
+		this->IO_ADDR_R = (void *)IO_ADDRESS(TS7XXX_NAND_FLASH2_DATA);
+		this->IO_ADDR_W = (void *)IO_ADDRESS(TS7XXX_NAND_FLASH2_DATA);
 		ts7250_nandbusy = IO_ADDRESS(TS7XXX_NAND_FLASH2_BUSY);
 		ts7250_nandctrl = IO_ADDRESS(TS7XXX_NAND_FLASH2_CTRL);
 	} else {
-		this->IO_ADDR_R = IO_ADDRESS(TS7XXX_NAND_FLASH_DATA);
-		this->IO_ADDR_W = IO_ADDRESS(TS7XXX_NAND_FLASH_DATA);
+		this->IO_ADDR_R = (void *)IO_ADDRESS(TS7XXX_NAND_FLASH_DATA);
+		this->IO_ADDR_W = (void *)IO_ADDRESS(TS7XXX_NAND_FLASH_DATA);
 		ts7250_nandbusy = IO_ADDRESS(TS7XXX_NAND_FLASH_BUSY);
 		ts7250_nandctrl = IO_ADDRESS(TS7XXX_NAND_FLASH_CTRL);
 	}
+
 	this->hwcontrol = ts7250_hwcontrol;
 	this->dev_ready = ts7250_device_ready;
 	this->chip_delay = 15;
 	this->eccmode = NAND_ECC_SOFT;
-	
+	this->options = 0;
+
+	tsmodel = (short *)__ioremap(0x22000000, 2, 0);
+
 	printk("Searching for NAND flash...\n");
 	/* Scan to find existence of the device */
 	if (nand_scan (ts7250_mtd, 1)) {
 		kfree (ts7250_mtd);
 		return -ENXIO;
 	}
-	
+	for (i=0;i<512;i++) {
+	  buf[i] = 0;
+	}
+
+	if((*tsmodel & 0x07) != 0x04)  { //if the current board isn't a TS-7400
+	  if (ts7250_mtd->size >= (128 * 0x100000)) {
+	    mtd_parts = partition_info128;
+	  } else {
+	    mtd_parts = partition_info32;
+	  }
+	  parts = 3;
+	} else {
+	  hwecc = detect_hwecc(this);
+	  printk("Hardware ECC ");
+	  if (!hwecc) {
+	    printk("NOT ");
+	  } else {
+	    this->eccmode = NAND_ECC_HW3_256;
+	    this->enable_hwecc = ts7250_enable_hwecc;
+	    this->calculate_ecc = ts7250_calculate_ecc;
+	    this->correct_data = nand_correct_data;
+	  }
+	  printk("detected\n");
+
+	  if (mbr->sigAA == 0xAA && mbr->sig55 == 0x55) {
+	    for (i=0;i<4;i++) {
+	      if (mbr->pt.part[i].total_sectors) {
+		parts++;
+		partition_mbr[i+1].offset = 
+		  512 * mbr->pt.part[i].relative_sector;
+		if (i < 2) {
+		  partition_mbr[i+1].size = 
+		    512 * mbr->pt.part[i+1].relative_sector -
+		    512 * mbr->pt.part[i].relative_sector;
+		} else {
+		  partition_mbr[i+1].size = 
+		    512 * mbr->pt.part[i].total_sectors;
+		}
+		ofs = partition_mbr[i+1].offset 
+		  + partition_mbr[i+1].size;
+	      }
+	    }
+	  } else {
+	    parts = 4;
+	  }
+	  mtd_parts = partition_mbr;
+	}
 #ifdef CONFIG_MTD_CMDLINE_PARTS
 	mtd_parts_nb = parse_cmdline_partitions(ts7250_mtd, &mtd_parts, 
 						"ts7250-nand");
@@ -185,11 +318,17 @@
 #endif
 	if (mtd_parts_nb == 0)
 	{
-		mtd_parts = partition_info32;
+	  //mtd_parts = partition_info32;
+		/*
 		if (ts7250_mtd->size >= (128 * 0x100000))
 			mtd_parts = partition_info128;
-		mtd_parts_nb = NUM_PARTITIONS;
-		part_type = "static";
+		*/
+		mtd_parts_nb = parts;
+		if (parts > 0) {
+		  part_type = "MBR";
+		} else {
+		  part_type = "static";
+		}
 	}
 	
 	/* Register the partitions */
@@ -206,9 +345,10 @@
  */
 static void __exit ts7250_cleanup (void)
 {
-	struct nand_chip *this = (struct nand_chip *) &ts7250_mtd[1];
+  //	struct nand_chip *this = (struct nand_chip *) &ts7250_mtd[1];
 	
 	/* Unregister the device */
+	del_mtd_partitions(ts7250_mtd);
 	del_mtd_device (ts7250_mtd);
 	
 	/* Free the MTD device structure */
Index: drivers/mtd2k/Config.in
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/mtd2k/Config.in,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- drivers/mtd2k/Config.in	14 Feb 2006 18:17:51 -0000	1.2
+++ drivers/mtd2k/Config.in	29 Sep 2006 22:31:03 -0000	1.3
@@ -1,5 +1,5 @@
 
-# $Id: Config.in,v 1.2 2006/02/14 18:17:51 michael Exp $
+# $Id: Config.in,v 1.3 2006/09/29 22:31:03 joff Exp $
 
 mainmenu_option next_comment
 comment 'Memory Technology Devices (MTD) for 2k NAND flash'
@@ -38,13 +38,13 @@
    fi
    dep_tristate '  INFTL (Inverse NAND Flash Translation Layer) support' CONFIG_INFTL $CONFIG_MTD2K
 
-   source drivers/mtd/chips/Config.in
+   source drivers/mtd2k/chips/Config.in
 
-   source drivers/mtd/maps/Config.in
+   source drivers/mtd2k/maps/Config.in
 
-   source drivers/mtd/devices/Config.in
+   source drivers/mtd2k/devices/Config.in
 
-   source drivers/mtd/nand/Config.in
+   source drivers/mtd2k/nand/Config.in
 
 fi
 
Index: drivers/mtd2k/nand/Config.in
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/mtd2k/nand/Config.in,v
retrieving revision 1.1
retrieving revision 1.5
diff -u -r1.1 -r1.5
--- drivers/mtd2k/nand/Config.in	9 Feb 2006 21:53:06 -0000	1.1
+++ drivers/mtd2k/nand/Config.in	16 Jan 2009 18:48:36 -0000	1.5
@@ -1,12 +1,12 @@
 # drivers/mtd/nand/Config.in
 
-# $Id: Config.in,v 1.1 2006/02/09 21:53:06 michael Exp $
+# $Id: Config.in,v 1.5 2009/01/16 18:48:36 eddie Exp $
 
 mainmenu_option next_comment
 
 comment 'NAND Flash Device Drivers'
 
-dep_tristate '  NAND Device Support' CONFIG_MTD_NAND $CONFIG_MTD
+dep_tristate '  NAND Device Support' CONFIG_MTD_NAND $CONFIG_MTD2K
 if [ "$CONFIG_MTD_NAND" = "y" -o "$CONFIG_MTD_NAND" = "m" ]; then
    bool '    Verify NAND page writes' CONFIG_MTD_NAND_VERIFY_WRITE
 fi
@@ -17,6 +17,9 @@
    dep_tristate ' SmartMedia Card on AUTCPU12 board' CONFIG_MTD_NAND_AUTCPU12 $CONFIG_MTD_NAND $CONFIG_ARCH_AUTCPU12
    dep_tristate '  NAND Flash device on EDP7312 board' CONFIG_MTD_NAND_EDB7312 $CONFIG_MTD_NAND $CONFIG_ARCH_EDB7312
    dep_tristate '  NAND Flash device on TS-7250 board' CONFIG_MTD_NAND_TS7250 $CONFIG_MTD_NAND
+   dep_tristate '  NAND Flash device on TS-7400 board' CONFIG_MTD_NAND_TS7400 $CONFIG_MTD_NAND
+   dep_tristate '  NAND Flash device on BT200 board' CONFIG_MTD_NAND_BT200 $CONFIG_MTD_NAND
+   dep_tristate '  NAND Flash device on Prosoft 8000 board' CONFIG_MTD_NAND_PROSOFT $CONFIG_MTD_NAND
 fi
 
 if [ "$CONFIG_MTD_DOC2001PLUS" = "y" -o "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" -o "$CONFIG_MTD_NAND" = "y" ]; then
Index: drivers/mtd2k/nand/Makefile.common
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/mtd2k/nand/Makefile.common,v
retrieving revision 1.1
retrieving revision 1.4
diff -u -r1.1 -r1.4
--- drivers/mtd2k/nand/Makefile.common	9 Feb 2006 21:53:06 -0000	1.1
+++ drivers/mtd2k/nand/Makefile.common	16 Jan 2009 18:50:18 -0000	1.4
@@ -1,7 +1,7 @@
 #
 # linux/drivers/nand/Makefile
 #
-# $Id: Makefile.common,v 1.1 2006/02/09 21:53:06 michael Exp $
+# $Id: Makefile.common,v 1.4 2009/01/16 18:50:18 eddie Exp $
 
 obj-$(CONFIG_MTD_NAND)			+= nand.o nand_ecc.o
 obj-$(CONFIG_MTD_NAND_IDS)		+= nand_ids.o
@@ -19,6 +19,9 @@
 obj-$(CONFIG_MTD_NAND_H1900)		+= h1910.o
 obj-$(CONFIG_MTD_NAND_RTC_FROM4)	+= rtc_from4.o
 obj-$(CONFIG_MTD_NAND_TS7250)		+= ts7250.o
+obj-$(CONFIG_MTD_NAND_BT200)		+= ts7400.o
+obj-$(CONFIG_MTD_NAND_TS7400)		+= ts7400.o
+obj-$(CONFIG_MTD_NAND_PROSOFT)		+= ts7350.o
 
 nand-objs = nand_ecc.o nand_ids.o
 
Index: drivers/mtd2k/nand/makeModules
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/mtd2k/nand/makeModules,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- drivers/mtd2k/nand/makeModules	14 Feb 2006 18:18:27 -0000	1.2
+++ drivers/mtd2k/nand/makeModules	19 Oct 2006 17:34:17 -0000	1.3
@@ -1,6 +1,10 @@
 CMD="/home/michael/ts-7200/tools/crosstool/arm-9tdmi-linux-gnu/gcc-3.3.4-glibc-2.3.2/bin/arm-9tdmi-linux-gnu-gcc -D__KERNEL__ -I/home/michael/ts-7200/dist/linux24/include -Wall -Wstrict-prototypes -Wno-trigraphs -Os -fno-strict-aliasing -fno-common -Uarm -fno-common -pipe -mapcs-32 -D__LINUX_ARM_ARCH__=4 -march=armv4 -mtune=arm9tdmi -msoft-float -Uarm -DMODULE  -nostdinc -iwithprefix include -DKBUILD_BASENAME=p80211mod  -DEXPORT_SYMTAB -c"
-`$CMD nand_ecc.c`
-`$CMD nand_ids.c`
-`$CMD nand.c`
-`$CMD ts7250.c`
-cp *.o /mnt/tmp/new
+`$CMD nand_ecc2k.c`
+`$CMD nand_ids2k.c`
+`$CMD nand2k.c`
+`$CMD ts72502k.c`
+sudo cp nand_ecc2k.o /u/x/lib/modules/2.4.26-ts11/kernel/drivers/mtd2k/nand/
+sudo cp nand_ids2k.o /u/x/lib/modules/2.4.26-ts11/kernel/drivers/mtd2k/nand/
+sudo cp nand2k.o /u/x/lib/modules/2.4.26-ts11/kernel/drivers/mtd2k/nand/
+sudo cp ts72502k.o /u/x/lib/modules/2.4.26-ts11/kernel/drivers/mtd2k/nand/
+
Index: drivers/mtd2k/nand/nand.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/mtd2k/nand/nand.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- drivers/mtd2k/nand/nand.c	9 Feb 2006 21:53:06 -0000	1.1
+++ drivers/mtd2k/nand/nand.c	22 Jan 2009 18:14:20 -0000	1.2
@@ -34,7 +34,7 @@
  *	Check if buffering the last not aligned read can speed up
  *	performance
  *
- * $Id: nand.c,v 1.1 2006/02/09 21:53:06 michael Exp $
+ * $Id: nand.c,v 1.2 2009/01/22 18:14:20 eddie Exp $
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -1737,7 +1737,7 @@
 	instr->state = MTD_ERASING;
 
 	while (len) {
-		/* Check if we have a bad block, we do not erase bad blocks ! */
+		/* Check if we have a bad block, we do not erase bad blocks ! 
 		if (this->block_bad(mtd, (loff_t) page, 0)) {
 			// COMMENT THIS OUT TO ENABLE ERASURE OF BAD BLOCKS
 			// Only useful for erasing accidentally marked bad blocks
@@ -1747,6 +1747,7 @@
 			goto erase_exit;
 			
 		}
+		*/
 
 		/* Send commands to erase a page */
 		this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
@@ -1958,10 +1959,18 @@
 			extid >>= 2;
 			/* Get buswidth information */
 			busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
-		
+/*
+printk("page size = 0x%x\n", mtd->oobblock);	
+printk("oob size = 0x%x\n", mtd->oobsize);	
+printk("erase size = 0x%x\n", mtd->erasesize);	
+printk("busw= 0x%x\n", busw);	
+*/
+
+
 			/* Do not replace user supplied command function ! */
-			if (this->cmdfunc == nand_command)
+			if (this->cmdfunc == nand_command) {
 				this->cmdfunc = nand_command_lp;
+			}
 		} else {
 			/* Old devices have this data hardcoded in the
 			 * device id table */
@@ -2028,11 +2037,13 @@
 	}
 	if (i > 1)
 		printk(KERN_INFO "%d NAND chips detected\n", i);
-	
+
+	/*
 	if (!this->oob_buf || !this->data_buf) {
 		printk (KERN_WARNING "nand_scan(): Buffers not set. Scan aborted\n"); 
 		return 1;
 	}
+	*/
 
 	/* Store the number of chips and calc total size for mtd */
 	this->numchips = i;
Index: drivers/mtd2k/nand/ts7250.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/mtd2k/nand/ts7250.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- drivers/mtd2k/nand/ts7250.c	9 Feb 2006 21:53:06 -0000	1.1
+++ drivers/mtd2k/nand/ts7250.c	6 Mar 2007 22:26:40 -0000	1.2
@@ -9,7 +9,7 @@
  *  Derived from drivers/mtd/nand/autcpu12.c
  *       Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
  *
- * $Id: ts7250.c,v 1.1 2006/02/09 21:53:06 michael Exp $
+ * $Id: ts7250.c,v 1.2 2007/03/06 22:26:40 eddie Exp $
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -290,6 +290,7 @@
   //	struct nand_chip *this = (struct nand_chip *) &ts7250_mtd[1];
 	
 	/* Unregister the device */
+	del_mtd_partitions(ts7250_mtd);
 	del_mtd_device (ts7250_mtd);
 	
 	/* Free the MTD device structure */
Index: drivers/net/cs89x0.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/net/cs89x0.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- drivers/net/cs89x0.c	17 Oct 2005 18:04:48 -0000	1.6
+++ drivers/net/cs89x0.c	2 May 2007 22:52:00 -0000	1.7
@@ -1787,7 +1787,7 @@
 	
 	// x86 IRQ --> ARM IRQ
 	if (irq == 6 || irq == 0) irq = 33;
-	if (irq == 7) irq = 34;
+	if (irq == 7) irq = 40;
 #endif
 
 	dev_cs89x0.irq = irq;
Index: drivers/serial/Config.in
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/serial/Config.in,v
retrieving revision 1.5
retrieving revision 1.10
diff -u -r1.5 -r1.10
--- drivers/serial/Config.in	24 May 2006 00:32:03 -0000	1.5
+++ drivers/serial/Config.in	15 Feb 2007 19:25:06 -0000	1.10
@@ -1,7 +1,7 @@
 #
 # Serial device configuration
 #
-# $Id: Config.in,v 1.5 2006/05/24 00:32:03 michael Exp $
+# $Id: Config.in,v 1.10 2007/02/15 19:25:06 eddie Exp $
 #
 mainmenu_option next_comment
 comment 'Serial drivers'
@@ -30,6 +30,11 @@
   tristate 'Support for TS-7300 TS-UARTs' CONFIG_SERIAL_TSUART73
   tristate 'Support for TS-940 TS-UARTs' CONFIG_SERIAL_TSUART940
   tristate 'Support for TS-BAT3 TS-UARTs' CONFIG_SERIAL_TSUART3BAT
+  tristate 'Support for TS-PAC4 TS-UARTs' CONFIG_SERIAL_TSUART4PAC
+  tristate 'Support for TS-7400 TS-UARTs' CONFIG_SERIAL_TSUART7400
+  tristate 'Support for TS-RF2 TS-UARTs' CONFIG_SERIAL_TSUARTRF2
+  tristate 'Support for BOC-EDWARDS TS-UARTs' CONFIG_SERIAL_TSUARTBOC
+  tristate 'Support for TS-733 TS-UARTs' CONFIG_SERIAL_TS733
 
 # EP93xx AMBA serial port
   if [ "$CONFIG_ARCH_EP9301" = "y" -o \
Index: drivers/serial/Makefile
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/serial/Makefile,v
retrieving revision 1.5
retrieving revision 1.11
diff -u -r1.5 -r1.11
--- drivers/serial/Makefile	24 May 2006 00:32:14 -0000	1.5
+++ drivers/serial/Makefile	15 Feb 2007 19:25:17 -0000	1.11
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definitions are now inherited from the
 # parent makes..
 #
-#  $Id: Makefile,v 1.5 2006/05/24 00:32:14 michael Exp $
+#  $Id: Makefile,v 1.11 2007/02/15 19:25:17 eddie Exp $
 #
 
 O_TARGET := serial.o
@@ -34,10 +34,15 @@
 obj-$(CONFIG_SERIAL_OMAHA) += omaha.o
 obj-$(CONFIG_SERIAL_AT91US3) += at91us3.o
 obj-$(CONFIG_SERIAL_TSUART) += tsuart0.o
-obj-$(CONFIG_SERIAL_TSUART) += tsuart-73.o
+obj-$(CONFIG_SERIAL_TSUART73) += tsuart-73.o
 obj-$(CONFIG_SERIAL_TSUART7260) += tsuart7260.o
 obj-$(CONFIG_SERIAL_TSUART940) += tsuart940.o
 obj-$(CONFIG_SERIAL_TSUART3BAT) += tsuart-bat3.o
+obj-$(CONFIG_SERIAL_TSUART4PAC) += tsuart-pac4.o
+obj-$(CONFIG_SERIAL_TSUART7400) += tsuart7400.o
+obj-$(CONFIG_SERIAL_TSUARTRF2) += tsuart-rf.o
+obj-$(CONFIG_SERIAL_TSUARTBOC) += tsuart-boc.o
+obj-$(CONFIG_SERIAL_TS733) += tsuart-733.o
 
 include $(TOPDIR)/Rules.make
 
Index: drivers/serial/ep93xx_amba.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/serial/ep93xx_amba.c,v
retrieving revision 1.15
retrieving revision 1.22
diff -u -r1.15 -r1.22
--- drivers/serial/ep93xx_amba.c	22 Mar 2006 23:06:16 -0000	1.15
+++ drivers/serial/ep93xx_amba.c	8 Feb 2007 15:27:14 -0000	1.22
@@ -24,7 +24,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- *  $Id: ep93xx_amba.c,v 1.15 2006/03/22 23:06:16 michael Exp $
+ *  $Id: ep93xx_amba.c,v 1.22 2007/02/08 15:27:14 michael Exp $
  *
  * The EP9312 serial ports are AMBA, but at different addresses from the
  * integrator.
@@ -47,6 +47,7 @@
 
 #include <asm/hardware.h>
 #include <asm/io.h>
+#include "ts7412.h"
 #include <asm/irq.h>
 
 #define DELAY_TIMEX HZ
@@ -76,13 +77,14 @@
 #define CALLOUT_AMBA_MINOR	16
 #define CALLOUT_AMBA_NR		UART_NR
 
-static struct timer_list timer = { .function = NULL };
+static struct timer_list timer = { .function = NULL }, timer0 = { .function = NULL };
 static struct tty_driver normal, callout;
 static struct tty_struct *cs_amba_table[UART_NR];
 static struct termios *cs_amba_termios[UART_NR], *cs_amba_termios_locked[UART_NR];
 static struct uart_amba_port amba_ports[];
 
 static int ts7200_uart_get_fr (struct uart_port *) ;
+static void ts7200_uart_set_mcr (struct uart_port *, unsigned ) ;
 
 #ifdef SUPPORT_SYSRQ
 static struct console cs_amba_console;
@@ -113,7 +115,9 @@
 #define UART_TX_READY(s)		(((s) & AMBA_UARTFR_TXFF) == 0)
 #define UART_TX_EMPTY(p)		((UART_GET_FR(p) & AMBA_UARTFR_TMSK) == 0)
 
-#define UART_PUT_MCR(c, p)  writel((c), (p)->membase + ((unsigned int)UART1MCR & 0xffff))
+#define UART_PUT_MCR(c, p)  (ts7200_uart_set_mcr(p,c))
+//#define UART_PUT_MCR(c, p)  writel((c), (p)->membase + ((unsigned int)UART1MCR & 0xffff))
+
 #define UART_CLEAR_ECR(p)   writel( 0, (p)->membase + AMBA_UARTECR)
 
 #define UART_DUMMY_RSR_RX	256
@@ -551,21 +555,23 @@
 
 static void csambauart_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
-	struct uart_amba_port *uap = (struct uart_amba_port *)port;
-	unsigned int ctrl = uap->rts_mask | uap->dtr_mask;
+  struct uart_amba_port *uap = (struct uart_amba_port *)port;
+  int rts_mask = gpio ? 2 : uap->rts_mask; // override default non-existence of RTS/DTR when 7412 used.
+  int dtr_mask = gpio ? 1 : uap->dtr_mask; // yech! this is awful...
+  unsigned int ctrl = rts_mask | dtr_mask;
 	
-	/* If there's no RTS and DTR for this UART, do nothing here */
-	if(	(uap->rts_mask == 0) && (uap->dtr_mask == 0) )
-		return;
+  /* If there's no RTS and DTR for this UART, do nothing here */
+  if( (rts_mask == 0) && (dtr_mask == 0) )
+    return;
   
-    if ((mctrl & TIOCM_RTS) == 0)
-		ctrl &= ~uap->rts_mask;
-	
-    if ((mctrl & TIOCM_DTR) == 0) {
-		ctrl &= ~uap->dtr_mask;
-    }
-
-	UART_PUT_MCR(ctrl, port);
+  if ((mctrl & TIOCM_RTS) == 0)
+    ctrl &= ~rts_mask;
+  
+  if ((mctrl & TIOCM_DTR) == 0) {
+    ctrl &= ~dtr_mask;
+  }
+  
+  UART_PUT_MCR(ctrl, port);
 }
 
 static void csambauart_break_ctl(struct uart_port *port, int break_state)
@@ -874,12 +880,30 @@
  * have DCD hooked up the typical way on COM1, hence, the following 
  * hack
  * 	-- L.A.Y, Technologic Systems 08/2004
+ *
+ * The TS-7412 board requires that we use DIO pins for our COM1 and COM2
+ * flow control signals.  Added ts7200_uart_set_mcr and modified the
+ * existing ts7200_uart_get_fr to accomodate this.
+ *      -- M 11/13/2006
  */
 static int ts7200_uart_get_fr (struct uart_port *port) {
 	volatile unsigned long uart_fr ;
 	unsigned short dcd_status;
+	int port1 = (port->membase != amba_ports[0].port.membase) ? 1 : 0;
+
+	if (gpio) {
+	  uart_fr = (readl(port->membase + AMBA_UARTFR) ) & 0xff ;
+          uart_fr |= AMBA_UARTFR_DSR; // NOT TIOCM_DSR!!!
+	  if (mdmctrl1_dcd_get(port1)) {
+	    uart_fr |= AMBA_UARTFR_DCD; // NOT TIOCM_CAR!!!
+    	  }
+	  if (mdmctrl1_cts_get(port1)) {
+	    uart_fr |= AMBA_UARTFR_CTS; // NOT TIOCM_CTS !!!
+	  }
+	  return uart_fr;
+	}
 
-	if (port->membase != amba_ports[0].port.membase )
+	if (port1 == 1)
 	  return ( (readl(port->membase + AMBA_UARTFR) ) & 0xff );
 
 	uart_fr = (readl(port->membase + AMBA_UARTFR) ) & 0xff ;
@@ -892,6 +916,17 @@
 
 	return uart_fr;
 }
+
+static void ts7200_uart_set_mcr (struct uart_port *p, unsigned c) {
+  int port1 = (p->membase != amba_ports[0].port.membase) ? 1 : 0;
+
+  if (gpio) {
+    mdmctrl1_dtr_set(port1,((c & 1) /* & TIOCM_DTR */)?1:0);
+    mdmctrl1_rts_set(port1,((c & 2) /*& TIOCM_RTS */)?1:0);
+  } else {
+    writel((c), (p)->membase + ((unsigned int)UART1MCR & 0xffff));
+  }
+}
 	
 #ifdef CONFIG_SERIAL_AMBA_CONSOLE
 
@@ -1020,7 +1055,13 @@
 
 void __init ambauart_console_init(void)
 {
-	register_console(&cs_amba_console);
+#ifdef TS7412_UNSAFE_DETECTION
+  timer0.expires = jiffies + HZ;
+  timer0.data = 0;
+  timer0.function = ts7412_detect_test;
+  add_timer (&timer0);
+#endif
+  register_console(&cs_amba_console);
 }
 
 #define AMBA_CONSOLE	&cs_amba_console
@@ -1081,3 +1122,168 @@
 MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd/Cirrus Logic, Inc.");
 MODULE_DESCRIPTION("EP9312 ARM AMBA serial port driver");
 MODULE_LICENSE("GPL");
+
+/*
+  All our TS-7412 specific stuff goes here.
+ */
+char *gpio = 0;
+
+static void gpio_dir_set(int pin,int output) {
+  if (!gpio) {
+    return;
+  }
+  if (pin > 7) {
+    pin -= 8;
+    if (output) {
+      gpio[1] |= (1 << pin);
+    } else {
+      gpio[1] &= ~(1 << pin);
+    }
+  } else {
+    if (output) {
+      gpio[0] |= (1 << pin);
+    } else {
+      gpio[0] &= ~(1 << pin);
+    }
+  }
+}
+
+static int gpio_data_get(int pin) {
+  if (!gpio) {
+    return 0;
+  }
+  if (pin > 7) {
+    pin -= 8;
+    return (gpio[3] >> pin) & 1;
+  } else {
+    return (gpio[2] >> pin) & 1;
+  }
+}
+
+static void gpio_data_set(int pin,int value) {
+  if (!gpio) {
+    return;
+  }
+  if (pin > 7) {
+    pin -= 8;
+    if (value) {
+      gpio[3] |= (1 << pin);
+    } else {
+      gpio[3] &= ~(1 << pin);
+    }
+  } else {
+    if (value) {
+      gpio[2] |= (1 << pin);
+    } else {
+      gpio[2] &= ~(1 << pin);
+    }
+  }
+}
+
+void mdmctrl_rts_set(int val) {
+  gpio_dir_set(10,1);
+  gpio_data_set(10,val?0:1);
+  //printk("RTS3=%d\n",val?0:1);
+}
+void mdmctrl_dtr_set(int val) {
+  gpio_dir_set(9,1);
+  gpio_data_set(9,val?0:1);
+  //printk("DTR3=%d\n",val?0:1);
+}
+int mdmctrl_cts_get(void) {
+  gpio_dir_set(5,1);
+  gpio_data_set(5,1);
+  //printk("CTS3==%d\n",gpio_data_get(2)?0:1);
+  return gpio_data_get(2)?0:1;
+}
+int mdmctrl_dcd_get(void) {
+  gpio_dir_set(5,1);
+  gpio_data_set(5,1);
+  //printk("DCD3==%d\n",gpio_data_get(3)?0:1);
+  return gpio_data_get(3)?0:1;
+}
+
+void mdmctrl1_rts_set(int port,int val) {
+  gpio_dir_set(port?14:12,1);
+  gpio_data_set(port?14:12,val?0:1);
+  //printk("RTS1 %d=%d\n",port,val?0:1);
+}
+void mdmctrl1_dtr_set(int port,int val) {
+  gpio_dir_set(port?13:11,1);
+  gpio_data_set(port?13:11,val?0:1);
+  //printk("DTR1 %d=%d\n",port,val?0:1);
+}
+int mdmctrl1_cts_get(int port) {
+  int ret;
+
+  gpio_dir_set(5,1);
+  gpio_data_set(5,port?0:1);
+  ret = gpio_data_get(0)?0:1;
+  //printk("CTS1 %d==%d\n",port,ret);
+  return ret;
+}
+int mdmctrl1_dcd_get(int port) {
+  int ret;
+
+  gpio_dir_set(5,1);
+  gpio_data_set(5,port?0:1);
+  ret = gpio_data_get(1)?0:1;
+  //printk("DCD1 %d==%d\n",port,ret);
+  return  ret;
+}
+
+int ts7412_detect_test(unsigned long data) {
+  char *ctrl = 0;
+  if (gpio == 0) {
+    gpio = (char *)__ioremap(0x12c00000,4096,0);
+    if (gpio == 0) {
+      printk(KERN_ERR "tsuart7400: cannot io map gpio\n");
+      return 0;
+    }
+    gpio_dir_set(5,1);
+    gpio_dir_set(6,1);
+    gpio_dir_set(14,1);
+    gpio_dir_set(3,0);
+    gpio_data_set(5,0);  // SEL
+    gpio_data_set(6,0);  // RD_257A#
+    gpio_data_set(14,0); // RTS1#
+    if (gpio_data_get(3) != 0) {
+      //printk("7412 test failed at 1\n");
+      __iounmap(gpio);
+      gpio = 0;
+      goto detect_7412_done;
+    }
+    gpio_data_set(14,1); // RTS1#
+    if (gpio_data_get(3) != 1) {
+      //printk("7412 test failed at 2\n");
+      __iounmap(gpio);
+      gpio = 0;
+      goto detect_7412_done;
+    }
+    gpio_data_set(5,1);  // RD_257A#
+    gpio_data_set(14,gpio_data_get(3)?0:1);
+    if (gpio_data_get(14) == gpio_data_get(3)) {
+      //printk("7412 test failed at 3.1\n");
+      __iounmap(gpio);
+      gpio = 0;
+      goto detect_7412_done;
+    }
+  }
+  ctrl = (char *)__ioremap(0x12000000,4096,0);
+  if (ctrl == 0) {
+    printk(KERN_ERR "ts7412 detection: cannot io map ctrl\n");
+    __iounmap(gpio);
+    gpio = 0;
+    return 0;
+  }
+  *ctrl |= 1;
+  //  printk("CTRL=%02X\n",*ctrl);
+  __iounmap(ctrl);
+ detect_7412_done:
+  if (gpio) {
+    printk("EP93XX_AMBA detected a TS-7400 with TS-7412 present\n");
+  } else {
+    //printk("EP93XX_AMBA did NOT detect a TS-7412.\n");
+  }
+  return (gpio != 0);
+}
Index: drivers/serial/tsuart-73.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/serial/tsuart-73.c,v
retrieving revision 1.3
retrieving revision 1.5
diff -u -r1.3 -r1.5
--- drivers/serial/tsuart-73.c	15 Jun 2006 18:51:10 -0000	1.3
+++ drivers/serial/tsuart-73.c	11 Jan 2007 00:44:41 -0000	1.5
@@ -55,6 +55,7 @@
   ts1->portStatus = &ts1->pstatus;
   ts1->pstatus = 0;
   ts1->u.irq = 40;
+  ts1->mdmctrl = 0;
   ts1->localStatus |= PORT_STATUS_SHIRQ;
   ts1->boardId = BOARD_ID_7300;
 
@@ -72,10 +73,12 @@
   ts2->debugFlags = portDebug;
   ts2->u.mapbase = 0x72000000 + base;
   ts2->portStatus = &ts1->pstatus;
+  ts2->pstatus |= PORT_STATUS_TXMSB;
   ts2->u.irq = 40;
+  ts2->mdmctrl = 0;
   ts2->localStatus |= PORT_STATUS_SHIRQ;
   ts2->boardId = BOARD_ID_7300;
-  //  printk("registering %p,%p\n",ts1,ts2);
+  printk("registering %p,%p\n",ts1,ts2);
   if (tsuart_register_port(dr8,ts1) == 0) {
     //    return 1;
   }
Index: drivers/serial/tsuart-bat3.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/serial/tsuart-bat3.c,v
retrieving revision 1.2
retrieving revision 1.4
diff -u -r1.2 -r1.4
--- drivers/serial/tsuart-bat3.c	26 May 2006 20:46:54 -0000	1.2
+++ drivers/serial/tsuart-bat3.c	4 Oct 2006 15:14:28 -0000	1.4
@@ -35,6 +35,8 @@
 
 #define BOARD_ID_BAT3 0xBA13
 
+struct uart_driver *dr3=0;
+
 static int tryInitPort(int iobase) {
   struct tsuart_port *ts;
   unsigned val;
@@ -51,6 +53,7 @@
   ts->debugFlags = portDebug;
   ts->u.iobase = TS7XXX_IO8_BASE + TS7XXX_ISAIO + iobase;
   ts->boardId = BOARD_ID_BAT3;
+  ts->mdmctrl = 0;
   val = tsuartGetIO8(ts,0);
   if (val == 0x28) {
     val = tsuartGetIO8(ts,1);
@@ -64,7 +67,7 @@
     }
     ts->u.iobase += 2; // point to STAT register
     printk("TS-UART/BAT3 found at port 0x%04X\n",iobase);
-    if (tsuart_register_port(0,ts) == 0) {
+    if (tsuart_register_port(dr3,ts) == 0) {
       return 1;
     }
   }
@@ -75,6 +78,16 @@
 {
   int ports=0;
 
+  dr3 = kmalloc(sizeof(struct uart_driver),GFP_KERNEL);
+  if (!dr3) {
+    printk("Unable to allocate memory for tsuart port\n");
+    return 0;
+  }
+ if (!tsuart_configure_driver(dr3,234,95,"ttyT3S","cuat3s",4)) {
+    kfree(dr3);
+    return 0;
+  }
+
   if (tryInitPort(0x100)) {
     ports++;
   }
Index: drivers/serial/tsuart-pac4.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/serial/tsuart-pac4.c,v
retrieving revision 1.1
retrieving revision 1.6
diff -u -r1.1 -r1.6
--- drivers/serial/tsuart-pac4.c	26 May 2006 23:26:03 -0000	1.1
+++ drivers/serial/tsuart-pac4.c	3 Mar 2009 00:45:53 -0000	1.6
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/serial/tsuart7260.c
+ *  linux/drivers/serial/tsuart-pac4.c
  *
  *  TS-UART loader for Technologic Systems TS-7260
  *
@@ -47,20 +47,41 @@
   its for us.
  */
 unsigned tsuartGetMUX8(struct tsuart_port *port,int offset) {
+  unsigned val = -1;
+  int src;
   /*
     if reading the STAT register, mask out the flow control bit since
     its really telling us whether data is for us or not.  use this bit
     to instead condition data ready, so we know whether or not data is
     for us.
    */
+  if (offset == 1) { // DATA
+    src = bit_CTS(*(volatile unsigned char *)port->u.membase);
+    if (src == port->u.line) {
+      return -1;
+    }
+    
+    val = *((volatile unsigned char *)port->u.membase + offset);
+  } else if (offset == 0) { // STAT
+    val = *((volatile unsigned char *)port->u.membase + offset);
+    
+    if (bit_CTS(val) == port->u.line) {
+      val = val & 0xFD;
+    }
+    
+    val = val | 0x08; // sorry, no constant, CTS wasn't writable
+  }
+  return val;
 }
 
 void tsuartPutMUX8(struct tsuart_port *port,int offset,unsigned value) {
+  *((volatile unsigned char *)port->u.membase + offset) = value;
 }
 
+struct uart_driver *drc=0,*drd=0;
 
 #define BOARD_ID_PAC4 0x7404
-static int tryInitPort(int base) {
+static int tryInitPort(int base,struct uart_driver *dr) {
   struct tsuart_port *ts;
 
   ts = kmalloc(sizeof(struct tsuart_port),GFP_KERNEL);
@@ -71,14 +92,22 @@
 
   init_tsuart_port(ts);
   ts->u.iotype = SERIAL_IO_MEM;
-  ts->get = tsuartGetMUX8;
-  ts->put = tsuartPutMUX8;
+  if (dr != 0) {
+    ts->get = tsuartGetMUX8;
+    ts->put = tsuartPutMUX8;
+  }  else {
+    ts->get = tsuartGetMEM8;
+    ts->put = tsuartPutMEM8;
+  }
   ts->debugFlags = portDebug;
   ts->u.mapbase = /*0 x72000000 + */ base;
   ts->u.irq = 32;
+  ts->mdmctrl = 0;
   ts->boardId = BOARD_ID_PAC4;
-
-  if (tsuart_register_port(0,ts) != 0) {
+  if (dr) {
+    //    ts->localStatus |= PORT_STATUS_SHIRQ;
+  }
+  if (tsuart_register_port(dr,ts) != 0) {
     return 0;
   }
   return 1;
@@ -87,7 +116,6 @@
 static int __init tsuart_init(void)
 {
   int ports=0;
-  int ret;
 	
   if (get_ts_model() != TS_MODEL_PAC4) {
     printk("TS-UART/PAC4 did not detect a TS-PAC4 board\n");
@@ -95,20 +123,48 @@
   } else {
     printk("TS-UART/PAC4 detected a TS-PAC4 board\n");
   }
-  ret = procfile_init();
-  if (ret) {
-    return ret;
+
+  drc = kmalloc(sizeof(struct uart_driver),GFP_KERNEL);
+  if (!drc) {
+    printk("Unable to allocate memory for tsuart port\n");
+    return 0;
+  }
+  drd = kmalloc(sizeof(struct uart_driver),GFP_KERNEL);
+  if (!drd) {
+    kfree(drc);
+    printk("Unable to allocate memory for tsuart port\n");
+    return 0;
+  }
+ if (!tsuart_configure_driver(drc,234,100,"ttyTSP","cuaTSC",2)) {
+    kfree(drc);
+    kfree(drd);
+    return 0;    
+  }
+ /*
+  if (!tsuart_configure_driver(drd,234,101,"ttyTSD","cuaTSD",1)) {
+    kfree(drc);
+    kfree(drd);
+    return 0;    
+  }
+ */
+  if (tryInitPort(0x12400000,0)) {
+    ports++;
   }
 
-  if (tryInitPort(0x12400000)) {
+  if (tryInitPort(0x12400002,drc)) {
     ports++;
   }
+
+  if (tryInitPort(0x12400002,drc)) {
+    ports++;
+  }
+
   return (ports > 0) ? 0 : -1;
 }
 
 static void __exit tsuart_exit(void)
 {
-  procfile_exit();  
+  // not safe to unload!
 }
 
 module_init(tsuart_init);
Index: drivers/serial/tsuart.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/serial/tsuart.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- drivers/serial/tsuart.c	24 Apr 2006 18:16:52 -0000	1.8
+++ drivers/serial/tsuart.c	19 Oct 2006 17:41:12 -0000	1.9
@@ -991,13 +991,8 @@
 
 static int __init tsuart_init(void)
 {
-	int ret;
+	int ret,i;
 	
-	ret = procfile_init();
-	if (ret) {
-	  return ret;
-	}
-
 	if (get_ts_model() != 2) {
 	    printk("tsuart not loading: ts7260 board not detected\n");
 	    procfile_exit();
@@ -1005,7 +1000,11 @@
 	}
 	ret = uart_register_driver(&tsuart_reg);
 	if (ret == 0) {
-		int i;
+		ret = procfile_init();
+		if (ret) {
+			uart_unregister_driver(&tsuart_reg);
+	  		return ret;
+		}
 
 		for (i=0;i<UART_NR;i++)
 			uart_add_one_port(&tsuart_reg,&tsuart_ports[i]);
Index: drivers/serial/tsuart.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/serial/tsuart.h,v
retrieving revision 1.3
retrieving revision 1.10
diff -u -r1.3 -r1.10
--- drivers/serial/tsuart.h	6 Jun 2006 00:13:29 -0000	1.3
+++ drivers/serial/tsuart.h	23 Apr 2008 20:38:38 -0000	1.10
@@ -4,6 +4,7 @@
 #define PORT_STATUS_PARODD 8
 #define PORT_STATUS_8PLUS1 16
 #define PORT_STATUS_SHIRQ 32
+#define PORT_STATUS_PARITY 128
 struct tsuart_port {
   struct uart_port u;
   struct tsuart_port *nextd; // next tsuart_port with different IRQ
@@ -20,6 +21,8 @@
   unsigned *parity;
   int dataMask,dataMask2;
   int boardId; // gives us a way to associate a port with a board
+  int (*mdmctrl)(struct tsuart_port *,int cmd);
+  unsigned int old_status;
 };
 #define TSPORT(port) ((struct tsuart_port *)port)
 
@@ -51,6 +54,10 @@
   return (reg_value & 0x08) == 0x08;
 }
 
+static inline unsigned bit_RXBRK(unsigned reg_value) {
+  return (reg_value & 0x0200) == 0x0200;
+}
+
 // accepts the value read from the TS UART STAT register
 // returns TRUE if the RTS bit is set, and FALSE if it is clear
 static inline int bit_RTS(unsigned char reg_value) {
@@ -64,6 +71,10 @@
   return (reg_value & ~0x10) | (set ? 0x10 : 0);
 }
 
+static inline unsigned assign_bit_TXBRK(unsigned reg_value,int set) {
+  return (reg_value & ~0x400) | (set ? 0x400 : 0);
+}
+
 static inline unsigned short assign_bit_MODE9(unsigned reg_value,int set)
 {
   return (reg_value & ~0x100) | (set ? 0x100 : 0);
@@ -94,8 +105,9 @@
 #define TS_MODEL_7250 1
 #define TS_MODEL_7260 2
 #define TS_MODEL_7300 3
+#define TS_MODEL_7400 4
 #define TS_MODEL_JNS200 6
-#define TS_MODEL_PAC4 4    // maybe not 4 - TO BE ANNOUNCED!!!
+#define TS_MODEL_PAC4 2  // really going to be 5 
 
 static inline int get_ts_model(void) {
   return (inw(TS7XXX_IO16_BASE + TS_MODEL_REG)) & 7;
@@ -127,5 +139,6 @@
   p->localStatus = 0;
   p->portStatus = 0;
   p->boardId = 0;
+  p->mdmctrl = 0;
 }
 #endif
Index: drivers/serial/tsuart0.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/serial/tsuart0.c,v
retrieving revision 1.5
retrieving revision 1.18
diff -u -r1.5 -r1.18
--- drivers/serial/tsuart0.c	22 Jun 2006 21:18:43 -0000	1.5
+++ drivers/serial/tsuart0.c	3 Mar 2009 16:23:32 -0000	1.18
@@ -76,6 +76,7 @@
 static void tsuart_stop_tx(struct uart_port *port, unsigned int tty_stop);
 static struct timer_list timer;
 static int timer_added = 0;
+static struct timer_list timer2 = { .function = NULL };
 static inline int bit_CTS(unsigned char reg_value);
 
 //-----------------------------
@@ -138,6 +139,8 @@
   *((volatile unsigned short *)port->u.membase + offset) = value;
 }
 
+static void tsuart_try_handleTx(struct tsuart_port *port);
+
 void tsuartPutMEM16_9(struct tsuart_port *port,int offset,unsigned value) {
   if (offset != 1) {
     *((volatile unsigned short *)port->u.membase + offset) = value;
@@ -145,10 +148,12 @@
     if ((value & 0xFE) == 0) {
       port->pstatus = (port->pstatus & 0xFFFF00FF) | ((value & 0xFF) << 8);
       port->pstatus &= ~PORT_STATUS_TXMSB;
-      uart_write_wakeup(&port->u);
     } else {
-      printk("***TX Rejected (sync)\n");
+      if (port->debugFlags & DEBUG_DATA) {      
+	printk("***TX Rejected (sync)\n");
+      }
     }
+    //tsuart_try_handleTx(port); // avoid stuckness
   } else {
     value += (port->pstatus & 0xFF00);
     *((volatile unsigned short *)port->u.membase + offset) = value;
@@ -173,6 +178,7 @@
 static void tsuart_handleRx(struct tsuart_port *port,int data) {
   struct tty_struct *tty;
   unsigned int ch = data;
+  int flag = 0;
 
   if (port->debugFlags & DEBUG_DATA) {
     unsigned int ttyinf = port->u.info ? (port->u.info->tty ? 3:1) : 0;
@@ -183,18 +189,35 @@
   }
   tty = port->u.info->tty;
   if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
-    tty_flip_buffer_push(tty); 
+    tty->flip.tqueue.routine((void *)tty);
+    //printk("->%X ",tty->flip.char_buf_ptr);
+    if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+      printk("TSUART0 - TTY FLIP BUF FAILED!\n");
+      return;
+    }
   }
+#ifdef TSUART_CORRECT_PARITY
+  if (port->localStatus & PORT_STATUS_PARITY) {
+    flag |= TTY_PARITY;
+  }
+#endif
   if (port->rxsize > 3) {
-    tty_insert_flip_char(tty, (ch >> 24) & 0xFF, 0);
+    tty_insert_flip_char(tty, (ch >> 24) & 0xFF, flag);
   }
   if (port->rxsize > 2) {
-    tty_insert_flip_char(tty, (ch >> 16) & 0xFF, 0);
+    tty_insert_flip_char(tty, (ch >> 16) & 0xFF, flag);
   }
   if (port->rxsize > 1) {
-    tty_insert_flip_char(tty, (ch >> 8) & 0xFF, 0);
+    tty_insert_flip_char(tty, (ch >> 8) & 0xFF, flag);
   }
-  tty_insert_flip_char(tty, (ch & 0xFF), 0);
+  if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+    tty->flip.tqueue.routine((void *)tty);
+    if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+      printk("TSUART0 - TTY FLIP BUF FAILED!\n");
+      return;
+    }
+  }
+  tty_insert_flip_char(tty, (ch & 0xFF), flag);
   port->gotBytes++;
   return;
 }
@@ -212,7 +235,7 @@
 // then feed to the byte to the UART
 static void tsuart_try_handleTx(struct tsuart_port *port) {
   struct circ_buf *xmit;
-  unsigned int ch=0;
+  unsigned int ch=0,i=0;
 
   if (!port->u.info || !port->u.info->tty) {
     if (port->debugFlags & DEBUG_DATA) {
@@ -237,20 +260,29 @@
     }
   }
   xmit = &port->u.info->xmit;
-  if (uart_circ_empty(xmit) || uart_tx_stopped(&(port->u))) {
-    tsuart_stop_tx(&port->u,0);
+  if (uart_circ_empty(xmit)) { // || uart_tx_stopped(&(port->u))) {
     return;
   }
-  // handle one character
-  ch = xmit->buf[xmit->tail];
-  if (port->debugFlags & DEBUG_DATA) {
-    unsigned int ttyinf = port->u.info ? (port->u.info->tty ? 3:1) : 0;
-    printk("tsuart Tx(%d):%02X:%d\n",port->u.line,ch,ttyinf);
-  }
-  tsuart_parity_Tx(port,ch);
-  xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
-  port->u.icount.tx++;
-  // done handling one character
+
+  i = 0;
+  //printk("%d:%d %p\n",xmit->tail,xmit->head,&i);
+  do {
+    // handle one character
+    ch = xmit->buf[xmit->tail];
+    if (port->debugFlags & DEBUG_DATA) {
+      unsigned int ttyinf = port->u.info ? (port->u.info->tty ? 3:1) : 0;
+      printk("tsuart Tx(%d):%02X:%d\n",port->u.line,ch,ttyinf);
+    }
+    xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+    port->u.icount.tx++;
+    tsuart_parity_Tx(port,ch);
+    // done handling one character
+    i++;
+    if (uart_circ_empty(xmit)) { // || uart_tx_stopped(&(port->u))) {
+      break;
+    }
+  } while (bit_TBRE(tsuartGetSTAT(port)) && i < 64);
+  //printk("%d %X\n",i,tsuartGetSTAT(port));
 
   if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) {
     uart_write_wakeup(&port->u);
@@ -263,7 +295,6 @@
   }
 }
 
-
 //----------------------------
 // Begin FPGA dependant code <
 //----------------------------
@@ -328,14 +359,20 @@
     data = rxdata & port->dataMask2;
     //    printk("%X (%X)\n",data,port->parity[data]);
   } else {
-    data = rxdata & port->dataMask2;
+    data = rxdata;
   }
 
   if (!port->parity || port->parity[data] == rxdata) {
+    port->localStatus &= ~PORT_STATUS_PARITY;
     return data;
   } else {
     port->u.icount.parity++;
+    port->localStatus |= ~PORT_STATUS_PARITY;
+#ifdef TSUART_CORRECT_PARITY
+    return data;
+#else
     return -1;
+#endif
   }
 }
 
@@ -352,7 +389,6 @@
       }
       port->data = tsuart_parity_Rx(port);
     }
-  } else {
   }
 
 }
@@ -470,7 +506,9 @@
   if (TSPORT(port)->debugFlags & DEBUG_INTERFACE) {  
     printk("tsuart_start_tx(%d,%d)\n",PORTNUM(port),tty_start);
   }
+  cli();
   tsuart_checkHandleTx(TSPORT(port));
+  sti();
 }
 
 static void tsuart_stop_rx(struct uart_port *port)
@@ -498,10 +536,15 @@
 
 static unsigned int tsuart_get_mctrl(struct uart_port *port)
 {
+  struct tsuart_port *p = TSPORT(port);
   unsigned int result = 0;
 
-  result |= TIOCM_CAR;  // pretend DCD is always set
-  result |= TIOCM_DSR;  // pretend DSR is always set
+  if (p->mdmctrl) {
+    result |= p->mdmctrl(p,0);
+  } else {
+    result |= TIOCM_CAR;  // pretend DCD is always set
+    result |= TIOCM_DSR;  // pretend DSR is always set
+  }
   if (bit_CTS(tsuartGetSTAT(TSPORT(port)))) {
     result |= TIOCM_CTS;
   }
@@ -520,19 +563,59 @@
     printk("tsuart_set_mctrl(%d,RTS=%d)\n",PORTNUM(port),((mctrl&TIOCM_RTS) != 0) ? 1:0);
   }
   tsuartPutSTAT(p,assign_bit_RTS(tsuartGetSTAT(p),(mctrl&TIOCM_RTS)!=0));
-  // No support for TIOCM_DTR, so ignore it
+  if (p->mdmctrl) {
+    p->mdmctrl(p,0x80000000 | mctrl);
+  } else {
+    // No support for TIOCM_DTR, so ignore it
+  }
 }
 
 static void tsuart_break_ctl(struct uart_port *port, int break_state)
 {
-  // UART does not support BREAK
+  // some UARTs do not support BREAK
+  if (break_state == -1) break_state = 1;
+  tsuartPutSTAT((struct tsuart_port *)port,assign_bit_TXBRK(tsuartGetSTAT((struct tsuart_port *)port),break_state));
   if (TSPORT(port)->debugFlags & DEBUG_INTERFACE) {
-    printk("tsuart_break_ctl(%d,%d)\n",PORTNUM(port),break_state);
+    printk("--- tsuart_break_ctl(%d,%d)\n",PORTNUM(port),break_state);
   }
 }
 
 static int started = 0;
 
+static void tsuart_mdm_timer(unsigned long data) {
+  struct tsuart_port *root = TS,*next;
+  unsigned int status, delta;
+
+  //printk("tsuart_mdm_timer()\n");
+  while (root) {
+    next = root;
+    while (next) {
+      status = tsuart_get_mctrl(&next->u);
+      delta = status ^ next->old_status;
+      next->old_status = status;
+      if (delta) {
+	if (delta & TIOCM_CAR) {
+	  //printk("tsuart_mdm_timer: DCD\n");
+	  //uart_handle_dcd_change(&next->u, status & TIOCM_CAR);
+	}
+	if (delta & TIOCM_DSR) {
+	  //printk("tsuart_mdm_timer: DSR\n");
+	  next->u.icount.dsr++;
+	}
+	if (delta & TIOCM_CTS) {
+	  //printk("tsuart_mdm_timer: CTS\n");
+	  //uart_handle_dcd_change(&next->u, status & TIOCM_CTS);
+	}
+	//wake_up_interruptible(&next->u.info->delta_msr_wait);
+      }
+      next = next->next;
+    }
+    root = root->nextd;
+  }
+  timer2.expires = jiffies + HZ;
+  add_timer (&timer2);
+}
+
 static void tsuart_cts_timer(unsigned long data) {
   //  struct uart_port *port = (struct uart_port *)(tsuart_ports);
   //  struct uart_port *port = (struct uart_port *)data;
@@ -574,6 +657,7 @@
   TSPORT(port)->localStatus |= PORT_STATUS_OPEN;
 
   setMODE9(port,TSPORT(port)->rxsize > 1 || (TSPORT(port)->localStatus & PORT_STATUS_8PLUS1));
+  TSPORT(port)->old_status = tsuart_get_mctrl(port);
   if (started++) { // not needed?
     return 0;
   }
@@ -582,6 +666,17 @@
   timer.data = (unsigned long )port;
   timer.function = tsuart_cts_timer;
 
+  if (timer2.function == NULL) {
+    if (TSPORT(port)->debugFlags & DEBUG_INTERRUPT) {
+      printk("Starting MDM timer\n");
+    }
+    timer.expires = jiffies + HZ;
+    timer2.function = tsuart_mdm_timer;
+    add_timer(&timer2);
+  } else {
+    printk("MDM timer can't start!\n");
+  }
+
 #if 0
   for (i=0;i<256;i++) {
     stats[i] = 0;
@@ -615,6 +710,10 @@
     del_timer (&timer);
     timer_added = 0;
   }
+  if (timer2.function) {
+    del_timer(&timer2);
+    timer2.function = NULL;
+  }
 
 #if 0
   for (i=0;i<6;i++) {
@@ -623,6 +722,8 @@
 #endif
 
   tsuartPutSTAT(p,assign_bits_BAUD(tsuartGetSTAT(p),0));
+  port->info->xmit.tail = port->info->xmit.head; // avoid stuckness
+
 }
 
 static int calcpar(int val,int parodd) {
@@ -769,7 +870,9 @@
   */
   TSPORT(port)->dataMask2 = (1 << (dbits)) - 1;
   // TO DO: only allow this on an 8-bit port!
-  if (dbits == 8 && par) {
+  if (TSPORT(port)->rxsize > 1) {
+    TSPORT(port)->dataMask2 = 0x1FF;
+  } else if (dbits == 8 && par) {
     TSPORT(port)->localStatus |= PORT_STATUS_8PLUS1;
     TSPORT(port)->dataMask = 0x1FF;
   } else {
@@ -781,7 +884,11 @@
   if (cflag & CSTOPB) {
     printk("tsuart:unsupported stop bits, using 1\n");
   }
-  initpar(TSPORT(port),dbits,par,parodd);  
+  if (TSPORT(port)->rxsize < 2) {
+    initpar(TSPORT(port),dbits,par,parodd);  
+  }
+  // clear the receive FIFO
+  while (bit_DR(tsuartGetSTAT(p))) tsuartGetDATA(p);
 }
 
 static const char *tsuart_type(struct uart_port *port)
@@ -814,13 +921,16 @@
     if (port->membase == 0) {
       port->membase = (void *) __ioremap(port->mapbase & 0xFFFFF000, 4096,0)
 	+ (port->mapbase & 0xFFF);
-      //      printk("port %p:mapbase=%X, membase=%X\n",port,port->mapbase,port->membase);
-      if (!port->membase) {
+      //printk("port %p:mapbase=%X, membase=%X\n",port,port->mapbase,port->membase);
+      if (port->membase == (void *)(port->mapbase & 0xFFF)) {
+	port->membase = 0;
 	printk(KERN_ERR "tsuart: cannot map io memory\n");
 	return -ENOMEM;
       }
     }
   }
+  // clear the receive FIFO
+  while (bit_DR(tsuartGetSTAT(TSPORT(port)))) tsuartGetDATA(TSPORT(port));
   return 0;
 }
 
@@ -850,6 +960,9 @@
     ret = -EINVAL;
   if (ser->irq < 0 || ser->irq >= NR_IRQS)
     ret = -EINVAL;
+  if (ser->baud_base < 9600) {
+    ser->baud_base = 115200; // required to allow ioctl to set low_latency
+  }
   //  if (ser->baud_base != 115200)
   //    ret = -EINVAL;
   if (TSPORT(port)->debugFlags & DEBUG_INTERFACE) {
@@ -1180,6 +1293,12 @@
 
     if (!tsuart_sanity_check_STAT(port)) {
       printk("WARNING: port %d failed STAT sanity check!\n",port->u.line);
+      // Note: This will fail in a number of cases which are fatal:
+      // 1. If the FPGA bitstream isn't loaded 
+      // 2. We are loaded on hardware that doesn't have a TS-UART present (e.g. wrong arch)
+      // It will also fail in a number of cases which are benign:
+      // 1. Loopback is installed on the flow control lines (e.g. RTS/CTS)
+      // 2. We are a specialized TS-UART that doesn't have all the bits in STAT defined
     }
   }
   return ret;
Index: drivers/serial/tsuart7260.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/serial/tsuart7260.c,v
retrieving revision 1.4
retrieving revision 1.6
diff -u -r1.4 -r1.6
--- drivers/serial/tsuart7260.c	15 Jun 2006 18:51:10 -0000	1.4
+++ drivers/serial/tsuart7260.c	29 Sep 2008 16:15:47 -0000	1.6
@@ -151,6 +151,7 @@
   ts->u.iotype = SERIAL_IO_MEM;
   ts->get = tsuartGetMEM8;
   ts->put = tsuartPutMEM8;
+  ts->mdmctrl = 0;
   ts->debugFlags = portDebug;
   ts->u.mapbase = /*0 x72000000 + */ base;
   ts->u.irq = 32;
@@ -179,6 +180,7 @@
     return ret;
   }
 
+  cli(); // bug fix for second uart always being on at startup
   if (tryInitPort(0x12400000)) {
     ports++;
   }
@@ -190,6 +192,7 @@
 	    ports++;
 	  }
   }
+  sti();
   return (ports > 0) ? 0 : -1;
 }
 
Index: drivers/serial/tsuart940.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/serial/tsuart940.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- drivers/serial/tsuart940.c	15 Jun 2006 18:51:10 -0000	1.3
+++ drivers/serial/tsuart940.c	5 Sep 2006 22:44:31 -0000	1.4
@@ -59,6 +59,7 @@
   ts->debugFlags = portDebug;
   ts->u.iobase = TS7XXX_IO8_BASE + TS7XXX_ISAIO + iobase;
   ts->u.irq = 33;
+  ts->mdmctrl = 0;
   ts->localStatus |= PORT_STATUS_SHIRQ;
   ts->boardId = BOARD_ID_940;
 
@@ -68,6 +69,7 @@
   ts2->debugFlags = portDebug;
   ts2->u.iobase = TS7XXX_IO8_BASE + TS7XXX_ISAIO + iobase;
   ts2->u.irq = 33;
+  ts2->mdmctrl = 0;
   ts2->localStatus |= PORT_STATUS_SHIRQ;
   ts2->boardId = BOARD_ID_940;
 
Index: drivers/sound/Config.in
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/sound/Config.in,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- drivers/sound/Config.in	4 Aug 2004 01:29:44 -0000	1.2
+++ drivers/sound/Config.in	20 Feb 2007 23:55:10 -0000	1.3
@@ -168,11 +168,13 @@
     if [ "$CONFIG_ARCH_EP9301" = "y" ]; then
         choice '  Cirrus I2S Codec Support' \
         "CS4271 CONFIG_CODEC_CS4271 \
-         CS4228A  CONFIG_CODEC_CS4228A" CS4271
+         CS4228A  CONFIG_CODEC_CS4228A \
+	 CS42L50 CONFIG_CODEC_CS42L50" CS4271
     else
         choice '  Cirrus I2S Codec Support' \
         "CS4271 CONFIG_CODEC_CS4271 \
-         CS4228A  CONFIG_CODEC_CS4228A" CS4228A
+         CS4228A  CONFIG_CODEC_CS4228A \
+	 CS42L50 CONFIG_CODEC_CS42L50" CS4228A
     fi
    fi 
    
Index: drivers/sound/ep93xx-audio.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/sound/ep93xx-audio.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- drivers/sound/ep93xx-audio.c	22 Jul 2004 19:50:19 -0000	1.1.1.1
+++ drivers/sound/ep93xx-audio.c	29 Sep 2006 22:29:35 -0000	1.2
@@ -1,2412 +1,2701 @@
-/*
- * ep93xx-audio.h
- *
- * Common audio handling for the Cirrus EP93xx processor.
- *
- * Copyright (c) 2003 Cirrus Logic Corp.
- * Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License.
- *
- * Taken from sa1100-audio.c
- *
- * This module handles the generic buffering/DMA/mmap audio interface for
- * codecs connected to the EP93xx chip.  All features depending on specific
- * hardware implementations like supported audio formats or samplerates are
- * relegated to separate specific modules.
- *
- *
- * History:
- *
- * 2000-05-21	Nicolas Pitre	Initial release.
- *
- * 2000-06-10	Erik Bunce	Add initial poll support.
- *
- * 2000-08-22	Nicolas Pitre	Removed all DMA stuff. Now using the
- * 				generic SA1100 DMA interface.
- *
- * 2000-11-30	Nicolas Pitre	- Validation of opened instances;
- * 				- Power handling at open/release time instead
- * 				  of driver load/unload;
- *
- * 2001-06-03	Nicolas Pitre	Made this file a separate module, based on
- * 				the former sa1100-uda1341.c driver.
- *
- * 2001-07-22	Nicolas Pitre	- added mmap() and realtime support
- * 				- corrected many details to better comply
- * 				  with the OSS API
- *
- * 2001-10-19	Nicolas Pitre	- brought DMA registration processing
- * 				  into this module for better ressource
- * 				  management.  This also fixes a bug
- * 				  with the suspend/resume logic.
- *
- * 2003-04-04   Adapted for EP93xx I2S/Ac97 audio.
- *
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/poll.h>
-#include <linux/pm.h>
-#include <linux/errno.h>
-#include <linux/sound.h>
-#include <linux/soundcard.h>
-#include <linux/sysrq.h>
-#include <linux/delay.h>
-
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/hardware.h>
-#include <asm/semaphore.h>
-#include <asm/arch/dma.h>
-
-#include "ep93xx-audio.h"
-
-#undef DEBUG 
-// #define DEBUG 1
-#ifdef DEBUG
-#define DPRINTK( x... )  printk( ##x )
-#else
-#define DPRINTK( x... )
-#endif
-
-/* Mostly just prints what ioctls got called */
-/* #define DEBUG 1 */
-#ifdef DEBUG
-#define DPRINTK_IOCTL( x... )  printk( ##x )
-#else
-#define DPRINTK_IOCTL( x... )
-#endif
-
-
-#define AUDIO_NAME		"ep93xx-audio"
-
-#define AUDIO_NBFRAGS_DEFAULT	8
-#define AUDIO_FRAGSIZE_DEFAULT	16384
-
-/*
- * Translates to:
- * 		s->buf_idx++;
- * 		s->buf_idx %= s->nbfrags;
- *		s->buf = s->buffers + s->buf_idx;
- *
- * So s->buf always points to the s->buf_idx-nth element of s->buffers.
- */
-#define NEXT_BUF(_s_,_b_) { \
-	(_s_)->_b_##_idx++; \
-	(_s_)->_b_##_idx %= (_s_)->nbfrags; \
-	(_s_)->_b_ = (_s_)->buffers + (_s_)->_b_##_idx; }
-
-#define AUDIO_ACTIVE(state)	((state)->rd_ref || (state)->wr_ref)
-
-/* Function prototypes */
-static int copy_n_convert
-(
-	audio_state_t *state,
-	const char *to, 
-	const char *from, 
-	int toCount 
-);
-static int scale_to_expanded_samples( audio_state_t *state, int input_bufsize );
-static void audio_dma_start( audio_state_t * state, audio_stream_t * stream );
-static void audio_dma_pause( audio_state_t * state, audio_stream_t * stream );
-static void audio_prime_dma( audio_state_t * state, audio_stream_t * s );
-
-/*
- * We samples of arbitrary format to be 32 bit for our hardware.
- *	Input format is arbitrary.
- *	Output format is either CM (compact mode) where two 16 bit samples
- *  together in a 32 bit word become a stereo sample or non-CM where
- *  each channel gets a 32 bit word.
- *
- *  Input format       Input sample     Output sample size    ratio (out:in)
- *  bits   channels    size (bytes)       CM   non-CM          CM   non-CM
- *   8       mono          1               4      8            4:1   8:1
- *   8      stereo         2			   4      8            2:1   4:1
- *   16      mono          2			   4      8            2:1   4:1
- *   16     stereo         4			   4      8            1:1   2:1
- *
- *   24      mono          3			   4      8             X    8:3
- *   24     stereo         6			   4      8             X    8:6
- *   32      mono          4			   4      8             X    2:1
- *   32     stereo         8			   4      8             X    1:1
- */
-static int scale_to_expanded_samples( audio_state_t *state, int input_bufsize )
-{
-	int output_bufsize = input_bufsize;
-
-#ifdef I2S_SCALE_BUFFERS
-	audio_stream_t *os = state->output_stream;
-	int output_sample_size, input_sample_size;
-	
-	if( state->bCompactMode )
-	{
-		output_sample_size = 4;
-	}
-	else
-	{
-		output_sample_size = 8;
-	}
-	
-	input_sample_size = os->audio_stream_bitwidth / 8;
-	
-	if( os->audio_num_channels != 1 )
-	{
-		input_sample_size <<= 1;
-	}
-
-	output_bufsize = input_bufsize * output_sample_size / input_sample_size;
-
-#endif
-	
-	return output_bufsize;
-}
-
-/*
- * audio_deallocate_buffers
- * 
- * This function frees all buffers
- */
-static void audio_deallocate_buffers( audio_state_t * state, audio_stream_t * s )
-{
-	int frag, i;
-	
-	DPRINTK("EP93xx - audio_deallocate_buffers\n");
-
-	/* ensure DMA won't run anymore */
-	audio_dma_pause( state, s );
-	s->active = 0;
-	s->stopped = 0;
-
-	for( i=0 ; i<s->NumDmaChannels ; i++ )
-	{
-		ep93xx_dma_flush( s->dmahandles[0] );
-	}
-	
-	if (s->buffers) 
-	{
-		for (frag = 0; frag < s->nbfrags; frag++) 
-		{
-			if (!s->buffers[frag].master)
-			{
-				continue;
-			}
-
-			consistent_free(s->buffers[frag].start,
-					s->buffers[frag].master,
-					s->buffers[frag].dma_addr);
-		}
-
-		/*
-		 * Free the space allocated to the array of buf structs.
-		 */
-		kfree(s->buffers);
-		s->buffers = NULL;
-	}
-
-	s->buf_idx = 0;
-	s->buf = NULL;
-
-	DPRINTK("EP93xx - audio_deallocate_buffers - EXIT\n");
-}
-
-
-/*
- * audio_allocate_buffers
- *
- * This function allocates the buffer structure array and buffer data space
- * according to the current number of fragments and fragment size.
- * Note that the output_stream and input_stream structs are allocated
- * in ep93xx-ac97.c or ep93xx-i2s.c.
- */
-static int audio_allocate_buffers( audio_state_t * state, audio_stream_t * s)
-{
-	int frag;
-	int dmasize = 0;
-	char *dmabuf = NULL;
-	dma_addr_t dmaphys = 0;
-
-	if (s->buffers)
-		return -EBUSY;
-
-	DPRINTK("EP93xx audio_allocate_buffers\n" );
-	
-	/*
-	 * Allocate space for the array of audio_buf_t structs.
-	 */
-	s->buffers = (audio_buf_t *)
-		kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL);
-
-	if (!s->buffers)
-	{
-		DPRINTK(AUDIO_NAME ": unable to allocate audio memory\n ");
-		audio_deallocate_buffers(state,s);
-		return -ENOMEM;
-	}
-
-	memset( s->buffers, 0, sizeof(audio_buf_t) * s->nbfrags );
-
-	/*
-	 * Let's allocate non-cached memory for DMA buffers.
-	 * We try to allocate all memory at once.
-	 * If this fails (a common reason is memory fragmentation),
-	 * then we allocate more smaller buffers.
-	 */
-	for (frag = 0; frag < s->nbfrags; frag++) 
-	{
-		audio_buf_t *buf = &s->buffers[frag];
-
-		if (!dmasize) 
-		{
-			/*
-			 * First try to allocate enough for all the frags that
-			 * don't yet have memory allocated.
-			 */
-			dmasize = (s->nbfrags - frag) * s->fragsize;
-			do {
-			  	dmabuf = consistent_alloc(GFP_KERNEL|GFP_DMA,
-							  dmasize, &dmaphys);
-				
-				/* 
-				 * If that fails, try to allocate a chunk of memory
-				 * that is one less fragment is size.
-				 */
-				if (!dmabuf)
-				{
-					dmasize -= s->fragsize;
-				}
-
-			  /* 
-			   * Keep trying but the minimum we'll attempt is one 
-			   * fragment.  If we can't even get that, give up.
-			   */
-			} while (!dmabuf && dmasize);
-		
-			/*
-			 * If we do fail to allocate enough for all the frags, 
-			 * deallocate whatever we did get and quit.
-			 */
-			if (!dmabuf)
-			{
-				DPRINTK(AUDIO_NAME ": unable to allocate audio memory\n ");
-				audio_deallocate_buffers(state, s);
-				return -ENOMEM;
-			}
-
-			DPRINTK("EP93xx allocated %d bytes:  dmabuf=0x%08x  dmaphys=0x%08x\n",
-					dmasize, (int)dmabuf, (int)dmaphys );
-			
-			/*
-			 * Success!	 Save the size of this chunk to use when we deallocate it.
-			 */
-			buf->master = dmasize;
-			memzero(dmabuf, dmasize);
-		}
-
-		/*
-		 * Save the start address of the buf fragment.
-		 * We know the size of the fragment is fragsize.
-		 */
-		buf->start = dmabuf;
-		buf->dma_addr = dmaphys;
-		buf->stream = s;
-		buf->size = 0;
-		buf->sent = 0;
-
-		sema_init(&buf->sem, 1);
-
-		/*
-		 * Now if we only allocated the minimal one frag of space, the
-		 * dmasize will be ==0 after this subtraction so it will allocate more
-		 * for the next frag.  Otherwise, the next time(s) thru this for loop
-		 * will dole out frag sized pieces of this big master chunk.
-		 */
-		dmabuf += s->fragsize;
-		dmaphys += s->fragsize;
-		dmasize -= s->fragsize;
-	}
-
-	/*
-	 * Initialize the stream.
-	 */
-	s->buf_idx = 0;				/* Init the current buffer index. 			*/
-	s->buf = &s->buffers[0];	/* Point buf to the current buffer struct. 	*/
-	s->bytecount = 0;
-	s->getptrCount = 0;
-	s->fragcount = 0;
-
-	DPRINTK("EP93xx audio_allocate_buffers -- exit SUCCESS\n" );
-	return 0;
-
-}
-
-
-/*
- * audio_reset_buffers
- *
- * This function stops and flushes the dma, gets all buffers back
- * from the DMA driver and resets them ready to be used again.
- */
-static void audio_reset_buffers( audio_state_t * state, audio_stream_t * s )
-{
-	int frag, i;
-
-	audio_dma_pause( state, s );
-	s->active = 0;
-	s->stopped = 0;
-
-	for( i=0 ; i < s->NumDmaChannels ; i++ )
-	{
-		ep93xx_dma_flush( s->dmahandles[0] );
-	}
-
-	if (s->buffers) 
-	{
-		for (frag = 0; frag < s->nbfrags; frag++) 
-		{
-			audio_buf_t *buf = &s->buffers[frag];
-			buf->size = 0;
-			buf->sent = 0;
-			sema_init(&buf->sem, 1);
-		}
-	}
-
-	s->bytecount = 0;
-	s->getptrCount = 0;
-	s->fragcount = 0;
-}
-
-
-/*
- * DMA callback functions
- */
-static void audio_dma_tx_callback
-( 
-	ep93xx_dma_int_t DMAInt,
-	ep93xx_dma_dev_t device, 
-	unsigned int user_data 
-)
-{
-	unsigned int buf_id;
-	int handle;
-	
-	audio_state_t *state = (audio_state_t *)user_data;
-	audio_stream_t *s = state->output_stream;
-	
- /*	DPRINTK( "audio_dma_tx_callback  - enter\n"); */
-	
-	switch( device )
-	{
-    	case DMATx_I2S3:
-			handle = s->dmahandles[2];
-			break;
-
-    	case DMATx_I2S2:
-			handle = s->dmahandles[1];
-			break;
-
-		case DMATx_I2S1:
-		default:
-			handle = s->dmahandles[0];
-			break;
-	}
-
-  	if (s->mapped)
-	{
-		/*
-		 * If we are mapped, get one buffer back and recycle it.
-		 */
-		if( ep93xx_dma_remove_buffer( handle, &buf_id ) >= 0 )
-		{
-		   	audio_buf_t *buf = (audio_buf_t *)buf_id;
-
-		 /*	DPRINTK( "audio_dma_tx_callback  - got buffer index=%d\n", buf_id); */
-		
-			/* Accounting */
-			s->bytecount += buf->size;
-			s->fragcount++;
-			buf->size = 0;
-		
-			/* Recycle buffer */
-		 	buf->size = s->fragsize;
-			
-			ep93xx_dma_add_buffer( s->dmahandles[0], 	/* dma instance 		*/
-								   (unsigned int)buf->dma_addr, /* source 		*/
-								   0, 					/* dest 				*/
-								   s->fragsize,			/* size					*/
-								   0,					/* is the last chunk? 	*/
-								   (unsigned int) buf ); /* buf id			 	*/	
-	  		
-			buf->sent = 1;
-		}
-	}
-	else
-	{
-		/*
-		 * Get all buffers that are free'ed back and clear their semephores.
-		 */
-		while( ep93xx_dma_remove_buffer( handle, &buf_id ) >= 0 )
-		{
-		   	audio_buf_t *buf = (audio_buf_t *)buf_id;
-
-		 /*	DPRINTK( "audio_dma_tx_callback  - got buffer index=%d\n", buf_id); */
-		
-			/* Accounting */
-			s->bytecount += buf->size;
-			s->fragcount++;
-			buf->size = 0;
-			buf->sent = 0;
-			
-			/*
-			 * Release the semaphore on this buf.
-			 * If write is waiting on this buf then it can go ahead and fill
-			 * it and send it to the dma.
-			 */
-			up(&buf->sem);
-		}
-	}
-
-	/* And any process polling on write. */
-	wake_up(&s->wq);
-}
-
-static void audio_dma_rx_callback
-( 
-	ep93xx_dma_int_t DMAInt,
-	ep93xx_dma_dev_t device, 
-	unsigned int user_data 
-)
-{
-	unsigned int buf_id;
-	int handle;
-	
-	audio_state_t *state = (audio_state_t *)user_data;
-	audio_stream_t *s = state->input_stream;
-
-	switch( device )
-	{
-    	case DMARx_I2S3:
-			handle = s->dmahandles[2];
-			break;
-
-    	case DMARx_I2S2:
-			handle = s->dmahandles[1];
-			break;
-
-		case DMARx_I2S1:
-		default:
-			handle = s->dmahandles[0];
-			break;
-	}
-	
-	/*
-	 * Keep removing and recycling buffers as long as there are buffers
-	 * to remove.
-	 */
-	while( !ep93xx_dma_remove_buffer( handle, &buf_id ) )
-	{
-	   	audio_buf_t *buf = (audio_buf_t *) buf_id;
-
-		/* Accounting */
-		s->bytecount += buf->size;
-		s->fragcount++;
-
-		/* Recycle buffer */
-		if( s->mapped ) 
-		{
-		 	ep93xx_dma_add_buffer( s->dmahandles[0], 	/* dma instance 		*/
-								   (unsigned int)buf->dma_addr, /* source 		*/
-								   0, 					/* dest 				*/
-								   s->fragsize,			/* size					*/
-								   0,					/* is the last chunk? 	*/
-								   (unsigned int) buf ); /* buf id			 	*/	
-		} 
-		else 
-		{
-			buf->size = s->fragsize;
-			up(&buf->sem);
-		}
-
-		/* And any process polling on write. */
-		wake_up(&s->wq);
-	}
-}
-
-/*
- * audio_sync
- *
- * Wait until the last byte written to this device has been played.
- */
-static int audio_sync(struct file *file)
-{
-	audio_state_t *state = (audio_state_t *)file->private_data;
-	audio_stream_t *s = state->output_stream;
-	audio_buf_t *buf;
-/*	int frag; */
-	int buf_wait_index;
-  /*	audio_buf_t *buftemp; */
-
-	DPRINTK( "EP93xx - audio_sync - enter\n" );
-
-	if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped)
-	{
-		DPRINTK( "EP93xx - audio_sync - exit immediately.\n" );
-		return 0;
-	}
-	
-	/*
-	 * Send current buffer if it contains data and hasn't been sent. 
-	 */
-#if 0	 
-	for (frag = 0; frag < s->nbfrags; frag++) 
-	{
-		buftemp = &s->buffers[frag];
-		DPRINTK("audio_sync - buf# %d: size=%d  sent=%d\n",
-			frag, buftemp->size, buftemp->sent );
-	}
-#endif
-
-	buf = s->buf;
-	if( buf->size && !buf->sent ) 
-	{
-		DPRINTK("audio_sync -- SENDING BUFFER index=%d size=%d\n",
-				s->buf_idx, buf->size );
-		
-		down(&buf->sem);
-	 	ep93xx_dma_add_buffer( s->dmahandles[0], 	/* dma instance 		*/
-							   (unsigned int)buf->dma_addr, /* source 		*/
-							   0, 					/* dest 				*/
-							   buf->size,			/* size					*/
-							   0,					/* is the last chunk? 	*/
-							   (unsigned int) buf ); /* buf id			 	*/	
-	 	buf->sent = 1;
-		NEXT_BUF(s, buf);
-	}
-
-	/*
-	 * Let's wait for the last buffer we sent i.e. the one before the
-	 * current buf_idx.  When we acquire the semaphore, this means either:
-	 * - DMA on the buffer completed or
-	 * - the buffer was already free thus nothing else to sync.
-	 */
-	buf_wait_index = ((s->nbfrags + s->buf_idx - 1) % s->nbfrags);
-	buf = s->buffers + buf_wait_index;
-
-#if 0
-	while( buf->sent )
-	{
-		mdelay( 1000 );
-		for (frag = 0; frag < s->nbfrags; frag++) 
-		{
-			buftemp = &s->buffers[frag];
-			DPRINTK("audio_sync - buf# %d: size=%d  sent=%d\n",
-				frag, buftemp->size, buftemp->sent );
-		}
-		DPRINTK("\n");
-	}
-	DPRINTK("audio_sync -- current buf index is %d.  Waiting on buffer index=%d\n",
-			s->buf_idx, buf_wait_index );
-#endif
-
-	if (down_interruptible(&buf->sem))
-	{
-		return -EINTR;
-	}
-	
-	up(&buf->sem);
-	DPRINTK("EP93xx - audio_sync - EXIT\n");
-	return 0;
-}
-
-//static int iWriteCount = 0;
-
+/*
+ * ep93xx-audio.c
+ *
+ * Common audio handling for the Cirrus EP93xx processor.
+ *
+ * Copyright (c) 2003 Cirrus Logic Corp.
+ * Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License.
+ *
+ * Taken from sa1100-audio.c
+ *
+ * This module handles the generic buffering/DMA/mmap audio interface for
+ * codecs connected to the EP93xx chip.  All features depending on specific
+ * hardware implementations like supported audio formats or samplerates are
+ * relegated to separate specific modules.
+ *
+ *
+ * History:
+ *
+ * 2000-05-21	Nicolas Pitre	Initial release.
+ *
+ * 2000-06-10	Erik Bunce	Add initial poll support.
+ *
+ * 2000-08-22	Nicolas Pitre	Removed all DMA stuff. Now using the
+ * 				generic SA1100 DMA interface.
+ *
+ * 2000-11-30	Nicolas Pitre	- Validation of opened instances;
+ * 				- Power handling at open/release time instead
+ * 				  of driver load/unload;
+ *
+ * 2001-06-03	Nicolas Pitre	Made this file a separate module, based on
+ * 				the former sa1100-uda1341.c driver.
+ *
+ * 2001-07-22	Nicolas Pitre	- added mmap() and realtime support
+ * 				- corrected many details to better comply
+ * 				  with the OSS API
+ *
+ * 2001-10-19	Nicolas Pitre	- brought DMA registration processing
+ * 				  into this module for better ressource
+ * 				  management.  This also fixes a bug
+ * 				  with the suspend/resume logic.
+ *
+ * 2003-04-04   Adapted for EP93xx I2S/Ac97 audio.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/pm.h>
+#include <linux/errno.h>
+#include <linux/sound.h>
+#include <linux/soundcard.h>
+#include <linux/sysrq.h>
+#include <linux/delay.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/semaphore.h>
+#include <asm/arch/dma.h>
+
+#include "ep93xx-audio.h"
+
+#undef DEBUG 
+// #define DEBUG 1
+#ifdef DEBUG
+#define DPRINTK( fmt, arg... )  printk( fmt, ##arg )
+#else
+#define DPRINTK( fmt, arg... )
+#endif
+
+/* Mostly just prints what ioctls got called */
+/* #define DEBUG 1 */
+#ifdef DEBUG
+#define DPRINTK_IOCTL( fmt, arg... )  printk( fmt, ##arg )
+#else
+#define DPRINTK_IOCTL( fmt, arg... )
+#endif
+
+#define AUDIO_NAME		"ep93xx-audio"
+
+#define AUDIO_NBFRAGS_DEFAULT	8
+#define AUDIO_FRAGSIZE_DEFAULT	16384
+
+/*
+ * NEXT_BUF
+ *
+ * Translates to:
+ * 		stream->dma_buffer_index++;
+ * 		stream->dma_buffer_index %= stream->nbfrags;
+ *		stream->dma_buffer = stream->buffers + stream->dma_buffer_index;
+ *
+ * So stream->dma_buffer always points to the stream->dma_buffer_index-nth element 
+ * of stream->buffers.
+ */
+#define NEXT_BUF(_s_,_b_) { \
+	(_s_)->_b_##_index++; \
+	(_s_)->_b_##_index %= (_s_)->nbfrags; \
+	(_s_)->_b_ = (_s_)->buffers + (_s_)->_b_##_index; }
+
+#define AUDIO_ACTIVE(state)	((state)->rd_ref || (state)->wr_ref)
+
+/* Function prototypes */
+static int copy_n_convert
+(
+	audio_state_t *state,
+	const char *to, 
+	const char *from, 
+	int toCount 
+);
+static int scale_to_expanded_samples( audio_state_t *state, int input_bufsize );
+static void audio_dma_start( audio_state_t * state, audio_stream_t * stream );
+static void audio_dma_pause( audio_state_t * state, audio_stream_t * stream );
+static void audio_prime_dma( audio_state_t * state, audio_stream_t * s );
+
+static unsigned long copy_to_user_S16_LE(audio_state_t *state,const char *to, const char *from, unsigned long to_count);
+
+/*
+ * We samples of arbitrary format to be 32 bit for our hardware.
+ *	Input format is arbitrary.
+ *	Output format is either CM (compact mode) where two 16 bit samples
+ *  together in a 32 bit word become a stereo sample or non-CM where
+ *  each channel gets a 32 bit word.
+ *
+ *  Input format       Input sample     Output sample size    ratio (out:in)
+ *  bits   channels    size (bytes)       CM   non-CM          CM   non-CM
+ *   8       mono          1               4      8            4:1   8:1
+ *   8      stereo         2			   4      8            2:1   4:1
+ *   16      mono          2			   4      8            2:1   4:1
+ *   16     stereo         4			   4      8            1:1   2:1
+ *
+ *   24      mono          3			   4      8             X    8:3
+ *   24     stereo         6			   4      8             X    8:6
+ *   32      mono          4			   4      8             X    2:1
+ *   32     stereo         8			   4      8             X    1:1
+ */
+static int scale_to_expanded_samples( audio_state_t *state, int input_bufsize )
+{
+	int output_bufsize = input_bufsize;
+
+#ifdef I2S_SCALE_BUFFERS
+	audio_stream_t *os = state->output_stream;
+	int output_sample_size, input_sample_size;
+	
+	if( state->bCompactMode )
+	{
+		output_sample_size = 4;
+	}
+	else
+	{
+		output_sample_size = 8;
+	}
+	
+	input_sample_size = os->audio_stream_bitwidth / 8;
+	
+	if( os->audio_num_channels != 1 )
+	{
+		input_sample_size <<= 1;
+	}
+
+	output_bufsize = input_bufsize * output_sample_size / input_sample_size;
+
+#endif
+	
+	return output_bufsize;
+}
+
+/*
+ *  calculate_dma2usr_ratio_etc()
+ *
+ *  For audio playback, we convert samples of arbitrary format to be 32 bit 
+ *  for our hardware. We're scaling a user buffer to a dma buffer.  So when
+ *  report byte counts, we scale them acording to the ratio of DMA sample
+ *  size to user buffer sample size.  When we report # of DMA fragments,
+ *  we don't scale that.  So:
+ *   - The fragment size the app sees  = (stream->fragsize/stream->dma2usr_ratio)
+ *   - The # of fragments the app sees = stream->nbfrags
+ *
+ *	User sample type can be stereo/mono/8/16/32 bit.  
+ *  DMA sample type will be either CM (compact mode) where two 16 bit 
+ *  samples together in a 32 bit word become a stereo sample or non-CM 
+ *  where each channel gets a 32 bit word.
+ *
+ *  Any time usr sample type changes, we need to call this function.
+ *
+ *  Also adjust the size and number of dma fragments if sample size changed.
+ *
+ *  Input format       Input sample     Output sample size    ratio (out:in)
+ *  bits   channels    size (bytes)       CM   non-CM          CM   non-CM
+ *   8       mono          1               4      8            4:1   8:1
+ *   8      stereo         2			   4      8            2:1   4:1
+ *   16      mono          2			   4      8            2:1   4:1
+ *   16     stereo         4			   4      8            1:1   2:1
+ *
+ *   24      mono          3			   4      8             X    8:3 not a real case
+ *   24     stereo         6			   4      8             X    8:6 not a real case
+ *   32      mono          4			   4      8             X    2:1
+ *   32     stereo         8			   4      8             X    1:1
+ *
+ */
+static void calculate_dma2usr_ratio_etc( audio_stream_t * stream )
+{
+	unsigned int dma_sample_size, user_sample_size;
+	
+	if( stream->bCompactMode )
+		dma_sample_size = 4;	/* each stereo sample is 2 * 16 bits */
+	else
+		dma_sample_size = 8;	/* each stereo sample is 2 * 32 bits */
+	
+	if( stream->audio_num_channels != 1 )
+	{
+		// If stereo 16 bit, user sample is 4 bytes.
+		// If stereo  8 bit, user sample is 2 bytes.
+		user_sample_size = stream->audio_stream_bitwidth / 4;
+	}
+	else
+	{
+		// If mono 16 bit, user sample is 2 bytes.
+		// If mono  8 bit, user sample is 1 bytes.
+		user_sample_size = stream->audio_stream_bitwidth / 8;
+	}
+
+	// dma2usr_ratio = (4 or 8) / (4, 2, or 1) = 8, 4, 2, or 1
+	stream->dma2usr_ratio = dma_sample_size / user_sample_size;
+
+	DPRINTK(" samplesize: dma %d  user %d  dma/usr ratio  %d\n",
+		dma_sample_size, user_sample_size, stream->dma2usr_ratio );
+	DPRINTK(" requested: fragsize %d  num frags %d  total bytes %d  total samples %d\n", 
+		stream->requested_fragsize, stream->requested_nbfrags,
+		stream->requested_fragsize * stream->requested_nbfrags,
+		(stream->requested_fragsize * stream->requested_nbfrags) / user_sample_size );
+	DPRINTK(" usr      : fragsize %d  num frags %d  total bytes %d  total samples %d\n", 
+		(stream->fragsize / stream->dma2usr_ratio),  stream->nbfrags,
+		stream->fragsize * stream->nbfrags / stream->dma2usr_ratio,
+		(stream->fragsize * stream->nbfrags) / (stream->dma2usr_ratio * user_sample_size) );
+	DPRINTK(" dma:       fragsize %d  num frags %d  total bytes %d  total samples %d\n", 
+		stream->fragsize,  stream->nbfrags,
+		stream->fragsize * stream->nbfrags,
+		(stream->fragsize * stream->nbfrags) / dma_sample_size );
+		
+}
+
+/*
+ * audio_deallocate_buffers
+ * 
+ * This function frees all buffers
+ */
+static void audio_deallocate_buffers( audio_state_t * state, audio_stream_t * s )
+{
+	int frag, i;
+	
+	DPRINTK("EP93xx - audio_deallocate_buffers\n");
+
+	// ensure DMA won't run anymore 
+	audio_dma_pause( state, s );
+	s->active = 0;
+	s->stopped = 0;
+
+	for( i=0 ; i<s->NumDmaChannels ; i++ )
+	{
+		ep93xx_dma_flush( s->dmahandles[0] );
+	}
+	
+	if (s->buffers) 
+	{
+		for (frag = 0; frag < s->nbfrags; frag++) 
+		{
+			if (!s->buffers[frag].master)
+			{
+				continue;
+			}
+
+			consistent_free(s->buffers[frag].start,
+					s->buffers[frag].master,
+					s->buffers[frag].dma_addr);
+		}
+
+		// Free the space allocated to the array of buf structs.
+		kfree(s->buffers);
+		s->buffers = NULL;
+	}
+
+	s->dma_buffer_index = 0;
+	s->dma_buffer = NULL;
+
+	DPRINTK("EP93xx - audio_deallocate_buffers - EXIT\n");
+}
+
+/*
+ * audio_allocate_buffers
+ *
+ * This function allocates the buffer structure array and buffer data space
+ * according to the current number of fragments and fragment size.
+ * Note that the output_stream and input_stream structs are allocated
+ * in ep93xx-ac97.c or ep93xx-i2s.c.
+ */
+static int audio_allocate_buffers( audio_state_t * state, audio_stream_t * s)
+{
+	int frag;
+	int dmasize = 0;
+	char *dmabuf = NULL;
+	dma_addr_t dmaphys = 0;
+
+	if (s->buffers)
+		return -EBUSY;
+
+	DPRINTK("EP93xx audio_allocate_buffers\n" );
+	
+	/*
+	 * Allocate space for the array of audio_buf_t structs.
+	 */
+	s->buffers = (audio_buf_t *)
+		kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL);
+
+	if (!s->buffers)
+	{
+		DPRINTK(AUDIO_NAME ": unable to allocate audio memory\n ");
+		audio_deallocate_buffers(state,s);
+		return -ENOMEM;
+	}
+
+	memset( s->buffers, 0, sizeof(audio_buf_t) * s->nbfrags );
+
+	/*
+	 * Let's allocate non-cached memory for DMA buffers.
+	 * We try to allocate all memory at once.
+	 * If this fails (a common reason is memory fragmentation),
+	 * then we allocate more smaller buffers.
+	 */
+	for (frag = 0; frag < s->nbfrags; frag++) 
+	{
+		audio_buf_t *buf = &s->buffers[frag];
+
+		if (!dmasize) 
+		{
+			/*
+			 * First try to allocate enough for all the frags that
+			 * don't yet have memory allocated.
+			 */
+			dmasize = (s->nbfrags - frag) * s->fragsize;
+			do {
+			  	dmabuf = consistent_alloc(GFP_KERNEL|GFP_DMA,
+							  dmasize, &dmaphys);
+				
+				/* 
+				 * If that fails, try to allocate a chunk of memory
+				 * that is one less fragment is size.
+				 */
+				if (!dmabuf)
+				{
+					dmasize -= s->fragsize;
+				}
+
+			  /* 
+			   * Keep trying but the minimum we'll attempt is one 
+			   * fragment.  If we can't even get that, give up.
+			   */
+			} while (!dmabuf && dmasize);
+		
+			/*
+			 * If we do fail to allocate enough for all the frags, 
+			 * deallocate whatever we did get and quit.
+			 */
+			if (!dmabuf)
+			{
+				DPRINTK(AUDIO_NAME ": unable to allocate audio memory\n ");
+				audio_deallocate_buffers(state, s);
+				return -ENOMEM;
+			}
+
+			DPRINTK("EP93xx allocated %d bytes:  dmabuf=0x%08x  dmaphys=0x%08x\n",
+					dmasize, (int)dmabuf, (int)dmaphys );
+			
+			/*
+			 * Success!	 Save the size of this chunk to use when we deallocate it.
+			 */
+			buf->master = dmasize;
+			memzero(dmabuf, dmasize);
+		}
+
+		/*
+		 * Save the start address of the buf fragment.
+		 * We know the size of the fragment is fragsize.
+		 */
+		buf->start = dmabuf;
+		buf->dma_addr = dmaphys;
+		buf->stream = s;
+		buf->size = 0;
+		buf->sent = 0;
+
+		sema_init(&buf->sem, 1);
+
+		/*
+		 * Now if we only allocated the minimal one frag of space, the
+		 * dmasize will be ==0 after this subtraction so it will allocate more
+		 * for the next frag.  Otherwise, the next time(s) thru this for loop
+		 * will dole out frag sized pieces of this big master chunk.
+		 */
+		dmabuf += s->fragsize;
+		dmaphys += s->fragsize;
+		dmasize -= s->fragsize;
+	}
+
+	/*
+	 * Initialize the stream.
+	 */
+	s->dma_buffer_index = 0;				/* Init the current buffer index. 			*/
+	s->dma_buffer = &s->buffers[0];	/* Point buf to the current buffer struct. 	*/
+	s->bytecount = 0;
+	s->getptrCount = 0;
+	s->fragcount = 0;
+
+	DPRINTK("EP93xx audio_allocate_buffers -- exit SUCCESS\n" );
+	return 0;
+
+}
+
+
+/*
+ * audio_reset_buffers
+ *
+ * This function stops and flushes the dma, gets all buffers back
+ * from the DMA driver and resets them ready to be used again.
+ */
+static void audio_reset_buffers( audio_state_t * state, audio_stream_t * s )
+{
+	int frag, i;
+
+	audio_dma_pause( state, s );
+	s->active = 0;
+	s->stopped = 0;
+
+	for( i=0 ; i < s->NumDmaChannels ; i++ )
+	{
+		ep93xx_dma_flush( s->dmahandles[0] );
+	}
+
+	if (s->buffers) 
+	{
+		for (frag = 0; frag < s->nbfrags; frag++) 
+		{
+			audio_buf_t *buf = &s->buffers[frag];
+			buf->size = 0;
+			buf->sent = 0;
+			sema_init(&buf->sem, 1);
+		}
+	}
+
+	s->bytecount = 0;
+	s->getptrCount = 0;
+	s->fragcount = 0;
+}
+
+
+/*
+ * DMA callback functions
+ */
+static void audio_dma_tx_callback
+( 
+	ep93xx_dma_int_t DMAInt,
+	ep93xx_dma_dev_t device, 
+	unsigned int user_data 
+)
+{
+	unsigned int buf_id;
+	int handle;
+	
+	audio_state_t *state = (audio_state_t *)user_data;
+	audio_stream_t *s = state->output_stream;
+	
+ /*	DPRINTK( "audio_dma_tx_callback  - enter\n"); */
+	
+	switch( device )
+	{
+    	case DMATx_I2S3:
+			handle = s->dmahandles[2];
+			break;
+
+    	case DMATx_I2S2:
+			handle = s->dmahandles[1];
+			break;
+
+		case DMATx_I2S1:
+		default:
+			handle = s->dmahandles[0];
+			break;
+	}
+
+  	if (s->mapped)
+	{
+		/*
+		 * If we are mapped, get one buffer back and recycle it.
+		 */
+		if( ep93xx_dma_remove_buffer( handle, &buf_id ) >= 0 )
+		{
+		   	audio_buf_t *buf = (audio_buf_t *)buf_id;
+
+		 /*	DPRINTK( "audio_dma_tx_callback  - got buffer index=%d\n", buf_id); */
+		
+			/* Accounting */
+			s->bytecount += buf->size;
+			s->fragcount++;
+			buf->size = 0;
+		
+			/* Recycle buffer */
+		 	buf->size = s->fragsize;
+			
+			ep93xx_dma_add_buffer( s->dmahandles[0], 	/* dma instance 		*/
+								   (unsigned int)buf->dma_addr, /* source 		*/
+								   0, 					/* dest 				*/
+								   s->fragsize,			/* size					*/
+								   0,					/* is the last chunk? 	*/
+								   (unsigned int) buf ); /* buf id			 	*/	
+	  		
+			buf->sent = 1;
+		}
+	}
+	else
+	{
+		/*
+		 * Get all buffers that are free'ed back and clear their semephores.
+		 */
+		while( ep93xx_dma_remove_buffer( handle, &buf_id ) >= 0 )
+		{
+		   	audio_buf_t *buf = (audio_buf_t *)buf_id;
+
+		 /*	DPRINTK( "audio_dma_tx_callback  - got buffer index=%d\n", buf_id); */
+		
+			/* Accounting */
+			s->bytecount += buf->size;
+			s->fragcount++;
+			buf->size = 0;
+			buf->sent = 0;
+			
+			/*
+			 * Release the semaphore on this buf.
+			 * If write is waiting on this buf then it can go ahead and fill
+			 * it and send it to the dma.
+			 */
+			up(&buf->sem);
+		}
+	}
+
+	/* And any process polling on write. */
+	wake_up(&s->wq);
+}
+
+/*static void audio_dma_rx_callback (ep93xx_dma_int_t DMAInt,ep93xx_dma_dev_t device, unsigned int user_data )
+{
+	unsigned int buf_id;
+	int handle;
+	
+	audio_state_t *state = (audio_state_t *)user_data;
+	audio_stream_t *s = state->input_stream;
+
+	DPRINTK("audio_dma_rx_callback called\n");
+
+	switch( device )
+	{
+    	case DMARx_I2S3:
+			handle = s->dmahandles[2];
+			break;
+
+    	case DMARx_I2S2:
+			handle = s->dmahandles[1];
+			break;
+
+	case DMARx_I2S1:
+		default:
+			handle = s->dmahandles[0];
+			break;
+	}
+	
+	// Keep removing and recycling buffers as long as there are buffers
+	// to remove.
+	while( !ep93xx_dma_remove_buffer( handle, &buf_id ) )
+	{
+	   	audio_buf_t *buf = (audio_buf_t *) buf_id;
+
+		// Accounting 
+		s->bytecount += buf->size;
+		s->fragcount++;
+
+		// Recycle buffer
+		if( s->mapped ) 
+		{
+		 	ep93xx_dma_add_buffer( s->dmahandles[0], 		// dma instance
+					       (unsigned int)buf->dma_addr, 	// source
+						0, 				// dest 
+						s->fragsize,			// size
+						0,				// is the last chunk? 
+						(unsigned int) buf ); 		// buf id		
+		} 
+		else 
+		{
+			buf->size = s->fragsize;
+			up(&buf->sem);
+		}
+
+		// And any process polling on write. 
+		wake_up(&s->wq);
+	}
+} */
+
+static void audio_dma_rx_callback(ep93xx_dma_int_t DMAInt, ep93xx_dma_dev_t device, unsigned int user_data)
+{
+	unsigned int buf_id;
+	int handle;
+	//int i;	
+
+	audio_state_t * state = (audio_state_t *)user_data;
+	audio_stream_t * stream = state->input_stream;
+
+	//DPRINTK("audio_dma_rx_callback\n");
+	
+	// Get the DMA handle that corresponds to the dma channel
+	// that needs servicing.  A multichannel audio stream will
+	// have a DMA handle for each stereo pair that it uses.
+	//for( i=0 ; ( i < stream->NumDmaChannels ) && ( stream->dmachannel[i] != device ) ; i++ );
+	//handle = stream->dmahandles[i];
+	handle = stream->dmahandles[0];
+
+	
+	// Keep removing and recycling buffers as long as there are buffers to remove.
+//	while( !DMA_REMOVE_BUFFER( handle, &buf_id ) )
+	while( !ep93xx_dma_remove_buffer( handle, &buf_id ) )
+	{
+	   	audio_buf_t *dma_buffer = (audio_buf_t *) buf_id;
+
+		// Accounting 
+		stream->bytecount += stream->fragsize;
+		if (stream->bytecount < 0)
+			stream->bytecount = 0;
+
+		stream->fragcount++;
+		if (stream->fragcount < 0)
+			stream->fragcount = 0;
+
+		// Recycle dma buffer 
+		if( stream->mapped ) 
+		{
+		 	//DMA_ADD_BUFFER( handle,			 		// dma instance 
+			//		(unsigned int) dma_buffer->dma_addr, 	// source 
+			//		0, 					// dest
+			//		stream->fragsize,			// size	
+			//		0,					// is the last chunk?
+			//		(unsigned int) dma_buffer ); 		// buf id	
+		 	ep93xx_dma_add_buffer( handle,			 	// dma instance 
+					(unsigned int) dma_buffer->dma_addr, 	// source 
+					0, 					// dest
+					stream->fragsize,			// size	
+					0,					// is the last chunk?
+					(unsigned int) dma_buffer ); 		// buf id	
+		} 
+		else 
+		{
+			dma_buffer->size = stream->fragsize;
+			//dma_buffer->owner = 0;
+			up(&dma_buffer->sem);
+		}
+
+		// And any process polling on write.
+		wake_up(&stream->wq);
+	}
+}
+
+
+
+/*
+ * audio_sync
+ *
+ * Wait until the last byte written to this device has been played.
+ */
+static int audio_sync(struct file *file)
+{
+	audio_state_t *state = (audio_state_t *)file->private_data;
+	audio_stream_t *s = state->output_stream;
+	audio_buf_t *buf;
+/*	int frag; */
+	int buf_wait_index;
+  /*	audio_buf_t *buftemp; */
+
+	DPRINTK( "EP93xx - audio_sync - enter\n" );
+
+	if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped)
+	{
+		DPRINTK( "EP93xx - audio_sync - exit immediately.\n" );
+		return 0;
+	}
+	
+	/*
+	 * Send current buffer if it contains data and hasn't been sent. 
+	 */
+#if 0	 
+	for (frag = 0; frag < s->nbfrags; frag++) 
+	{
+		buftemp = &s->buffers[frag];
+		DPRINTK("audio_sync - buf# %d: size=%d  sent=%d\n",
+			frag, buftemp->size, buftemp->sent );
+	}
+#endif
+
+	buf = s->dma_buffer;
+	if( buf->size && !buf->sent ) 
+	{
+		DPRINTK("audio_sync -- SENDING BUFFER index=%d size=%d\n",
+				s->dma_buffer_index, buf->size );
+		
+		down(&buf->sem);
+	 	ep93xx_dma_add_buffer( s->dmahandles[0], 	/* dma instance 		*/
+							   (unsigned int)buf->dma_addr, /* source 		*/
+							   0, 					/* dest 				*/
+							   buf->size,			/* size					*/
+							   0,					/* is the last chunk? 	*/
+							   (unsigned int) buf ); /* buf id			 	*/	
+	 	buf->sent = 1;
+		NEXT_BUF(s, dma_buffer);
+	}
+
+	/*
+	 * Let's wait for the last buffer we sent i.e. the one before the
+	 * current dma_buffer_index.  When we acquire the semaphore, this means either:
+	 * - DMA on the buffer completed or
+	 * - the buffer was already free thus nothing else to sync.
+	 */
+	buf_wait_index = ((s->nbfrags + s->dma_buffer_index - 1) % s->nbfrags);
+	buf = s->buffers + buf_wait_index;
+
+#if 0
+	while( buf->sent )
+	{
+		mdelay( 1000 );
+		for (frag = 0; frag < s->nbfrags; frag++) 
+		{
+			buftemp = &s->buffers[frag];
+			DPRINTK("audio_sync - buf# %d: size=%d  sent=%d\n",
+				frag, buftemp->size, buftemp->sent );
+		}
+		DPRINTK("\n");
+	}
+	DPRINTK("audio_sync -- current buf index is %d.  Waiting on buffer index=%d\n",
+			s->dma_buffer_index, buf_wait_index );
+#endif
+
+	if (down_interruptible(&buf->sem))
+	{
+		return -EINTR;
+	}
+	
+	up(&buf->sem);
+	DPRINTK("EP93xx - audio_sync - EXIT\n");
+	return 0;
+}
+
+//static int iWriteCount = 0;
+
 /*
  * Need to convert to 32 bit stereo format:	  
  * 16 bit signed
  * 16 bit unsigned
  *  8 bit signed
  *  8 bit unsigned
- */
-static int audio_write
-(
-	struct file 	*file, 
-	const char 		*buffer,
-	size_t 			src_count, 
-	loff_t 			*ppos
-)
-{
-	const char *buffer0 = buffer;
-	audio_state_t *state = (audio_state_t *)file->private_data;
-	audio_stream_t *s = state->output_stream;
-	int dma_xfer_count, src_xfer_count, expanded_count, ret = 0;
-//	int i,j;
-	
- 	/* DPRINTK_IOCTL( "EP93xx - audio_write: count=%d\n", src_count ); */
-
-	if (ppos != &file->f_pos)
-		return -ESPIPE;
-		
-	if (s->mapped)
-		return -ENXIO;
-		
-	if( !access_ok( VERIFY_READ, buffer, src_count ) )
-	{
-		DPRINTK( "access_ok failed!!!!\n" );
-		return -EFAULT;
-	}
-
-	/*
-	 * Allocate dma buffers if we haven't already done so.
-	 */
-	if( !s->buffers && audio_allocate_buffers( state, s) )
-	{
-		return -ENOMEM;
-	}
-
-//	if( iWriteCount > 40 )
-//	{
-//		for( i=0; i<4 ; i++ )
-//		{
-//			for( j=0; j<8 ; j++ )
-//			{
-//				printk("%02x ", buffer[(i*8)+j] );
-//			}
-//			printk("\n");
-//		}
-//		iWriteCount = 0;
-//	}
-//	iWriteCount++;
-	
-	/*
-	 * Stay in this loop until we have copied all of the file
-	 * into user memory.
-	 */
-	while( src_count > 0 ) 
-	{
-		audio_buf_t *buf = s->buf;
-
-		/* Wait for a buffer to become free */
-		if (file->f_flags & O_NONBLOCK)
-		{
-			ret = -EAGAIN;
-			if (down_trylock(&buf->sem))
-				break;
-		} 
-		else 
-		{
-			ret = -ERESTARTSYS;
-			if (down_interruptible(&buf->sem))
-				break;
-		}
-
-		/* 
-		 * Feed the current buffer (s->buf) 
-		 * This involves expanding sample size from the source buffer
-		 * to be 32 bit stereo for our dma.
-		 */
-		
-		/*
-		 * How much space is left in the current buf?
-		 *
-		 * dma_xfer_count is # of bytes placed into the dma buffer
-		 * where each sample is 4 bytes (4 bytes left, 4 bytes right)
-		 */
-		dma_xfer_count = s->fragsize - buf->size;
-		
-		/*
-		 * Input buffer is src_count (bytes) of whatever format.
-		 * How big will src_count be when sample size is expanded
-		 * to 32 bit samples for our hardware?
-		 */
-		expanded_count = scale_to_expanded_samples( state, src_count );
-		
-		/*
-		 * See if we can fit all the remaining input buffer in
-		 * the current buffer...
-		 */
-		if (dma_xfer_count > expanded_count)
-		{
-			dma_xfer_count = expanded_count;
-		}
-		
-		//DPRINTK( "EP93xx - audio_write: %d to buf # %d\n", dma_xfer_count, s->buf_idx );
-		
-		src_xfer_count = copy_n_convert( 	state, 
-											buf->start + buf->size, 
-											buffer, 
-											dma_xfer_count);
-		if( src_xfer_count <= 0 ) 
-		{
-			up(&buf->sem);
-			return -EFAULT;
-		}
-
-		/*
-		 * Increment dma buffer pointer.
-		 */
-		buf->size += dma_xfer_count;
-
-		/* 
-		 * Increment source buffer pointer.
-		 * Decrement the input buffer size count.
-		 */
-		buffer += src_xfer_count;
-		src_count  -= src_xfer_count;
-
-		/*
-		 * If the chunk is less than a whole fragment, don't
-		 * send it, wait for more ??? I say, go ahead and send it.
-		 */
-#if 0
-		if (buf->size < s->fragsize) 
-		{
-			up(&buf->sem);
-			break;
-		}	 
-#endif
-
-		/*
-		 * If we haven't already started the DMA start it.
-		 * But don't start it if we are waiting on a trigger.
-		 */
-		if( !s->active && !s->stopped )
-		{
-			s->active = 1;
-			audio_dma_start( state, s );
-		}
-		
-		/* Send current buffer to dma */
-	 	ep93xx_dma_add_buffer( s->dmahandles[0], 	/* dma instance 		*/
-							   (unsigned int)buf->dma_addr, /* source 		*/
-							   0, 					/* dest 				*/
-							   buf->size,			/* size					*/
-							   0,					/* is the last chunk? 	*/
-							   (unsigned int) buf ); /* buf id			 	*/	
-		
-		/* 
-		 * Indicate that the buffer has been sent
-	 	 * used to set buf->size = 0; but we can't use that anymore
-		 */
-		buf->sent = 1;
-		
-		/*
-		 *
-		 */
-		NEXT_BUF(s, buf);
-	}
-	
-	/*
-	 * Return the number of bytes transferred.
-	 */
-	if( buffer - buffer0 )
-	{
-		ret = buffer - buffer0;
-	}
-	
-/*	DPRINTK( "EP93xx - audio_write: return=%d\n", ret );*/
-	return ret;
-}
-
-
-/*
- * audio_dma_start
- *
- * Our Ac97 has a specific start order that it likes.  Enable the 
- * Ac97 channel AFTER enabling DMA.  Our I2S is not so picky.
- */
-void audio_dma_start( audio_state_t * state, audio_stream_t * stream )
-{
-	ep93xx_dma_start( stream->dmahandles[0],
-					  stream->NumDmaChannels, 
-					  stream->dmahandles );
-	
-	if (state->hw_enable)
-	{
-		state->hw_enable( state->output_stream == stream );
-	}
-}
-
-/*
- * audio_dma_pause
- */
-void audio_dma_pause( audio_state_t * state, audio_stream_t * stream )
-{
-	if (state->hw_disable)
-	{
-		state->hw_disable( state->output_stream == stream );
-	}
-	
-	ep93xx_dma_pause( stream->dmahandles[0],
-					  stream->NumDmaChannels, 
-					  stream->dmahandles );
-}
-
-static void audio_prime_dma( audio_state_t * state, audio_stream_t * s )
-{
-	int i;
-
-	/*
-	 * If we haven't already started the DMA start it.
-	 * But don't start it if we are waiting on a trigger.
-	 */
-	if( !s->active && !s->stopped )
-	{
-		s->active = 1;
-		audio_dma_start( state, s );
-	}
-
-	for (i = 0; i < s->nbfrags; i++) 
-	{
-		audio_buf_t *buf = s->buf;
-		down(&buf->sem);
-	 	ep93xx_dma_add_buffer( s->dmahandles[0], 	/* dma instance 		*/
-							   (unsigned int)buf->dma_addr, /* source 		*/
-							   0, 					/* dest 				*/
-							   s->fragsize,			/* size					*/
-							   0,					/* is the last chunk? 	*/
-							   (unsigned int) buf ); /* buf id			 	*/	
-		NEXT_BUF(s, buf);
-	}
-}
-
-
-static int audio_read(struct file *file, char *buffer,
-		      size_t count, loff_t * ppos)
-{
-	char *buffer0 = buffer;
-	audio_state_t *state = (audio_state_t *)file->private_data;
-	audio_stream_t *s = state->input_stream;
-	int dest_xfer_count, ret = 0;
-
-	DPRINTK("EP93xx - audio_read: count=%d\n", count);
-
-	if (ppos != &file->f_pos)
-		return -ESPIPE;
-	if (s->mapped)
-		return -ENXIO;
-
-	if (!s->active) 
-	{
-		if (!s->buffers && audio_allocate_buffers( state, s))
-		{
-			return -ENOMEM;
-		}
-		audio_prime_dma( state, s);
-	}
-
-	while (count > 0) 
-	{
-		audio_buf_t *buf = s->buf;
-
-		/* Wait for a buffer to become full */
-		if (file->f_flags & O_NONBLOCK) 
-		{
-			ret = -EAGAIN;
-			if (down_trylock(&buf->sem))
-				break;
-		} 
-		else 
-		{
-			ret = -ERESTARTSYS;
-			if (down_interruptible(&buf->sem))
-				break;
-		}
-
-		/* Grab data from the current buffer */
-		dest_xfer_count = buf->size;
-		if (dest_xfer_count > count)
-		{
-			dest_xfer_count = count;
-		}
-			
-		DPRINTK("EP93xx - read %d from %d\n", dest_xfer_count, s->buf_idx);
-		
-		if (copy_to_user(buffer,
-				 buf->start + s->fragsize - buf->size,
-				 dest_xfer_count)) 
-		{
-			up(&buf->sem);
-			return -EFAULT;
-		}
-		
-		buf->size -= dest_xfer_count;
-		buffer += dest_xfer_count;
-		count -= dest_xfer_count;
-		
-		if (buf->size > 0) 
-		{
-			up(&buf->sem);
-			break;
-		}
-
-		/* Make current buffer available for DMA again */
-	 	ep93xx_dma_add_buffer( s->dmahandles[0], 	/* dma instance 		*/
-							   (unsigned int)buf->dma_addr, /* source 		*/
-							   0, 					/* dest 				*/
-							   s->fragsize,			/* size					*/
-							   0,					/* is the last chunk? 	*/
-							   (unsigned int) buf ); /* buf id			 	*/	
-
-		NEXT_BUF(s, buf);
-	}
-
-	if( buffer - buffer0 )
-	{
-		ret = buffer - buffer0;
-	}
-	
-	DPRINTK("EP93xx - audio_read: return=%d\n", ret);
-	return ret;
-}
-
-
-static int audio_mmap(struct file *file, struct vm_area_struct *vma)
-{
-	audio_state_t *state = (audio_state_t *)file->private_data;
-	audio_stream_t *s;
-	unsigned long size, vma_addr;
-	int i, ret;
-
-	if (vma->vm_pgoff != 0)
-	{
-		return -EINVAL;
-	}
-
-	if (vma->vm_flags & VM_WRITE) 
-	{
-		if (!state->wr_ref)
-		{
-			return -EINVAL;
-		}
-		s = state->output_stream;
-	}
-	else if (vma->vm_flags & VM_READ) 
-	{
-		if (!state->rd_ref)
-		{
-			return -EINVAL;
-		}
-		s = state->input_stream;
-	} 
-	else 
-	{
-		return -EINVAL;
-	}
-
-	if (s->mapped)
-	{
-		return -EINVAL;
-	}
-
-	size = vma->vm_end - vma->vm_start;
-	
-	if (size != s->fragsize * s->nbfrags)
-	{
-		return -EINVAL;
-	}
-	
-	if (!s->buffers && audio_allocate_buffers( state, s))
-	{
-		return -ENOMEM;
-	}
-
-	vma_addr = vma->vm_start;
-
-	for (i = 0; i < s->nbfrags; i++) 
-	{
-		audio_buf_t *buf = &s->buffers[i];
-		
-		if (!buf->master)
-		{
-			continue;
-		}
-
-		ret = remap_page_range(vma_addr, buf->dma_addr, buf->master, vma->vm_page_prot);
-
-		if (ret)
-		{
-			return ret;
-		}
-
-		vma_addr += buf->master;
-	}
-
-	s->mapped = 1;
-
-	return 0;
-}
-
-
-static unsigned int audio_poll(struct file *file,
-			       struct poll_table_struct *wait)
-{
-	audio_state_t *state = (audio_state_t *)file->private_data;
-	audio_stream_t *is = state->input_stream;
-	audio_stream_t *os = state->output_stream;
-	unsigned int mask = 0;
-	int i;
-
-	DPRINTK("EP93xx - audio_poll(): mode=%s%s\n",
-		(file->f_mode & FMODE_READ) ? "r" : "",
-		(file->f_mode & FMODE_WRITE) ? "w" : "");
-
-	if (file->f_mode & FMODE_READ) 
-	{
-		/* Start audio input if not already active */
-		if (!is->active) 
-		{
-			if (!is->buffers && audio_allocate_buffers( state, is))
-			{
-				return -ENOMEM;
-			}
-
-			audio_prime_dma( state, is);
-		}
-
-		poll_wait(file, &is->wq, wait);
-	}
-
-	if (file->f_mode & FMODE_WRITE) 
-	{
-		if (!os->buffers && audio_allocate_buffers( state, os))
-		{
-			return -ENOMEM;
-		}
-
-		poll_wait(file, &os->wq, wait);
-	}
-
-	if (file->f_mode & FMODE_READ) 
-	{
-		if (is->mapped) 
-		{
-			/* 
-			 * if the buffer is mapped assume we care that there are 
-			 * more bytes available than when we last asked using 
-			 * SNDCTL_DSP_GETxPTR 
-			 */
-			if (is->bytecount != is->getptrCount)
-			{
-				mask |= POLLIN | POLLRDNORM;
-			}
-		} 
-		else 
-		{
-			for (i = 0; i < is->nbfrags; i++) 
-			{
-				if (atomic_read(&is->buffers[i].sem.count) > 0) 
-				{
-					mask |= POLLIN | POLLRDNORM;
-					break;
-				}
-			}
-		}
-	}
-	
-	if (file->f_mode & FMODE_WRITE) 
-	{
-		if (os->mapped) 
-		{
-			if (os->bytecount != os->getptrCount)
-				mask |= POLLOUT | POLLWRNORM;
-		} 
-		else 
-		{
-			for (i = 0; i < os->nbfrags; i++) 
-			{
-				if (atomic_read(&os->buffers[i].sem.count) > 0) 
-				{
-					mask |= POLLOUT | POLLWRNORM;
-					break;
-				}
-			}
-		}
-	}
-
-	DPRINTK("EP93xx - audio_poll() returned mask of %s%s\n",
-		(mask & POLLIN) ? "r" : "",
-		(mask & POLLOUT) ? "w" : "");
-
-	return mask;
-}
-
-/*
- * audio_set_fragments
- *
- * Used to process SNDCTL_DSP_SETFRAGMENT.
- *
- * Argument is 0xMMMMSSSS where:
- * 		SSSS sets dma fragment (buf) size.  size = 2^SSSS bytes
- *		MMMM sets number of fragments.
- */
-static int audio_set_fragments( audio_state_t *state, audio_stream_t *s, int val )
-{
-	if (s->active)
-	{
-		return -EBUSY;
-	}
-	
-	/*
-	 * We're gonna change the buffers, so free the ones we currently have
-	 */
-	if (s->buffers)
-	{
-		audio_deallocate_buffers(state, s);
-	}
-	
-	/*
-	 * Get number of fragments from upper 16 or so bits of argument.
-	 */
-	s->nbfrags = (val >> 16) & 0x7FFF;
-
-	/*
-	 * Get size of fragments from lower 16 bits of argument.
-	 */
-	val &= 0xffff;
-
-	DPRINTK_IOCTL( "audio_set_fragments: requested num = %d ; size = 2^%d\n",
-				s->nbfrags, val );
-
-	if (val < 4)
-	{
-		val = 4;
-	}
-	
-	if (val > 15)
-	{
-		val = 15;
-	}
-	
-	s->fragsize = 1 << val;
-
-	if (s->nbfrags < 2)
-	{
-		s->nbfrags = 2;
-	}
-	
-	/*
-	 * Limit total dma buffer size to 128K
-	 */
-	if (s->nbfrags * s->fragsize > 128 * 1024)
-	{
-		s->nbfrags = 128 * 1024 / s->fragsize;
-	}
-	
-	/*
-	 * Keep track of how many frags we are telling the app we allocated.
-	 */
-	s->reportedfrags = s->nbfrags;
-
-	s->nbfrags = scale_to_expanded_samples( state, s->nbfrags );
-
-	if (audio_allocate_buffers( state, s))
-	{
-		return -ENOMEM;
-	}
-	
-	DPRINTK_IOCTL( "audio_set_fragments: allocated num = %d ; size = %d bytes\n",
-				s->nbfrags, s->fragsize );
-
-	return val|(s->reportedfrags << 16);
-}
-
-	
-static void audio_set_num_channels( audio_stream_t * s, long val)
-{
-	DPRINTK( "audio_set_num_channels = %d\n", (int)val );
-	s->audio_num_channels = val;
-}
-
-static void print_audio_format( long format )
-{
-	switch( format )
-	{
-		case AFMT_U8:		   
-			DPRINTK_IOCTL( "AFMT_U8\n" );		   
-			break;
-
-		case AFMT_S8:
-			DPRINTK_IOCTL( "AFMT_S8\n" );		   
-			break;
-
-		case AFMT_S32_BLOCKED:
-			DPRINTK_IOCTL( "AFMT_S32_BLOCKED\n" );		   
-			break;
-
-		case AFMT_S16_LE:
-			DPRINTK_IOCTL( "AFMT_S16_LE\n" );		   
-			break;
-
-		case AFMT_S16_BE:
-			DPRINTK_IOCTL( "AFMT_S16_BE\n" );		   
-			break;
-
-		case AFMT_U16_LE:
-			DPRINTK_IOCTL( "AFMT_U16_LE\n" );		   
-			break;
-
-		case AFMT_U16_BE:
-		default:
-			DPRINTK_IOCTL( "AFMT_U16_BE\n" );		   
-			break;
-	}
-}
-
+ */
+static int audio_write
+(
+	struct file 	*file, 
+	const char 		*buffer,
+	size_t 			src_count, 
+	loff_t 			*ppos
+)
+{
+	const char *buffer0 = buffer;
+	audio_state_t *state = (audio_state_t *)file->private_data;
+	audio_stream_t *s = state->output_stream;
+	int dma_xfer_count, src_xfer_count, expanded_count, ret = 0;
+//	int i,j;
+	
+ 	/* DPRINTK_IOCTL( "EP93xx - audio_write: count=%d\n", src_count ); */
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+		
+	if (s->mapped)
+		return -ENXIO;
+		
+	if( !access_ok( VERIFY_READ, buffer, src_count ) )
+	{
+		DPRINTK( "access_ok failed!!!!\n" );
+		return -EFAULT;
+	}
+
+	/*
+	 * Allocate dma buffers if we haven't already done so.
+	 */
+	if( !s->buffers && audio_allocate_buffers( state, s) )
+	{
+		return -ENOMEM;
+	}
+
+//	if( iWriteCount > 40 )
+//	{
+//		for( i=0; i<4 ; i++ )
+//		{
+//			for( j=0; j<8 ; j++ )
+//			{
+//				printk("%02x ", buffer[(i*8)+j] );
+//			}
+//			printk("\n");
+//		}
+//		iWriteCount = 0;
+//	}
+//	iWriteCount++;
+	
+	/*
+	 * Stay in this loop until we have copied all of the file
+	 * into user memory.
+	 */
+	while( src_count > 0 ) 
+	{
+		audio_buf_t *buf = s->dma_buffer;
+
+		/* Wait for a buffer to become free */
+		if (file->f_flags & O_NONBLOCK)
+		{
+			ret = -EAGAIN;
+			if (down_trylock(&buf->sem))
+				break;
+		} 
+		else 
+		{
+			ret = -ERESTARTSYS;
+			if (down_interruptible(&buf->sem))
+				break;
+		}
+
+		/* 
+		 * Feed the current buffer (s->dma_buffer) 
+		 * This involves expanding sample size from the source buffer
+		 * to be 32 bit stereo for our dma.
+		 */
+		
+		/*
+		 * How much space is left in the current buf?
+		 *
+		 * dma_xfer_count is # of bytes placed into the dma buffer
+		 * where each sample is 4 bytes (4 bytes left, 4 bytes right)
+		 */
+		dma_xfer_count = s->fragsize - buf->size;
+		
+		/*
+		 * Input buffer is src_count (bytes) of whatever format.
+		 * How big will src_count be when sample size is expanded
+		 * to 32 bit samples for our hardware?
+		 */
+		expanded_count = scale_to_expanded_samples( state, src_count );
+		
+		/*
+		 * See if we can fit all the remaining input buffer in
+		 * the current buffer...
+		 */
+		if (dma_xfer_count > expanded_count)
+		{
+			dma_xfer_count = expanded_count;
+		}
+		
+		//DPRINTK( "EP93xx - audio_write: %d to buf # %d\n", dma_xfer_count, s->dma_buffer_index );
+		
+		src_xfer_count = copy_n_convert( 	state, 
+											buf->start + buf->size, 
+											buffer, 
+											dma_xfer_count);
+		if( src_xfer_count <= 0 ) 
+		{
+			up(&buf->sem);
+			return -EFAULT;
+		}
+
+		/*
+		 * Increment dma buffer pointer.
+		 */
+		buf->size += dma_xfer_count;
+
+		/* 
+		 * Increment source buffer pointer.
+		 * Decrement the input buffer size count.
+		 */
+		buffer += src_xfer_count;
+		src_count  -= src_xfer_count;
+
+		/*
+		 * If the chunk is less than a whole fragment, don't
+		 * send it, wait for more ??? I say, go ahead and send it.
+		 */
+#if 0
+		if (buf->size < s->fragsize) 
+		{
+			up(&buf->sem);
+			break;
+		}	 
+#endif
+
+		/*
+		 * If we haven't already started the DMA start it.
+		 * But don't start it if we are waiting on a trigger.
+		 */
+		if( !s->active && !s->stopped )
+		{
+			s->active = 1;
+			audio_dma_start( state, s );
+		}
+		
+		/* Send current buffer to dma */
+	 	ep93xx_dma_add_buffer( s->dmahandles[0], 	/* dma instance 		*/
+							   (unsigned int)buf->dma_addr, /* source 		*/
+							   0, 					/* dest 				*/
+							   buf->size,			/* size					*/
+							   0,					/* is the last chunk? 	*/
+							   (unsigned int) buf ); /* buf id			 	*/	
+		
+		/* 
+		 * Indicate that the buffer has been sent
+	 	 * used to set buf->size = 0; but we can't use that anymore
+		 */
+		buf->sent = 1;
+		
+		/*
+		 *
+		 */
+		NEXT_BUF(s, dma_buffer);
+	}
+	
+	/*
+	 * Return the number of bytes transferred.
+	 */
+	if( buffer - buffer0 )
+	{
+		ret = buffer - buffer0;
+	}
+	
+/*	DPRINTK( "EP93xx - audio_write: return=%d\n", ret );*/
+	return ret;
+}
+
+
+/*
+ * audio_dma_start
+ *
+ * Our Ac97 has a specific start order that it likes.  Enable the 
+ * Ac97 channel AFTER enabling DMA.  Our I2S is not so picky.
+ */
+void audio_dma_start( audio_state_t * state, audio_stream_t * stream )
+{
+	ep93xx_dma_start( stream->dmahandles[0],
+					  stream->NumDmaChannels, 
+					  stream->dmahandles );
+	
+	if (state->hw_enable)
+	{
+		state->hw_enable( state->output_stream == stream );
+	}
+}
+
+/*
+ * audio_dma_pause
+ */
+void audio_dma_pause( audio_state_t * state, audio_stream_t * stream )
+{
+	if (state->hw_disable)
+	{
+		state->hw_disable( state->output_stream == stream );
+	}
+	
+	ep93xx_dma_pause( stream->dmahandles[0],
+					  stream->NumDmaChannels, 
+					  stream->dmahandles );
+}
+
+static void audio_prime_dma( audio_state_t * state, audio_stream_t * s )
+{
+	int i;
+
+	/*
+	 * If we haven't already started the DMA start it.
+	 * But don't start it if we are waiting on a trigger.
+	 */
+	if( !s->active && !s->stopped )
+	{
+		s->active = 1;
+		audio_dma_start( state, s );
+	}
+
+	for (i = 0; i < s->nbfrags; i++) 
+	{
+		audio_buf_t *buf = s->dma_buffer;
+		down(&buf->sem);
+	 	ep93xx_dma_add_buffer( s->dmahandles[0], 	/* dma instance 		*/
+							   (unsigned int)buf->dma_addr, /* source 		*/
+							   0, 					/* dest 				*/
+							   s->fragsize,			/* size					*/
+							   0,					/* is the last chunk? 	*/
+							   (unsigned int) buf ); /* buf id			 	*/	
+		NEXT_BUF(s, dma_buffer);
+	}
+}
+
+/*
+ * audio_read
+ *
+ * Audio capture function.
+ */
+static int audio_read(struct file *file, char *user_buffer,size_t count, loff_t *ppos) {
+
+  char *user_buffer_start = user_buffer;
+  audio_state_t *state = (audio_state_t *)file->private_data;
+  audio_stream_t *stream = state->input_stream;
+  unsigned int user_buffer_xfer_count = 0, dma_buffer_xfer_count = 0; 
+  int ret = 0;
+
+	DPRINTK("EP93xx - audio_read: count=%d\n", count);
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+		
+	if (stream->mapped)
+		return -ENXIO;
+
+	if( !access_ok( VERIFY_WRITE, user_buffer, count ) )
+	{
+		DPRINTK( "access_ok failed for audio_read!!!!\n" );
+		return -EFAULT;
+	}
+
+	if (!stream->active) 
+	{
+		if (!stream->buffers && audio_allocate_buffers( state, stream))
+			return -ENOMEM;
+
+		audio_prime_dma( state, stream );
+	}
+
+	while (count > 0) 
+	{
+		/*
+		 * Get the current buffer.
+		 */
+		audio_buf_t *dma_buffer = stream->dma_buffer;
+
+		/* 
+		 * Wait for a dma buffer to become full 
+		 */
+		if (file->f_flags & O_NONBLOCK) 
+		{
+			//DPRINTK("file->f_flags & O_NONBLOCK\n");
+			ret = -EAGAIN;
+			//if(dma_buffer->owner != 0) break;
+			if (down_trylock(&dma_buffer->sem)) break;
+		} 
+		else 
+		{
+			//while (dma_buffer->owner == 1) {
+			//	if (signal_pending(current))
+			//		return -ERESTARTSYS;
+			//	schedule();
+			//}
+			ret = -ERESTARTSYS;
+			if (down_interruptible(&dma_buffer->sem)) break;
+
+		}
+
+		/*
+		 * If this is the first buffer captured,
+		 * dump the first 8 samples of the dma buffer.
+		 */
+//		if( stream->bFirstCaptureBuffer )
+//		{
+//			dma_buffer->size -= 64;
+//			stream->bFirstCaptureBuffer = 0;
+//		}	
+		
+		/*
+		 * How much user buffer would a whole DMA buffer fill?
+	 	 */
+		user_buffer_xfer_count = dma_buffer->size / stream->dma2usr_ratio;
+
+		if (user_buffer_xfer_count > count)
+			user_buffer_xfer_count = count;
+		
+		dma_buffer_xfer_count = user_buffer_xfer_count * stream->dma2usr_ratio;
+
+		DPRINTK("dma_buffer: start=0x%08x fragsz=%d size=%d\n",
+				 (int)dma_buffer->start, (int)stream->fragsize, dma_buffer->size);
+		
+		DPRINTK("user_buffer=0x%08x  from=0x%08x  user_count=%d  dma_count=%d\n",
+				 (int)user_buffer,
+				 (int)dma_buffer->start + stream->fragsize - dma_buffer->size,
+				 user_buffer_xfer_count, dma_buffer_xfer_count);
+		
+		
+		if (copy_to_user_S16_LE( state, 
+				 user_buffer,
+				 dma_buffer->start + stream->fragsize - dma_buffer->size,
+				 user_buffer_xfer_count) <= 0 ) {
+
+			up(&dma_buffer->sem);
+			return -EFAULT;
+		}
+		
+		dma_buffer->size -= dma_buffer_xfer_count;
+
+		user_buffer += user_buffer_xfer_count;
+		count -= user_buffer_xfer_count;
+		
+		/* 
+		 * Grab data from the current dma buffer 
+		 */
+		//DPRINTK("Read: read %d bytes from %d.  dmabufsize=%d.  count=%d\n", 
+		//			user_buffer_xfer_count, stream->dma_buffer_index,
+		//			dma_buffer->size, count);
+		
+		/*
+		 * If there's still data in this buffer to be read, release
+		 * the semiphore and don't give it back yet.  We may come back
+		 * and read from it in a minute when the app calls for another read.
+		 */
+		if (dma_buffer->size > 0) {
+		    up(&dma_buffer->sem);
+		    break;
+		}
+
+		// Make current dma buffer available for DMA again 
+		//dma_buffer->owner = 1;
+	 	//DMA_ADD_BUFFER( stream->dmahandles[0],	    /// dma instance 
+		//		(unsigned int)dma_buffer->dma_addr, // source 
+		//		0, 				    // dest  
+		//		stream->fragsize,		    // size	 
+		//		0,				    // the last chunk? 
+		//		(unsigned int) dma_buffer );        // buf id
+
+	 	ep93xx_dma_add_buffer( stream->dmahandles[0],	    // dma instance  
+				(unsigned int)dma_buffer->dma_addr, // source 
+				0, 				    // dest  
+				stream->fragsize,		    // size
+				0,				    // the last chunk? 
+				(unsigned int) dma_buffer );        // buf id 	
+
+
+		NEXT_BUF(stream, dma_buffer);
+	}
+
+	if ((int)user_buffer - (int)user_buffer_start )
+		ret = (int)user_buffer - (int)user_buffer_start;
+	
+	//DPRINTK("EP93xx - audio_read: return=%d\n", ret);
+	
+	return ret;
+}
+
+static int audio_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	audio_state_t *state = (audio_state_t *)file->private_data;
+	audio_stream_t *s;
+	unsigned long size, vma_addr;
+	int i, ret;
+
+	if (vma->vm_pgoff != 0)
+	{
+		return -EINVAL;
+	}
+
+	if (vma->vm_flags & VM_WRITE) 
+	{
+		if (!state->wr_ref)
+		{
+			return -EINVAL;
+		}
+		s = state->output_stream;
+	}
+	else if (vma->vm_flags & VM_READ) 
+	{
+		if (!state->rd_ref)
+		{
+			return -EINVAL;
+		}
+		s = state->input_stream;
+	} 
+	else 
+	{
+		return -EINVAL;
+	}
+
+	if (s->mapped)
+	{
+		return -EINVAL;
+	}
+
+	size = vma->vm_end - vma->vm_start;
+	
+	if (size != s->fragsize * s->nbfrags)
+	{
+		return -EINVAL;
+	}
+	
+	if (!s->buffers && audio_allocate_buffers( state, s))
+	{
+		return -ENOMEM;
+	}
+
+	vma_addr = vma->vm_start;
+
+	for (i = 0; i < s->nbfrags; i++) 
+	{
+		audio_buf_t *buf = &s->buffers[i];
+		
+		if (!buf->master)
+		{
+			continue;
+		}
+
+		ret = remap_page_range(vma_addr, buf->dma_addr, buf->master, vma->vm_page_prot);
+
+		if (ret)
+		{
+			return ret;
+		}
+
+		vma_addr += buf->master;
+	}
+
+	s->mapped = 1;
+
+	return 0;
+}
+
+
+static unsigned int audio_poll(struct file *file,
+			       struct poll_table_struct *wait)
+{
+	audio_state_t *state = (audio_state_t *)file->private_data;
+	audio_stream_t *is = state->input_stream;
+	audio_stream_t *os = state->output_stream;
+	unsigned int mask = 0;
+	int i;
+
+	DPRINTK("EP93xx - audio_poll(): mode=%s%s\n",
+		(file->f_mode & FMODE_READ) ? "r" : "",
+		(file->f_mode & FMODE_WRITE) ? "w" : "");
+
+	if (file->f_mode & FMODE_READ) 
+	{
+		/* Start audio input if not already active */
+		if (!is->active) 
+		{
+			if (!is->buffers && audio_allocate_buffers( state, is))
+			{
+				return -ENOMEM;
+			}
+
+			audio_prime_dma( state, is);
+		}
+
+		poll_wait(file, &is->wq, wait);
+	}
+
+	if (file->f_mode & FMODE_WRITE) 
+	{
+		if (!os->buffers && audio_allocate_buffers( state, os))
+		{
+			return -ENOMEM;
+		}
+
+		poll_wait(file, &os->wq, wait);
+	}
+
+	if (file->f_mode & FMODE_READ) 
+	{
+		if (is->mapped) 
+		{
+			/* 
+			 * if the buffer is mapped assume we care that there are 
+			 * more bytes available than when we last asked using 
+			 * SNDCTL_DSP_GETxPTR 
+			 */
+			if (is->bytecount != is->getptrCount)
+			{
+				mask |= POLLIN | POLLRDNORM;
+			}
+		} 
+		else 
+		{
+			for (i = 0; i < is->nbfrags; i++) 
+			{
+				if (atomic_read(&is->buffers[i].sem.count) > 0) 
+				{
+					mask |= POLLIN | POLLRDNORM;
+					break;
+				}
+			}
+		}
+	}
+	
+	if (file->f_mode & FMODE_WRITE) 
+	{
+		if (os->mapped) 
+		{
+			if (os->bytecount != os->getptrCount)
+				mask |= POLLOUT | POLLWRNORM;
+		} 
+		else 
+		{
+			for (i = 0; i < os->nbfrags; i++) 
+			{
+				if (atomic_read(&os->buffers[i].sem.count) > 0) 
+				{
+					mask |= POLLOUT | POLLWRNORM;
+					break;
+				}
+			}
+		}
+	}
+
+	DPRINTK("EP93xx - audio_poll() returned mask of %s%s\n",
+		(mask & POLLIN) ? "r" : "",
+		(mask & POLLOUT) ? "w" : "");
+
+	return mask;
+}
+
+/*
+ * audio_set_fragments
+ *
+ * Used to process SNDCTL_DSP_SETFRAGMENT.
+ *
+ * Argument is 0xMMMMSSSS where:
+ * 		SSSS sets dma fragment (buf) size.  size = 2^SSSS bytes
+ *		MMMM sets number of fragments.
+ */
+static int audio_set_fragments( audio_state_t *state, audio_stream_t *s, int val )
+{
+	if (s->active)
+	{
+		return -EBUSY;
+	}
+	
+	/*
+	 * We're gonna change the buffers, so free the ones we currently have
+	 */
+	if (s->buffers)
+	{
+		audio_deallocate_buffers(state, s);
+	}
+	
+	/*
+	 * Get number of fragments from upper 16 or so bits of argument.
+	 */
+	s->nbfrags = (val >> 16) & 0x7FFF;
+
+	/*
+	 * Get size of fragments from lower 16 bits of argument.
+	 */
+	val &= 0xffff;
+
+	DPRINTK_IOCTL( "audio_set_fragments: requested num = %d ; size = 2^%d\n",
+				s->nbfrags, val );
+
+	if (val < 4)
+	{
+		val = 4;
+	}
+	
+	if (val > 15)
+	{
+		val = 15;
+	}
+	
+	s->fragsize = 1 << val;
+
+	if (s->nbfrags < 2)
+	{
+		s->nbfrags = 2;
+	}
+	
+	/*
+	 * Limit total dma buffer size to 128K
+	 */
+	if (s->nbfrags * s->fragsize > 128 * 1024)
+	{
+		s->nbfrags = 128 * 1024 / s->fragsize;
+	}
+	
+	/*
+	 * Keep track of how many frags we are telling the app we allocated.
+	 */
+	s->requested_fragsize = s->nbfrags;
+
+	s->nbfrags = scale_to_expanded_samples( state, s->nbfrags );
+
+	if (audio_allocate_buffers( state, s))
+	{
+		return -ENOMEM;
+	}
+	
+	DPRINTK_IOCTL( "audio_set_fragments: allocated num = %d ; size = %d bytes\n",
+				s->nbfrags, s->fragsize );
+
+	return val|(s->requested_fragsize << 16);
+}
+
+static void print_audio_format( long format )
+{
+	switch( format )
+	{
+		case AFMT_U8:		   
+			DPRINTK_IOCTL( "AFMT_U8\n" );		   
+			break;
+
+		case AFMT_S8:
+			DPRINTK_IOCTL( "AFMT_S8\n" );		   
+			break;
+
+		case AFMT_S32_BLOCKED:
+			DPRINTK_IOCTL( "AFMT_S32_BLOCKED\n" );		   
+			break;
+
+		case AFMT_S16_LE:
+			DPRINTK_IOCTL( "AFMT_S16_LE\n" );		   
+			break;
+
+		case AFMT_S16_BE:
+			DPRINTK_IOCTL( "AFMT_S16_BE\n" );		   
+			break;
+
+		case AFMT_U16_LE:
+			DPRINTK_IOCTL( "AFMT_U16_LE\n" );		   
+			break;
+
+		case AFMT_U16_BE:
+		default:
+			DPRINTK_IOCTL( "AFMT_U16_BE\n" );		   
+			break;
+	}
+}
+
 /*
  * We convert to 24 bit samples that occupy 32 bits each.
  * Formats we support:
  *
- * AFMT_U8		   
- * AFMT_S16_LE	   	Little endian signed 16
- * AFMT_S8		   
- * AFMT_U16_LE	   	Little endian U16
- * AFMT_S32_BLOCKED	32 bit little endian format, taken from the rme96xx driver.
- *
- */
+ * AFMT_U8		   
+ * AFMT_S16_LE	   	Little endian signed 16
+ * AFMT_S8		   
+ * AFMT_U16_LE	   	Little endian U16
+ * AFMT_S32_BLOCKED	32 bit little endian format, taken from the rme96xx driver.
+ *
+ */
 static void audio_set_format( audio_stream_t * s, long val )
 {
-	DPRINTK_IOCTL( "audio_set_format enter.  Format requested (%d) ", 
-				(int)val );
-	print_audio_format( val );
-	
-	switch( val )
-	{
-		case AFMT_U8:		   
-			s->audio_format = val;
-			s->audio_stream_bitwidth = 8;
-			break;
-
-		case AFMT_S8:
-			s->audio_format = val;
-			s->audio_stream_bitwidth = 8;
-			break;
-
-		case AFMT_S32_BLOCKED:
-			s->audio_format = val;
-			s->audio_stream_bitwidth = 32;
-			break;
-
-		case AFMT_S16_LE:
-		case AFMT_S16_BE:
-			s->audio_format = AFMT_S16_LE;
-			s->audio_stream_bitwidth = 16;
-			break;
-
-		case AFMT_U16_LE:
-		case AFMT_U16_BE:
-		default:
-			s->audio_format = AFMT_U16_LE;
-			s->audio_stream_bitwidth = 16;
-			break;
-	}
-
-	DPRINTK_IOCTL( "audio_set_format EXIT format set to be (%d) ", 
-				(int)s->audio_format );
-	print_audio_format( (long)s->audio_format );
-}
-
-static int copy_from_user_U16_LE_CM
-(
-	audio_state_t *state,
-	const char *to, 
-	const char *from, 
-	int toCount 
-)
-{
-	unsigned short data;
-	unsigned int val;
-	int toCount0 = toCount;
-	unsigned int * p = (unsigned int *)to;
-	unsigned short * userPtr = (unsigned short *)from;
-	int mono = (state->output_stream->audio_num_channels == 1);
-
-	/*
-	 * Compact mode - slot 3 (left) is the lower 16 bits.  slot 4 (right)
-	 * is the upper 16 bits.
-	 */
-	while (toCount > 0) 
-	{
-		if (__get_user(data, userPtr++))
-		{
-			return -EFAULT;
-		}
-		val = (unsigned int)data & 0x0000ffff;
-		
-		/*
-		 * If mono, use this mono sample for both channels, else we just got
-		 * the left channel sample.  So get the right sample and use it for 
-		 * the right channel.
-		 */
-		if( !mono )
-		{
-			if (__get_user(data, userPtr++))
-			{
-				return -EFAULT;
-			}
-		}
-
-		*p++ = (((unsigned int)data << 16) | val) ^ 0x80008000;
-
-		toCount -= 4;
-	}
-
-	/*
-	 * Each mono or stereo 16 bit sample going in results
-	 * in one stereo 32 bit sample going out.  So
-	 * mono   = (2 bytes in)/(4 bytes out)
-	 * stereo = (4 bytes in)/(4 bytes out)
-	 */
-	if( mono )
-	{
-		return toCount0 / 2;
-	}
-	
-	return toCount0;
-}
-
+	DPRINTK_IOCTL( "audio_set_format enter.  Format requested (%d) ", 
+				(int)val );
+	print_audio_format( val );
+	
+	switch( val )
+	{
+		case AFMT_U8:		   
+			s->audio_format = val;
+			s->audio_stream_bitwidth = 8;
+			break;
+
+		case AFMT_S8:
+			s->audio_format = val;
+			s->audio_stream_bitwidth = 8;
+			break;
+
+		case AFMT_S32_BLOCKED:
+			s->audio_format = val;
+			s->audio_stream_bitwidth = 32;
+			break;
+
+		case AFMT_S16_LE:
+		case AFMT_S16_BE:
+			s->audio_format = AFMT_S16_LE;
+			s->audio_stream_bitwidth = 16;
+			break;
+
+		case AFMT_U16_LE:
+		case AFMT_U16_BE:
+		default:
+			s->audio_format = AFMT_U16_LE;
+			s->audio_stream_bitwidth = 16;
+			break;
+	}
+
+	DPRINTK_IOCTL( "audio_set_format EXIT format set to be (%d) ", 
+				(int)s->audio_format );
+	print_audio_format( (long)s->audio_format );
+}
+
+static unsigned long copy_to_user_S16_LE(audio_state_t *state,const char *to, const char *from, unsigned long to_count)
+{
+	int total_to_count = to_count;
+	short *user_ptr = (short *)to;	// 16 bit user buffer
+	int *dma_ptr = (int *)from;	// 32 bit dma buffer
+
+	int shift = state->input_stream->hw_bit_width - 16;	
+
+	if( state->input_stream->audio_num_channels != 1 ) 
+	{
+		while (to_count > 0) 
+		{
+			__put_user( (short)( (*dma_ptr++) >> shift ), user_ptr++ );
+			__put_user( (short)( (*dma_ptr++) >> shift ), user_ptr++ );
+			to_count -= 4;
+		}
+	}
+	else
+	{
+		while (to_count > 0) 
+		{
+			__put_user( (short)((*dma_ptr++) >> shift), user_ptr++ );
+			dma_ptr++;	/* skip right channel sample */
+			to_count -= 2;
+		}
+	}
+	
+	return total_to_count;
+} 
+
+static int copy_from_user_U16_LE_CM
+(
+	audio_state_t *state,
+	const char *to, 
+	const char *from, 
+	int toCount 
+)
+{
+	unsigned short data;
+	unsigned int val;
+	int toCount0 = toCount;
+	unsigned int * p = (unsigned int *)to;
+	unsigned short * userPtr = (unsigned short *)from;
+	int mono = (state->output_stream->audio_num_channels == 1);
+
+	/*
+	 * Compact mode - slot 3 (left) is the lower 16 bits.  slot 4 (right)
+	 * is the upper 16 bits.
+	 */
+	while (toCount > 0) 
+	{
+		if (__get_user(data, userPtr++))
+		{
+			return -EFAULT;
+		}
+		val = (unsigned int)data & 0x0000ffff;
+		
+		/*
+		 * If mono, use this mono sample for both channels, else we just got
+		 * the left channel sample.  So get the right sample and use it for 
+		 * the right channel.
+		 */
+		if( !mono )
+		{
+			if (__get_user(data, userPtr++))
+			{
+				return -EFAULT;
+			}
+		}
+
+		*p++ = (((unsigned int)data << 16) | val) ^ 0x80008000;
+
+		toCount -= 4;
+	}
+
+	/*
+	 * Each mono or stereo 16 bit sample going in results
+	 * in one stereo 32 bit sample going out.  So
+	 * mono   = (2 bytes in)/(4 bytes out)
+	 * stereo = (4 bytes in)/(4 bytes out)
+	 */
+	if( mono )
+	{
+		return toCount0 / 2;
+	}
+	
+	return toCount0;
+}
+
 static int copy_from_user_U16_LE
 (
-	audio_state_t *state,
-	const char *to, 
-	const char *from, 
+	audio_state_t *state,
+	const char *to, 
+	const char *from, 
 	int toCount 
 )
 {
 	unsigned short data;
 	int val;
-	int toCount0 = toCount;
+	int toCount0 = toCount;
 	int * p = (int *)to;
 	unsigned short * userPtr = (unsigned short *)from;
-	int mono = (state->output_stream->audio_num_channels == 1);
-	int shift = state->DAC_bit_width - 16;
-
+	int mono = (state->output_stream->audio_num_channels == 1);
+	int shift = state->DAC_bit_width - 16;
+
 	while (toCount > 0) 
-	{
-		if (__get_user(data, userPtr++))
-		{
-			return -EFAULT;
+	{
+		if (__get_user(data, userPtr++))
+		{
+			return -EFAULT;
 		}
-		val = (data ^ 0x8000) << shift;
+		val = (data ^ 0x8000) << shift;
 		*p++ = val;
 		toCount -= 4;
-
+
 		if (!mono) 
-		{
-			if (__get_user(data, userPtr++))
-			{
-				return -EFAULT;
-			}
-			val = (data ^ 0x8000) << shift;
+		{
+			if (__get_user(data, userPtr++))
+			{
+				return -EFAULT;
+			}
+			val = (data ^ 0x8000) << shift;
+		}
+
+		*p++ = val;
+		toCount -= 4;
+	}
+
+	/*
+	 * Each mono or stereo 16 bit sample going in results
+	 * in a two 32 bit (left & right) samples going out.  So
+	 * mono   = (2 bytes in)/(8 bytes out)
+	 * stereo = (4 bytes in)/(8 bytes out)
+	 */
+	if( mono )
+	{
+		return toCount0 / 4;
+	}
+	
+	return toCount0 / 2;
+}
+
+static int copy_from_user_S16_LE_CM
+(
+	audio_state_t *state,
+	const char *to, 
+	const char *from, 
+	int toCount 
+)
+{
+	short data;
+	unsigned int val;
+	unsigned int * p = (int *)to;
+	int toCount0 = toCount;
+	unsigned short * userPtr = (unsigned short *)from;
+	int mono = (state->output_stream->audio_num_channels == 1);
+
+	while (toCount > 0) 
+	{
+		if (__get_user(data, userPtr++))
+		{
+			return -EFAULT;
 		}
-
-		*p++ = val;
+		val = (unsigned int)data & 0x0000ffff;
+
+		if( !mono )
+		{
+			if (__get_user(data, userPtr++))
+			{
+				return -EFAULT;
+			}
+		}
+
+		*p++ = ((unsigned int)data << 16) | val;
 		toCount -= 4;
-	}
-
-	/*
-	 * Each mono or stereo 16 bit sample going in results
-	 * in a two 32 bit (left & right) samples going out.  So
-	 * mono   = (2 bytes in)/(8 bytes out)
-	 * stereo = (4 bytes in)/(8 bytes out)
-	 */
-	if( mono )
-	{
-		return toCount0 / 4;
-	}
-	
-	return toCount0 / 2;
-}
-
-static int copy_from_user_S16_LE_CM
-(
-	audio_state_t *state,
-	const char *to, 
-	const char *from, 
-	int toCount 
-)
-{
-	short data;
-	unsigned int val;
-	unsigned int * p = (int *)to;
-	int toCount0 = toCount;
-	unsigned short * userPtr = (unsigned short *)from;
-	int mono = (state->output_stream->audio_num_channels == 1);
-
-	while (toCount > 0) 
-	{
-		if (__get_user(data, userPtr++))
-		{
-			return -EFAULT;
-		}
-		val = (unsigned int)data & 0x0000ffff;
-
-		if( !mono )
-		{
-			if (__get_user(data, userPtr++))
-			{
-				return -EFAULT;
-			}
-		}
-
-		*p++ = ((unsigned int)data << 16) | val;
-		toCount -= 4;
-	}
-
-	/*
-	 * Each mono or stereo 16 bit sample going in results
-	 * in one stereo 32 bit sample going out.  So
-	 * mono   = (2 bytes in)/(4 bytes out)
-	 * stereo = (4 bytes in)/(4 bytes out)
-	 */
-	if( mono )
-	{
-		return toCount0 / 2;
-	}
-	
-	return toCount0;
-}
-
-
+	}
+
+	/*
+	 * Each mono or stereo 16 bit sample going in results
+	 * in one stereo 32 bit sample going out.  So
+	 * mono   = (2 bytes in)/(4 bytes out)
+	 * stereo = (4 bytes in)/(4 bytes out)
+	 */
+	if( mono )
+	{
+		return toCount0 / 2;
+	}
+	
+	return toCount0;
+}
+
+
 static int copy_from_user_S16_LE
 (
-	audio_state_t *state,
-	const char *to, 
-	const char *from, 
+	audio_state_t *state,
+	const char *to, 
+	const char *from, 
 	int toCount 
 )
 {
 	short data;
 	int val;
-	int toCount0 = toCount;
+	int toCount0 = toCount;
 	int * p = (int *)to;
 	unsigned short * userPtr = (unsigned short *)from;
-	int mono = (state->output_stream->audio_num_channels == 1);
-	int shift = state->DAC_bit_width - 16;
-
+	int mono = (state->output_stream->audio_num_channels == 1);
+	int shift = state->DAC_bit_width - 16;
+
 	while (toCount > 0) 
-	{
-		if (__get_user(data, userPtr++))
-		{
-			return -EFAULT;
-		}
-		val = data << shift;
+	{
+		if (__get_user(data, userPtr++))
+		{
+			return -EFAULT;
+		}
+		val = data << shift;
 		*p++ = val;
 		toCount -= 4;
-
-		if (!mono) 
-		{
-			if (__get_user(data, userPtr++))
-			{
-				return -EFAULT;
-			}
+
+		if (!mono) 
+		{
+			if (__get_user(data, userPtr++))
+			{
+				return -EFAULT;
+			}
 			val = data << shift;
 		}
-
-		*p++ = val;
+
+		*p++ = val;
+		toCount -= 4;
+	}
+
+	/*
+	 * Each mono or stereo 16 bit sample going in results
+	 * in a two 32 bit samples (left & right) going out.  So
+	 * mono   = (2 bytes in)/(8 bytes out)
+	 * stereo = (4 bytes in)/(8 bytes out)
+	 */
+	if( mono )
+	{
+		return toCount0 / 4;
+	}
+	
+	return toCount0 / 2;
+}
+
+static int copy_from_user_S8_CM
+(
+	audio_state_t *state,
+	const char *to, 
+	const char *from, 
+	int toCount 
+)
+{
+	char data;
+	int val;
+	int toCount0 = toCount;
+	int * p = (int *)to;
+	unsigned char * userPtr = (unsigned char *)from;
+	int mono = (state->output_stream->audio_num_channels == 1);
+
+	while (toCount > 0) 
+	{
+		if (__get_user(data, userPtr++))
+		{
+			return -EFAULT;
+		}
+		val = (data << 8) & 0x0000ffff;
+
+		if (!mono) 
+		{
+			if (__get_user(data, userPtr++))
+			{
+				return -EFAULT;
+			}
+		}
+
+		*p++ = (data << 24) | val;
 		toCount -= 4;
-	}
+	}
+
+	/*
+	 * Each mono or stereo 8 bit sample going in results
+	 * in one stereo 32 bit sample going out.  So
+	 * mono   = (1 byte  in)/(4 bytes out)
+	 * stereo = (2 bytes in)/(4 bytes out)
+	 */
+	if( mono )
+	{
+		return toCount0 / 4;
+	}
+	
+	return toCount0 / 2;
+}
 
-	/*
-	 * Each mono or stereo 16 bit sample going in results
-	 * in a two 32 bit samples (left & right) going out.  So
-	 * mono   = (2 bytes in)/(8 bytes out)
-	 * stereo = (4 bytes in)/(8 bytes out)
-	 */
-	if( mono )
-	{
-		return toCount0 / 4;
-	}
-	
-	return toCount0 / 2;
-}
-
-static int copy_from_user_S8_CM
-(
-	audio_state_t *state,
-	const char *to, 
-	const char *from, 
-	int toCount 
-)
-{
-	char data;
-	int val;
-	int toCount0 = toCount;
-	int * p = (int *)to;
-	unsigned char * userPtr = (unsigned char *)from;
-	int mono = (state->output_stream->audio_num_channels == 1);
-
-	while (toCount > 0) 
-	{
-		if (__get_user(data, userPtr++))
-		{
-			return -EFAULT;
-		}
-		val = (data << 8) & 0x0000ffff;
-
-		if (!mono) 
-		{
-			if (__get_user(data, userPtr++))
-			{
-				return -EFAULT;
-			}
-		}
-
-		*p++ = (data << 24) | val;
-		toCount -= 4;
-	}
-
-	/*
-	 * Each mono or stereo 8 bit sample going in results
-	 * in one stereo 32 bit sample going out.  So
-	 * mono   = (1 byte  in)/(4 bytes out)
-	 * stereo = (2 bytes in)/(4 bytes out)
-	 */
-	if( mono )
-	{
-		return toCount0 / 4;
-	}
-	
-	return toCount0 / 2;
-}
-
 static int copy_from_user_S8
 (
-	audio_state_t *state,
-	const char *to, 
-	const char *from, 
+	audio_state_t *state,
+	const char *to, 
+	const char *from, 
 	int toCount 
 )
 {
 	char data;
-	int val;
-	int toCount0 = toCount;
+	int val;
+	int toCount0 = toCount;
 	int * p = (int *)to;
 	unsigned char * userPtr = (unsigned char *)from;
-	int mono = (state->output_stream->audio_num_channels == 1);
-	int shift = state->DAC_bit_width - 8;
-
+	int mono = (state->output_stream->audio_num_channels == 1);
+	int shift = state->DAC_bit_width - 8;
+
 	while (toCount > 0) 
-	{
-		if (__get_user(data, userPtr++))
-		{
-			return -EFAULT;
-		}
-		val = data << shift;
+	{
+		if (__get_user(data, userPtr++))
+		{
+			return -EFAULT;
+		}
+		val = data << shift;
 		*p++ = val;
 		toCount -= 4;
-
-		if (!mono) 
-		{
-			if (__get_user(data, userPtr++))
-			{
-				return -EFAULT;
-			}
+
+		if (!mono) 
+		{
+			if (__get_user(data, userPtr++))
+			{
+				return -EFAULT;
+			}
 			val = data << shift;
 		}
-
-		*p++ = val;
+
+		*p++ = val;
+		toCount -= 4;
+	}
+
+	/*
+	 * Each mono or stereo 8 bit sample going in results
+	 * in a two 32 bit samples (left & right) going out.  So
+	 * mono   = (1 byte  in)/(8 bytes out)
+	 * stereo = (2 bytes in)/(8 bytes out)
+	 */
+	if( mono )
+	{
+		return toCount0 / 8;
+	}
+	
+	return toCount0 / 4;
+}
+
+static int copy_from_user_U8_CM
+(
+	audio_state_t *state,
+	const char *to, 
+	const char *from, 
+	int toCount 
+)
+{
+	int * p = (int *)to;
+	unsigned char * userPtr = (unsigned char *)from;
+	unsigned char data;
+	int val;
+	int toCount0 = toCount;
+
+	int mono = (state->output_stream->audio_num_channels == 1);
+
+	while (toCount > 0) 
+	{
+		if (__get_user(data, userPtr++))
+		{
+			return -EFAULT;
+		}
+		val = ((data ^ 0x80) << 8) & 0x0000ffff;
+
+		if (!mono) 
+		{
+			if (__get_user(data, userPtr++))
+			{
+				return -EFAULT;
+			}
+		}
+
+		*p++ = ((data ^ 0x80) << 24) | val;
 		toCount -= 4;
-	}
+	}
+
+	/*
+	 * Each mono or stereo 8 bit sample going in results
+	 * in one stereo 32 bit sample going out.  So
+	 * mono   = (1 byte  in)/(4 bytes out)
+	 * stereo = (2 bytes in)/(4 bytes out)
+	 */
+	if( mono )
+	{
+		return toCount0 / 4;
+	}
+	
+	return toCount0 / 2;
+}
 
-	/*
-	 * Each mono or stereo 8 bit sample going in results
-	 * in a two 32 bit samples (left & right) going out.  So
-	 * mono   = (1 byte  in)/(8 bytes out)
-	 * stereo = (2 bytes in)/(8 bytes out)
-	 */
-	if( mono )
-	{
-		return toCount0 / 8;
-	}
-	
-	return toCount0 / 4;
-}
-
-static int copy_from_user_U8_CM
-(
-	audio_state_t *state,
-	const char *to, 
-	const char *from, 
-	int toCount 
-)
-{
-	int * p = (int *)to;
-	unsigned char * userPtr = (unsigned char *)from;
-	unsigned char data;
-	int val;
-	int toCount0 = toCount;
-
-	int mono = (state->output_stream->audio_num_channels == 1);
-
-	while (toCount > 0) 
-	{
-		if (__get_user(data, userPtr++))
-		{
-			return -EFAULT;
-		}
-		val = ((data ^ 0x80) << 8) & 0x0000ffff;
-
-		if (!mono) 
-		{
-			if (__get_user(data, userPtr++))
-			{
-				return -EFAULT;
-			}
-		}
-
-		*p++ = ((data ^ 0x80) << 24) | val;
-		toCount -= 4;
-	}
-
-	/*
-	 * Each mono or stereo 8 bit sample going in results
-	 * in one stereo 32 bit sample going out.  So
-	 * mono   = (1 byte  in)/(4 bytes out)
-	 * stereo = (2 bytes in)/(4 bytes out)
-	 */
-	if( mono )
-	{
-		return toCount0 / 4;
-	}
-	
-	return toCount0 / 2;
-}
-
 static int copy_from_user_U8
 (
-	audio_state_t *state,
-	const char *to, 
-	const char *from, 
+	audio_state_t *state,
+	const char *to, 
+	const char *from, 
 	int toCount 
 )
 {
-	int * p = (int *)to;
-	unsigned char * userPtr = (unsigned char *)from;
-
+	int * p = (int *)to;
+	unsigned char * userPtr = (unsigned char *)from;
+
 	unsigned char data;
-	int val;
-	int toCount0 = toCount;
-	int shift = state->DAC_bit_width - 8;
-
-	int mono = (state->output_stream->audio_num_channels == 1);
-
+	int val;
+	int toCount0 = toCount;
+	int shift = state->DAC_bit_width - 8;
+
+	int mono = (state->output_stream->audio_num_channels == 1);
+
 	while (toCount > 0) 
-	{
-		if (__get_user(data, userPtr++))
-		{
-			return -EFAULT;
-		}
-		val = (data ^ 0x80) << shift;
+	{
+		if (__get_user(data, userPtr++))
+		{
+			return -EFAULT;
+		}
+		val = (data ^ 0x80) << shift;
 		*p++ = val;
 		toCount -= 4;
-
-		if (!mono) 
-		{
-			if (__get_user(data, userPtr++))
-			{
-				return -EFAULT;
-			}
+
+		if (!mono) 
+		{
+			if (__get_user(data, userPtr++))
+			{
+				return -EFAULT;
+			}
 			val = (data ^ 0x80) << shift;
 		}
-
-		*p++ = val;
+
+		*p++ = val;
 		toCount -= 4;
-	}
+	}
+
+	/*
+	 * Each mono or stereo 8 bit sample going in results
+	 * in a two 32 bit samples (left & right) going out.  So
+	 * mono   = (1 byte  in)/(8 bytes out)
+	 * stereo = (2 bytes in)/(8 bytes out)
+	 */
+	if( mono )
+	{
+		return toCount0 / 8;
+	}
+	
+	return toCount0 / 4;
+}
+
+static int copy_from_user_U32
+(
+	audio_state_t *state,
+	const char *to, 
+	const char *from, 
+	int toCount 
+)
+{
+	if (copy_from_user( (char *)to, from, toCount)) 
+	{
+		return -EFAULT;
+	}
+
+	return toCount;
+}
+
+/*
+ * Returns negative for error
+ * Returns # of bytes transferred out of the from buffer
+ * for success.
+ */
+static int copy_n_convert
+(
+	audio_state_t *state,
+	const char *to, 
+	const char *from, 
+	int toCount 
+)
+{
+	int ret=0;
+	
+	if( toCount == 0 )
+	{
+		DPRINTK("copy_n_convert - nothing to copy!\n");
+	}
+
+	/*
+	 * Compact Mode means that each 32 bit word has both the
+	 * left and right sample in it.
+	 */
+	if( state->bCompactMode )
+	{
+		switch( state->output_stream->audio_format )
+		{
+			case AFMT_U8:
+				ret = copy_from_user_U8_CM( state, to, from, toCount );
+				break;
+
+			case AFMT_S16_LE:
+				ret = copy_from_user_S16_LE_CM( state, to, from, toCount );
+				break;
+		
+			case AFMT_S8:
+				ret = copy_from_user_S8_CM( state, to, from, toCount );
+				break;
+				
+			case AFMT_U16_LE:
+				ret = copy_from_user_U16_LE_CM( state, to, from, toCount );
+				break;
+				
+			case AFMT_S32_BLOCKED:
+			default:
+				break;
+		}
+	}
+	else
+	{
+		switch( state->output_stream->audio_format )
+		{
+			case AFMT_U8:
+				ret = copy_from_user_U8( state, to, from, toCount );
+				break;
+
+			case AFMT_S16_LE:
+				ret = copy_from_user_S16_LE( state, to, from, toCount );
+				break;
+		
+			case AFMT_S8:
+				ret = copy_from_user_S8( state, to, from, toCount );
+				break;
+				
+			case AFMT_U16_LE:
+				ret = copy_from_user_U16_LE( state, to, from, toCount );
+				break;
+				
+			case AFMT_S32_BLOCKED:
+			default:
+				ret = copy_from_user_U32( state, to, from, toCount );
+				break;
+		}
+	}
+	
+	if( ret == -EFAULT )
+	{
+		DPRINTK( "copy_n_convert: get_user failed!!!!\n" );
+	}
+	return ret;
+}
+
+static int audio_ioctl(struct inode *inode, struct file *file,
+		       uint cmd, ulong arg)
+{
+	audio_state_t *state = (audio_state_t *)file->private_data;
+	audio_stream_t *os = state->output_stream;
+	audio_stream_t *is = state->input_stream;
+	long val;
+        unsigned int *current_frac;
+
+	/* dispatch based on command */
+	switch (cmd) 
+	{
+		case SNDCTL_DSP_STEREO:
+			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_STEREO\n");
+			if (get_user(val, (int *)arg))
+			{
+				return -EFAULT;
+			}
+			if (val > 1 || val < 0)
+			{
+				return -EINVAL;
+			}
+
+			if (file->f_mode & FMODE_WRITE)
+			{
+				if (os->audio_num_channels != (val + 1)) {
+					os->audio_num_channels = val + 1;
+					calculate_dma2usr_ratio_etc( os );
+				}
+			} 
+
+			if (file->f_mode & FMODE_READ) 
+			{
+				if (is->audio_num_channels != (val + 1)) {
+					is->audio_num_channels = val + 1;
+					calculate_dma2usr_ratio_etc( is );
+				}
+			} 
+
+			return put_user( val, (long *) arg);
+
+		case SNDCTL_DSP_CHANNELS:
+			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_CHANNELS\n");
+			if (get_user(val, (int *)arg))
+			{
+				return -EFAULT;
+			}
+			if (val > 2 || val < 0)
+			{
+				return -EINVAL;
+			}
+
+			if (file->f_mode & FMODE_WRITE)
+			{
+				if (os->audio_num_channels != val) {
+					os->audio_num_channels = val;
+					calculate_dma2usr_ratio_etc( os );
+				} 
+			} 
+			if (file->f_mode & FMODE_READ) 
+			{
+				if (is->audio_num_channels != val) {
+					is->audio_num_channels = val;
+					calculate_dma2usr_ratio_etc( is );
+				} 
+			}
+
+			return put_user( val, (long *) arg);
+
+		case SOUND_PCM_READ_CHANNELS:
+			DPRINTK_IOCTL("audio_ioctl - SOUND_PCM_READ_CHANNELS\n");
+			return put_user( os->audio_num_channels, (long *) arg);
+
+		case SNDCTL_DSP_SETFMT:
+			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_SETFMT\n");
+			if (get_user(val, (int *)arg))
+			{
+				return -EFAULT;
+			}
+			
+			/*
+			 * Do the set format for audio streaming.
+			 */
+			audio_set_format( os, val);
+			 
+			/*
+			 * If the audio hardware needs some setup, do it.
+			 */
+			if( state->set_hw_serial_format )
+			{
+				state->set_hw_serial_format( os->audio_format );
+			}
+
+			return put_user( val, (long *) arg);
+
+		case SNDCTL_DSP_GETFMTS:
+			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_GETFMTS\n");
+			return put_user( (AFMT_U8 | AFMT_S8 | AFMT_S16_LE | AFMT_U16_LE), 
+							 (long *) arg);
+
+		case OSS_GETVERSION:
+			DPRINTK_IOCTL("audio_ioctl - OSS_GETVERSION\n");
+			return put_user(SOUND_VERSION, (int *)arg);
+
+		case SNDCTL_DSP_GETBLKSIZE:
+			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_GETBLKSIZE\n");
+			if (file->f_mode & FMODE_WRITE)
+			{
+				return put_user(os->fragsize, (int *)arg);
+			}
+			return put_user(is->fragsize, (int *)arg);
+			
+		case SNDCTL_DSP_GETCAPS:
+			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_GETCAPS\n");
+			/*
+			 * Note that we are not capable of precisely reporting
+			 * dma position so we can't claim DSP_CAP_REALTIME.
+			 */
+		 /*	val = DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP; */
+			val = DSP_CAP_TRIGGER|DSP_CAP_MMAP;
+			if (is && os)
+			{
+				val |= DSP_CAP_DUPLEX;
+			}
+			return put_user(val, (int *)arg);
+
+		case SNDCTL_DSP_SETFRAGMENT:
+			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_SETFRAGMENT\n");
+			if (get_user(val, (long *) arg))
+			{
+				return -EFAULT;
+			}
+			
+			if (file->f_mode & FMODE_READ) 
+			{
+				int ret = audio_set_fragments(state, is, val);
+				if (ret < 0)
+				{
+					return ret;
+				}
+				
+				ret = put_user(ret, (int *)arg);
+				if (ret)
+				{
+					return ret;
+				}
+			}
+			
+			if (file->f_mode & FMODE_WRITE) 
+			{
+				int ret = audio_set_fragments(state, os, val);
+				if (ret < 0)
+				{
+					return ret;
+				}
+				
+				ret = put_user(ret, (int *)arg);
+				if (ret)
+				{
+					return ret;
+				}
+			}
+			return 0;
+
+		case SNDCTL_DSP_SYNC:
+			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_SYNC\n");
+			return audio_sync(file);
+
+		case SNDCTL_DSP_SETDUPLEX:
+			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_SETDUPLEX\n");
+			return 0;
+
+		case SNDCTL_DSP_POST:
+			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_POST\n");
+			return 0;
+
+		case SNDCTL_DSP_GETTRIGGER:
+			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_GETTRIGGER\n");
+			val = 0;
+			
+			if (file->f_mode & FMODE_READ && is->active && !is->stopped)
+				val |= PCM_ENABLE_INPUT;
+				
+			if (file->f_mode & FMODE_WRITE && os->active && !os->stopped)
+				val |= PCM_ENABLE_OUTPUT;
+				
+			return put_user(val, (int *)arg);
+
+		case SNDCTL_DSP_SETTRIGGER:
+			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_SETTRIGGER\n");
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+				
+			if (file->f_mode & FMODE_READ) 
+			{
+				if (val & PCM_ENABLE_INPUT) 
+				{
+					if (!is->active) 
+					{
+						if (!is->buffers && audio_allocate_buffers( state, is))
+							return -ENOMEM;
+						audio_prime_dma( state, is);
+					}
+					
+					if (is->stopped) 
+					{
+						is->stopped = 0;
+						os->active = 1;
+						audio_dma_start( state, is );
+					}
+				} 
+				else 
+				{
+					audio_dma_pause( state, is );
+					is->stopped = 1;
+					is->active = 0;
+				}
+			}
+			
+			if (file->f_mode & FMODE_WRITE) 
+			{
+				if (val & PCM_ENABLE_OUTPUT) 
+				{
+					if (!os->active) 
+					{
+						if (!os->buffers && audio_allocate_buffers( state, os))
+							return -ENOMEM;
+							
+						if (os->mapped)
+							audio_prime_dma( state, os );
+					}
+					
+					if (os->stopped) 
+					{
+						os->stopped = 0;
+						os->active = 1;
+						audio_dma_start( state, os );
+					}
+				} 
+				else 
+				{
+					audio_dma_pause( state, os );
+					os->stopped = 1;
+					os->active = 0;
+				}
+			}
+			return 0;
+
+		case SNDCTL_DSP_GETOPTR:
+		case SNDCTL_DSP_GETIPTR:
+		{
+			count_info inf = { 0, };
+			audio_stream_t *s = (cmd == SNDCTL_DSP_GETOPTR) ? os : is;
+			int bytecount, flags;
+			unsigned int buf_id, total;
+
+			DPRINTK_IOCTL("audio_ioctl - called SNDCTL_DSP_GETOPTR.\n");
+
+			if ((s == is && !(file->f_mode & FMODE_READ)) ||
+			    (s == os && !(file->f_mode & FMODE_WRITE)))
+			{
+				return -EINVAL;
+			}	
+				
+			if( !s->active ) 
+			{
+				return copy_to_user((void *)arg, &inf, sizeof(inf));
+			}
+			
+		   	save_flags_cli(flags);
+			
+			/*
+			 * Our DMA block doesn't update its buffer position regs unless
+			 * it is stopped.  So we don't calculate an offset.  All we know
+			 * is what buf is currently being played.
+			 */
+			if( (ep93xx_dma_get_position(s->dmahandles[0], &buf_id, &total, current_frac) == 0) &&
+				(buf_id != -1) )
+			{
+				inf.ptr = buf_id * s->fragsize;
+			} 
+
+			bytecount = s->bytecount;
+			s->getptrCount = s->bytecount;	/* so poll can tell if it changes */
+
+			inf.blocks = s->fragcount;
+			s->fragcount = 0;
+			
+			restore_flags(flags);
+			
+			if (bytecount < 0)
+			{
+				bytecount = 0;
+			}
+			
+			inf.bytes = bytecount;
+
+#ifdef I2S_SCALE_BUFFERS
+			inf.blocks = inf.blocks * os->requested_fragsize / os->nbfrags;
+			inf.bytes = inf.bytes * os->requested_fragsize / os->nbfrags;
+#endif				
+
+			return copy_to_user((void *)arg, &inf, sizeof(inf));
+		}
+			
+		case SNDCTL_DSP_GETOSPACE:
+		    {
+				/*
+				 * It looks like this code is assuming that nobody
+				 * is writing the device while calling this ioctl
+				 * which may be a reasonable assumption.  I hope.
+				 * Maybe.
+				 */
+				audio_buf_info inf = { 0, };
+				int i;
+
+				DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_GETOSPACE\n");
+
+				if (!(file->f_mode & FMODE_WRITE))
+					return -EINVAL;
+				if (!os->buffers && audio_allocate_buffers( state, os))
+					return -ENOMEM;
+					
+				for (i = 0; i < os->nbfrags; i++) 
+				{
+					/*
+					 * If the semaphore is 1, the buffer is available.
+					 */
+					if (atomic_read(&os->buffers[i].sem.count) > 0) 
+					{
+						if (os->buffers[i].size == 0)
+						{
+							inf.fragments++;
+						}
+						
+						inf.bytes += os->fragsize - os->buffers[i].size;
+					}
+				}
+				
+				inf.fragsize = os->fragsize;
+				inf.fragstotal = os->requested_fragsize;
+
+#ifdef I2S_SCALE_BUFFERS
+				inf.fragments = inf.fragments * os->requested_fragsize / os->nbfrags;
+				inf.bytes = inf.bytes * os->requested_fragsize / os->nbfrags;
+#endif				
+			
+				return copy_to_user((void *)arg, &inf, sizeof(inf));
+		    }
+
+		case SNDCTL_DSP_GETISPACE:
+		    {
+				audio_buf_info inf = { 0, };
+				int i;
+
+			 	DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_GETISPACE\n");
+
+				if (!(file->f_mode & FMODE_READ))
+					return -EINVAL;
+				if (!is->buffers && audio_allocate_buffers( state, is))
+					return -ENOMEM;
+					
+				for (i = 0; i < is->nbfrags; i++) 
+				{
+					if (atomic_read(&is->buffers[i].sem.count) > 0) 
+					{
+						if (is->buffers[i].size == is->fragsize)
+							inf.fragments++;
+						
+						inf.bytes += is->buffers[i].size;
+					}
+				}
+				
+				inf.fragsize = is->fragsize;
+				inf.fragstotal = is->requested_fragsize;
+
+#ifdef I2S_SCALE_BUFFERS
+				inf.fragments = inf.fragments * is->requested_fragsize / is->nbfrags;
+				inf.bytes = inf.bytes * is->requested_fragsize / is->nbfrags;
+#endif				
+
+				return copy_to_user((void *)arg, &inf, sizeof(inf));
+		    }
+
+		case SNDCTL_DSP_NONBLOCK:
+			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_NONBLOCK\n");
+			file->f_flags |= O_NONBLOCK;
+			return 0;
+
+		case SNDCTL_DSP_RESET:
+			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_RESET\n");
+			if (file->f_mode & FMODE_READ) 
+			{
+				audio_reset_buffers(state,is);
+			}
+			
+			if (file->f_mode & FMODE_WRITE) 
+			{
+				audio_reset_buffers(state,os);
+			}
+			return 0;
+
+		default:
+			/*
+			 * Let the client of this module handle the
+			 * non generic ioctls
+			 */
+			return state->client_ioctl(inode, file, cmd, arg);
+	}
+
+	return 0;
+}
+
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+	int i;
+	audio_state_t *state = (audio_state_t *)file->private_data;
+
+	DPRINTK("EP93xx - audio_release -- enter.  Write=%d  Read=%d\n",
+			((file->f_mode & FMODE_WRITE) != 0),
+			((file->f_mode & FMODE_READ) != 0)	);
+
+	down(&state->sem);
+
+	if (file->f_mode & FMODE_READ) 
+	{
+		audio_deallocate_buffers(state, state->input_stream);
+		
+		for( i=0 ; i < state->input_stream->NumDmaChannels ; i++ )
+		{
+			ep93xx_dma_free( state->input_stream->dmahandles[i] );
+		}
+		state->rd_ref = 0;
+	}
+
+	if (file->f_mode & FMODE_WRITE) 
+	{
+		/*
+		 * Wait for all our buffers to play.
+		 */
+		audio_sync(file);
+		
+		/*
+		 * audio_deallocate_buffers will pause and flush the dma
+		 * then deallocate the buffers.
+		 */
+		audio_deallocate_buffers(state, state->output_stream);
+		for( i=0 ; i < state->output_stream->NumDmaChannels ; i++ )
+		{
+			ep93xx_dma_free( state->output_stream->dmahandles[i] );
+		}
+		state->wr_ref = 0;
+	}
+
+	up(&state->sem);
+
+	DPRINTK("EP93xx - audio_release -- EXIT\n");
+
+	return 0;
+}
+
+/*
+ *  ep93xx_clear_stream_struct
+ *
+ *  Initialize stuff or at least clear it to zero for a stream structure.
+ */
+static void ep93xx_clear_stream_struct( audio_stream_t * stream )
+{
+	
+/*	stream->dmahandles[0] 		= 0;	// handles for dma driver instances 
+	stream->dmahandles[1] 		= 0;	// handles for dma driver instances
+	stream->dmahandles[2] 		= 0;	// handles for dma driver instances
+	stream->NumDmaChannels		= 0;	// 1, 2, or 3 DMA channels 
+	stream->TX_dma_ch_bitfield	= 0;	// which dma channels used for strm
+	stream->RX_dma_ch_bitfield	= 0;	// which dma channels used for strm 
+	stream->buffers				= 0;	// array of audio buffer structures
+	stream->dma_buffer			= 0;	// current buffer used by read/write
+	stream->dma_buffer_index	= 0;	// index for the pointer above... 
+	stream->fragsize			= AUDIO_FRAGSIZE_DEFAULT;	// fragment i.e. buffer size 
+	stream->nbfrags				= AUDIO_NBFRAGS_DEFAULT;	// nbr of fragments i.e. buffers
+	stream->requested_fragsize	= 0;	// This is ignored if 0 
+	stream->requested_nbfrags	= 0;	// This is ignored if 0 
+	stream->bytecount			= 0;	// nbr of processed bytes 
+	stream->getptrCount			= 0;	// value of bytecount last time 
+	 							  		/* anyone asked via GETxPTR 
+	stream->fragcount			= 0;	// nbr of fragment transitions
+	stream->mapped				= 0;	// mmap()'ed buffers 
+	stream->active				= 0;	// actually in progress
+	stream->stopped				= 0;	// might be active but stopped
+	stream->bFirstCaptureBuffer = 1;	// flag meaning there is junk in rx fifo
+	stream->buffered_bytes_to_play = 0;
+*/	
+	calculate_dma2usr_ratio_etc( stream );
+}
+
+
+/*
+ *	ep93xx_audio_attach
+ *
+ *  Initialize the state structure for read or write.  Allocate dma channels.
+ *  Default is for 2 channel (stereo) operation.
+ *
+ */
+int ep93xx_audio_attach
+(
+	struct inode *inode, 
+	struct file *file,
+	audio_state_t *state
+)
+{
+	int err;
+	audio_stream_t *os = state->output_stream;
+	audio_stream_t *is = state->input_stream;
+
+	DPRINTK("ep93xx_audio_attach -- enter.  Write=%d  Read=%d\n",
+			((file->f_mode & FMODE_WRITE) != 0),
+			((file->f_mode & FMODE_READ) != 0)	);
+
+	down(&state->sem);
+
+	/* access control */
+	if( ((file->f_mode & FMODE_WRITE) && !os) ||
+	    ((file->f_mode & FMODE_READ) && !is) )
+	{
+		up(&state->sem);
+		return -ENODEV;
+	}
+
+	if( ((file->f_mode & FMODE_WRITE) && state->wr_ref) ||
+	    ((file->f_mode & FMODE_READ) && state->rd_ref) )
+	{
+		up(&state->sem);
+		return -EBUSY;
+	}
+
+	/* 
+	 * Request DMA channels 
+	 */
+	if( file->f_mode & FMODE_WRITE ) 
+	{
+		DPRINTK("ep93xx_audio_attach -- FMODE_WRITE\n");
+		ep93xx_clear_stream_struct( os );
+		strncpy( os->devicename, "I2S_Tx1", MAX_DEVICE_NAME );
+	
+		err = ep93xx_dma_request( 	&os->dmahandles[0], 
+									os->devicename,
+                       				state->output_dma );
+		if (err)
+		{
+			up(&state->sem);
+			//DPRINTK("ep93xx_audio_attach -- EXIT ERROR dma request failed\n");
+			printk("ep93xx_audio_attach -- EXIT ERROR dma request failed\n");
+			return err;
+		}
+
+		err = ep93xx_dma_config( 	os->dmahandles[0], 
+									IGNORE_CHANNEL_ERROR, 
+									0,
+									audio_dma_tx_callback, 
+									(unsigned int)state );
+		if (err) 
+		{
+			ep93xx_dma_free( os->dmahandles[0] );
+			up(&state->sem);
+			//DPRINTK("ep93xx_audio_attach -- EXIT ERROR dma config failed\n");
+			printk("ep93xx_audio_attach -- EXIT ERROR dma config failed\n");
+			return err;
+		}
+
+		os->NumDmaChannels = 1;
+		state->wr_ref = 1;
+		audio_deallocate_buffers(state,os);
+
+		os->fragsize 	= AUDIO_FRAGSIZE_DEFAULT;
+		os->nbfrags  	= AUDIO_NBFRAGS_DEFAULT;
+
+		os->requested_fragsize = scale_to_expanded_samples( state, AUDIO_NBFRAGS_DEFAULT );
+
+		os->mapped 	= 0;
+
+		init_waitqueue_head(&os->wq);
+	}
+	
+	if( file->f_mode & FMODE_READ ) 
+	{
+		
+		DPRINTK("ep93xx_audio_attach -- FMODE_READ\n");
+		ep93xx_clear_stream_struct( is );
+		strncpy( is->devicename, "I2S_Rx1", MAX_DEVICE_NAME );
+	
+		err = ep93xx_dma_request( &is->dmahandles[0], is->devicename,state->input_dma);
+		if (err) 
+		{
+			up(&state->sem);
+			//DPRINTK("ep93xx_audio_attach -- EXIT ERROR dma request failed\n");
+			printk("ep93xx_audio_attach -- EXIT ERROR dma request failed\n");
+			return err;
+		}
+
+		err = ep93xx_dma_config( is->dmahandles[0], IGNORE_CHANNEL_ERROR, 0,
+					audio_dma_rx_callback, (unsigned int)state );
+		if (err) 
+		{
+			ep93xx_dma_free( is->dmahandles[0] );
+			up(&state->sem);
+			//DPRINTK("ep93xx_audio_attach -- EXIT ERROR dma config failed\n");
+			printk("ep93xx_audio_attach -- EXIT ERROR dma config failed\n");
+			return err;
+		}
+
+		is->NumDmaChannels = 1;
+		state->rd_ref = 1;
+		audio_deallocate_buffers(state,is);
+		is->fragsize 	= AUDIO_FRAGSIZE_DEFAULT;
+		is->nbfrags 	= AUDIO_NBFRAGS_DEFAULT;
+
+		os->requested_fragsize = scale_to_expanded_samples( state, AUDIO_NBFRAGS_DEFAULT );
+
+		is->mapped 	= 0;
+
+		init_waitqueue_head(&is->wq);
+	}
+
+	/*
+	 * Fill out the rest of the file_operations struct.
+	 */
+	file->private_data	= state;
+	file->f_op->release	= audio_release;
+	file->f_op->write	= audio_write;
+	file->f_op->read	= audio_read;
+	file->f_op->mmap	= audio_mmap;
+	file->f_op->poll	= audio_poll;
+	file->f_op->ioctl	= audio_ioctl;
+	file->f_op->llseek	= no_llseek;
+
+	up(&state->sem);
+
+	DPRINTK("ep93xx_audio_attach -- EXIT\n");
+
+	return 0;
+}
+
+EXPORT_SYMBOL(ep93xx_audio_attach);
 
-	/*
-	 * Each mono or stereo 8 bit sample going in results
-	 * in a two 32 bit samples (left & right) going out.  So
-	 * mono   = (1 byte  in)/(8 bytes out)
-	 * stereo = (2 bytes in)/(8 bytes out)
-	 */
-	if( mono )
-	{
-		return toCount0 / 8;
-	}
-	
-	return toCount0 / 4;
-}
-
-static int copy_from_user_U32
-(
-	audio_state_t *state,
-	const char *to, 
-	const char *from, 
-	int toCount 
-)
-{
-	if (copy_from_user( (char *)to, from, toCount)) 
-	{
-		return -EFAULT;
-	}
-
-	return toCount;
-}
-
-/*
- * Returns negative for error
- * Returns # of bytes transferred out of the from buffer
- * for success.
- */
-static int copy_n_convert
-(
-	audio_state_t *state,
-	const char *to, 
-	const char *from, 
-	int toCount 
-)
-{
-	int ret=0;
-	
-	if( toCount == 0 )
-	{
-		DPRINTK("copy_n_convert - nothing to copy!\n");
-	}
-
-	/*
-	 * Compact Mode means that each 32 bit word has both the
-	 * left and right sample in it.
-	 */
-	if( state->bCompactMode )
-	{
-		switch( state->output_stream->audio_format )
-		{
-			case AFMT_U8:
-				ret = copy_from_user_U8_CM( state, to, from, toCount );
-				break;
-
-			case AFMT_S16_LE:
-				ret = copy_from_user_S16_LE_CM( state, to, from, toCount );
-				break;
-		
-			case AFMT_S8:
-				ret = copy_from_user_S8_CM( state, to, from, toCount );
-				break;
-				
-			case AFMT_U16_LE:
-				ret = copy_from_user_U16_LE_CM( state, to, from, toCount );
-				break;
-				
-			case AFMT_S32_BLOCKED:
-			default:
-				break;
-		}
-	}
-	else
-	{
-		switch( state->output_stream->audio_format )
-		{
-			case AFMT_U8:
-				ret = copy_from_user_U8( state, to, from, toCount );
-				break;
-
-			case AFMT_S16_LE:
-				ret = copy_from_user_S16_LE( state, to, from, toCount );
-				break;
-		
-			case AFMT_S8:
-				ret = copy_from_user_S8( state, to, from, toCount );
-				break;
-				
-			case AFMT_U16_LE:
-				ret = copy_from_user_U16_LE( state, to, from, toCount );
-				break;
-				
-			case AFMT_S32_BLOCKED:
-			default:
-				ret = copy_from_user_U32( state, to, from, toCount );
-				break;
-		}
-	}
-	
-	if( ret == -EFAULT )
-	{
-		DPRINTK( "copy_n_convert: get_user failed!!!!\n" );
-	}
-	return ret;
-}
-
-static int audio_ioctl(struct inode *inode, struct file *file,
-		       uint cmd, ulong arg)
-{
-	audio_state_t *state = (audio_state_t *)file->private_data;
-	audio_stream_t *os = state->output_stream;
-	audio_stream_t *is = state->input_stream;
-	long val;
-
-	/* dispatch based on command */
-	switch (cmd) 
-	{
-		case SNDCTL_DSP_STEREO:
-			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_STEREO\n");
-			if (get_user(val, (int *)arg))
-			{
-				return -EFAULT;
-			}
-			if (val > 1 || val < 0)
-			{
-				return -EINVAL;
-			}
-			audio_set_num_channels( os, val + 1 );
-			return put_user( (os->audio_num_channels-1), (long *) arg);
-
-		case SNDCTL_DSP_CHANNELS:
-			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_CHANNELS\n");
-			if (get_user(val, (int *)arg))
-			{
-				return -EFAULT;
-			}
-			if (val > 2 || val < 0)
-			{
-				return -EINVAL;
-			}
-			audio_set_num_channels( os, val);
-			return put_user( os->audio_num_channels, (long *) arg);
-
-		case SOUND_PCM_READ_CHANNELS:
-			DPRINTK_IOCTL("audio_ioctl - SOUND_PCM_READ_CHANNELS\n");
-			return put_user( os->audio_num_channels, (long *) arg);
-
-		case SNDCTL_DSP_SETFMT:
-			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_SETFMT\n");
-			if (get_user(val, (int *)arg))
-			{
-				return -EFAULT;
-			}
-			
-			/*
-			 * Do the set format for audio streaming.
-			 */
-			audio_set_format( os, val);
-			 
-			/*
-			 * If the audio hardware needs some setup, do it.
-			 */
-			if( state->set_hw_serial_format )
-			{
-				state->set_hw_serial_format( os->audio_format );
-			}
-
-			return put_user( os->audio_format, (long *) arg);
-
-		case SNDCTL_DSP_GETFMTS:
-			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_GETFMTS\n");
-			return put_user( (AFMT_U8 | AFMT_S8 | AFMT_S16_LE | AFMT_U16_LE), 
-							 (long *) arg);
-
-		case OSS_GETVERSION:
-			DPRINTK_IOCTL("audio_ioctl - OSS_GETVERSION\n");
-			return put_user(SOUND_VERSION, (int *)arg);
-
-		case SNDCTL_DSP_GETBLKSIZE:
-			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_GETBLKSIZE\n");
-			if (file->f_mode & FMODE_WRITE)
-			{
-				return put_user(os->fragsize, (int *)arg);
-			}
-			return put_user(is->fragsize, (int *)arg);
-			
-		case SNDCTL_DSP_GETCAPS:
-			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_GETCAPS\n");
-			/*
-			 * Note that we are not capable of precisely reporting
-			 * dma position so we can't claim DSP_CAP_REALTIME.
-			 */
-		 /*	val = DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP; */
-			val = DSP_CAP_TRIGGER|DSP_CAP_MMAP;
-			if (is && os)
-			{
-				val |= DSP_CAP_DUPLEX;
-			}
-			return put_user(val, (int *)arg);
-
-		case SNDCTL_DSP_SETFRAGMENT:
-			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_SETFRAGMENT\n");
-			if (get_user(val, (long *) arg))
-			{
-				return -EFAULT;
-			}
-			
-			if (file->f_mode & FMODE_READ) 
-			{
-				int ret = audio_set_fragments(state, is, val);
-				if (ret < 0)
-				{
-					return ret;
-				}
-				
-				ret = put_user(ret, (int *)arg);
-				if (ret)
-				{
-					return ret;
-				}
-			}
-			
-			if (file->f_mode & FMODE_WRITE) 
-			{
-				int ret = audio_set_fragments(state, os, val);
-				if (ret < 0)
-				{
-					return ret;
-				}
-				
-				ret = put_user(ret, (int *)arg);
-				if (ret)
-				{
-					return ret;
-				}
-			}
-			return 0;
-
-		case SNDCTL_DSP_SYNC:
-			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_SYNC\n");
-			return audio_sync(file);
-
-		case SNDCTL_DSP_SETDUPLEX:
-			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_SETDUPLEX\n");
-			return 0;
-
-		case SNDCTL_DSP_POST:
-			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_POST\n");
-			return 0;
-
-		case SNDCTL_DSP_GETTRIGGER:
-			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_GETTRIGGER\n");
-			val = 0;
-			
-			if (file->f_mode & FMODE_READ && is->active && !is->stopped)
-				val |= PCM_ENABLE_INPUT;
-				
-			if (file->f_mode & FMODE_WRITE && os->active && !os->stopped)
-				val |= PCM_ENABLE_OUTPUT;
-				
-			return put_user(val, (int *)arg);
-
-		case SNDCTL_DSP_SETTRIGGER:
-			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_SETTRIGGER\n");
-			if (get_user(val, (int *)arg))
-				return -EFAULT;
-				
-			if (file->f_mode & FMODE_READ) 
-			{
-				if (val & PCM_ENABLE_INPUT) 
-				{
-					if (!is->active) 
-					{
-						if (!is->buffers && audio_allocate_buffers( state, is))
-							return -ENOMEM;
-						audio_prime_dma( state, is);
-					}
-					
-					if (is->stopped) 
-					{
-						is->stopped = 0;
-						os->active = 1;
-						audio_dma_start( state, is );
-					}
-				} 
-				else 
-				{
-					audio_dma_pause( state, is );
-					is->stopped = 1;
-					is->active = 0;
-				}
-			}
-			
-			if (file->f_mode & FMODE_WRITE) 
-			{
-				if (val & PCM_ENABLE_OUTPUT) 
-				{
-					if (!os->active) 
-					{
-						if (!os->buffers && audio_allocate_buffers( state, os))
-							return -ENOMEM;
-							
-						if (os->mapped)
-							audio_prime_dma( state, os );
-					}
-					
-					if (os->stopped) 
-					{
-						os->stopped = 0;
-						os->active = 1;
-						audio_dma_start( state, os );
-					}
-				} 
-				else 
-				{
-					audio_dma_pause( state, os );
-					os->stopped = 1;
-					os->active = 0;
-				}
-			}
-			return 0;
-
-		case SNDCTL_DSP_GETOPTR:
-		case SNDCTL_DSP_GETIPTR:
-		{
-			count_info inf = { 0, };
-			audio_stream_t *s = (cmd == SNDCTL_DSP_GETOPTR) ? os : is;
-			int bytecount, flags;
-			unsigned int buf_id, total;
-
-			DPRINTK_IOCTL("audio_ioctl - called SNDCTL_DSP_GETOPTR.\n");
-
-			if ((s == is && !(file->f_mode & FMODE_READ)) ||
-			    (s == os && !(file->f_mode & FMODE_WRITE)))
-			{
-				return -EINVAL;
-			}	
-				
-			if( !s->active ) 
-			{
-				return copy_to_user((void *)arg, &inf, sizeof(inf));
-			}
-			
-		   	save_flags_cli(flags);
-			
-			/*
-			 * Our DMA block doesn't update its buffer position regs unless
-			 * it is stopped.  So we don't calculate an offset.  All we know
-			 * is what buf is currently being played.
-			 */
-			if( (ep93xx_dma_get_position(s->dmahandles[0], &buf_id, &total) == 0) &&
-				(buf_id != -1) )
-			{
-				inf.ptr = buf_id * s->fragsize;
-			} 
-
-			bytecount = s->bytecount;
-			s->getptrCount = s->bytecount;	/* so poll can tell if it changes */
-
-			inf.blocks = s->fragcount;
-			s->fragcount = 0;
-			
-			restore_flags(flags);
-			
-			if (bytecount < 0)
-			{
-				bytecount = 0;
-			}
-			
-			inf.bytes = bytecount;
-
-#ifdef I2S_SCALE_BUFFERS
-			inf.blocks = inf.blocks * os->reportedfrags / os->nbfrags;
-			inf.bytes = inf.bytes * os->reportedfrags / os->nbfrags;
-#endif				
-
-			return copy_to_user((void *)arg, &inf, sizeof(inf));
-		}
-			
-		case SNDCTL_DSP_GETOSPACE:
-		    {
-				/*
-				 * It looks like this code is assuming that nobody
-				 * is writing the device while calling this ioctl
-				 * which may be a reasonable assumption.  I hope.
-				 * Maybe.
-				 */
-				audio_buf_info inf = { 0, };
-				int i;
-
-				DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_GETOSPACE\n");
-
-				if (!(file->f_mode & FMODE_WRITE))
-					return -EINVAL;
-				if (!os->buffers && audio_allocate_buffers( state, os))
-					return -ENOMEM;
-					
-				for (i = 0; i < os->nbfrags; i++) 
-				{
-					/*
-					 * If the semaphore is 1, the buffer is available.
-					 */
-					if (atomic_read(&os->buffers[i].sem.count) > 0) 
-					{
-						if (os->buffers[i].size == 0)
-						{
-							inf.fragments++;
-						}
-						
-						inf.bytes += os->fragsize - os->buffers[i].size;
-					}
-				}
-				
-				inf.fragsize = os->fragsize;
-				inf.fragstotal = os->reportedfrags;
-
-#ifdef I2S_SCALE_BUFFERS
-				inf.fragments = inf.fragments * os->reportedfrags / os->nbfrags;
-				inf.bytes = inf.bytes * os->reportedfrags / os->nbfrags;
-#endif				
-			
-				return copy_to_user((void *)arg, &inf, sizeof(inf));
-		    }
-
-		case SNDCTL_DSP_GETISPACE:
-		    {
-				audio_buf_info inf = { 0, };
-				int i;
-
-			 	DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_GETISPACE\n");
-
-				if (!(file->f_mode & FMODE_READ))
-					return -EINVAL;
-				if (!is->buffers && audio_allocate_buffers( state, is))
-					return -ENOMEM;
-					
-				for (i = 0; i < is->nbfrags; i++) 
-				{
-					if (atomic_read(&is->buffers[i].sem.count) > 0) 
-					{
-						if (is->buffers[i].size == is->fragsize)
-							inf.fragments++;
-						
-						inf.bytes += is->buffers[i].size;
-					}
-				}
-				
-				inf.fragsize = is->fragsize;
-				inf.fragstotal = is->reportedfrags;
-
-#ifdef I2S_SCALE_BUFFERS
-				inf.fragments = inf.fragments * is->reportedfrags / is->nbfrags;
-				inf.bytes = inf.bytes * is->reportedfrags / is->nbfrags;
-#endif				
-
-				return copy_to_user((void *)arg, &inf, sizeof(inf));
-		    }
-
-		case SNDCTL_DSP_NONBLOCK:
-			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_NONBLOCK\n");
-			file->f_flags |= O_NONBLOCK;
-			return 0;
-
-		case SNDCTL_DSP_RESET:
-			DPRINTK_IOCTL("audio_ioctl - SNDCTL_DSP_RESET\n");
-			if (file->f_mode & FMODE_READ) 
-			{
-				audio_reset_buffers(state,is);
-			}
-			
-			if (file->f_mode & FMODE_WRITE) 
-			{
-				audio_reset_buffers(state,os);
-			}
-			return 0;
-
-		default:
-			/*
-			 * Let the client of this module handle the
-			 * non generic ioctls
-			 */
-			return state->client_ioctl(inode, file, cmd, arg);
-	}
-
-	return 0;
-}
-
-
-static int audio_release(struct inode *inode, struct file *file)
-{
-	int i;
-	audio_state_t *state = (audio_state_t *)file->private_data;
-
-	DPRINTK("EP93xx - audio_release -- enter.  Write=%d  Read=%d\n",
-			((file->f_mode & FMODE_WRITE) != 0),
-			((file->f_mode & FMODE_READ) != 0)	);
-
-	down(&state->sem);
-
-	if (file->f_mode & FMODE_READ) 
-	{
-		audio_deallocate_buffers(state, state->input_stream);
-		
-		for( i=0 ; i < state->input_stream->NumDmaChannels ; i++ )
-		{
-			ep93xx_dma_free( state->input_stream->dmahandles[i] );
-		}
-		state->rd_ref = 0;
-	}
-
-	if (file->f_mode & FMODE_WRITE) 
-	{
-		/*
-		 * Wait for all our buffers to play.
-		 */
-		audio_sync(file);
-		
-		/*
-		 * audio_deallocate_buffers will pause and flush the dma
-		 * then deallocate the buffers.
-		 */
-		audio_deallocate_buffers(state, state->output_stream);
-		for( i=0 ; i < state->output_stream->NumDmaChannels ; i++ )
-		{
-			ep93xx_dma_free( state->output_stream->dmahandles[i] );
-		}
-		state->wr_ref = 0;
-	}
-
-	up(&state->sem);
-
-	DPRINTK("EP93xx - audio_release -- EXIT\n");
-
-	return 0;
-}
-
-/*
- *	ep93xx_audio_attach
- *
- *  Initialize the state structure for read or write.  Allocate dma channels.
- *  Default is for 2 channel (stereo) operation.
- *
- */
-int ep93xx_audio_attach
-(
-	struct inode *inode, 
-	struct file *file,
-	audio_state_t *state
-)
-{
-	int err;
-	audio_stream_t *os = state->output_stream;
-	audio_stream_t *is = state->input_stream;
-
-	DPRINTK("ep93xx_audio_attach -- enter.  Write=%d  Read=%d\n",
-			((file->f_mode & FMODE_WRITE) != 0),
-			((file->f_mode & FMODE_READ) != 0)	);
-
-	down(&state->sem);
-
-	/* access control */
-	if( ((file->f_mode & FMODE_WRITE) && !os) ||
-	    ((file->f_mode & FMODE_READ) && !is) )
-	{
-		up(&state->sem);
-		return -ENODEV;
-	}
-
-	if( ((file->f_mode & FMODE_WRITE) && state->wr_ref) ||
-	    ((file->f_mode & FMODE_READ) && state->rd_ref) )
-	{
-		up(&state->sem);
-		return -EBUSY;
-	}
-
-	/* 
-	 * Request DMA channels 
-	 */
-	if( file->f_mode & FMODE_WRITE ) 
-	{
-		DPRINTK("ep93xx_audio_attach -- FMODE_WRITE\n");
-		strncpy( os->devicename, "I2S_Tx1", MAX_DEVICE_NAME );
-	
-		err = ep93xx_dma_request( 	&os->dmahandles[0], 
-									os->devicename,
-                       				state->output_dma );
-		if (err)
-		{
-			up(&state->sem);
-			DPRINTK("ep93xx_audio_attach -- EXIT ERROR dma request failed\n");
-			return err;
-		}
-
-		err = ep93xx_dma_config( 	os->dmahandles[0], 
-									IGNORE_CHANNEL_ERROR, 
-									0,
-									audio_dma_tx_callback, 
-									(unsigned int)state );
-		if (err) 
-		{
-			ep93xx_dma_free( os->dmahandles[0] );
-			up(&state->sem);
-			DPRINTK("ep93xx_audio_attach -- EXIT ERROR dma config failed\n");
-			return err;
-		}
-
-		os->NumDmaChannels = 1;
-		state->wr_ref = 1;
-		audio_deallocate_buffers(state,os);
-
-		os->fragsize 	= AUDIO_FRAGSIZE_DEFAULT;
-		os->nbfrags  	= AUDIO_NBFRAGS_DEFAULT;
-
-		os->reportedfrags = scale_to_expanded_samples( state, AUDIO_NBFRAGS_DEFAULT );
-
-		os->mapped 	= 0;
-
-		init_waitqueue_head(&os->wq);
-	}
-	
-	if( file->f_mode & FMODE_READ ) 
-	{
-		strncpy( is->devicename, "I2S_Rx1", MAX_DEVICE_NAME );
-	
-		err = ep93xx_dma_request( 	&is->dmahandles[0], 
-									is->devicename,
-                       				state->input_dma);
-		if (err) 
-		{
-			up(&state->sem);
-			DPRINTK("ep93xx_audio_attach -- EXIT ERROR dma request failed\n");
-			return err;
-		}
-
-		err = ep93xx_dma_config( 	is->dmahandles[0], 
-									IGNORE_CHANNEL_ERROR, 
-									0,
-									audio_dma_rx_callback, 
-									(unsigned int)state );
-		if (err) 
-		{
-			ep93xx_dma_free( is->dmahandles[0] );
-			up(&state->sem);
-			DPRINTK("ep93xx_audio_attach -- EXIT ERROR dma config failed\n");
-			return err;
-		}
-
-		is->NumDmaChannels = 1;
-		state->rd_ref = 1;
-		audio_deallocate_buffers(state,is);
-		is->fragsize 	= AUDIO_FRAGSIZE_DEFAULT;
-		is->nbfrags 	= AUDIO_NBFRAGS_DEFAULT;
-
-		os->reportedfrags = scale_to_expanded_samples( state, AUDIO_NBFRAGS_DEFAULT );
-
-		is->mapped 	= 0;
-
-		init_waitqueue_head(&is->wq);
-	}
-
-	/*
-	 * Fill out the rest of the file_operations struct.
-	 */
-	file->private_data	= state;
-	file->f_op->release	= audio_release;
-	file->f_op->write	= audio_write;
-	file->f_op->read	= audio_read;
-	file->f_op->mmap	= audio_mmap;
-	file->f_op->poll	= audio_poll;
-	file->f_op->ioctl	= audio_ioctl;
-	file->f_op->llseek	= no_llseek;
-
-	up(&state->sem);
-
-	DPRINTK("ep93xx_audio_attach -- EXIT\n");
-
-	return 0;
-}
-
-EXPORT_SYMBOL(ep93xx_audio_attach);
-
-MODULE_DESCRIPTION("Common audio handling for the Cirrus EP93xx processor");
-MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Common audio handling for the Cirrus EP93xx processor");
+MODULE_LICENSE("GPL");
Index: drivers/sound/ep93xx-audio.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/sound/ep93xx-audio.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- drivers/sound/ep93xx-audio.h	22 Jul 2004 19:50:19 -0000	1.1.1.1
+++ drivers/sound/ep93xx-audio.h	29 Sep 2006 22:29:35 -0000	1.2
@@ -1,128 +1,178 @@
-/*
- * ep93xx-audio.h
- *
- * Common audio handling for the Cirrus Logic EP93xx
- *
- * Copyright (c) 2003 Cirrus Logic Corp.
- * Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
- *
- * Taken from sa1100-audio.h
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License.
- */
-
-/*
- * If this is #defined we will up the number of buffers we allocate so 
- * that if an application requests 2 16K buffers for playing 16 bit audio,
- * we will allocate 4 16K buffers so we have room to expand the 16 bit 
- * samples to 32 bit samples.
- *
- * We also do some scaling in the ioctls that have to do with reporting
- * position and amount of buffer available.
- */
-#define I2S_SCALE_BUFFERS
-
-/*
- *  Getting this from the rme96xx driver.  Has same numerical value
- *  as AC3.  Hmmmm.
- */
-#ifndef AFMT_S32_BLOCKED
-#define AFMT_S32_BLOCKED 0x0000400
-#endif
-
-#define EP93XX_DEFAULT_NUM_CHANNELS      2
-#define EP93XX_DEFAULT_FORMAT            AFMT_S16_LE
-#define EP93XX_DEFAULT_BIT_WIDTH         16
-
-/*
- * Buffer Management
- */
-
-typedef struct {
-
-	int 				size;			/* buffer size 						*/
-	int 				sent;			/* indicates that dma has the buf	*/
-	char 				*start;			/* points to actual buffer 			*/
-	dma_addr_t 			dma_addr;		/* physical buffer address 			*/
-	struct semaphore 	sem;			/* down before touching the buffer 	*/
-	int 				master;			/* owner for buffer allocation,    	*/
-										/* contains size when true 			*/
-	struct audio_stream_s 	*stream;	/* owning stream 					*/
-
-} audio_buf_t;
-
-#define MAX_DEVICE_NAME 20
-
-typedef struct audio_stream_s {
-
-	/* dma stuff */
-	int				dmahandles[3];		/* handles for dma driver instances */
-	char			devicename[MAX_DEVICE_NAME]; /* string - name of device */
-	int				NumDmaChannels;		/* 1, 2, or 3 DMA channels 			*/
-	
-	audio_buf_t 	*buffers;			/* array of audio buffer structures */
-	audio_buf_t 	*buf;				/* current buffer used by read/write */
-	u_int 			buf_idx;			/* index for the pointer above... 	*/
-	u_int 			fragsize;			/* fragment i.e. buffer size 		*/
-	u_int 			nbfrags;			/* nbr of fragments i.e. buffers 	*/
-	u_int			reportedfrags;		/* # of frags reported to app		*/
-	int 			bytecount;			/* nbr of processed bytes 			*/
-	int 			getptrCount;		/* value of bytecount last time 	*/
-										/* anyone asked via GETxPTR 		*/
-	int 			fragcount;			/* nbr of fragment transitions 		*/
-	ep93xx_dma_int_t dma_ch;			/* DMA channel ID 					*/
-	wait_queue_head_t 	wq;				/* for poll 						*/
-	int 			mapped:1;			/* mmap()'ed buffers 				*/
-	int 			active:1;			/* actually in progress 			*/
-	int 			stopped:1;			/* might be active but stopped 		*/
-
-	/*
-	 * Data about the file format that we are configured to play.
-	 */
-	long 				audio_num_channels;			/* Range: 1 to 6 */
-	long 				audio_format;
-	long 				audio_stream_bitwidth;		/* Range: 8, 16, 24, 32 */
-
-} audio_stream_t;
-
-
-/*
- * State structure for one instance
- */
-typedef struct {
-	audio_stream_t 		*output_stream;
-	audio_stream_t 		*input_stream;
-	
-	ep93xx_dma_dev_t 	output_dma;
-	ep93xx_dma_dev_t 	input_dma;
-	
-	char 				*output_id;
-	char 				*input_id;
-	
-	int 				rd_ref:1;			/* open reference for recording */
-	int 				wr_ref:1;			/* open reference for playback */
-	
-	/*
-	 * Pointers to init, shutdown, and ioctl functions.
-	 */
-	void 				(*hw_enable)(int input_or_output_stream);
-	void 				(*hw_disable)(int input_or_output_stream);
-	int 				(*client_ioctl)(struct inode *, struct file *, uint, ulong);
-	void				(*set_hw_serial_format)( long );
-
-	struct 				semaphore sem;		/* to protect against races in attach() */
-
-	/*
-	 * Data about how the audio controller and codec are set up.
-	 */
-	int					DAC_bit_width;		/* 16, 20, 24 bits */
-	int					bCompactMode;		/* set if 32bits = a stereo sample */
-
-} audio_state_t;
-
-/*
- * Functions exported by this module
- */
-extern int ep93xx_audio_attach( struct inode *inode, 
-								struct file *file,
-								audio_state_t *state);
+/*
+ * ep93xx-audio.h
+ *
+ * Common audio handling for the Cirrus Logic EP93xx
+ *
+ * Copyright (c) 2003 Cirrus Logic Corp.
+ * Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
+ *
+ * Taken from sa1100-audio.h
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License.
+ */
+
+/*
+ * If this is #defined we will up the number of buffers we allocate so 
+ * that if an application requests 2 16K buffers for playing 16 bit audio,
+ * we will allocate 4 16K buffers so we have room to expand the 16 bit 
+ * samples to 32 bit samples.
+ *
+ * We also do some scaling in the ioctls that have to do with reporting
+ * position and amount of buffer available.
+ */
+#define I2S_SCALE_BUFFERS
+
+/*
+ *  Getting this from the rme96xx driver.  Has same numerical value
+ *  as AC3.  Hmmmm.
+ */
+#ifndef AFMT_S32_BLOCKED
+#define AFMT_S32_BLOCKED 0x0000400
+#endif
+
+#define EP93XX_DEFAULT_NUM_CHANNELS      2
+#define EP93XX_DEFAULT_FORMAT            AFMT_S16_LE
+#define EP93XX_DEFAULT_BIT_WIDTH         16
+
+/*
+ * Buffer Management
+ */
+
+typedef struct {
+
+	int 				size;			/* buffer size 						*/
+	int 				sent;			/* indicates that dma has the buf	*/
+	char 				*start;			/* points to actual buffer 			*/
+	dma_addr_t 			dma_addr;		/* physical buffer address 			*/
+	int				owner;
+	struct semaphore 	sem;				/* down before touching the buffer 	*/
+	int 				master;			/* owner for buffer allocation,    	*/
+								/* contains size when true 			*/
+	int					num;		/* which out of nbfrags buffers is this?  */
+	struct audio_stream_s 	*stream;			/* owning stream 					*/
+
+} audio_buf_t;
+
+#define MAX_DEVICE_NAME 20
+
+typedef struct audio_stream_s {
+
+	/* dma stuff */
+	int				dmahandles[3];		/* handles for dma driver instances */
+	char			devicename[MAX_DEVICE_NAME]; /* string - name of device */
+	int				NumDmaChannels;		/* 1, 2, or 3 DMA channels 			*/
+	
+	audio_buf_t 	*buffers;			/* array of audio buffer structures */
+	audio_buf_t 	*dma_buffer;				/* current buffer used by read/write */
+	u_int 			dma_buffer_index;		/* index for the pointer above... 	*/
+	u_int 			fragsize;			/* fragment i.e. buffer size 		*/
+	u_int 			nbfrags;			/* nbr of fragments i.e. buffers 	*/
+	u_int			requested_fragsize;
+	u_int			requested_nbfrags;
+	int 			bytecount;			/* nbr of processed bytes 			*/
+	int 			getptrCount;		/* value of bytecount last time 	*/
+										/* anyone asked via GETxPTR 		*/
+	int 			fragcount;			/* nbr of fragment transitions 		*/
+	ep93xx_dma_int_t dma_ch;			/* DMA channel ID 					*/
+	wait_queue_head_t 	wq;				/* for poll 						*/
+	int 			mapped:1;			/* mmap()'ed buffers 				*/
+	int 			active:1;			/* actually in progress 			*/
+	int 			stopped:1;			/* might be active but stopped 		*/
+	u_int			TX_dma_ch_bitfield;	/* bit0 = txdma1, bit1= txdma2, etc */
+	u_int			RX_dma_ch_bitfield;	/* bit0 = rxdma1, bit1= rxdma2, etc */
+	int			bFirstCaptureBuffer;	/* flag meaning there is junk in rx fifo */
+
+	/*
+	 * Data about the file format that we are configured to play.
+	 */
+	long 				audio_num_channels;			/* Range: 1 to 6 */
+	long 				audio_format;
+	long 				audio_stream_bitwidth;		/* Range: 8, 16, 24, 32 */
+
+	long			sample_rate;		/* Fs in Hz. */
+
+	/*
+	 * Data about how the audio controller and codec are set up.
+	 */
+	int			    hw_bit_width;		/* width of dac or adc */
+	int			    bCompactMode;		/* set if 32bits == a stereo sample */
+
+	/*
+	 * dma2usr_ratio = dma sample size (8 or 4 bytes) / usr sample size (8,4,2,1)
+	 */
+	int				dma2usr_ratio;
+
+	/*
+	 * This provides a way of determining audio playback position that will not
+	 * overflow.  This is (size of buffers written) - (buffers we've gotten back)
+	 */
+	int				buffered_bytes_to_play;
+
+} audio_stream_t;
+
+
+/*
+ * No matter how many streams are going, we are using one hardware device.
+ * This struct describes that hardware.
+ */
+typedef struct {
+
+	ep93xx_dma_dev_t	txdmachannels[3];	/* all dma channels available for */
+	ep93xx_dma_dev_t	rxdmachannels[3];	/* this audio ctrlr */
+	
+	int					MaxTxDmaChannels;	/* number of tx and rx channels */
+	int					MaxRxDmaChannels;	/* number of tx and rx channels */
+	
+	/*
+	 * Pointers to init, shutdown, and ioctl functions.
+	 */
+	void 				(*hw_enable)( audio_stream_t * stream);
+	void 				(*hw_disable)(audio_stream_t * stream);
+	void				(*hw_clear_fifo)(audio_stream_t * stream);
+	int 				(*client_ioctl)(struct inode *, struct file *, uint, ulong);
+	void				(*set_hw_serial_format)( audio_stream_t *, long );
+
+	unsigned int		modcnt; 			/* count the # of volume writes */
+
+} audio_hw_t;
+
+/*
+ * State structure for one instance
+ */
+typedef struct {
+	audio_stream_t 		*output_stream;
+	audio_stream_t 		*input_stream;
+	
+	ep93xx_dma_dev_t 	output_dma;
+	ep93xx_dma_dev_t 	input_dma;
+	
+	char 				*output_id;
+	char 				*input_id;
+	
+	int 				rd_ref:1;			/* open reference for recording */
+	int 				wr_ref:1;			/* open reference for playback */
+	
+	/*
+	 * Pointers to init, shutdown, and ioctl functions.
+	 */
+	void 				(*hw_enable)(int input_or_output_stream);
+	void 				(*hw_disable)(int input_or_output_stream);
+	int 				(*client_ioctl)(struct inode *, struct file *, uint, ulong);
+	void				(*set_hw_serial_format)( long );
+
+	struct 				semaphore sem;		/* to protect against races in attach() */
+
+	/*
+	 * Data about how the audio controller and codec are set up.
+	 */
+	int					DAC_bit_width;		/* 16, 20, 24 bits */
+	int					bCompactMode;		/* set if 32bits = a stereo sample */
+
+} audio_state_t;
+
+/*
+ * Functions exported by this module
+ */
+extern int ep93xx_audio_attach( struct inode *inode, 
+								struct file *file,
+								audio_state_t *state);
Index: drivers/sound/ep93xx-i2s.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/sound/ep93xx-i2s.c,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- drivers/sound/ep93xx-i2s.c	22 Jul 2004 19:50:19 -0000	1.1.1.1
+++ drivers/sound/ep93xx-i2s.c	20 Feb 2007 23:55:10 -0000	1.3
@@ -1,1298 +1,1477 @@
-/*
- * Glue audio driver for the Cirrus EP93xx I2S Controller
- *
- * Copyright (c) 2003 Cirrus Logic Corp.
- * Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License.
- *
- * This file taken from h3600-uda1341.c
- *
- * History:
- *
- * 2000-05-21	Nicolas Pitre	Initial UDA1341 driver release.
- *
- * 2000-07-??	George France	Bitsy support.
- *
- * 2000-12-13	Deborah Wallach Fixed power handling for iPAQ/h3600
- *
- * 2001-06-03	Nicolas Pitre	Made this file a separate module, based on
- *				the former sa1100-uda1341.c driver.
- *
- * 2001-07-13	Nicolas Pitre	Fixes for all supported samplerates.
- *
- * 2003-04-04   Changes for Cirrus Logic EP93xx
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/fs.h>
-#include <linux/delay.h>
-#include <linux/pm.h>
-#include <linux/errno.h>
-#include <linux/sound.h>
-#include <linux/soundcard.h>
-
-#include <asm/semaphore.h>
-#include <asm/uaccess.h>
-#include <asm/hardware.h>
-#include <asm/dma.h>
-#include <asm/arch/ssp.h>
-#include <asm/arch/clocks.h>
-#include <asm/io.h>
-
-#include "ep93xx-audio.h"
-
-
-#undef DEBUG
-// #define DEBUG 1
-#ifdef DEBUG
-#define DPRINTK( x... )  printk( ##x )
-#else
-#define DPRINTK( x... )
-#endif
-
-
-#define AUDIO_NAME		"ep93xx_i2s"
-
-#define AUDIO_SAMPLE_RATE_DEFAULT	44100
-
-/*
- * OSS mixer volume levels are 16 bit with two 8 bit fields for left and right.
- * Each is in % of full.  So range is 0..100 for each of two channels.
- */ 
-#define AUDIO_DEFAULT_VOLUME ( (100<<8) | 100 )
-
-/*
- * Statics
- */
-static int giOSS_Volume = AUDIO_DEFAULT_VOLUME;
-static long audio_samplerate = AUDIO_SAMPLE_RATE_DEFAULT;
-static ulong actual_samplerate = AUDIO_SAMPLE_RATE_DEFAULT;
-static int giDAC_Volume_L = 0;
-static int giDAC_Volume_R = 0;
-static int giDAC_Mute = 0;
-
-static int audio_has_been_initialized = 0;
-static int audio_clocks_enabled_in_syscon = 0;
-static int SSP_Handle = -1;
-
-/* prototypes */
-static void ep93xx_audio_enable(void);
-static void ep93xx_audio_disable(void *dummy);
-static int ep93xx_calc_closest_freq
-(
-    ulong   ulPLLFreq, 
-    ulong   ulRequestedMClkFreq,
-    ulong * pulActualMClkFreq,
-    ulong * pulI2SDiv
-);
-static void ep93xx_set_samplerate(long lFrequency);
-static void ep93xx_init_i2s_controller( void );
-
-static void ep93xx_init_i2s_codec( void );
-static void ep93xx_mute_i2s_codec( void );
-static void ep93xx_automute_i2s_codec( void );
-static int ep93xx_set_volume( int val );
-
-static int mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg);
-
-static void ep93xx_audio_init(void *dummy);
-static int ep93xx_audio_ioctl(struct inode *inode, struct file *file,
-			     uint cmd, ulong arg);
-static int ep93xx_audio_open(struct inode *inode, struct file *file);
-
-static int __init ep93xx_i2s_init(void);
-static void __exit ep93xx_i2s_exit(void);
-
-static audio_stream_t output_stream =
-{
-	audio_num_channels: 	EP93XX_DEFAULT_NUM_CHANNELS,
-	audio_format: 			EP93XX_DEFAULT_FORMAT,
-	audio_stream_bitwidth: 	EP93XX_DEFAULT_BIT_WIDTH,
-};
-
-static audio_stream_t input_stream =
-{
-	audio_num_channels: 	EP93XX_DEFAULT_NUM_CHANNELS,
-	audio_format: 			EP93XX_DEFAULT_FORMAT,
-	audio_stream_bitwidth: 	EP93XX_DEFAULT_BIT_WIDTH,
-};
-
-static audio_state_t audio_state =
-{
-	output_stream:		&output_stream,
-	output_dma:			DMATx_I2S1,
-	output_id:			"I2S out",
-	
-	input_stream:		&input_stream,
-	input_dma:			DMARx_I2S1,
-	input_id:			"I2S in",
-	
-	client_ioctl:		ep93xx_audio_ioctl,
-	hw_enable:          0,
-	hw_disable:         0,
-	
-	set_hw_serial_format:  0,
-
-	sem:				__MUTEX_INITIALIZER(audio_state.sem),
-	
-	DAC_bit_width:		24,
-	bCompactMode:		0,
-};
-
-
-
-typedef struct {
-    ulong   ulTotalDiv;
-    ulong   ulI2SDiv;
-} DIV_TABLE;
-
-static const DIV_TABLE I2SDivTable[] =
-{
-    {   6, SYSCON_I2SDIV_PDIV_2  | (  2 & SYSCON_I2SDIV_MDIV_MASK) },  /* Fake entry for lower limit. */
-    {   8, SYSCON_I2SDIV_PDIV_2  | (  2 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  10, SYSCON_I2SDIV_PDIV_25 | (  2 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  12, SYSCON_I2SDIV_PDIV_3  | (  2 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  15, SYSCON_I2SDIV_PDIV_25 | (  3 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  16, SYSCON_I2SDIV_PDIV_2  | (  4 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  18, SYSCON_I2SDIV_PDIV_3  | (  3 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  20, SYSCON_I2SDIV_PDIV_25 | (  4 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  24, SYSCON_I2SDIV_PDIV_3  | (  4 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  25, SYSCON_I2SDIV_PDIV_25 | (  5 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  28, SYSCON_I2SDIV_PDIV_2  | (  7 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  30, SYSCON_I2SDIV_PDIV_3  | (  5 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  32, SYSCON_I2SDIV_PDIV_2  | (  8 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  35, SYSCON_I2SDIV_PDIV_25 | (  7 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  36, SYSCON_I2SDIV_PDIV_3  | (  6 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  40, SYSCON_I2SDIV_PDIV_25 | (  8 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  42, SYSCON_I2SDIV_PDIV_3  | (  7 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  44, SYSCON_I2SDIV_PDIV_2  | ( 11 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  45, SYSCON_I2SDIV_PDIV_25 | (  9 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  48, SYSCON_I2SDIV_PDIV_3  | (  8 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  50, SYSCON_I2SDIV_PDIV_25 | ( 10 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  52, SYSCON_I2SDIV_PDIV_2  | ( 13 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  54, SYSCON_I2SDIV_PDIV_3  | (  9 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  55, SYSCON_I2SDIV_PDIV_25 | ( 11 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  56, SYSCON_I2SDIV_PDIV_2  | ( 14 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  60, SYSCON_I2SDIV_PDIV_3  | ( 10 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  64, SYSCON_I2SDIV_PDIV_2  | ( 16 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  65, SYSCON_I2SDIV_PDIV_25 | ( 13 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  66, SYSCON_I2SDIV_PDIV_3  | ( 11 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  68, SYSCON_I2SDIV_PDIV_2  | ( 17 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  70, SYSCON_I2SDIV_PDIV_25 | ( 14 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  72, SYSCON_I2SDIV_PDIV_3  | ( 12 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  75, SYSCON_I2SDIV_PDIV_25 | ( 15 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  76, SYSCON_I2SDIV_PDIV_2  | ( 19 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  78, SYSCON_I2SDIV_PDIV_3  | ( 13 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  80, SYSCON_I2SDIV_PDIV_25 | ( 16 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  84, SYSCON_I2SDIV_PDIV_3  | ( 14 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  85, SYSCON_I2SDIV_PDIV_25 | ( 17 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  88, SYSCON_I2SDIV_PDIV_2  | ( 22 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  90, SYSCON_I2SDIV_PDIV_3  | ( 15 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  92, SYSCON_I2SDIV_PDIV_2  | ( 23 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  95, SYSCON_I2SDIV_PDIV_25 | ( 19 & SYSCON_I2SDIV_MDIV_MASK) },
-    {  96, SYSCON_I2SDIV_PDIV_3  | ( 16 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 100, SYSCON_I2SDIV_PDIV_25 | ( 20 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 102, SYSCON_I2SDIV_PDIV_3  | ( 17 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 104, SYSCON_I2SDIV_PDIV_2  | ( 26 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 105, SYSCON_I2SDIV_PDIV_25 | ( 21 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 108, SYSCON_I2SDIV_PDIV_3  | ( 18 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 110, SYSCON_I2SDIV_PDIV_25 | ( 22 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 112, SYSCON_I2SDIV_PDIV_2  | ( 28 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 114, SYSCON_I2SDIV_PDIV_3  | ( 19 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 115, SYSCON_I2SDIV_PDIV_25 | ( 23 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 116, SYSCON_I2SDIV_PDIV_2  | ( 29 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 120, SYSCON_I2SDIV_PDIV_3  | ( 20 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 124, SYSCON_I2SDIV_PDIV_2  | ( 31 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 125, SYSCON_I2SDIV_PDIV_25 | ( 25 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 126, SYSCON_I2SDIV_PDIV_3  | ( 21 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 128, SYSCON_I2SDIV_PDIV_2  | ( 32 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 130, SYSCON_I2SDIV_PDIV_25 | ( 26 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 132, SYSCON_I2SDIV_PDIV_3  | ( 22 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 135, SYSCON_I2SDIV_PDIV_25 | ( 27 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 136, SYSCON_I2SDIV_PDIV_2  | ( 34 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 138, SYSCON_I2SDIV_PDIV_3  | ( 23 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 140, SYSCON_I2SDIV_PDIV_25 | ( 28 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 144, SYSCON_I2SDIV_PDIV_3  | ( 24 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 145, SYSCON_I2SDIV_PDIV_25 | ( 29 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 148, SYSCON_I2SDIV_PDIV_2  | ( 37 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 150, SYSCON_I2SDIV_PDIV_3  | ( 25 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 152, SYSCON_I2SDIV_PDIV_2  | ( 38 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 155, SYSCON_I2SDIV_PDIV_25 | ( 31 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 156, SYSCON_I2SDIV_PDIV_3  | ( 26 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 160, SYSCON_I2SDIV_PDIV_25 | ( 32 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 162, SYSCON_I2SDIV_PDIV_3  | ( 27 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 164, SYSCON_I2SDIV_PDIV_2  | ( 41 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 165, SYSCON_I2SDIV_PDIV_25 | ( 33 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 168, SYSCON_I2SDIV_PDIV_3  | ( 28 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 170, SYSCON_I2SDIV_PDIV_25 | ( 34 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 172, SYSCON_I2SDIV_PDIV_2  | ( 43 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 174, SYSCON_I2SDIV_PDIV_3  | ( 29 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 175, SYSCON_I2SDIV_PDIV_25 | ( 35 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 176, SYSCON_I2SDIV_PDIV_2  | ( 44 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 180, SYSCON_I2SDIV_PDIV_3  | ( 30 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 184, SYSCON_I2SDIV_PDIV_2  | ( 46 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 185, SYSCON_I2SDIV_PDIV_25 | ( 37 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 186, SYSCON_I2SDIV_PDIV_3  | ( 31 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 188, SYSCON_I2SDIV_PDIV_2  | ( 47 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 190, SYSCON_I2SDIV_PDIV_25 | ( 38 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 192, SYSCON_I2SDIV_PDIV_3  | ( 32 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 195, SYSCON_I2SDIV_PDIV_25 | ( 39 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 196, SYSCON_I2SDIV_PDIV_2  | ( 49 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 198, SYSCON_I2SDIV_PDIV_3  | ( 33 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 200, SYSCON_I2SDIV_PDIV_25 | ( 40 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 204, SYSCON_I2SDIV_PDIV_3  | ( 34 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 205, SYSCON_I2SDIV_PDIV_25 | ( 41 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 208, SYSCON_I2SDIV_PDIV_2  | ( 52 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 210, SYSCON_I2SDIV_PDIV_3  | ( 35 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 212, SYSCON_I2SDIV_PDIV_2  | ( 53 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 215, SYSCON_I2SDIV_PDIV_25 | ( 43 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 216, SYSCON_I2SDIV_PDIV_3  | ( 36 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 220, SYSCON_I2SDIV_PDIV_25 | ( 44 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 222, SYSCON_I2SDIV_PDIV_3  | ( 37 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 224, SYSCON_I2SDIV_PDIV_2  | ( 56 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 225, SYSCON_I2SDIV_PDIV_25 | ( 45 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 228, SYSCON_I2SDIV_PDIV_3  | ( 38 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 230, SYSCON_I2SDIV_PDIV_25 | ( 46 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 232, SYSCON_I2SDIV_PDIV_2  | ( 58 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 234, SYSCON_I2SDIV_PDIV_3  | ( 39 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 235, SYSCON_I2SDIV_PDIV_25 | ( 47 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 236, SYSCON_I2SDIV_PDIV_2  | ( 59 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 240, SYSCON_I2SDIV_PDIV_3  | ( 40 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 244, SYSCON_I2SDIV_PDIV_2  | ( 61 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 245, SYSCON_I2SDIV_PDIV_25 | ( 49 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 246, SYSCON_I2SDIV_PDIV_3  | ( 41 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 248, SYSCON_I2SDIV_PDIV_2  | ( 62 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 250, SYSCON_I2SDIV_PDIV_25 | ( 50 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 252, SYSCON_I2SDIV_PDIV_3  | ( 42 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 255, SYSCON_I2SDIV_PDIV_25 | ( 51 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 256, SYSCON_I2SDIV_PDIV_2  | ( 64 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 258, SYSCON_I2SDIV_PDIV_3  | ( 43 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 260, SYSCON_I2SDIV_PDIV_25 | ( 52 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 264, SYSCON_I2SDIV_PDIV_3  | ( 44 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 265, SYSCON_I2SDIV_PDIV_25 | ( 53 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 268, SYSCON_I2SDIV_PDIV_2  | ( 67 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 270, SYSCON_I2SDIV_PDIV_3  | ( 45 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 272, SYSCON_I2SDIV_PDIV_2  | ( 68 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 275, SYSCON_I2SDIV_PDIV_25 | ( 55 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 276, SYSCON_I2SDIV_PDIV_3  | ( 46 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 280, SYSCON_I2SDIV_PDIV_25 | ( 56 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 282, SYSCON_I2SDIV_PDIV_3  | ( 47 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 284, SYSCON_I2SDIV_PDIV_2  | ( 71 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 285, SYSCON_I2SDIV_PDIV_25 | ( 57 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 288, SYSCON_I2SDIV_PDIV_3  | ( 48 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 290, SYSCON_I2SDIV_PDIV_25 | ( 58 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 292, SYSCON_I2SDIV_PDIV_2  | ( 73 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 294, SYSCON_I2SDIV_PDIV_3  | ( 49 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 295, SYSCON_I2SDIV_PDIV_25 | ( 59 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 296, SYSCON_I2SDIV_PDIV_2  | ( 74 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 300, SYSCON_I2SDIV_PDIV_3  | ( 50 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 304, SYSCON_I2SDIV_PDIV_2  | ( 76 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 305, SYSCON_I2SDIV_PDIV_25 | ( 61 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 306, SYSCON_I2SDIV_PDIV_3  | ( 51 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 308, SYSCON_I2SDIV_PDIV_2  | ( 77 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 310, SYSCON_I2SDIV_PDIV_25 | ( 62 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 312, SYSCON_I2SDIV_PDIV_3  | ( 52 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 315, SYSCON_I2SDIV_PDIV_25 | ( 63 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 316, SYSCON_I2SDIV_PDIV_2  | ( 79 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 318, SYSCON_I2SDIV_PDIV_3  | ( 53 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 320, SYSCON_I2SDIV_PDIV_25 | ( 64 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 324, SYSCON_I2SDIV_PDIV_3  | ( 54 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 325, SYSCON_I2SDIV_PDIV_25 | ( 65 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 328, SYSCON_I2SDIV_PDIV_2  | ( 82 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 330, SYSCON_I2SDIV_PDIV_3  | ( 55 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 332, SYSCON_I2SDIV_PDIV_2  | ( 83 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 335, SYSCON_I2SDIV_PDIV_25 | ( 67 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 336, SYSCON_I2SDIV_PDIV_3  | ( 56 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 340, SYSCON_I2SDIV_PDIV_25 | ( 68 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 342, SYSCON_I2SDIV_PDIV_3  | ( 57 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 344, SYSCON_I2SDIV_PDIV_2  | ( 86 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 345, SYSCON_I2SDIV_PDIV_25 | ( 69 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 348, SYSCON_I2SDIV_PDIV_3  | ( 58 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 350, SYSCON_I2SDIV_PDIV_25 | ( 70 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 352, SYSCON_I2SDIV_PDIV_2  | ( 88 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 354, SYSCON_I2SDIV_PDIV_3  | ( 59 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 355, SYSCON_I2SDIV_PDIV_25 | ( 71 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 356, SYSCON_I2SDIV_PDIV_2  | ( 89 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 360, SYSCON_I2SDIV_PDIV_3  | ( 60 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 364, SYSCON_I2SDIV_PDIV_2  | ( 91 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 365, SYSCON_I2SDIV_PDIV_25 | ( 73 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 366, SYSCON_I2SDIV_PDIV_3  | ( 61 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 368, SYSCON_I2SDIV_PDIV_2  | ( 92 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 370, SYSCON_I2SDIV_PDIV_25 | ( 74 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 372, SYSCON_I2SDIV_PDIV_3  | ( 62 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 375, SYSCON_I2SDIV_PDIV_25 | ( 75 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 376, SYSCON_I2SDIV_PDIV_2  | ( 94 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 378, SYSCON_I2SDIV_PDIV_3  | ( 63 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 380, SYSCON_I2SDIV_PDIV_25 | ( 76 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 384, SYSCON_I2SDIV_PDIV_3  | ( 64 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 385, SYSCON_I2SDIV_PDIV_25 | ( 77 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 388, SYSCON_I2SDIV_PDIV_2  | ( 97 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 390, SYSCON_I2SDIV_PDIV_3  | ( 65 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 392, SYSCON_I2SDIV_PDIV_2  | ( 98 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 395, SYSCON_I2SDIV_PDIV_25 | ( 79 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 396, SYSCON_I2SDIV_PDIV_3  | ( 66 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 400, SYSCON_I2SDIV_PDIV_25 | ( 80 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 402, SYSCON_I2SDIV_PDIV_3  | ( 67 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 404, SYSCON_I2SDIV_PDIV_2  | (101 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 405, SYSCON_I2SDIV_PDIV_25 | ( 81 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 408, SYSCON_I2SDIV_PDIV_3  | ( 68 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 410, SYSCON_I2SDIV_PDIV_25 | ( 82 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 412, SYSCON_I2SDIV_PDIV_2  | (103 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 414, SYSCON_I2SDIV_PDIV_3  | ( 69 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 415, SYSCON_I2SDIV_PDIV_25 | ( 83 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 416, SYSCON_I2SDIV_PDIV_2  | (104 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 420, SYSCON_I2SDIV_PDIV_3  | ( 70 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 424, SYSCON_I2SDIV_PDIV_2  | (106 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 425, SYSCON_I2SDIV_PDIV_25 | ( 85 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 426, SYSCON_I2SDIV_PDIV_3  | ( 71 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 428, SYSCON_I2SDIV_PDIV_2  | (107 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 430, SYSCON_I2SDIV_PDIV_25 | ( 86 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 432, SYSCON_I2SDIV_PDIV_3  | ( 72 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 435, SYSCON_I2SDIV_PDIV_25 | ( 87 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 436, SYSCON_I2SDIV_PDIV_2  | (109 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 438, SYSCON_I2SDIV_PDIV_3  | ( 73 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 440, SYSCON_I2SDIV_PDIV_25 | ( 88 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 444, SYSCON_I2SDIV_PDIV_3  | ( 74 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 445, SYSCON_I2SDIV_PDIV_25 | ( 89 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 448, SYSCON_I2SDIV_PDIV_2  | (112 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 450, SYSCON_I2SDIV_PDIV_3  | ( 75 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 452, SYSCON_I2SDIV_PDIV_2  | (113 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 455, SYSCON_I2SDIV_PDIV_25 | ( 91 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 456, SYSCON_I2SDIV_PDIV_3  | ( 76 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 460, SYSCON_I2SDIV_PDIV_25 | ( 92 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 462, SYSCON_I2SDIV_PDIV_3  | ( 77 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 464, SYSCON_I2SDIV_PDIV_2  | (116 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 465, SYSCON_I2SDIV_PDIV_25 | ( 93 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 468, SYSCON_I2SDIV_PDIV_3  | ( 78 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 470, SYSCON_I2SDIV_PDIV_25 | ( 94 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 472, SYSCON_I2SDIV_PDIV_2  | (118 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 474, SYSCON_I2SDIV_PDIV_3  | ( 79 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 475, SYSCON_I2SDIV_PDIV_25 | ( 95 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 476, SYSCON_I2SDIV_PDIV_2  | (119 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 480, SYSCON_I2SDIV_PDIV_3  | ( 80 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 484, SYSCON_I2SDIV_PDIV_2  | (121 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 485, SYSCON_I2SDIV_PDIV_25 | ( 97 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 486, SYSCON_I2SDIV_PDIV_3  | ( 81 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 488, SYSCON_I2SDIV_PDIV_2  | (122 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 490, SYSCON_I2SDIV_PDIV_25 | ( 98 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 492, SYSCON_I2SDIV_PDIV_3  | ( 82 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 495, SYSCON_I2SDIV_PDIV_25 | ( 99 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 496, SYSCON_I2SDIV_PDIV_2  | (124 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 498, SYSCON_I2SDIV_PDIV_3  | ( 83 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 500, SYSCON_I2SDIV_PDIV_25 | (100 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 504, SYSCON_I2SDIV_PDIV_3  | ( 84 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 505, SYSCON_I2SDIV_PDIV_25 | (101 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 508, SYSCON_I2SDIV_PDIV_2  | (127 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 510, SYSCON_I2SDIV_PDIV_3  | ( 85 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 515, SYSCON_I2SDIV_PDIV_25 | (103 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 516, SYSCON_I2SDIV_PDIV_3  | ( 86 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 520, SYSCON_I2SDIV_PDIV_25 | (104 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 522, SYSCON_I2SDIV_PDIV_3  | ( 87 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 525, SYSCON_I2SDIV_PDIV_25 | (105 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 528, SYSCON_I2SDIV_PDIV_3  | ( 88 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 530, SYSCON_I2SDIV_PDIV_25 | (106 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 534, SYSCON_I2SDIV_PDIV_3  | ( 89 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 535, SYSCON_I2SDIV_PDIV_25 | (107 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 540, SYSCON_I2SDIV_PDIV_3  | ( 90 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 545, SYSCON_I2SDIV_PDIV_25 | (109 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 546, SYSCON_I2SDIV_PDIV_3  | ( 91 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 550, SYSCON_I2SDIV_PDIV_25 | (110 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 552, SYSCON_I2SDIV_PDIV_3  | ( 92 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 555, SYSCON_I2SDIV_PDIV_25 | (111 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 558, SYSCON_I2SDIV_PDIV_3  | ( 93 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 560, SYSCON_I2SDIV_PDIV_25 | (112 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 564, SYSCON_I2SDIV_PDIV_3  | ( 94 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 565, SYSCON_I2SDIV_PDIV_25 | (113 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 570, SYSCON_I2SDIV_PDIV_3  | ( 95 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 575, SYSCON_I2SDIV_PDIV_25 | (115 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 576, SYSCON_I2SDIV_PDIV_3  | ( 96 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 580, SYSCON_I2SDIV_PDIV_25 | (116 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 582, SYSCON_I2SDIV_PDIV_3  | ( 97 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 585, SYSCON_I2SDIV_PDIV_25 | (117 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 588, SYSCON_I2SDIV_PDIV_3  | ( 98 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 590, SYSCON_I2SDIV_PDIV_25 | (118 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 594, SYSCON_I2SDIV_PDIV_3  | ( 99 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 595, SYSCON_I2SDIV_PDIV_25 | (119 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 600, SYSCON_I2SDIV_PDIV_3  | (100 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 605, SYSCON_I2SDIV_PDIV_25 | (121 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 606, SYSCON_I2SDIV_PDIV_3  | (101 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 610, SYSCON_I2SDIV_PDIV_25 | (122 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 612, SYSCON_I2SDIV_PDIV_3  | (102 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 615, SYSCON_I2SDIV_PDIV_25 | (123 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 618, SYSCON_I2SDIV_PDIV_3  | (103 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 620, SYSCON_I2SDIV_PDIV_25 | (124 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 624, SYSCON_I2SDIV_PDIV_3  | (104 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 625, SYSCON_I2SDIV_PDIV_25 | (125 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 630, SYSCON_I2SDIV_PDIV_3  | (105 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 635, SYSCON_I2SDIV_PDIV_25 | (127 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 636, SYSCON_I2SDIV_PDIV_3  | (106 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 642, SYSCON_I2SDIV_PDIV_3  | (107 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 648, SYSCON_I2SDIV_PDIV_3  | (108 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 654, SYSCON_I2SDIV_PDIV_3  | (109 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 660, SYSCON_I2SDIV_PDIV_3  | (110 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 666, SYSCON_I2SDIV_PDIV_3  | (111 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 672, SYSCON_I2SDIV_PDIV_3  | (112 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 678, SYSCON_I2SDIV_PDIV_3  | (113 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 684, SYSCON_I2SDIV_PDIV_3  | (114 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 690, SYSCON_I2SDIV_PDIV_3  | (115 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 696, SYSCON_I2SDIV_PDIV_3  | (116 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 702, SYSCON_I2SDIV_PDIV_3  | (117 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 708, SYSCON_I2SDIV_PDIV_3  | (118 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 714, SYSCON_I2SDIV_PDIV_3  | (119 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 720, SYSCON_I2SDIV_PDIV_3  | (120 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 726, SYSCON_I2SDIV_PDIV_3  | (121 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 732, SYSCON_I2SDIV_PDIV_3  | (122 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 738, SYSCON_I2SDIV_PDIV_3  | (123 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 744, SYSCON_I2SDIV_PDIV_3  | (124 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 750, SYSCON_I2SDIV_PDIV_3  | (125 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 756, SYSCON_I2SDIV_PDIV_3  | (126 & SYSCON_I2SDIV_MDIV_MASK) },
-    { 762, SYSCON_I2SDIV_PDIV_3  | (127 & SYSCON_I2SDIV_MDIV_MASK) }
-};
-
-/*
- * When we get to the multichannel case the pre-fill and enable code
- * will go to the dma driver's start routine.
- */
-static void ep93xx_audio_enable(void)
-{
-    int i;
-	
-	DPRINTK("ep93xx_audio_enable - enter\n");
-
-	/*
-	 * Disable the channels, disable the i2s controller to clear out the 
-	 * fifo.  Disabling the controller will clear the flags	in i2s_csr.
-	 */
-	outl( 0, I2STX0En );
-	outl( 0, I2STX1En );
-	outl( 0, I2STX2En );
-	outl( 0, I2SRX0En );
-	outl( 0, I2SRX1En );
-	outl( 0, I2SRX2En );
-	 
-	/*
-	 * Disable the i2s controller to clear out the fifo and clear
-	 * the flags in i2s_csr.
-	 */
-	outl( 0, I2SGlCtrl );
-
-	/*
-	 * Enable the i2s controller.  It is important not to just
-	 * leave the i2s controller enabled and skip this step. 
-	 */
-	outl( 1, I2SGlCtrl );
-
-	/*
-	 * Prefill the tx fifo with zeros.
-	 */
-	if( (inl(I2SGlSts) & TX0_FIFO_EMPTY) != TX0_FIFO_EMPTY )
-	{
-		DPRINTK( "ep93xx_audio_enable - Tx fifos not empty.  Unable to prefill.\n" );
-	}
-	else
-	{
-	    for( i = 0 ; i < 8 ; i++ )
-	    {
-			outl( 0, I2STX0Lft );
-			outl( 0, I2STX0Rt );
-	    }
-	}
-
-    /*
-	 * Enable the tx channel.
-	 */
-	outl( 1, I2STX0En );
-
-    /*
-	 * Must delay >= 2 sample periods after enabling the channel before 
-	 * writing any more to the FIFO.
-	 */
-	udelay( (3 * 1000 * 1000) / audio_samplerate );
-
-	DPRINTK("ep93xx_audio_enable - EXIT\n");
-
-}
-
-static void ep93xx_audio_disable(void *dummy)
-{
-	DPRINTK("ep93xx_audio_disable - enter\n");
-
-	audio_has_been_initialized = 0;
-	ep93xx_mute_i2s_codec();
-
-	/*
-	 * Disable the channels, disable the i2s controller to clear out the 
-	 * fifo.  Disabling the controller will clear the flags	in i2s_csr.
-	 */
-	outl( 0, I2STX0En );
-	outl( 0, I2STX1En );
-	outl( 0, I2STX2En );
-	outl( 0, I2SRX0En );
-	outl( 0, I2SRX1En );
-	outl( 0, I2SRX2En );
-	outl( 0, I2SGlCtrl );
-
-	DPRINTK("ep93xx_audio_disable - EXIT\n");
-}
-
-
-/*
- * ep93xx_calc_closest_freq
- * 
- *   ulPLLFreq           -  PLL output Frequency (Mhz)
- *   ulRequestedMClkFreq -  Requested Video Clock Frequency.
- *   pulActualMClkFreq   -  Returned Actual Video Rate.
- *   pulI2SDiv           -  Video Divider register.
- *
- *   Return            0 - Failure
- *                     1 - Success
- */
-static int ep93xx_calc_closest_freq
-(
-    ulong   ulPLLFreq, 
-    ulong   ulRequestedMClkFreq,
-    ulong * pulActualMClkFreq,
-    ulong * pulI2SDiv
-)
-{
-    
-    ulong   ulLower;
-    ulong   ulUpper;
-    ulong   ulDiv;
-    int     x;
-
-    /*
-     * Calculate the closest divisor.
-     */
-    ulDiv =  (ulPLLFreq * 2)/ ulRequestedMClkFreq;
-
-    for(x = 1; x < sizeof(I2SDivTable)/sizeof(DIV_TABLE); x++)
-    {
-
-        /*
-         * Calculate the next greater and lower value.
-         */
-        ulLower = I2SDivTable[x - 1].ulTotalDiv;     
-        ulUpper = I2SDivTable[x].ulTotalDiv;     
-
-        /*
-         * Check to see if it is in between the two values.
-         */
-        if(ulLower <= ulDiv && ulDiv < ulUpper)
-        {
-            break;
-        }
-    }
-
-    /*
-     * Return if we did not find a divisor.
-     */
-    if(x == sizeof(I2SDivTable)/sizeof(DIV_TABLE))
-    {
-		DPRINTK("couldn't find a divisor.\n");
-
-        *pulActualMClkFreq  = 0;
-        *pulI2SDiv          = 0;
-        return -1;
-    }
-
-    /*
-     * See which is closer, the upper or the lower case.
-     */
-    if(ulUpper * ulRequestedMClkFreq - ulPLLFreq * 2 >  
-      ulPLLFreq * 2 - ulLower * ulRequestedMClkFreq)
-    {
-        x-=1;
-    }
-    *pulActualMClkFreq  = (ulPLLFreq * 2)/ I2SDivTable[x].ulTotalDiv;
-    *pulI2SDiv          = I2SDivTable[x].ulI2SDiv;
-
-    return 0;
-}
-
-/*
- * SetSampleRate
- * 		disables i2s channels and sets up i2s divisors
- * 		in syscon for the requested sample rate.
- *
- * lFrequency       - Sample Rate in Hz
- */
-static void ep93xx_set_samplerate(long lFrequency)
-{
-    ulong ulRequestedMClkFreq;
-    ulong ulMClkFreq1, ulMClkFreq2;
-    ulong ulI2SDiv, ulI2SDiv1, ulI2SDiv2, ulI2SDIV;
-    
-	/*
-	 * Clock ratios: MCLK to SCLK and SCLK to LRCK
-	 */
-	ulong ulM2SClock  = 4;
-    ulong ulS2LRClock = 64;
-
-	DPRINTK( "ep93xx_set_samplerate = %d\n", (int)lFrequency );
-
-    ulRequestedMClkFreq = ( lFrequency * ulM2SClock * ulS2LRClock);
-
-
-    ep93xx_calc_closest_freq
-    (
-        PLL1_CLOCK, 
-        ulRequestedMClkFreq,
-        &ulMClkFreq1,
-        &ulI2SDiv1
-    );
-    ep93xx_calc_closest_freq
-    (
-        PLL2_CLOCK, 
-        ulRequestedMClkFreq,
-        &ulMClkFreq2,
-        &ulI2SDiv2
-    );
-
-    /*
-     * See which is closer, MClk rate 1 or MClk rate 2.
-     */
-    if(abs(ulMClkFreq1 - ulRequestedMClkFreq) < abs(ulMClkFreq2 -ulRequestedMClkFreq))
-    {
-        ulI2SDiv    = ulI2SDiv1;
-        actual_samplerate = ulMClkFreq1/ (ulM2SClock * ulS2LRClock);
-		DPRINTK( "ep93xx_set_samplerate - using PLL1\n" );
-    }
-    else
-    {
-        ulI2SDiv = ulI2SDiv2 | SYSCON_I2SDIV_PSEL;
-        actual_samplerate = ulMClkFreq1 / (ulM2SClock * ulS2LRClock);
-		DPRINTK( "ep93xx_set_samplerate - using PLL2\n" );
-    }
-
-    /*
-     * Calculate the new I2SDIV register value and write it out.
-     */
-    ulI2SDIV = ulI2SDiv | SYSCON_I2SDIV_SENA |  SYSCON_I2SDIV_ORIDE | 
-				SYSCON_I2SDIV_SPOL| SYSCON_I2SDIV_LRDIV_64 | 
-				SYSCON_I2SDIV_SDIV | SYSCON_I2SDIV_MENA | SYSCON_I2SDIV_ESEL;
-
-    SysconSetLocked(SYSCON_I2SDIV, ulI2SDIV);
-	audio_clocks_enabled_in_syscon = 1;
-
-}
-
-/*
- * BringUpI2S
- *
- * This routine sets up the I2S Controller.
- */
-static void ep93xx_init_i2s_controller( void )
-{
-	unsigned int uiDEVCFG;
-	
-	DPRINTK("ep93xx_init_i2s_controller - enter\n");
-	
-    /*
-     * Configure the multiplexed Ac'97 pins to be I2S and 
-     * EGPIO4 and EGPIO6 to be SDOUT's for the second and third I2S
-     * controller channel.
-     */
-	uiDEVCFG = inl(SYSCON_DEVCFG);
-	
-	uiDEVCFG |= SYSCON_DEVCFG_I2SonAC97 | 
-		        SYSCON_DEVCFG_A1onG |
-		        SYSCON_DEVCFG_A2onG;
-	    
-    SysconSetLocked(SYSCON_DEVCFG, uiDEVCFG);
-
-    /* ------------- Configure I2S Tx channel ---------------------*/
-
-    /*
-	 * Justify Left, MSB first	
-     */
-	outl( 0, I2STXLinCtrlData );
-
-	/*
-	 * WL = 24 bits 
-	 */
-	outl( 1, I2STXWrdLen );
-
-	/*
-     * Set the I2S control block to master mode.
-	 * Tx bit clk rate determined by word legnth 
-	 * Do gate off last 8 clks (24 bit samples in a 32 bit field)
-	 * LRclk = I2S timing; LRck = 0 for left
-	 */
-	outl( (i2s_txcc_mstr | i2s_txcc_trel), I2STxClkCfg );
-	
-    /* ------------- Configure I2S rx channel --------------------- */
-    
-    /*
-     * First, clear all config bits.
-     */
-	outl( 0, I2SRXLinCtrlData );
-
-	/*
-	 * WL = 24 bits 
-	 */
-	outl( 1, I2SRXWrdLen );
-
-	/* 
-     * Set the I2S control block to master mode.
-	 * Rx bit clk rate determined by word legnth 
-	 * Do gate off last 8 clks 
-	 * LRclk = I2S timing; LRck = 0 for left 
-	 */
-	outl( (i2s_rxcc_mstr | i2s_rxcc_rrel), I2SRxClkCfg );
-    
-	DPRINTK("ep93xx_init_i2s_controller - EXIT\n");
-}
-
-/*
- * ep93xx_init_i2s_codec
- *
- * Note that codec must be getting i2s clocks for any codec
- * register writes to work.
- */
-static void ep93xx_init_i2s_codec( void )
-{
-	DPRINTK("ep93xx_init_i2s_codec - enter\n");
-    
-	if( !audio_clocks_enabled_in_syscon )
-	{
-		DPRINTK("ep93xx_init_i2s_codec - EXIT - clocks not enabled\n");
-		return;
-	}
-
-#ifdef CONFIG_CODEC_CS4271
-
-    /*
-     * Write to the control port, setting the enable control port bit
-     * so that we can write to the control port.  OK?
-     */
-	SSPDriver->Write( SSP_Handle, 7, 0x02 );
-
-    /*
-     * Select slave, 24Bit I2S serial mode
-     */
-	SSPDriver->Write( SSP_Handle, 1, 0x01 );
-
-    /*
-     * Set AMUTE (auto-mute) bit.
-     */
-	SSPDriver->Write( SSP_Handle, 2, 0x00 );
-
-    /*
-     * Set SOFT  bit.
-     */
-	//SSPDriver->Write( SSP_Handle, 3, 0x29 );
-
-#else // CONFIG_CODEC_CS4228A
-
-    /*
-     * Nothing needs to happen here
-     */
-
-#endif
-    
-	DPRINTK("ep93xx_init_i2s_codec - EXIT\n");
-}
-
-/*
- * ep93xx_mute_i2s_codec
- *
- * Note that codec must be getting i2s clocks for any codec
- * register writes to work.
- */
-static void ep93xx_mute_i2s_codec( void )
-{
-	DPRINTK("ep93xx_mute_i2s_codec - enter\n");
-
-	if( !audio_clocks_enabled_in_syscon )
-	{
-		DPRINTK("ep93xx_mute_i2s_codec - EXIT - clocks not enabled\n");
-		return;
-	}
-
-#ifdef CONFIG_CODEC_CS4271
-
-    /*
-     * Set the driver's mute flag and use the set_volume routine
-     * to write the current volume out with the mute bit set.
-     */
-    giDAC_Mute = 1;
-    ep93xx_set_volume( giOSS_Volume );
-
-#else // CONFIG_CODEC_CS4228A
-
-    /*
-     * Mute the MUTEC pin.
-     */
-	SSPDriver->Write( SSP_Handle, 5, 0x80 );
-
-    /*
-     * Mute the DACs
-     */
-	SSPDriver->Write( SSP_Handle, 4, 0xFC );
-    
-    giDAC_Mute = 1;
-
-#endif
-
-	DPRINTK("ep93xx_mute_i2s_codec - EXIT\n");
-}
-
-/*
- * ep93xx_automute_i2s_codec
- *
- * Note that codec must be getting i2s clocks for any codec
- * register writes to work.
- */
-static void ep93xx_automute_i2s_codec( void )
-{
-	DPRINTK("ep93xx_automute_i2s_codec - enter\n");
-
-	if( !audio_clocks_enabled_in_syscon )
-	{
-		DPRINTK("ep93xx_automute_i2s_codec - EXIT - clocks not enabled\n");
-		return;
-	}
-
-#ifdef CONFIG_CODEC_CS4271
-
-    /*
-     * The automute bit is set by default for the CS4271.
-     * Clear the driver's mute flag and use the set_volume routine
-     * to write the current volume out with the mute bit cleared.
-     */
-    giDAC_Mute = 0;
-    ep93xx_set_volume( giOSS_Volume );
-
-#else // CONFIG_CODEC_CS4228A
-
-	SSPDriver->Write( SSP_Handle, 4, 0 );
-
-    /*
-     * Unmute the DACs
-     */
-	SSPDriver->Write( SSP_Handle, 5, 0xF0 );
-
-    /*
-     * Unmute the MUTEC pin, turn on automute.
-     */
-	SSPDriver->Write( SSP_Handle, 5, 0x40 );
-    
-    giDAC_Mute = 0;
-    
-#endif
-
-	DPRINTK("ep93xx_automute_i2s_codec - EXIT\n");
-}
-
-static int ep93xx_set_volume( int iOSS_Volume )
-{
-    int iOSS_Volume_L, iOSS_Volume_R, iMute=0;
-    
-	if( !audio_clocks_enabled_in_syscon )
-	{
-		return -EINVAL;
-	}
-    
-	giOSS_Volume = iOSS_Volume;
-
-    iOSS_Volume_L =  iOSS_Volume & 0x00ff;
-    iOSS_Volume_R = (iOSS_Volume & 0xff00) >> 8;
-
-	if (iOSS_Volume_L > 100)
-		iOSS_Volume_L = 100;
-	if (iOSS_Volume_L < 0)
-		iOSS_Volume_L = 0;
-	if (iOSS_Volume_R > 100)
-		iOSS_Volume_R = 100;
-	if (iOSS_Volume_R < 0)
-		iOSS_Volume_R = 0;
-
-#ifdef CONFIG_CODEC_CS4271
-    /*
-     * CS4271 volume is 0 to -127 dB in 1 dB increments.
-     * So scale the volume so a range of 0 to 100 becomes 127 to 0.
-     */
-    giDAC_Volume_L = 127 - ((iOSS_Volume_L * 127) / 100);
-    giDAC_Volume_R = 127 - ((iOSS_Volume_R * 127) / 100);
-
-    if( giDAC_Mute )
-    {
-        iMute = 0x80;
-    }
-
-    /*
-     * Write the volume for DAC1 and DAC2 (reg 4 and 5)
-     */
-	SSPDriver->Write( SSP_Handle, 4, (giDAC_Volume_L | iMute) );
-	SSPDriver->Write( SSP_Handle, 5, (giDAC_Volume_R | iMute) );
-
-#else // CONFIG_CODEC_CS4228A
-
-	/*
-	 * CS4228A DAC attenuation is 0 to 90.5 dB in 0.5 dB steps so scale
-	 * volume so that a value of 0 to 100 is scaled to 181 to 0
-	 */
-    giDAC_Volume_L = 181 - ((iOSS_Volume_L * 181) / 100);
-    giDAC_Volume_R = 181 - ((iOSS_Volume_R * 181) / 100);
-
-    /*
-     * Write the volume for DAC1 and DAC2 (reg 7 and 8)
-     */
-	SSPDriver->Write( SSP_Handle, 7, giDAC_Volume_L );
-	SSPDriver->Write( SSP_Handle, 8, giDAC_Volume_R );
-	
-#endif
-
-	DPRINTK("ep93xx_set_volume: OSS volume is 0x%04x, regs set to 0x%02x, 0x%02x\n",
-		giOSS_Volume, (giDAC_Volume_L | iMute), (giDAC_Volume_R | iMute) );	
-
-	return 0;
-}
-
-#define REC_MASK	(SOUND_MASK_LINE)
-#define DEV_MASK	(REC_MASK | SOUND_MASK_VOLUME)
-
-static int
-mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
-{
-	int val, nr, ret = 0;
-	struct mixer_info mi;
-
-	DPRINTK("mixer_ioctl - enter.  IOC_TYPE is %c \n", _IOC_TYPE(cmd) );
-
-	/*
-	 * We only accept mixer (type 'M') ioctls.
-	 */
-	if (_IOC_TYPE(cmd) != 'M')
-	{
-		return -EINVAL;
-	}
-
-	DPRINTK("mixer_ioctl - 2\n" );
-
-	if (cmd == SOUND_MIXER_INFO) 
-	{
-		DPRINTK("mixer_ioctl - SOUND_MIXER_INFO\n" );
-
-#ifdef CONFIG_CODEC_CS4271
-		strncpy(mi.id, "CS4271", sizeof(mi.id));
-		strncpy(mi.name, "Cirrus CS4271", sizeof(mi.name));
-#else // CONFIG_CODEC_CS4228A
-		strncpy(mi.id, "CS4228A", sizeof(mi.id));
-		strncpy(mi.name, "Cirrus CS4228A", sizeof(mi.name));
-#endif
-
-		return copy_to_user( (void *)arg, &mi, sizeof(mi));
-	}
-
-	nr = _IOC_NR(cmd);
-
-	if (_IOC_DIR(cmd) & _IOC_WRITE) 
-	{
-		ret = get_user(val, (int *)arg);
-		if (ret)
-		{
-			return ret;
-		}
-
-		DPRINTK("mixer_ioctl write - nr is %d, val is 0x%04x\n", nr, val );
-		
-		switch (nr) 
-		{
-			case SOUND_MIXER_VOLUME:
-				ret = ep93xx_set_volume( val );
-				break;
-
-			/*
-			 * Can't actually set the record volume.  But
-			 * if we return error, that might be bad.
-			 */
-		 /*	case SOUND_MIXER_LINE:
-				break; */
-
-			case SOUND_MIXER_RECSRC:
-				break;
-
-			default:
-				ret = -EINVAL;
-		}
-	}
-
-	if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) 
-	{
-		int nr = _IOC_NR(cmd);
-		ret = 0;
-
-
-		switch (nr) 
-		{
-			case SOUND_MIXER_VOLUME:     
-        		DPRINTK("mixer_ioctl read - nr is %d (SOUND_MIXER_VOLUME)\n", nr );
-				val = giOSS_Volume;	
-				break;
-				
-		   	case SOUND_MIXER_LINE:       
-        		DPRINTK("mixer_ioctl read - nr is %d (SOUND_MIXER_LINE)\n", nr );
-				val = ((100<<8) | 100 );	
-				break;
-				
-			case SOUND_MIXER_RECSRC:     
-        		DPRINTK("mixer_ioctl read - nr is %d (SOUND_MIXER_RECSRC)\n", nr );
-				val = REC_MASK;	
-				break;
-				
-			case SOUND_MIXER_RECMASK:    
-        		DPRINTK("mixer_ioctl read - nr is %d (SOUND_MIXER_RECMASK)\n", nr );
-				val = REC_MASK;	
-				break;
-			
-			case SOUND_MIXER_DEVMASK:    
-        		DPRINTK("mixer_ioctl read - nr is %d (SOUND_MIXER_DEVMASK)\n", nr );
-				val = DEV_MASK;	
-				break;
-			
-			case SOUND_MIXER_CAPS:
-        		DPRINTK("mixer_ioctl read - nr is %d (SOUND_MIXER_CAPS)\n", nr );
-				val = 0;		
-				break;
-
-			case SOUND_MIXER_STEREODEVS: 
-        		DPRINTK("mixer_ioctl read - nr is %d (SOUND_MIXER_STEREODEVS)\n", nr );
-				val = 0;		
-				break;
-
-			default:	
-        		DPRINTK("mixer_ioctl read - nr is %d (not handled, return 0)\n", nr );
-				val = 0;     
-				ret = -EINVAL;	
-				break;
-		}
-
-		if (ret == 0)
-			ret = put_user(val, (int *)arg);
-	}
-
-	return ret;
-}
-
-static struct file_operations h3600_mixer_fops = {
-	ioctl:		mixer_ioctl,
-	owner:		THIS_MODULE
-};
-
-
-/*
- * ep93xx_audio_init
- * Note that the codec powers up with its DAC's muted and
- * the serial format set to 24 bit I2S mode.
- */
-static void ep93xx_audio_init(void *dummy)
-{
-	DPRINTK("ep93xx_audio_init - enter\n");
-	
-	if( audio_has_been_initialized )
-	{
-		return;
-	}
-
-    /*
-	 * Mute the DACs. Disable all audio channels.  
-	 * Must do this to change sample rate.
-	 */
-    ep93xx_audio_disable( 0 );
-
-	/*
-	 * Set up the i2s clocks in syscon.  Enable them.
-	 */ 
-	ep93xx_set_samplerate( AUDIO_SAMPLE_RATE_DEFAULT );
-
-	/*
-	 * Set i2s' controller serial format, and enable 
-	 * the i2s controller.
-	 */
-	ep93xx_init_i2s_controller();
-	
-    /*
-     * Initialize codec serial format, etc.
-     */
-    ep93xx_init_i2s_codec();
-
-	/*
-	 * Clear the fifo and enable the tx0 channel.
-	 */
-	ep93xx_audio_enable();
-
-	/*
-	 * Set the volume for the first time.
-	 */
-	ep93xx_set_volume( AUDIO_DEFAULT_VOLUME );
-	
-	/*
-	 * Unmute the DAC and set the mute pin MUTEC to automute.
-	 */
-	ep93xx_automute_i2s_codec();
-	
-	audio_has_been_initialized = 1;
-	
-	DPRINTK("ep93xx_audio_init - EXIT\n");
-}
-
-
-
-static int ep93xx_audio_ioctl(struct inode *inode, struct file *file,
-			     uint cmd, ulong arg)
-{
-	long val;
-	int ret = 0;
-
-	/*
-	 * These are platform dependent ioctls which are not handled by the
-	 * generic ep93xx-audio module.
-	 */
-	switch (cmd) 
-	{
-		case SNDCTL_DSP_SPEED:
-			
-			DPRINTK("ep93xx_audio_ioctl - SNDCTL_DSP_SPEED\n");
-			
-			if (get_user(val, (int *)arg))
-			{
-				return -EFAULT;
-			}
-			
-		    /*
-			 * Mute the DACs. Disable all audio channels.  
-			 * Must do this to change sample rate.
-			 */
-		    ep93xx_audio_disable( 0 );
-			ep93xx_set_samplerate( val );
-	        
-            /*
-	         * We sorta should report the actual samplerate back to the calling
-	         * application.  But some applications freak out if they don't get
-	         * exactly what they asked for.  So we fudge and tell them what
-	         * they want to hear.
-	         */
-	        /* audio_samplerate = actual_samplerate; */ /* Report actual fs */
-	
-	        audio_samplerate = val;  /* tell me lies tell me sweet little lies */
-
-			ep93xx_audio_enable();
-
-	        DPRINTK( "ep93xx_set_samplerate - requested %d Hz, got %d Hz, reporting %d Hz.\n",
-			        (int)val, (int)actual_samplerate, (int)audio_samplerate );
-
-			/*
-			 * Unmute the DAC and set the external pin control to automute.
-			 */
-			ep93xx_automute_i2s_codec();
-
-			return put_user(audio_samplerate, (long *) arg);
-
-		case SOUND_PCM_READ_RATE:
-			DPRINTK("ep93xx_audio_ioctl - SOUND_PCM_READ_RATE\n");
-			return put_user(audio_samplerate, (long *) arg);
-
-		default:
-			DPRINTK("ep93xx_audio_ioctl-->mixer_ioctl\n");
-			/* Maybe this is meant for the mixer (As per OSS Docs) */
-			return mixer_ioctl(inode, file, cmd, arg);
-	}
-
-	return ret;
-}
-
-static int ep93xx_audio_open(struct inode *inode, struct file *file)
-{
-	DPRINTK( "ep93xx_audio_open\n" );
-
-#ifndef CONFIG_EP93XX_DMA
-	printk( "-------------------------------------------------------------\n");
-	printk( "-/drivers/sound/ep93xx-i2s.c: CONFIG_EP93XX_DMA isn't set! --\n");
-	printk( "-------------------------------------------------------------\n");
-#endif
-
-	return ep93xx_audio_attach( inode, file, &audio_state );
-}
-
-/*
- * Missing fields of this structure will be patched with the call
- * to ep93xx_audio_attach().
- */
-static struct file_operations h3600_audio_fops = 
-{
-	open:		ep93xx_audio_open,
-	owner:		THIS_MODULE
-};
-
-
-static int audio_dev_id, mixer_dev_id;
-
-static int __init ep93xx_i2s_init(void)
-{
-	DPRINTK("ep93xx_i2s_init - enter\n");
-	
-	/*
-	 * Make sure that if someone configures the kernel wrong we know
-	 * about it sooner rather than later.
-	 */
-#ifndef CONFIG_EP93XX_DMA
-	printk( "-------------------------------------------------------------\n");
-	printk( "-/drivers/sound/ep93xx-i2s.c: CONFIG_EP93XX_DMA isn't set! --\n");
-	printk( "-------------------------------------------------------------\n");
-#endif
-
-    /*
-     * Open an instance of the SSP driver for I2S codec register access.
-     */
-    SSP_Handle = SSPDriver->Open(I2S_CODEC,0);
-    if( SSP_Handle < 1 )
-    {
-	    printk( "/drivers/sound/ep93xx-i2s.c: Couldn't open SSP driver!\n");
-		return -ENODEV;
-    }
-
-	/*
-	 * Enable audio early on, give the DAC time to come up.
-	 */	
-	ep93xx_audio_init( 0 );
-
-	/* 
-	 * Register devices using sound_core.c's devfs stuff
-	*/
-	audio_dev_id = register_sound_dsp(&h3600_audio_fops, -1);
-	if( audio_dev_id < 0 )
-	{
-		DPRINTK(" ep93xx_i2s_init: register_sound_dsp failed for dsp.\n" );
-		return -ENODEV;
-	}
-	
-	mixer_dev_id = register_sound_mixer(&h3600_mixer_fops, -1);
-	if( mixer_dev_id < 0 )
-	{
-		DPRINTK(" ep93xx_i2s_init: register_sound_dsp failed for mixer.\n" );
-		return -ENODEV;
-	}
-
-	printk( KERN_INFO "EP93xx I2S audio support initialized.\n" );
-	return 0;
-}
-
-static void __exit ep93xx_i2s_exit(void)
-{
-    SSPDriver->Close(SSP_Handle);
-	unregister_sound_dsp( audio_dev_id );
-	unregister_sound_mixer( mixer_dev_id );
-}
-
-module_init(ep93xx_i2s_init);
-module_exit(ep93xx_i2s_exit);
-
-MODULE_DESCRIPTION("Audio driver for the Cirrus EP93xx I2S controller.");
-
-/* EXPORT_NO_SYMBOLS; */
+/*
+ * Glue audio driver for the Cirrus EP93xx I2S Controller
+ *
+ * Copyright (c) 2003 Cirrus Logic Corp.
+ * Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License.
+ *
+ * This file taken from h3600-uda1341.c
+ *
+ * History:
+ *
+ * 2000-05-21	Nicolas Pitre	Initial UDA1341 driver release.
+ *
+ * 2000-07-??	George France	Bitsy support.
+ *
+ * 2000-12-13	Deborah Wallach Fixed power handling for iPAQ/h3600
+ *
+ * 2001-06-03	Nicolas Pitre	Made this file a separate module, based on
+ *				the former sa1100-uda1341.c driver.
+ *
+ * 2001-07-13	Nicolas Pitre	Fixes for all supported samplerates.
+ *
+ * 2003-04-04   Changes for Cirrus Logic EP93xx
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/errno.h>
+#include <linux/sound.h>
+#include <linux/soundcard.h>
+#include <linux/i2c.h>
+
+#include <linux/ioport.h>   // Added for request_mem_region functions
+
+
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include <asm/hardware.h>
+#include <asm/dma.h>
+#include <asm/arch/ssp.h>
+#include <asm/arch/clocks.h>
+#include <asm/io.h>
+
+#include "ep93xx-audio.h"
+
+#undef DEBUG
+// #define DEBUG 1
+#ifdef DEBUG
+#define DPRINTK( fmt, arg... )  printk( fmt, ##arg )
+#else
+#define DPRINTK( fmt, arg... )
+#endif
+
+#define AUDIO_NAME		"ep93xx_i2s"
+#define AUDIO_SAMPLE_RATE_DEFAULT	44100
+
+/*
+ * OSS mixer volume levels are 16 bit with two 8 bit fields for left and right.
+ * Each is in % of full.  So range is 0..100 for each of two channels.
+ */ 
+#define AUDIO_DEFAULT_VOLUME ( (85<<8) | 85 )
+
+/*
+ * Statics
+ */
+static int giOSS_Volume = AUDIO_DEFAULT_VOLUME;
+static long audio_samplerate = AUDIO_SAMPLE_RATE_DEFAULT;
+static ulong actual_samplerate = AUDIO_SAMPLE_RATE_DEFAULT;
+static int giDAC_Volume_L = 0;
+static int giDAC_Volume_R = 0;
+static int giDAC_Mute = 0;
+
+static int audio_has_been_initialized = 0;
+static int audio_clocks_enabled_in_syscon = 0;
+static int SSP_Handle = -1;
+
+/* prototypes */
+static void ep93xx_audio_enable(void);
+static void ep93xx_audio_disable(void *dummy);
+static int ep93xx_calc_closest_freq
+(
+    ulong   ulPLLFreq, 
+    ulong   ulRequestedMClkFreq,
+    ulong * pulActualMClkFreq,
+    ulong * pulI2SDiv
+);
+static void ep93xx_set_samplerate(long lFrequency);
+static void ep93xx_init_i2s_controller( void );
+
+static void ep93xx_init_i2s_codec( void );
+//static void ep93xx_mute_i2s_codec( void );
+static void ep93xx_automute_i2s_codec( void );
+static int ep93xx_set_volume( int val );
+
+static int mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg);
+
+static void ep93xx_audio_init(void *dummy);
+static int ep93xx_audio_ioctl(struct inode *inode, struct file *file,
+			     uint cmd, ulong arg);
+static int ep93xx_audio_open(struct inode *inode, struct file *file);
+
+static int __init ep93xx_i2s_init(void);
+static void __exit ep93xx_i2s_exit(void);
+
+static audio_stream_t output_stream =
+{
+	audio_num_channels: 	EP93XX_DEFAULT_NUM_CHANNELS,
+	audio_format: 			EP93XX_DEFAULT_FORMAT,
+	audio_stream_bitwidth: 	EP93XX_DEFAULT_BIT_WIDTH,
+	hw_bit_width: 24,
+};
+
+static audio_stream_t input_stream =
+{
+	audio_num_channels: 	EP93XX_DEFAULT_NUM_CHANNELS,
+	audio_format: 			EP93XX_DEFAULT_FORMAT,
+	audio_stream_bitwidth: 	EP93XX_DEFAULT_BIT_WIDTH,
+	hw_bit_width: 24,
+};
+
+static audio_state_t audio_state =
+{
+	output_stream:		&output_stream,
+	output_dma:			DMATx_I2S1,
+	output_id:			"I2S out",
+	
+	input_stream:		&input_stream,
+	input_dma:			DMARx_I2S1,
+	input_id:			"I2S in",
+	
+	client_ioctl:		ep93xx_audio_ioctl,
+	hw_enable:          0,
+	hw_disable:         0,
+	
+	set_hw_serial_format:  0,
+
+	sem:				__MUTEX_INITIALIZER(audio_state.sem),
+	
+	DAC_bit_width:		24,
+	bCompactMode:		0,
+};
+
+
+
+typedef struct {
+    ulong   ulTotalDiv;
+    ulong   ulI2SDiv;
+} DIV_TABLE;
+
+static const DIV_TABLE I2SDivTable[] =
+{
+    {   6, SYSCON_I2SDIV_PDIV_2  | (  2 & SYSCON_I2SDIV_MDIV_MASK) },  /* Fake entry for lower limit. */
+    {   8, SYSCON_I2SDIV_PDIV_2  | (  2 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  10, SYSCON_I2SDIV_PDIV_25 | (  2 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  12, SYSCON_I2SDIV_PDIV_3  | (  2 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  15, SYSCON_I2SDIV_PDIV_25 | (  3 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  16, SYSCON_I2SDIV_PDIV_2  | (  4 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  18, SYSCON_I2SDIV_PDIV_3  | (  3 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  20, SYSCON_I2SDIV_PDIV_25 | (  4 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  24, SYSCON_I2SDIV_PDIV_3  | (  4 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  25, SYSCON_I2SDIV_PDIV_25 | (  5 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  28, SYSCON_I2SDIV_PDIV_2  | (  7 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  30, SYSCON_I2SDIV_PDIV_3  | (  5 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  32, SYSCON_I2SDIV_PDIV_2  | (  8 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  35, SYSCON_I2SDIV_PDIV_25 | (  7 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  36, SYSCON_I2SDIV_PDIV_3  | (  6 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  40, SYSCON_I2SDIV_PDIV_25 | (  8 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  42, SYSCON_I2SDIV_PDIV_3  | (  7 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  44, SYSCON_I2SDIV_PDIV_2  | ( 11 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  45, SYSCON_I2SDIV_PDIV_25 | (  9 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  48, SYSCON_I2SDIV_PDIV_3  | (  8 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  50, SYSCON_I2SDIV_PDIV_25 | ( 10 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  52, SYSCON_I2SDIV_PDIV_2  | ( 13 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  54, SYSCON_I2SDIV_PDIV_3  | (  9 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  55, SYSCON_I2SDIV_PDIV_25 | ( 11 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  56, SYSCON_I2SDIV_PDIV_2  | ( 14 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  60, SYSCON_I2SDIV_PDIV_3  | ( 10 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  64, SYSCON_I2SDIV_PDIV_2  | ( 16 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  65, SYSCON_I2SDIV_PDIV_25 | ( 13 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  66, SYSCON_I2SDIV_PDIV_3  | ( 11 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  68, SYSCON_I2SDIV_PDIV_2  | ( 17 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  70, SYSCON_I2SDIV_PDIV_25 | ( 14 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  72, SYSCON_I2SDIV_PDIV_3  | ( 12 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  75, SYSCON_I2SDIV_PDIV_25 | ( 15 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  76, SYSCON_I2SDIV_PDIV_2  | ( 19 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  78, SYSCON_I2SDIV_PDIV_3  | ( 13 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  80, SYSCON_I2SDIV_PDIV_25 | ( 16 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  84, SYSCON_I2SDIV_PDIV_3  | ( 14 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  85, SYSCON_I2SDIV_PDIV_25 | ( 17 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  88, SYSCON_I2SDIV_PDIV_2  | ( 22 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  90, SYSCON_I2SDIV_PDIV_3  | ( 15 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  92, SYSCON_I2SDIV_PDIV_2  | ( 23 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  95, SYSCON_I2SDIV_PDIV_25 | ( 19 & SYSCON_I2SDIV_MDIV_MASK) },
+    {  96, SYSCON_I2SDIV_PDIV_3  | ( 16 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 100, SYSCON_I2SDIV_PDIV_25 | ( 20 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 102, SYSCON_I2SDIV_PDIV_3  | ( 17 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 104, SYSCON_I2SDIV_PDIV_2  | ( 26 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 105, SYSCON_I2SDIV_PDIV_25 | ( 21 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 108, SYSCON_I2SDIV_PDIV_3  | ( 18 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 110, SYSCON_I2SDIV_PDIV_25 | ( 22 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 112, SYSCON_I2SDIV_PDIV_2  | ( 28 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 114, SYSCON_I2SDIV_PDIV_3  | ( 19 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 115, SYSCON_I2SDIV_PDIV_25 | ( 23 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 116, SYSCON_I2SDIV_PDIV_2  | ( 29 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 120, SYSCON_I2SDIV_PDIV_3  | ( 20 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 124, SYSCON_I2SDIV_PDIV_2  | ( 31 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 125, SYSCON_I2SDIV_PDIV_25 | ( 25 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 126, SYSCON_I2SDIV_PDIV_3  | ( 21 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 128, SYSCON_I2SDIV_PDIV_2  | ( 32 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 130, SYSCON_I2SDIV_PDIV_25 | ( 26 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 132, SYSCON_I2SDIV_PDIV_3  | ( 22 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 135, SYSCON_I2SDIV_PDIV_25 | ( 27 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 136, SYSCON_I2SDIV_PDIV_2  | ( 34 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 138, SYSCON_I2SDIV_PDIV_3  | ( 23 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 140, SYSCON_I2SDIV_PDIV_25 | ( 28 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 144, SYSCON_I2SDIV_PDIV_3  | ( 24 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 145, SYSCON_I2SDIV_PDIV_25 | ( 29 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 148, SYSCON_I2SDIV_PDIV_2  | ( 37 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 150, SYSCON_I2SDIV_PDIV_3  | ( 25 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 152, SYSCON_I2SDIV_PDIV_2  | ( 38 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 155, SYSCON_I2SDIV_PDIV_25 | ( 31 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 156, SYSCON_I2SDIV_PDIV_3  | ( 26 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 160, SYSCON_I2SDIV_PDIV_25 | ( 32 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 162, SYSCON_I2SDIV_PDIV_3  | ( 27 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 164, SYSCON_I2SDIV_PDIV_2  | ( 41 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 165, SYSCON_I2SDIV_PDIV_25 | ( 33 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 168, SYSCON_I2SDIV_PDIV_3  | ( 28 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 170, SYSCON_I2SDIV_PDIV_25 | ( 34 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 172, SYSCON_I2SDIV_PDIV_2  | ( 43 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 174, SYSCON_I2SDIV_PDIV_3  | ( 29 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 175, SYSCON_I2SDIV_PDIV_25 | ( 35 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 176, SYSCON_I2SDIV_PDIV_2  | ( 44 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 180, SYSCON_I2SDIV_PDIV_3  | ( 30 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 184, SYSCON_I2SDIV_PDIV_2  | ( 46 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 185, SYSCON_I2SDIV_PDIV_25 | ( 37 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 186, SYSCON_I2SDIV_PDIV_3  | ( 31 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 188, SYSCON_I2SDIV_PDIV_2  | ( 47 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 190, SYSCON_I2SDIV_PDIV_25 | ( 38 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 192, SYSCON_I2SDIV_PDIV_3  | ( 32 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 195, SYSCON_I2SDIV_PDIV_25 | ( 39 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 196, SYSCON_I2SDIV_PDIV_2  | ( 49 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 198, SYSCON_I2SDIV_PDIV_3  | ( 33 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 200, SYSCON_I2SDIV_PDIV_25 | ( 40 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 204, SYSCON_I2SDIV_PDIV_3  | ( 34 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 205, SYSCON_I2SDIV_PDIV_25 | ( 41 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 208, SYSCON_I2SDIV_PDIV_2  | ( 52 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 210, SYSCON_I2SDIV_PDIV_3  | ( 35 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 212, SYSCON_I2SDIV_PDIV_2  | ( 53 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 215, SYSCON_I2SDIV_PDIV_25 | ( 43 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 216, SYSCON_I2SDIV_PDIV_3  | ( 36 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 220, SYSCON_I2SDIV_PDIV_25 | ( 44 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 222, SYSCON_I2SDIV_PDIV_3  | ( 37 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 224, SYSCON_I2SDIV_PDIV_2  | ( 56 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 225, SYSCON_I2SDIV_PDIV_25 | ( 45 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 228, SYSCON_I2SDIV_PDIV_3  | ( 38 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 230, SYSCON_I2SDIV_PDIV_25 | ( 46 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 232, SYSCON_I2SDIV_PDIV_2  | ( 58 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 234, SYSCON_I2SDIV_PDIV_3  | ( 39 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 235, SYSCON_I2SDIV_PDIV_25 | ( 47 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 236, SYSCON_I2SDIV_PDIV_2  | ( 59 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 240, SYSCON_I2SDIV_PDIV_3  | ( 40 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 244, SYSCON_I2SDIV_PDIV_2  | ( 61 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 245, SYSCON_I2SDIV_PDIV_25 | ( 49 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 246, SYSCON_I2SDIV_PDIV_3  | ( 41 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 248, SYSCON_I2SDIV_PDIV_2  | ( 62 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 250, SYSCON_I2SDIV_PDIV_25 | ( 50 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 252, SYSCON_I2SDIV_PDIV_3  | ( 42 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 255, SYSCON_I2SDIV_PDIV_25 | ( 51 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 256, SYSCON_I2SDIV_PDIV_2  | ( 64 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 258, SYSCON_I2SDIV_PDIV_3  | ( 43 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 260, SYSCON_I2SDIV_PDIV_25 | ( 52 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 264, SYSCON_I2SDIV_PDIV_3  | ( 44 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 265, SYSCON_I2SDIV_PDIV_25 | ( 53 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 268, SYSCON_I2SDIV_PDIV_2  | ( 67 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 270, SYSCON_I2SDIV_PDIV_3  | ( 45 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 272, SYSCON_I2SDIV_PDIV_2  | ( 68 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 275, SYSCON_I2SDIV_PDIV_25 | ( 55 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 276, SYSCON_I2SDIV_PDIV_3  | ( 46 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 280, SYSCON_I2SDIV_PDIV_25 | ( 56 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 282, SYSCON_I2SDIV_PDIV_3  | ( 47 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 284, SYSCON_I2SDIV_PDIV_2  | ( 71 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 285, SYSCON_I2SDIV_PDIV_25 | ( 57 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 288, SYSCON_I2SDIV_PDIV_3  | ( 48 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 290, SYSCON_I2SDIV_PDIV_25 | ( 58 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 292, SYSCON_I2SDIV_PDIV_2  | ( 73 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 294, SYSCON_I2SDIV_PDIV_3  | ( 49 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 295, SYSCON_I2SDIV_PDIV_25 | ( 59 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 296, SYSCON_I2SDIV_PDIV_2  | ( 74 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 300, SYSCON_I2SDIV_PDIV_3  | ( 50 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 304, SYSCON_I2SDIV_PDIV_2  | ( 76 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 305, SYSCON_I2SDIV_PDIV_25 | ( 61 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 306, SYSCON_I2SDIV_PDIV_3  | ( 51 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 308, SYSCON_I2SDIV_PDIV_2  | ( 77 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 310, SYSCON_I2SDIV_PDIV_25 | ( 62 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 312, SYSCON_I2SDIV_PDIV_3  | ( 52 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 315, SYSCON_I2SDIV_PDIV_25 | ( 63 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 316, SYSCON_I2SDIV_PDIV_2  | ( 79 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 318, SYSCON_I2SDIV_PDIV_3  | ( 53 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 320, SYSCON_I2SDIV_PDIV_25 | ( 64 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 324, SYSCON_I2SDIV_PDIV_3  | ( 54 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 325, SYSCON_I2SDIV_PDIV_25 | ( 65 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 328, SYSCON_I2SDIV_PDIV_2  | ( 82 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 330, SYSCON_I2SDIV_PDIV_3  | ( 55 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 332, SYSCON_I2SDIV_PDIV_2  | ( 83 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 335, SYSCON_I2SDIV_PDIV_25 | ( 67 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 336, SYSCON_I2SDIV_PDIV_3  | ( 56 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 340, SYSCON_I2SDIV_PDIV_25 | ( 68 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 342, SYSCON_I2SDIV_PDIV_3  | ( 57 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 344, SYSCON_I2SDIV_PDIV_2  | ( 86 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 345, SYSCON_I2SDIV_PDIV_25 | ( 69 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 348, SYSCON_I2SDIV_PDIV_3  | ( 58 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 350, SYSCON_I2SDIV_PDIV_25 | ( 70 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 352, SYSCON_I2SDIV_PDIV_2  | ( 88 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 354, SYSCON_I2SDIV_PDIV_3  | ( 59 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 355, SYSCON_I2SDIV_PDIV_25 | ( 71 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 356, SYSCON_I2SDIV_PDIV_2  | ( 89 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 360, SYSCON_I2SDIV_PDIV_3  | ( 60 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 364, SYSCON_I2SDIV_PDIV_2  | ( 91 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 365, SYSCON_I2SDIV_PDIV_25 | ( 73 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 366, SYSCON_I2SDIV_PDIV_3  | ( 61 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 368, SYSCON_I2SDIV_PDIV_2  | ( 92 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 370, SYSCON_I2SDIV_PDIV_25 | ( 74 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 372, SYSCON_I2SDIV_PDIV_3  | ( 62 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 375, SYSCON_I2SDIV_PDIV_25 | ( 75 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 376, SYSCON_I2SDIV_PDIV_2  | ( 94 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 378, SYSCON_I2SDIV_PDIV_3  | ( 63 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 380, SYSCON_I2SDIV_PDIV_25 | ( 76 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 384, SYSCON_I2SDIV_PDIV_3  | ( 64 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 385, SYSCON_I2SDIV_PDIV_25 | ( 77 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 388, SYSCON_I2SDIV_PDIV_2  | ( 97 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 390, SYSCON_I2SDIV_PDIV_3  | ( 65 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 392, SYSCON_I2SDIV_PDIV_2  | ( 98 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 395, SYSCON_I2SDIV_PDIV_25 | ( 79 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 396, SYSCON_I2SDIV_PDIV_3  | ( 66 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 400, SYSCON_I2SDIV_PDIV_25 | ( 80 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 402, SYSCON_I2SDIV_PDIV_3  | ( 67 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 404, SYSCON_I2SDIV_PDIV_2  | (101 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 405, SYSCON_I2SDIV_PDIV_25 | ( 81 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 408, SYSCON_I2SDIV_PDIV_3  | ( 68 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 410, SYSCON_I2SDIV_PDIV_25 | ( 82 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 412, SYSCON_I2SDIV_PDIV_2  | (103 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 414, SYSCON_I2SDIV_PDIV_3  | ( 69 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 415, SYSCON_I2SDIV_PDIV_25 | ( 83 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 416, SYSCON_I2SDIV_PDIV_2  | (104 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 420, SYSCON_I2SDIV_PDIV_3  | ( 70 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 424, SYSCON_I2SDIV_PDIV_2  | (106 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 425, SYSCON_I2SDIV_PDIV_25 | ( 85 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 426, SYSCON_I2SDIV_PDIV_3  | ( 71 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 428, SYSCON_I2SDIV_PDIV_2  | (107 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 430, SYSCON_I2SDIV_PDIV_25 | ( 86 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 432, SYSCON_I2SDIV_PDIV_3  | ( 72 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 435, SYSCON_I2SDIV_PDIV_25 | ( 87 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 436, SYSCON_I2SDIV_PDIV_2  | (109 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 438, SYSCON_I2SDIV_PDIV_3  | ( 73 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 440, SYSCON_I2SDIV_PDIV_25 | ( 88 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 444, SYSCON_I2SDIV_PDIV_3  | ( 74 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 445, SYSCON_I2SDIV_PDIV_25 | ( 89 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 448, SYSCON_I2SDIV_PDIV_2  | (112 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 450, SYSCON_I2SDIV_PDIV_3  | ( 75 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 452, SYSCON_I2SDIV_PDIV_2  | (113 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 455, SYSCON_I2SDIV_PDIV_25 | ( 91 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 456, SYSCON_I2SDIV_PDIV_3  | ( 76 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 460, SYSCON_I2SDIV_PDIV_25 | ( 92 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 462, SYSCON_I2SDIV_PDIV_3  | ( 77 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 464, SYSCON_I2SDIV_PDIV_2  | (116 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 465, SYSCON_I2SDIV_PDIV_25 | ( 93 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 468, SYSCON_I2SDIV_PDIV_3  | ( 78 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 470, SYSCON_I2SDIV_PDIV_25 | ( 94 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 472, SYSCON_I2SDIV_PDIV_2  | (118 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 474, SYSCON_I2SDIV_PDIV_3  | ( 79 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 475, SYSCON_I2SDIV_PDIV_25 | ( 95 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 476, SYSCON_I2SDIV_PDIV_2  | (119 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 480, SYSCON_I2SDIV_PDIV_3  | ( 80 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 484, SYSCON_I2SDIV_PDIV_2  | (121 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 485, SYSCON_I2SDIV_PDIV_25 | ( 97 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 486, SYSCON_I2SDIV_PDIV_3  | ( 81 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 488, SYSCON_I2SDIV_PDIV_2  | (122 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 490, SYSCON_I2SDIV_PDIV_25 | ( 98 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 492, SYSCON_I2SDIV_PDIV_3  | ( 82 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 495, SYSCON_I2SDIV_PDIV_25 | ( 99 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 496, SYSCON_I2SDIV_PDIV_2  | (124 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 498, SYSCON_I2SDIV_PDIV_3  | ( 83 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 500, SYSCON_I2SDIV_PDIV_25 | (100 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 504, SYSCON_I2SDIV_PDIV_3  | ( 84 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 505, SYSCON_I2SDIV_PDIV_25 | (101 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 508, SYSCON_I2SDIV_PDIV_2  | (127 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 510, SYSCON_I2SDIV_PDIV_3  | ( 85 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 515, SYSCON_I2SDIV_PDIV_25 | (103 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 516, SYSCON_I2SDIV_PDIV_3  | ( 86 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 520, SYSCON_I2SDIV_PDIV_25 | (104 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 522, SYSCON_I2SDIV_PDIV_3  | ( 87 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 525, SYSCON_I2SDIV_PDIV_25 | (105 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 528, SYSCON_I2SDIV_PDIV_3  | ( 88 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 530, SYSCON_I2SDIV_PDIV_25 | (106 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 534, SYSCON_I2SDIV_PDIV_3  | ( 89 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 535, SYSCON_I2SDIV_PDIV_25 | (107 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 540, SYSCON_I2SDIV_PDIV_3  | ( 90 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 545, SYSCON_I2SDIV_PDIV_25 | (109 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 546, SYSCON_I2SDIV_PDIV_3  | ( 91 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 550, SYSCON_I2SDIV_PDIV_25 | (110 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 552, SYSCON_I2SDIV_PDIV_3  | ( 92 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 555, SYSCON_I2SDIV_PDIV_25 | (111 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 558, SYSCON_I2SDIV_PDIV_3  | ( 93 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 560, SYSCON_I2SDIV_PDIV_25 | (112 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 564, SYSCON_I2SDIV_PDIV_3  | ( 94 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 565, SYSCON_I2SDIV_PDIV_25 | (113 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 570, SYSCON_I2SDIV_PDIV_3  | ( 95 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 575, SYSCON_I2SDIV_PDIV_25 | (115 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 576, SYSCON_I2SDIV_PDIV_3  | ( 96 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 580, SYSCON_I2SDIV_PDIV_25 | (116 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 582, SYSCON_I2SDIV_PDIV_3  | ( 97 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 585, SYSCON_I2SDIV_PDIV_25 | (117 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 588, SYSCON_I2SDIV_PDIV_3  | ( 98 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 590, SYSCON_I2SDIV_PDIV_25 | (118 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 594, SYSCON_I2SDIV_PDIV_3  | ( 99 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 595, SYSCON_I2SDIV_PDIV_25 | (119 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 600, SYSCON_I2SDIV_PDIV_3  | (100 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 605, SYSCON_I2SDIV_PDIV_25 | (121 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 606, SYSCON_I2SDIV_PDIV_3  | (101 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 610, SYSCON_I2SDIV_PDIV_25 | (122 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 612, SYSCON_I2SDIV_PDIV_3  | (102 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 615, SYSCON_I2SDIV_PDIV_25 | (123 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 618, SYSCON_I2SDIV_PDIV_3  | (103 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 620, SYSCON_I2SDIV_PDIV_25 | (124 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 624, SYSCON_I2SDIV_PDIV_3  | (104 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 625, SYSCON_I2SDIV_PDIV_25 | (125 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 630, SYSCON_I2SDIV_PDIV_3  | (105 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 635, SYSCON_I2SDIV_PDIV_25 | (127 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 636, SYSCON_I2SDIV_PDIV_3  | (106 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 642, SYSCON_I2SDIV_PDIV_3  | (107 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 648, SYSCON_I2SDIV_PDIV_3  | (108 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 654, SYSCON_I2SDIV_PDIV_3  | (109 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 660, SYSCON_I2SDIV_PDIV_3  | (110 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 666, SYSCON_I2SDIV_PDIV_3  | (111 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 672, SYSCON_I2SDIV_PDIV_3  | (112 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 678, SYSCON_I2SDIV_PDIV_3  | (113 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 684, SYSCON_I2SDIV_PDIV_3  | (114 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 690, SYSCON_I2SDIV_PDIV_3  | (115 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 696, SYSCON_I2SDIV_PDIV_3  | (116 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 702, SYSCON_I2SDIV_PDIV_3  | (117 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 708, SYSCON_I2SDIV_PDIV_3  | (118 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 714, SYSCON_I2SDIV_PDIV_3  | (119 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 720, SYSCON_I2SDIV_PDIV_3  | (120 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 726, SYSCON_I2SDIV_PDIV_3  | (121 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 732, SYSCON_I2SDIV_PDIV_3  | (122 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 738, SYSCON_I2SDIV_PDIV_3  | (123 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 744, SYSCON_I2SDIV_PDIV_3  | (124 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 750, SYSCON_I2SDIV_PDIV_3  | (125 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 756, SYSCON_I2SDIV_PDIV_3  | (126 & SYSCON_I2SDIV_MDIV_MASK) },
+    { 762, SYSCON_I2SDIV_PDIV_3  | (127 & SYSCON_I2SDIV_MDIV_MASK) }
+};
+
+#ifdef CONFIG_CODEC_CS42L50
+extern struct i2c_adapter ep93xx_i2c_adapter;
+#define CS42L50_ADC	0x10
+#define CS42L50_DAC	0x11
+
+static int codec_i2c_read(int add, int reg) {
+	struct i2c_msg msg[2];
+	int ret;
+	char buf = reg & 0xff;
+
+	msg[0].addr = add;
+	msg[0].flags = 0;
+	msg[0].len = 1;
+	msg[0].buf = &buf;
+
+	msg[1].addr = add;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = 1;
+	msg[1].buf = &buf;
+
+	ret = i2c_transfer(&ep93xx_i2c_adapter, msg, 2);
+	if (ret != 2) panic("codec_i2c_read");
+
+	return buf;
+}
+
+static void codec_i2c_write(int add, int reg, int val) {
+	struct i2c_msg msg;
+	char buf[2];
+	int ret;
+
+	codec_i2c_read(add, reg);
+
+	buf[0] = reg & 0xff;
+	buf[1] = val & 0xff;
+	msg.addr = add;
+	msg.flags = 0;
+	msg.len = 2;
+	msg.buf = buf;
+
+	ret = i2c_transfer(&ep93xx_i2c_adapter, &msg, 1);
+	if (ret != 1) panic("codec_i2c_write");
+}
+#endif
+
+/*
+ * When we get to the multichannel case the pre-fill and enable code
+ * will go to the dma driver's start routine.
+ */
+static void ep93xx_audio_enable(void)
+{
+    int i;
+    unsigned long ulTemp;
+	
+	DPRINTK("ep93xx_audio_enable - enter\n");
+
+	/*
+	 * Disable the channels, disable the i2s controller to clear out the 
+	 * fifo.  Disabling the controller will clear the flags	in i2s_csr.
+	 */
+	outl( 0, I2STX0En );
+	outl( 0, I2STX1En );
+	outl( 0, I2STX2En );
+	outl( 0, I2SRX0En );
+	outl( 0, I2SRX1En );
+	outl( 0, I2SRX2En );
+	ulTemp = inl( I2SRX2En ); /* input to push the outl's past the wrapper */
+	 
+	/*
+	 * Disable the i2s controller to clear out the fifo and clear
+	 * the flags in i2s_csr.
+	 */
+	outl( 0, I2SGlCtrl );
+	ulTemp = inl( I2SGlCtrl );
+
+	/*
+	 * Enable the i2s controller.  It is important not to just
+	 * leave the i2s controller enabled and skip this step. 
+	 */
+	outl( 1, I2SGlCtrl );
+	ulTemp = inl( I2SGlCtrl );
+
+	/*
+	 * Prefill the tx fifo with zeros.
+	 */
+	if( (inl(I2SGlSts) & TX0_FIFO_EMPTY) != TX0_FIFO_EMPTY )
+	{
+		DPRINTK( "ep93xx_audio_enable - Tx fifos not empty.  Unable to prefill.\n" );
+	}
+	else
+	{
+	    for( i = 0 ; i < 8 ; i++ )
+	    {
+			outl( 0, I2STX0Lft );
+			outl( 0, I2STX0Rt );
+			ulTemp = inl( I2SRX2En ); /* input to push the outl's past the wrapper */
+	    }
+	}
+
+    	/*
+	 * Enable the tx channels.
+	 */
+	outl( 1, I2STX0En );
+	outl( 1, I2SRX0En );
+	ulTemp = inl( I2SRX2En ); /* input to push the outl's past the wrapper */
+
+    	/*
+	 * Must delay >= 2 sample periods after enabling the channel before 
+	 * writing any more to the FIFO.
+	 */
+	udelay( (3 * 1000 * 1000) / audio_samplerate );
+
+	DPRINTK("ep93xx_audio_enable - EXIT\n");
+
+}
+
+static void ep93xx_audio_disable(void *dummy)
+{
+	unsigned long ulTemp;
+
+	DPRINTK("ep93xx_audio_disable - enter\n");
+
+	audio_has_been_initialized = 0;
+	//ep93xx_mute_i2s_codec();
+
+	/*
+	 * Disable the channels, disable the i2s controller to clear out the 
+	 * fifo.  Disabling the controller will clear the flags	in i2s_csr.
+	 */
+	outl( 0, I2STX0En );
+	outl( 0, I2STX1En );
+	outl( 0, I2STX2En );
+	outl( 0, I2SRX0En );
+	outl( 0, I2SRX1En );
+	outl( 0, I2SRX2En );
+	outl( 0, I2SGlCtrl );
+	ulTemp = inl( I2SRX2En ); /* input to push the outl's past the wrapper */
+
+	DPRINTK("ep93xx_audio_disable - EXIT\n");
+}
+
+
+/*
+ * ep93xx_calc_closest_freq
+ * 
+ *   ulPLLFreq           -  PLL output Frequency (Mhz)
+ *   ulRequestedMClkFreq -  Requested Video Clock Frequency.
+ *   pulActualMClkFreq   -  Returned Actual Video Rate.
+ *   pulI2SDiv           -  Video Divider register.
+ *
+ *   Return            0 - Failure
+ *                     1 - Success
+ */
+static int ep93xx_calc_closest_freq
+(
+    ulong   ulPLLFreq, 
+    ulong   ulRequestedMClkFreq,
+    ulong * pulActualMClkFreq,
+    ulong * pulI2SDiv
+)
+{
+    
+    ulong   ulLower;
+    ulong   ulUpper;
+    ulong   ulDiv;
+    int     x;
+
+    /*
+     * Calculate the closest divisor.
+     */
+    ulDiv =  (ulPLLFreq * 2)/ ulRequestedMClkFreq;
+
+    for(x = 1; x < sizeof(I2SDivTable)/sizeof(DIV_TABLE); x++)
+    {
+        /*
+         * Calculate the next greater and lower value.
+         */
+        ulLower = I2SDivTable[x - 1].ulTotalDiv;     
+        ulUpper = I2SDivTable[x].ulTotalDiv;     
+
+        /*
+         * Check to see if it is in between the two values.
+         */
+        if(ulLower <= ulDiv && ulDiv < ulUpper)
+        {
+            break;
+        }
+    }
+
+    /*
+     * Return if we did not find a divisor.
+     */
+    if(x == sizeof(I2SDivTable)/sizeof(DIV_TABLE))
+    {
+		DPRINTK("couldn't find a divisor.\n");
+
+        *pulActualMClkFreq  = 0;
+        *pulI2SDiv          = 0;
+        return -1;
+    }
+
+    /*
+     * See which is closer, the upper or the lower case.
+     */
+    if(ulUpper * ulRequestedMClkFreq - ulPLLFreq * 2 >  
+      ulPLLFreq * 2 - ulLower * ulRequestedMClkFreq)
+    {
+        x-=1;
+    }
+    *pulActualMClkFreq  = (ulPLLFreq * 2)/ I2SDivTable[x].ulTotalDiv;
+    *pulI2SDiv          = I2SDivTable[x].ulI2SDiv;
+
+    return 0;
+}
+
+/*
+ * SetSampleRate
+ * 		disables i2s channels and sets up i2s divisors
+ * 		in syscon for the requested sample rate.
+ *
+ * lFrequency       - Sample Rate in Hz
+ */
+static void ep93xx_set_samplerate(long lFrequency)
+{
+    ulong ulRequestedMClkFreq;
+    ulong ulMClkFreq1, ulMClkFreq2;
+    ulong ulI2SDiv, ulI2SDiv1, ulI2SDiv2, ulI2SDIV;
+    
+	/*
+	 * Clock ratios: MCLK to SCLK and SCLK to LRCK
+	 */
+	ulong ulM2SClock  = 4;
+    ulong ulS2LRClock = 64;
+
+	DPRINTK( "ep93xx_set_samplerate = %d\n", (int)lFrequency );
+
+    ulRequestedMClkFreq = ( lFrequency * ulM2SClock * ulS2LRClock);
+
+    ep93xx_calc_closest_freq
+    (
+        PLL1_CLOCK, 
+        ulRequestedMClkFreq,
+        &ulMClkFreq1,
+        &ulI2SDiv1
+    );
+    ep93xx_calc_closest_freq
+    (
+        PLL2_CLOCK, 
+        ulRequestedMClkFreq,
+        &ulMClkFreq2,
+        &ulI2SDiv2
+    );
+
+    /*
+     * See which is closer, MClk rate 1 or MClk rate 2.
+     */
+    if(abs(ulMClkFreq1 - ulRequestedMClkFreq) < abs(ulMClkFreq2 -ulRequestedMClkFreq))
+    {
+        ulI2SDiv    = ulI2SDiv1;
+        actual_samplerate = ulMClkFreq1/ (ulM2SClock * ulS2LRClock);
+		DPRINTK( "ep93xx_set_samplerate - using PLL1\n" );
+    }
+    else
+    {
+        ulI2SDiv = ulI2SDiv2 | SYSCON_I2SDIV_PSEL;
+        actual_samplerate = ulMClkFreq1 / (ulM2SClock * ulS2LRClock);
+		DPRINTK( "ep93xx_set_samplerate - using PLL2\n" );
+    }
+
+    /*
+     * Calculate the new I2SDIV register value and write it out.
+     */
+#ifdef CONFIG_CODEC_CS42L50
+    ulI2SDIV = ulI2SDiv | SYSCON_I2SDIV_SENA |  SYSCON_I2SDIV_ORIDE | 
+				SYSCON_I2SDIV_SPOL| SYSCON_I2SDIV_LRDIV_128 | 
+				SYSCON_I2SDIV_MENA | SYSCON_I2SDIV_ESEL;
+#else
+    ulI2SDIV = ulI2SDiv | SYSCON_I2SDIV_SENA |  SYSCON_I2SDIV_ORIDE | 
+				SYSCON_I2SDIV_SPOL| SYSCON_I2SDIV_LRDIV_128 | 
+				SYSCON_I2SDIV_SDIV | SYSCON_I2SDIV_MENA | SYSCON_I2SDIV_ESEL;
+#endif
+
+    SysconSetLocked(SYSCON_I2SDIV, ulI2SDIV);
+	audio_clocks_enabled_in_syscon = 1;
+
+}
+
+/*
+ * BringUpI2S
+ *
+ * This routine sets up the I2S Controller.
+ */
+static void ep93xx_init_i2s_controller( void )
+{
+	unsigned int uiDEVCFG;
+	
+	DPRINTK("ep93xx_init_i2s_controller - enter\n");
+	
+    /*
+     * Configure the multiplexed Ac'97 pins to be I2S and 
+     * EGPIO4 and EGPIO6 to be SDOUT's for the second and third I2S
+     * controller channel.
+     */
+	uiDEVCFG = inl(SYSCON_DEVCFG);
+	
+	uiDEVCFG |= SYSCON_DEVCFG_I2SonAC97;
+	    
+    SysconSetLocked(SYSCON_DEVCFG, uiDEVCFG);
+
+    /* ------------- Configure I2S Tx channel ---------------------*/
+
+    /*
+	 * Justify Left, MSB first	
+     */
+	outl( 0, I2STXLinCtrlData );
+
+	/*
+	 * WL = 24 bits 
+	 */
+	outl( 1, I2STXWrdLen );
+
+	/*
+     * Set the I2S control block to master mode.
+	 * Tx bit clk rate determined by word legnth 
+	 * Do gate off last 8 clks (24 bit samples in a 32 bit field)
+	 * LRclk = I2S timing; LRck = 0 for left
+	 */
+#ifdef CONFIG_CODEC_CS42L50
+	 outl( (i2s_txcc_bcr_128x | i2s_txcc_mstr | i2s_txcc_trel | i2s_txcc_nbcg), I2STxClkCfg );
+#else
+	 outl( (i2s_txcc_mstr | i2s_txcc_trel), I2STxClkCfg );
+#endif
+	
+    /* ------------- Configure I2S rx channel --------------------- */
+    
+    /*
+     * First, clear all config bits.
+     */
+	outl( 0, I2SRXLinCtrlData );
+
+	/*
+	 * WL = 24 bits 
+	 */
+	outl( 1, I2SRXWrdLen );
+
+	/* 
+     * Set the I2S control block to master mode.
+	 * Rx bit clk rate determined by word legnth 
+	 * Do gate off last 8 clks 
+	 * setting i2s_rxcc_rrel gives us I2S timing
+	 * clearing i2s_rlrs gives us LRck = 0 for left, 1 for right
+	 * setting i2s_rxcc_nbcg specifies to not gate off extra 8 clocks 
+	 */
+#ifdef CONFIG_CODEC_CS42L50
+	outl( (i2s_rxcc_bcr_128x | i2s_rxcc_nbcg |i2s_rxcc_mstr | i2s_rxcc_rrel), I2SRxClkCfg );
+#else
+	outl( (i2s_rxcc_bcr_64x | i2s_rxcc_nbcg |i2s_rxcc_mstr | i2s_rxcc_rrel), I2SRxClkCfg );
+#endif
+	/* 
+	 * Do an input to push the outl's past the wrapper 
+	 */
+	uiDEVCFG = inl(SYSCON_DEVCFG);
+    
+	DPRINTK("ep93xx_init_i2s_controller - EXIT\n");
+}
+
+/*
+ * ep93xx_init_i2s_codec
+ *
+ * Note that codec must be getting i2s clocks for any codec
+ * register writes to work.
+ */
+static void ep93xx_init_i2s_codec( void )
+{
+	DPRINTK("ep93xx_init_i2s_codec - enter\n");
+    
+	if( !audio_clocks_enabled_in_syscon )
+	{
+		DPRINTK("ep93xx_init_i2s_codec - EXIT - clocks not enabled\n");
+		return;
+	}
+
+#ifdef CONFIG_CODEC_CS4271
+
+    /*
+     * Write to the control port, setting the enable control port bit
+     * so that we can write to the control port.  OK?
+     */
+	SSPDriver->Write( SSP_Handle, 7, 0x02 );
+
+    /*
+     * Select slave, 24Bit I2S serial mode
+     */
+	SSPDriver->Write( SSP_Handle, 1, 0x01 );
+	SSPDriver->Write( SSP_Handle, 6, 0x10 );
+
+    /*
+     * Set AMUTE (auto-mute) bit.
+     */
+	SSPDriver->Write( SSP_Handle, 2, 0x00 );
+
+    /*
+     * Set SOFT  bit.
+     */
+	//SSPDriver->Write( SSP_Handle, 3, 0x29 );
+
+#elif defined(CONFIG_CODEC_CS4228A)
+
+    /*
+     * Nothing needs to happen here
+     */
+
+#elif defined(CONFIG_CODEC_CS42L50)
+	codec_i2c_write(CS42L50_ADC, 0x1, 0x1);	// control port enable
+	udelay(10000);
+	codec_i2c_write(CS42L50_ADC, 0x1, 0x1);	// power on
+	udelay(10000);
+	codec_i2c_write(CS42L50_DAC, 0x1, 0xd1); // control port enable
+	udelay(10000);
+	codec_i2c_write(CS42L50_DAC, 0x1, 0xd1); // power on
+	udelay(10000);
+
+
+
+#endif
+    
+	DPRINTK("ep93xx_init_i2s_codec - EXIT\n");
+}
+
+#if 0
+/*
+ * ep93xx_mute_i2s_codec
+ *
+ * Note that codec must be getting i2s clocks for any codec
+ * register writes to work.
+ */
+static void ep93xx_mute_i2s_codec( void )
+{
+	DPRINTK("ep93xx_mute_i2s_codec - enter\n");
+
+	if( !audio_clocks_enabled_in_syscon )
+	{
+		DPRINTK("ep93xx_mute_i2s_codec - EXIT - clocks not enabled\n");
+		return;
+	}
+
+#ifdef CONFIG_CODEC_CS4271
+
+    /*
+     * Set the driver's mute flag and use the set_volume routine
+     * to write the current volume out with the mute bit set.
+     */
+    giDAC_Mute = 1;
+    ep93xx_set_volume( giOSS_Volume );
+
+#else // CONFIG_CODEC_CS4228A
+
+    /*
+     * Unmute the DACs
+     */
+	SSPDriver->Write( SSP_Handle, 4, 0 );
+
+    /*
+     * Unmute the MUTEC pin, turn on automute.
+     */
+	SSPDriver->Write( SSP_Handle, 5, 0x40 );
+    
+    giDAC_Mute = 0;
+    
+#endif
+
+	DPRINTK("ep93xx_mute_i2s_codec - EXIT\n");
+}
+#endif
+
+/*
+ * ep93xx_automute_i2s_codec
+ *
+ * Note that codec must be getting i2s clocks for any codec
+ * register writes to work.
+ */
+static void ep93xx_automute_i2s_codec( void )
+{
+	DPRINTK("ep93xx_automute_i2s_codec - enter\n");
+
+	if( !audio_clocks_enabled_in_syscon )
+	{
+		DPRINTK("ep93xx_automute_i2s_codec - EXIT - clocks not enabled\n");
+		return;
+	}
+
+#ifdef CONFIG_CODEC_CS4271
+
+    /*
+     * The automute bit is set by default for the CS4271.
+     * Clear the driver's mute flag and use the set_volume routine
+     * to write the current volume out with the mute bit cleared.
+     */
+    giDAC_Mute = 0;
+    ep93xx_set_volume( giOSS_Volume );
+
+#elif defined(CONFIG_CODEC_CS4228A)
+
+	SSPDriver->Write( SSP_Handle, 4, 0 );
+
+    /*
+     * Unmute the DACs
+     */
+	SSPDriver->Write( SSP_Handle, 5, 0xF0 );
+
+    /*
+     * Unmute the MUTEC pin, turn on automute.
+     */
+	SSPDriver->Write( SSP_Handle, 5, 0x40 );
+    
+    giDAC_Mute = 0;
+#elif defined(CONFIG_CODEC_CS42L50)
+    
+#endif
+
+	DPRINTK("ep93xx_automute_i2s_codec - EXIT\n");
+}
+
+/*
+ * Set the volume in the I2S DAC or ADC.
+ * The channel parameter specifies which stereo channel in the
+ * case of a 6 channel codec (channel 0, 1, or 2).
+ */
+static int ep93xx_set_volume( int iOSS_Volume )
+{
+    int iOSS_Volume_L, iOSS_Volume_R, iMute=0;
+	    
+	if( !audio_clocks_enabled_in_syscon )
+	{
+		return -EINVAL;
+	}
+    
+	giOSS_Volume = iOSS_Volume;
+
+    iOSS_Volume_L =  iOSS_Volume & 0x00ff;
+    iOSS_Volume_R = (iOSS_Volume & 0xff00) >> 8;
+
+	if (iOSS_Volume_L > 100)
+		iOSS_Volume_L = 100;
+	if (iOSS_Volume_L < 0)
+		iOSS_Volume_L = 0;
+	if (iOSS_Volume_R > 100)
+		iOSS_Volume_R = 100;
+	if (iOSS_Volume_R < 0)
+		iOSS_Volume_R = 0;
+
+#ifdef CONFIG_CODEC_CS4271
+    /*
+     * CS4271 volume is 0 to -127 dB in 1 dB increments.
+     * So scale the volume so a range of 0 to 100 becomes 127 to 0.
+     */
+    giDAC_Volume_L = 127 - ((iOSS_Volume_L * 127) / 100);
+    giDAC_Volume_R = 127 - ((iOSS_Volume_R * 127) / 100);
+
+    if( giDAC_Mute )
+    {
+        iMute = 0x80;
+    }
+
+    /*
+     * Write the volume for DAC1 and DAC2 (reg 4 and 5)
+     */
+	SSPDriver->Write( SSP_Handle, 4, (giDAC_Volume_L | iMute) );
+	SSPDriver->Write( SSP_Handle, 5, (giDAC_Volume_R | iMute) );
+
+#elif defined(CONFIG_CODEC_CS4228A)
+
+	/*
+	 * CS4228A DAC attenuation is 0 to 90.5 dB in 0.5 dB steps so scale
+	 * volume so that a value of 0 to 100 is scaled to 181 to 0
+	 */
+    giDAC_Volume_L = 181 - ((iOSS_Volume_L * 181) / 100);
+    giDAC_Volume_R = 181 - ((iOSS_Volume_R * 181) / 100);
+
+    /*
+     * Write the volume for DAC1 and DAC2 (reg 7 and 8)
+     */
+	SSPDriver->Write( SSP_Handle, 7, giDAC_Volume_L );
+	SSPDriver->Write( SSP_Handle, 8, giDAC_Volume_R );
+#elif defined(CONFIG_CODEC_CS42L50)
+	/*
+	 * CS42L50 DAC attenuation is -96 to +18 dB in 1 dB steps so scale
+	 * volume so that a value of 0 to 100 is scaled to -96 to 18 
+	 */
+    giDAC_Volume_L = ((iOSS_Volume_L * 114) / 100) - 96;
+    giDAC_Volume_R = ((iOSS_Volume_R * 114) / 100) - 96;
+    {
+    	char l, r;
+	l = giDAC_Volume_L;
+	r = giDAC_Volume_R;
+
+	codec_i2c_write(CS42L50_DAC, 0x4, l);
+	codec_i2c_write(CS42L50_DAC, 0x5, r);
+    }
+	
+#endif
+
+	DPRINTK("ep93xx_set_volume: OSS volume is 0x%04x, regs set to 0x%02x, 0x%02x\n",
+		giOSS_Volume, (giDAC_Volume_L | iMute), (giDAC_Volume_R | iMute) );	
+
+	return 0;
+}
+
+/* 
+ * We can record, but we can't change the record input volume.
+ * So we don't report that we can (we used to report that we can).
+ * change to (SOUND_MASK_LINE) to show a record volume that is not settable
+ * if your app needs this which it shouldn't
+ */
+#define REC_MASK	(0) // change to (SOUND_MASK_LINE) to show non settable record volume
+
+#ifdef CONFIG_CODEC_CS4271
+#define DEV_MASK	(REC_MASK | SOUND_MASK_VOLUME )
+#else
+#define DEV_MASK	(REC_MASK | SOUND_MASK_VOLUME | SOUND_MASK_LINE1 | SOUND_MASK_LINE2)
+#endif
+
+static int
+mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+{
+	int val, nr, ret = 0;
+	struct mixer_info mi;
+
+	DPRINTK("mixer_ioctl - enter.  IOC_TYPE is %c \n", _IOC_TYPE(cmd) );
+
+	/*
+	 * We only accept mixer (type 'M') ioctls.
+	 */
+	if (_IOC_TYPE(cmd) != 'M')
+	{
+		return -EINVAL;
+	}
+
+	DPRINTK("mixer_ioctl - 2\n" );
+
+	if (cmd == SOUND_MIXER_INFO) 
+	{
+		DPRINTK("mixer_ioctl - SOUND_MIXER_INFO\n" );
+
+#ifdef CONFIG_CODEC_CS4271
+		strncpy(mi.id, "CS4271", sizeof(mi.id));
+		strncpy(mi.name, "Cirrus CS4271", sizeof(mi.name));
+#elif defined(CONFIG_CODEC_CS4228A)
+		strncpy(mi.id, "CS4228A", sizeof(mi.id));
+		strncpy(mi.name, "Cirrus CS4228A", sizeof(mi.name));
+#elif defined(CONFIG_CODEC_CS42L50)
+		strncpy(mi.id, "CS42L50", sizeof(mi.id));
+		strncpy(mi.name, "Cirrus CS42L50", sizeof(mi.name));
+#endif
+
+		return copy_to_user( (void *)arg, &mi, sizeof(mi));
+	}
+
+	nr = _IOC_NR(cmd);
+
+	if (_IOC_DIR(cmd) & _IOC_WRITE) 
+	{
+		ret = get_user(val, (int *)arg);
+		if (ret)
+		{
+			return ret;
+		}
+
+		DPRINTK("mixer_ioctl write - nr is %d, val is 0x%04x\n", nr, val );
+		
+		switch (nr) 
+		{
+			case SOUND_MIXER_VOLUME:
+				ret = ep93xx_set_volume( val );
+				break;
+
+			/*
+			 * Can't actually set the record volume.  But
+			 * if we return error, that might be bad.
+			 */
+		 /*	case SOUND_MIXER_LINE:
+				break; */
+
+			case SOUND_MIXER_RECSRC:
+				break;
+
+			default:
+				ret = -EINVAL;
+		}
+	}
+
+	if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) 
+	{
+		int nr = _IOC_NR(cmd);
+		ret = 0;
+
+
+		switch (nr) 
+		{
+			case SOUND_MIXER_VOLUME:     
+        		DPRINTK("mixer_ioctl read - nr is %d (SOUND_MIXER_VOLUME)\n", nr );
+				val = giOSS_Volume;	
+				break;
+				
+		   	case SOUND_MIXER_LINE:       
+        		DPRINTK("mixer_ioctl read - nr is %d (SOUND_MIXER_LINE)\n", nr );
+				val = ((100<<8) | 100 );	
+				break;
+				
+			case SOUND_MIXER_RECSRC:     
+        		DPRINTK("mixer_ioctl read - nr is %d (SOUND_MIXER_RECSRC)\n", nr );
+				val = REC_MASK;	
+				break;
+				
+			case SOUND_MIXER_RECMASK:    
+        		DPRINTK("mixer_ioctl read - nr is %d (SOUND_MIXER_RECMASK)\n", nr );
+				val = REC_MASK;	
+				break;
+			
+			case SOUND_MIXER_DEVMASK:    
+        		DPRINTK("mixer_ioctl read - nr is %d (SOUND_MIXER_DEVMASK)\n", nr );
+				val = DEV_MASK;	
+				break;
+			
+			case SOUND_MIXER_CAPS:
+        		DPRINTK("mixer_ioctl read - nr is %d (SOUND_MIXER_CAPS)\n", nr );
+				val = 0;		
+				break;
+
+			case SOUND_MIXER_STEREODEVS: 
+        		DPRINTK("mixer_ioctl read - nr is %d (SOUND_MIXER_STEREODEVS)\n", nr );
+				val = 0;		
+				break;
+
+			default:	
+        		DPRINTK("mixer_ioctl read - nr is %d (not handled, return 0)\n", nr );
+				val = 0;     
+				ret = -EINVAL;	
+				break;
+		}
+
+		if (ret == 0)
+			ret = put_user(val, (int *)arg);
+	}
+
+	return ret;
+}
+
+static struct file_operations h3600_mixer_fops = {
+	ioctl:		mixer_ioctl,
+	owner:		THIS_MODULE
+};
+
+
+/*
+ * ep93xx_audio_init
+ * Note that the codec powers up with its DAC's muted and
+ * the serial format set to 24 bit I2S mode.
+ */
+static void ep93xx_audio_init(void *dummy)
+{
+	DPRINTK("ep93xx_audio_init - enter\n");
+	
+	if( audio_has_been_initialized )
+	{
+		return;
+	}
+
+    /*
+	 * Mute the DACs. Disable all audio channels.  
+	 * Must do this to change sample rate.
+	 */
+    ep93xx_audio_disable( 0 );
+
+	/*
+	 * Set up the i2s clocks in syscon.  Enable them.
+	 */ 
+	ep93xx_set_samplerate( AUDIO_SAMPLE_RATE_DEFAULT );
+
+	/*
+	 * Set i2s' controller serial format, and enable 
+	 * the i2s controller.
+	 */
+	ep93xx_init_i2s_controller();
+	
+    /*
+     * Initialize codec serial format, etc.
+     */
+    ep93xx_init_i2s_codec();
+
+	/*
+	 * Clear the fifo and enable the tx0 channel.
+	 */
+	ep93xx_audio_enable();
+
+	/*
+	 * Set the volume for the first time.
+	 */
+	ep93xx_set_volume( AUDIO_DEFAULT_VOLUME );
+	
+	/*
+	 * Unmute the DAC and set the mute pin MUTEC to automute.
+	 */
+	ep93xx_automute_i2s_codec();
+	
+	audio_has_been_initialized = 1;
+	
+	DPRINTK("ep93xx_audio_init - EXIT\n");
+}
+
+
+
+static int ep93xx_audio_ioctl(struct inode *inode, struct file *file,
+			     uint cmd, ulong arg)
+{
+	long val;
+	int ret = 0;
+
+	/*
+	 * These are platform dependent ioctls which are not handled by the
+	 * generic ep93xx-audio module.
+	 */
+	switch (cmd) 
+	{
+		case SNDCTL_DSP_SPEED:
+			
+			DPRINTK("ep93xx_audio_ioctl - SNDCTL_DSP_SPEED\n");
+			
+			if (get_user(val, (int *)arg))
+			{
+				return -EFAULT;
+			}
+			
+		    /*
+			 * Mute the DACs. Disable all audio channels.  
+			 * Must do this to change sample rate.
+			 */
+		    ep93xx_audio_disable( 0 );
+			ep93xx_set_samplerate( val );
+	        
+            /*
+	         * We sorta should report the actual samplerate back to the calling
+	         * application.  But some applications freak out if they don't get
+	         * exactly what they asked for.  So we fudge and tell them what
+	         * they want to hear.
+	         */
+	        /* audio_samplerate = actual_samplerate; */ /* Report actual fs */
+	
+	        audio_samplerate = val;  /* tell me lies tell me sweet little lies */
+
+			ep93xx_audio_enable();
+
+	        DPRINTK( "ep93xx_set_samplerate - requested %d Hz, got %d Hz, reporting %d Hz.\n",
+			        (int)val, (int)actual_samplerate, (int)audio_samplerate );
+
+			/*
+			 * Unmute the DAC and set the external pin control to automute.
+			 */
+			ep93xx_automute_i2s_codec();
+
+			return put_user(audio_samplerate, (long *) arg);
+
+		case SOUND_PCM_READ_RATE:
+			DPRINTK("ep93xx_audio_ioctl - SOUND_PCM_READ_RATE\n");
+			return put_user(audio_samplerate, (long *) arg);
+
+		default:
+			DPRINTK("ep93xx_audio_ioctl-->mixer_ioctl\n");
+			/* Maybe this is meant for the mixer (As per OSS Docs) */
+			return mixer_ioctl(inode, file, cmd, arg);
+	}
+
+	return ret;
+}
+
+static int ep93xx_audio_open(struct inode *inode, struct file *file)
+{
+	DPRINTK( "ep93xx_audio_open\n" );
+
+#ifndef CONFIG_EP93XX_DMA
+	printk( "-------------------------------------------------------------\n");
+	printk( "-/drivers/sound/ep93xx-i2s.c: CONFIG_EP93XX_DMA isn't set! --\n");
+	printk( "-------------------------------------------------------------\n");
+#endif
+
+	return ep93xx_audio_attach( inode, file, &audio_state );
+}
+
+/*
+ * Missing fields of this structure will be patched with the call
+ * to ep93xx_audio_attach().
+ */
+static struct file_operations h3600_audio_fops = 
+{
+	open:		ep93xx_audio_open,
+	owner:		THIS_MODULE
+};
+
+
+static int audio_dev_id, mixer_dev_id;
+
+static int __init ep93xx_i2s_init(void)
+{
+  unsigned long start;
+  unsigned long length;
+  volatile char *gpio_ddr;
+  volatile char *gpio_data;
+  
+	DPRINTK("ep93xx_i2s_init - enter\n");
+	
+	/*
+	 * Make sure that if someone configures the kernel wrong we know
+	 * about it sooner rather than later.
+	 */
+#ifndef CONFIG_EP93XX_DMA
+	printk( "-------------------------------------------------------------\n");
+	printk( "-/drivers/sound/ep93xx-i2s.c: CONFIG_EP93XX_DMA isn't set! --\n");
+	printk( "-------------------------------------------------------------\n");
+#endif
+
+#ifdef CONFIG_CODEC_CS4271
+    /*
+     * Take CS4271 out of reset
+     */
+	 // Set Data Driection for GPIO1 to GPIO7
+	 start = 0x80840010;
+	 length = sizeof(char);
+	 if (check_mem_region(start, length)) {
+		printk(KERN_ERR "irq: Unable to acquire CS4271 Reset DIO1.\n");
+		return -EBUSY;
+	 }
+	 request_mem_region(start, length, "GPIO 1");
+	 gpio_ddr = (volatile char *) __ioremap(start, length, 0);
+	 if (gpio_ddr != NULL) {
+		printk(KERN_INFO "Taking CS4271 out of reset\n");		
+		*gpio_ddr = (char)(0x02); // Set DIO 01 to output	
+		release_mem_region(start, length);
+	 } else {
+		printk(KERN_ERR "Error Taking CS4271 out of reset\n");		
+		release_mem_region(start, length);
+		return -EBUSY;	 	
+	 }
+	 // Set Data to 0xFF for GPIO1 to GPIO7
+	 start = 0x80840000;
+	 length = 1;
+	 if (check_mem_region(start, length)) {
+		printk(KERN_ERR "irq: Unable to acquire CS4271 Reset DIO1.\n");
+		return -EBUSY;
+	 }
+	 request_mem_region(start, length, "GPIO 1");
+	 gpio_data = (volatile char *) __ioremap(start, length, 0);
+	 if (gpio_data != NULL) {
+		printk(KERN_INFO "Taking CS4271 out of reset\n");		
+		*gpio_data = (char)(0x02); // SET DIO 01 to 1	
+		release_mem_region(start, length);
+	 } else {
+		printk(KERN_ERR "Error Taking CS4271 out of reset\n");		
+		release_mem_region(start, length);
+		return -EBUSY;	 	
+	 }
+     // Allow CS4271 to settle when comming out of reset
+     udelay(200);
+#endif
+       
+    /*
+     * Open an instance of the SSP driver for I2S codec register access.
+     */
+    SSP_Handle = SSPDriver->Open(I2S_CODEC,0);
+    if( SSP_Handle < 1 )
+    {
+	    printk( "/drivers/sound/ep93xx-i2s.c: Couldn't open SSP driver!\n");
+		return -ENODEV;
+    }
+
+	/*
+	 * Enable audio early on, give the DAC time to come up.
+	 */	
+	ep93xx_audio_init( 0 );
+
+	/* 
+	 * Register devices using sound_core.c's devfs stuff
+	*/
+	audio_dev_id = register_sound_dsp(&h3600_audio_fops, -1);
+	if( audio_dev_id < 0 )
+	{
+		DPRINTK(" ep93xx_i2s_init: register_sound_dsp failed for dsp.\n" );
+		return -ENODEV;
+	}
+	
+	mixer_dev_id = register_sound_mixer(&h3600_mixer_fops, -1);
+	if( mixer_dev_id < 0 )
+	{
+		DPRINTK(" ep93xx_i2s_init: register_sound_dsp failed for mixer.\n" );
+		return -ENODEV;
+	}
+
+	printk( KERN_INFO "EP93xx I2S audio support initialized.\n" );
+	return 0;
+}
+
+static void __exit ep93xx_i2s_exit(void)
+{
+    SSPDriver->Close(SSP_Handle);
+	unregister_sound_dsp( audio_dev_id );
+	unregister_sound_mixer( mixer_dev_id );
+
+#ifdef CONFIG_CODEC_CS4228A
+	unregister_sound_dsp( audio_dev_id1 );
+	unregister_sound_dsp( audio_dev_id2 );
+#endif
+
+}
+
+module_init(ep93xx_i2s_init);
+module_exit(ep93xx_i2s_exit);
+
+MODULE_DESCRIPTION("Audio driver for the Cirrus EP93xx I2S controller.");
+
+/* EXPORT_NO_SYMBOLS; */
Index: drivers/usb/host/usb-ohci.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/usb/host/usb-ohci.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- drivers/usb/host/usb-ohci.c	4 Aug 2004 01:29:54 -0000	1.2
+++ drivers/usb/host/usb-ohci.c	8 Nov 2006 22:42:03 -0000	1.3
@@ -112,7 +112,12 @@
 static inline u32 roothub_status (struct ohci *hc)
 	{ return readl (&hc->regs->roothub.status); }
 static u32 roothub_portstatus (struct ohci *hc, int i)
-	{ return read_roothub (hc, portstatus [i], 0xffe0fce0); }
+	{ 
+		if(i == 1)	//port1 is not bonded out to a pin(EP902)
+			return 0; 
+		else
+			return read_roothub (hc, portstatus [i], 0xffe0fce0); 
+	}
 
 
 /*-------------------------------------------------------------------------*
Index: drivers/usb/serial/pl2303.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/usb/serial/pl2303.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- drivers/usb/serial/pl2303.c	4 Aug 2004 01:29:55 -0000	1.2
+++ drivers/usb/serial/pl2303.c	18 Apr 2007 22:43:47 -0000	1.3
@@ -59,7 +59,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v0.10"
+#define DRIVER_VERSION "v0.10.1"	/* Takes from 2.6's */
 #define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver"
 
 
@@ -107,6 +107,7 @@
 #define VENDOR_READ_REQUEST		0x01
 
 #define UART_STATE			0x08
+#define UART_STATE_TRANSIENT_MASK	0x74
 #define UART_DCD			0x01
 #define UART_DSR			0x02
 #define UART_BREAK_ERROR		0x04
@@ -155,20 +156,38 @@
 	.shutdown =		pl2303_shutdown,
 };
 
+enum pl2303_type {
+	type_0,		/* don't know the difference between type 0 and */
+	type_1,		/* type 1, until someone from prolific tells us... */
+	HX,		/* HX version of the pl2303 chip */
+};
+
 struct pl2303_private {
 	spinlock_t lock;
 	wait_queue_head_t delta_msr_wait;
 	u8 line_control;
 	u8 line_status;
 	u8 termios_initialized;
+	enum pl2303_type type;
 };
 
 
 static int pl2303_startup (struct usb_serial *serial)
 {
 	struct pl2303_private *priv;
+	enum pl2303_type type = type_0;
 	int i;
 
+	if (serial->dev->descriptor.bDeviceClass == 0x02)
+		type = type_0;
+	else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40)
+		type = HX;
+	else if (serial->dev->descriptor.bDeviceClass == 0x00)
+		type = type_1;
+	else if (serial->dev->descriptor.bDeviceClass == 0xFF)
+		type = type_1;
+	dbg("device type: %d", type);
+
 	for (i = 0; i < serial->num_ports; ++i) {
 		priv = kmalloc (sizeof (struct pl2303_private), GFP_KERNEL);
 		if (!priv)
@@ -177,6 +196,7 @@
 		spin_lock_init(&priv->lock);
 		init_waitqueue_head(&priv->delta_msr_wait);
 		usb_set_serial_port_data(&serial->port[i], priv);
+		priv->type = type;
 	}
 	return 0;
 }
@@ -198,6 +218,9 @@
 
 	dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count);
 
+	if (!count)
+		return count;
+
 	if (port->write_urb->status == -EINPROGRESS) {
 		dbg("%s - already writing", __FUNCTION__);
 		return 0;
@@ -376,10 +399,17 @@
 	     buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
 
 	if (cflag & CRTSCTS) {
-		i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
-				     VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE,
-				     0x0, 0x41, NULL, 0, 100);
-		dbg ("0x40:0x1:0x0:0x41  %d", i);
+		__u16 index;
+		if (priv->type == HX)
+			index = 0x61;
+		else
+			index = 0x41;
+		i = usb_control_msg(serial->dev, 
+				    usb_sndctrlpipe(serial->dev, 0),
+				    VENDOR_WRITE_REQUEST,
+				    VENDOR_WRITE_REQUEST_TYPE,
+				    0x0, index, NULL, 0, 100);
+		dbg ("0x40:0x1:0x0:0x%x  %d", index, i);
 	}
 
 	kfree (buf);
@@ -391,6 +421,7 @@
 	struct termios tmp_termios;
 	struct usb_serial *serial = port->serial;
 	unsigned char *buf;
+	struct pl2303_private *priv = port->private;
 	int result;
 
 	if (port_paranoia_check (port, __FUNCTION__))
@@ -398,8 +429,11 @@
 		
 	dbg("%s -  port %d", __FUNCTION__, port->number);
 
-	usb_clear_halt(serial->dev, port->write_urb->pipe);
-	usb_clear_halt(serial->dev, port->read_urb->pipe);
+	if (priv->type != HX)
+	{
+		usb_clear_halt(serial->dev, port->write_urb->pipe);
+		usb_clear_halt(serial->dev, port->read_urb->pipe);
+	}
 
 	buf = kmalloc(10, GFP_KERNEL);
 	if (buf==NULL)
@@ -423,6 +457,18 @@
 	SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0x0404, 1);
 	FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0);
 	FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0);
+	SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0, 1);
+	SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 1, 0);
+ 
+	if (priv->type == HX) {
+		/* HX chip */
+		SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 2, 0x44);
+		/* reset upstream data pipes */
+          	SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 8, 0);
+        	SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 9, 0);
+	} else {
+		SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 2, 0x24);
+	}
 
 	kfree(buf);
 
@@ -648,7 +694,7 @@
 		state = BREAK_OFF;
 	else
 		state = BREAK_ON;
-	dbg("%s - turning break %s", state==BREAK_OFF ? "off" : "on", __FUNCTION__);
+	dbg("%s - turning break %s", __FUNCTION__, state==BREAK_OFF ? "off" : "on");
 
 	result = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
 				  BREAK_REQUEST, BREAK_REQUEST_TYPE, state, 
@@ -678,6 +724,7 @@
 	struct pl2303_private *priv = usb_get_serial_port_data(port);
 	unsigned char *data = urb->transfer_buffer;
 	unsigned long flags;
+	u8 uart_state;
 
 	dbg("%s (%d)", __FUNCTION__, port->number);
 
@@ -708,8 +755,10 @@
 		return;
 
 	/* Save off the uart status for others to look at */
+	uart_state = data[UART_STATE];
 	spin_lock_irqsave(&priv->lock, flags);
-	priv->line_status = data[UART_STATE];
+	uart_state |= (priv->line_status & UART_STATE_TRANSIENT_MASK);
+	priv->line_status = uart_state;
 	spin_unlock_irqrestore(&priv->lock, flags);
 	wake_up_interruptible (&priv->delta_msr_wait);
 
@@ -767,7 +816,9 @@
 
 	spin_lock_irqsave(&priv->lock, flags);
 	status = priv->line_status;
+	priv->line_status &= ~UART_STATE_TRANSIENT_MASK;
 	spin_unlock_irqrestore(&priv->lock, flags);
+	wake_up_interruptible (&priv->delta_msr_wait); //AF from 2.6
 
 	/* break takes precedence over parity, */
 	/* which takes precedence over framing errors */
Index: drivers/video/ts7300fb.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/video/ts7300fb.c,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- drivers/video/ts7300fb.c	7 Apr 2006 23:12:25 -0000	1.2
+++ drivers/video/ts7300fb.c	28 Feb 2007 21:33:02 -0000	1.5
@@ -38,6 +38,8 @@
 	unsigned long fb_phys;
 	volatile unsigned short *fpga_regs;
 	unsigned int page;
+	unsigned int xres;
+	unsigned int yres;
 	struct { unsigned red, green, blue, pad; } palette[16];
 
 	u16 fbcon_cmap16[16];
@@ -72,9 +74,16 @@
 	unsigned short vidctrl;
 	unsigned int direction = 0;
 
+	if (height > 511) {
+		ts7300_blt(src, dest, width, 511, pxfill);
+		height -= 511;
+		if (!pxfill) src += fb_info.xres * 511;
+		dest += fb_info.xres * 511;
+	}
+
 	if (pxfill == 0 && src < dest) {
-		src += (height - 1) * XRES;
-		dest += (height - 1) * XRES;
+		src += (height - 1) * fb_info.xres;
+		dest += (height - 1) * fb_info.xres;
 		direction = 1;
 	}
 
@@ -92,8 +101,8 @@
 static void fbcon_ts7300_bmove(struct display *p, int sy, int sx, int dy, int dx,
 	                        int height, int width)
 {
-	unsigned int src = sy * fontheight(p) * XRES + sx * fontwidth(p);
-	unsigned int dst = dy * fontheight(p) * XRES + dx * fontwidth(p);
+	unsigned int src = sy * fontheight(p) * fb_info.xres + sx * fontwidth(p);
+	unsigned int dst = dy * fontheight(p) * fb_info.xres + dx * fontwidth(p);
 
 	width  *= fontwidth(p);
 	height *= fontheight(p);
@@ -104,7 +113,7 @@
 void fbcon_ts7300_clear(struct vc_data *conp, struct display *p, int sy, int sx,
                        int height, int width)
 {
-	unsigned int dst = sy * fontheight(p) * XRES + sx * fontwidth(p);
+	unsigned int dst = sy * fontheight(p) * fb_info.xres + sx * fontwidth(p);
 	unsigned short bgcolor;
 
 	width  *= fontwidth(p);
@@ -145,7 +154,7 @@
 	fix->ywrapstep = 0;
 	fix->xpanstep = 0;
 	fix->ypanstep = 0;
-	fix->line_length = XRES * 2;
+	fix->line_length = fb_info.xres * 2;
 	fix->accel = FB_ACCELF_TEXT;
 
 	return 0;
@@ -154,7 +163,7 @@
 static void set_color_bitfields(struct fb_var_screeninfo *var)
 {
 	var->red.offset = 11;
-	var->green.offset = 6;
+	var->green.offset = 5;
 	var->blue.offset = 0;
 	var->transp.offset = 0;
 
@@ -174,7 +183,7 @@
 {
 	struct ts7300fb_par *par = (struct ts7300fb_par *)_par;
 
-	if (var->xres != XRES || var->yres != YRES)
+	if (var->xres != fb_info.xres || var->yres != fb_info.yres)
 		return -EINVAL;
 
 	if(var->bits_per_pixel != BPP)
@@ -304,20 +313,50 @@
 	ts7300_set_disp
 };
 
+/* Use a bit blit to determine the screen resolution, assumes 4:3 aspect ratio*/
+int detect_screen_width()
+{
+	unsigned int i=0, found = 0;
+	unsigned short *mem = (unsigned short *) fb_info.fb_virt_start;
+
+	ts7300_blt(0xFFFF, 0, MAX_WIDTH-1, 2, 1);
+	ts7300_blt(0, 0, 1-1, 2, 1);
+
+	for(i=0;i<(MAX_WIDTH*2);i++) {
+		if(mem[i] == 0x0) found++;
+		if(found == 2) break;
+	}
+
+	if(found != 2) {
+		printk("Framebuffer doesn't support this video core\n");
+		return 0;
+	} else { //Assumes a 4:3 aspect ratio
+		fb_info.xres=i;
+		fb_info.yres=i*3/4;
+		printk("Detected %dx%d video core\n",
+		  fb_info.xres, fb_info.yres);
+	}
+
+	return 1;
+}
+
 int __init ts7300fb_init(void)
 {
 	int return_val = 0;
-	int i;
-
+	fb_info.xres = MAX_WIDTH; //we have to define these to be non null until
+	fb_info.yres = MAX_HEIGHT; //we detect the correct values
 	fb_info.fb_size = VIDEOMEMORYSIZE;
 	fb_info.fb_virt_start = __ioremap(0x72200000, 0x100000, 0);
 	fb_info.fb_phys = 0x72200000;
 	fb_info.fpga_regs = (short *)__ioremap(0x72000030, 0x10, 0);
 
-	current_par.var.xres = XRES;
-	current_par.var.xres_virtual = XRES;
-	current_par.var.yres = YRES;
-	current_par.var.yres_virtual = YRES;
+	if(!detect_screen_width())
+		return -1;
+
+	current_par.var.xres = fb_info.xres;
+	current_par.var.xres_virtual = fb_info.xres;
+	current_par.var.yres = fb_info.yres;
+	current_par.var.yres_virtual = fb_info.yres;
 	current_par.var.bits_per_pixel = BPP;
 
 	//timing information
@@ -334,9 +373,10 @@
 	fb_info.fpga_regs[VIDCTRL] = 0x300;
 	fb_info.page = 0;
 
-	// Blank screen
-	ts7300_blt(0, 0, 639, 480, 1);
 
+	// Blank screen
+	ts7300_blt(0, 0, fb_info.xres-1, fb_info.yres, 1);
+	
 	strcpy(fb_info.gen.info.modename, "TS-7300");
 	strcpy(fb_info.gen.info.fontname, "Acorn8x8");
 	fb_info.gen.parsize = sizeof(struct ts7300fb_par);
Index: drivers/video/ts7300fb.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/video/ts7300fb.h,v
retrieving revision 1.1
retrieving revision 1.3
diff -u -r1.1 -r1.3
--- drivers/video/ts7300fb.h	23 Mar 2006 20:29:10 -0000	1.1
+++ drivers/video/ts7300fb.h	28 Feb 2007 21:34:17 -0000	1.3
@@ -6,8 +6,10 @@
 #define VIDCTRL 4
 #define DEVSEL  15
 
-#define XRES  640
-#define YRES  480
 #define BPP   16
 
 #define VIDEOMEMORYSIZE (1*1024*1024)
+
+//These vals need to be modified if we support a larger resolution
+#define MAX_WIDTH 1024
+#define MAX_HEIGHT 768
Index: drivers/video/ts7kvfb.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/drivers/video/ts7kvfb.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- drivers/video/ts7kvfb.c	25 Jan 2006 22:48:25 -0000	1.9
+++ drivers/video/ts7kvfb.c	20 Sep 2007 18:05:10 -0000	1.10
@@ -371,7 +371,7 @@
 	if(fb_info.fpga_regs[BLTSZ] == CRT) 
 	{
 		fb_info.xres = 640;
-		fb_info.yres = 320;
+		fb_info.yres = 480;
 		fb_info.id = CRT;
 	}else if(fb_info.fpga_regs[BLTSZ] == LCD_MONO) 
 	{
Index: fs/Config.in
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/Config.in,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- fs/Config.in	9 Feb 2006 22:05:46 -0000	1.6
+++ fs/Config.in	19 Mar 2007 19:19:20 -0000	1.7
@@ -184,7 +184,7 @@
 fi
 
 tristate "Yaffs filesystem on NAND" CONFIG_YAFFS_FS
-tristate "Yaffs2 filesystem on NAND" CONFIG_YAFFS2_FS
+tristate "Yaffs2 filesystem on NAND" CONFIG_YAFFS2_FS CONFIG_YAFFS_AUTO_YAFFS2
 
 mainmenu_option next_comment
 comment 'Partition Types'
Index: fs/devices.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/devices.c,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- fs/devices.c	22 Jul 2004 19:48:47 -0000	1.1.1.1
+++ fs/devices.c	18 Dec 2006 23:02:23 -0000	1.3
@@ -110,6 +110,12 @@
 		write_unlock(&chrdevs_lock);
 		return -EBUSY;
 	}
+
+	write_lock(&chrdevs_lock);
+	if((chrdevs[major].fops != NULL) && (!strcmp(chrdevs[major].name,name)))
+		return -EBUSY;
+	write_unlock(&chrdevs_lock);
+	
 	if (major >= MAX_CHRDEV)
 		return -EINVAL;
 	write_lock(&chrdevs_lock);
Index: fs/yaffs2/Makefile
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/Makefile,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- fs/yaffs2/Makefile	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/Makefile	29 Sep 2006 22:26:37 -0000	1.2
@@ -90,7 +90,7 @@
 O_TARGET := yaffs2.o
 
 #obj-y   := yaffs_fs.o yaffs_guts.o yaffs_ramem.o yaffs_mtdif.o yaffs_ecc.o yaffs_tagscompat.o yaffs_ramem2k.o yaffs_packedtags2.o yaffs_mtdif2.o
-obj-y   := yaffs_fs.o yaffs_guts.o yaffs_mtdif.o yaffs_ecc.o yaffs_tagscompat.o yaffs_packedtags2.o yaffs_mtdif2.o yaffs_tagsvalidity.o
+obj-y   := yaffs_fs.o yaffs_guts.o yaffs_mtdif.o yaffs_ecc.o yaffs_tagscompat.o yaffs_packedtags2.o yaffs_mtdif2.o yaffs_tagsvalidity.o yaffs_qsort.o yaffs_checkptrw.o yaffs_nand.o
 obj-m   := $(O_TARGET)
 
 EXTRA_CFLAGS += $(YAFFS2_CONFIGS)
Index: fs/yaffs2/devextras.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/devextras.h,v
retrieving revision 1.1
retrieving revision 1.3
diff -u -r1.1 -r1.3
--- fs/yaffs2/devextras.h	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/devextras.h	24 Apr 2007 19:30:25 -0000	1.3
@@ -1,8 +1,7 @@
 /*
- * YAFFS: Yet another FFS. A NAND-flash specific file system. 
- * devextras.h
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. 
  *
- * Copyright (C) 2002 Aleph One Ltd.
+ * Copyright (C) 2002-2007 Aleph One Ltd.
  *   for Toby Churchill Ltd and Brightstar Engineering
  *
  * Created by Charles Manning <charles@aleph1.co.uk>
@@ -12,13 +11,13 @@
  * published by the Free Software Foundation.
  *
  * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
- *
+ */
+
+/*
  * This file is just holds extra declarations used during development.
  * Most of these are from kernel includes placed here so we can use them in 
  * applications.
  *
- * $Id: devextras.h,v 1.1 2006/02/09 21:08:29 michael Exp $
- *
  */
 
 #ifndef __EXTRAS_H__
Index: fs/yaffs2/moduleconfig.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/moduleconfig.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- fs/yaffs2/moduleconfig.h	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/moduleconfig.h	24 Apr 2007 19:30:25 -0000	1.2
@@ -1,3 +1,18 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. 
+ *
+ * Copyright (C) 2002-2007 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Martin Fouts <Martin.Fouts@palmsource.com> 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
 #ifndef __YAFFS_CONFIG_H__
 #define __YAFFS_CONFIG_H__
 
@@ -27,6 +42,10 @@
 /* Meaning: Cache short names, taking more RAM, but faster look-ups */
 #define CONFIG_YAFFS_SHORT_NAMES_IN_RAM
 
+/* Default: 10 */
+/* Meaning: set the count of blocks to reserve for checkpointing */
+#define YAFFS_CHECKPOINT_RESERVED_BLOCKS 10
+
 #endif /* YAFFS_OUT_OF_TREE */
 
 #endif /* __YAFFS_CONFIG_H__ */
Index: fs/yaffs2/yaffs_ecc.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yaffs_ecc.c,v
retrieving revision 1.1
retrieving revision 1.3
diff -u -r1.1 -r1.3
--- fs/yaffs2/yaffs_ecc.c	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/yaffs_ecc.c	24 Apr 2007 19:30:25 -0000	1.3
@@ -1,27 +1,25 @@
 /*
- * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
  *
- * yaffs_ecc.c: ECC generation/correction algorithms.
- *
- * Copyright (C) 2002 Aleph One Ltd.
+ * Copyright (C) 2002-2007 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
  *
  * Created by Charles Manning <charles@aleph1.co.uk>
  *
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * version 2.1 as published by the Free Software Foundation.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
  */
 
- /*
-  * This code implements the ECC algorithm used in SmartMedia.
-  *
-  * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. 
-  * The two unused bit are set to 1.
-  * The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC 
-  * blocks are used on a 512-byte NAND page.
-  *
-  */
+/*
+ * This code implements the ECC algorithm used in SmartMedia.
+ *
+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. 
+ * The two unused bit are set to 1.
+ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC 
+ * blocks are used on a 512-byte NAND page.
+ *
+ */
 
 /* Table generated by gen-ecc.c
  * Using a table means we do not have to calculate p1..p4 and p1'..p4'
@@ -31,7 +29,7 @@
  */
 
 const char *yaffs_ecc_c_version =
-    "$Id: yaffs_ecc.c,v 1.1 2006/02/09 21:08:29 michael Exp $";
+    "$Id: yaffs_ecc.c,v 1.3 2007/04/24 19:30:25 eddie Exp $";
 
 #include "yportenv.h"
 
@@ -295,7 +293,8 @@
 	if ((cDelta | lDelta | lDeltaPrime) == 0)
 		return 0; /* no error */
 
-	if (lDelta == ~lDeltaPrime && (((cDelta ^ (cDelta >> 1)) & 0x15) == 0x15))
+	if (lDelta == ~lDeltaPrime && 
+	    (((cDelta ^ (cDelta >> 1)) & 0x15) == 0x15))
 	{
 		/* Single bit (recoverable) error in data */
 
@@ -308,6 +307,9 @@
 		if (cDelta & 0x02)
 			bit |= 0x01;
 
+		if(lDelta >= nBytes)
+			return -1;
+			
 		data[lDelta] ^= (1 << bit);
 
 		return 1; /* corrected */
Index: fs/yaffs2/yaffs_ecc.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yaffs_ecc.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- fs/yaffs2/yaffs_ecc.h	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/yaffs_ecc.h	24 Apr 2007 19:30:25 -0000	1.2
@@ -1,16 +1,16 @@
 /*
- * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. 
  *
- * yaffs_ecc.c: ECC generation/correction algorithms.
- *
- * Copyright (C) 2002 Aleph One Ltd.
+ * Copyright (C) 2002-2007 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
  *
  * Created by Charles Manning <charles@aleph1.co.uk>
  *
  * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
  * published by the Free Software Foundation.
  *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
  */
 
  /*
Index: fs/yaffs2/yaffs_fs.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yaffs_fs.c,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- fs/yaffs2/yaffs_fs.c	14 Feb 2006 18:20:41 -0000	1.2
+++ fs/yaffs2/yaffs_fs.c	24 Apr 2007 19:30:25 -0000	1.5
@@ -1,15 +1,24 @@
 /*
- * YAFFS: Yet another FFS. A NAND-flash specific file system.
- * yaffs_fs.c
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
  *
- * Copyright (C) 2002 Aleph One Ltd.
+ * Copyright (C) 2002-2007 Aleph One Ltd.
  *   for Toby Churchill Ltd and Brightstar Engineering
  *
  * Created by Charles Manning <charles@aleph1.co.uk>
+ * Acknowledgements:
+ * Luc van OostenRyck for numerous patches.
+ * Nick Bane for numerous patches.
+ * Nick Bane for 2.5/2.6 integration.
+ * Andras Toth for mknod rdev issue.
+ * Michael Fischer for finding the problem with inode inconsistency.
+ * Some code bodily lifted from JFFS
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
+ */
+
+/*
  *
  * This is the file system front-end to YAFFS that hooks it up to
  * the VFS.
@@ -20,24 +29,18 @@
  * >> 2.6: sb->s_fs_info  points to the yaffs_Device associated with this
  *         superblock
  * >> inode->u.generic_ip points to the associated yaffs_Object.
- *
- * Acknowledgements:
- * * Luc van OostenRyck for numerous patches.
- * * Nick Bane for numerous patches.
- * * Nick Bane for 2.5/2.6 integration.
- * * Andras Toth for mknod rdev issue.
- * * Michael Fischer for finding the problem with inode inconsistency.
- * * Some code bodily lifted from JFFS2.
  */
 
 const char *yaffs_fs_c_version =
-    "$Id: yaffs_fs.c,v 1.2 2006/02/14 18:20:41 michael Exp $";
+    "$Id: yaffs_fs.c,v 1.5 2007/04/24 19:30:25 eddie Exp $";
 extern const char *yaffs_guts_c_version;
 
+#include <linux/version.h>
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
 #include <linux/config.h>
+#endif
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/version.h>
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/list.h>
@@ -48,6 +51,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/interrupt.h>
 #include <linux/string.h>
+#include <linux/ctype.h>
 
 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
 
@@ -65,6 +69,11 @@
 #define	BDEVNAME_SIZE		0
 #define	yaffs_devname(sb, buf)	kdevname(sb->s_dev)
 
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+/* added NCB 26/5/2006 for 2.4.25-vrs2-tcl1 kernel */
+#define __user
+#endif
+
 #endif
 
 #include <asm/uaccess.h>
@@ -72,7 +81,11 @@
 #include "yportenv.h"
 #include "yaffs_guts.h"
 
-unsigned yaffs_traceMask = 0; // YAFFS_TRACE_ALWAYS | YAFFS_TRACE_BAD_BLOCKS /* | 0xFFFFFFFF */;
+unsigned yaffs_traceMask = YAFFS_TRACE_ALWAYS | 
+			   YAFFS_TRACE_BAD_BLOCKS  
+			   /* | YAFFS_TRACE_CHECKPOINT 
+			      | YAFFS_TRACE_OS
+			      | 0xFFFFFFFF */; 
 
 #include <linux/mtd/mtd.h>
 #include "yaffs_mtdif.h"
@@ -80,7 +93,13 @@
 
 /*#define T(x) printk x */
 
-#define yaffs_InodeToObject(iptr) ((yaffs_Object *)((iptr)->u.generic_ip))
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
+#define yaffs_InodeToObjectLV(iptr) (iptr)->i_private
+#else
+#define yaffs_InodeToObjectLV(iptr) (iptr)->u.generic_ip
+#endif
+
+#define yaffs_InodeToObject(iptr) ((yaffs_Object *)(yaffs_InodeToObjectLV(iptr)))
 #define yaffs_DentryToObject(dptr) yaffs_InodeToObject((dptr)->d_inode)
 
 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
@@ -94,7 +113,11 @@
 static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n,
 				loff_t * pos);
 
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+static int yaffs_file_flush(struct file *file, fl_owner_t id);
+#else
 static int yaffs_file_flush(struct file *file);
+#endif
 
 static int yaffs_sync_object(struct file *file, struct dentry *dentry,
 			     int datasync);
@@ -128,7 +151,19 @@
 			struct inode *new_dir, struct dentry *new_dentry);
 static int yaffs_setattr(struct dentry *dentry, struct iattr *attr);
 
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+static int yaffs_sync_fs(struct super_block *sb, int wait);
+static void yaffs_write_super(struct super_block *sb);
+#else
+static int yaffs_sync_fs(struct super_block *sb);
+static int yaffs_write_super(struct super_block *sb);
+#endif
+
+static int yaffs_remount_fs(struct super_block *, int *, char *);
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf);
+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
 static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf);
 #else
 static int yaffs_statfs(struct super_block *sb, struct statfs *buf);
@@ -166,11 +201,22 @@
 };
 
 static struct file_operations yaffs_file_operations = {
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
+	.read = do_sync_read,
+	.write = do_sync_write,
+	.aio_read = generic_file_aio_read,
+	.aio_write = generic_file_aio_write,
+#else
 	.read = generic_file_read,
 	.write = generic_file_write,
+#endif
 	.mmap = generic_file_mmap,
 	.flush = yaffs_file_flush,
 	.fsync = yaffs_sync_object,
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+	.sendfile = generic_file_sendfile,
+#endif
+
 };
 
 static struct inode_operations yaffs_file_inode_operations = {
@@ -207,8 +253,11 @@
 	.read_inode = yaffs_read_inode,
 	.put_inode = yaffs_put_inode,
 	.put_super = yaffs_put_super,
+	.remount_fs = yaffs_remount_fs,
 	.delete_inode = yaffs_delete_inode,
 	.clear_inode = yaffs_clear_inode,
+	.sync_fs = yaffs_sync_fs,
+	.write_super = yaffs_write_super,
 };
 
 static void yaffs_GrossLock(yaffs_Device * dev)
@@ -379,7 +428,7 @@
 		 * the yaffs_Object.
 		 */
 		obj->myInode = NULL;
-		inode->u.generic_ip = NULL;
+		yaffs_InodeToObjectLV(inode) = NULL;
 
 		/* If the object freeing was deferred, then the real
 		 * free happens now.
@@ -420,7 +469,11 @@
 	clear_inode(inode);
 }
 
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+static int yaffs_file_flush(struct file *file, fl_owner_t id)
+#else
 static int yaffs_file_flush(struct file *file)
+#endif
 {
 	yaffs_Object *obj = yaffs_DentryToObject(file->f_dentry);
 
@@ -624,20 +677,56 @@
 
 	T(YAFFS_TRACE_OS,
 	  (KERN_DEBUG "yaffs_commit_write returning %d\n",
-	   nWritten == nBytes ? 0 : -1));
+	   nWritten == nBytes ? 0 : nWritten));
 
-	return nWritten == nBytes ? 0 : -1;
+	return nWritten == nBytes ? 0 : nWritten;
 
 }
 
 static void yaffs_FillInodeFromObject(struct inode *inode, yaffs_Object * obj)
 {
 	if (inode && obj) {
+
+
+		/* Check mode against the variant type and attempt to repair if broken. */
+ 		__u32 mode = obj->yst_mode;
+ 		switch( obj->variantType ){
+ 		case YAFFS_OBJECT_TYPE_FILE :
+ 		        if( ! S_ISREG(mode) ){
+ 			        obj->yst_mode &= ~S_IFMT;
+ 			        obj->yst_mode |= S_IFREG;
+ 			}
+ 
+ 			break;
+ 		case YAFFS_OBJECT_TYPE_SYMLINK :
+ 		        if( ! S_ISLNK(mode) ){
+ 			        obj->yst_mode &= ~S_IFMT;
+ 				obj->yst_mode |= S_IFLNK;
+ 			}
+ 
+ 			break;
+ 		case YAFFS_OBJECT_TYPE_DIRECTORY :
+ 		        if( ! S_ISDIR(mode) ){
+ 			        obj->yst_mode &= ~S_IFMT;
+ 			        obj->yst_mode |= S_IFDIR;
+ 			}
+ 
+ 			break;
+ 		case YAFFS_OBJECT_TYPE_UNKNOWN :
+ 		case YAFFS_OBJECT_TYPE_HARDLINK :
+ 		case YAFFS_OBJECT_TYPE_SPECIAL :
+ 		default:
+ 		        /* TODO? */
+ 		        break;
+ 		}
+
 		inode->i_ino = obj->objectId;
 		inode->i_mode = obj->yst_mode;
 		inode->i_uid = obj->yst_uid;
 		inode->i_gid = obj->yst_gid;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
 		inode->i_blksize = inode->i_sb->s_blocksize;
+#endif
 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
 
 		inode->i_rdev = old_decode_dev(obj->yst_rdev);
@@ -689,7 +778,8 @@
 			break;
 		}
 
-		inode->u.generic_ip = obj;
+		yaffs_InodeToObjectLV(inode) = obj;
+
 		obj->myInode = inode;
 
 	} else {
@@ -784,8 +874,7 @@
 
 	}
 	yaffs_GrossUnlock(dev);
-
-	return nWritten != n ? -ENOSPC : nWritten;
+	return nWritten == 0 ? -ENOSPC : nWritten;
 }
 
 static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir)
@@ -833,17 +922,15 @@
 	}
 
 	curoffs = 1;
-//++ PATCH
+
 	/* If the directory has changed since the open or last call to
 	   readdir, rewind to after the 2 canned entries. */
 
 	if (f->f_version != inode->i_version) {
-	  offset = 2;
-	  f->f_pos = offset;
-	  f->f_version = inode->i_version;
+		offset = 2;
+		f->f_pos = offset;
+		f->f_version = inode->i_version;
 	}
-//++ ENDPATCH
-
 
 	list_for_each(i, &obj->variant.directoryVariant.children) {
 		curoffs++;
@@ -900,6 +987,9 @@
 	int error = -ENOSPC;
 	uid_t uid = current->fsuid;
 	gid_t gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
+	
+	if((dir->i_mode & S_ISGID) && S_ISDIR(mode))
+		mode |= S_ISGID;
 
 	if (parent) {
 		T(YAFFS_TRACE_OS,
@@ -1205,14 +1295,21 @@
 	return error;
 }
 
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+	yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev;
+	struct super_block *sb = dentry->d_sb;
+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
 static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+	yaffs_Device *dev = yaffs_SuperToDevice(sb);
 #else
 static int yaffs_statfs(struct super_block *sb, struct statfs *buf)
-#endif
 {
-
 	yaffs_Device *dev = yaffs_SuperToDevice(sb);
+#endif
+
 	T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_statfs\n"));
 
 	yaffs_GrossLock(dev);
@@ -1220,23 +1317,23 @@
 	buf->f_type = YAFFS_MAGIC;
 	buf->f_bsize = sb->s_blocksize;
 	buf->f_namelen = 255;
-	if (sb->s_blocksize > dev->nBytesPerChunk) {
+	if (sb->s_blocksize > dev->nDataBytesPerChunk) {
 
 		buf->f_blocks =
 		    (dev->endBlock - dev->startBlock +
 		     1) * dev->nChunksPerBlock / (sb->s_blocksize /
-						  dev->nBytesPerChunk);
+						  dev->nDataBytesPerChunk);
 		buf->f_bfree =
 		    yaffs_GetNumberOfFreeChunks(dev) / (sb->s_blocksize /
-							dev->nBytesPerChunk);
+							dev->nDataBytesPerChunk);
 	} else {
 
 		buf->f_blocks =
 		    (dev->endBlock - dev->startBlock +
-		     1) * dev->nChunksPerBlock * (dev->nBytesPerChunk /
+		     1) * dev->nChunksPerBlock * (dev->nDataBytesPerChunk /
 						  sb->s_blocksize);
 		buf->f_bfree =
-		    yaffs_GetNumberOfFreeChunks(dev) * (dev->nBytesPerChunk /
+		    yaffs_GetNumberOfFreeChunks(dev) * (dev->nDataBytesPerChunk /
 							sb->s_blocksize);
 	}
 	buf->f_files = 0;
@@ -1247,6 +1344,50 @@
 	return 0;
 }
 
+static int yaffs_do_sync_fs(struct super_block *sb)
+{
+
+	yaffs_Device *dev = yaffs_SuperToDevice(sb);
+	T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_do_sync_fs\n"));
+
+	if(sb->s_dirt) {
+		yaffs_GrossLock(dev);
+
+		if(dev)
+			yaffs_CheckpointSave(dev);
+		
+		yaffs_GrossUnlock(dev);
+
+		sb->s_dirt = 0;
+	}
+	return 0;
+}
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+static void yaffs_write_super(struct super_block *sb)
+#else
+static int yaffs_write_super(struct super_block *sb)
+#endif
+{
+
+	T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_write_super\n"));
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18))
+	return yaffs_do_sync_fs(sb);
+#endif
+}
+
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+static int yaffs_sync_fs(struct super_block *sb, int wait)
+#else
+static int yaffs_sync_fs(struct super_block *sb)
+#endif
+{
+	T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_sync_fs\n"));
+	return yaffs_do_sync_fs(sb);
+}
+
+
 static void yaffs_read_inode(struct inode *inode)
 {
 	/* NB This is called as a side effect of other functions, but
@@ -1271,19 +1412,62 @@
 
 static LIST_HEAD(yaffs_dev_list);
 
+static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data)
+{
+	yaffs_Device    *dev = yaffs_SuperToDevice(sb);
+
+	if( *flags & MS_RDONLY ) {
+		struct mtd_info *mtd = yaffs_SuperToDevice(sb)->genericDevice;
+	    
+		T(YAFFS_TRACE_OS,
+			(KERN_DEBUG "yaffs_remount_fs: %s: RO\n", dev->name ));
+
+		yaffs_GrossLock(dev);
+     	 
+		yaffs_FlushEntireDeviceCache(dev);
+    	
+		yaffs_CheckpointSave(dev);
+ 
+		if (mtd->sync)
+			mtd->sync(mtd);
+
+		yaffs_GrossUnlock(dev);
+	}
+	else {
+		T(YAFFS_TRACE_OS, 
+			(KERN_DEBUG "yaffs_remount_fs: %s: RW\n", dev->name ));
+	}
+ 
+	return 0;
+}
+
 static void yaffs_put_super(struct super_block *sb)
 {
 	yaffs_Device *dev = yaffs_SuperToDevice(sb);
 
+	T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_put_super\n"));
+
 	yaffs_GrossLock(dev);
+	
+	yaffs_FlushEntireDeviceCache(dev);
+
+	yaffs_CheckpointSave(dev);
+
 	if (dev->putSuperFunc) {
 		dev->putSuperFunc(sb);
 	}
+
 	yaffs_Deinitialise(dev);
+	
 	yaffs_GrossUnlock(dev);
 
 	/* we assume this is protected by lock_kernel() in mount/umount */
 	list_del(&dev->devList);
+	
+	if(dev->spareBuffer){
+		YFREE(dev->spareBuffer);
+		dev->spareBuffer = NULL;
+	}
 
 	kfree(dev);
 }
@@ -1302,6 +1486,14 @@
 }
 
 
+static void yaffs_MarkSuperBlockDirty(void *vsb)
+{
+	struct super_block *sb = (struct super_block *)vsb;
+	T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_MarkSuperBlockDirty() sb = %p\n",sb));
+	if(sb) 
+		sb->s_dirt = 1;
+}
+
 static struct super_block *yaffs_internal_read_super(int yaffsVersion,
 						     struct super_block *sb,
 						     void *data, int silent)
@@ -1317,7 +1509,6 @@
 	sb->s_magic = YAFFS_MAGIC;
 	sb->s_op = &yaffs_super_ops;
 
-	/*
 	if (!sb)
 		printk(KERN_INFO "yaffs: sb is NULL\n");
 	else if (!sb->s_dev)
@@ -1328,7 +1519,7 @@
 		printk(KERN_INFO "yaffs: dev is %d name is \"%s\"\n",
 		       sb->s_dev,
 		       yaffs_devname(sb, devname_buf));
-	*/
+
 	sb->s_blocksize = PAGE_CACHE_SIZE;
 	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
 	T(YAFFS_TRACE_OS, ("yaffs_read_super: Using yaffs%d\n", yaffsVersion));
@@ -1372,18 +1563,38 @@
 	T(YAFFS_TRACE_OS, (" writeoob %p\n", mtd->write_oob));
 	T(YAFFS_TRACE_OS, (" block_isbad %p\n", mtd->block_isbad));
 	T(YAFFS_TRACE_OS, (" block_markbad %p\n", mtd->block_markbad));
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+	T(YAFFS_TRACE_OS, (" writesize %d\n", mtd->writesize));
+#else
 	T(YAFFS_TRACE_OS, (" oobblock %d\n", mtd->oobblock));
+#endif
 	T(YAFFS_TRACE_OS, (" oobsize %d\n", mtd->oobsize));
 	T(YAFFS_TRACE_OS, (" erasesize %d\n", mtd->erasesize));
 	T(YAFFS_TRACE_OS, (" size %d\n", mtd->size));
-	
+
 #ifdef CONFIG_YAFFS_AUTO_YAFFS2
 
 	if (yaffsVersion == 1 && 
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+	    mtd->writesize >= 2048) {
+#else
 	    mtd->oobblock >= 2048) {
+#endif
 	    T(YAFFS_TRACE_ALWAYS,("yaffs: auto selecting yaffs2\n"));
 	    yaffsVersion = 2;
 	}	
+	
+	/* Added NCB 26/5/2006 for completeness */
+	if (yaffsVersion == 2 && 
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+	    mtd->writesize == 512) {
+#else
+	    mtd->oobblock == 512) {
+#endif
+	    T(YAFFS_TRACE_ALWAYS,("yaffs: auto selecting yaffs1\n"));
+	    yaffsVersion = 1;
+	}	
+
 #endif
 
 	if (yaffsVersion == 2) {
@@ -1393,18 +1604,27 @@
 		    !mtd->block_markbad ||
 		    !mtd->read ||
 		    !mtd->write ||
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+		    !mtd->read_oob || !mtd->write_oob) {
+#else
 		    !mtd->write_ecc ||
 		    !mtd->read_ecc || !mtd->read_oob || !mtd->write_oob) {
+#endif
 			T(YAFFS_TRACE_ALWAYS,
 			  ("yaffs: MTD device does not support required "
 			   "functions\n"));;
 			return NULL;
 		}
 
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+		if (mtd->writesize < YAFFS_MIN_YAFFS2_CHUNK_SIZE ||
+#else
 		if (mtd->oobblock < YAFFS_MIN_YAFFS2_CHUNK_SIZE ||
+#endif
 		    mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) {
+
 			T(YAFFS_TRACE_ALWAYS,
-			  ("yaffs: MTD device does not support have the "
+			  ("yaffs: MTD device does not have the "
 			   "right page sizes\n"));
 			return NULL;
 		}
@@ -1413,15 +1633,23 @@
 		if (!mtd->erase ||
 		    !mtd->read ||
 		    !mtd->write ||
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+		    !mtd->read_oob || !mtd->write_oob) {
+#else
 		    !mtd->write_ecc ||
 		    !mtd->read_ecc || !mtd->read_oob || !mtd->write_oob) {
+#endif
 			T(YAFFS_TRACE_ALWAYS,
 			  ("yaffs: MTD device does not support required "
 			   "functions\n"));;
 			return NULL;
 		}
 
-		if (mtd->oobblock != YAFFS_BYTES_PER_CHUNK ||
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+		if (mtd->writesize < YAFFS_BYTES_PER_CHUNK ||
+#else
+		if (mtd->oobblock < YAFFS_BYTES_PER_CHUNK ||
+#endif
 		    mtd->oobsize != YAFFS_BYTES_PER_SPARE) {
 			T(YAFFS_TRACE_ALWAYS,
 			  ("yaffs: MTD device does not support have the "
@@ -1458,7 +1686,7 @@
 	dev->startBlock = 0;
 	dev->endBlock = nBlocks - 1;
 	dev->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK;
-	dev->nBytesPerChunk = YAFFS_BYTES_PER_CHUNK;
+	dev->nDataBytesPerChunk = YAFFS_BYTES_PER_CHUNK;
 	dev->nReservedBlocks = 5;
 	dev->nShortOpCaches = 10;	/* Enable short op caching */
 
@@ -1472,9 +1700,16 @@
 		dev->queryNANDBlock = nandmtd2_QueryNANDBlock;
 		dev->spareBuffer = YMALLOC(mtd->oobsize);
 		dev->isYaffs2 = 1;
-		dev->nBytesPerChunk = mtd->oobblock;
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+		dev->nDataBytesPerChunk = mtd->writesize;
+		dev->nChunksPerBlock = mtd->erasesize / mtd->writesize;
+#else
+		dev->nDataBytesPerChunk = mtd->oobblock;
 		dev->nChunksPerBlock = mtd->erasesize / mtd->oobblock;
+#endif
 		nBlocks = mtd->size / mtd->erasesize;
+
+		dev->nCheckpointReservedBlocks = 10;
 		dev->startBlock = 0;
 		dev->endBlock = nBlocks - 1;
 	} else {
@@ -1487,6 +1722,10 @@
 	dev->initialiseNAND = nandmtd_InitialiseNAND;
 
 	dev->putSuperFunc = yaffs_MTDPutSuper;
+	
+	dev->superBlock = (void *)sb;
+	dev->markSuperBlockDirty = yaffs_MarkSuperBlockDirty;
+	
 
 #ifndef CONFIG_YAFFS_DOES_ECC
 	dev->useNANDECC = 1;
@@ -1539,7 +1778,7 @@
 	return sb;
 }
 
-
+#ifdef CONFIG_YAFFS_YAFFS1
 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
 static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data,
 					 int silent)
@@ -1547,6 +1786,16 @@
 	return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL;
 }
 
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+static int yaffs_read_super(struct file_system_type *fs,
+			    int flags, const char *dev_name,
+			    void *data, struct vfsmount *mnt)
+{
+
+	return get_sb_bdev(fs, flags, dev_name, data,
+			   yaffs_internal_read_super_mtd, mnt);
+}
+#else
 static struct super_block *yaffs_read_super(struct file_system_type *fs,
 					    int flags, const char *dev_name,
 					    void *data)
@@ -1555,6 +1804,7 @@
 	return get_sb_bdev(fs, flags, dev_name, data,
 			   yaffs_internal_read_super_mtd);
 }
+#endif
 
 static struct file_system_type yaffs_fs_type = {
 	.owner = THIS_MODULE,
@@ -1569,10 +1819,10 @@
 {
 	return yaffs_internal_read_super(1, sb, data, silent);
 }
-
 static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super,
 		      FS_REQUIRES_DEV);
 #endif
+#endif				/* CONFIG_YAFFS_YAFFS1 */
 
 
 #ifdef CONFIG_YAFFS_YAFFS2
@@ -1584,6 +1834,15 @@
 	return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL;
 }
 
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+static int yaffs2_read_super(struct file_system_type *fs,
+			int flags, const char *dev_name, void *data,
+			struct vfsmount *mnt)
+{
+	return get_sb_bdev(fs, flags, dev_name, data,
+			yaffs2_internal_read_super_mtd, mnt);
+}
+#else
 static struct super_block *yaffs2_read_super(struct file_system_type *fs,
 					     int flags, const char *dev_name,
 					     void *data)
@@ -1592,6 +1851,7 @@
 	return get_sb_bdev(fs, flags, dev_name, data,
 			   yaffs2_internal_read_super_mtd);
 }
+#endif
 
 static struct file_system_type yaffs2_fs_type = {
 	.owner = THIS_MODULE,
@@ -1696,6 +1956,119 @@
 	return buf - page < count ? buf - page : count;
 }
 
+/**
+ * Set the verbosity of the warnings and error messages.
+ *
+ */
+
+static struct {
+	char *mask_name;
+	unsigned mask_bitfield;
+} mask_flags[] = {
+	{"allocate", YAFFS_TRACE_ALLOCATE},
+	{"always", YAFFS_TRACE_ALWAYS},
+	{"bad_blocks", YAFFS_TRACE_BAD_BLOCKS},
+	{"buffers", YAFFS_TRACE_BUFFERS},
+	{"bug", YAFFS_TRACE_BUG},
+	{"deletion", YAFFS_TRACE_DELETION},
+	{"erase", YAFFS_TRACE_ERASE},
+	{"error", YAFFS_TRACE_ERROR},
+	{"gc_detail", YAFFS_TRACE_GC_DETAIL},
+	{"gc", YAFFS_TRACE_GC},
+	{"mtd", YAFFS_TRACE_MTD},
+	{"nandaccess", YAFFS_TRACE_NANDACCESS},
+	{"os", YAFFS_TRACE_OS},
+	{"scan_debug", YAFFS_TRACE_SCAN_DEBUG},
+	{"scan", YAFFS_TRACE_SCAN},
+	{"tracing", YAFFS_TRACE_TRACING},
+	{"write", YAFFS_TRACE_WRITE},
+	{"all", 0xffffffff},
+	{"none", 0},
+	{NULL, 0},
+};
+
+static int yaffs_proc_write(struct file *file, const char *buf,
+					 unsigned long count, void *data)
+{
+	unsigned rg = 0, mask_bitfield;
+	char *end, *mask_name;
+	int i;
+	int done = 0;
+	int add, len = 0;
+	int pos = 0;
+
+	rg = yaffs_traceMask;
+
+	while (!done && (pos < count)) {
+		done = 1;
+		while ((pos < count) && isspace(buf[pos])) {
+			pos++;
+		}
+
+		switch (buf[pos]) {
+		case '+':
+		case '-':
+		case '=':
+			add = buf[pos];
+			pos++;
+			break;
+
+		default:
+			add = ' ';
+			break;
+		}
+		mask_name = NULL;
+		mask_bitfield = simple_strtoul(buf + pos, &end, 0);
+		if (end > buf + pos) {
+			mask_name = "numeral";
+			len = end - (buf + pos);
+			done = 0;
+		} else {
+
+			for (i = 0; mask_flags[i].mask_name != NULL; i++) {
+				len = strlen(mask_flags[i].mask_name);
+				if (strncmp(buf + pos, mask_flags[i].mask_name, len) == 0) {
+					mask_name = mask_flags[i].mask_name;
+					mask_bitfield = mask_flags[i].mask_bitfield;
+					done = 0;
+					break;
+				}
+			}
+		}
+
+		if (mask_name != NULL) {
+			pos += len;
+			done = 0;
+			switch(add) {
+			case '-':
+				rg &= ~mask_bitfield;
+				break;
+			case '+':
+				rg |= mask_bitfield;
+				break;
+			case '=':
+				rg = mask_bitfield;
+				break;
+			default:
+				rg |= mask_bitfield;
+				break;
+			}
+		}
+	}
+
+	yaffs_traceMask = rg | YAFFS_TRACE_ALWAYS;
+	
+	if (rg & YAFFS_TRACE_ALWAYS) {
+		for (i = 0; mask_flags[i].mask_name != NULL; i++) {
+			char flag;
+			flag = ((rg & mask_flags[i].mask_bitfield) == mask_flags[i].mask_bitfield) ? '+' : '-';
+			printk("%c%s\n", flag, mask_flags[i].mask_name);
+		}
+	}
+
+	return count;
+}
+
 /* Stuff to handle installation of file systems */
 struct file_system_to_install {
 	struct file_system_type *fst;
@@ -1703,12 +2076,12 @@
 };
 
 static struct file_system_to_install fs_to_install[] = {
-//#ifdef CONFIG_YAFFS_YAFFS1
+#ifdef CONFIG_YAFFS_YAFFS1
 	{&yaffs_fs_type, 0},
-//#endif
-//#ifdef CONFIG_YAFFS_YAFFS2
+#endif
+#ifdef CONFIG_YAFFS_YAFFS2
 	{&yaffs2_fs_type, 0},
-//#endif
+#endif
 	{NULL, 0}
 };
 
@@ -1721,11 +2094,15 @@
 	  ("yaffs " __DATE__ " " __TIME__ " Installing. \n"));
 
 	/* Install the proc_fs entry */
-	my_proc_entry = create_proc_read_entry("yaffs",
+	my_proc_entry = create_proc_entry("yaffs",
 					       S_IRUGO | S_IFREG,
-					       &proc_root,
-					       yaffs_proc_read, NULL);
-	if (!my_proc_entry) {
+					       &proc_root);
+
+	if (my_proc_entry) {
+		my_proc_entry->write_proc = yaffs_proc_write;
+		my_proc_entry->read_proc = yaffs_proc_read;
+		my_proc_entry->data = NULL;
+	} else {
 		return -ENOMEM;
 	}
 
@@ -1783,5 +2160,5 @@
 module_exit(exit_yaffs_fs)
 
 MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system");
-MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002,2003,2004");
+MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2006");
 MODULE_LICENSE("GPL");
Index: fs/yaffs2/yaffs_guts.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yaffs_guts.c,v
retrieving revision 1.1
retrieving revision 1.4
diff -u -r1.1 -r1.4
--- fs/yaffs2/yaffs_guts.c	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/yaffs_guts.c	24 Apr 2007 19:30:25 -0000	1.4
@@ -1,5784 +1,6680 @@
-/*
- * YAFFS: Yet another FFS. A NAND-flash specific file system. 
- *
- * Copyright (C) 2002 Aleph One Ltd.
- *   for Toby Churchill Ltd and Brightstar Engineering
- *
- * Created by Charles Manning <charles@aleph1.co.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-const char *yaffs_guts_c_version =
-    "$Id: yaffs_guts.c,v 1.1 2006/02/09 21:08:29 michael Exp $";
-
-#include "yportenv.h"
-
-#include "yaffsinterface.h"
-#include "yaffs_guts.h"
-#include "yaffs_tagsvalidity.h"
-
-#include "yaffs_tagscompat.h"
-
-#ifdef CONFIG_YAFFS_WINCE
-void yfsd_LockYAFFS(BOOL fsLockOnly);
-void yfsd_UnlockYAFFS(BOOL fsLockOnly);
-#endif
-
-#define YAFFS_PASSIVE_GC_CHUNKS 2
-
-#include "yaffs_ecc.h"
-
-/* NAND access */
-
-static Y_INLINE int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device * dev,
-						    int chunkInNAND,
-						    __u8 * buffer,
-						    yaffs_ExtendedTags * tags);
-static Y_INLINE int yaffs_WriteChunkWithTagsToNAND(yaffs_Device * dev,
-						   int chunkInNAND,
-						   const __u8 * data,
-						   yaffs_ExtendedTags * tags);
-static Y_INLINE int yaffs_MarkBlockBad(yaffs_Device * dev, int blockNo);
-static Y_INLINE int yaffs_QueryInitialBlockState(yaffs_Device * dev,
-						 int blockNo,
-						 yaffs_BlockState * state,
-						 unsigned *sequenceNumber);
-/* Robustification (if it ever comes about...) */
-static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND);
-static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND);
-static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND,
-				     const __u8 * data,
-				     const yaffs_ExtendedTags * tags);
-static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND,
-				    const yaffs_ExtendedTags * tags);
-
-/* Other local prototypes */
-static int yaffs_UnlinkObject( yaffs_Object *obj);
-
-static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device * dev,
-					     const __u8 * buffer,
-					     yaffs_ExtendedTags * tags,
-					     int useReserve);
-static int yaffs_PutChunkIntoFile(yaffs_Object * in, int chunkInInode,
-				  int chunkInNAND, int inScan);
-
-static yaffs_Object *yaffs_CreateNewObject(yaffs_Device * dev, int number,
-					   yaffs_ObjectType type);
-static void yaffs_AddObjectToDirectory(yaffs_Object * directory,
-				       yaffs_Object * obj);
-static int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name,
-				    int force, int isShrink, int shadows);
-static void yaffs_RemoveObjectFromDirectory(yaffs_Object * obj);
-static int yaffs_CheckStructures(void);
-static int yaffs_DeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, __u32 level,
-			      int chunkOffset, int *limit);
-static int yaffs_DoGenericObjectDeletion(yaffs_Object * in);
-
-static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blockNo);
-
-static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo);
-static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer,
-				    int lineNo);
-
-static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,
-				  int chunkInNAND);
-
-static int yaffs_UnlinkWorker(yaffs_Object * obj);
-static void yaffs_DestroyObject(yaffs_Object * obj);
-
-static int yaffs_TagsMatch(const yaffs_ExtendedTags * tags, int objectId,
-			   int chunkInObject);
-
-loff_t yaffs_GetFileSize(yaffs_Object * obj);
-
-static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve);
-
-static void yaffs_VerifyFreeChunks(yaffs_Device * dev);
-
-#ifdef YAFFS_PARANOID
-static int yaffs_CheckFileSanity(yaffs_Object * in);
-#else
-#define yaffs_CheckFileSanity(in)
-#endif
-
-static void yaffs_InvalidateWholeChunkCache(yaffs_Object * in);
-static void yaffs_InvalidateChunkCache(yaffs_Object * object, int chunkId);
-
-
-/*
- *   Start of real code.
- */
-
-/*
- * NAND access layer
- */
- 
-
-
-static int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
-					   __u8 * buffer,
-					   yaffs_ExtendedTags * tags)
-{
-	chunkInNAND -= dev->chunkOffset;
-
-	if (dev->readChunkWithTagsFromNAND)
-		return dev->readChunkWithTagsFromNAND(dev, chunkInNAND, buffer,
-						      tags);
-	else
-		return yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(dev,
-									chunkInNAND,
-									buffer,
-									tags);
-}
-
-static Y_INLINE int yaffs_WriteChunkWithTagsToNAND(yaffs_Device * dev,
-						   int chunkInNAND,
-						   const __u8 * buffer,
-						   yaffs_ExtendedTags * tags)
-{
-	chunkInNAND -= dev->chunkOffset;
-
-	if (tags) {
-		tags->sequenceNumber = dev->sequenceNumber;
-		tags->chunkUsed = 1;
-		if (!yaffs_ValidateTags(tags)) {
-			T(YAFFS_TRACE_ERROR,
-			  (TSTR("Writing uninitialised tags" TENDSTR)));
-			YBUG();
-		}
-		T(YAFFS_TRACE_WRITE,
-		  (TSTR("Writing chunk %d tags %d %d" TENDSTR), chunkInNAND,
-		   tags->objectId, tags->chunkId));
-	} else {
-		T(YAFFS_TRACE_ERROR, (TSTR("Writing with no tags" TENDSTR)));
-		YBUG();
-	}
-
-	if (dev->writeChunkWithTagsToNAND)
-		return dev->writeChunkWithTagsToNAND(dev, chunkInNAND, buffer,
-						     tags);
-	else
-		return yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(dev,
-								       chunkInNAND,
-								       buffer,
-								       tags);
-}
-
-static Y_INLINE int yaffs_MarkBlockBad(yaffs_Device * dev, int blockNo)
-{
-	blockNo -= dev->blockOffset;
-
-	if (dev->markNANDBlockBad)
-		return dev->markNANDBlockBad(dev, blockNo);
-	else
-		return yaffs_TagsCompatabilityMarkNANDBlockBad(dev, blockNo);
-}
-static Y_INLINE int yaffs_QueryInitialBlockState(yaffs_Device * dev,
-						 int blockNo,
-						 yaffs_BlockState * state,
-						 unsigned *sequenceNumber)
-{
-	blockNo -= dev->blockOffset;
-
-	if (dev->queryNANDBlock)
-		return dev->queryNANDBlock(dev, blockNo, state, sequenceNumber);
-	else
-		return yaffs_TagsCompatabilityQueryNANDBlock(dev, blockNo,
-							     state,
-							     sequenceNumber);
-}
-
-static int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,
-				  int blockInNAND)
-{
-	int result;
-
-	blockInNAND -= dev->blockOffset;
-
-	dev->nBlockErasures++;
-	result = dev->eraseBlockInNAND(dev, blockInNAND);
-
-	/* If at first we don't succeed, try again *once*.*/
-	if (!result)
-		result = dev->eraseBlockInNAND(dev, blockInNAND);	
-	return result;
-}
-
-static int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev)
-{
-	return dev->initialiseNAND(dev);
-}
-
-/* 
- * Temporary buffer manipulations.
- */
-
-static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo)
-{
-	int i, j;
-	for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
-		if (dev->tempBuffer[i].line == 0) {
-			dev->tempBuffer[i].line = lineNo;
-			if ((i + 1) > dev->maxTemp) {
-				dev->maxTemp = i + 1;
-				for (j = 0; j <= i; j++)
-					dev->tempBuffer[j].maxLine =
-					    dev->tempBuffer[j].line;
-			}
-
-			return dev->tempBuffer[i].buffer;
-		}
-	}
-
-	T(YAFFS_TRACE_BUFFERS,
-	  (TSTR("Out of temp buffers at line %d, other held by lines:"),
-	   lineNo));
-	for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
-		T(YAFFS_TRACE_BUFFERS, (TSTR(" %d "), dev->tempBuffer[i].line));
-	}
-	T(YAFFS_TRACE_BUFFERS, (TSTR(" " TENDSTR)));
-
-	/*
-	 * If we got here then we have to allocate an unmanaged one
-	 * This is not good.
-	 */
-
-	dev->unmanagedTempAllocations++;
-	return YMALLOC(dev->nBytesPerChunk);
-
-}
-
-static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer,
-				    int lineNo)
-{
-	int i;
-	for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
-		if (dev->tempBuffer[i].buffer == buffer) {
-			dev->tempBuffer[i].line = 0;
-			return;
-		}
-	}
-
-	if (buffer) {
-		/* assume it is an unmanaged one. */
-		T(YAFFS_TRACE_BUFFERS,
-		  (TSTR("Releasing unmanaged temp buffer in line %d" TENDSTR),
-		   lineNo));
-		YFREE(buffer);
-		dev->unmanagedTempDeallocations++;
-	}
-
-}
-
-/*
- * Chunk bitmap manipulations
- */
-
-static Y_INLINE __u8 *yaffs_BlockBits(yaffs_Device * dev, int blk)
-{
-	if (blk < dev->internalStartBlock || blk > dev->internalEndBlock) {
-		T(YAFFS_TRACE_ERROR,
-		  (TSTR("**>> yaffs: BlockBits block %d is not valid" TENDSTR),
-		   blk));
-		YBUG();
-	}
-	return dev->chunkBits +
-	    (dev->chunkBitmapStride * (blk - dev->internalStartBlock));
-}
-
-static Y_INLINE void yaffs_ClearChunkBits(yaffs_Device * dev, int blk)
-{
-	__u8 *blkBits = yaffs_BlockBits(dev, blk);
-
-	memset(blkBits, 0, dev->chunkBitmapStride);
-}
-
-static Y_INLINE void yaffs_ClearChunkBit(yaffs_Device * dev, int blk, int chunk)
-{
-	__u8 *blkBits = yaffs_BlockBits(dev, blk);
-
-	blkBits[chunk / 8] &= ~(1 << (chunk & 7));
-}
-
-static Y_INLINE void yaffs_SetChunkBit(yaffs_Device * dev, int blk, int chunk)
-{
-	__u8 *blkBits = yaffs_BlockBits(dev, blk);
-
-	blkBits[chunk / 8] |= (1 << (chunk & 7));
-}
-
-static Y_INLINE int yaffs_CheckChunkBit(yaffs_Device * dev, int blk, int chunk)
-{
-	__u8 *blkBits = yaffs_BlockBits(dev, blk);
-	return (blkBits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0;
-}
-
-static Y_INLINE int yaffs_StillSomeChunkBits(yaffs_Device * dev, int blk)
-{
-	__u8 *blkBits = yaffs_BlockBits(dev, blk);
-	int i;
-	for (i = 0; i < dev->chunkBitmapStride; i++) {
-		if (*blkBits)
-			return 1;
-		blkBits++;
-	}
-	return 0;
-}
-
-/*
- *  Simple hash function. Needs to have a reasonable spread
- */
- 
-static Y_INLINE int yaffs_HashFunction(int n)
-{
-	n = abs(n);
-	
-	return (n % YAFFS_NOBJECT_BUCKETS);
-}
-
-/*
- * Access functions to useful fake objects
- */
- 
-yaffs_Object *yaffs_Root(yaffs_Device * dev)
-{
-	return dev->rootDir;
-}
-
-yaffs_Object *yaffs_LostNFound(yaffs_Device * dev)
-{
-	return dev->lostNFoundDir;
-}
-
-
-/*
- *  Erased NAND checking functions
- */
- 
-int yaffs_CheckFF(__u8 * buffer, int nBytes)
-{
-	/* Horrible, slow implementation */
-	while (nBytes--) {
-		if (*buffer != 0xFF)
-			return 0;
-		buffer++;
-	}
-	return 1;
-}
-
-static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,
-				  int chunkInNAND)
-{
-
-	int retval = YAFFS_OK;
-	__u8 *data = yaffs_GetTempBuffer(dev, __LINE__);
-	yaffs_ExtendedTags tags;
-
-	yaffs_ReadChunkWithTagsFromNAND(dev, chunkInNAND, data, &tags);
-
-	if (!yaffs_CheckFF(data, dev->nBytesPerChunk) || tags.chunkUsed) {
-		T(YAFFS_TRACE_NANDACCESS,
-		  (TSTR("Chunk %d not erased" TENDSTR), chunkInNAND));
-		retval = YAFFS_FAIL;
-	}
-
-	yaffs_ReleaseTempBuffer(dev, data, __LINE__);
-
-	return retval;
-
-}
-
-static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev,
-					     const __u8 * data,
-					     yaffs_ExtendedTags * tags,
-					     int useReserve)
-{
-	int chunk;
-
-	int writeOk = 1;
-	int attempts = 0;
-
-	do {
-		chunk = yaffs_AllocateChunk(dev, useReserve);
-
-		if (chunk >= 0) {
-
-			/* First check this chunk is erased... */
-#ifndef CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK
-			writeOk = yaffs_CheckChunkErased(dev, chunk);
-#endif
-			if (!writeOk) {
-				T(YAFFS_TRACE_ERROR,
-				  (TSTR
-				   ("**>> yaffs chunk %d was not erased"
-				    TENDSTR), chunk));
-			} else {
-				writeOk =
-				    yaffs_WriteChunkWithTagsToNAND(dev, chunk,
-								   data, tags);
-			}
-			attempts++;
-
-			if (writeOk) {
-				/*
-				 *  Copy the data into the robustification buffer.
-				 *  NB We do this at the end to prevent duplicates in the case of a write error.
-				 *  Todo
-				 */
-				yaffs_HandleWriteChunkOk(dev, chunk, data,
-							 tags);
-			} else {
-				yaffs_HandleWriteChunkError(dev, chunk);
-			}
-		}
-
-	} while (chunk >= 0 && !writeOk);
-
-	if (attempts > 1) {
-		T(YAFFS_TRACE_ERROR,
-		  (TSTR("**>> yaffs write required %d attempts" TENDSTR),
-		   attempts));
-		dev->nRetriedWrites += (attempts - 1);
-	}
-
-	return chunk;
-}
-
-/*
- * Block retiring for handling a broken block.
- */
- 
-static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND)
-{
-
-	yaffs_MarkBlockBad(dev, blockInNAND);
-
-	yaffs_GetBlockInfo(dev, blockInNAND)->blockState =
-	    YAFFS_BLOCK_STATE_DEAD;
-
-	dev->nRetiredBlocks++;
-}
-
-/*
- * Functions for robustisizing TODO
- *
- */
- 
-static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND,
-				     const __u8 * data,
-				     const yaffs_ExtendedTags * tags)
-{
-}
-
-static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND,
-				    const yaffs_ExtendedTags * tags)
-{
-}
-
-static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND)
-{
-	int blockInNAND = chunkInNAND / dev->nChunksPerBlock;
-
-	/* Mark the block for retirement */
-	yaffs_GetBlockInfo(dev, blockInNAND)->needsRetiring = 1;
-	/* Delete the chunk */
-	yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__);
-}
-
-
-/*---------------- Name handling functions ------------*/ 
-
-static __u16 yaffs_CalcNameSum(const YCHAR * name)
-{
-	__u16 sum = 0;
-	__u16 i = 1;
-
-	YUCHAR *bname = (YUCHAR *) name;
-	if (bname) {
-		while ((*bname) && (i <= YAFFS_MAX_NAME_LENGTH)) {
-
-#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
-			sum += yaffs_toupper(*bname) * i;
-#else
-			sum += (*bname) * i;
-#endif
-			i++;
-			bname++;
-		}
-	}
-	return sum;
-}
-
-static void yaffs_SetObjectName(yaffs_Object * obj, const YCHAR * name)
-{
-#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
-	if (name && yaffs_strlen(name) <= YAFFS_SHORT_NAME_LENGTH) {
-		yaffs_strcpy(obj->shortName, name);
-	} else {
-		obj->shortName[0] = _Y('\0');
-	}
-#endif
-	obj->sum = yaffs_CalcNameSum(name);
-}
-
-/*-------------------- TNODES -------------------
-
- * List of spare tnodes
- * The list is hooked together using the first pointer
- * in the tnode.
- */
- 
-/* yaffs_CreateTnodes creates a bunch more tnodes and
- * adds them to the tnode free list.
- * Don't use this function directly
- */
-
-static int yaffs_CreateTnodes(yaffs_Device * dev, int nTnodes)
-{
-	int i;
-	int tnodeSize;
-	yaffs_Tnode *newTnodes;
-	__u8 *mem;
-	yaffs_Tnode *curr;
-	yaffs_Tnode *next;
-	yaffs_TnodeList *tnl;
-
-	if (nTnodes < 1)
-		return YAFFS_OK;
-		
-	/* Calculate the tnode size in bytes for variable width tnode support.
-	 * Must be a multiple of 32-bits  */
-	tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8;
-
-	/* make these things */
-
-	newTnodes = YMALLOC(nTnodes * tnodeSize);
-	mem = (__u8 *)newTnodes;
-
-	if (!newTnodes) {
-		T(YAFFS_TRACE_ERROR,
-		  (TSTR("yaffs: Could not allocate Tnodes" TENDSTR)));
-		return YAFFS_FAIL;
-	}
-
-	/* Hook them into the free list */
-#if 0
-	for (i = 0; i < nTnodes - 1; i++) {
-		newTnodes[i].internal[0] = &newTnodes[i + 1];
-#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
-		newTnodes[i].internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
-#endif
-	}
-
-	newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes;
-#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
-	newTnodes[nTnodes - 1].internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
-#endif
-	dev->freeTnodes = newTnodes;
-#else
-	/* New hookup for wide tnodes */
-	for(i = 0; i < nTnodes -1; i++) {
-		curr = (yaffs_Tnode *) &mem[i * tnodeSize];
-		next = (yaffs_Tnode *) &mem[(i+1) * tnodeSize];
-		curr->internal[0] = next;
-	}
-	
-	curr = (yaffs_Tnode *) &mem[(nTnodes - 1) * tnodeSize];
-	curr->internal[0] = dev->freeTnodes;
-	dev->freeTnodes = (yaffs_Tnode *)mem;
-
-#endif
-
-
-	dev->nFreeTnodes += nTnodes;
-	dev->nTnodesCreated += nTnodes;
-
-	/* Now add this bunch of tnodes to a list for freeing up.
-	 * NB If we can't add this to the management list it isn't fatal
-	 * but it just means we can't free this bunch of tnodes later.
-	 */
-	 
-	tnl = YMALLOC(sizeof(yaffs_TnodeList));
-	if (!tnl) {
-		T(YAFFS_TRACE_ERROR,
-		  (TSTR
-		   ("yaffs: Could not add tnodes to management list" TENDSTR)));
-
-	} else {
-		tnl->tnodes = newTnodes;
-		tnl->next = dev->allocatedTnodeList;
-		dev->allocatedTnodeList = tnl;
-	}
-
-	T(YAFFS_TRACE_ALLOCATE, (TSTR("yaffs: Tnodes added" TENDSTR)));
-
-	return YAFFS_OK;
-}
-
-/* GetTnode gets us a clean tnode. Tries to make allocate more if we run out */
-
-static yaffs_Tnode *yaffs_GetTnode(yaffs_Device * dev)
-{
-	yaffs_Tnode *tn = NULL;
-
-	/* If there are none left make more */
-	if (!dev->freeTnodes) {
-		yaffs_CreateTnodes(dev, YAFFS_ALLOCATION_NTNODES);
-	}
-
-	if (dev->freeTnodes) {
-		tn = dev->freeTnodes;
-#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
-		if (tn->internal[YAFFS_NTNODES_INTERNAL] != (void *)1) {
-			/* Hoosterman, this thing looks like it isn't in the list */
-			T(YAFFS_TRACE_ALWAYS,
-			  (TSTR("yaffs: Tnode list bug 1" TENDSTR)));
-		}
-#endif
-		dev->freeTnodes = dev->freeTnodes->internal[0];
-		dev->nFreeTnodes--;
-		/* zero out */
-		memset(tn, 0, (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8);
-	}
-
-	return tn;
-}
-
-/* FreeTnode frees up a tnode and puts it back on the free list */
-static void yaffs_FreeTnode(yaffs_Device * dev, yaffs_Tnode * tn)
-{
-	if (tn) {
-#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
-		if (tn->internal[YAFFS_NTNODES_INTERNAL] != 0) {
-			/* Hoosterman, this thing looks like it is already in the list */
-			T(YAFFS_TRACE_ALWAYS,
-			  (TSTR("yaffs: Tnode list bug 2" TENDSTR)));
-		}
-		tn->internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
-#endif
-		tn->internal[0] = dev->freeTnodes;
-		dev->freeTnodes = tn;
-		dev->nFreeTnodes++;
-	}
-}
-
-static void yaffs_DeinitialiseTnodes(yaffs_Device * dev)
-{
-	/* Free the list of allocated tnodes */
-	yaffs_TnodeList *tmp;
-
-	while (dev->allocatedTnodeList) {
-		tmp = dev->allocatedTnodeList->next;
-
-		YFREE(dev->allocatedTnodeList->tnodes);
-		YFREE(dev->allocatedTnodeList);
-		dev->allocatedTnodeList = tmp;
-
-	}
-
-	dev->freeTnodes = NULL;
-	dev->nFreeTnodes = 0;
-}
-
-static void yaffs_InitialiseTnodes(yaffs_Device * dev)
-{
-	dev->allocatedTnodeList = NULL;
-	dev->freeTnodes = NULL;
-	dev->nFreeTnodes = 0;
-	dev->nTnodesCreated = 0;
-
-}
-
-
-void yaffs_PutLevel0Tnode(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos, unsigned val)
-{
-  __u32 *map = (__u32 *)tn;
-  __u32 bitInMap;
-  __u32 bitInWord;
-  __u32 wordInMap;
-  __u32 mask;
-  
-  pos &= YAFFS_TNODES_LEVEL0_MASK;
-  val >>= dev->chunkGroupBits;
-  
-  bitInMap = pos * dev->tnodeWidth;
-  wordInMap = bitInMap /32;
-  bitInWord = bitInMap & (32 -1);
-  
-  mask = dev->tnodeMask << bitInWord;
-  
-  map[wordInMap] &= ~mask;
-  map[wordInMap] |= (mask & (val << bitInWord));
-  
-  if(dev->tnodeWidth > (32-bitInWord)) {
-    bitInWord = (32 - bitInWord);
-    wordInMap++;;
-    mask = dev->tnodeMask >> (/*dev->tnodeWidth -*/ bitInWord);
-    map[wordInMap] &= ~mask;
-    map[wordInMap] |= (mask & (val >> bitInWord));
-  }
-}
-
-__u32 yaffs_GetChunkGroupBase(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos)
-{
-  __u32 *map = (__u32 *)tn;
-  __u32 bitInMap;
-  __u32 bitInWord;
-  __u32 wordInMap;
-  __u32 val;
-  
-  pos &= YAFFS_TNODES_LEVEL0_MASK;
-  
-  bitInMap = pos * dev->tnodeWidth;
-  wordInMap = bitInMap /32;
-  bitInWord = bitInMap & (32 -1);
-  
-  val = map[wordInMap] >> bitInWord;
-  
-  if(dev->tnodeWidth > (32-bitInWord)) {
-    bitInWord = (32 - bitInWord);
-    wordInMap++;;
-    val |= (map[wordInMap] << bitInWord);
-  }
-  
-  val &= dev->tnodeMask;
-  val <<= dev->chunkGroupBits;
-  
-  return val;
-}
-
-/* ------------------- End of individual tnode manipulation -----------------*/
-
-/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------
- * The look up tree is represented by the top tnode and the number of topLevel
- * in the tree. 0 means only the level 0 tnode is in the tree.
- */
-
-/* FindLevel0Tnode finds the level 0 tnode, if one exists. */
-static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device * dev,
-					  yaffs_FileStructure * fStruct,
-					  __u32 chunkId)
-{
-
-	yaffs_Tnode *tn = fStruct->top;
-	__u32 i;
-	int requiredTallness;
-	int level = fStruct->topLevel;
-
-	/* Check sane level and chunk Id */
-	if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) {
-		return NULL;
-	}
-
-	if (chunkId > YAFFS_MAX_CHUNK_ID) {
-		return NULL;
-	}
-
-	/* First check we're tall enough (ie enough topLevel) */
-
-	i = chunkId >> YAFFS_TNODES_LEVEL0_BITS;
-	requiredTallness = 0;
-	while (i) {
-		i >>= YAFFS_TNODES_INTERNAL_BITS;
-		requiredTallness++;
-	}
-
-	if (requiredTallness > fStruct->topLevel) {
-		/* Not tall enough, so we can't find it, return NULL. */
-		return NULL;
-	}
-
-	/* Traverse down to level 0 */
-	while (level > 0 && tn) {
-		tn = tn->
-		    internal[(chunkId >>
-			       ( YAFFS_TNODES_LEVEL0_BITS + 
-			         (level - 1) *
-			         YAFFS_TNODES_INTERNAL_BITS)
-			      ) &
-			     YAFFS_TNODES_INTERNAL_MASK];
-		level--;
-
-	}
-
-	return tn;
-}
-
-/* AddOrFindLevel0Tnode finds the level 0 tnode if it exists, otherwise first expands the tree.
- * This happens in two steps:
- *  1. If the tree isn't tall enough, then make it taller.
- *  2. Scan down the tree towards the level 0 tnode adding tnodes if required.
- *
- * Used when modifying the tree.
- */
- 
-static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device * dev,
-					       yaffs_FileStructure * fStruct,
-					       __u32 chunkId)
-{
-
-	yaffs_Tnode *tn;
-
-	int requiredTallness;
-	int i;
-	int l;
-
-	__u32 x;
-
-
-	/* Check sane level and page Id */
-	if (fStruct->topLevel < 0 || fStruct->topLevel > YAFFS_TNODES_MAX_LEVEL) {
-		return NULL;
-	}
-
-	if (chunkId > YAFFS_MAX_CHUNK_ID) {
-		return NULL;
-	}
-
-	/* First check we're tall enough (ie enough topLevel) */
-
-	x = chunkId >> YAFFS_TNODES_LEVEL0_BITS;
-	requiredTallness = 0;
-	while (x) {
-		x >>= YAFFS_TNODES_INTERNAL_BITS;
-		requiredTallness++;
-	}
-
-
-	if (requiredTallness > fStruct->topLevel) {
-		/* Not tall enough,gotta make the tree taller */
-		for (i = fStruct->topLevel; i < requiredTallness; i++) {
-		
-			tn = yaffs_GetTnode(dev);
-
-			if (tn) {
-				tn->internal[0] = fStruct->top;
-				fStruct->top = tn;
-			} else {
-				T(YAFFS_TRACE_ERROR,
-				  (TSTR("yaffs: no more tnodes" TENDSTR)));
-			}
-		}
-
-		fStruct->topLevel = requiredTallness;
-	}
-
-	/* Traverse down to level 0, adding anything we need */
-
-	l = fStruct->topLevel;
-	tn = fStruct->top;
-	while (l > 0 && tn) {
-		x = (chunkId >>
-		     ( YAFFS_TNODES_LEVEL0_BITS +
-		      (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) &
-		    YAFFS_TNODES_INTERNAL_MASK;
-
-
-		if (!tn->internal[x])
-			tn->internal[x] = yaffs_GetTnode(dev);
-
-		tn = tn->internal[x];
-		l--;
-
-	}
-
-	return tn;
-}
-
-static int yaffs_FindChunkInGroup(yaffs_Device * dev, int theChunk,
-				  yaffs_ExtendedTags * tags, int objectId,
-				  int chunkInInode)
-{
-	int j;
-
-	for (j = 0; theChunk && j < dev->chunkGroupSize; j++) {
-		if (yaffs_CheckChunkBit
-		    (dev, theChunk / dev->nChunksPerBlock,
-		     theChunk % dev->nChunksPerBlock)) {
-			yaffs_ReadChunkWithTagsFromNAND(dev, theChunk, NULL,
-							tags);
-			if (yaffs_TagsMatch(tags, objectId, chunkInInode)) {
-				/* found it; */
-				return theChunk;
-
-			}
-		}
-		theChunk++;
-	}
-	return -1;
-}
-
-
-/* DeleteWorker scans backwards through the tnode tree and deletes all the
- * chunks and tnodes in the file
- * Returns 1 if the tree was deleted. 
- * Returns 0 if it stopped early due to hitting the limit and the delete is incomplete.
- */
-
-static int yaffs_DeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, __u32 level,
-			      int chunkOffset, int *limit)
-{
-	int i;
-	int chunkInInode;
-	int theChunk;
-	yaffs_ExtendedTags tags;
-	int foundChunk;
-	yaffs_Device *dev = in->myDev;
-
-	int allDone = 1;
-
-	if (tn) {
-		if (level > 0) {
-
-			for (i = YAFFS_NTNODES_INTERNAL - 1; allDone && i >= 0;
-			     i--) {
-				if (tn->internal[i]) {
-					if (limit && (*limit) < 0) {
-						allDone = 0;
-					} else {
-						allDone =
-						    yaffs_DeleteWorker(in,
-								       tn->
-								       internal
-								       [i],
-								       level -
-								       1,
-								       (chunkOffset
-									<<
-									YAFFS_TNODES_INTERNAL_BITS)
-								       + i,
-								       limit);
-					}
-					if (allDone) {
-						yaffs_FreeTnode(dev,
-								tn->
-								internal[i]);
-						tn->internal[i] = NULL;
-					}
-				}
-
-			}
-			return (allDone) ? 1 : 0;
-		} else if (level == 0) {
-			int hitLimit = 0;
-
-			for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0 && !hitLimit;
-			     i--) {
-			        theChunk = yaffs_GetChunkGroupBase(dev,tn,i);
-				if (theChunk) {
-
-					chunkInInode =
-					    (chunkOffset <<
-					     YAFFS_TNODES_LEVEL0_BITS) + i;
-
-					foundChunk =
-					    yaffs_FindChunkInGroup(dev,
-								   theChunk,
-								   &tags,
-								   in->objectId,
-								   chunkInInode);
-
-					if (foundChunk > 0) {
-						yaffs_DeleteChunk(dev,
-								  foundChunk, 1,
-								  __LINE__);
-						in->nDataChunks--;
-						if (limit) {
-							*limit = *limit - 1;
-							if (*limit <= 0) {
-								hitLimit = 1;
-							}
-						}
-
-					}
-
-					yaffs_PutLevel0Tnode(dev,tn,i,0);
-				}
-
-			}
-			return (i < 0) ? 1 : 0;
-
-		}
-
-	}
-
-	return 1;
-
-}
-
-static void yaffs_SoftDeleteChunk(yaffs_Device * dev, int chunk)
-{
-
-	yaffs_BlockInfo *theBlock;
-
-	T(YAFFS_TRACE_DELETION, (TSTR("soft delete chunk %d" TENDSTR), chunk));
-
-	theBlock = yaffs_GetBlockInfo(dev, chunk / dev->nChunksPerBlock);
-	if (theBlock) {
-		theBlock->softDeletions++;
-		dev->nFreeChunks++;
-	}
-}
-
-/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all the chunks in the file.
- * All soft deleting does is increment the block's softdelete count and pulls the chunk out
- * of the tnode.
- * Thus, essentially this is the same as DeleteWorker except that the chunks are soft deleted.
- */
- 
-static int yaffs_SoftDeleteWorker(yaffs_Object * in, yaffs_Tnode * tn,
-				  __u32 level, int chunkOffset)
-{
-	int i;
-	int theChunk;
-	int allDone = 1;
-	yaffs_Device *dev = in->myDev;
-
-	if (tn) {
-		if (level > 0) {
-
-			for (i = YAFFS_NTNODES_INTERNAL - 1; allDone && i >= 0;
-			     i--) {
-				if (tn->internal[i]) {
-					allDone =
-					    yaffs_SoftDeleteWorker(in,
-								   tn->
-								   internal[i],
-								   level - 1,
-								   (chunkOffset
-								    <<
-								    YAFFS_TNODES_INTERNAL_BITS)
-								   + i);
-					if (allDone) {
-						yaffs_FreeTnode(dev,
-								tn->
-								internal[i]);
-						tn->internal[i] = NULL;
-					} else {
-						/* Hoosterman... how could this happen? */
-					}
-				}
-			}
-			return (allDone) ? 1 : 0;
-		} else if (level == 0) {
-
-			for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) {
-				theChunk = yaffs_GetChunkGroupBase(dev,tn,i);
-				if (theChunk) {
-					/* Note this does not find the real chunk, only the chunk group.
-					 * We make an assumption that a chunk group is not larger than 
-					 * a block.
-					 */
-					yaffs_SoftDeleteChunk(dev, theChunk);
-					yaffs_PutLevel0Tnode(dev,tn,i,0);
-				}
-
-			}
-			return 1;
-
-		}
-
-	}
-
-	return 1;
-
-}
-
-static void yaffs_SoftDeleteFile(yaffs_Object * obj)
-{
-	if (obj->deleted &&
-	    obj->variantType == YAFFS_OBJECT_TYPE_FILE && !obj->softDeleted) {
-		if (obj->nDataChunks <= 0) {
-			/* Empty file with no duplicate object headers, just delete it immediately */
-			yaffs_FreeTnode(obj->myDev,
-					obj->variant.fileVariant.top);
-			obj->variant.fileVariant.top = NULL;
-			T(YAFFS_TRACE_TRACING,
-			  (TSTR("yaffs: Deleting empty file %d" TENDSTR),
-			   obj->objectId));
-			yaffs_DoGenericObjectDeletion(obj);
-		} else {
-			yaffs_SoftDeleteWorker(obj,
-					       obj->variant.fileVariant.top,
-					       obj->variant.fileVariant.
-					       topLevel, 0);
-			obj->softDeleted = 1;
-		}
-	}
-}
-
-/* Pruning removes any part of the file structure tree that is beyond the
- * bounds of the file (ie that does not point to chunks).
- *
- * A file should only get pruned when its size is reduced.
- *
- * Before pruning, the chunks must be pulled from the tree and the
- * level 0 tnode entries must be zeroed out.
- * Could also use this for file deletion, but that's probably better handled
- * by a special case.
- */
-
-static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device * dev, yaffs_Tnode * tn,
-				      __u32 level, int del0)
-{
-	int i;
-	int hasData;
-
-	if (tn) {
-		hasData = 0;
-
-		for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) {
-			if (tn->internal[i] && level > 0) {
-				tn->internal[i] =
-				    yaffs_PruneWorker(dev, tn->internal[i],
-						      level - 1,
-						      (i == 0) ? del0 : 1);
-			}
-
-			if (tn->internal[i]) {
-				hasData++;
-			}
-		}
-
-		if (hasData == 0 && del0) {
-			/* Free and return NULL */
-
-			yaffs_FreeTnode(dev, tn);
-			tn = NULL;
-		}
-
-	}
-
-	return tn;
-
-}
-
-static int yaffs_PruneFileStructure(yaffs_Device * dev,
-				    yaffs_FileStructure * fStruct)
-{
-	int i;
-	int hasData;
-	int done = 0;
-	yaffs_Tnode *tn;
-
-	if (fStruct->topLevel > 0) {
-		fStruct->top =
-		    yaffs_PruneWorker(dev, fStruct->top, fStruct->topLevel, 0);
-
-		/* Now we have a tree with all the non-zero branches NULL but the height
-		 * is the same as it was.
-		 * Let's see if we can trim internal tnodes to shorten the tree.
-		 * We can do this if only the 0th element in the tnode is in use 
-		 * (ie all the non-zero are NULL)
-		 */
-
-		while (fStruct->topLevel && !done) {
-			tn = fStruct->top;
-
-			hasData = 0;
-			for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) {
-				if (tn->internal[i]) {
-					hasData++;
-				}
-			}
-
-			if (!hasData) {
-				fStruct->top = tn->internal[0];
-				fStruct->topLevel--;
-				yaffs_FreeTnode(dev, tn);
-			} else {
-				done = 1;
-			}
-		}
-	}
-
-	return YAFFS_OK;
-}
-
-/*-------------------- End of File Structure functions.-------------------*/
-
-/* yaffs_CreateFreeObjects creates a bunch more objects and
- * adds them to the object free list.
- */
-static int yaffs_CreateFreeObjects(yaffs_Device * dev, int nObjects)
-{
-	int i;
-	yaffs_Object *newObjects;
-	yaffs_ObjectList *list;
-
-	if (nObjects < 1)
-		return YAFFS_OK;
-
-	/* make these things */
-	newObjects = YMALLOC(nObjects * sizeof(yaffs_Object));
-
-	if (!newObjects) {
-		T(YAFFS_TRACE_ALLOCATE,
-		  (TSTR("yaffs: Could not allocate more objects" TENDSTR)));
-		return YAFFS_FAIL;
-	}
-	
-	/* Hook them into the free list */
-	for (i = 0; i < nObjects - 1; i++) {
-		newObjects[i].siblings.next =
-		    (struct list_head *)(&newObjects[i + 1]);
-	}
-
-	newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects;
-	dev->freeObjects = newObjects;
-	dev->nFreeObjects += nObjects;
-	dev->nObjectsCreated += nObjects;
-
-	/* Now add this bunch of Objects to a list for freeing up. */
-
-	list = YMALLOC(sizeof(yaffs_ObjectList));
-	if (!list) {
-		T(YAFFS_TRACE_ALLOCATE,
-		  (TSTR("Could not add objects to management list" TENDSTR)));
-	} else {
-		list->objects = newObjects;
-		list->next = dev->allocatedObjectList;
-		dev->allocatedObjectList = list;
-	}
-
-	return YAFFS_OK;
-}
-
-
-/* AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out */
-static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device * dev)
-{
-	yaffs_Object *tn = NULL;
-
-	/* If there are none left make more */
-	if (!dev->freeObjects) {
-		yaffs_CreateFreeObjects(dev, YAFFS_ALLOCATION_NOBJECTS);
-	}
-
-	if (dev->freeObjects) {
-		tn = dev->freeObjects;
-		dev->freeObjects =
-		    (yaffs_Object *) (dev->freeObjects->siblings.next);
-		dev->nFreeObjects--;
-
-		/* Now sweeten it up... */
-
-		memset(tn, 0, sizeof(yaffs_Object));
-		tn->myDev = dev;
-		tn->chunkId = -1;
-		tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN;
-		INIT_LIST_HEAD(&(tn->hardLinks));
-		INIT_LIST_HEAD(&(tn->hashLink));
-		INIT_LIST_HEAD(&tn->siblings);
-
-		/* Add it to the lost and found directory.
-		 * NB Can't put root or lostNFound in lostNFound so
-		 * check if lostNFound exists first
-		 */
-		if (dev->lostNFoundDir) {
-			yaffs_AddObjectToDirectory(dev->lostNFoundDir, tn);
-		}
-	}
-
-	return tn;
-}
-
-static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device * dev, int number,
-					       __u32 mode)
-{
-
-	yaffs_Object *obj =
-	    yaffs_CreateNewObject(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY);
-	if (obj) {
-		obj->fake = 1;		/* it is fake so it has no NAND presence... */
-		obj->renameAllowed = 0;	/* ... and we're not allowed to rename it... */
-		obj->unlinkAllowed = 0;	/* ... or unlink it */
-		obj->deleted = 0;
-		obj->unlinked = 0;
-		obj->yst_mode = mode;
-		obj->myDev = dev;
-		obj->chunkId = 0;	/* Not a valid chunk. */
-	}
-
-	return obj;
-
-}
-
-static void yaffs_UnhashObject(yaffs_Object * tn)
-{
-	int bucket;
-	yaffs_Device *dev = tn->myDev;
-
-	/* If it is still linked into the bucket list, free from the list */
-	if (!list_empty(&tn->hashLink)) {
-		list_del_init(&tn->hashLink);
-		bucket = yaffs_HashFunction(tn->objectId);
-		dev->objectBucket[bucket].count--;
-	}
-
-}
-
-/*  FreeObject frees up a Object and puts it back on the free list */
-static void yaffs_FreeObject(yaffs_Object * tn)
-{
-
-	yaffs_Device *dev = tn->myDev;
-
-#ifdef  __KERNEL__
-	if (tn->myInode) {
-		/* We're still hooked up to a cached inode.
-		 * Don't delete now, but mark for later deletion
-		 */
-		tn->deferedFree = 1;
-		return;
-	}
-#endif
-
-	yaffs_UnhashObject(tn);
-
-	/* Link into the free list. */
-	tn->siblings.next = (struct list_head *)(dev->freeObjects);
-	dev->freeObjects = tn;
-	dev->nFreeObjects++;
-}
-
-#ifdef __KERNEL__
-
-void yaffs_HandleDeferedFree(yaffs_Object * obj)
-{
-	if (obj->deferedFree) {
-		yaffs_FreeObject(obj);
-	}
-}
-
-#endif
-
-static void yaffs_DeinitialiseObjects(yaffs_Device * dev)
-{
-	/* Free the list of allocated Objects */
-
-	yaffs_ObjectList *tmp;
-
-	while (dev->allocatedObjectList) {
-		tmp = dev->allocatedObjectList->next;
-		YFREE(dev->allocatedObjectList->objects);
-		YFREE(dev->allocatedObjectList);
-
-		dev->allocatedObjectList = tmp;
-	}
-
-	dev->freeObjects = NULL;
-	dev->nFreeObjects = 0;
-}
-
-static void yaffs_InitialiseObjects(yaffs_Device * dev)
-{
-	int i;
-
-	dev->allocatedObjectList = NULL;
-	dev->freeObjects = NULL;
-	dev->nFreeObjects = 0;
-
-	for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) {
-		INIT_LIST_HEAD(&dev->objectBucket[i].list);
-		dev->objectBucket[i].count = 0;
-	}
-
-}
-
-static int yaffs_FindNiceObjectBucket(yaffs_Device * dev)
-{
-	static int x = 0;
-	int i;
-	int l = 999;
-	int lowest = 999999;
-
-	/* First let's see if we can find one that's empty. */
-
-	for (i = 0; i < 10 && lowest > 0; i++) {
-		x++;
-		x %= YAFFS_NOBJECT_BUCKETS;
-		if (dev->objectBucket[x].count < lowest) {
-			lowest = dev->objectBucket[x].count;
-			l = x;
-		}
-
-	}
-
-	/* If we didn't find an empty list, then try
-	 * looking a bit further for a short one
-	 */
-
-	for (i = 0; i < 10 && lowest > 3; i++) {
-		x++;
-		x %= YAFFS_NOBJECT_BUCKETS;
-		if (dev->objectBucket[x].count < lowest) {
-			lowest = dev->objectBucket[x].count;
-			l = x;
-		}
-
-	}
-
-	return l;
-}
-
-static int yaffs_CreateNewObjectNumber(yaffs_Device * dev)
-{
-	int bucket = yaffs_FindNiceObjectBucket(dev);
-
-	/* Now find an object value that has not already been taken
-	 * by scanning the list.
-	 */
-
-	int found = 0;
-	struct list_head *i;
-
-	__u32 n = (__u32) bucket;
-
-	/* yaffs_CheckObjectHashSanity();  */
-
-	while (!found) {
-		found = 1;
-		n += YAFFS_NOBJECT_BUCKETS;
-		if (1 || dev->objectBucket[bucket].count > 0) {
-			list_for_each(i, &dev->objectBucket[bucket].list) {
-				/* If there is already one in the list */
-				if (i
-				    && list_entry(i, yaffs_Object,
-						  hashLink)->objectId == n) {
-					found = 0;
-				}
-			}
-		}
-	}
-
-
-	return n;
-}
-
-static void yaffs_HashObject(yaffs_Object * in)
-{
-	int bucket = yaffs_HashFunction(in->objectId);
-	yaffs_Device *dev = in->myDev;
-
-	list_add(&in->hashLink, &dev->objectBucket[bucket].list);
-	dev->objectBucket[bucket].count++;
-
-}
-
-yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device * dev, __u32 number)
-{
-	int bucket = yaffs_HashFunction(number);
-	struct list_head *i;
-	yaffs_Object *in;
-
-	
-	list_for_each(i, &dev->objectBucket[bucket].list) {
-		/* Look if it is in the list */
-		if (i) {
-			in = list_entry(i, yaffs_Object, hashLink);
-			if (in->objectId == number) {
-#ifdef __KERNEL__
-				/* Don't tell the VFS about this one if it is defered free */
-				if (in->deferedFree)
-					return NULL;
-#endif
-
-				return in;
-			}
-		}
-	}
-
-	return NULL;
-}
-
-yaffs_Object *yaffs_CreateNewObject(yaffs_Device * dev, int number,
-				    yaffs_ObjectType type)
-{
-
-	yaffs_Object *theObject;
-
-	if (number < 0) {
-		number = yaffs_CreateNewObjectNumber(dev);
-	}
-
-	theObject = yaffs_AllocateEmptyObject(dev);
-
-	if (theObject) {
-		theObject->fake = 0;
-		theObject->renameAllowed = 1;
-		theObject->unlinkAllowed = 1;
-		theObject->objectId = number;
-		yaffs_HashObject(theObject);
-		theObject->variantType = type;
-#ifdef CONFIG_YAFFS_WINCE
-		yfsd_WinFileTimeNow(theObject->win_atime);
-		theObject->win_ctime[0] = theObject->win_mtime[0] =
-		    theObject->win_atime[0];
-		theObject->win_ctime[1] = theObject->win_mtime[1] =
-		    theObject->win_atime[1];
-
-#else
-
-		theObject->yst_atime = theObject->yst_mtime =
-		    theObject->yst_ctime = Y_CURRENT_TIME;
-#endif
-		switch (type) {
-		case YAFFS_OBJECT_TYPE_FILE:
-			theObject->variant.fileVariant.fileSize = 0;
-			theObject->variant.fileVariant.scannedFileSize = 0;
-			theObject->variant.fileVariant.shrinkSize = 0xFFFFFFFF;	/* max __u32 */
-			theObject->variant.fileVariant.topLevel = 0;
-			theObject->variant.fileVariant.top =
-			    yaffs_GetTnode(dev);
-			break;
-		case YAFFS_OBJECT_TYPE_DIRECTORY:
-			INIT_LIST_HEAD(&theObject->variant.directoryVariant.
-				       children);
-			break;
-		case YAFFS_OBJECT_TYPE_SYMLINK:
-		case YAFFS_OBJECT_TYPE_HARDLINK:
-		case YAFFS_OBJECT_TYPE_SPECIAL:
-			/* No action required */
-			break;
-		case YAFFS_OBJECT_TYPE_UNKNOWN:
-			/* todo this should not happen */
-			break;
-		}
-	}
-
-	return theObject;
-}
-
-static yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device * dev,
-						      int number,
-						      yaffs_ObjectType type)
-{
-	yaffs_Object *theObject = NULL;
-
-	if (number > 0) {
-		theObject = yaffs_FindObjectByNumber(dev, number);
-	}
-
-	if (!theObject) {
-		theObject = yaffs_CreateNewObject(dev, number, type);
-	}
-
-	return theObject;
-
-}
-
-static YCHAR *yaffs_CloneString(const YCHAR * str)
-{
-	YCHAR *newStr = NULL;
-
-	if (str && *str) {
-		newStr = YMALLOC((yaffs_strlen(str) + 1) * sizeof(YCHAR));
-		yaffs_strcpy(newStr, str);
-	}
-
-	return newStr;
-
-}
-
-/*
- * Mknod (create) a new object.
- * equivalentObject only has meaning for a hard link;
- * aliasString only has meaning for a sumlink.
- * rdev only has meaning for devices (a subset of special objects)
- */
- 
-static yaffs_Object *yaffs_MknodObject(yaffs_ObjectType type,
-				       yaffs_Object * parent,
-				       const YCHAR * name,
-				       __u32 mode,
-				       __u32 uid,
-				       __u32 gid,
-				       yaffs_Object * equivalentObject,
-				       const YCHAR * aliasString, __u32 rdev)
-{
-	yaffs_Object *in;
-
-	yaffs_Device *dev = parent->myDev;
-
-	/* Check if the entry exists. If it does then fail the call since we don't want a dup.*/
-	if (yaffs_FindObjectByName(parent, name)) {
-		return NULL;
-	}
-
-	in = yaffs_CreateNewObject(dev, -1, type);
-
-	if (in) {
-		in->chunkId = -1;
-		in->valid = 1;
-		in->variantType = type;
-
-		in->yst_mode = mode;
-
-#ifdef CONFIG_YAFFS_WINCE
-		yfsd_WinFileTimeNow(in->win_atime);
-		in->win_ctime[0] = in->win_mtime[0] = in->win_atime[0];
-		in->win_ctime[1] = in->win_mtime[1] = in->win_atime[1];
-
-#else
-		in->yst_atime = in->yst_mtime = in->yst_ctime = Y_CURRENT_TIME;
-
-		in->yst_rdev = rdev;
-		in->yst_uid = uid;
-		in->yst_gid = gid;
-#endif
-		in->nDataChunks = 0;
-
-		yaffs_SetObjectName(in, name);
-		in->dirty = 1;
-
-		yaffs_AddObjectToDirectory(parent, in);
-
-		in->myDev = parent->myDev;
-
-		switch (type) {
-		case YAFFS_OBJECT_TYPE_SYMLINK:
-			in->variant.symLinkVariant.alias =
-			    yaffs_CloneString(aliasString);
-			break;
-		case YAFFS_OBJECT_TYPE_HARDLINK:
-			in->variant.hardLinkVariant.equivalentObject =
-			    equivalentObject;
-			in->variant.hardLinkVariant.equivalentObjectId =
-			    equivalentObject->objectId;
-			list_add(&in->hardLinks, &equivalentObject->hardLinks);
-			break;
-		case YAFFS_OBJECT_TYPE_FILE:	
-		case YAFFS_OBJECT_TYPE_DIRECTORY:
-		case YAFFS_OBJECT_TYPE_SPECIAL:
-		case YAFFS_OBJECT_TYPE_UNKNOWN:
-			/* do nothing */
-			break;
-		}
-
-		if (yaffs_UpdateObjectHeader(in, name, 0, 0, 0) < 0) {
-			/* Could not create the object header, fail the creation */
-			yaffs_DestroyObject(in);
-			in = NULL;
-		}
-
-	}
-
-	return in;
-}
-
-yaffs_Object *yaffs_MknodFile(yaffs_Object * parent, const YCHAR * name,
-			      __u32 mode, __u32 uid, __u32 gid)
-{
-	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_FILE, parent, name, mode,
-				 uid, gid, NULL, NULL, 0);
-}
-
-yaffs_Object *yaffs_MknodDirectory(yaffs_Object * parent, const YCHAR * name,
-				   __u32 mode, __u32 uid, __u32 gid)
-{
-	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name,
-				 mode, uid, gid, NULL, NULL, 0);
-}
-
-yaffs_Object *yaffs_MknodSpecial(yaffs_Object * parent, const YCHAR * name,
-				 __u32 mode, __u32 uid, __u32 gid, __u32 rdev)
-{
-	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode,
-				 uid, gid, NULL, NULL, rdev);
-}
-
-yaffs_Object *yaffs_MknodSymLink(yaffs_Object * parent, const YCHAR * name,
-				 __u32 mode, __u32 uid, __u32 gid,
-				 const YCHAR * alias)
-{
-	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode,
-				 uid, gid, NULL, alias, 0);
-}
-
-/* yaffs_Link returns the object id of the equivalent object.*/
-yaffs_Object *yaffs_Link(yaffs_Object * parent, const YCHAR * name,
-			 yaffs_Object * equivalentObject)
-{
-	/* Get the real object in case we were fed a hard link as an equivalent object */
-	equivalentObject = yaffs_GetEquivalentObject(equivalentObject);
-
-	if (yaffs_MknodObject
-	    (YAFFS_OBJECT_TYPE_HARDLINK, parent, name, 0, 0, 0,
-	     equivalentObject, NULL, 0)) {
-		return equivalentObject;
-	} else {
-		return NULL;
-	}
-
-}
-
-static int yaffs_ChangeObjectName(yaffs_Object * obj, yaffs_Object * newDir,
-				  const YCHAR * newName, int force, int shadows)
-{
-	int unlinkOp;
-	int deleteOp;
-
-	yaffs_Object *existingTarget;
-
-	if (newDir == NULL) {
-		newDir = obj->parent;	/* use the old directory */
-	}
-
-	if (newDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) {
-		T(YAFFS_TRACE_ALWAYS,
-		  (TSTR
-		   ("tragendy: yaffs_ChangeObjectName: newDir is not a directory"
-		    TENDSTR)));
-		YBUG();
-	}
-	
-	/* TODO: Do we need this different handling for YAFFS2 and YAFFS1?? */
-	if (obj->myDev->isYaffs2) {
-		unlinkOp = (newDir == obj->myDev->unlinkedDir);
-	} else {
-		unlinkOp = (newDir == obj->myDev->unlinkedDir
-			    && obj->variantType == YAFFS_OBJECT_TYPE_FILE);
-	}
-
-	deleteOp = (newDir == obj->myDev->deletedDir);
-
-	existingTarget = yaffs_FindObjectByName(newDir, newName);
-
-	/* If the object is a file going into the unlinked directory, 
-	 *   then it is OK to just stuff it in since duplicate names are allowed.
-	 *   else only proceed if the new name does not exist and if we're putting 
-	 *   it into a directory.
-	 */
-	if ((unlinkOp ||
-	     deleteOp ||
-	     force ||
-	     (shadows > 0) ||
-	     !existingTarget) &&
-	    newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY) {
-		yaffs_SetObjectName(obj, newName);
-		obj->dirty = 1;
-
-		yaffs_AddObjectToDirectory(newDir, obj);
-
-		if (unlinkOp)
-			obj->unlinked = 1;
-
-		/* If it is a deletion then we mark it as a shrink for gc purposes. */
-		if (yaffs_UpdateObjectHeader(obj, newName, 0, deleteOp, shadows)>= 0)
-			return YAFFS_OK;
-	}
-
-	return YAFFS_FAIL;
-}
-
-int yaffs_RenameObject(yaffs_Object * oldDir, const YCHAR * oldName,
-		       yaffs_Object * newDir, const YCHAR * newName)
-{
-	yaffs_Object *obj;
-	yaffs_Object *existingTarget;
-	int force = 0;
-
-#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
-	/* Special case for case insemsitive systems (eg. WinCE).
-	 * While look-up is case insensitive, the name isn't.
-	 * Therefore we might want to change x.txt to X.txt
-	*/
-	if (oldDir == newDir && yaffs_strcmp(oldName, newName) == 0) {
-		force = 1;
-	}
-#endif
-
-	obj = yaffs_FindObjectByName(oldDir, oldName);
-
-	if (obj && obj->renameAllowed) {
-
-		/* Now do the handling for an existing target, if there is one */
-
-		existingTarget = yaffs_FindObjectByName(newDir, newName);
-		if (existingTarget &&
-		    existingTarget->variantType == YAFFS_OBJECT_TYPE_DIRECTORY &&
-		    !list_empty(&existingTarget->variant.directoryVariant.children)) {
-			/* There is a target that is a non-empty directory, so we fail */
-			return YAFFS_FAIL;	/* EEXIST or ENOTEMPTY */
-		} else if (existingTarget && existingTarget != obj) {
-			/* Nuke the target first, using shadowing, 
-			 * but only if it isn't the same object
-			 */
-			yaffs_ChangeObjectName(obj, newDir, newName, force,
-					       existingTarget->objectId);
-			yaffs_UnlinkObject(existingTarget);
-		}
-
-		return yaffs_ChangeObjectName(obj, newDir, newName, 1, 0);
-	}
-	return YAFFS_FAIL;
-}
-
-/*------------------------- Block Management and Page Allocation ----------------*/
-
-static int yaffs_InitialiseBlocks(yaffs_Device * dev, int nBlocks)
-{
-	dev->allocationBlock = -1;	/* force it to get a new one */
-
-	/* Todo we're assuming the malloc will pass. */
-	dev->blockInfo = YMALLOC(nBlocks * sizeof(yaffs_BlockInfo));
-	if(!dev->blockInfo){
-		dev->blockInfo = YMALLOC_ALT(nBlocks * sizeof(yaffs_BlockInfo));
-		dev->blockInfoAlt = 1;
-	}
-	else
-		dev->blockInfoAlt = 0;
-	
-	/* Set up dynamic blockinfo stuff. */
-	dev->chunkBitmapStride = (dev->nChunksPerBlock + 7) / 8;
-	dev->chunkBits = YMALLOC(dev->chunkBitmapStride * nBlocks);
-	if(!dev->chunkBits){
-		dev->chunkBits = YMALLOC_ALT(dev->chunkBitmapStride * nBlocks);
-		dev->chunkBitsAlt = 1;
-	}
-	else
-		dev->chunkBitsAlt = 0;
-	
-	if (dev->blockInfo && dev->chunkBits) {
-		memset(dev->blockInfo, 0, nBlocks * sizeof(yaffs_BlockInfo));
-		memset(dev->chunkBits, 0, dev->chunkBitmapStride * nBlocks);
-		return YAFFS_OK;
-	}
-
-	return YAFFS_FAIL;
-
-}
-
-static void yaffs_DeinitialiseBlocks(yaffs_Device * dev)
-{
-	if(dev->blockInfoAlt)
-		YFREE_ALT(dev->blockInfo);
-	else
-		YFREE(dev->blockInfo);
-	dev->blockInfoAlt = 0;
-
-	dev->blockInfo = NULL;
-	
-	if(dev->chunkBitsAlt)
-		YFREE_ALT(dev->chunkBits);
-	else
-		YFREE(dev->chunkBits);
-	dev->chunkBitsAlt = 0;
-	dev->chunkBits = NULL;
-}
-
-static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device * dev,
-					    yaffs_BlockInfo * bi)
-{
-	int i;
-	__u32 seq;
-	yaffs_BlockInfo *b;
-
-	if (!dev->isYaffs2)
-		return 1;	/* disqualification only applies to yaffs2. */
-
-	if (!bi->hasShrinkHeader)
-		return 1;	/* can gc */
-
-	/* Find the oldest dirty sequence number if we don't know it and save it
-	 * so we don't have to keep recomputing it.
-	 */
-	if (!dev->oldestDirtySequence) {
-		seq = dev->sequenceNumber;
-
-		for (i = dev->internalStartBlock; i <= dev->internalEndBlock;
-		     i++) {
-			b = yaffs_GetBlockInfo(dev, i);
-			if (b->blockState == YAFFS_BLOCK_STATE_FULL &&
-			    (b->pagesInUse - b->softDeletions) <
-			    dev->nChunksPerBlock && b->sequenceNumber < seq) {
-				seq = b->sequenceNumber;
-			}
-		}
-		dev->oldestDirtySequence = seq;
-	}
-
-	/* Can't do gc of this block if there are any blocks older than this one that have
-	 * discarded pages.
-	 */
-	return (bi->sequenceNumber <= dev->oldestDirtySequence);
-
-	return 1;
-
-}
-
-/* FindDiretiestBlock is used to select the dirtiest block (or close enough)
- * for garbage collection.
- */
-
-static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev,
-					       int aggressive)
-{
-
-	int b = dev->currentDirtyChecker;
-
-	int i;
-	int iterations;
-	int dirtiest = -1;
-	int pagesInUse;
-	yaffs_BlockInfo *bi;
-	static int nonAggressiveSkip = 0;
-
-	/* If we're doing aggressive GC then we are happy to take a less-dirty block, and
-	 * search harder.
-	 * else (we're doing a leasurely gc), then we only bother to do this if the
-	 * block has only a few pages in use.
-	 */
-
-	nonAggressiveSkip--;
-
-	if (!aggressive && (nonAggressiveSkip > 0)) {
-		return -1;
-	}
-
-	pagesInUse =
-	    (aggressive) ? dev->nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1;
-
-	if (aggressive) {
-		iterations =
-		    dev->internalEndBlock - dev->internalStartBlock + 1;
-	} else {
-		iterations =
-		    dev->internalEndBlock - dev->internalStartBlock + 1;
-		iterations = iterations / 16;
-		if (iterations > 200) {
-			iterations = 200;
-		}
-	}
-
-	for (i = 0; i <= iterations && pagesInUse > 0; i++) {
-		b++;
-		if (b < dev->internalStartBlock || b > dev->internalEndBlock) {
-			b = dev->internalStartBlock;
-		}
-
-		if (b < dev->internalStartBlock || b > dev->internalEndBlock) {
-			T(YAFFS_TRACE_ERROR,
-			  (TSTR("**>> Block %d is not valid" TENDSTR), b));
-			YBUG();
-		}
-
-		bi = yaffs_GetBlockInfo(dev, b);
-
-		if (bi->blockState == YAFFS_BLOCK_STATE_FULL &&
-		    (bi->pagesInUse - bi->softDeletions) < pagesInUse &&
-		    yaffs_BlockNotDisqualifiedFromGC(dev, bi)) {
-			dirtiest = b;
-			pagesInUse = (bi->pagesInUse - bi->softDeletions);
-		}
-	}
-
-	dev->currentDirtyChecker = b;
-
-	if (dirtiest > 0) {
-		T(YAFFS_TRACE_GC,
-		  (TSTR("GC Selected block %d with %d free" TENDSTR), dirtiest,
-		   dev->nChunksPerBlock - pagesInUse));
-	}
-
-	dev->oldestDirtySequence = 0;
-
-	if (dirtiest > 0) {
-		nonAggressiveSkip = 4;
-	}
-
-	return dirtiest;
-}
-
-static void yaffs_BlockBecameDirty(yaffs_Device * dev, int blockNo)
-{
-	yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockNo);
-
-	int erasedOk = 0;
-
-	/* If the block is still healthy erase it and mark as clean.
-	 * If the block has had a data failure, then retire it.
-	 */
-	bi->blockState = YAFFS_BLOCK_STATE_DIRTY;
-
-	if (!bi->needsRetiring) {
-		erasedOk = yaffs_EraseBlockInNAND(dev, blockNo);
-		if (!erasedOk) {
-			dev->nErasureFailures++;
-			T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
-			  (TSTR("**>> Erasure failed %d" TENDSTR), blockNo));
-		}
-	}
-
-	if (erasedOk && (yaffs_traceMask & YAFFS_TRACE_ERASE)) {
-		int i;
-		for (i = 0; i < dev->nChunksPerBlock; i++) {
-			if (!yaffs_CheckChunkErased
-			    (dev, blockNo * dev->nChunksPerBlock + i)) {
-				T(YAFFS_TRACE_ERROR,
-				  (TSTR
-				   (">>Block %d erasure supposedly OK, but chunk %d not erased"
-				    TENDSTR), blockNo, i));
-			}
-		}
-	}
-
-	if (erasedOk) {
-		/* Clean it up... */
-		bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
-		dev->nErasedBlocks++;
-		bi->pagesInUse = 0;
-		bi->softDeletions = 0;
-		bi->hasShrinkHeader = 0;
-		yaffs_ClearChunkBits(dev, blockNo);
-
-		T(YAFFS_TRACE_ERASE,
-		  (TSTR("Erased block %d" TENDSTR), blockNo));
-	} else {
-		dev->nFreeChunks -= dev->nChunksPerBlock;	/* We lost a block of free space */
-
-		yaffs_RetireBlock(dev, blockNo);
-		T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
-		  (TSTR("**>> Block %d retired" TENDSTR), blockNo));
-	}
-}
-
-static int yaffs_FindBlockForAllocation(yaffs_Device * dev)
-{
-	int i;
-
-	yaffs_BlockInfo *bi;
-
-	if (dev->nErasedBlocks < 1) {
-		/* Hoosterman we've got a problem.
-		 * Can't get space to gc
-		 */
-		T(YAFFS_TRACE_ERROR,
-		  (TSTR("yaffs tragedy: no more eraased blocks" TENDSTR)));
-
-		return -1;
-	}
-	
-	/* Find an empty block. */
-
-	for (i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) {
-		dev->allocationBlockFinder++;
-		if (dev->allocationBlockFinder < dev->internalStartBlock
-		    || dev->allocationBlockFinder > dev->internalEndBlock) {
-			dev->allocationBlockFinder = dev->internalStartBlock;
-		}
-
-		bi = yaffs_GetBlockInfo(dev, dev->allocationBlockFinder);
-
-		if (bi->blockState == YAFFS_BLOCK_STATE_EMPTY) {
-			bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING;
-			dev->sequenceNumber++;
-			bi->sequenceNumber = dev->sequenceNumber;
-			dev->nErasedBlocks--;
-			T(YAFFS_TRACE_ALLOCATE,
-			  (TSTR("Allocated block %d, seq  %d, %d left" TENDSTR),
-			   dev->allocationBlockFinder, dev->sequenceNumber,
-			   dev->nErasedBlocks));
-			return dev->allocationBlockFinder;
-		}
-	}
-
-	T(YAFFS_TRACE_ALWAYS,
-	  (TSTR
-	   ("yaffs tragedy: no more eraased blocks, but there should have been %d"
-	    TENDSTR), dev->nErasedBlocks));
-
-	return -1;
-}
-
-
-
-static int yaffs_CheckSpaceForAllocation(yaffs_Device * dev)
-{
-	int reservedChunks = (dev->nReservedBlocks * dev->nChunksPerBlock);
-	return (dev->nFreeChunks > reservedChunks);
-}
-
-static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve)
-{
-	int retVal;
-	yaffs_BlockInfo *bi;
-
-	if (dev->allocationBlock < 0) {
-		/* Get next block to allocate off */
-		dev->allocationBlock = yaffs_FindBlockForAllocation(dev);
-		dev->allocationPage = 0;
-	}
-
-	if (!useReserve && !yaffs_CheckSpaceForAllocation(dev)) {
-		/* Not enough space to allocate unless we're allowed to use the reserve. */
-		return -1;
-	}
-
-	if (dev->nErasedBlocks < dev->nReservedBlocks
-	    && dev->allocationPage == 0) {
-		T(YAFFS_TRACE_ALLOCATE, (TSTR("Allocating reserve" TENDSTR)));
-	}
-
-	/* Next page please.... */
-	if (dev->allocationBlock >= 0) {
-		bi = yaffs_GetBlockInfo(dev, dev->allocationBlock);
-
-		retVal = (dev->allocationBlock * dev->nChunksPerBlock) +
-		    dev->allocationPage;
-		bi->pagesInUse++;
-		yaffs_SetChunkBit(dev, dev->allocationBlock,
-				  dev->allocationPage);
-
-		dev->allocationPage++;
-
-		dev->nFreeChunks--;
-
-		/* If the block is full set the state to full */
-		if (dev->allocationPage >= dev->nChunksPerBlock) {
-			bi->blockState = YAFFS_BLOCK_STATE_FULL;
-			dev->allocationBlock = -1;
-		}
-
-		return retVal;
-	}
-	
-	T(YAFFS_TRACE_ERROR,
-	  (TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR)));
-
-	return -1;
-}
-
-static int yaffs_GetErasedChunks(yaffs_Device * dev)
-{
-	int n;
-
-	n = dev->nErasedBlocks * dev->nChunksPerBlock;
-
-	if (dev->allocationBlock > 0) {
-		n += (dev->nChunksPerBlock - dev->allocationPage);
-	}
-
-	return n;
-
-}
-
-static int yaffs_GarbageCollectBlock(yaffs_Device * dev, int block)
-{
-	int oldChunk;
-	int newChunk;
-	int chunkInBlock;
-	int markNAND;
-	int retVal = YAFFS_OK;
-	int cleanups = 0;
-	int i;
-
-	int chunksBefore = yaffs_GetErasedChunks(dev);
-	int chunksAfter;
-
-	yaffs_ExtendedTags tags;
-
-	yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, block);
-
-	yaffs_Object *object;
-
-	bi->blockState = YAFFS_BLOCK_STATE_COLLECTING;
-
-	T(YAFFS_TRACE_TRACING | YAFFS_TRACE_GC_DETAIL,
-	  (TSTR("Collecting block %d, in use %d, shrink %d, " TENDSTR), block,
-	   bi->pagesInUse, bi->hasShrinkHeader));
-
-	/*yaffs_VerifyFreeChunks(dev); */
-
-	bi->hasShrinkHeader = 0;	/* clear the flag so that the block can erase */
-
-	/* Take off the number of soft deleted entries because
-	 * they're going to get really deleted during GC.
-	 */
-	dev->nFreeChunks -= bi->softDeletions;
-
-	dev->isDoingGC = 1;
-
-	if (!yaffs_StillSomeChunkBits(dev, block)) {
-		T(YAFFS_TRACE_TRACING | YAFFS_TRACE_GC_DETAIL,
-		  (TSTR
-		   ("Collecting block %d that has no chunks in use" TENDSTR),
-		   block));
-		yaffs_BlockBecameDirty(dev, block);
-	} else {
-
-		__u8 *buffer = yaffs_GetTempBuffer(dev, __LINE__);
-
-		for (chunkInBlock = 0, oldChunk = block * dev->nChunksPerBlock;
-		     chunkInBlock < dev->nChunksPerBlock
-		     && yaffs_StillSomeChunkBits(dev, block);
-		     chunkInBlock++, oldChunk++) {
-			if (yaffs_CheckChunkBit(dev, block, chunkInBlock)) {
-
-				/* This page is in use and might need to be copied off */
-
-				markNAND = 1;
-
-				yaffs_InitialiseTags(&tags);
-
-				yaffs_ReadChunkWithTagsFromNAND(dev, oldChunk,
-								buffer, &tags);
-
-				T(YAFFS_TRACE_GC_DETAIL,
-				  (TSTR
-				   ("Collecting page %d, %d %d %d " TENDSTR),
-				   chunkInBlock, tags.objectId, tags.chunkId,
-				   tags.byteCount));
-
-				object =
-				    yaffs_FindObjectByNumber(dev,
-							     tags.objectId);
-
-
-				if (!object) {
-					T(YAFFS_TRACE_ERROR,
-					  (TSTR
-					   ("page %d in gc has no object "
-					    TENDSTR), oldChunk));
-				}
-
-				if (object && object->deleted
-				    && tags.chunkId != 0) {
-					/* Data chunk in a deleted file, throw it away
-					 * It's a soft deleted data chunk,
-					 * No need to copy this, just forget about it and 
-					 * fix up the object.
-					 */
-
-					object->nDataChunks--;
-
-					if (object->nDataChunks <= 0) {
-						/* remeber to clean up the object */
-						dev->gcCleanupList[cleanups] =
-						    tags.objectId;
-						cleanups++;
-					}
-					markNAND = 0;
-				} else if (0
-					   /* Todo object && object->deleted && object->nDataChunks == 0 */
-					   ) {
-					/* Deleted object header with no data chunks.
-					 * Can be discarded and the file deleted.
-					 */
-					object->chunkId = 0;
-					yaffs_FreeTnode(object->myDev,
-							object->variant.
-							fileVariant.top);
-					object->variant.fileVariant.top = NULL;
-					yaffs_DoGenericObjectDeletion(object);
-
-				} else if (object) {
-					/* It's either a data chunk in a live file or
-					 * an ObjectHeader, so we're interested in it.
-					 * NB Need to keep the ObjectHeaders of deleted files
-					 * until the whole file has been deleted off
-					 */
-					tags.serialNumber++;
-
-					dev->nGCCopies++;
-
-					if (tags.chunkId == 0) {
-						/* It is an object Id,
-						 * We need to nuke the shrinkheader flags first
-						 * We no longer want the shrinkHeader flag since its work is done
-						 * and if it is left in place it will mess up scanning.
-						 * Also, clear out any shadowing stuff
-						 */
-
-						yaffs_ObjectHeader *oh;
-						oh = (yaffs_ObjectHeader *)buffer;
-						oh->isShrink = 0;
-						oh->shadowsObject = -1;
-						tags.extraShadows = 0;
-						tags.extraIsShrinkHeader = 0;
-					}
-
-					newChunk =
-					    yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &tags, 1);
-
-					if (newChunk < 0) {
-						retVal = YAFFS_FAIL;
-					} else {
-
-						/* Ok, now fix up the Tnodes etc. */
-
-						if (tags.chunkId == 0) {
-							/* It's a header */
-							object->chunkId =  newChunk;
-							object->serial =   tags.serialNumber;
-						} else {
-							/* It's a data chunk */
-							yaffs_PutChunkIntoFile
-							    (object,
-							     tags.chunkId,
-							     newChunk, 0);
-						}
-					}
-				} 
-
-				yaffs_DeleteChunk(dev, oldChunk, markNAND, __LINE__);
-
-			}else {
-				/* No chunk bit set */
-				T(YAFFS_TRACE_GC_DETAIL,
-				  (TSTR("Page %d deleted, so skip " TENDSTR),
-				   chunkInBlock));
-			}
-		}
-
-		T(YAFFS_TRACE_GC_DETAIL,(TSTR("Done scanning gc block: %d" TENDSTR), cleanups));
-
-		yaffs_ReleaseTempBuffer(dev, buffer, __LINE__);
-
-
-
-		/* Do any required cleanups */
-		for (i = 0; i < cleanups; i++) {
-			/* Time to delete the file too */
-			object =
-			    yaffs_FindObjectByNumber(dev,
-						     dev->gcCleanupList[i]);
-			if (object) {
-				yaffs_FreeTnode(dev,
-						object->variant.fileVariant.
-						top);
-				object->variant.fileVariant.top = NULL;
-				T(YAFFS_TRACE_GC,
-				  (TSTR
-				   ("yaffs: About to finally delete object %d"
-				    TENDSTR), object->objectId));
-				yaffs_DoGenericObjectDeletion(object);
-				object->myDev->nDeletedFiles--;
-			}
-
-		}
-
-	}
-
-
-	if (chunksBefore >= (chunksAfter = yaffs_GetErasedChunks(dev))) {
-		T(YAFFS_TRACE_GC,
-		  (TSTR
-		   ("gc did not increase free chunks before %d after %d"
-		    TENDSTR), chunksBefore, chunksAfter));
-	}
-
-	T(YAFFS_TRACE_GC_DETAIL,
-		(TSTR("Done gc %d chunks freed." TENDSTR),
-		chunksAfter - chunksBefore));
-		
-	dev->isDoingGC = 0;
-
-	return YAFFS_OK;
-}
-
-/* New garbage collector
- * If we're very low on erased blocks then we do aggressive garbage collection
- * otherwise we do "leasurely" garbage collection.
- * Aggressive gc looks further (whole array) and will accept less dirty blocks.
- * Passive gc only inspects smaller areas and will only accept more dirty blocks.
- *
- * The idea is to help clear out space in a more spread-out manner.
- * Dunno if it really does anything useful.
- */
-static int yaffs_CheckGarbageCollection(yaffs_Device * dev)
-{
-	int block;
-	int aggressive;
-	int gcOk = YAFFS_OK;
-	int maxTries = 0;
-
-	if (dev->isDoingGC) {
-		/* Bail out so we don't get recursive gc */
-		return YAFFS_OK;
-	}
-	
-	/* This loop should pass the first time.
-	 * We'll only see looping here if the erase of the collected block fails.
-	 */
-
-	do {
-		maxTries++;
-		if (dev->nErasedBlocks < dev->nReservedBlocks) {
-			/* We need a block soon...*/
-			aggressive = 1;
-		} else {
-			/* We're in no hurry */
-			aggressive = 0;
-		}
-
-		block = yaffs_FindBlockForGarbageCollection(dev, aggressive);
-
-		if (block > 0) {
-			dev->garbageCollections++;
-			if (!aggressive) {
-				dev->passiveGarbageCollections++;
-			}
-
-			T(YAFFS_TRACE_GC,
-			  (TSTR
-			   ("yaffs: GC erasedBlocks %d aggressive %d" TENDSTR),
-			   dev->nErasedBlocks, aggressive));
-
-			gcOk = yaffs_GarbageCollectBlock(dev, block);
-		}
-
-		if (dev->nErasedBlocks < (dev->nReservedBlocks) && block > 0) {
-			T(YAFFS_TRACE_GC,
-			  (TSTR
-			   ("yaffs: GC !!!no reclaim!!! erasedBlocks %d after try %d block %d"
-			    TENDSTR), dev->nErasedBlocks, maxTries, block));
-		}
-	} while ((dev->nErasedBlocks < dev->nReservedBlocks) && (block > 0)
-		 && (maxTries < 2));
-
-	return aggressive ? gcOk : YAFFS_OK;
-}
-
-/*-------------------------  TAGS --------------------------------*/
-
-static int yaffs_TagsMatch(const yaffs_ExtendedTags * tags, int objectId,
-			   int chunkInObject)
-{
-	return (tags->chunkId == chunkInObject &&
-		tags->objectId == objectId && !tags->chunkDeleted) ? 1 : 0;
-
-}
-
-
-/*-------------------- Data file manipulation -----------------*/
-
-static int yaffs_FindChunkInFile(yaffs_Object * in, int chunkInInode,
-				 yaffs_ExtendedTags * tags)
-{
-	/*Get the Tnode, then get the level 0 offset chunk offset */
-	yaffs_Tnode *tn;
-	int theChunk = -1;
-	yaffs_ExtendedTags localTags;
-	int retVal = -1;
-
-	yaffs_Device *dev = in->myDev;
-
-	if (!tags) {
-		/* Passed a NULL, so use our own tags space */
-		tags = &localTags;
-	}
-
-	tn = yaffs_FindLevel0Tnode(dev, &in->variant.fileVariant, chunkInInode);
-
-	if (tn) {
-		theChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode);
-
-		retVal =
-		    yaffs_FindChunkInGroup(dev, theChunk, tags, in->objectId,
-					   chunkInInode);
-	}
-	return retVal;
-}
-
-static int yaffs_FindAndDeleteChunkInFile(yaffs_Object * in, int chunkInInode,
-					  yaffs_ExtendedTags * tags)
-{
-	/* Get the Tnode, then get the level 0 offset chunk offset */
-	yaffs_Tnode *tn;
-	int theChunk = -1;
-	yaffs_ExtendedTags localTags;
-
-	yaffs_Device *dev = in->myDev;
-	int retVal = -1;
-
-	if (!tags) {
-		/* Passed a NULL, so use our own tags space */
-		tags = &localTags;
-	}
-
-	tn = yaffs_FindLevel0Tnode(dev, &in->variant.fileVariant, chunkInInode);
-
-	if (tn) {
-
-		theChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode);
-
-		retVal =
-		    yaffs_FindChunkInGroup(dev, theChunk, tags, in->objectId,
-					   chunkInInode);
-
-		/* Delete the entry in the filestructure (if found) */
-		if (retVal != -1) {
-			yaffs_PutLevel0Tnode(dev,tn,chunkInInode,0);
-		}
-	} else {
-		/*T(("No level 0 found for %d\n", chunkInInode)); */
-	}
-
-	if (retVal == -1) {
-		/* T(("Could not find %d to delete\n",chunkInInode)); */
-	}
-	return retVal;
-}
-
-#ifdef YAFFS_PARANOID
-
-static int yaffs_CheckFileSanity(yaffs_Object * in)
-{
-	int chunk;
-	int nChunks;
-	int fSize;
-	int failed = 0;
-	int objId;
-	yaffs_Tnode *tn;
-	yaffs_Tags localTags;
-	yaffs_Tags *tags = &localTags;
-	int theChunk;
-	int chunkDeleted;
-
-	if (in->variantType != YAFFS_OBJECT_TYPE_FILE) {
-		/* T(("Object not a file\n")); */
-		return YAFFS_FAIL;
-	}
-
-	objId = in->objectId;
-	fSize = in->variant.fileVariant.fileSize;
-	nChunks =
-	    (fSize + in->myDev->nBytesPerChunk - 1) / in->myDev->nBytesPerChunk;
-
-	for (chunk = 1; chunk <= nChunks; chunk++) {
-		tn = yaffs_FindLevel0Tnode(in->myDev, &in->variant.fileVariant,
-					   chunk);
-
-		if (tn) {
-
-			theChunk = yaffs_GetChunkGroupBase(dev,tn,chunk);
-
-			if (yaffs_CheckChunkBits
-			    (dev, theChunk / dev->nChunksPerBlock,
-			     theChunk % dev->nChunksPerBlock)) {
-
-				yaffs_ReadChunkTagsFromNAND(in->myDev, theChunk,
-							    tags,
-							    &chunkDeleted);
-				if (yaffs_TagsMatch
-				    (tags, in->objectId, chunk, chunkDeleted)) {
-					/* found it; */
-
-				}
-			} else {
-
-				failed = 1;
-			}
-
-		} else {
-			/* T(("No level 0 found for %d\n", chunk)); */
-		}
-	}
-
-	return failed ? YAFFS_FAIL : YAFFS_OK;
-}
-
-#endif
-
-static int yaffs_PutChunkIntoFile(yaffs_Object * in, int chunkInInode,
-				  int chunkInNAND, int inScan)
-{
-	/* NB inScan is zero unless scanning. 
-	 * For forward scanning, inScan is > 0; 
-	 * for backward scanning inScan is < 0
-	 */
-	 
-	yaffs_Tnode *tn;
-	yaffs_Device *dev = in->myDev;
-	int existingChunk;
-	yaffs_ExtendedTags existingTags;
-	yaffs_ExtendedTags newTags;
-	unsigned existingSerial, newSerial;
-
-	if (in->variantType != YAFFS_OBJECT_TYPE_FILE) {
-		/* Just ignore an attempt at putting a chunk into a non-file during scanning
-		 * If it is not during Scanning then something went wrong!
-		 */
-		if (!inScan) {
-			T(YAFFS_TRACE_ERROR,
-			  (TSTR
-			   ("yaffs tragedy:attempt to put data chunk into a non-file"
-			    TENDSTR)));
-			YBUG();
-		}
-
-		yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__);
-		return YAFFS_OK;
-	}
-
-	tn = yaffs_AddOrFindLevel0Tnode(dev, &in->variant.fileVariant,
-					chunkInInode);
-	if (!tn) {
-		return YAFFS_FAIL;
-	}
-
-	existingChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode);
-
-	if (inScan != 0) {
-		/* If we're scanning then we need to test for duplicates
-		 * NB This does not need to be efficient since it should only ever 
-		 * happen when the power fails during a write, then only one
-		 * chunk should ever be affected.
-		 *
-		 * Correction for YAFFS2: This could happen quite a lot and we need to think about efficiency! TODO
-		 * Update: For backward scanning we don't need to re-read tags so this is quite cheap.
-		 */
-
-		if (existingChunk != 0) {
-			/* NB Right now existing chunk will not be real chunkId if the device >= 32MB
-			 *    thus we have to do a FindChunkInFile to get the real chunk id.
-			 *
-			 * We have a duplicate now we need to decide which one to use:
-			 *
-			 * Backwards scanning YAFFS2: The old one is what we use, dump the new one.
-			 * Forward scanning YAFFS2: The new one is what we use, dump the old one.
-			 * YAFFS1: Get both sets of tags and compare serial numbers.
-			 */
-
-			if (inScan > 0) {
-				/* Only do this for forward scanning */
-				yaffs_ReadChunkWithTagsFromNAND(dev,
-								chunkInNAND,
-								NULL, &newTags);
-
-				/* Do a proper find */
-				existingChunk =
-				    yaffs_FindChunkInFile(in, chunkInInode,
-							  &existingTags);
-			}
-
-			if (existingChunk <= 0) {
-				/*Hoosterman - how did this happen? */
-
-				T(YAFFS_TRACE_ERROR,
-				  (TSTR
-				   ("yaffs tragedy: existing chunk < 0 in scan"
-				    TENDSTR)));
-
-			}
-
-			/* NB The deleted flags should be false, otherwise the chunks will 
-			 * not be loaded during a scan
-			 */
-
-			newSerial = newTags.serialNumber;
-			existingSerial = existingTags.serialNumber;
-
-			if ((inScan > 0) &&
-			    (in->myDev->isYaffs2 ||
-			     existingChunk <= 0 ||
-			     ((existingSerial + 1) & 3) == newSerial)) {
-				/* Forward scanning.                            
-				 * Use new
-				 * Delete the old one and drop through to update the tnode
-				 */
-				yaffs_DeleteChunk(dev, existingChunk, 1,
-						  __LINE__);
-			} else {
-				/* Backward scanning or we want to use the existing one
-				 * Use existing.
-				 * Delete the new one and return early so that the tnode isn't changed
-				 */
-				yaffs_DeleteChunk(dev, chunkInNAND, 1,
-						  __LINE__);
-				return YAFFS_OK;
-			}
-		}
-
-	}
-
-	if (existingChunk == 0) {
-		in->nDataChunks++;
-	}
-
-	yaffs_PutLevel0Tnode(dev,tn,chunkInInode,chunkInNAND);
-
-	return YAFFS_OK;
-}
-
-static int yaffs_ReadChunkDataFromObject(yaffs_Object * in, int chunkInInode,
-					 __u8 * buffer)
-{
-	int chunkInNAND = yaffs_FindChunkInFile(in, chunkInInode, NULL);
-
-	//	if (chunkInNAND < 0) chunkInNAND = 0; //debug
-	if (chunkInNAND >= 0) {
-		return yaffs_ReadChunkWithTagsFromNAND(in->myDev, chunkInNAND,
-						       buffer, NULL);
-	} else {
-		T(YAFFS_TRACE_NANDACCESS,
-		  (TSTR("Chunk %d not found zero instead" TENDSTR),
-		   chunkInNAND));
-		/* get sane (zero) data if you read a hole */
-		memset(buffer, 0, in->myDev->nBytesPerChunk);	
-		return 0;
-	}
-
-}
-
-void yaffs_DeleteChunk(yaffs_Device * dev, int chunkId, int markNAND, int lyn)
-{
-	int block;
-	int page;
-	yaffs_ExtendedTags tags;
-	yaffs_BlockInfo *bi;
-
-	if (chunkId <= 0)
-		return;
-
-	dev->nDeletions++;
-	block = chunkId / dev->nChunksPerBlock;
-	page = chunkId % dev->nChunksPerBlock;
-
-	bi = yaffs_GetBlockInfo(dev, block);
-
-	T(YAFFS_TRACE_DELETION,
-	  (TSTR("line %d delete of chunk %d" TENDSTR), lyn, chunkId));
-
-	if (markNAND &&
-	    bi->blockState != YAFFS_BLOCK_STATE_COLLECTING && !dev->isYaffs2) {
-
-		yaffs_InitialiseTags(&tags);
-
-		tags.chunkDeleted = 1;
-
-		yaffs_WriteChunkWithTagsToNAND(dev, chunkId, NULL, &tags);
-		yaffs_HandleUpdateChunk(dev, chunkId, &tags);
-	} else {
-		dev->nUnmarkedDeletions++;
-	}
-
-	/* Pull out of the management area.
-	 * If the whole block became dirty, this will kick off an erasure.
-	 */
-	if (bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING ||
-	    bi->blockState == YAFFS_BLOCK_STATE_FULL ||
-	    bi->blockState == YAFFS_BLOCK_STATE_NEEDS_SCANNING ||
-	    bi->blockState == YAFFS_BLOCK_STATE_COLLECTING) {
-		dev->nFreeChunks++;
-
-		yaffs_ClearChunkBit(dev, block, page);
-
-		bi->pagesInUse--;
-
-		if (bi->pagesInUse == 0 &&
-		    !bi->hasShrinkHeader &&
-		    bi->blockState != YAFFS_BLOCK_STATE_ALLOCATING &&
-		    bi->blockState != YAFFS_BLOCK_STATE_NEEDS_SCANNING) {
-			yaffs_BlockBecameDirty(dev, block);
-		}
-
-	} else {
-		/* T(("Bad news deleting chunk %d\n",chunkId)); */
-	}
-
-}
-
-static int yaffs_WriteChunkDataToObject(yaffs_Object * in, int chunkInInode,
-					const __u8 * buffer, int nBytes,
-					int useReserve)
-{
-	/* Find old chunk Need to do this to get serial number
-	 * Write new one and patch into tree.
-	 * Invalidate old tags.
-	 */
-
-	int prevChunkId;
-	yaffs_ExtendedTags prevTags;
-
-	int newChunkId;
-	yaffs_ExtendedTags newTags;
-
-	yaffs_Device *dev = in->myDev;
-
-	yaffs_CheckGarbageCollection(dev);
-
-	/* Get the previous chunk at this location in the file if it exists */
-	prevChunkId = yaffs_FindChunkInFile(in, chunkInInode, &prevTags);
-
-	/* Set up new tags */
-	yaffs_InitialiseTags(&newTags);
-
-	newTags.chunkId = chunkInInode;
-	newTags.objectId = in->objectId;
-	newTags.serialNumber =
-	    (prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1;
-	newTags.byteCount = nBytes;
-
-	newChunkId =
-	    yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags,
-					      useReserve);
-
-	if (newChunkId >= 0) {
-		yaffs_PutChunkIntoFile(in, chunkInInode, newChunkId, 0);
-
-		if (prevChunkId >= 0) {
-			yaffs_DeleteChunk(dev, prevChunkId, 1, __LINE__);
-
-		}
-
-		yaffs_CheckFileSanity(in);
-	}
-	return newChunkId;
-
-}
-
-/* UpdateObjectHeader updates the header on NAND for an object.
- * If name is not NULL, then that new name is used.
- */
-int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name, int force,
-			     int isShrink, int shadows)
-{
-
-	yaffs_BlockInfo *bi;
-
-	yaffs_Device *dev = in->myDev;
-
-	int prevChunkId;
-	int retVal = 0;
-
-	int newChunkId;
-	yaffs_ExtendedTags newTags;
-
-	__u8 *buffer = NULL;
-	YCHAR oldName[YAFFS_MAX_NAME_LENGTH + 1];
-
-	yaffs_ObjectHeader *oh = NULL;
-
-	if (!in->fake || force) {
-
-		yaffs_CheckGarbageCollection(dev);
-
-		buffer = yaffs_GetTempBuffer(in->myDev, __LINE__);
-		oh = (yaffs_ObjectHeader *) buffer;
-
-		prevChunkId = in->chunkId;
-
-		if (prevChunkId >= 0) {
-			yaffs_ReadChunkWithTagsFromNAND(dev, prevChunkId,
-							buffer, NULL);
-			memcpy(oldName, oh->name, sizeof(oh->name));
-		}
-
-		memset(buffer, 0xFF, dev->nBytesPerChunk);
-
-		oh->type = in->variantType;
-		oh->yst_mode = in->yst_mode;
-		oh->shadowsObject = shadows;
-
-#ifdef CONFIG_YAFFS_WINCE
-		oh->win_atime[0] = in->win_atime[0];
-		oh->win_ctime[0] = in->win_ctime[0];
-		oh->win_mtime[0] = in->win_mtime[0];
-		oh->win_atime[1] = in->win_atime[1];
-		oh->win_ctime[1] = in->win_ctime[1];
-		oh->win_mtime[1] = in->win_mtime[1];
-#else
-		oh->yst_uid = in->yst_uid;
-		oh->yst_gid = in->yst_gid;
-		oh->yst_atime = in->yst_atime;
-		oh->yst_mtime = in->yst_mtime;
-		oh->yst_ctime = in->yst_ctime;
-		oh->yst_rdev = in->yst_rdev;
-#endif
-		if (in->parent) {
-			oh->parentObjectId = in->parent->objectId;
-		} else {
-			oh->parentObjectId = 0;
-		}
-
-		if (name && *name) {
-			memset(oh->name, 0, sizeof(oh->name));
-			yaffs_strncpy(oh->name, name, YAFFS_MAX_NAME_LENGTH);
-		} else if (prevChunkId) {
-			memcpy(oh->name, oldName, sizeof(oh->name));
-		} else {
-			memset(oh->name, 0, sizeof(oh->name));
-		}
-
-		oh->isShrink = isShrink;
-
-		switch (in->variantType) {
-		case YAFFS_OBJECT_TYPE_UNKNOWN:
-			/* Should not happen */
-			break;
-		case YAFFS_OBJECT_TYPE_FILE:
-			oh->fileSize =
-			    (oh->parentObjectId == YAFFS_OBJECTID_DELETED
-			     || oh->parentObjectId ==
-			     YAFFS_OBJECTID_UNLINKED) ? 0 : in->variant.
-			    fileVariant.fileSize;
-			break;
-		case YAFFS_OBJECT_TYPE_HARDLINK:
-			oh->equivalentObjectId =
-			    in->variant.hardLinkVariant.equivalentObjectId;
-			break;
-		case YAFFS_OBJECT_TYPE_SPECIAL:
-			/* Do nothing */
-			break;
-		case YAFFS_OBJECT_TYPE_DIRECTORY:
-			/* Do nothing */
-			break;
-		case YAFFS_OBJECT_TYPE_SYMLINK:
-			yaffs_strncpy(oh->alias,
-				      in->variant.symLinkVariant.alias,
-				      YAFFS_MAX_ALIAS_LENGTH);
-			oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0;
-			break;
-		}
-
-		/* Tags */
-		yaffs_InitialiseTags(&newTags);
-		in->serial++;
-		newTags.chunkId = 0;
-		newTags.objectId = in->objectId;
-		newTags.serialNumber = in->serial;
-
-		/* Add extra info for file header */
-
-		newTags.extraHeaderInfoAvailable = 1;
-		newTags.extraParentObjectId = oh->parentObjectId;
-		newTags.extraFileLength = oh->fileSize;
-		newTags.extraIsShrinkHeader = oh->isShrink;
-		newTags.extraEquivalentObjectId = oh->equivalentObjectId;
-		newTags.extraShadows = (oh->shadowsObject > 0) ? 1 : 0;
-		newTags.extraObjectType = in->variantType;
-
-		/* Create new chunk in NAND */
-		newChunkId =
-		    yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags,
-						      (prevChunkId >= 0) ? 1 : 0);
-
-		if (newChunkId >= 0) {
-
-			in->chunkId = newChunkId;
-
-			if (prevChunkId >= 0) {
-				yaffs_DeleteChunk(dev, prevChunkId, 1,
-						  __LINE__);
-			}
-
-			in->dirty = 0;
-
-			/* If this was a shrink, then mark the block that the chunk lives on */
-			if (isShrink) {
-				bi = yaffs_GetBlockInfo(in->myDev,
-							newChunkId /in->myDev->	nChunksPerBlock);
-				bi->hasShrinkHeader = 1;
-			}
-
-		}
-
-		retVal = newChunkId;
-
-	}
-
-	if (buffer)
-		yaffs_ReleaseTempBuffer(dev, buffer, __LINE__);
-
-	return retVal;
-}
-
-/*------------------------ Short Operations Cache ----------------------------------------
- *   In many situations where there is no high level buffering (eg WinCE) a lot of
- *   reads might be short sequential reads, and a lot of writes may be short 
- *   sequential writes. eg. scanning/writing a jpeg file.
- *   In these cases, a short read/write cache can provide a huge perfomance benefit 
- *   with dumb-as-a-rock code.
- *   In Linux, the page cache provides read buffering aand the short op cache provides write 
- *   buffering.
- *
- *   There are a limited number (~10) of cache chunks per device so that we don't
- *   need a very intelligent search.
- */
-
-static void yaffs_FlushFilesChunkCache(yaffs_Object * obj)
-{
-	yaffs_Device *dev = obj->myDev;
-	int lowest = -99;	/* Stop compiler whining. */
-	int i;
-	yaffs_ChunkCache *cache;
-	int chunkWritten = 0;
-	int nCaches = obj->myDev->nShortOpCaches;
-
-	if (nCaches > 0) {
-		do {
-			cache = NULL;
-
-			/* Find the dirty cache for this object with the lowest chunk id. */
-			for (i = 0; i < nCaches; i++) {
-				if (dev->srCache[i].object == obj &&
-				    dev->srCache[i].dirty) {
-					if (!cache
-					    || dev->srCache[i].chunkId <
-					    lowest) {
-						cache = &dev->srCache[i];
-						lowest = cache->chunkId;
-					}
-				}
-			}
-
-			if (cache && !cache->locked) {
-				/* Write it out and free it up */
-
-				chunkWritten =
-				    yaffs_WriteChunkDataToObject(cache->object,
-								 cache->chunkId,
-								 cache->data,
-								 cache->nBytes,
-								 1);
-				cache->dirty = 0;
-				cache->object = NULL;
-			}
-
-		} while (cache && chunkWritten > 0);
-
-		if (cache) {
-			/* Hoosterman, disk full while writing cache out. */
-			T(YAFFS_TRACE_ERROR,
-			  (TSTR("yaffs tragedy: no space during cache write" TENDSTR)));
-
-		}
-	}
-
-}
-
-/* Grab us a cache chunk for use.
- * First look for an empty one. 
- * Then look for the least recently used non-dirty one.
- * Then look for the least recently used dirty one...., flush and look again.
- */
-static yaffs_ChunkCache *yaffs_GrabChunkCacheWorker(yaffs_Device * dev)
-{
-	int i;
-	int usage;
-	int theOne;
-
-	if (dev->nShortOpCaches > 0) {
-		for (i = 0; i < dev->nShortOpCaches; i++) {
-			if (!dev->srCache[i].object) 
-				return &dev->srCache[i];
-		}
-
-		return NULL;
-
-		theOne = -1;
-		usage = 0;	/* just to stop the compiler grizzling */
-
-		for (i = 0; i < dev->nShortOpCaches; i++) {
-			if (!dev->srCache[i].dirty &&
-			    ((dev->srCache[i].lastUse < usage && theOne >= 0) ||
-			     theOne < 0)) {
-				usage = dev->srCache[i].lastUse;
-				theOne = i;
-			}
-		}
-
-
-		return theOne >= 0 ? &dev->srCache[theOne] : NULL;
-	} else {
-		return NULL;
-	}
-
-}
-
-static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device * dev)
-{
-	yaffs_ChunkCache *cache;
-	yaffs_Object *theObj;
-	int usage;
-	int i;
-	int pushout;
-
-	if (dev->nShortOpCaches > 0) {
-		/* Try find a non-dirty one... */
-
-		cache = yaffs_GrabChunkCacheWorker(dev);
-
-		if (!cache) {
-			/* They were all dirty, find the last recently used object and flush
-			 * its cache, then  find again.
-			 * NB what's here is not very accurate, we actually flush the object
-			 * the last recently used page.
-			 */
-
-			/* With locking we can't assume we can use entry zero */
-
-			theObj = NULL;
-			usage = -1;
-			cache = NULL;
-			pushout = -1;
-
-			for (i = 0; i < dev->nShortOpCaches; i++) {
-				if (dev->srCache[i].object &&
-				    !dev->srCache[i].locked &&
-				    (dev->srCache[i].lastUse < usage || !cache))
-				{
-					usage = dev->srCache[i].lastUse;
-					theObj = dev->srCache[i].object;
-					cache = &dev->srCache[i];
-					pushout = i;
-				}
-			}
-
-			if (!cache || cache->dirty) {
-				/* Flush and try again */
-				yaffs_FlushFilesChunkCache(theObj);
-				cache = yaffs_GrabChunkCacheWorker(dev);
-			}
-
-		}
-		return cache;
-	} else
-		return NULL;
-
-}
-
-/* Find a cached chunk */
-static yaffs_ChunkCache *yaffs_FindChunkCache(const yaffs_Object * obj,
-					      int chunkId)
-{
-	yaffs_Device *dev = obj->myDev;
-	int i;
-	if (dev->nShortOpCaches > 0) {
-		for (i = 0; i < dev->nShortOpCaches; i++) {
-			if (dev->srCache[i].object == obj &&
-			    dev->srCache[i].chunkId == chunkId) {
-				dev->cacheHits++;
-
-				return &dev->srCache[i];
-			}
-		}
-	}
-	return NULL;
-}
-
-/* Mark the chunk for the least recently used algorithym */
-static void yaffs_UseChunkCache(yaffs_Device * dev, yaffs_ChunkCache * cache,
-				int isAWrite)
-{
-
-	if (dev->nShortOpCaches > 0) {
-		if (dev->srLastUse < 0 || dev->srLastUse > 100000000) {
-			/* Reset the cache usages */
-			int i;
-			for (i = 1; i < dev->nShortOpCaches; i++) {
-				dev->srCache[i].lastUse = 0;
-			}
-			dev->srLastUse = 0;
-		}
-
-		dev->srLastUse++;
-
-		cache->lastUse = dev->srLastUse;
-
-		if (isAWrite) {
-			cache->dirty = 1;
-		}
-	}
-}
-
-/* Invalidate a single cache page.
- * Do this when a whole page gets written,
- * ie the short cache for this page is no longer valid.
- */
-static void yaffs_InvalidateChunkCache(yaffs_Object * object, int chunkId)
-{
-	if (object->myDev->nShortOpCaches > 0) {
-		yaffs_ChunkCache *cache = yaffs_FindChunkCache(object, chunkId);
-
-		if (cache) {
-			cache->object = NULL;
-		}
-	}
-}
-
-/* Invalidate all the cache pages associated with this object
- * Do this whenever ther file is deleted or resized.
- */
-static void yaffs_InvalidateWholeChunkCache(yaffs_Object * in)
-{
-	int i;
-	yaffs_Device *dev = in->myDev;
-
-	if (dev->nShortOpCaches > 0) {
-		/* Invalidate it. */
-		for (i = 0; i < dev->nShortOpCaches; i++) {
-			if (dev->srCache[i].object == in) {
-				dev->srCache[i].object = NULL;
-			}
-		}
-	}
-}
-
-/*--------------------- File read/write ------------------------
- * Read and write have very similar structures.
- * In general the read/write has three parts to it
- * An incomplete chunk to start with (if the read/write is not chunk-aligned)
- * Some complete chunks
- * An incomplete chunk to end off with
- *
- * Curve-balls: the first chunk might also be the last chunk.
- */
-
-int yaffs_ReadDataFromFile(yaffs_Object * in, __u8 * buffer, __u32 offset,
-			   int nBytes)
-{
-
-	int chunk;
-	int start;
-	int nToCopy;
-	int n = nBytes;
-	int nDone = 0;
-	yaffs_ChunkCache *cache;
-
-	yaffs_Device *dev;
-
-	dev = in->myDev;
-
-	while (n > 0) {
-		chunk = offset / dev->nBytesPerChunk + 1;   /* The first chunk is 1 */
-		start = offset % dev->nBytesPerChunk;
-
-		/* OK now check for the curveball where the start and end are in
-		 * the same chunk.      
-		 */
-		if ((start + n) < dev->nBytesPerChunk) {
-			nToCopy = n;
-		} else {
-			nToCopy = dev->nBytesPerChunk - start;
-		}
-
-		cache = yaffs_FindChunkCache(in, chunk);
-
-		/* If the chunk is already in the cache or it is less than a whole chunk
-		 * then use the cache (if there is caching)
-		 * else bypass the cache.
-		 */
-		if (cache || nToCopy != dev->nBytesPerChunk) {
-			if (dev->nShortOpCaches > 0) {
-
-				/* If we can't find the data in the cache, then load it up. */
-
-				if (!cache) {
-					cache = yaffs_GrabChunkCache(in->myDev);
-					cache->object = in;
-					cache->chunkId = chunk;
-					cache->dirty = 0;
-					cache->locked = 0;
-					yaffs_ReadChunkDataFromObject(in, chunk,
-								      cache->
-								      data);
-					cache->nBytes = 0;
-				}
-
-				yaffs_UseChunkCache(dev, cache, 0);
-
-				cache->locked = 1;
-
-#ifdef CONFIG_YAFFS_WINCE
-				yfsd_UnlockYAFFS(TRUE);
-#endif
-				memcpy(buffer, &cache->data[start], nToCopy);
-
-#ifdef CONFIG_YAFFS_WINCE
-				yfsd_LockYAFFS(TRUE);
-#endif
-				cache->locked = 0;
-			} else {
-				/* Read into the local buffer then copy..*/
-
-				__u8 *localBuffer =
-				    yaffs_GetTempBuffer(dev, __LINE__);
-				yaffs_ReadChunkDataFromObject(in, chunk,
-							      localBuffer);
-#ifdef CONFIG_YAFFS_WINCE
-				yfsd_UnlockYAFFS(TRUE);
-#endif
-				memcpy(buffer, &localBuffer[start], nToCopy);
-
-#ifdef CONFIG_YAFFS_WINCE
-				yfsd_LockYAFFS(TRUE);
-#endif
-				yaffs_ReleaseTempBuffer(dev, localBuffer,
-							__LINE__);
-			}
-
-		} else {
-#ifdef CONFIG_YAFFS_WINCE
-			__u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__);
-
-			/* Under WinCE can't do direct transfer. Need to use a local buffer.
-			 * This is because we otherwise screw up WinCE's memory mapper
-			 */
-			yaffs_ReadChunkDataFromObject(in, chunk, localBuffer);
-
-#ifdef CONFIG_YAFFS_WINCE
-			yfsd_UnlockYAFFS(TRUE);
-#endif
-			memcpy(buffer, localBuffer, dev->nBytesPerChunk);
-
-#ifdef CONFIG_YAFFS_WINCE
-			yfsd_LockYAFFS(TRUE);
-			yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__);
-#endif
-
-#else
-			/* A full chunk. Read directly into the supplied buffer. */
-			yaffs_ReadChunkDataFromObject(in, chunk, buffer);
-#endif
-		}
-
-		n -= nToCopy;
-		offset += nToCopy;
-		buffer += nToCopy;
-		nDone += nToCopy;
-
-	}
-
-	return nDone;
-}
-
-int yaffs_WriteDataToFile(yaffs_Object * in, const __u8 * buffer, __u32 offset,
-			  int nBytes, int writeThrough)
-{
-
-	int chunk;
-	int start;
-	int nToCopy;
-	int n = nBytes;
-	int nDone = 0;
-	int nToWriteBack;
-	int startOfWrite = offset;
-	int chunkWritten = 0;
-	int nBytesRead;
-
-	yaffs_Device *dev;
-
-	dev = in->myDev;
-
-	while (n > 0 && chunkWritten >= 0) {
-		chunk = offset / dev->nBytesPerChunk + 1;
-		start = offset % dev->nBytesPerChunk;
-
-		/* OK now check for the curveball where the start and end are in
-		 * the same chunk.
-		 */
-
-		if ((start + n) < dev->nBytesPerChunk) {
-			nToCopy = n;
-
-			/* Now folks, to calculate how many bytes to write back....
-			 * If we're overwriting and not writing to then end of file then
-			 * we need to write back as much as was there before.
-			 */
-
-			nBytesRead =
-			    in->variant.fileVariant.fileSize -
-			    ((chunk - 1) * dev->nBytesPerChunk);
-
-			if (nBytesRead > dev->nBytesPerChunk) {
-				nBytesRead = dev->nBytesPerChunk;
-			}
-
-			nToWriteBack =
-			    (nBytesRead >
-			     (start + n)) ? nBytesRead : (start + n);
-
-		} else {
-			nToCopy = dev->nBytesPerChunk - start;
-			nToWriteBack = dev->nBytesPerChunk;
-		}
-
-		if (nToCopy != dev->nBytesPerChunk) {
-			/* An incomplete start or end chunk (or maybe both start and end chunk) */
-			if (dev->nShortOpCaches > 0) {
-				yaffs_ChunkCache *cache;
-				/* If we can't find the data in the cache, then load the cache */
-				cache = yaffs_FindChunkCache(in, chunk);
-				if (!cache
-				    && yaffs_CheckSpaceForAllocation(in->
-								     myDev)) {
-					cache = yaffs_GrabChunkCache(in->myDev);
-					cache->object = in;
-					cache->chunkId = chunk;
-					cache->dirty = 0;
-					cache->locked = 0;
-					yaffs_ReadChunkDataFromObject(in, chunk,
-								      cache->
-								      data);
-				}
-
-				if (cache) {
-					yaffs_UseChunkCache(dev, cache, 1);
-					cache->locked = 1;
-#ifdef CONFIG_YAFFS_WINCE
-					yfsd_UnlockYAFFS(TRUE);
-#endif
-
-					memcpy(&cache->data[start], buffer,
-					       nToCopy);
-
-#ifdef CONFIG_YAFFS_WINCE
-					yfsd_LockYAFFS(TRUE);
-#endif
-					cache->locked = 0;
-					cache->nBytes = nToWriteBack;
-
-					if (writeThrough) {
-						chunkWritten =
-						    yaffs_WriteChunkDataToObject
-						    (cache->object,
-						     cache->chunkId,
-						     cache->data, cache->nBytes,
-						     1);
-						cache->dirty = 0;
-					}
-
-				} else {
-					chunkWritten = -1;	/* fail the write */
-				}
-			} else {
-				/* An incomplete start or end chunk (or maybe both start and end chunk)
-				 * Read into the local buffer then copy, then copy over and write back.
-				 */
-
-				__u8 *localBuffer =
-				    yaffs_GetTempBuffer(dev, __LINE__);
-
-				yaffs_ReadChunkDataFromObject(in, chunk,
-							      localBuffer);
-
-#ifdef CONFIG_YAFFS_WINCE
-				yfsd_UnlockYAFFS(TRUE);
-#endif
-
-				memcpy(&localBuffer[start], buffer, nToCopy);
-
-#ifdef CONFIG_YAFFS_WINCE
-				yfsd_LockYAFFS(TRUE);
-#endif
-				chunkWritten =
-				    yaffs_WriteChunkDataToObject(in, chunk,
-								 localBuffer,
-								 nToWriteBack,
-								 0);
-
-				yaffs_ReleaseTempBuffer(dev, localBuffer,
-							__LINE__);
-
-			}
-
-		} else {
-
-#ifdef CONFIG_YAFFS_WINCE
-			/* Under WinCE can't do direct transfer. Need to use a local buffer.
-			 * This is because we otherwise screw up WinCE's memory mapper
-			 */
-			__u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__);
-#ifdef CONFIG_YAFFS_WINCE
-			yfsd_UnlockYAFFS(TRUE);
-#endif
-			memcpy(localBuffer, buffer, dev->nBytesPerChunk);
-#ifdef CONFIG_YAFFS_WINCE
-			yfsd_LockYAFFS(TRUE);
-#endif
-			chunkWritten =
-			    yaffs_WriteChunkDataToObject(in, chunk, localBuffer,
-							 dev->nBytesPerChunk,
-							 0);
-			yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__);
-#else
-			/* A full chunk. Write directly from the supplied buffer. */
-			chunkWritten =
-			    yaffs_WriteChunkDataToObject(in, chunk, buffer,
-							 dev->nBytesPerChunk,
-							 0);
-#endif
-			/* Since we've overwritten the cached data, we better invalidate it. */
-			yaffs_InvalidateChunkCache(in, chunk);
-		}
-
-		if (chunkWritten >= 0) {
-			n -= nToCopy;
-			offset += nToCopy;
-			buffer += nToCopy;
-			nDone += nToCopy;
-		}
-
-	}
-
-	/* Update file object */
-
-	if ((startOfWrite + nDone) > in->variant.fileVariant.fileSize) {
-		in->variant.fileVariant.fileSize = (startOfWrite + nDone);
-	}
-
-	in->dirty = 1;
-
-	return nDone;
-}
-
-
-/* ---------------------- File resizing stuff ------------------ */
-
-static void yaffs_PruneResizedChunks(yaffs_Object * in, int newSize)
-{
-
-	yaffs_Device *dev = in->myDev;
-	int oldFileSize = in->variant.fileVariant.fileSize;
-
-	int lastDel = 1 + (oldFileSize - 1) / dev->nBytesPerChunk;
-
-	int startDel = 1 + (newSize + dev->nBytesPerChunk - 1) /
-	    dev->nBytesPerChunk;
-	int i;
-	int chunkId;
-
-	/* Delete backwards so that we don't end up with holes if
-	 * power is lost part-way through the operation.
-	 */
-	for (i = lastDel; i >= startDel; i--) {
-		/* NB this could be optimised somewhat,
-		 * eg. could retrieve the tags and write them without
-		 * using yaffs_DeleteChunk
-		 */
-
-		chunkId = yaffs_FindAndDeleteChunkInFile(in, i, NULL);
-		if (chunkId > 0) {
-			if (chunkId <
-			    (dev->internalStartBlock * dev->nChunksPerBlock)
-			    || chunkId >=
-			    ((dev->internalEndBlock +
-			      1) * dev->nChunksPerBlock)) {
-				T(YAFFS_TRACE_ALWAYS,
-				  (TSTR("Found daft chunkId %d for %d" TENDSTR),
-				   chunkId, i));
-			} else {
-				in->nDataChunks--;
-				yaffs_DeleteChunk(dev, chunkId, 1, __LINE__);
-			}
-		}
-	}
-
-}
-
-int yaffs_ResizeFile(yaffs_Object * in, int newSize)
-{
-
-	int oldFileSize = in->variant.fileVariant.fileSize;
-	int sizeOfPartialChunk;
-	yaffs_Device *dev = in->myDev;
-
-	sizeOfPartialChunk = newSize % dev->nBytesPerChunk;
-
-	yaffs_FlushFilesChunkCache(in);
-	yaffs_InvalidateWholeChunkCache(in);
-
-	yaffs_CheckGarbageCollection(dev);
-
-	if (in->variantType != YAFFS_OBJECT_TYPE_FILE) {
-		return yaffs_GetFileSize(in);
-	}
-
-	if (newSize == oldFileSize) {
-		return oldFileSize;
-	}
-
-	if (newSize < oldFileSize) {
-
-		yaffs_PruneResizedChunks(in, newSize);
-
-		if (sizeOfPartialChunk != 0) {
-			int lastChunk = 1 + newSize / dev->nBytesPerChunk;
-			__u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__);
-
-			/* Got to read and rewrite the last chunk with its new size and zero pad */
-			yaffs_ReadChunkDataFromObject(in, lastChunk,
-						      localBuffer);
-
-			memset(localBuffer + sizeOfPartialChunk, 0,
-			       dev->nBytesPerChunk - sizeOfPartialChunk);
-
-			yaffs_WriteChunkDataToObject(in, lastChunk, localBuffer,
-						     sizeOfPartialChunk, 1);
-
-			yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__);
-		}
-
-		in->variant.fileVariant.fileSize = newSize;
-
-		yaffs_PruneFileStructure(dev, &in->variant.fileVariant);
-	}
-	/* Write a new object header.
-	 * show we've shrunk the file, if need be
-	 * Do this only if the file is not in the deleted directories.
-	 */
-	if (in->parent->objectId != YAFFS_OBJECTID_UNLINKED &&
-	    in->parent->objectId != YAFFS_OBJECTID_DELETED) {
-		yaffs_UpdateObjectHeader(in, NULL, 0,
-					 (newSize < oldFileSize) ? 1 : 0, 0);
-	}
-
-	return newSize;
-}
-
-loff_t yaffs_GetFileSize(yaffs_Object * obj)
-{
-	obj = yaffs_GetEquivalentObject(obj);
-
-	switch (obj->variantType) {
-	case YAFFS_OBJECT_TYPE_FILE:
-		return obj->variant.fileVariant.fileSize;
-	case YAFFS_OBJECT_TYPE_SYMLINK:
-		return yaffs_strlen(obj->variant.symLinkVariant.alias);
-	default:
-		return 0;
-	}
-}
-
-
-
-int yaffs_FlushFile(yaffs_Object * in, int updateTime)
-{
-	int retVal;
-	if (in->dirty) {
-		yaffs_FlushFilesChunkCache(in);
-		if (updateTime) {
-#ifdef CONFIG_YAFFS_WINCE
-			yfsd_WinFileTimeNow(in->win_mtime);
-#else
-
-			in->yst_mtime = Y_CURRENT_TIME;
-
-#endif
-		}
-
-		retVal =
-		    (yaffs_UpdateObjectHeader(in, NULL, 0, 0, 0) >=
-		     0) ? YAFFS_OK : YAFFS_FAIL;
-	} else {
-		retVal = YAFFS_OK;
-	}
-
-	return retVal;
-
-}
-
-static int yaffs_DoGenericObjectDeletion(yaffs_Object * in)
-{
-
-	/* First off, invalidate the file's data in the cache, without flushing. */
-	yaffs_InvalidateWholeChunkCache(in);
-
-	if (in->myDev->isYaffs2 && (in->parent != in->myDev->deletedDir)) {
-		/* Move to the unlinked directory so we have a record that it was deleted. */
-		yaffs_ChangeObjectName(in, in->myDev->deletedDir, NULL, 0, 0);
-
-	}
-
-	yaffs_RemoveObjectFromDirectory(in);
-	yaffs_DeleteChunk(in->myDev, in->chunkId, 1, __LINE__);
-	in->chunkId = -1;
-
-	yaffs_FreeObject(in);
-	return YAFFS_OK;
-
-}
-
-/* yaffs_DeleteFile deletes the whole file data
- * and the inode associated with the file.
- * It does not delete the links associated with the file.
- */
-static int yaffs_UnlinkFile(yaffs_Object * in)
-{
-
-	int retVal;
-	int immediateDeletion = 0;
-
-	if (1) {
-#ifdef __KERNEL__
-		if (!in->myInode) {
-			immediateDeletion = 1;
-
-		}
-#else
-		if (in->inUse <= 0) {
-			immediateDeletion = 1;
-
-		}
-#endif
-		if (immediateDeletion) {
-			retVal =
-			    yaffs_ChangeObjectName(in, in->myDev->deletedDir,
-						   NULL, 0, 0);
-			T(YAFFS_TRACE_TRACING,
-			  (TSTR("yaffs: immediate deletion of file %d" TENDSTR),
-			   in->objectId));
-			in->deleted = 1;
-			in->myDev->nDeletedFiles++;
-			if (0 && in->myDev->isYaffs2) {
-				yaffs_ResizeFile(in, 0);
-			}
-			yaffs_SoftDeleteFile(in);
-		} else {
-			retVal =
-			    yaffs_ChangeObjectName(in, in->myDev->unlinkedDir,
-						   NULL, 0, 0);
-		}
-
-	}
-	return retVal;
-}
-
-int yaffs_DeleteFile(yaffs_Object * in)
-{
-	int retVal = YAFFS_OK;
-
-	if (in->nDataChunks > 0) {
-		/* Use soft deletion if there is data in the file */
-		if (!in->unlinked) {
-			retVal = yaffs_UnlinkFile(in);
-		}
-		if (retVal == YAFFS_OK && in->unlinked && !in->deleted) {
-			in->deleted = 1;
-			in->myDev->nDeletedFiles++;
-			yaffs_SoftDeleteFile(in);
-		}
-		return in->deleted ? YAFFS_OK : YAFFS_FAIL;
-	} else {
-		/* The file has no data chunks so we toss it immediately */
-		yaffs_FreeTnode(in->myDev, in->variant.fileVariant.top);
-		in->variant.fileVariant.top = NULL;
-		yaffs_DoGenericObjectDeletion(in);
-
-		return YAFFS_OK;
-	}
-}
-
-static int yaffs_DeleteDirectory(yaffs_Object * in)
-{
-	/* First check that the directory is empty. */
-	if (list_empty(&in->variant.directoryVariant.children)) {
-		return yaffs_DoGenericObjectDeletion(in);
-	}
-
-	return YAFFS_FAIL;
-
-}
-
-static int yaffs_DeleteSymLink(yaffs_Object * in)
-{
-	YFREE(in->variant.symLinkVariant.alias);
-
-	return yaffs_DoGenericObjectDeletion(in);
-}
-
-static int yaffs_DeleteHardLink(yaffs_Object * in)
-{
-	/* remove this hardlink from the list assocaited with the equivalent
-	 * object
-	 */
-	list_del(&in->hardLinks);
-	return yaffs_DoGenericObjectDeletion(in);
-}
-
-static void yaffs_DestroyObject(yaffs_Object * obj)
-{
-	switch (obj->variantType) {
-	case YAFFS_OBJECT_TYPE_FILE:
-		yaffs_DeleteFile(obj);
-		break;
-	case YAFFS_OBJECT_TYPE_DIRECTORY:
-		yaffs_DeleteDirectory(obj);
-		break;
-	case YAFFS_OBJECT_TYPE_SYMLINK:
-		yaffs_DeleteSymLink(obj);
-		break;
-	case YAFFS_OBJECT_TYPE_HARDLINK:
-		yaffs_DeleteHardLink(obj);
-		break;
-	case YAFFS_OBJECT_TYPE_SPECIAL:
-		yaffs_DoGenericObjectDeletion(obj);
-		break;
-	case YAFFS_OBJECT_TYPE_UNKNOWN:
-		break;		/* should not happen. */
-	}
-}
-
-static int yaffs_UnlinkWorker(yaffs_Object * obj)
-{
-
-	if (obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) {
-		return yaffs_DeleteHardLink(obj);
-	} else if (!list_empty(&obj->hardLinks)) {
-		/* Curve ball: We're unlinking an object that has a hardlink.
-		 *
-		 * This problem arises because we are not strictly following
-		 * The Linux link/inode model.
-		 *
-		 * We can't really delete the object.
-		 * Instead, we do the following:
-		 * - Select a hardlink.
-		 * - Unhook it from the hard links
-		 * - Unhook it from its parent directory (so that the rename can work)
-		 * - Rename the object to the hardlink's name.
-		 * - Delete the hardlink
-		 */
-
-		yaffs_Object *hl;
-		int retVal;
-		YCHAR name[YAFFS_MAX_NAME_LENGTH + 1];
-
-		hl = list_entry(obj->hardLinks.next, yaffs_Object, hardLinks);
-
-		list_del_init(&hl->hardLinks);
-		list_del_init(&hl->siblings);
-
-		yaffs_GetObjectName(hl, name, YAFFS_MAX_NAME_LENGTH + 1);
-
-		retVal = yaffs_ChangeObjectName(obj, hl->parent, name, 0, 0);
-
-		if (retVal == YAFFS_OK) {
-			retVal = yaffs_DoGenericObjectDeletion(hl);
-		}
-		return retVal;
-
-	} else {
-		switch (obj->variantType) {
-		case YAFFS_OBJECT_TYPE_FILE:
-			return yaffs_UnlinkFile(obj);
-			break;
-		case YAFFS_OBJECT_TYPE_DIRECTORY:
-			return yaffs_DeleteDirectory(obj);
-			break;
-		case YAFFS_OBJECT_TYPE_SYMLINK:
-			return yaffs_DeleteSymLink(obj);
-			break;
-		case YAFFS_OBJECT_TYPE_SPECIAL:
-			return yaffs_DoGenericObjectDeletion(obj);
-			break;
-		case YAFFS_OBJECT_TYPE_HARDLINK:
-		case YAFFS_OBJECT_TYPE_UNKNOWN:
-		default:
-			return YAFFS_FAIL;
-		}
-	}
-}
-
-
-static int yaffs_UnlinkObject( yaffs_Object *obj)
-{
-
-	if (obj && obj->unlinkAllowed) {
-		return yaffs_UnlinkWorker(obj);
-	}
-
-	return YAFFS_FAIL;
-
-}
-int yaffs_Unlink(yaffs_Object * dir, const YCHAR * name)
-{
-	yaffs_Object *obj;
-
-	obj = yaffs_FindObjectByName(dir, name);
-	return yaffs_UnlinkObject(obj);
-}
-
-/*----------------------- Initialisation Scanning ---------------------- */
-
-static void yaffs_HandleShadowedObject(yaffs_Device * dev, int objId,
-				       int backwardScanning)
-{
-	yaffs_Object *obj;
-
-	if (!backwardScanning) {
-		/* Handle YAFFS1 forward scanning case
-		 * For YAFFS1 we always do the deletion
-		 */
-
-	} else {
-		/* Handle YAFFS2 case (backward scanning)
-		 * If the shadowed object exists then ignore.
-		 */
-		if (yaffs_FindObjectByNumber(dev, objId)) {
-			return;
-		}
-	}
-
-	/* Let's create it (if it does not exist) assuming it is a file so that it can do shrinking etc.
-	 * We put it in unlinked dir to be cleaned up after the scanning
-	 */
-	obj =
-	    yaffs_FindOrCreateObjectByNumber(dev, objId,
-					     YAFFS_OBJECT_TYPE_FILE);
-	yaffs_AddObjectToDirectory(dev->unlinkedDir, obj);
-	obj->variant.fileVariant.shrinkSize = 0;
-	obj->valid = 1;		/* So that we don't read any other info for this file */
-
-}
-
-typedef struct {
-	int seq;
-	int block;
-} yaffs_BlockIndex;
-
-static int yaffs_Scan(yaffs_Device * dev)
-{
-	yaffs_ExtendedTags tags;
-	int blk;
-	int blockIterator;
-	int startIterator;
-	int endIterator;
-	int nBlocksToScan = 0;
-
-	int chunk;
-	int c;
-	int deleted;
-	yaffs_BlockState state;
-	yaffs_Object *hardList = NULL;
-	yaffs_Object *hl;
-	yaffs_BlockInfo *bi;
-	int sequenceNumber;
-	yaffs_ObjectHeader *oh;
-	yaffs_Object *in;
-	yaffs_Object *parent;
-	int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
-
-	__u8 *chunkData;
-
-	yaffs_BlockIndex *blockIndex = NULL;
-
-	T(YAFFS_TRACE_SCAN,
-	  (TSTR("yaffs_Scan starts  intstartblk %d intendblk %d..." TENDSTR),
-	   dev->internalStartBlock, dev->internalEndBlock));
-
-	chunkData = yaffs_GetTempBuffer(dev, __LINE__);
-
-	dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER;
-
-	if (dev->isYaffs2) {
-		blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex));
-	}
-
-	/* Scan all the blocks to determine their state */
-	for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) {
-		bi = yaffs_GetBlockInfo(dev, blk);
-		yaffs_ClearChunkBits(dev, blk);
-		bi->pagesInUse = 0;
-		bi->softDeletions = 0;
-
-		yaffs_QueryInitialBlockState(dev, blk, &state, &sequenceNumber);
-
-		bi->blockState = state;
-		bi->sequenceNumber = sequenceNumber;
-
-		T(YAFFS_TRACE_SCAN_DEBUG,
-		  (TSTR("Block scanning block %d state %d seq %d" TENDSTR), blk,
-		   state, sequenceNumber));
-
-		if (state == YAFFS_BLOCK_STATE_DEAD) {
-			T(YAFFS_TRACE_BAD_BLOCKS,
-			  (TSTR("block %d is bad" TENDSTR), blk));
-		} else if (state == YAFFS_BLOCK_STATE_EMPTY) {
-			T(YAFFS_TRACE_SCAN_DEBUG,
-			  (TSTR("Block empty " TENDSTR)));
-			dev->nErasedBlocks++;
-			dev->nFreeChunks += dev->nChunksPerBlock;
-		} else if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) {
-
-			/* Determine the highest sequence number */
-			if (dev->isYaffs2 &&
-			    sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER &&
-			    sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER) {
-
-				blockIndex[nBlocksToScan].seq = sequenceNumber;
-				blockIndex[nBlocksToScan].block = blk;
-
-				nBlocksToScan++;
-
-				if (sequenceNumber >= dev->sequenceNumber) {
-					dev->sequenceNumber = sequenceNumber;
-				}
-			} else if (dev->isYaffs2) {
-				/* TODO: Nasty sequence number! */
-				T(YAFFS_TRACE_SCAN,
-				  (TSTR
-				   ("Block scanning block %d has bad sequence number %d"
-				    TENDSTR), blk, sequenceNumber));
-
-			}
-		}
-	}
-
-	/* Sort the blocks
-	 * Dungy old bubble sort for now...
-	 */
-	if (dev->isYaffs2) {
-		yaffs_BlockIndex temp;
-		int i;
-		int j;
-
-		for (i = 0; i < nBlocksToScan; i++)
-			for (j = i + 1; j < nBlocksToScan; j++)
-				if (blockIndex[i].seq > blockIndex[j].seq) {
-					temp = blockIndex[j];
-					blockIndex[j] = blockIndex[i];
-					blockIndex[i] = temp;
-				}
-	}
-
-	/* Now scan the blocks looking at the data. */
-	if (dev->isYaffs2) {
-		startIterator = 0;
-		endIterator = nBlocksToScan - 1;
-		T(YAFFS_TRACE_SCAN_DEBUG,
-		  (TSTR("%d blocks to be scanned" TENDSTR), nBlocksToScan));
-	} else {
-		startIterator = dev->internalStartBlock;
-		endIterator = dev->internalEndBlock;
-	}
-
-	/* For each block.... */
-	for (blockIterator = startIterator; blockIterator <= endIterator;
-	     blockIterator++) {
-
-		if (dev->isYaffs2) {
-			/* get the block to scan in the correct order */
-			blk = blockIndex[blockIterator].block;
-		} else {
-			blk = blockIterator;
-		}
-
-		bi = yaffs_GetBlockInfo(dev, blk);
-		state = bi->blockState;
-
-		deleted = 0;
-
-		/* For each chunk in each block that needs scanning....*/
-		for (c = 0; c < dev->nChunksPerBlock &&
-		     state == YAFFS_BLOCK_STATE_NEEDS_SCANNING; c++) {
-			/* Read the tags and decide what to do */
-			chunk = blk * dev->nChunksPerBlock + c;
-
-			yaffs_ReadChunkWithTagsFromNAND(dev, chunk, NULL,
-							&tags);
-
-			/* Let's have a good look at this chunk... */
-
-			if (!dev->isYaffs2 && tags.chunkDeleted) {
-				/* YAFFS1 only...
-				 * A deleted chunk
-				 */
-				deleted++;
-				dev->nFreeChunks++;
-				/*T((" %d %d deleted\n",blk,c)); */
-			} else if (!tags.chunkUsed) {
-				/* An unassigned chunk in the block
-				 * This means that either the block is empty or 
-				 * this is the one being allocated from
-				 */
-
-				if (c == 0) {
-					/* We're looking at the first chunk in the block so the block is unused */
-					state = YAFFS_BLOCK_STATE_EMPTY;
-					dev->nErasedBlocks++;
-				} else {
-					/* this is the block being allocated from */
-					T(YAFFS_TRACE_SCAN,
-					  (TSTR
-					   (" Allocating from %d %d" TENDSTR),
-					   blk, c));
-					state = YAFFS_BLOCK_STATE_ALLOCATING;
-					dev->allocationBlock = blk;
-					dev->allocationPage = c;
-					dev->allocationBlockFinder = blk;	
-					/* Set it to here to encourage the allocator to go forth from here. */
-					
-					/* Yaffs2 sanity check:
-					 * This should be the one with the highest sequence number
-					 */
-					if (dev->isYaffs2
-					    && (dev->sequenceNumber !=
-						bi->sequenceNumber)) {
-						T(YAFFS_TRACE_ALWAYS,
-						  (TSTR
-						   ("yaffs: Allocation block %d was not highest sequence id:"
-						    " block seq = %d, dev seq = %d"
-						    TENDSTR), blk,bi->sequenceNumber,dev->sequenceNumber));
-					}
-				}
-
-				dev->nFreeChunks += (dev->nChunksPerBlock - c);
-			} else if (tags.chunkId > 0) {
-				/* chunkId > 0 so it is a data chunk... */
-				unsigned int endpos;
-
-				yaffs_SetChunkBit(dev, blk, c);
-				bi->pagesInUse++;
-
-				in = yaffs_FindOrCreateObjectByNumber(dev,
-								      tags.
-								      objectId,
-								      YAFFS_OBJECT_TYPE_FILE);
-				/* PutChunkIntoFile checks for a clash (two data chunks with
-				 * the same chunkId).
-				 */
-				yaffs_PutChunkIntoFile(in, tags.chunkId, chunk,
-						       1);
-				endpos =
-				    (tags.chunkId - 1) * dev->nBytesPerChunk +
-				    tags.byteCount;
-				if (in->variantType == YAFFS_OBJECT_TYPE_FILE
-				    && in->variant.fileVariant.scannedFileSize <
-				    endpos) {
-					in->variant.fileVariant.
-					    scannedFileSize = endpos;
-					if (!dev->useHeaderFileSize) {
-						in->variant.fileVariant.
-						    fileSize =
-						    in->variant.fileVariant.
-						    scannedFileSize;
-					}
-
-				}
-				/* T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId));   */
-			} else {
-				/* chunkId == 0, so it is an ObjectHeader.
-				 * Thus, we read in the object header and make the object
-				 */
-				yaffs_SetChunkBit(dev, blk, c);
-				bi->pagesInUse++;
-
-				yaffs_ReadChunkWithTagsFromNAND(dev, chunk,
-								chunkData,
-								NULL);
-
-				oh = (yaffs_ObjectHeader *) chunkData;
-
-				in = yaffs_FindObjectByNumber(dev,
-							      tags.objectId);
-				if (in && in->variantType != oh->type) {
-					/* This should not happen, but somehow
-					 * Wev'e ended up with an objectId that has been reused but not yet 
-					 * deleted, and worse still it has changed type. Delete the old object.
-					 */
-
-					yaffs_DestroyObject(in);
-
-					in = 0;
-				}
-
-				in = yaffs_FindOrCreateObjectByNumber(dev,
-								      tags.
-								      objectId,
-								      oh->type);
-
-				if (oh->shadowsObject > 0) {
-					yaffs_HandleShadowedObject(dev,
-								   oh->
-								   shadowsObject,
-								   0);
-				}
-
-				if (in->valid) {
-					/* We have already filled this one. We have a duplicate and need to resolve it. */
-
-					unsigned existingSerial = in->serial;
-					unsigned newSerial = tags.serialNumber;
-
-					if (dev->isYaffs2 ||
-					    ((existingSerial + 1) & 3) ==
-					    newSerial) {
-						/* Use new one - destroy the exisiting one */
-						yaffs_DeleteChunk(dev,
-								  in->chunkId,
-								  1, __LINE__);
-						in->valid = 0;
-					} else {
-						/* Use existing - destroy this one. */
-						yaffs_DeleteChunk(dev, chunk, 1,
-								  __LINE__);
-					}
-				}
-
-				if (!in->valid &&
-				    (tags.objectId == YAFFS_OBJECTID_ROOT ||
-				     tags.objectId == YAFFS_OBJECTID_LOSTNFOUND)) {
-					/* We only load some info, don't fiddle with directory structure */
-					in->valid = 1;
-					in->variantType = oh->type;
-
-					in->yst_mode = oh->yst_mode;
-#ifdef CONFIG_YAFFS_WINCE
-					in->win_atime[0] = oh->win_atime[0];
-					in->win_ctime[0] = oh->win_ctime[0];
-					in->win_mtime[0] = oh->win_mtime[0];
-					in->win_atime[1] = oh->win_atime[1];
-					in->win_ctime[1] = oh->win_ctime[1];
-					in->win_mtime[1] = oh->win_mtime[1];
-#else
-					in->yst_uid = oh->yst_uid;
-					in->yst_gid = oh->yst_gid;
-					in->yst_atime = oh->yst_atime;
-					in->yst_mtime = oh->yst_mtime;
-					in->yst_ctime = oh->yst_ctime;
-					in->yst_rdev = oh->yst_rdev;
-#endif
-					in->chunkId = chunk;
-
-				} else if (!in->valid) {
-					/* we need to load this info */
-
-					in->valid = 1;
-					in->variantType = oh->type;
-
-					in->yst_mode = oh->yst_mode;
-#ifdef CONFIG_YAFFS_WINCE
-					in->win_atime[0] = oh->win_atime[0];
-					in->win_ctime[0] = oh->win_ctime[0];
-					in->win_mtime[0] = oh->win_mtime[0];
-					in->win_atime[1] = oh->win_atime[1];
-					in->win_ctime[1] = oh->win_ctime[1];
-					in->win_mtime[1] = oh->win_mtime[1];
-#else
-					in->yst_uid = oh->yst_uid;
-					in->yst_gid = oh->yst_gid;
-					in->yst_atime = oh->yst_atime;
-					in->yst_mtime = oh->yst_mtime;
-					in->yst_ctime = oh->yst_ctime;
-					in->yst_rdev = oh->yst_rdev;
-#endif
-					in->chunkId = chunk;
-
-					yaffs_SetObjectName(in, oh->name);
-					in->dirty = 0;
-
-					/* directory stuff...
-					 * hook up to parent
-					 */
-
-					parent =
-					    yaffs_FindOrCreateObjectByNumber
-					    (dev, oh->parentObjectId,
-					     YAFFS_OBJECT_TYPE_DIRECTORY);
-					if (parent->variantType ==
-					    YAFFS_OBJECT_TYPE_UNKNOWN) {
-						/* Set up as a directory */
-						parent->variantType =
-						    YAFFS_OBJECT_TYPE_DIRECTORY;
-						INIT_LIST_HEAD(&parent->variant.
-							       directoryVariant.
-							       children);
-					} else if (parent->variantType !=
-						   YAFFS_OBJECT_TYPE_DIRECTORY)
-					{
-						/* Hoosterman, another problem....
-						 * We're trying to use a non-directory as a directory
-						 */
-
-						T(YAFFS_TRACE_ERROR,
-						  (TSTR
-						   ("yaffs tragedy: attempting to use non-directory as"
-						    " a directory in scan. Put in lost+found."
-						    TENDSTR)));
-						parent = dev->lostNFoundDir;
-					}
-
-					yaffs_AddObjectToDirectory(parent, in);
-
-					if (0 && (parent == dev->deletedDir ||
-						  parent == dev->unlinkedDir)) {
-						in->deleted = 1;	/* If it is unlinked at start up then it wants deleting */
-						dev->nDeletedFiles++;
-					}
-					/* Note re hardlinks.
-					 * Since we might scan a hardlink before its equivalent object is scanned
-					 * we put them all in a list.
-					 * After scanning is complete, we should have all the objects, so we run through this
-					 * list and fix up all the chains.              
-					 */
-
-					switch (in->variantType) {
-					case YAFFS_OBJECT_TYPE_UNKNOWN:	
-						/* Todo got a problem */
-						break;
-					case YAFFS_OBJECT_TYPE_FILE:
-						if (dev->isYaffs2
-						    && oh->isShrink) {
-							/* Prune back the shrunken chunks */
-							yaffs_PruneResizedChunks
-							    (in, oh->fileSize);
-							/* Mark the block as having a shrinkHeader */
-							bi->hasShrinkHeader = 1;
-						}
-
-						if (dev->useHeaderFileSize)
-
-							in->variant.fileVariant.
-							    fileSize =
-							    oh->fileSize;
-
-						break;
-					case YAFFS_OBJECT_TYPE_HARDLINK:
-						in->variant.hardLinkVariant.
-						    equivalentObjectId =
-						    oh->equivalentObjectId;
-						in->hardLinks.next =
-						    (struct list_head *)
-						    hardList;
-						hardList = in;
-						break;
-					case YAFFS_OBJECT_TYPE_DIRECTORY:
-						/* Do nothing */
-						break;
-					case YAFFS_OBJECT_TYPE_SPECIAL:
-						/* Do nothing */
-						break;
-					case YAFFS_OBJECT_TYPE_SYMLINK:	
-						in->variant.symLinkVariant.
-						    alias =
-						    yaffs_CloneString(oh->alias);
-						break;
-					}
-
-					if (parent == dev->deletedDir) {
-						yaffs_DestroyObject(in);
-						bi->hasShrinkHeader = 1;
-					}
-				}
-			}
-		}
-
-		if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) {
-			/* If we got this far while scanning, then the block is fully allocated.*/
-			state = YAFFS_BLOCK_STATE_FULL;
-		}
-
-		bi->blockState = state;
-
-		/* Now let's see if it was dirty */
-		if (bi->pagesInUse == 0 &&
-		    !bi->hasShrinkHeader &&
-		    bi->blockState == YAFFS_BLOCK_STATE_FULL) {
-			yaffs_BlockBecameDirty(dev, blk);
-		}
-
-	}
-
-	if (blockIndex) {
-		YFREE(blockIndex);
-	}
-	
-	/* Ok, we've done all the scanning.
-	 * Fix up the hard link chains.
-	 * We should now have scanned all the objects, now it's time to add these 
-	 * hardlinks.
-	 */
-	while (hardList) {
-		hl = hardList;
-		hardList = (yaffs_Object *) (hardList->hardLinks.next);
-
-		in = yaffs_FindObjectByNumber(dev,
-					      hl->variant.hardLinkVariant.
-					      equivalentObjectId);
-
-		if (in) {
-			/* Add the hardlink pointers */
-			hl->variant.hardLinkVariant.equivalentObject = in;
-			list_add(&hl->hardLinks, &in->hardLinks);
-		} else {
-			/* Todo Need to report/handle this better.
-			 * Got a problem... hardlink to a non-existant object
-			 */
-			hl->variant.hardLinkVariant.equivalentObject = NULL;
-			INIT_LIST_HEAD(&hl->hardLinks);
-
-		}
-
-	}
-
-	/* Handle the unlinked files. Since they were left in an unlinked state we should
-	 * just delete them.
-	 */
-	{
-		struct list_head *i;
-		struct list_head *n;
-
-		yaffs_Object *l;
-		/* Soft delete all the unlinked files */
-		list_for_each_safe(i, n,
-				   &dev->unlinkedDir->variant.directoryVariant.
-				   children) {
-			if (i) {
-				l = list_entry(i, yaffs_Object, siblings);
-				yaffs_DestroyObject(l);
-			}
-		}
-	}
-
-	yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__);
-
-	T(YAFFS_TRACE_SCAN, (TSTR("yaffs_Scan ends" TENDSTR)));
-
-	return YAFFS_OK;
-}
-
-static int yaffs_ScanBackwards(yaffs_Device * dev)
-{
-	yaffs_ExtendedTags tags;
-	int blk;
-	int blockIterator;
-	int startIterator;
-	int endIterator;
-	int nBlocksToScan = 0;
-
-	int chunk;
-	int c;
-	int deleted;
-	yaffs_BlockState state;
-	yaffs_Object *hardList = NULL;
-	yaffs_Object *hl;
-	yaffs_BlockInfo *bi;
-	int sequenceNumber;
-	yaffs_ObjectHeader *oh;
-	yaffs_Object *in;
-	yaffs_Object *parent;
-	int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
-	int itsUnlinked;
-	__u8 *chunkData;
-
-	yaffs_BlockIndex *blockIndex = NULL;
-
-	if (!dev->isYaffs2) {
-		T(YAFFS_TRACE_SCAN,
-		  (TSTR("yaffs_ScanBackwards is only for YAFFS2!" TENDSTR)));
-		return YAFFS_FAIL;
-	}
-
-	T(YAFFS_TRACE_SCAN,
-	  (TSTR
-	   ("yaffs_ScanBackwards starts  intstartblk %d intendblk %d..."
-	    TENDSTR), dev->internalStartBlock, dev->internalEndBlock));
-
-	chunkData = yaffs_GetTempBuffer(dev, __LINE__);
-
-	dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER;
-
-	blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex));
-
-	/* Scan all the blocks to determine their state */
-	for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) {
-		bi = yaffs_GetBlockInfo(dev, blk);
-		yaffs_ClearChunkBits(dev, blk);
-		bi->pagesInUse = 0;
-		bi->softDeletions = 0;
-
-		yaffs_QueryInitialBlockState(dev, blk, &state, &sequenceNumber);
-
-		bi->blockState = state;
-		bi->sequenceNumber = sequenceNumber;
-
-		T(YAFFS_TRACE_SCAN_DEBUG,
-		  (TSTR("Block scanning block %d state %d seq %d" TENDSTR), blk,
-		   state, sequenceNumber));
-
-		if (state == YAFFS_BLOCK_STATE_DEAD) {
-			T(YAFFS_TRACE_BAD_BLOCKS,
-			  (TSTR("block %d is bad" TENDSTR), blk));
-		} else if (state == YAFFS_BLOCK_STATE_EMPTY) {
-			T(YAFFS_TRACE_SCAN_DEBUG,
-			  (TSTR("Block empty " TENDSTR)));
-			dev->nErasedBlocks++;
-			dev->nFreeChunks += dev->nChunksPerBlock;
-		} else if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) {
-
-			/* Determine the highest sequence number */
-			if (dev->isYaffs2 &&
-			    sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER &&
-			    sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER) {
-
-				blockIndex[nBlocksToScan].seq = sequenceNumber;
-				blockIndex[nBlocksToScan].block = blk;
-
-				nBlocksToScan++;
-
-				if (sequenceNumber >= dev->sequenceNumber) {
-					dev->sequenceNumber = sequenceNumber;
-				}
-			} else if (dev->isYaffs2) {
-				/* TODO: Nasty sequence number! */
-				T(YAFFS_TRACE_SCAN,
-				  (TSTR
-				   ("Block scanning block %d has bad sequence number %d"
-				    TENDSTR), blk, sequenceNumber));
-
-			}
-		}
-	}
-
-	/*
-	 * Sort the blocks
-	 * Dungy old bubble sort for now...
-	 */
-	{
-		yaffs_BlockIndex temp;
-		int i;
-		int j;
-
-		for (i = 0; i < nBlocksToScan; i++)
-			for (j = i + 1; j < nBlocksToScan; j++)
-				if (blockIndex[i].seq > blockIndex[j].seq) {
-					temp = blockIndex[j];
-					blockIndex[j] = blockIndex[i];
-					blockIndex[i] = temp;
-				}
-	}
-
-	/* Now scan the blocks looking at the data. */
-	startIterator = 0;
-	endIterator = nBlocksToScan - 1;
-	T(YAFFS_TRACE_SCAN_DEBUG,
-	  (TSTR("%d blocks to be scanned" TENDSTR), nBlocksToScan));
-
-	/* For each block.... backwards */
-	for (blockIterator = endIterator; blockIterator >= startIterator;
-	     blockIterator--) {
-
-		/* get the block to scan in the correct order */
-		blk = blockIndex[blockIterator].block;
-
-		bi = yaffs_GetBlockInfo(dev, blk);
-		state = bi->blockState;
-
-		deleted = 0;
-
-		/* For each chunk in each block that needs scanning.... */
-		for (c = dev->nChunksPerBlock - 1; c >= 0 &&
-		     (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING ||
-		      state == YAFFS_BLOCK_STATE_ALLOCATING); c--) {
-			/* Scan backwards... 
-			 * Read the tags and decide what to do
-			 */
-			chunk = blk * dev->nChunksPerBlock + c;
-
-			yaffs_ReadChunkWithTagsFromNAND(dev, chunk, NULL,
-							&tags);
-
-			/* Let's have a good look at this chunk... */
-
-			if (!tags.chunkUsed) {
-				// An unassigned chunk in the block
-				// This means that either the block is empty or 
-				// this is the one being allocated from
-
-				if (c == 0) {
-					/* We're looking at the first chunk in the block so the block is unused */
-					state = YAFFS_BLOCK_STATE_EMPTY;
-					dev->nErasedBlocks++;
-				} else {
-					/* this is the block being allocated from */
-					if (state ==
-					    YAFFS_BLOCK_STATE_NEEDS_SCANNING) {
-						T(YAFFS_TRACE_SCAN,
-						  (TSTR
-						   (" Allocating from %d %d"
-						    TENDSTR), blk, c));
-					}
-					state = YAFFS_BLOCK_STATE_ALLOCATING;
-					dev->allocationBlock = blk;
-					dev->allocationPage = c;
-					dev->allocationBlockFinder = blk;	
-					/* Set it to here to encourage the allocator to 
-					 *  go forth from here.
-					 */
-					 
-					/* Yaffs2 sanity check:
-					 * This should be the one with the highest sequence number
-					 */
-					if (dev->isYaffs2
-					    && (dev->sequenceNumber !=
-						bi->sequenceNumber)) {
-						T(YAFFS_TRACE_ALWAYS,
-						  (TSTR
-						   ("yaffs: Allocation block %d was not highest sequence "
-						    "id: block seq = %d, dev seq = %d"
-						    TENDSTR), blk,
-						   bi->sequenceNumber,
-						   dev->sequenceNumber));
-					}
-				}
-
-				dev->nFreeChunks++;
-			} else if (tags.chunkId > 0) {
-				/* chunkId > 0 so it is a data chunk... */
-				unsigned int endpos;
-
-				__u32 chunkBase =
-				    (tags.chunkId - 1) * dev->nBytesPerChunk;
-
-				yaffs_SetChunkBit(dev, blk, c);
-				bi->pagesInUse++;
-
-				in = yaffs_FindOrCreateObjectByNumber(dev,
-								      tags.
-								      objectId,
-								      YAFFS_OBJECT_TYPE_FILE);
-				//				in->chunkId = tags.chunkId; // debug
-				{
-				  char name[YAFFS_MAX_NAME_LENGTH+1];
-				  yaffs_GetObjectName(in, name, YAFFS_MAX_NAME_LENGTH + 1);
-				} 
-
-				if (in->variantType == YAFFS_OBJECT_TYPE_FILE
-				    && chunkBase <
-				    in->variant.fileVariant.shrinkSize) {
-					/* This has not been invalidated by a resize */
-					yaffs_PutChunkIntoFile(in, tags.chunkId,
-							       chunk, -1);
-
-					/* File size is calculated by looking at the data chunks if we have not 
-					 * seen an object header yet. Stop this practice once we find an object header.
-					 */
-					endpos =
-					    (tags.chunkId -
-					     1) * dev->nBytesPerChunk +
-					    tags.byteCount;
-					    
-					if (!in->valid &&	/* have not got an object header yet */
-					    in->variant.fileVariant.
-					    scannedFileSize < endpos) {
-						in->variant.fileVariant.
-						    scannedFileSize = endpos;
-						in->variant.fileVariant.
-						    fileSize =
-						    in->variant.fileVariant.
-						    scannedFileSize;
-					}
-
-				} else {
-					/* This chunk has been invalidated by a resize, so delete */
-					yaffs_DeleteChunk(dev, chunk, 1, __LINE__);
-
-				}
-				{
-				  char name[YAFFS_MAX_NAME_LENGTH+1];
-				  yaffs_GetObjectName(in, name, YAFFS_MAX_NAME_LENGTH + 1);
-				} 
-			} else {
-				/* chunkId == 0, so it is an ObjectHeader.
-				 * Thus, we read in the object header and make the object
-				 */
-				yaffs_SetChunkBit(dev, blk, c);
-				bi->pagesInUse++;
-
-				oh = NULL;
-				in = NULL;
-
-				if (tags.extraHeaderInfoAvailable) {
-					in = yaffs_FindOrCreateObjectByNumber
-					    (dev, tags.objectId,
-					     tags.extraObjectType);
-				}
-
-				if (!in || !in->valid) {
-
-					/* If we don't have  valid info then we need to read the chunk
-					 * TODO In future we can probably defer reading the chunk and 
-					 * living with invalid data until needed.
-					 */
-
-					yaffs_ReadChunkWithTagsFromNAND(dev,
-									chunk,
-									chunkData,
-									NULL);
-
-					oh = (yaffs_ObjectHeader *) chunkData;
-
-					if (!in)
-						in = yaffs_FindOrCreateObjectByNumber(dev, tags.objectId, oh->type);
-
-				}
-
-				if (!in) {
-					/* TODO Hoosterman we have a problem! */
-					T(YAFFS_TRACE_ERROR,
-					  (TSTR
-					   ("yaffs tragedy: Could not make object for object  %d  "
-					    "at chunk %d during scan"
-					    TENDSTR), tags.objectId, chunk));
-
-				}
-
-				if (in->valid) {
-					/* We have already filled this one.
-					 * We have a duplicate that will be discarded, but 
-					 * we first have to suck out resize info if it is a file.
-					 */
-
-					if ((in->variantType ==
-					     YAFFS_OBJECT_TYPE_FILE) && ((oh
-									  &&
-									  oh->
-									  type
-									  ==
-									  YAFFS_OBJECT_TYPE_FILE)
-									 ||
-									 (tags.
-									  extraHeaderInfoAvailable
-									  &&
-									  tags.
-									  extraObjectType
-									  ==
-									  YAFFS_OBJECT_TYPE_FILE))
-					    ) {
-						__u32 thisSize =
-						    (oh) ? oh->fileSize : tags.
-						    extraFileLength;
-						__u32 parentObjectId =
-						    (oh) ? oh->
-						    parentObjectId : tags.
-						    extraParentObjectId;
-						unsigned isShrink =
-						    (oh) ? oh->isShrink : tags.
-						    extraIsShrinkHeader;
-
-						/* If it is deleted (unlinked at start also means deleted)
-						 * we treat the file size as being zeroed at this point.
-						 */
-						if (parentObjectId ==
-						    YAFFS_OBJECTID_DELETED
-						    || parentObjectId ==
-						    YAFFS_OBJECTID_UNLINKED) {
-							thisSize = 0;
-							isShrink = 1;
-						}
-
-						if (isShrink &&
-						    in->variant.fileVariant.
-						    shrinkSize > thisSize) {
-							in->variant.fileVariant.
-							    shrinkSize =
-							    thisSize;
-						}
-
-						if (isShrink) {
-							bi->hasShrinkHeader = 1;
-						}
-
-					}
-					/* Use existing - destroy this one. */
-					yaffs_DeleteChunk(dev, chunk, 1, __LINE__);
-
-				}
-
-				if (!in->valid &&
-				    (tags.objectId == YAFFS_OBJECTID_ROOT ||
-				     tags.objectId ==
-				     YAFFS_OBJECTID_LOSTNFOUND)) {
-					/* We only load some info, don't fiddle with directory structure */
-					in->valid = 1;
-					in->variantType = oh->type;
-
-					in->yst_mode = oh->yst_mode;
-#ifdef CONFIG_YAFFS_WINCE
-					in->win_atime[0] = oh->win_atime[0];
-					in->win_ctime[0] = oh->win_ctime[0];
-					in->win_mtime[0] = oh->win_mtime[0];
-					in->win_atime[1] = oh->win_atime[1];
-					in->win_ctime[1] = oh->win_ctime[1];
-					in->win_mtime[1] = oh->win_mtime[1];
-#else
-					in->yst_uid = oh->yst_uid;
-					in->yst_gid = oh->yst_gid;
-					in->yst_atime = oh->yst_atime;
-					in->yst_mtime = oh->yst_mtime;
-					in->yst_ctime = oh->yst_ctime;
-					in->yst_rdev = oh->yst_rdev;
-#endif
-					in->chunkId = chunk;
-
-				} else if (!in->valid) {
-					/* we need to load this info */
-
-					in->valid = 1;
-					in->variantType = oh->type;
-
-					in->yst_mode = oh->yst_mode;
-#ifdef CONFIG_YAFFS_WINCE
-					in->win_atime[0] = oh->win_atime[0];
-					in->win_ctime[0] = oh->win_ctime[0];
-					in->win_mtime[0] = oh->win_mtime[0];
-					in->win_atime[1] = oh->win_atime[1];
-					in->win_ctime[1] = oh->win_ctime[1];
-					in->win_mtime[1] = oh->win_mtime[1];
-#else
-					in->yst_uid = oh->yst_uid;
-					in->yst_gid = oh->yst_gid;
-					in->yst_atime = oh->yst_atime;
-					in->yst_mtime = oh->yst_mtime;
-					in->yst_ctime = oh->yst_ctime;
-					in->yst_rdev = oh->yst_rdev;
-#endif
-					in->chunkId = chunk;
-
-					if (oh->shadowsObject > 0) {
-						yaffs_HandleShadowedObject(dev,
-									   oh->
-									   shadowsObject,
-									   1);
-					}
-
-					yaffs_SetObjectName(in, oh->name);
-					in->dirty = 0;
-
-					/* directory stuff...
-					 * hook up to parent
-					 */
-
-					parent =
-					    yaffs_FindOrCreateObjectByNumber
-					    (dev, oh->parentObjectId,
-					     YAFFS_OBJECT_TYPE_DIRECTORY);
-					if (parent->variantType ==
-					    YAFFS_OBJECT_TYPE_UNKNOWN) {
-						/* Set up as a directory */
-						parent->variantType =
-						    YAFFS_OBJECT_TYPE_DIRECTORY;
-						INIT_LIST_HEAD(&parent->variant.
-							       directoryVariant.
-							       children);
-					} else if (parent->variantType !=
-						   YAFFS_OBJECT_TYPE_DIRECTORY)
-					{
-						/* Hoosterman, another problem....
-						 * We're trying to use a non-directory as a directory
-						 */
-
-						T(YAFFS_TRACE_ERROR,
-						  (TSTR
-						   ("yaffs tragedy: attempting to use non-directory as"
-						    " a directory in scan. Put in lost+found."
-						    TENDSTR)));
-						parent = dev->lostNFoundDir;
-					}
-
-					yaffs_AddObjectToDirectory(parent, in);
-
-					itsUnlinked = (parent == dev->deletedDir) ||
-						      (parent == dev->unlinkedDir);
-
-					if (oh->isShrink) {
-						/* Mark the block as having a shrinkHeader */
-						bi->hasShrinkHeader = 1;
-					}
-
-					/* Note re hardlinks.
-					 * Since we might scan a hardlink before its equivalent object is scanned
-					 * we put them all in a list.
-					 * After scanning is complete, we should have all the objects, so we run
-					 * through this list and fix up all the chains.              
-					 */
-
-					switch (in->variantType) {
-					case YAFFS_OBJECT_TYPE_UNKNOWN:	
-						/* Todo got a problem */
-						break;
-					case YAFFS_OBJECT_TYPE_FILE:
-
-						if (in->variant.fileVariant.
-						    scannedFileSize <
-						    oh->fileSize) {
-							/* This covers the case where the file size is greater
-							 * than where the data is
-							 * This will happen if the file is resized to be larger 
-							 * than its current data extents.
-							 */
-							in->variant.fileVariant.fileSize =
-							    oh->fileSize;
-							in->variant.fileVariant.scannedFileSize =
-							    in->variant.fileVariant.fileSize;
-						}
-
-						if (oh->isShrink &&
-						    in->variant.fileVariant.shrinkSize > 
-						    oh->fileSize) {
-							in->variant.fileVariant.shrinkSize =
-							    oh->fileSize;
-						}
-
-						break;
-					case YAFFS_OBJECT_TYPE_HARDLINK:
-						if(!itsUnlinked) {
-						  in->variant.hardLinkVariant.equivalentObjectId =
-						    oh->equivalentObjectId;
-						  in->hardLinks.next =
-						    (struct list_head *) hardList;
-						  hardList = in;
-						}
-						break;
-					case YAFFS_OBJECT_TYPE_DIRECTORY:
-						/* Do nothing */
-						break;
-					case YAFFS_OBJECT_TYPE_SPECIAL:
-						/* Do nothing */
-						break;
-					case YAFFS_OBJECT_TYPE_SYMLINK:
-						in->variant.symLinkVariant.
-						    alias =
-						    yaffs_CloneString(oh->
-								      alias);
-						break;
-					}
-
-				}
-			}
-		}
-
-		if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) {
-			/* If we got this far while scanning, then the block is fully allocated. */
-			state = YAFFS_BLOCK_STATE_FULL;
-		}
-
-		bi->blockState = state;
-
-		/* Now let's see if it was dirty */
-		if (bi->pagesInUse == 0 &&
-		    !bi->hasShrinkHeader &&
-		    bi->blockState == YAFFS_BLOCK_STATE_FULL) {
-			yaffs_BlockBecameDirty(dev, blk);
-		}
-
-	}
-
-	if (blockIndex) {
-		YFREE(blockIndex);
-	}
-	/* Ok, we've done all the scanning.
-	 * Fix up the hard link chains.
-	 * We should now have scanned all the objects, now it's time to add these 
-	 * hardlinks.
-	 */
-	while (hardList) {
-	  char name[YAFFS_MAX_NAME_LENGTH+1];
-	  yaffs_GetObjectName(hardList, name, YAFFS_MAX_NAME_LENGTH + 1);
-
-		hl = hardList;
-		hardList = (yaffs_Object *) (hardList->hardLinks.next);
-
-		in = yaffs_FindObjectByNumber(dev,
-					      hl->variant.hardLinkVariant.
-					      equivalentObjectId);
-
-		if (in) {
-			/* Add the hardlink pointers */
-			hl->variant.hardLinkVariant.equivalentObject = in;
-			list_add(&hl->hardLinks, &in->hardLinks);
-		} else {
-			/* Todo Need to report/handle this better.
-			 * Got a problem... hardlink to a non-existant object
-			 */
-			hl->variant.hardLinkVariant.equivalentObject = NULL;
-			INIT_LIST_HEAD(&hl->hardLinks);
-
-		}
-
-	}
-
-	{
-		struct list_head *i;
-		struct list_head *n;
-
-		yaffs_Object *l;
-
-		/* Soft delete all the unlinked files */
-		list_for_each_safe(i, n,
-				   &dev->unlinkedDir->variant.directoryVariant.
-				   children) {
-			if (i) {
-				l = list_entry(i, yaffs_Object, siblings);
-				yaffs_DestroyObject(l);
-			}
-		}
-
-		/* Soft delete all the deletedDir files */
-		list_for_each_safe(i, n,
-				   &dev->deletedDir->variant.directoryVariant.
-				   children) {
-			if (i) {
-				l = list_entry(i, yaffs_Object, siblings);
-				yaffs_DestroyObject(l);
-
-			}
-		}
-	}
-
-	yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__);
-
-	T(YAFFS_TRACE_SCAN, (TSTR("yaffs_ScanBackwards ends" TENDSTR)));
-
-	return YAFFS_OK;
-}
-
-/*------------------------------  Directory Functions ----------------------------- */
-
-static void yaffs_RemoveObjectFromDirectory(yaffs_Object * obj)
-{
-	yaffs_Device *dev = obj->myDev;
-	
-	if(dev && dev->removeObjectCallback)
-		dev->removeObjectCallback(obj);
-	   
-	list_del_init(&obj->siblings);
-	obj->parent = NULL;
-}
-
-
-static void yaffs_AddObjectToDirectory(yaffs_Object * directory,
-				       yaffs_Object * obj)
-{
-
-	if (!directory) {
-		T(YAFFS_TRACE_ALWAYS,
-		  (TSTR
-		   ("tragedy: Trying to add an object to a null pointer directory"
-		    TENDSTR)));
-		YBUG();
-	}
-	if (directory->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) {
-		T(YAFFS_TRACE_ALWAYS,
-		  (TSTR
-		   ("tragedy: Trying to add an object to a non-directory"
-		    TENDSTR)));
-		YBUG();
-	}
-
-	if (obj->siblings.prev == NULL) {
-		/* Not initialised */
-		INIT_LIST_HEAD(&obj->siblings);
-
-	} else if (!list_empty(&obj->siblings)) {
-		/* If it is holed up somewhere else, un hook it */
-		yaffs_RemoveObjectFromDirectory(obj);
-	}
-	/* Now add it */
-	list_add(&obj->siblings, &directory->variant.directoryVariant.children);
-	obj->parent = directory;
-
-	if (directory == obj->myDev->unlinkedDir
-	    || directory == obj->myDev->deletedDir) {
-		obj->unlinked = 1;
-		obj->myDev->nUnlinkedFiles++;
-		obj->renameAllowed = 0;
-	}
-}
-
-yaffs_Object *yaffs_FindObjectByName(yaffs_Object * directory,
-				     const YCHAR * name)
-{
-	int sum;
-
-	struct list_head *i;
-	YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1];
-
-	yaffs_Object *l;
-
-	if (!name) {
-		return NULL;
-	}
-
-	if (!directory) {
-		T(YAFFS_TRACE_ALWAYS,
-		  (TSTR
-		   ("tragedy: yaffs_FindObjectByName: null pointer directory"
-		    TENDSTR)));
-		YBUG();
-	}
-	if (directory->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) {
-		T(YAFFS_TRACE_ALWAYS,
-		  (TSTR
-		   ("tragedy: yaffs_FindObjectByName: non-directory" TENDSTR)));
-		YBUG();
-	}
-
-	sum = yaffs_CalcNameSum(name);
-
-	list_for_each(i, &directory->variant.directoryVariant.children) {
-		if (i) {
-			l = list_entry(i, yaffs_Object, siblings);
-
-			/* Special case for lost-n-found */
-			if (l->objectId == YAFFS_OBJECTID_LOSTNFOUND) {
-				if (yaffs_strcmp(name, YAFFS_LOSTNFOUND_NAME) == 0) {
-					return l;
-				}
-			} else if (yaffs_SumCompare(l->sum, sum) || l->chunkId <= 0)	
-			{
-				/* LostnFound cunk called Objxxx
-				 * Do a real check
-				 */
-				yaffs_GetObjectName(l, buffer,
-						    YAFFS_MAX_NAME_LENGTH);
-				if (yaffs_strcmp(name, buffer) == 0) {
-					return l;
-				}
-
-			}
-		}
-	}
-
-	return NULL;
-}
-
-
-#if 0
-int yaffs_ApplyToDirectoryChildren(yaffs_Object * theDir,
-				   int (*fn) (yaffs_Object *))
-{
-	struct list_head *i;
-	yaffs_Object *l;
-
-	if (!theDir) {
-		T(YAFFS_TRACE_ALWAYS,
-		  (TSTR
-		   ("tragedy: yaffs_FindObjectByName: null pointer directory"
-		    TENDSTR)));
-		YBUG();
-	}
-	if (theDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) {
-		T(YAFFS_TRACE_ALWAYS,
-		  (TSTR
-		   ("tragedy: yaffs_FindObjectByName: non-directory" TENDSTR)));
-		YBUG();
-	}
-
-	list_for_each(i, &theDir->variant.directoryVariant.children) {
-		if (i) {
-			l = list_entry(i, yaffs_Object, siblings);
-			if (l && !fn(l)) {
-				return YAFFS_FAIL;
-			}
-		}
-	}
-
-	return YAFFS_OK;
-
-}
-#endif
-
-/* GetEquivalentObject dereferences any hard links to get to the
- * actual object.
- */
-
-yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object * obj)
-{
-	if (obj && obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) {
-		/* We want the object id of the equivalent object, not this one */
-		obj = obj->variant.hardLinkVariant.equivalentObject;
-	}
-	return obj;
-
-}
-
-int yaffs_GetObjectName(yaffs_Object * obj, YCHAR * name, int buffSize)
-{
-	memset(name, 0, buffSize * sizeof(YCHAR));
-
-	if (obj->objectId == YAFFS_OBJECTID_LOSTNFOUND) {
-		yaffs_strncpy(name, YAFFS_LOSTNFOUND_NAME, buffSize - 1);
-	} else if (obj->chunkId <= 0) {
-		YCHAR locName[20];
-		/* make up a name */
-		yaffs_sprintf(locName, _Y("%s%d"), YAFFS_LOSTNFOUND_PREFIX,
-			      obj->objectId);
-		yaffs_strncpy(name, locName, buffSize - 1);
-
-	}
-#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
-	else if (obj->shortName[0]) {
-		yaffs_strcpy(name, obj->shortName);
-	}
-#endif
-	else {
-		__u8 *buffer = yaffs_GetTempBuffer(obj->myDev, __LINE__);
-
-		yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *) buffer;
-
-		memset(buffer, 0, obj->myDev->nBytesPerChunk);
-
-		if (obj->chunkId >= 0) {
-			yaffs_ReadChunkWithTagsFromNAND(obj->myDev,
-							obj->chunkId, buffer,
-							NULL);
-		}
-		yaffs_strncpy(name, oh->name, buffSize - 1);
-
-		yaffs_ReleaseTempBuffer(obj->myDev, buffer, __LINE__);
-	}
-
-	return yaffs_strlen(name);
-}
-
-int yaffs_GetObjectFileLength(yaffs_Object * obj)
-{
-
-	/* Dereference any hard linking */
-	obj = yaffs_GetEquivalentObject(obj);
-
-	if (obj->variantType == YAFFS_OBJECT_TYPE_FILE) {
-		return obj->variant.fileVariant.fileSize;
-	}
-	if (obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) {
-		return yaffs_strlen(obj->variant.symLinkVariant.alias);
-	} else {
-		/* Only a directory should drop through to here */
-		return obj->myDev->nBytesPerChunk;
-	}
-}
-
-int yaffs_GetObjectLinkCount(yaffs_Object * obj)
-{
-	int count = 0;
-	struct list_head *i;
-
-	if (!obj->unlinked) {
-		count++;	/* the object itself */
-	}
-	list_for_each(i, &obj->hardLinks) {
-		count++;	/* add the hard links; */
-	}
-	return count;
-
-}
-
-int yaffs_GetObjectInode(yaffs_Object * obj)
-{
-	obj = yaffs_GetEquivalentObject(obj);
-
-	return obj->objectId;
-}
-
-unsigned yaffs_GetObjectType(yaffs_Object * obj)
-{
-	obj = yaffs_GetEquivalentObject(obj);
-
-	switch (obj->variantType) {
-	case YAFFS_OBJECT_TYPE_FILE:
-		return DT_REG;
-		break;
-	case YAFFS_OBJECT_TYPE_DIRECTORY:
-		return DT_DIR;
-		break;
-	case YAFFS_OBJECT_TYPE_SYMLINK:
-		return DT_LNK;
-		break;
-	case YAFFS_OBJECT_TYPE_HARDLINK:
-		return DT_REG;
-		break;
-	case YAFFS_OBJECT_TYPE_SPECIAL:
-		if (S_ISFIFO(obj->yst_mode))
-			return DT_FIFO;
-		if (S_ISCHR(obj->yst_mode))
-			return DT_CHR;
-		if (S_ISBLK(obj->yst_mode))
-			return DT_BLK;
-		if (S_ISSOCK(obj->yst_mode))
-			return DT_SOCK;
-	default:
-		return DT_REG;
-		break;
-	}
-}
-
-YCHAR *yaffs_GetSymlinkAlias(yaffs_Object * obj)
-{
-	obj = yaffs_GetEquivalentObject(obj);
-	if (obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) {
-		return yaffs_CloneString(obj->variant.symLinkVariant.alias);
-	} else {
-		return yaffs_CloneString(_Y(""));
-	}
-}
-
-#ifndef CONFIG_YAFFS_WINCE
-
-int yaffs_SetAttributes(yaffs_Object * obj, struct iattr *attr)
-{
-	unsigned int valid = attr->ia_valid;
-
-	if (valid & ATTR_MODE)
-		obj->yst_mode = attr->ia_mode;
-	if (valid & ATTR_UID)
-		obj->yst_uid = attr->ia_uid;
-	if (valid & ATTR_GID)
-		obj->yst_gid = attr->ia_gid;
-
-	if (valid & ATTR_ATIME)
-		obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime);
-	if (valid & ATTR_CTIME)
-		obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime);
-	if (valid & ATTR_MTIME)
-		obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime);
-
-	if (valid & ATTR_SIZE)
-		yaffs_ResizeFile(obj, attr->ia_size);
-
-	yaffs_UpdateObjectHeader(obj, NULL, 1, 0, 0);
-
-	return YAFFS_OK;
-
-}
-int yaffs_GetAttributes(yaffs_Object * obj, struct iattr *attr)
-{
-	unsigned int valid = 0;
-
-	attr->ia_mode = obj->yst_mode;
-	valid |= ATTR_MODE;
-	attr->ia_uid = obj->yst_uid;
-	valid |= ATTR_UID;
-	attr->ia_gid = obj->yst_gid;
-	valid |= ATTR_GID;
-
-	Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime;
-	valid |= ATTR_ATIME;
-	Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime;
-	valid |= ATTR_CTIME;
-	Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime;
-	valid |= ATTR_MTIME;
-
-	attr->ia_size = yaffs_GetFileSize(obj);
-	valid |= ATTR_SIZE;
-
-	attr->ia_valid = valid;
-
-	return YAFFS_OK;
-
-}
-
-#endif
-
-#if 0
-int yaffs_DumpObject(yaffs_Object * obj)
-{
-	YCHAR name[257];
-
-	yaffs_GetObjectName(obj, name, 256);
-
-	T(YAFFS_TRACE_ALWAYS,
-	  (TSTR
-	   ("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d"
-	    " chunk %d type %d size %d\n"
-	    TENDSTR), obj->objectId, yaffs_GetObjectInode(obj), name,
-	   obj->dirty, obj->valid, obj->serial, obj->sum, obj->chunkId,
-	   yaffs_GetObjectType(obj), yaffs_GetObjectFileLength(obj)));
-
-	return YAFFS_OK;
-}
-#endif
-
-/*---------------------------- Initialisation code -------------------------------------- */
-
-static int yaffs_CheckDevFunctions(const yaffs_Device * dev)
-{
-
-	/* Common functions, gotta have */
-	if (!dev->eraseBlockInNAND || !dev->initialiseNAND)
-		return 0;
-
-#ifdef CONFIG_YAFFS_YAFFS2
-
-	/* Can use the "with tags" style interface for yaffs1 or yaffs2 */
-	if (dev->writeChunkWithTagsToNAND &&
-	    dev->readChunkWithTagsFromNAND &&
-	    !dev->writeChunkToNAND &&
-	    !dev->readChunkFromNAND &&
-	    dev->markNANDBlockBad && dev->queryNANDBlock)
-		return 1;
-#endif
-
-	/* Can use the "spare" style interface for yaffs1 */
-	if (!dev->isYaffs2 &&
-	    !dev->writeChunkWithTagsToNAND &&
-	    !dev->readChunkWithTagsFromNAND &&
-	    dev->writeChunkToNAND &&
-	    dev->readChunkFromNAND &&
-	    !dev->markNANDBlockBad && !dev->queryNANDBlock)
-		return 1;
-
-	return 0;		/* bad */
-}
-
-int yaffs_GutsInitialise(yaffs_Device * dev)
-{
-	unsigned x;
-	int bits;
-	int extraBits;
-	int nBlocks;
-
-	T(YAFFS_TRACE_TRACING, (TSTR("yaffs: yaffs_GutsInitialise()" TENDSTR)));
-
-	/* Check stuff that must be set */
-
-	if (!dev) {
-		T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: Need a device" TENDSTR)));
-		return YAFFS_FAIL;
-	}
-
-	dev->internalStartBlock = dev->startBlock;
-	dev->internalEndBlock = dev->endBlock;
-	dev->blockOffset = 0;
-	dev->chunkOffset = 0;
-	dev->nFreeChunks = 0;
-
-	if (dev->startBlock == 0) {
-		dev->internalStartBlock = dev->startBlock + 1;
-		dev->internalEndBlock = dev->endBlock + 1;
-		dev->blockOffset = 1;
-		dev->chunkOffset = dev->nChunksPerBlock;
-	}
-
-	/* Check geometry parameters. */
-
-	if ((dev->isYaffs2 && dev->nBytesPerChunk < 1024) || (!dev->isYaffs2 && dev->nBytesPerChunk != 512) || dev->nChunksPerBlock < 2 || dev->nReservedBlocks < 2 || dev->internalStartBlock <= 0 || dev->internalEndBlock <= 0 || dev->internalEndBlock <= (dev->internalStartBlock + dev->nReservedBlocks + 2)	// otherwise it is too small
-	    ) {
-		T(YAFFS_TRACE_ALWAYS,
-		  (TSTR
-		   ("yaffs: NAND geometry problems: chunk size %d, type is yaffs%s "
-		    TENDSTR), dev->nBytesPerChunk, dev->isYaffs2 ? "2" : ""));
-		return YAFFS_FAIL;
-	}
-
-	if (yaffs_InitialiseNAND(dev) != YAFFS_OK) {
-		T(YAFFS_TRACE_ALWAYS,
-		  (TSTR("yaffs: InitialiseNAND failed" TENDSTR)));
-		return YAFFS_FAIL;
-	}
-
-	/* Got the right mix of functions? */
-	if (!yaffs_CheckDevFunctions(dev)) {
-		/* Function missing */
-		T(YAFFS_TRACE_ALWAYS,
-		  (TSTR
-		   ("yaffs: device function(s) missing or wrong\n" TENDSTR)));
-
-		return YAFFS_FAIL;
-	}
-
-	/* This is really a compilation check. */
-	if (!yaffs_CheckStructures()) {
-		T(YAFFS_TRACE_ALWAYS,
-		  (TSTR("yaffs_CheckStructures failed\n" TENDSTR)));
-		return YAFFS_FAIL;
-	}
-
-	if (dev->isMounted) {
-		T(YAFFS_TRACE_ALWAYS,
-		  (TSTR("yaffs: device already mounted\n" TENDSTR)));
-		return YAFFS_FAIL;
-	}
-
-	/* Finished with most checks. One or two more checks happen later on too. */
-
-	dev->isMounted = 1;
-
-	nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
-
-	/* OK now calculate a few things for the device
-	 * Calculate chunkGroupBits.
-	 * We need to find the next power of 2 > than internalEndBlock
-	 */
-
-	x = dev->nChunksPerBlock * (dev->internalEndBlock + 1);
-
-	for (bits = extraBits = 0; x > 1; bits++) {
-		if (x & 1)
-			extraBits++;
-		x >>= 1;
-	}
-
-	if (extraBits > 0)
-		bits++;
-	
-	/* Set up tnode width if wide tnodes are enabled. */
-	if(!dev->wideTnodesDisabled){
-		/* bits must be even so that we end up with 32-bit words */
-		if(bits & 1)
-			bits++;
-		if(bits < 16)
-			dev->tnodeWidth = 16;
-		else
-			dev->tnodeWidth = bits;
-	}
-	else
-		dev->tnodeWidth = 16;
- 
-	dev->tnodeMask = (1<<dev->tnodeWidth)-1;
-		
-	/* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled),
-	 * so if the bitwidth of the
-	 * chunk range we're using is greater than 16 we need
-	 * to figure out chunk shift and chunkGroupSize
-	 */
-		 
-	if (bits <= dev->tnodeWidth)
-		dev->chunkGroupBits = 0;
-	else
-		dev->chunkGroupBits = bits - dev->tnodeWidth;
-		
-
-	dev->chunkGroupSize = 1 << dev->chunkGroupBits;
-
-	if (dev->nChunksPerBlock < dev->chunkGroupSize) {
-		/* We have a problem because the soft delete won't work if
-		 * the chunk group size > chunks per block.
-		 * This can be remedied by using larger "virtual blocks".
-		 */
-		T(YAFFS_TRACE_ALWAYS,
-		  (TSTR("yaffs: chunk group too large\n" TENDSTR)));
-
-		return YAFFS_FAIL;
-	}
-
-	/* OK, we've finished verifying the device, lets continue with initialisation */
-
-	/* More device initialisation */
-	dev->garbageCollections = 0;
-	dev->passiveGarbageCollections = 0;
-	dev->currentDirtyChecker = 0;
-	dev->bufferedBlock = -1;
-	dev->doingBufferedBlockRewrite = 0;
-	dev->nDeletedFiles = 0;
-	dev->nBackgroundDeletions = 0;
-	dev->nUnlinkedFiles = 0;
-	dev->eccFixed = 0;
-	dev->eccUnfixed = 0;
-	dev->tagsEccFixed = 0;
-	dev->tagsEccUnfixed = 0;
-	dev->nErasureFailures = 0;
-	dev->nErasedBlocks = 0;
-	dev->isDoingGC = 0;
-
-	/* Initialise temporary buffers */
-	{
-		int i;
-		for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
-			dev->tempBuffer[i].line = 0;	/* not in use */
-			dev->tempBuffer[i].buffer =
-			    YMALLOC(dev->nBytesPerChunk);
-		}
-	}
-
-	yaffs_InitialiseBlocks(dev, nBlocks);
-
-	yaffs_InitialiseTnodes(dev);
-
-	yaffs_InitialiseObjects(dev);
-
-	dev->gcCleanupList = YMALLOC(dev->nChunksPerBlock * sizeof(__u32));
-
-	if (dev->nShortOpCaches > 0) {
-		int i;
-
-		if (dev->nShortOpCaches > YAFFS_MAX_SHORT_OP_CACHES) {
-			dev->nShortOpCaches = YAFFS_MAX_SHORT_OP_CACHES;
-		}
-
-		dev->srCache =
-		    YMALLOC(dev->nShortOpCaches * sizeof(yaffs_ChunkCache));
-
-		for (i = 0; i < dev->nShortOpCaches; i++) {
-			dev->srCache[i].object = NULL;
-			dev->srCache[i].lastUse = 0;
-			dev->srCache[i].dirty = 0;
-			dev->srCache[i].data = YMALLOC(dev->nBytesPerChunk);
-		}
-		dev->srLastUse = 0;
-	}
-
-	dev->cacheHits = 0;
-
-	/* Initialise the unlinked, root and lost and found directories */
-	dev->lostNFoundDir = dev->rootDir =  NULL;
-	dev->unlinkedDir = dev->deletedDir = NULL;
-
-	dev->unlinkedDir =
-	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR);
-	dev->deletedDir =
-	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_DELETED, S_IFDIR);
-
-	dev->rootDir =
-	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_ROOT,
-				      YAFFS_ROOT_MODE | S_IFDIR);
-	dev->lostNFoundDir =
-	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_LOSTNFOUND,
-				      YAFFS_LOSTNFOUND_MODE | S_IFDIR);
-	yaffs_AddObjectToDirectory(dev->rootDir, dev->lostNFoundDir);
-
-	if (dev->isYaffs2) {
-		dev->useHeaderFileSize = 1;
-	}
-
-	/* Now scan the flash.   */
-	if (dev->isYaffs2)
-		yaffs_ScanBackwards(dev);
-	else
-		yaffs_Scan(dev);
-
-	/* Zero out stats */
-	dev->nPageReads = 0;
-	dev->nPageWrites = 0;
-	dev->nBlockErasures = 0;
-	dev->nGCCopies = 0;
-	dev->nRetriedWrites = 0;
-
-	dev->nRetiredBlocks = 0;
-
-	yaffs_VerifyFreeChunks(dev);
-
-	T(YAFFS_TRACE_TRACING,
-	  (TSTR("yaffs: yaffs_GutsInitialise() done.\n" TENDSTR)));
-	return YAFFS_OK;
-
-}
-
-void yaffs_Deinitialise(yaffs_Device * dev)
-{
-	if (dev->isMounted) {
-		int i;
-
-		yaffs_DeinitialiseBlocks(dev);
-		yaffs_DeinitialiseTnodes(dev);
-		yaffs_DeinitialiseObjects(dev);
-		if (dev->nShortOpCaches > 0) {
-
-			for (i = 0; i < dev->nShortOpCaches; i++) {
-				YFREE(dev->srCache[i].data);
-			}
-
-			YFREE(dev->srCache);
-		}
-
-		YFREE(dev->gcCleanupList);
-
-		for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
-			YFREE(dev->tempBuffer[i].buffer);
-		}
-
-		dev->isMounted = 0;
-	}
-
-}
-
-static int yaffs_CountFreeChunks(yaffs_Device * dev)
-{
-	int nFree;
-	int b;
-
-	yaffs_BlockInfo *blk;
-
-	for (nFree = 0, b = dev->internalStartBlock; b <= dev->internalEndBlock;
-	     b++) {
-		blk = yaffs_GetBlockInfo(dev, b);
-
-		switch (blk->blockState) {
-		case YAFFS_BLOCK_STATE_EMPTY:
-		case YAFFS_BLOCK_STATE_ALLOCATING:
-		case YAFFS_BLOCK_STATE_COLLECTING:
-		case YAFFS_BLOCK_STATE_FULL:
-			nFree +=
-			    (dev->nChunksPerBlock - blk->pagesInUse +
-			     blk->softDeletions);
-			break;
-		default:
-			break;
-		}
-
-	}
-
-	return nFree;
-}
-
-int yaffs_GetNumberOfFreeChunks(yaffs_Device * dev)
-{
-	/* This is what we report to the outside world */
-
-	int nFree;
-	int nDirtyCacheChunks;
-
-#if 1
-	nFree = dev->nFreeChunks;
-#else
-	nFree = yaffs_CountFreeChunks(dev);
-#endif
-	nFree += dev->nDeletedFiles;
-
-	/* Now count the number of dirty chunks in the cache and subtract those */
-
-	{
-		int i;
-		for (nDirtyCacheChunks = 0, i = 0; i < dev->nShortOpCaches; i++) {
-			if (dev->srCache[i].dirty)
-				nDirtyCacheChunks++;
-		}
-	}
-
-	nFree -= nDirtyCacheChunks;
-
-	nFree -= ((dev->nReservedBlocks + 1) * dev->nChunksPerBlock);
-
-	if (nFree < 0)
-		nFree = 0;
-
-	return nFree;
-
-}
-
-static int yaffs_freeVerificationFailures;
-
-static void yaffs_VerifyFreeChunks(yaffs_Device * dev)
-{
-	int counted = yaffs_CountFreeChunks(dev);
-
-	int difference = dev->nFreeChunks - counted;
-
-	if (difference) {
-		T(YAFFS_TRACE_ALWAYS,
-		  (TSTR("Freechunks verification failure %d %d %d" TENDSTR),
-		   dev->nFreeChunks, counted, difference));
-		yaffs_freeVerificationFailures++;
-	}
-}
-
-/*---------------------------------------- YAFFS test code ----------------------*/
-
-#define yaffs_CheckStruct(structure,syze, name) \
-           if(sizeof(structure) != syze) \
-	       { \
-	         T(YAFFS_TRACE_ALWAYS,(TSTR("%s should be %d but is %d\n" TENDSTR),\
-		 name,syze,sizeof(structure))); \
-	         return YAFFS_FAIL; \
-		}
-
-static int yaffs_CheckStructures(void)
-{
-/*      yaffs_CheckStruct(yaffs_Tags,8,"yaffs_Tags") */
-/*      yaffs_CheckStruct(yaffs_TagsUnion,8,"yaffs_TagsUnion") */
-/*      yaffs_CheckStruct(yaffs_Spare,16,"yaffs_Spare") */
-#ifndef CONFIG_YAFFS_TNODE_LIST_DEBUG
-	yaffs_CheckStruct(yaffs_Tnode, 2 * YAFFS_NTNODES_LEVEL0, "yaffs_Tnode")
-#endif
-	    yaffs_CheckStruct(yaffs_ObjectHeader, 512, "yaffs_ObjectHeader")
-
-	    return YAFFS_OK;
-}
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2007 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+const char *yaffs_guts_c_version =
+    "$Id: yaffs_guts.c,v 1.4 2007/04/24 19:30:25 eddie Exp $";
+
+#include "yportenv.h"
+
+#include "yaffsinterface.h"
+#include "yaffs_guts.h"
+#include "yaffs_tagsvalidity.h"
+
+#include "yaffs_tagscompat.h"
+#ifndef CONFIG_YAFFS_OWN_SORT
+#include "yaffs_qsort.h"
+#endif
+#include "yaffs_nand.h"
+
+#include "yaffs_checkptrw.h"
+
+#include "yaffs_nand.h"
+#include "yaffs_packedtags2.h"
+
+
+#ifdef CONFIG_YAFFS_WINCE
+void yfsd_LockYAFFS(BOOL fsLockOnly);
+void yfsd_UnlockYAFFS(BOOL fsLockOnly);
+#endif
+
+#define YAFFS_PASSIVE_GC_CHUNKS 2
+
+#include "yaffs_ecc.h"
+
+
+/* Robustification (if it ever comes about...) */
+static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND);
+static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND, int erasedOk);
+static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND,
+				     const __u8 * data,
+				     const yaffs_ExtendedTags * tags);
+static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND,
+				    const yaffs_ExtendedTags * tags);
+
+/* Other local prototypes */
+static int yaffs_UnlinkObject( yaffs_Object *obj);
+static int yaffs_ObjectHasCachedWriteData(yaffs_Object *obj);
+
+static void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList);
+
+static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device * dev,
+					     const __u8 * buffer,
+					     yaffs_ExtendedTags * tags,
+					     int useReserve);
+static int yaffs_PutChunkIntoFile(yaffs_Object * in, int chunkInInode,
+				  int chunkInNAND, int inScan);
+
+static yaffs_Object *yaffs_CreateNewObject(yaffs_Device * dev, int number,
+					   yaffs_ObjectType type);
+static void yaffs_AddObjectToDirectory(yaffs_Object * directory,
+				       yaffs_Object * obj);
+static int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name,
+				    int force, int isShrink, int shadows);
+static void yaffs_RemoveObjectFromDirectory(yaffs_Object * obj);
+static int yaffs_CheckStructures(void);
+static int yaffs_DeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, __u32 level,
+			      int chunkOffset, int *limit);
+static int yaffs_DoGenericObjectDeletion(yaffs_Object * in);
+
+static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blockNo);
+
+static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo);
+static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer,
+				    int lineNo);
+
+static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,
+				  int chunkInNAND);
+
+static int yaffs_UnlinkWorker(yaffs_Object * obj);
+static void yaffs_DestroyObject(yaffs_Object * obj);
+
+static int yaffs_TagsMatch(const yaffs_ExtendedTags * tags, int objectId,
+			   int chunkInObject);
+
+loff_t yaffs_GetFileSize(yaffs_Object * obj);
+
+static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve, yaffs_BlockInfo **blockUsedPtr);
+
+static void yaffs_VerifyFreeChunks(yaffs_Device * dev);
+
+#ifdef YAFFS_PARANOID
+static int yaffs_CheckFileSanity(yaffs_Object * in);
+#else
+#define yaffs_CheckFileSanity(in)
+#endif
+
+static void yaffs_InvalidateWholeChunkCache(yaffs_Object * in);
+static void yaffs_InvalidateChunkCache(yaffs_Object * object, int chunkId);
+
+static void yaffs_InvalidateCheckpoint(yaffs_Device *dev);
+
+
+
+/* Function to calculate chunk and offset */
+
+static void yaffs_AddrToChunk(yaffs_Device *dev, loff_t addr, __u32 *chunk, __u32 *offset)
+{
+	if(dev->chunkShift){
+		/* Easy-peasy power of 2 case */
+		*chunk  = (__u32)(addr >> dev->chunkShift);
+		*offset = (__u32)(addr & dev->chunkMask);
+	}
+	else if(dev->crumbsPerChunk)
+	{
+		/* Case where we're using "crumbs" */
+		*offset = (__u32)(addr & dev->crumbMask);
+		addr >>= dev->crumbShift;
+		*chunk = ((__u32)addr)/dev->crumbsPerChunk;
+		*offset += ((addr - (*chunk * dev->crumbsPerChunk)) << dev->crumbShift);
+	}
+	else
+		YBUG();
+}
+
+/* Function to return the number of shifts for a power of 2 greater than or equal 
+ * to the given number
+ * Note we don't try to cater for all possible numbers and this does not have to
+ * be hellishly efficient.
+ */
+ 
+static __u32 ShiftsGE(__u32 x)
+{
+	int extraBits;
+	int nShifts;
+	
+	nShifts = extraBits = 0;
+	
+	while(x>1){
+		if(x & 1) extraBits++;
+		x>>=1;
+		nShifts++;
+	}
+
+	if(extraBits) 
+		nShifts++;
+		
+	return nShifts;
+}
+
+/* Function to return the number of shifts to get a 1 in bit 0
+ */
+ 
+static __u32 ShiftDiv(__u32 x)
+{
+	int nShifts;
+	
+	nShifts =  0;
+	
+	if(!x) return 0;
+	
+	while( !(x&1)){
+		x>>=1;
+		nShifts++;
+	}
+		
+	return nShifts;
+}
+
+
+
+/* 
+ * Temporary buffer manipulations.
+ */
+
+static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo)
+{
+	int i, j;
+	for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
+		if (dev->tempBuffer[i].line == 0) {
+			dev->tempBuffer[i].line = lineNo;
+			if ((i + 1) > dev->maxTemp) {
+				dev->maxTemp = i + 1;
+				for (j = 0; j <= i; j++)
+					dev->tempBuffer[j].maxLine =
+					    dev->tempBuffer[j].line;
+			}
+
+			return dev->tempBuffer[i].buffer;
+		}
+	}
+
+	T(YAFFS_TRACE_BUFFERS,
+	  (TSTR("Out of temp buffers at line %d, other held by lines:"),
+	   lineNo));
+	for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
+		T(YAFFS_TRACE_BUFFERS, (TSTR(" %d "), dev->tempBuffer[i].line));
+	}
+	T(YAFFS_TRACE_BUFFERS, (TSTR(" " TENDSTR)));
+
+	/*
+	 * If we got here then we have to allocate an unmanaged one
+	 * This is not good.
+	 */
+
+	dev->unmanagedTempAllocations++;
+	return YMALLOC(dev->nDataBytesPerChunk);
+
+}
+
+static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer,
+				    int lineNo)
+{
+	int i;
+	for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
+		if (dev->tempBuffer[i].buffer == buffer) {
+			dev->tempBuffer[i].line = 0;
+			return;
+		}
+	}
+
+	if (buffer) {
+		/* assume it is an unmanaged one. */
+		T(YAFFS_TRACE_BUFFERS,
+		  (TSTR("Releasing unmanaged temp buffer in line %d" TENDSTR),
+		   lineNo));
+		YFREE(buffer);
+		dev->unmanagedTempDeallocations++;
+	}
+
+}
+
+/*
+ * Determine if we have a managed buffer.
+ */
+int yaffs_IsManagedTempBuffer(yaffs_Device * dev, const __u8 * buffer)
+{
+	int i;
+	for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
+		if (dev->tempBuffer[i].buffer == buffer)
+			return 1;
+
+	}
+
+    for (i = 0; i < dev->nShortOpCaches; i++) {
+        if( dev->srCache[i].data == buffer )
+            return 1;
+
+    }
+
+    if (buffer == dev->checkpointBuffer)
+      return 1;
+
+    T(YAFFS_TRACE_ALWAYS,
+	  (TSTR("yaffs: unmaged buffer detected.\n" TENDSTR)));
+    return 0;
+}
+
+/*
+ * Chunk bitmap manipulations
+ */
+
+static Y_INLINE __u8 *yaffs_BlockBits(yaffs_Device * dev, int blk)
+{
+	if (blk < dev->internalStartBlock || blk > dev->internalEndBlock) {
+		T(YAFFS_TRACE_ERROR,
+		  (TSTR("**>> yaffs: BlockBits block %d is not valid" TENDSTR),
+		   blk));
+		YBUG();
+	}
+	return dev->chunkBits +
+	    (dev->chunkBitmapStride * (blk - dev->internalStartBlock));
+}
+
+static Y_INLINE void yaffs_ClearChunkBits(yaffs_Device * dev, int blk)
+{
+	__u8 *blkBits = yaffs_BlockBits(dev, blk);
+
+	memset(blkBits, 0, dev->chunkBitmapStride);
+}
+
+static Y_INLINE void yaffs_ClearChunkBit(yaffs_Device * dev, int blk, int chunk)
+{
+	__u8 *blkBits = yaffs_BlockBits(dev, blk);
+
+	blkBits[chunk / 8] &= ~(1 << (chunk & 7));
+}
+
+static Y_INLINE void yaffs_SetChunkBit(yaffs_Device * dev, int blk, int chunk)
+{
+	__u8 *blkBits = yaffs_BlockBits(dev, blk);
+
+	blkBits[chunk / 8] |= (1 << (chunk & 7));
+}
+
+static Y_INLINE int yaffs_CheckChunkBit(yaffs_Device * dev, int blk, int chunk)
+{
+	__u8 *blkBits = yaffs_BlockBits(dev, blk);
+	return (blkBits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0;
+}
+
+static Y_INLINE int yaffs_StillSomeChunkBits(yaffs_Device * dev, int blk)
+{
+	__u8 *blkBits = yaffs_BlockBits(dev, blk);
+	int i;
+	for (i = 0; i < dev->chunkBitmapStride; i++) {
+		if (*blkBits)
+			return 1;
+		blkBits++;
+	}
+	return 0;
+}
+
+/*
+ *  Simple hash function. Needs to have a reasonable spread
+ */
+ 
+static Y_INLINE int yaffs_HashFunction(int n)
+{
+	n = abs(n);
+	return (n % YAFFS_NOBJECT_BUCKETS);
+}
+
+/*
+ * Access functions to useful fake objects
+ */
+ 
+yaffs_Object *yaffs_Root(yaffs_Device * dev)
+{
+	return dev->rootDir;
+}
+
+yaffs_Object *yaffs_LostNFound(yaffs_Device * dev)
+{
+	return dev->lostNFoundDir;
+}
+
+
+/*
+ *  Erased NAND checking functions
+ */
+ 
+int yaffs_CheckFF(__u8 * buffer, int nBytes)
+{
+	/* Horrible, slow implementation */
+	while (nBytes--) {
+		if (*buffer != 0xFF)
+			return 0;
+		buffer++;
+	}
+	return 1;
+}
+
+static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,
+				  int chunkInNAND)
+{
+
+	int retval = YAFFS_OK;
+	__u8 *data = yaffs_GetTempBuffer(dev, __LINE__);
+	yaffs_ExtendedTags tags;
+	int result;
+
+	result = yaffs_ReadChunkWithTagsFromNAND(dev, chunkInNAND, data, &tags);
+	
+	if(tags.eccResult > YAFFS_ECC_RESULT_NO_ERROR)
+		retval = YAFFS_FAIL;
+		
+
+	if (!yaffs_CheckFF(data, dev->nDataBytesPerChunk) || tags.chunkUsed) {
+		T(YAFFS_TRACE_NANDACCESS,
+		  (TSTR("Chunk %d not erased" TENDSTR), chunkInNAND));
+		retval = YAFFS_FAIL;
+	}
+
+	yaffs_ReleaseTempBuffer(dev, data, __LINE__);
+
+	return retval;
+
+}
+
+static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev,
+					     const __u8 * data,
+					     yaffs_ExtendedTags * tags,
+					     int useReserve)
+{
+	int chunk;
+
+	int writeOk = 0;
+	int erasedOk = 1;
+	int attempts = 0;
+	yaffs_BlockInfo *bi;
+	
+	yaffs_InvalidateCheckpoint(dev);
+
+	do {
+		chunk = yaffs_AllocateChunk(dev, useReserve,&bi);
+
+		if (chunk >= 0) {
+			/* First check this chunk is erased, if it needs checking.
+			 * The checking policy (unless forced always on) is as follows:
+			 * Check the first page we try to write in a block.
+			 * - If the check passes then we don't need to check any more.
+			 * - If the check fails, we check again...
+			 * If the block has been erased, we don't need to check.
+			 *
+			 * However, if the block has been prioritised for gc, then
+			 * we think there might be something odd about this block
+			 * and stop using it.
+			 *
+			 * Rationale:
+			 * We should only ever see chunks that have not been erased
+			 * if there was a partially written chunk due to power loss
+			 * This checking policy should catch that case with very
+			 * few checks and thus save a lot of checks that are most likely not
+			 * needed.
+			 */
+			 
+			 if(bi->gcPrioritise){
+			 		yaffs_DeleteChunk(dev, chunk, 1, __LINE__);
+			} else {
+#ifdef CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED
+
+				bi->skipErasedCheck = 0;
+
+#endif
+				if(!bi->skipErasedCheck){
+					erasedOk = yaffs_CheckChunkErased(dev, chunk);
+					if(erasedOk && !bi->gcPrioritise)
+						bi->skipErasedCheck = 1;
+				}
+
+				if (!erasedOk) {
+					T(YAFFS_TRACE_ERROR,
+					  (TSTR
+					   ("**>> yaffs chunk %d was not erased"
+					    TENDSTR), chunk));
+				} else {
+					writeOk =
+					    yaffs_WriteChunkWithTagsToNAND(dev, chunk,
+									   data, tags);
+				}
+			
+				attempts++;
+
+				if (writeOk) {
+					/*
+					 *  Copy the data into the robustification buffer.
+					 *  NB We do this at the end to prevent duplicates in the case of a write error.
+					 *  Todo
+					 */
+					yaffs_HandleWriteChunkOk(dev, chunk, data, tags);
+				
+				} else {
+					/* The erased check or write failed */
+					yaffs_HandleWriteChunkError(dev, chunk, erasedOk);
+				}
+			}
+		}
+
+	} while (chunk >= 0 && !writeOk);
+
+	if (attempts > 1) {
+		T(YAFFS_TRACE_ERROR,
+		  (TSTR("**>> yaffs write required %d attempts" TENDSTR),
+		   attempts));
+		dev->nRetriedWrites += (attempts - 1);
+	}
+
+	return chunk;
+}
+
+/*
+ * Block retiring for handling a broken block.
+ */
+ 
+static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND)
+{
+	yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND);
+
+	yaffs_InvalidateCheckpoint(dev);
+	
+	yaffs_MarkBlockBad(dev, blockInNAND);
+
+	bi->blockState = YAFFS_BLOCK_STATE_DEAD;
+	bi->gcPrioritise = 0;
+	bi->needsRetiring = 0;
+
+	dev->nRetiredBlocks++;
+}
+
+/*
+ * Functions for robustisizing TODO
+ *
+ */
+ 
+static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND,
+				     const __u8 * data,
+				     const yaffs_ExtendedTags * tags)
+{
+}
+
+static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND,
+				    const yaffs_ExtendedTags * tags)
+{
+}
+
+void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi)
+{
+	if(!bi->gcPrioritise){
+		bi->gcPrioritise = 1;
+		dev->hasPendingPrioritisedGCs = 1;
+		bi->chunkErrorStrikes ++;
+		
+		if(bi->chunkErrorStrikes > 3){
+			bi->needsRetiring = 1; /* Too many stikes, so retire this */
+			T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: Block struck out" TENDSTR)));
+
+		}
+		
+	}
+}
+
+static void yaffs_ReportOddballBlocks(yaffs_Device *dev)
+{
+	int i;
+		
+	for(i = dev->internalStartBlock; i <= dev->internalEndBlock && (yaffs_traceMask & YAFFS_TRACE_BAD_BLOCKS); i++){
+		yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i);
+		if(bi->needsRetiring || bi->gcPrioritise)
+			T(YAFFS_TRACE_BAD_BLOCKS,(TSTR("yaffs block %d%s%s" TENDSTR),
+				i,
+				bi->needsRetiring ? " needs retiring" : "",
+				bi->gcPrioritise ?  " gc prioritised" : ""));
+		
+	}
+}
+
+static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND, int erasedOk)
+{
+
+	int blockInNAND = chunkInNAND / dev->nChunksPerBlock;
+	yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND);
+
+	yaffs_HandleChunkError(dev,bi);
+		
+	
+	if(erasedOk ) {
+		/* Was an actual write failure, so mark the block for retirement  */
+		bi->needsRetiring = 1;
+		T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
+		  (TSTR("**>> Block %d needs retiring" TENDSTR), blockInNAND));
+
+		
+	}
+	
+	/* Delete the chunk */
+	yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__);
+}
+
+
+/*---------------- Name handling functions ------------*/ 
+
+static __u16 yaffs_CalcNameSum(const YCHAR * name)
+{
+	__u16 sum = 0;
+	__u16 i = 1;
+
+	YUCHAR *bname = (YUCHAR *) name;
+	if (bname) {
+		while ((*bname) && (i <= YAFFS_MAX_NAME_LENGTH)) {
+
+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
+			sum += yaffs_toupper(*bname) * i;
+#else
+			sum += (*bname) * i;
+#endif
+			i++;
+			bname++;
+		}
+	}
+	return sum;
+}
+
+static void yaffs_SetObjectName(yaffs_Object * obj, const YCHAR * name)
+{
+#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
+	if (name && yaffs_strlen(name) <= YAFFS_SHORT_NAME_LENGTH) {
+		yaffs_strcpy(obj->shortName, name);
+	} else {
+		obj->shortName[0] = _Y('\0');
+	}
+#endif
+	obj->sum = yaffs_CalcNameSum(name);
+}
+
+/*-------------------- TNODES -------------------
+
+ * List of spare tnodes
+ * The list is hooked together using the first pointer
+ * in the tnode.
+ */
+ 
+/* yaffs_CreateTnodes creates a bunch more tnodes and
+ * adds them to the tnode free list.
+ * Don't use this function directly
+ */
+
+static int yaffs_CreateTnodes(yaffs_Device * dev, int nTnodes)
+{
+	int i;
+	int tnodeSize;
+	yaffs_Tnode *newTnodes;
+	__u8 *mem;
+	yaffs_Tnode *curr;
+	yaffs_Tnode *next;
+	yaffs_TnodeList *tnl;
+
+	if (nTnodes < 1)
+		return YAFFS_OK;
+		
+	/* Calculate the tnode size in bytes for variable width tnode support.
+	 * Must be a multiple of 32-bits  */
+	tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8;
+
+	/* make these things */
+
+	newTnodes = YMALLOC(nTnodes * tnodeSize);
+	mem = (__u8 *)newTnodes;
+
+	if (!newTnodes) {
+		T(YAFFS_TRACE_ERROR,
+		  (TSTR("yaffs: Could not allocate Tnodes" TENDSTR)));
+		return YAFFS_FAIL;
+	}
+
+	/* Hook them into the free list */
+#if 0
+	for (i = 0; i < nTnodes - 1; i++) {
+		newTnodes[i].internal[0] = &newTnodes[i + 1];
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+		newTnodes[i].internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
+#endif
+	}
+
+	newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes;
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+	newTnodes[nTnodes - 1].internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
+#endif
+	dev->freeTnodes = newTnodes;
+#else
+	/* New hookup for wide tnodes */
+	for(i = 0; i < nTnodes -1; i++) {
+		curr = (yaffs_Tnode *) &mem[i * tnodeSize];
+		next = (yaffs_Tnode *) &mem[(i+1) * tnodeSize];
+		curr->internal[0] = next;
+	}
+	
+	curr = (yaffs_Tnode *) &mem[(nTnodes - 1) * tnodeSize];
+	curr->internal[0] = dev->freeTnodes;
+	dev->freeTnodes = (yaffs_Tnode *)mem;
+
+#endif
+
+
+	dev->nFreeTnodes += nTnodes;
+	dev->nTnodesCreated += nTnodes;
+
+	/* Now add this bunch of tnodes to a list for freeing up.
+	 * NB If we can't add this to the management list it isn't fatal
+	 * but it just means we can't free this bunch of tnodes later.
+	 */
+	 
+	tnl = YMALLOC(sizeof(yaffs_TnodeList));
+	if (!tnl) {
+		T(YAFFS_TRACE_ERROR,
+		  (TSTR
+		   ("yaffs: Could not add tnodes to management list" TENDSTR)));
+
+	} else {
+		tnl->tnodes = newTnodes;
+		tnl->next = dev->allocatedTnodeList;
+		dev->allocatedTnodeList = tnl;
+	}
+
+	T(YAFFS_TRACE_ALLOCATE, (TSTR("yaffs: Tnodes added" TENDSTR)));
+
+	return YAFFS_OK;
+}
+
+/* GetTnode gets us a clean tnode. Tries to make allocate more if we run out */
+
+static yaffs_Tnode *yaffs_GetTnodeRaw(yaffs_Device * dev)
+{
+	yaffs_Tnode *tn = NULL;
+
+	/* If there are none left make more */
+	if (!dev->freeTnodes) {
+		yaffs_CreateTnodes(dev, YAFFS_ALLOCATION_NTNODES);
+	}
+
+	if (dev->freeTnodes) {
+		tn = dev->freeTnodes;
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+		if (tn->internal[YAFFS_NTNODES_INTERNAL] != (void *)1) {
+			/* Hoosterman, this thing looks like it isn't in the list */
+			T(YAFFS_TRACE_ALWAYS,
+			  (TSTR("yaffs: Tnode list bug 1" TENDSTR)));
+		}
+#endif
+		dev->freeTnodes = dev->freeTnodes->internal[0];
+		dev->nFreeTnodes--;
+	}
+
+	return tn;
+}
+
+static yaffs_Tnode *yaffs_GetTnode(yaffs_Device * dev)
+{
+	yaffs_Tnode *tn = yaffs_GetTnodeRaw(dev);
+	
+	if(tn)
+		memset(tn, 0, (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8);
+
+	return tn;	
+}
+
+/* FreeTnode frees up a tnode and puts it back on the free list */
+static void yaffs_FreeTnode(yaffs_Device * dev, yaffs_Tnode * tn)
+{
+	if (tn) {
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+		if (tn->internal[YAFFS_NTNODES_INTERNAL] != 0) {
+			/* Hoosterman, this thing looks like it is already in the list */
+			T(YAFFS_TRACE_ALWAYS,
+			  (TSTR("yaffs: Tnode list bug 2" TENDSTR)));
+		}
+		tn->internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
+#endif
+		tn->internal[0] = dev->freeTnodes;
+		dev->freeTnodes = tn;
+		dev->nFreeTnodes++;
+	}
+}
+
+static void yaffs_DeinitialiseTnodes(yaffs_Device * dev)
+{
+	/* Free the list of allocated tnodes */
+	yaffs_TnodeList *tmp;
+
+	while (dev->allocatedTnodeList) {
+		tmp = dev->allocatedTnodeList->next;
+
+		YFREE(dev->allocatedTnodeList->tnodes);
+		YFREE(dev->allocatedTnodeList);
+		dev->allocatedTnodeList = tmp;
+
+	}
+
+	dev->freeTnodes = NULL;
+	dev->nFreeTnodes = 0;
+}
+
+static void yaffs_InitialiseTnodes(yaffs_Device * dev)
+{
+	dev->allocatedTnodeList = NULL;
+	dev->freeTnodes = NULL;
+	dev->nFreeTnodes = 0;
+	dev->nTnodesCreated = 0;
+
+}
+
+
+void yaffs_PutLevel0Tnode(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos, unsigned val)
+{
+  __u32 *map = (__u32 *)tn;
+  __u32 bitInMap;
+  __u32 bitInWord;
+  __u32 wordInMap;
+  __u32 mask;
+  
+  pos &= YAFFS_TNODES_LEVEL0_MASK;
+  val >>= dev->chunkGroupBits;
+  
+  bitInMap = pos * dev->tnodeWidth;
+  wordInMap = bitInMap /32;
+  bitInWord = bitInMap & (32 -1);
+  
+  mask = dev->tnodeMask << bitInWord;
+  
+  map[wordInMap] &= ~mask;
+  map[wordInMap] |= (mask & (val << bitInWord));
+  
+  if(dev->tnodeWidth > (32-bitInWord)) {
+    bitInWord = (32 - bitInWord);
+    wordInMap++;;
+    mask = dev->tnodeMask >> (/*dev->tnodeWidth -*/ bitInWord);
+    map[wordInMap] &= ~mask;
+    map[wordInMap] |= (mask & (val >> bitInWord));
+  }
+}
+
+__u32 yaffs_GetChunkGroupBase(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos)
+{
+  __u32 *map = (__u32 *)tn;
+  __u32 bitInMap;
+  __u32 bitInWord;
+  __u32 wordInMap;
+  __u32 val;
+  
+  pos &= YAFFS_TNODES_LEVEL0_MASK;
+  
+  bitInMap = pos * dev->tnodeWidth;
+  wordInMap = bitInMap /32;
+  bitInWord = bitInMap & (32 -1);
+  
+  val = map[wordInMap] >> bitInWord;
+  
+  if(dev->tnodeWidth > (32-bitInWord)) {
+    bitInWord = (32 - bitInWord);
+    wordInMap++;;
+    val |= (map[wordInMap] << bitInWord);
+  }
+  
+  val &= dev->tnodeMask;
+  val <<= dev->chunkGroupBits;
+  
+  return val;
+}
+
+/* ------------------- End of individual tnode manipulation -----------------*/
+
+/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------
+ * The look up tree is represented by the top tnode and the number of topLevel
+ * in the tree. 0 means only the level 0 tnode is in the tree.
+ */
+
+/* FindLevel0Tnode finds the level 0 tnode, if one exists. */
+static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device * dev,
+					  yaffs_FileStructure * fStruct,
+					  __u32 chunkId)
+{
+
+	yaffs_Tnode *tn = fStruct->top;
+	__u32 i;
+	int requiredTallness;
+	int level = fStruct->topLevel;
+
+	/* Check sane level and chunk Id */
+	if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) {
+		return NULL;
+	}
+
+	if (chunkId > YAFFS_MAX_CHUNK_ID) {
+		return NULL;
+	}
+
+	/* First check we're tall enough (ie enough topLevel) */
+
+	i = chunkId >> YAFFS_TNODES_LEVEL0_BITS;
+	requiredTallness = 0;
+	while (i) {
+		i >>= YAFFS_TNODES_INTERNAL_BITS;
+		requiredTallness++;
+	}
+
+	if (requiredTallness > fStruct->topLevel) {
+		/* Not tall enough, so we can't find it, return NULL. */
+		return NULL;
+	}
+
+	/* Traverse down to level 0 */
+	while (level > 0 && tn) {
+		tn = tn->
+		    internal[(chunkId >>
+			       ( YAFFS_TNODES_LEVEL0_BITS + 
+			         (level - 1) *
+			         YAFFS_TNODES_INTERNAL_BITS)
+			      ) &
+			     YAFFS_TNODES_INTERNAL_MASK];
+		level--;
+
+	}
+
+	return tn;
+}
+
+/* AddOrFindLevel0Tnode finds the level 0 tnode if it exists, otherwise first expands the tree.
+ * This happens in two steps:
+ *  1. If the tree isn't tall enough, then make it taller.
+ *  2. Scan down the tree towards the level 0 tnode adding tnodes if required.
+ *
+ * Used when modifying the tree.
+ *
+ *  If the tn argument is NULL, then a fresh tnode will be added otherwise the specified tn will
+ *  be plugged into the ttree.
+ */
+ 
+static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device * dev,
+					       yaffs_FileStructure * fStruct,
+					       __u32 chunkId,
+					       yaffs_Tnode *passedTn)
+{
+
+	int requiredTallness;
+	int i;
+	int l;
+	yaffs_Tnode *tn;
+
+	__u32 x;
+
+
+	/* Check sane level and page Id */
+	if (fStruct->topLevel < 0 || fStruct->topLevel > YAFFS_TNODES_MAX_LEVEL) {
+		return NULL;
+	}
+
+	if (chunkId > YAFFS_MAX_CHUNK_ID) {
+		return NULL;
+	}
+
+	/* First check we're tall enough (ie enough topLevel) */
+
+	x = chunkId >> YAFFS_TNODES_LEVEL0_BITS;
+	requiredTallness = 0;
+	while (x) {
+		x >>= YAFFS_TNODES_INTERNAL_BITS;
+		requiredTallness++;
+	}
+
+
+	if (requiredTallness > fStruct->topLevel) {
+		/* Not tall enough,gotta make the tree taller */
+		for (i = fStruct->topLevel; i < requiredTallness; i++) {
+		
+			tn = yaffs_GetTnode(dev);
+
+			if (tn) {
+				tn->internal[0] = fStruct->top;
+				fStruct->top = tn;
+			} else {
+				T(YAFFS_TRACE_ERROR,
+				  (TSTR("yaffs: no more tnodes" TENDSTR)));
+			}
+		}
+
+		fStruct->topLevel = requiredTallness;
+	}
+
+	/* Traverse down to level 0, adding anything we need */
+
+	l = fStruct->topLevel;
+	tn = fStruct->top;
+	
+	if(l > 0) {
+		while (l > 0 && tn) {
+			x = (chunkId >>
+			     ( YAFFS_TNODES_LEVEL0_BITS +
+			      (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) &
+			    YAFFS_TNODES_INTERNAL_MASK;
+
+
+			if((l>1) && !tn->internal[x]){
+				/* Add missing non-level-zero tnode */
+				tn->internal[x] = yaffs_GetTnode(dev);
+
+			} else if(l == 1) {
+				/* Looking from level 1 at level 0 */
+			 	if (passedTn) {
+					/* If we already have one, then release it.*/
+					if(tn->internal[x])
+						yaffs_FreeTnode(dev,tn->internal[x]);
+					tn->internal[x] = passedTn;
+			
+				} else if(!tn->internal[x]) {
+					/* Don't have one, none passed in */
+					tn->internal[x] = yaffs_GetTnode(dev);
+				}
+			}
+		
+			tn = tn->internal[x];
+			l--;
+		}
+	} else {
+		/* top is level 0 */
+		if(passedTn) {
+			memcpy(tn,passedTn,(dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8);
+			yaffs_FreeTnode(dev,passedTn);
+		}
+	}
+
+	return tn;
+}
+
+static int yaffs_FindChunkInGroup(yaffs_Device * dev, int theChunk,
+				  yaffs_ExtendedTags * tags, int objectId,
+				  int chunkInInode)
+{
+	int j;
+
+	for (j = 0; theChunk && j < dev->chunkGroupSize; j++) {
+		if (yaffs_CheckChunkBit
+		    (dev, theChunk / dev->nChunksPerBlock,
+		     theChunk % dev->nChunksPerBlock)) {
+			yaffs_ReadChunkWithTagsFromNAND(dev, theChunk, NULL,
+							tags);
+			if (yaffs_TagsMatch(tags, objectId, chunkInInode)) {
+				/* found it; */
+				return theChunk;
+
+			}
+		}
+		theChunk++;
+	}
+	return -1;
+}
+
+
+/* DeleteWorker scans backwards through the tnode tree and deletes all the
+ * chunks and tnodes in the file
+ * Returns 1 if the tree was deleted. 
+ * Returns 0 if it stopped early due to hitting the limit and the delete is incomplete.
+ */
+
+static int yaffs_DeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, __u32 level,
+			      int chunkOffset, int *limit)
+{
+	int i;
+	int chunkInInode;
+	int theChunk;
+	yaffs_ExtendedTags tags;
+	int foundChunk;
+	yaffs_Device *dev = in->myDev;
+
+	int allDone = 1;
+
+	if (tn) {
+		if (level > 0) {
+
+			for (i = YAFFS_NTNODES_INTERNAL - 1; allDone && i >= 0;
+			     i--) {
+				if (tn->internal[i]) {
+					if (limit && (*limit) < 0) {
+						allDone = 0;
+					} else {
+						allDone =
+						    yaffs_DeleteWorker(in,
+								       tn->
+								       internal
+								       [i],
+								       level -
+								       1,
+								       (chunkOffset
+									<<
+									YAFFS_TNODES_INTERNAL_BITS)
+								       + i,
+								       limit);
+					}
+					if (allDone) {
+						yaffs_FreeTnode(dev,
+								tn->
+								internal[i]);
+						tn->internal[i] = NULL;
+					}
+				}
+
+			}
+			return (allDone) ? 1 : 0;
+		} else if (level == 0) {
+			int hitLimit = 0;
+
+			for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0 && !hitLimit;
+			     i--) {
+			        theChunk = yaffs_GetChunkGroupBase(dev,tn,i);
+				if (theChunk) {
+
+					chunkInInode =
+					    (chunkOffset <<
+					     YAFFS_TNODES_LEVEL0_BITS) + i;
+
+					foundChunk =
+					    yaffs_FindChunkInGroup(dev,
+								   theChunk,
+								   &tags,
+								   in->objectId,
+								   chunkInInode);
+
+					if (foundChunk > 0) {
+						yaffs_DeleteChunk(dev,
+								  foundChunk, 1,
+								  __LINE__);
+						in->nDataChunks--;
+						if (limit) {
+							*limit = *limit - 1;
+							if (*limit <= 0) {
+								hitLimit = 1;
+							}
+						}
+
+					}
+
+					yaffs_PutLevel0Tnode(dev,tn,i,0);
+				}
+
+			}
+			return (i < 0) ? 1 : 0;
+
+		}
+
+	}
+
+	return 1;
+
+}
+
+static void yaffs_SoftDeleteChunk(yaffs_Device * dev, int chunk)
+{
+
+	yaffs_BlockInfo *theBlock;
+
+	T(YAFFS_TRACE_DELETION, (TSTR("soft delete chunk %d" TENDSTR), chunk));
+
+	theBlock = yaffs_GetBlockInfo(dev, chunk / dev->nChunksPerBlock);
+	if (theBlock) {
+		theBlock->softDeletions++;
+		dev->nFreeChunks++;
+	}
+}
+
+/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all the chunks in the file.
+ * All soft deleting does is increment the block's softdelete count and pulls the chunk out
+ * of the tnode.
+ * Thus, essentially this is the same as DeleteWorker except that the chunks are soft deleted.
+ */
+ 
+static int yaffs_SoftDeleteWorker(yaffs_Object * in, yaffs_Tnode * tn,
+				  __u32 level, int chunkOffset)
+{
+	int i;
+	int theChunk;
+	int allDone = 1;
+	yaffs_Device *dev = in->myDev;
+
+	if (tn) {
+		if (level > 0) {
+
+			for (i = YAFFS_NTNODES_INTERNAL - 1; allDone && i >= 0;
+			     i--) {
+				if (tn->internal[i]) {
+					allDone =
+					    yaffs_SoftDeleteWorker(in,
+								   tn->
+								   internal[i],
+								   level - 1,
+								   (chunkOffset
+								    <<
+								    YAFFS_TNODES_INTERNAL_BITS)
+								   + i);
+					if (allDone) {
+						yaffs_FreeTnode(dev,
+								tn->
+								internal[i]);
+						tn->internal[i] = NULL;
+					} else {
+						/* Hoosterman... how could this happen? */
+					}
+				}
+			}
+			return (allDone) ? 1 : 0;
+		} else if (level == 0) {
+
+			for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) {
+				theChunk = yaffs_GetChunkGroupBase(dev,tn,i);
+				if (theChunk) {
+					/* Note this does not find the real chunk, only the chunk group.
+					 * We make an assumption that a chunk group is not larger than 
+					 * a block.
+					 */
+					yaffs_SoftDeleteChunk(dev, theChunk);
+					yaffs_PutLevel0Tnode(dev,tn,i,0);
+				}
+
+			}
+			return 1;
+
+		}
+
+	}
+
+	return 1;
+
+}
+
+static void yaffs_SoftDeleteFile(yaffs_Object * obj)
+{
+	if (obj->deleted &&
+	    obj->variantType == YAFFS_OBJECT_TYPE_FILE && !obj->softDeleted) {
+		if (obj->nDataChunks <= 0) {
+			/* Empty file with no duplicate object headers, just delete it immediately */
+			yaffs_FreeTnode(obj->myDev,
+					obj->variant.fileVariant.top);
+			obj->variant.fileVariant.top = NULL;
+			T(YAFFS_TRACE_TRACING,
+			  (TSTR("yaffs: Deleting empty file %d" TENDSTR),
+			   obj->objectId));
+			yaffs_DoGenericObjectDeletion(obj);
+		} else {
+			yaffs_SoftDeleteWorker(obj,
+					       obj->variant.fileVariant.top,
+					       obj->variant.fileVariant.
+					       topLevel, 0);
+			obj->softDeleted = 1;
+		}
+	}
+}
+
+/* Pruning removes any part of the file structure tree that is beyond the
+ * bounds of the file (ie that does not point to chunks).
+ *
+ * A file should only get pruned when its size is reduced.
+ *
+ * Before pruning, the chunks must be pulled from the tree and the
+ * level 0 tnode entries must be zeroed out.
+ * Could also use this for file deletion, but that's probably better handled
+ * by a special case.
+ */
+
+static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device * dev, yaffs_Tnode * tn,
+				      __u32 level, int del0)
+{
+	int i;
+	int hasData;
+
+	if (tn) {
+		hasData = 0;
+
+		for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) {
+			if (tn->internal[i] && level > 0) {
+				tn->internal[i] =
+				    yaffs_PruneWorker(dev, tn->internal[i],
+						      level - 1,
+						      (i == 0) ? del0 : 1);
+			}
+
+			if (tn->internal[i]) {
+				hasData++;
+			}
+		}
+
+		if (hasData == 0 && del0) {
+			/* Free and return NULL */
+
+			yaffs_FreeTnode(dev, tn);
+			tn = NULL;
+		}
+
+	}
+
+	return tn;
+
+}
+
+static int yaffs_PruneFileStructure(yaffs_Device * dev,
+				    yaffs_FileStructure * fStruct)
+{
+	int i;
+	int hasData;
+	int done = 0;
+	yaffs_Tnode *tn;
+
+	if (fStruct->topLevel > 0) {
+		fStruct->top =
+		    yaffs_PruneWorker(dev, fStruct->top, fStruct->topLevel, 0);
+
+		/* Now we have a tree with all the non-zero branches NULL but the height
+		 * is the same as it was.
+		 * Let's see if we can trim internal tnodes to shorten the tree.
+		 * We can do this if only the 0th element in the tnode is in use 
+		 * (ie all the non-zero are NULL)
+		 */
+
+		while (fStruct->topLevel && !done) {
+			tn = fStruct->top;
+
+			hasData = 0;
+			for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) {
+				if (tn->internal[i]) {
+					hasData++;
+				}
+			}
+
+			if (!hasData) {
+				fStruct->top = tn->internal[0];
+				fStruct->topLevel--;
+				yaffs_FreeTnode(dev, tn);
+			} else {
+				done = 1;
+			}
+		}
+	}
+
+	return YAFFS_OK;
+}
+
+/*-------------------- End of File Structure functions.-------------------*/
+
+/* yaffs_CreateFreeObjects creates a bunch more objects and
+ * adds them to the object free list.
+ */
+static int yaffs_CreateFreeObjects(yaffs_Device * dev, int nObjects)
+{
+	int i;
+	yaffs_Object *newObjects;
+	yaffs_ObjectList *list;
+
+	if (nObjects < 1)
+		return YAFFS_OK;
+
+	/* make these things */
+	newObjects = YMALLOC(nObjects * sizeof(yaffs_Object));
+
+	if (!newObjects) {
+		T(YAFFS_TRACE_ALLOCATE,
+		  (TSTR("yaffs: Could not allocate more objects" TENDSTR)));
+		return YAFFS_FAIL;
+	}
+	
+	/* Hook them into the free list */
+	for (i = 0; i < nObjects - 1; i++) {
+		newObjects[i].siblings.next =
+		    (struct list_head *)(&newObjects[i + 1]);
+	}
+
+	newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects;
+	dev->freeObjects = newObjects;
+	dev->nFreeObjects += nObjects;
+	dev->nObjectsCreated += nObjects;
+
+	/* Now add this bunch of Objects to a list for freeing up. */
+
+	list = YMALLOC(sizeof(yaffs_ObjectList));
+	if (!list) {
+		T(YAFFS_TRACE_ALLOCATE,
+		  (TSTR("Could not add objects to management list" TENDSTR)));
+	} else {
+		list->objects = newObjects;
+		list->next = dev->allocatedObjectList;
+		dev->allocatedObjectList = list;
+	}
+
+	return YAFFS_OK;
+}
+
+
+/* AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out */
+static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device * dev)
+{
+	yaffs_Object *tn = NULL;
+
+	/* If there are none left make more */
+	if (!dev->freeObjects) {
+		yaffs_CreateFreeObjects(dev, YAFFS_ALLOCATION_NOBJECTS);
+	}
+
+	if (dev->freeObjects) {
+		tn = dev->freeObjects;
+		dev->freeObjects =
+		    (yaffs_Object *) (dev->freeObjects->siblings.next);
+		dev->nFreeObjects--;
+
+		/* Now sweeten it up... */
+
+		memset(tn, 0, sizeof(yaffs_Object));
+		tn->myDev = dev;
+		tn->chunkId = -1;
+		tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN;
+		INIT_LIST_HEAD(&(tn->hardLinks));
+		INIT_LIST_HEAD(&(tn->hashLink));
+		INIT_LIST_HEAD(&tn->siblings);
+
+		/* Add it to the lost and found directory.
+		 * NB Can't put root or lostNFound in lostNFound so
+		 * check if lostNFound exists first
+		 */
+		if (dev->lostNFoundDir) {
+			yaffs_AddObjectToDirectory(dev->lostNFoundDir, tn);
+		}
+	}
+
+	return tn;
+}
+
+static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device * dev, int number,
+					       __u32 mode)
+{
+
+	yaffs_Object *obj =
+	    yaffs_CreateNewObject(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY);
+	if (obj) {
+		obj->fake = 1;		/* it is fake so it has no NAND presence... */
+		obj->renameAllowed = 0;	/* ... and we're not allowed to rename it... */
+		obj->unlinkAllowed = 0;	/* ... or unlink it */
+		obj->deleted = 0;
+		obj->unlinked = 0;
+		obj->yst_mode = mode;
+		obj->myDev = dev;
+		obj->chunkId = 0;	/* Not a valid chunk. */
+	}
+
+	return obj;
+
+}
+
+static void yaffs_UnhashObject(yaffs_Object * tn)
+{
+	int bucket;
+	yaffs_Device *dev = tn->myDev;
+
+	/* If it is still linked into the bucket list, free from the list */
+	if (!list_empty(&tn->hashLink)) {
+		list_del_init(&tn->hashLink);
+		bucket = yaffs_HashFunction(tn->objectId);
+		dev->objectBucket[bucket].count--;
+	}
+
+}
+
+/*  FreeObject frees up a Object and puts it back on the free list */
+static void yaffs_FreeObject(yaffs_Object * tn)
+{
+
+	yaffs_Device *dev = tn->myDev;
+
+#ifdef  __KERNEL__
+	if (tn->myInode) {
+		/* We're still hooked up to a cached inode.
+		 * Don't delete now, but mark for later deletion
+		 */
+		tn->deferedFree = 1;
+		return;
+	}
+#endif
+
+	yaffs_UnhashObject(tn);
+
+	/* Link into the free list. */
+	tn->siblings.next = (struct list_head *)(dev->freeObjects);
+	dev->freeObjects = tn;
+	dev->nFreeObjects++;
+}
+
+#ifdef __KERNEL__
+
+void yaffs_HandleDeferedFree(yaffs_Object * obj)
+{
+	if (obj->deferedFree) {
+		yaffs_FreeObject(obj);
+	}
+}
+
+#endif
+
+static void yaffs_DeinitialiseObjects(yaffs_Device * dev)
+{
+	/* Free the list of allocated Objects */
+
+	yaffs_ObjectList *tmp;
+
+	while (dev->allocatedObjectList) {
+		tmp = dev->allocatedObjectList->next;
+		YFREE(dev->allocatedObjectList->objects);
+		YFREE(dev->allocatedObjectList);
+
+		dev->allocatedObjectList = tmp;
+	}
+
+	dev->freeObjects = NULL;
+	dev->nFreeObjects = 0;
+}
+
+static void yaffs_InitialiseObjects(yaffs_Device * dev)
+{
+	int i;
+
+	dev->allocatedObjectList = NULL;
+	dev->freeObjects = NULL;
+	dev->nFreeObjects = 0;
+
+	for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) {
+		INIT_LIST_HEAD(&dev->objectBucket[i].list);
+		dev->objectBucket[i].count = 0;
+	}
+
+}
+
+static int yaffs_FindNiceObjectBucket(yaffs_Device * dev)
+{
+	static int x = 0;
+	int i;
+	int l = 999;
+	int lowest = 999999;
+
+	/* First let's see if we can find one that's empty. */
+
+	for (i = 0; i < 10 && lowest > 0; i++) {
+		x++;
+		x %= YAFFS_NOBJECT_BUCKETS;
+		if (dev->objectBucket[x].count < lowest) {
+			lowest = dev->objectBucket[x].count;
+			l = x;
+		}
+
+	}
+
+	/* If we didn't find an empty list, then try
+	 * looking a bit further for a short one
+	 */
+
+	for (i = 0; i < 10 && lowest > 3; i++) {
+		x++;
+		x %= YAFFS_NOBJECT_BUCKETS;
+		if (dev->objectBucket[x].count < lowest) {
+			lowest = dev->objectBucket[x].count;
+			l = x;
+		}
+
+	}
+
+	return l;
+}
+
+static int yaffs_CreateNewObjectNumber(yaffs_Device * dev)
+{
+	int bucket = yaffs_FindNiceObjectBucket(dev);
+
+	/* Now find an object value that has not already been taken
+	 * by scanning the list.
+	 */
+
+	int found = 0;
+	struct list_head *i;
+
+	__u32 n = (__u32) bucket;
+
+	/* yaffs_CheckObjectHashSanity();  */
+
+	while (!found) {
+		found = 1;
+		n += YAFFS_NOBJECT_BUCKETS;
+		if (1 || dev->objectBucket[bucket].count > 0) {
+			list_for_each(i, &dev->objectBucket[bucket].list) {
+				/* If there is already one in the list */
+				if (i
+				    && list_entry(i, yaffs_Object,
+						  hashLink)->objectId == n) {
+					found = 0;
+				}
+			}
+		}
+	}
+
+
+	return n;
+}
+
+static void yaffs_HashObject(yaffs_Object * in)
+{
+	int bucket = yaffs_HashFunction(in->objectId);
+	yaffs_Device *dev = in->myDev;
+
+	list_add(&in->hashLink, &dev->objectBucket[bucket].list);
+	dev->objectBucket[bucket].count++;
+
+}
+
+yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device * dev, __u32 number)
+{
+	int bucket = yaffs_HashFunction(number);
+	struct list_head *i;
+	yaffs_Object *in;
+
+	list_for_each(i, &dev->objectBucket[bucket].list) {
+		/* Look if it is in the list */
+		if (i) {
+			in = list_entry(i, yaffs_Object, hashLink);
+			if (in->objectId == number) {
+#ifdef __KERNEL__
+				/* Don't tell the VFS about this one if it is defered free */
+				if (in->deferedFree)
+					return NULL;
+#endif
+
+				return in;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+yaffs_Object *yaffs_CreateNewObject(yaffs_Device * dev, int number,
+				    yaffs_ObjectType type)
+{
+
+	yaffs_Object *theObject;
+
+	if (number < 0) {
+		number = yaffs_CreateNewObjectNumber(dev);
+	}
+
+	theObject = yaffs_AllocateEmptyObject(dev);
+
+	if (theObject) {
+		theObject->fake = 0;
+		theObject->renameAllowed = 1;
+		theObject->unlinkAllowed = 1;
+		theObject->objectId = number;
+		yaffs_HashObject(theObject);
+		theObject->variantType = type;
+#ifdef CONFIG_YAFFS_WINCE
+		yfsd_WinFileTimeNow(theObject->win_atime);
+		theObject->win_ctime[0] = theObject->win_mtime[0] =
+		    theObject->win_atime[0];
+		theObject->win_ctime[1] = theObject->win_mtime[1] =
+		    theObject->win_atime[1];
+
+#else
+
+		theObject->yst_atime = theObject->yst_mtime =
+		    theObject->yst_ctime = Y_CURRENT_TIME;
+#endif
+		switch (type) {
+		case YAFFS_OBJECT_TYPE_FILE:
+			theObject->variant.fileVariant.fileSize = 0;
+			theObject->variant.fileVariant.scannedFileSize = 0;
+			theObject->variant.fileVariant.shrinkSize = 0xFFFFFFFF;	/* max __u32 */
+			theObject->variant.fileVariant.topLevel = 0;
+			theObject->variant.fileVariant.top =
+			    yaffs_GetTnode(dev);
+			break;
+		case YAFFS_OBJECT_TYPE_DIRECTORY:
+			INIT_LIST_HEAD(&theObject->variant.directoryVariant.
+				       children);
+			break;
+		case YAFFS_OBJECT_TYPE_SYMLINK:
+		case YAFFS_OBJECT_TYPE_HARDLINK:
+		case YAFFS_OBJECT_TYPE_SPECIAL:
+			/* No action required */
+			break;
+		case YAFFS_OBJECT_TYPE_UNKNOWN:
+			/* todo this should not happen */
+			break;
+		}
+	}
+
+	return theObject;
+}
+
+static yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device * dev,
+						      int number,
+						      yaffs_ObjectType type)
+{
+	yaffs_Object *theObject = NULL;
+
+	if (number > 0) {
+		theObject = yaffs_FindObjectByNumber(dev, number);
+	}
+
+	if (!theObject) {
+		theObject = yaffs_CreateNewObject(dev, number, type);
+	}
+
+	return theObject;
+
+}
+			
+
+static YCHAR *yaffs_CloneString(const YCHAR * str)
+{
+	YCHAR *newStr = NULL;
+
+	if (str && *str) {
+		newStr = YMALLOC((yaffs_strlen(str) + 1) * sizeof(YCHAR));
+		yaffs_strcpy(newStr, str);
+	}
+
+	return newStr;
+
+}
+
+/*
+ * Mknod (create) a new object.
+ * equivalentObject only has meaning for a hard link;
+ * aliasString only has meaning for a sumlink.
+ * rdev only has meaning for devices (a subset of special objects)
+ */
+ 
+static yaffs_Object *yaffs_MknodObject(yaffs_ObjectType type,
+				       yaffs_Object * parent,
+				       const YCHAR * name,
+				       __u32 mode,
+				       __u32 uid,
+				       __u32 gid,
+				       yaffs_Object * equivalentObject,
+				       const YCHAR * aliasString, __u32 rdev)
+{
+	yaffs_Object *in;
+
+	yaffs_Device *dev = parent->myDev;
+
+	/* Check if the entry exists. If it does then fail the call since we don't want a dup.*/
+	if (yaffs_FindObjectByName(parent, name)) {
+		return NULL;
+	}
+
+	in = yaffs_CreateNewObject(dev, -1, type);
+
+	if (in) {
+		in->chunkId = -1;
+		in->valid = 1;
+		in->variantType = type;
+
+		in->yst_mode = mode;
+
+#ifdef CONFIG_YAFFS_WINCE
+		yfsd_WinFileTimeNow(in->win_atime);
+		in->win_ctime[0] = in->win_mtime[0] = in->win_atime[0];
+		in->win_ctime[1] = in->win_mtime[1] = in->win_atime[1];
+
+#else
+		in->yst_atime = in->yst_mtime = in->yst_ctime = Y_CURRENT_TIME;
+
+		in->yst_rdev = rdev;
+		in->yst_uid = uid;
+		in->yst_gid = gid;
+#endif
+		in->nDataChunks = 0;
+
+		yaffs_SetObjectName(in, name);
+		in->dirty = 1;
+
+		yaffs_AddObjectToDirectory(parent, in);
+
+		in->myDev = parent->myDev;
+
+		switch (type) {
+		case YAFFS_OBJECT_TYPE_SYMLINK:
+			in->variant.symLinkVariant.alias =
+			    yaffs_CloneString(aliasString);
+			break;
+		case YAFFS_OBJECT_TYPE_HARDLINK:
+			in->variant.hardLinkVariant.equivalentObject =
+			    equivalentObject;
+			in->variant.hardLinkVariant.equivalentObjectId =
+			    equivalentObject->objectId;
+			list_add(&in->hardLinks, &equivalentObject->hardLinks);
+			break;
+		case YAFFS_OBJECT_TYPE_FILE:	
+		case YAFFS_OBJECT_TYPE_DIRECTORY:
+		case YAFFS_OBJECT_TYPE_SPECIAL:
+		case YAFFS_OBJECT_TYPE_UNKNOWN:
+			/* do nothing */
+			break;
+		}
+
+		if (yaffs_UpdateObjectHeader(in, name, 0, 0, 0) < 0) {
+			/* Could not create the object header, fail the creation */
+			yaffs_DestroyObject(in);
+			in = NULL;
+		}
+
+	}
+
+	return in;
+}
+
+yaffs_Object *yaffs_MknodFile(yaffs_Object * parent, const YCHAR * name,
+			      __u32 mode, __u32 uid, __u32 gid)
+{
+	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_FILE, parent, name, mode,
+				 uid, gid, NULL, NULL, 0);
+}
+
+yaffs_Object *yaffs_MknodDirectory(yaffs_Object * parent, const YCHAR * name,
+				   __u32 mode, __u32 uid, __u32 gid)
+{
+	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name,
+				 mode, uid, gid, NULL, NULL, 0);
+}
+
+yaffs_Object *yaffs_MknodSpecial(yaffs_Object * parent, const YCHAR * name,
+				 __u32 mode, __u32 uid, __u32 gid, __u32 rdev)
+{
+	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode,
+				 uid, gid, NULL, NULL, rdev);
+}
+
+yaffs_Object *yaffs_MknodSymLink(yaffs_Object * parent, const YCHAR * name,
+				 __u32 mode, __u32 uid, __u32 gid,
+				 const YCHAR * alias)
+{
+	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode,
+				 uid, gid, NULL, alias, 0);
+}
+
+/* yaffs_Link returns the object id of the equivalent object.*/
+yaffs_Object *yaffs_Link(yaffs_Object * parent, const YCHAR * name,
+			 yaffs_Object * equivalentObject)
+{
+	/* Get the real object in case we were fed a hard link as an equivalent object */
+	equivalentObject = yaffs_GetEquivalentObject(equivalentObject);
+
+	if (yaffs_MknodObject
+	    (YAFFS_OBJECT_TYPE_HARDLINK, parent, name, 0, 0, 0,
+	     equivalentObject, NULL, 0)) {
+		return equivalentObject;
+	} else {
+		return NULL;
+	}
+
+}
+
+static int yaffs_ChangeObjectName(yaffs_Object * obj, yaffs_Object * newDir,
+				  const YCHAR * newName, int force, int shadows)
+{
+	int unlinkOp;
+	int deleteOp;
+
+	yaffs_Object *existingTarget;
+
+	if (newDir == NULL) {
+		newDir = obj->parent;	/* use the old directory */
+	}
+
+	if (newDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) {
+		T(YAFFS_TRACE_ALWAYS,
+		  (TSTR
+		   ("tragendy: yaffs_ChangeObjectName: newDir is not a directory"
+		    TENDSTR)));
+		YBUG();
+	}
+	
+	/* TODO: Do we need this different handling for YAFFS2 and YAFFS1?? */
+	if (obj->myDev->isYaffs2) {
+		unlinkOp = (newDir == obj->myDev->unlinkedDir);
+	} else {
+		unlinkOp = (newDir == obj->myDev->unlinkedDir
+			    && obj->variantType == YAFFS_OBJECT_TYPE_FILE);
+	}
+
+	deleteOp = (newDir == obj->myDev->deletedDir);
+
+	existingTarget = yaffs_FindObjectByName(newDir, newName);
+
+	/* If the object is a file going into the unlinked directory, 
+	 *   then it is OK to just stuff it in since duplicate names are allowed.
+	 *   else only proceed if the new name does not exist and if we're putting 
+	 *   it into a directory.
+	 */
+	if ((unlinkOp ||
+	     deleteOp ||
+	     force ||
+	     (shadows > 0) ||
+	     !existingTarget) &&
+	    newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY) {
+		yaffs_SetObjectName(obj, newName);
+		obj->dirty = 1;
+
+		yaffs_AddObjectToDirectory(newDir, obj);
+
+		if (unlinkOp)
+			obj->unlinked = 1;
+
+		/* If it is a deletion then we mark it as a shrink for gc purposes. */
+		if (yaffs_UpdateObjectHeader(obj, newName, 0, deleteOp, shadows)>= 0)
+			return YAFFS_OK;
+	}
+
+	return YAFFS_FAIL;
+}
+
+int yaffs_RenameObject(yaffs_Object * oldDir, const YCHAR * oldName,
+		       yaffs_Object * newDir, const YCHAR * newName)
+{
+	yaffs_Object *obj;
+	yaffs_Object *existingTarget;
+	int force = 0;
+
+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
+	/* Special case for case insemsitive systems (eg. WinCE).
+	 * While look-up is case insensitive, the name isn't.
+	 * Therefore we might want to change x.txt to X.txt
+	*/
+	if (oldDir == newDir && yaffs_strcmp(oldName, newName) == 0) {
+		force = 1;
+	}
+#endif
+
+	obj = yaffs_FindObjectByName(oldDir, oldName);
+	/* Check new name to long. */
+	if (obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK &&
+	    yaffs_strlen(newName) > YAFFS_MAX_ALIAS_LENGTH)
+	  /* ENAMETOOLONG */
+	  return YAFFS_FAIL;
+	else if (obj->variantType != YAFFS_OBJECT_TYPE_SYMLINK &&
+		 yaffs_strlen(newName) > YAFFS_MAX_NAME_LENGTH)
+	  /* ENAMETOOLONG */
+	  return YAFFS_FAIL;
+
+	if (obj && obj->renameAllowed) {
+
+		/* Now do the handling for an existing target, if there is one */
+
+		existingTarget = yaffs_FindObjectByName(newDir, newName);
+		if (existingTarget &&
+		    existingTarget->variantType == YAFFS_OBJECT_TYPE_DIRECTORY &&
+		    !list_empty(&existingTarget->variant.directoryVariant.children)) {
+			/* There is a target that is a non-empty directory, so we fail */
+			return YAFFS_FAIL;	/* EEXIST or ENOTEMPTY */
+		} else if (existingTarget && existingTarget != obj) {
+			/* Nuke the target first, using shadowing, 
+			 * but only if it isn't the same object
+			 */
+			yaffs_ChangeObjectName(obj, newDir, newName, force,
+					       existingTarget->objectId);
+			yaffs_UnlinkObject(existingTarget);
+		}
+
+		return yaffs_ChangeObjectName(obj, newDir, newName, 1, 0);
+	}
+	return YAFFS_FAIL;
+}
+
+/*------------------------- Block Management and Page Allocation ----------------*/
+
+static int yaffs_InitialiseBlocks(yaffs_Device * dev)
+{
+	int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
+	
+	dev->allocationBlock = -1;	/* force it to get a new one */
+
+	/* Todo we're assuming the malloc will pass. */
+	dev->blockInfo = YMALLOC(nBlocks * sizeof(yaffs_BlockInfo));
+	if(!dev->blockInfo){
+		dev->blockInfo = YMALLOC_ALT(nBlocks * sizeof(yaffs_BlockInfo));
+		dev->blockInfoAlt = 1;
+	}
+	else
+		dev->blockInfoAlt = 0;
+	
+	/* Set up dynamic blockinfo stuff. */
+	dev->chunkBitmapStride = (dev->nChunksPerBlock + 7) / 8; /* round up bytes */
+	dev->chunkBits = YMALLOC(dev->chunkBitmapStride * nBlocks);
+	if(!dev->chunkBits){
+		dev->chunkBits = YMALLOC_ALT(dev->chunkBitmapStride * nBlocks);
+		dev->chunkBitsAlt = 1;
+	}
+	else
+		dev->chunkBitsAlt = 0;
+	
+	if (dev->blockInfo && dev->chunkBits) {
+		memset(dev->blockInfo, 0, nBlocks * sizeof(yaffs_BlockInfo));
+		memset(dev->chunkBits, 0, dev->chunkBitmapStride * nBlocks);
+		return YAFFS_OK;
+	}
+
+	return YAFFS_FAIL;
+
+}
+
+static void yaffs_DeinitialiseBlocks(yaffs_Device * dev)
+{
+	if(dev->blockInfoAlt)
+		YFREE_ALT(dev->blockInfo);
+	else
+		YFREE(dev->blockInfo);
+	dev->blockInfoAlt = 0;
+
+	dev->blockInfo = NULL;
+	
+	if(dev->chunkBitsAlt)
+		YFREE_ALT(dev->chunkBits);
+	else
+		YFREE(dev->chunkBits);
+	dev->chunkBitsAlt = 0;
+	dev->chunkBits = NULL;
+}
+
+static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device * dev,
+					    yaffs_BlockInfo * bi)
+{
+	int i;
+	__u32 seq;
+	yaffs_BlockInfo *b;
+
+	if (!dev->isYaffs2)
+		return 1;	/* disqualification only applies to yaffs2. */
+
+	if (!bi->hasShrinkHeader)
+		return 1;	/* can gc */
+
+	/* Find the oldest dirty sequence number if we don't know it and save it
+	 * so we don't have to keep recomputing it.
+	 */
+	if (!dev->oldestDirtySequence) {
+		seq = dev->sequenceNumber;
+
+		for (i = dev->internalStartBlock; i <= dev->internalEndBlock;
+		     i++) {
+			b = yaffs_GetBlockInfo(dev, i);
+			if (b->blockState == YAFFS_BLOCK_STATE_FULL &&
+			    (b->pagesInUse - b->softDeletions) <
+			    dev->nChunksPerBlock && b->sequenceNumber < seq) {
+				seq = b->sequenceNumber;
+			}
+		}
+		dev->oldestDirtySequence = seq;
+	}
+
+	/* Can't do gc of this block if there are any blocks older than this one that have
+	 * discarded pages.
+	 */
+	return (bi->sequenceNumber <= dev->oldestDirtySequence);
+
+}
+
+/* FindDiretiestBlock is used to select the dirtiest block (or close enough)
+ * for garbage collection.
+ */
+
+static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev,
+					       int aggressive)
+{
+
+	int b = dev->currentDirtyChecker;
+
+	int i;
+	int iterations;
+	int dirtiest = -1;
+	int pagesInUse=0;
+	int prioritised=0;
+	yaffs_BlockInfo *bi;
+	static int nonAggressiveSkip = 0;
+	int pendingPrioritisedExist = 0;
+	
+	/* First let's see if we need to grab a prioritised block */
+	if(dev->hasPendingPrioritisedGCs){
+		for(i = dev->internalStartBlock; i < dev->internalEndBlock && !prioritised; i++){
+
+			bi = yaffs_GetBlockInfo(dev, i);
+			if(bi->gcPrioritise) {
+				pendingPrioritisedExist = 1;
+				if(bi->blockState == YAFFS_BLOCK_STATE_FULL &&
+				   yaffs_BlockNotDisqualifiedFromGC(dev, bi)){
+					pagesInUse = (bi->pagesInUse - bi->softDeletions);
+					dirtiest = i;
+					prioritised = 1;
+					aggressive = 1; /* Fool the non-aggressive skip logiv below */
+				}
+			}
+		}
+		
+		if(!pendingPrioritisedExist) /* None found, so we can clear this */
+			dev->hasPendingPrioritisedGCs = 0;
+	}
+
+	/* If we're doing aggressive GC then we are happy to take a less-dirty block, and
+	 * search harder.
+	 * else (we're doing a leasurely gc), then we only bother to do this if the
+	 * block has only a few pages in use.
+	 */
+
+	nonAggressiveSkip--;
+
+	if (!aggressive && (nonAggressiveSkip > 0)) {
+		return -1;
+	}
+
+	if(!prioritised)
+		pagesInUse =
+	    		(aggressive) ? dev->nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1;
+
+	if (aggressive) {
+		iterations =
+		    dev->internalEndBlock - dev->internalStartBlock + 1;
+	} else {
+		iterations =
+		    dev->internalEndBlock - dev->internalStartBlock + 1;
+		iterations = iterations / 16;
+		if (iterations > 200) {
+			iterations = 200;
+		}
+	}
+
+	for (i = 0; i <= iterations && pagesInUse > 0 && !prioritised; i++) {
+		b++;
+		if (b < dev->internalStartBlock || b > dev->internalEndBlock) {
+			b = dev->internalStartBlock;
+		}
+
+		if (b < dev->internalStartBlock || b > dev->internalEndBlock) {
+			T(YAFFS_TRACE_ERROR,
+			  (TSTR("**>> Block %d is not valid" TENDSTR), b));
+			YBUG();
+		}
+
+		bi = yaffs_GetBlockInfo(dev, b);
+
+#if 0
+		if (bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT) {
+			dirtiest = b;
+			pagesInUse = 0;
+		}
+		else 
+#endif
+
+		if (bi->blockState == YAFFS_BLOCK_STATE_FULL &&
+		       (bi->pagesInUse - bi->softDeletions) < pagesInUse &&
+		        yaffs_BlockNotDisqualifiedFromGC(dev, bi)) {
+			dirtiest = b;
+			pagesInUse = (bi->pagesInUse - bi->softDeletions);
+		}
+	}
+
+	dev->currentDirtyChecker = b;
+
+	if (dirtiest > 0) {
+		T(YAFFS_TRACE_GC,
+		  (TSTR("GC Selected block %d with %d free, prioritised:%d" TENDSTR), dirtiest,
+		   dev->nChunksPerBlock - pagesInUse,prioritised));
+	}
+
+	dev->oldestDirtySequence = 0;
+
+	if (dirtiest > 0) {
+		nonAggressiveSkip = 4;
+	}
+
+	return dirtiest;
+}
+
+static void yaffs_BlockBecameDirty(yaffs_Device * dev, int blockNo)
+{
+	yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockNo);
+
+	int erasedOk = 0;
+
+	/* If the block is still healthy erase it and mark as clean.
+	 * If the block has had a data failure, then retire it.
+	 */
+	 
+	T(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE,
+		(TSTR("yaffs_BlockBecameDirty block %d state %d %s"TENDSTR),
+		blockNo, bi->blockState, (bi->needsRetiring) ? "needs retiring" : ""));
+		
+	bi->blockState = YAFFS_BLOCK_STATE_DIRTY;
+
+	if (!bi->needsRetiring) {
+		yaffs_InvalidateCheckpoint(dev);
+		erasedOk = yaffs_EraseBlockInNAND(dev, blockNo);
+		if (!erasedOk) {
+			dev->nErasureFailures++;
+			T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
+			  (TSTR("**>> Erasure failed %d" TENDSTR), blockNo));
+		}
+	}
+
+	if (erasedOk && (yaffs_traceMask & YAFFS_TRACE_ERASE)) {
+		int i;
+		for (i = 0; i < dev->nChunksPerBlock; i++) {
+			if (!yaffs_CheckChunkErased
+			    (dev, blockNo * dev->nChunksPerBlock + i)) {
+				T(YAFFS_TRACE_ERROR,
+				  (TSTR
+				   (">>Block %d erasure supposedly OK, but chunk %d not erased"
+				    TENDSTR), blockNo, i));
+			}
+		}
+	}
+
+	if (erasedOk) {
+		/* Clean it up... */
+		bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
+		dev->nErasedBlocks++;
+		bi->pagesInUse = 0;
+		bi->softDeletions = 0;
+		bi->hasShrinkHeader = 0;
+		bi->skipErasedCheck = 1;  /* This is clean, so no need to check */
+		bi->gcPrioritise = 0;
+		yaffs_ClearChunkBits(dev, blockNo);
+
+		T(YAFFS_TRACE_ERASE,
+		  (TSTR("Erased block %d" TENDSTR), blockNo));
+	} else {
+		dev->nFreeChunks -= dev->nChunksPerBlock;	/* We lost a block of free space */
+
+		yaffs_RetireBlock(dev, blockNo);
+		T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
+		  (TSTR("**>> Block %d retired" TENDSTR), blockNo));
+	}
+}
+
+static int yaffs_FindBlockForAllocation(yaffs_Device * dev)
+{
+	int i;
+
+	yaffs_BlockInfo *bi;
+
+	if (dev->nErasedBlocks < 1) {
+		/* Hoosterman we've got a problem.
+		 * Can't get space to gc
+		 */
+		T(YAFFS_TRACE_ERROR,
+		  (TSTR("yaffs tragedy: no more eraased blocks" TENDSTR)));
+
+		return -1;
+	}
+	
+	/* Find an empty block. */
+
+	for (i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) {
+		dev->allocationBlockFinder++;
+		if (dev->allocationBlockFinder < dev->internalStartBlock
+		    || dev->allocationBlockFinder > dev->internalEndBlock) {
+			dev->allocationBlockFinder = dev->internalStartBlock;
+		}
+
+		bi = yaffs_GetBlockInfo(dev, dev->allocationBlockFinder);
+
+		if (bi->blockState == YAFFS_BLOCK_STATE_EMPTY) {
+			bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING;
+			dev->sequenceNumber++;
+			bi->sequenceNumber = dev->sequenceNumber;
+			dev->nErasedBlocks--;
+			T(YAFFS_TRACE_ALLOCATE,
+			  (TSTR("Allocated block %d, seq  %d, %d left" TENDSTR),
+			   dev->allocationBlockFinder, dev->sequenceNumber,
+			   dev->nErasedBlocks));
+			return dev->allocationBlockFinder;
+		}
+	}
+
+	T(YAFFS_TRACE_ALWAYS,
+	  (TSTR
+	   ("yaffs tragedy: no more eraased blocks, but there should have been %d"
+	    TENDSTR), dev->nErasedBlocks));
+
+	return -1;
+}
+
+
+// Check if there's space to allocate...
+// Thinks.... do we need top make this ths same as yaffs_GetFreeChunks()?
+static int yaffs_CheckSpaceForAllocation(yaffs_Device * dev)
+{
+	int reservedChunks;
+	int reservedBlocks = dev->nReservedBlocks;
+	int checkpointBlocks;
+	
+	checkpointBlocks =  dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint;
+	if(checkpointBlocks < 0)
+		checkpointBlocks = 0;
+	
+	reservedChunks = ((reservedBlocks + checkpointBlocks) * dev->nChunksPerBlock);
+	
+	return (dev->nFreeChunks > reservedChunks);
+}
+
+static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve, yaffs_BlockInfo **blockUsedPtr)
+{
+	int retVal;
+	yaffs_BlockInfo *bi;
+
+	if (dev->allocationBlock < 0) {
+		/* Get next block to allocate off */
+		dev->allocationBlock = yaffs_FindBlockForAllocation(dev);
+		dev->allocationPage = 0;
+	}
+
+	if (!useReserve && !yaffs_CheckSpaceForAllocation(dev)) {
+		/* Not enough space to allocate unless we're allowed to use the reserve. */
+		return -1;
+	}
+
+	if (dev->nErasedBlocks < dev->nReservedBlocks
+	    && dev->allocationPage == 0) {
+		T(YAFFS_TRACE_ALLOCATE, (TSTR("Allocating reserve" TENDSTR)));
+	}
+
+	/* Next page please.... */
+	if (dev->allocationBlock >= 0) {
+		bi = yaffs_GetBlockInfo(dev, dev->allocationBlock);
+
+		retVal = (dev->allocationBlock * dev->nChunksPerBlock) +
+		    dev->allocationPage;
+		bi->pagesInUse++;
+		yaffs_SetChunkBit(dev, dev->allocationBlock,
+				  dev->allocationPage);
+
+		dev->allocationPage++;
+
+		dev->nFreeChunks--;
+
+		/* If the block is full set the state to full */
+		if (dev->allocationPage >= dev->nChunksPerBlock) {
+			bi->blockState = YAFFS_BLOCK_STATE_FULL;
+			dev->allocationBlock = -1;
+		}
+
+		if(blockUsedPtr)
+			*blockUsedPtr = bi;
+			
+		return retVal;
+	}
+	
+	T(YAFFS_TRACE_ERROR,
+	  (TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR)));
+
+	return -1;
+}
+
+static int yaffs_GetErasedChunks(yaffs_Device * dev)
+{
+	int n;
+
+	n = dev->nErasedBlocks * dev->nChunksPerBlock;
+
+	if (dev->allocationBlock > 0) {
+		n += (dev->nChunksPerBlock - dev->allocationPage);
+	}
+
+	return n;
+
+}
+
+static int yaffs_GarbageCollectBlock(yaffs_Device * dev, int block)
+{
+	int oldChunk;
+	int newChunk;
+	int chunkInBlock;
+	int markNAND;
+	int retVal = YAFFS_OK;
+	int cleanups = 0;
+	int i;
+	int isCheckpointBlock;
+
+	int chunksBefore = yaffs_GetErasedChunks(dev);
+	int chunksAfter;
+
+	yaffs_ExtendedTags tags;
+
+	yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, block);
+
+	yaffs_Object *object;
+
+	isCheckpointBlock = (bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT);
+	
+	bi->blockState = YAFFS_BLOCK_STATE_COLLECTING;
+
+	T(YAFFS_TRACE_TRACING,
+	  (TSTR("Collecting block %d, in use %d, shrink %d, " TENDSTR), block,
+	   bi->pagesInUse, bi->hasShrinkHeader));
+
+	/*yaffs_VerifyFreeChunks(dev); */
+
+	bi->hasShrinkHeader = 0;	/* clear the flag so that the block can erase */
+
+	/* Take off the number of soft deleted entries because
+	 * they're going to get really deleted during GC.
+	 */
+	dev->nFreeChunks -= bi->softDeletions;
+
+	dev->isDoingGC = 1;
+
+	if (isCheckpointBlock ||
+	    !yaffs_StillSomeChunkBits(dev, block)) {
+		T(YAFFS_TRACE_TRACING,
+		  (TSTR
+		   ("Collecting block %d that has no chunks in use" TENDSTR),
+		   block));
+		yaffs_BlockBecameDirty(dev, block);
+	} else {
+
+		__u8 *buffer = yaffs_GetTempBuffer(dev, __LINE__);
+
+		for (chunkInBlock = 0, oldChunk = block * dev->nChunksPerBlock;
+		     chunkInBlock < dev->nChunksPerBlock
+		     && yaffs_StillSomeChunkBits(dev, block);
+		     chunkInBlock++, oldChunk++) {
+			if (yaffs_CheckChunkBit(dev, block, chunkInBlock)) {
+
+				/* This page is in use and might need to be copied off */
+
+				markNAND = 1;
+
+				yaffs_InitialiseTags(&tags);
+
+				yaffs_ReadChunkWithTagsFromNAND(dev, oldChunk,
+								buffer, &tags);
+
+				object =
+				    yaffs_FindObjectByNumber(dev,
+							     tags.objectId);
+
+				T(YAFFS_TRACE_GC_DETAIL,
+				  (TSTR
+				   ("Collecting page %d, %d %d %d " TENDSTR),
+				   chunkInBlock, tags.objectId, tags.chunkId,
+				   tags.byteCount));
+
+				if (!object) {
+					T(YAFFS_TRACE_ERROR,
+					  (TSTR
+					   ("page %d in gc has no object "
+					    TENDSTR), oldChunk));
+				}
+
+				if (object && object->deleted
+				    && tags.chunkId != 0) {
+					/* Data chunk in a deleted file, throw it away
+					 * It's a soft deleted data chunk,
+					 * No need to copy this, just forget about it and 
+					 * fix up the object.
+					 */
+
+					object->nDataChunks--;
+
+					if (object->nDataChunks <= 0) {
+						/* remeber to clean up the object */
+						dev->gcCleanupList[cleanups] =
+						    tags.objectId;
+						cleanups++;
+					}
+					markNAND = 0;
+				} else if (0
+					   /* Todo object && object->deleted && object->nDataChunks == 0 */
+					   ) {
+					/* Deleted object header with no data chunks.
+					 * Can be discarded and the file deleted.
+					 */
+					object->chunkId = 0;
+					yaffs_FreeTnode(object->myDev,
+							object->variant.
+							fileVariant.top);
+					object->variant.fileVariant.top = NULL;
+					yaffs_DoGenericObjectDeletion(object);
+
+				} else if (object) {
+					/* It's either a data chunk in a live file or
+					 * an ObjectHeader, so we're interested in it.
+					 * NB Need to keep the ObjectHeaders of deleted files
+					 * until the whole file has been deleted off
+					 */
+					tags.serialNumber++;
+
+					dev->nGCCopies++;
+
+					if (tags.chunkId == 0) {
+						/* It is an object Id,
+						 * We need to nuke the shrinkheader flags first
+						 * We no longer want the shrinkHeader flag since its work is done
+						 * and if it is left in place it will mess up scanning.
+						 * Also, clear out any shadowing stuff
+						 */
+
+						yaffs_ObjectHeader *oh;
+						oh = (yaffs_ObjectHeader *)buffer;
+						oh->isShrink = 0;
+						oh->shadowsObject = -1;
+						tags.extraShadows = 0;
+						tags.extraIsShrinkHeader = 0;
+					}
+
+					newChunk =
+					    yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &tags, 1);
+
+					if (newChunk < 0) {
+						retVal = YAFFS_FAIL;
+					} else {
+
+						/* Ok, now fix up the Tnodes etc. */
+
+						if (tags.chunkId == 0) {
+							/* It's a header */
+							object->chunkId =  newChunk;
+							object->serial =   tags.serialNumber;
+						} else {
+							/* It's a data chunk */
+							yaffs_PutChunkIntoFile
+							    (object,
+							     tags.chunkId,
+							     newChunk, 0);
+						}
+					}
+				}
+
+				yaffs_DeleteChunk(dev, oldChunk, markNAND, __LINE__);
+
+			}
+		}
+
+		yaffs_ReleaseTempBuffer(dev, buffer, __LINE__);
+
+
+		/* Do any required cleanups */
+		for (i = 0; i < cleanups; i++) {
+			/* Time to delete the file too */
+			object =
+			    yaffs_FindObjectByNumber(dev,
+						     dev->gcCleanupList[i]);
+			if (object) {
+				yaffs_FreeTnode(dev,
+						object->variant.fileVariant.
+						top);
+				object->variant.fileVariant.top = NULL;
+				T(YAFFS_TRACE_GC,
+				  (TSTR
+				   ("yaffs: About to finally delete object %d"
+				    TENDSTR), object->objectId));
+				yaffs_DoGenericObjectDeletion(object);
+				object->myDev->nDeletedFiles--;
+			}
+
+		}
+
+	}
+
+	if (chunksBefore >= (chunksAfter = yaffs_GetErasedChunks(dev))) {
+		T(YAFFS_TRACE_GC,
+		  (TSTR
+		   ("gc did not increase free chunks before %d after %d"
+		    TENDSTR), chunksBefore, chunksAfter));
+	}
+
+	dev->isDoingGC = 0;
+
+	return YAFFS_OK;
+}
+
+/* New garbage collector
+ * If we're very low on erased blocks then we do aggressive garbage collection
+ * otherwise we do "leasurely" garbage collection.
+ * Aggressive gc looks further (whole array) and will accept less dirty blocks.
+ * Passive gc only inspects smaller areas and will only accept more dirty blocks.
+ *
+ * The idea is to help clear out space in a more spread-out manner.
+ * Dunno if it really does anything useful.
+ */
+static int yaffs_CheckGarbageCollection(yaffs_Device * dev)
+{
+	int block;
+	int aggressive;
+	int gcOk = YAFFS_OK;
+	int maxTries = 0;
+	
+	int checkpointBlockAdjust;
+
+	if (dev->isDoingGC) {
+		/* Bail out so we don't get recursive gc */
+		return YAFFS_OK;
+	}
+	
+	/* This loop should pass the first time.
+	 * We'll only see looping here if the erase of the collected block fails.
+	 */
+
+	do {
+		maxTries++;
+		
+		checkpointBlockAdjust = (dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint);
+		if(checkpointBlockAdjust < 0)
+			checkpointBlockAdjust = 0;
+
+		if (dev->nErasedBlocks < (dev->nReservedBlocks + checkpointBlockAdjust)) {
+			/* We need a block soon...*/
+			aggressive = 1;
+		} else {
+			/* We're in no hurry */
+			aggressive = 0;
+		}
+
+		block = yaffs_FindBlockForGarbageCollection(dev, aggressive);
+
+		if (block > 0) {
+			dev->garbageCollections++;
+			if (!aggressive) {
+				dev->passiveGarbageCollections++;
+			}
+
+			T(YAFFS_TRACE_GC,
+			  (TSTR
+			   ("yaffs: GC erasedBlocks %d aggressive %d" TENDSTR),
+			   dev->nErasedBlocks, aggressive));
+
+			gcOk = yaffs_GarbageCollectBlock(dev, block);
+		}
+
+		if (dev->nErasedBlocks < (dev->nReservedBlocks) && block > 0) {
+			T(YAFFS_TRACE_GC,
+			  (TSTR
+			   ("yaffs: GC !!!no reclaim!!! erasedBlocks %d after try %d block %d"
+			    TENDSTR), dev->nErasedBlocks, maxTries, block));
+		}
+	} while ((dev->nErasedBlocks < dev->nReservedBlocks) && (block > 0)
+		 && (maxTries < 2));
+
+	return aggressive ? gcOk : YAFFS_OK;
+}
+
+/*-------------------------  TAGS --------------------------------*/
+
+static int yaffs_TagsMatch(const yaffs_ExtendedTags * tags, int objectId,
+			   int chunkInObject)
+{
+	return (tags->chunkId == chunkInObject &&
+		tags->objectId == objectId && !tags->chunkDeleted) ? 1 : 0;
+
+}
+
+
+/*-------------------- Data file manipulation -----------------*/
+
+static int yaffs_FindChunkInFile(yaffs_Object * in, int chunkInInode,
+				 yaffs_ExtendedTags * tags)
+{
+	/*Get the Tnode, then get the level 0 offset chunk offset */
+	yaffs_Tnode *tn;
+	int theChunk = -1;
+	yaffs_ExtendedTags localTags;
+	int retVal = -1;
+
+	yaffs_Device *dev = in->myDev;
+
+	if (!tags) {
+		/* Passed a NULL, so use our own tags space */
+		tags = &localTags;
+	}
+
+	tn = yaffs_FindLevel0Tnode(dev, &in->variant.fileVariant, chunkInInode);
+
+	if (tn) {
+		theChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode);
+
+		retVal =
+		    yaffs_FindChunkInGroup(dev, theChunk, tags, in->objectId,
+					   chunkInInode);
+	}
+	return retVal;
+}
+
+static int yaffs_FindAndDeleteChunkInFile(yaffs_Object * in, int chunkInInode,
+					  yaffs_ExtendedTags * tags)
+{
+	/* Get the Tnode, then get the level 0 offset chunk offset */
+	yaffs_Tnode *tn;
+	int theChunk = -1;
+	yaffs_ExtendedTags localTags;
+
+	yaffs_Device *dev = in->myDev;
+	int retVal = -1;
+
+	if (!tags) {
+		/* Passed a NULL, so use our own tags space */
+		tags = &localTags;
+	}
+
+	tn = yaffs_FindLevel0Tnode(dev, &in->variant.fileVariant, chunkInInode);
+
+	if (tn) {
+
+		theChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode);
+
+		retVal =
+		    yaffs_FindChunkInGroup(dev, theChunk, tags, in->objectId,
+					   chunkInInode);
+
+		/* Delete the entry in the filestructure (if found) */
+		if (retVal != -1) {
+			yaffs_PutLevel0Tnode(dev,tn,chunkInInode,0);
+		}
+	} else {
+		/*T(("No level 0 found for %d\n", chunkInInode)); */
+	}
+
+	if (retVal == -1) {
+		/* T(("Could not find %d to delete\n",chunkInInode)); */
+	}
+	return retVal;
+}
+
+#ifdef YAFFS_PARANOID
+
+static int yaffs_CheckFileSanity(yaffs_Object * in)
+{
+	int chunk;
+	int nChunks;
+	int fSize;
+	int failed = 0;
+	int objId;
+	yaffs_Tnode *tn;
+	yaffs_Tags localTags;
+	yaffs_Tags *tags = &localTags;
+	int theChunk;
+	int chunkDeleted;
+
+	if (in->variantType != YAFFS_OBJECT_TYPE_FILE) {
+		/* T(("Object not a file\n")); */
+		return YAFFS_FAIL;
+	}
+
+	objId = in->objectId;
+	fSize = in->variant.fileVariant.fileSize;
+	nChunks =
+	    (fSize + in->myDev->nDataBytesPerChunk - 1) / in->myDev->nDataBytesPerChunk;
+
+	for (chunk = 1; chunk <= nChunks; chunk++) {
+		tn = yaffs_FindLevel0Tnode(in->myDev, &in->variant.fileVariant,
+					   chunk);
+
+		if (tn) {
+
+			theChunk = yaffs_GetChunkGroupBase(dev,tn,chunk);
+
+			if (yaffs_CheckChunkBits
+			    (dev, theChunk / dev->nChunksPerBlock,
+			     theChunk % dev->nChunksPerBlock)) {
+
+				yaffs_ReadChunkTagsFromNAND(in->myDev, theChunk,
+							    tags,
+							    &chunkDeleted);
+				if (yaffs_TagsMatch
+				    (tags, in->objectId, chunk, chunkDeleted)) {
+					/* found it; */
+
+				}
+			} else {
+
+				failed = 1;
+			}
+
+		} else {
+			/* T(("No level 0 found for %d\n", chunk)); */
+		}
+	}
+
+	return failed ? YAFFS_FAIL : YAFFS_OK;
+}
+
+#endif
+
+static int yaffs_PutChunkIntoFile(yaffs_Object * in, int chunkInInode,
+				  int chunkInNAND, int inScan)
+{
+	/* NB inScan is zero unless scanning. 
+	 * For forward scanning, inScan is > 0; 
+	 * for backward scanning inScan is < 0
+	 */
+	 
+	yaffs_Tnode *tn;
+	yaffs_Device *dev = in->myDev;
+	int existingChunk;
+	yaffs_ExtendedTags existingTags;
+	yaffs_ExtendedTags newTags;
+	unsigned existingSerial, newSerial;
+
+	if (in->variantType != YAFFS_OBJECT_TYPE_FILE) {
+		/* Just ignore an attempt at putting a chunk into a non-file during scanning
+		 * If it is not during Scanning then something went wrong!
+		 */
+		if (!inScan) {
+			T(YAFFS_TRACE_ERROR,
+			  (TSTR
+			   ("yaffs tragedy:attempt to put data chunk into a non-file"
+			    TENDSTR)));
+			YBUG();
+		}
+
+		yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__);
+		return YAFFS_OK;
+	}
+
+	tn = yaffs_AddOrFindLevel0Tnode(dev, 
+					&in->variant.fileVariant,
+					chunkInInode,
+					NULL);
+	if (!tn) {
+		return YAFFS_FAIL;
+	}
+
+	existingChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode);
+
+	if (inScan != 0) {
+		/* If we're scanning then we need to test for duplicates
+		 * NB This does not need to be efficient since it should only ever 
+		 * happen when the power fails during a write, then only one
+		 * chunk should ever be affected.
+		 *
+		 * Correction for YAFFS2: This could happen quite a lot and we need to think about efficiency! TODO
+		 * Update: For backward scanning we don't need to re-read tags so this is quite cheap.
+		 */
+
+		if (existingChunk != 0) {
+			/* NB Right now existing chunk will not be real chunkId if the device >= 32MB
+			 *    thus we have to do a FindChunkInFile to get the real chunk id.
+			 *
+			 * We have a duplicate now we need to decide which one to use:
+			 *
+			 * Backwards scanning YAFFS2: The old one is what we use, dump the new one.
+			 * Forward scanning YAFFS2: The new one is what we use, dump the old one.
+			 * YAFFS1: Get both sets of tags and compare serial numbers.
+			 */
+
+			if (inScan > 0) {
+				/* Only do this for forward scanning */
+				yaffs_ReadChunkWithTagsFromNAND(dev,
+								chunkInNAND,
+								NULL, &newTags);
+
+				/* Do a proper find */
+				existingChunk =
+				    yaffs_FindChunkInFile(in, chunkInInode,
+							  &existingTags);
+			}
+
+			if (existingChunk <= 0) {
+				/*Hoosterman - how did this happen? */
+
+				T(YAFFS_TRACE_ERROR,
+				  (TSTR
+				   ("yaffs tragedy: existing chunk < 0 in scan"
+				    TENDSTR)));
+
+			}
+
+			/* NB The deleted flags should be false, otherwise the chunks will 
+			 * not be loaded during a scan
+			 */
+
+			newSerial = newTags.serialNumber;
+			existingSerial = existingTags.serialNumber;
+
+			if ((inScan > 0) &&
+			    (in->myDev->isYaffs2 ||
+			     existingChunk <= 0 ||
+			     ((existingSerial + 1) & 3) == newSerial)) {
+				/* Forward scanning.                            
+				 * Use new
+				 * Delete the old one and drop through to update the tnode
+				 */
+				yaffs_DeleteChunk(dev, existingChunk, 1,
+						  __LINE__);
+			} else {
+				/* Backward scanning or we want to use the existing one
+				 * Use existing.
+				 * Delete the new one and return early so that the tnode isn't changed
+				 */
+				yaffs_DeleteChunk(dev, chunkInNAND, 1,
+						  __LINE__);
+				return YAFFS_OK;
+			}
+		}
+
+	}
+
+	if (existingChunk == 0) {
+		in->nDataChunks++;
+	}
+
+	yaffs_PutLevel0Tnode(dev,tn,chunkInInode,chunkInNAND);
+
+	return YAFFS_OK;
+}
+
+static int yaffs_ReadChunkDataFromObject(yaffs_Object * in, int chunkInInode,
+					 __u8 * buffer)
+{
+	int chunkInNAND = yaffs_FindChunkInFile(in, chunkInInode, NULL);
+
+	if (chunkInNAND >= 0) {
+		return yaffs_ReadChunkWithTagsFromNAND(in->myDev, chunkInNAND,
+						       buffer,NULL);
+	} else {
+		T(YAFFS_TRACE_NANDACCESS,
+		  (TSTR("Chunk %d not found zero instead" TENDSTR),
+		   chunkInNAND));
+		/* get sane (zero) data if you read a hole */
+		memset(buffer, 0, in->myDev->nDataBytesPerChunk);	
+		return 0;
+	}
+
+}
+
+void yaffs_DeleteChunk(yaffs_Device * dev, int chunkId, int markNAND, int lyn)
+{
+	int block;
+	int page;
+	yaffs_ExtendedTags tags;
+	yaffs_BlockInfo *bi;
+
+	if (chunkId <= 0)
+		return;
+
+	dev->nDeletions++;
+	block = chunkId / dev->nChunksPerBlock;
+	page = chunkId % dev->nChunksPerBlock;
+
+	bi = yaffs_GetBlockInfo(dev, block);
+
+	T(YAFFS_TRACE_DELETION,
+	  (TSTR("line %d delete of chunk %d" TENDSTR), lyn, chunkId));
+
+	if (markNAND &&
+	    bi->blockState != YAFFS_BLOCK_STATE_COLLECTING && !dev->isYaffs2) {
+
+		yaffs_InitialiseTags(&tags);
+
+		tags.chunkDeleted = 1;
+
+		yaffs_WriteChunkWithTagsToNAND(dev, chunkId, NULL, &tags);
+		yaffs_HandleUpdateChunk(dev, chunkId, &tags);
+	} else {
+		dev->nUnmarkedDeletions++;
+	}
+
+	/* Pull out of the management area.
+	 * If the whole block became dirty, this will kick off an erasure.
+	 */
+	if (bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING ||
+	    bi->blockState == YAFFS_BLOCK_STATE_FULL ||
+	    bi->blockState == YAFFS_BLOCK_STATE_NEEDS_SCANNING ||
+	    bi->blockState == YAFFS_BLOCK_STATE_COLLECTING) {
+		dev->nFreeChunks++;
+
+		yaffs_ClearChunkBit(dev, block, page);
+
+		bi->pagesInUse--;
+
+		if (bi->pagesInUse == 0 &&
+		    !bi->hasShrinkHeader &&
+		    bi->blockState != YAFFS_BLOCK_STATE_ALLOCATING &&
+		    bi->blockState != YAFFS_BLOCK_STATE_NEEDS_SCANNING) {
+			yaffs_BlockBecameDirty(dev, block);
+		}
+
+	} else {
+		/* T(("Bad news deleting chunk %d\n",chunkId)); */
+	}
+
+}
+
+static int yaffs_WriteChunkDataToObject(yaffs_Object * in, int chunkInInode,
+					const __u8 * buffer, int nBytes,
+					int useReserve)
+{
+	/* Find old chunk Need to do this to get serial number
+	 * Write new one and patch into tree.
+	 * Invalidate old tags.
+	 */
+
+	int prevChunkId;
+	yaffs_ExtendedTags prevTags;
+
+	int newChunkId;
+	yaffs_ExtendedTags newTags;
+
+	yaffs_Device *dev = in->myDev;
+
+	yaffs_CheckGarbageCollection(dev);
+
+	/* Get the previous chunk at this location in the file if it exists */
+	prevChunkId = yaffs_FindChunkInFile(in, chunkInInode, &prevTags);
+
+	/* Set up new tags */
+	yaffs_InitialiseTags(&newTags);
+
+	newTags.chunkId = chunkInInode;
+	newTags.objectId = in->objectId;
+	newTags.serialNumber =
+	    (prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1;
+	newTags.byteCount = nBytes;
+
+	newChunkId =
+	    yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags,
+					      useReserve);
+
+	if (newChunkId >= 0) {
+		yaffs_PutChunkIntoFile(in, chunkInInode, newChunkId, 0);
+
+		if (prevChunkId >= 0) {
+			yaffs_DeleteChunk(dev, prevChunkId, 1, __LINE__);
+
+		}
+
+		yaffs_CheckFileSanity(in);
+	}
+	return newChunkId;
+
+}
+
+/* UpdateObjectHeader updates the header on NAND for an object.
+ * If name is not NULL, then that new name is used.
+ */
+int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name, int force,
+			     int isShrink, int shadows)
+{
+
+	yaffs_BlockInfo *bi;
+
+	yaffs_Device *dev = in->myDev;
+
+	int prevChunkId;
+	int retVal = 0;
+	int result = 0;
+
+	int newChunkId;
+	yaffs_ExtendedTags newTags;
+
+	__u8 *buffer = NULL;
+	YCHAR oldName[YAFFS_MAX_NAME_LENGTH + 1];
+
+	yaffs_ObjectHeader *oh = NULL;
+
+	if (!in->fake || force) {
+
+		yaffs_CheckGarbageCollection(dev);
+
+		buffer = yaffs_GetTempBuffer(in->myDev, __LINE__);
+		oh = (yaffs_ObjectHeader *) buffer;
+
+		prevChunkId = in->chunkId;
+
+		if (prevChunkId >= 0) {
+			result = yaffs_ReadChunkWithTagsFromNAND(dev, prevChunkId,
+							buffer, NULL);
+			memcpy(oldName, oh->name, sizeof(oh->name));
+		}
+
+		memset(buffer, 0xFF, dev->nDataBytesPerChunk);
+
+		oh->type = in->variantType;
+		oh->yst_mode = in->yst_mode;
+		oh->shadowsObject = shadows;
+
+#ifdef CONFIG_YAFFS_WINCE
+		oh->win_atime[0] = in->win_atime[0];
+		oh->win_ctime[0] = in->win_ctime[0];
+		oh->win_mtime[0] = in->win_mtime[0];
+		oh->win_atime[1] = in->win_atime[1];
+		oh->win_ctime[1] = in->win_ctime[1];
+		oh->win_mtime[1] = in->win_mtime[1];
+#else
+		oh->yst_uid = in->yst_uid;
+		oh->yst_gid = in->yst_gid;
+		oh->yst_atime = in->yst_atime;
+		oh->yst_mtime = in->yst_mtime;
+		oh->yst_ctime = in->yst_ctime;
+		oh->yst_rdev = in->yst_rdev;
+#endif
+		if (in->parent) {
+			oh->parentObjectId = in->parent->objectId;
+		} else {
+			oh->parentObjectId = 0;
+		}
+
+		if (name && *name) {
+			memset(oh->name, 0, sizeof(oh->name));
+			yaffs_strncpy(oh->name, name, YAFFS_MAX_NAME_LENGTH);
+		} else if (prevChunkId) {
+			memcpy(oh->name, oldName, sizeof(oh->name));
+		} else {
+			memset(oh->name, 0, sizeof(oh->name));
+		}
+
+		oh->isShrink = isShrink;
+
+		switch (in->variantType) {
+		case YAFFS_OBJECT_TYPE_UNKNOWN:
+			/* Should not happen */
+			break;
+		case YAFFS_OBJECT_TYPE_FILE:
+			oh->fileSize =
+			    (oh->parentObjectId == YAFFS_OBJECTID_DELETED
+			     || oh->parentObjectId ==
+			     YAFFS_OBJECTID_UNLINKED) ? 0 : in->variant.
+			    fileVariant.fileSize;
+			break;
+		case YAFFS_OBJECT_TYPE_HARDLINK:
+			oh->equivalentObjectId =
+			    in->variant.hardLinkVariant.equivalentObjectId;
+			break;
+		case YAFFS_OBJECT_TYPE_SPECIAL:
+			/* Do nothing */
+			break;
+		case YAFFS_OBJECT_TYPE_DIRECTORY:
+			/* Do nothing */
+			break;
+		case YAFFS_OBJECT_TYPE_SYMLINK:
+			yaffs_strncpy(oh->alias,
+				      in->variant.symLinkVariant.alias,
+				      YAFFS_MAX_ALIAS_LENGTH);
+			oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0;
+			break;
+		}
+
+		/* Tags */
+		yaffs_InitialiseTags(&newTags);
+		in->serial++;
+		newTags.chunkId = 0;
+		newTags.objectId = in->objectId;
+		newTags.serialNumber = in->serial;
+
+		/* Add extra info for file header */
+
+		newTags.extraHeaderInfoAvailable = 1;
+		newTags.extraParentObjectId = oh->parentObjectId;
+		newTags.extraFileLength = oh->fileSize;
+		newTags.extraIsShrinkHeader = oh->isShrink;
+		newTags.extraEquivalentObjectId = oh->equivalentObjectId;
+		newTags.extraShadows = (oh->shadowsObject > 0) ? 1 : 0;
+		newTags.extraObjectType = in->variantType;
+
+		/* Create new chunk in NAND */
+		newChunkId =
+		    yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags,
+						      (prevChunkId >= 0) ? 1 : 0);
+
+		if (newChunkId >= 0) {
+
+			in->chunkId = newChunkId;
+
+			if (prevChunkId >= 0) {
+				yaffs_DeleteChunk(dev, prevChunkId, 1,
+						  __LINE__);
+			}
+
+			if(!yaffs_ObjectHasCachedWriteData(in))
+				in->dirty = 0;
+
+			/* If this was a shrink, then mark the block that the chunk lives on */
+			if (isShrink) {
+				bi = yaffs_GetBlockInfo(in->myDev,
+							newChunkId /in->myDev->	nChunksPerBlock);
+				bi->hasShrinkHeader = 1;
+			}
+
+		}
+
+		retVal = newChunkId;
+
+	}
+
+	if (buffer)
+		yaffs_ReleaseTempBuffer(dev, buffer, __LINE__);
+
+	return retVal;
+}
+
+/*------------------------ Short Operations Cache ----------------------------------------
+ *   In many situations where there is no high level buffering (eg WinCE) a lot of
+ *   reads might be short sequential reads, and a lot of writes may be short 
+ *   sequential writes. eg. scanning/writing a jpeg file.
+ *   In these cases, a short read/write cache can provide a huge perfomance benefit 
+ *   with dumb-as-a-rock code.
+ *   In Linux, the page cache provides read buffering aand the short op cache provides write 
+ *   buffering.
+ *
+ *   There are a limited number (~10) of cache chunks per device so that we don't
+ *   need a very intelligent search.
+ */
+
+static int yaffs_ObjectHasCachedWriteData(yaffs_Object *obj)
+{
+	yaffs_Device *dev = obj->myDev;
+	int i;
+	yaffs_ChunkCache *cache;
+	int nCaches = obj->myDev->nShortOpCaches;
+	
+	for(i = 0; i < nCaches; i++){
+		cache = &dev->srCache[i];
+		if (cache->object == obj &&
+		    cache->dirty)
+			return 1;
+	}
+	
+	return 0;
+}
+
+
+static void yaffs_FlushFilesChunkCache(yaffs_Object * obj)
+{
+	yaffs_Device *dev = obj->myDev;
+	int lowest = -99;	/* Stop compiler whining. */
+	int i;
+	yaffs_ChunkCache *cache;
+	int chunkWritten = 0;
+	int nCaches = obj->myDev->nShortOpCaches;
+
+	if (nCaches > 0) {
+		do {
+			cache = NULL;
+
+			/* Find the dirty cache for this object with the lowest chunk id. */
+			for (i = 0; i < nCaches; i++) {
+				if (dev->srCache[i].object == obj &&
+				    dev->srCache[i].dirty) {
+					if (!cache
+					    || dev->srCache[i].chunkId <
+					    lowest) {
+						cache = &dev->srCache[i];
+						lowest = cache->chunkId;
+					}
+				}
+			}
+
+			if (cache && !cache->locked) {
+				/* Write it out and free it up */
+
+				chunkWritten =
+				    yaffs_WriteChunkDataToObject(cache->object,
+								 cache->chunkId,
+								 cache->data,
+								 cache->nBytes,
+								 1);
+				cache->dirty = 0;
+				cache->object = NULL;
+			}
+
+		} while (cache && chunkWritten > 0);
+
+		if (cache) {
+			/* Hoosterman, disk full while writing cache out. */
+			T(YAFFS_TRACE_ERROR,
+			  (TSTR("yaffs tragedy: no space during cache write" TENDSTR)));
+
+		}
+	}
+
+}
+
+/*yaffs_FlushEntireDeviceCache(dev)
+ *
+ *
+ */
+
+void yaffs_FlushEntireDeviceCache(yaffs_Device *dev)
+{
+	yaffs_Object *obj;
+	int nCaches = dev->nShortOpCaches;
+	int i;
+	
+	/* Find a dirty object in the cache and flush it...
+	 * until there are no further dirty objects.
+	 */
+	do {
+		obj = NULL;
+		for( i = 0; i < nCaches && !obj; i++) {
+			if (dev->srCache[i].object &&
+			    dev->srCache[i].dirty)
+				obj = dev->srCache[i].object;
+			    
+		}
+		if(obj)
+			yaffs_FlushFilesChunkCache(obj);
+			
+	} while(obj);
+	
+}
+
+
+/* Grab us a cache chunk for use.
+ * First look for an empty one. 
+ * Then look for the least recently used non-dirty one.
+ * Then look for the least recently used dirty one...., flush and look again.
+ */
+static yaffs_ChunkCache *yaffs_GrabChunkCacheWorker(yaffs_Device * dev)
+{
+	int i;
+	int usage;
+	int theOne;
+
+	if (dev->nShortOpCaches > 0) {
+		for (i = 0; i < dev->nShortOpCaches; i++) {
+			if (!dev->srCache[i].object) 
+				return &dev->srCache[i];
+		}
+
+		return NULL;
+
+		theOne = -1;
+		usage = 0;	/* just to stop the compiler grizzling */
+
+		for (i = 0; i < dev->nShortOpCaches; i++) {
+			if (!dev->srCache[i].dirty &&
+			    ((dev->srCache[i].lastUse < usage && theOne >= 0) ||
+			     theOne < 0)) {
+				usage = dev->srCache[i].lastUse;
+				theOne = i;
+			}
+		}
+
+
+		return theOne >= 0 ? &dev->srCache[theOne] : NULL;
+	} else {
+		return NULL;
+	}
+
+}
+
+static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device * dev)
+{
+	yaffs_ChunkCache *cache;
+	yaffs_Object *theObj;
+	int usage;
+	int i;
+	int pushout;
+
+	if (dev->nShortOpCaches > 0) {
+		/* Try find a non-dirty one... */
+
+		cache = yaffs_GrabChunkCacheWorker(dev);
+
+		if (!cache) {
+			/* They were all dirty, find the last recently used object and flush
+			 * its cache, then  find again.
+			 * NB what's here is not very accurate, we actually flush the object
+			 * the last recently used page.
+			 */
+
+			/* With locking we can't assume we can use entry zero */
+
+			theObj = NULL;
+			usage = -1;
+			cache = NULL;
+			pushout = -1;
+
+			for (i = 0; i < dev->nShortOpCaches; i++) {
+				if (dev->srCache[i].object &&
+				    !dev->srCache[i].locked &&
+				    (dev->srCache[i].lastUse < usage || !cache))
+				{
+					usage = dev->srCache[i].lastUse;
+					theObj = dev->srCache[i].object;
+					cache = &dev->srCache[i];
+					pushout = i;
+				}
+			}
+
+			if (!cache || cache->dirty) {
+				/* Flush and try again */
+				yaffs_FlushFilesChunkCache(theObj);
+				cache = yaffs_GrabChunkCacheWorker(dev);
+			}
+
+		}
+		return cache;
+	} else
+		return NULL;
+
+}
+
+/* Find a cached chunk */
+static yaffs_ChunkCache *yaffs_FindChunkCache(const yaffs_Object * obj,
+					      int chunkId)
+{
+	yaffs_Device *dev = obj->myDev;
+	int i;
+	if (dev->nShortOpCaches > 0) {
+		for (i = 0; i < dev->nShortOpCaches; i++) {
+			if (dev->srCache[i].object == obj &&
+			    dev->srCache[i].chunkId == chunkId) {
+				dev->cacheHits++;
+
+				return &dev->srCache[i];
+			}
+		}
+	}
+	return NULL;
+}
+
+/* Mark the chunk for the least recently used algorithym */
+static void yaffs_UseChunkCache(yaffs_Device * dev, yaffs_ChunkCache * cache,
+				int isAWrite)
+{
+
+	if (dev->nShortOpCaches > 0) {
+		if (dev->srLastUse < 0 || dev->srLastUse > 100000000) {
+			/* Reset the cache usages */
+			int i;
+			for (i = 1; i < dev->nShortOpCaches; i++) {
+				dev->srCache[i].lastUse = 0;
+			}
+			dev->srLastUse = 0;
+		}
+
+		dev->srLastUse++;
+
+		cache->lastUse = dev->srLastUse;
+
+		if (isAWrite) {
+			cache->dirty = 1;
+		}
+	}
+}
+
+/* Invalidate a single cache page.
+ * Do this when a whole page gets written,
+ * ie the short cache for this page is no longer valid.
+ */
+static void yaffs_InvalidateChunkCache(yaffs_Object * object, int chunkId)
+{
+	if (object->myDev->nShortOpCaches > 0) {
+		yaffs_ChunkCache *cache = yaffs_FindChunkCache(object, chunkId);
+
+		if (cache) {
+			cache->object = NULL;
+		}
+	}
+}
+
+/* Invalidate all the cache pages associated with this object
+ * Do this whenever ther file is deleted or resized.
+ */
+static void yaffs_InvalidateWholeChunkCache(yaffs_Object * in)
+{
+	int i;
+	yaffs_Device *dev = in->myDev;
+
+	if (dev->nShortOpCaches > 0) {
+		/* Invalidate it. */
+		for (i = 0; i < dev->nShortOpCaches; i++) {
+			if (dev->srCache[i].object == in) {
+				dev->srCache[i].object = NULL;
+			}
+		}
+	}
+}
+
+/*--------------------- Checkpointing --------------------*/
+
+
+static int yaffs_WriteCheckpointValidityMarker(yaffs_Device *dev,int head)
+{
+	yaffs_CheckpointValidity cp;
+	cp.structType = sizeof(cp);
+	cp.magic = YAFFS_MAGIC;
+	cp.version = YAFFS_CHECKPOINT_VERSION;
+	cp.head = (head) ? 1 : 0;
+	
+	return (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp))?
+		1 : 0;
+}
+
+static int yaffs_ReadCheckpointValidityMarker(yaffs_Device *dev, int head)
+{
+	yaffs_CheckpointValidity cp;
+	int ok;
+	
+	ok = (yaffs_CheckpointRead(dev,&cp,sizeof(cp)) == sizeof(cp));
+	
+	if(ok)
+		ok = (cp.structType == sizeof(cp)) &&
+		     (cp.magic == YAFFS_MAGIC) &&
+		     (cp.version == YAFFS_CHECKPOINT_VERSION) &&
+		     (cp.head == ((head) ? 1 : 0));
+	return ok ? 1 : 0;
+}
+
+static void yaffs_DeviceToCheckpointDevice(yaffs_CheckpointDevice *cp, 
+					   yaffs_Device *dev)
+{
+	cp->nErasedBlocks = dev->nErasedBlocks;
+	cp->allocationBlock = dev->allocationBlock;
+	cp->allocationPage = dev->allocationPage;
+	cp->nFreeChunks = dev->nFreeChunks;
+	
+	cp->nDeletedFiles = dev->nDeletedFiles;
+	cp->nUnlinkedFiles = dev->nUnlinkedFiles;
+	cp->nBackgroundDeletions = dev->nBackgroundDeletions;
+	cp->sequenceNumber = dev->sequenceNumber;
+	cp->oldestDirtySequence = dev->oldestDirtySequence;
+	
+}
+
+static void yaffs_CheckpointDeviceToDevice(yaffs_Device *dev,
+					   yaffs_CheckpointDevice *cp)
+{
+	dev->nErasedBlocks = cp->nErasedBlocks;
+	dev->allocationBlock = cp->allocationBlock;
+	dev->allocationPage = cp->allocationPage;
+	dev->nFreeChunks = cp->nFreeChunks;
+	
+	dev->nDeletedFiles = cp->nDeletedFiles;
+	dev->nUnlinkedFiles = cp->nUnlinkedFiles;
+	dev->nBackgroundDeletions = cp->nBackgroundDeletions;
+	dev->sequenceNumber = cp->sequenceNumber;
+	dev->oldestDirtySequence = cp->oldestDirtySequence;
+}
+
+
+static int yaffs_WriteCheckpointDevice(yaffs_Device *dev)
+{
+	yaffs_CheckpointDevice cp;
+	__u32 nBytes;
+	__u32 nBlocks = (dev->internalEndBlock - dev->internalStartBlock + 1);
+
+	int ok;
+		
+	/* Write device runtime values*/
+	yaffs_DeviceToCheckpointDevice(&cp,dev);
+	cp.structType = sizeof(cp);
+	
+	ok = (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp));
+	
+	/* Write block info */
+	if(ok) {
+		nBytes = nBlocks * sizeof(yaffs_BlockInfo);
+		ok = (yaffs_CheckpointWrite(dev,dev->blockInfo,nBytes) == nBytes);
+	}
+		
+	/* Write chunk bits */		
+	if(ok) {
+		nBytes = nBlocks * dev->chunkBitmapStride;
+		ok = (yaffs_CheckpointWrite(dev,dev->chunkBits,nBytes) == nBytes);
+	}
+	return	 ok ? 1 : 0;
+
+}
+
+static int yaffs_ReadCheckpointDevice(yaffs_Device *dev)
+{
+	yaffs_CheckpointDevice cp;
+	__u32 nBytes;
+	__u32 nBlocks = (dev->internalEndBlock - dev->internalStartBlock + 1);
+
+	int ok;	
+	
+	ok = (yaffs_CheckpointRead(dev,&cp,sizeof(cp)) == sizeof(cp));
+	if(!ok)
+		return 0;
+		
+	if(cp.structType != sizeof(cp))
+		return 0;
+		
+	
+	yaffs_CheckpointDeviceToDevice(dev,&cp);
+	
+	nBytes = nBlocks * sizeof(yaffs_BlockInfo);
+	
+	ok = (yaffs_CheckpointRead(dev,dev->blockInfo,nBytes) == nBytes);
+	
+	if(!ok)
+		return 0;
+	nBytes = nBlocks * dev->chunkBitmapStride;
+	
+	ok = (yaffs_CheckpointRead(dev,dev->chunkBits,nBytes) == nBytes);
+	
+	return ok ? 1 : 0;
+}
+
+static void yaffs_ObjectToCheckpointObject(yaffs_CheckpointObject *cp,
+					   yaffs_Object *obj)
+{
+
+	cp->objectId = obj->objectId;
+	cp->parentId = (obj->parent) ? obj->parent->objectId : 0;
+	cp->chunkId = obj->chunkId;
+	cp->variantType = obj->variantType;			
+	cp->deleted = obj->deleted;
+	cp->softDeleted = obj->softDeleted;
+	cp->unlinked = obj->unlinked;
+	cp->fake = obj->fake;
+	cp->renameAllowed = obj->renameAllowed;
+	cp->unlinkAllowed = obj->unlinkAllowed;
+	cp->serial = obj->serial;
+	cp->nDataChunks = obj->nDataChunks;
+	
+	if(obj->variantType == YAFFS_OBJECT_TYPE_FILE)
+		cp->fileSizeOrEquivalentObjectId = obj->variant.fileVariant.fileSize;
+	else if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)
+		cp->fileSizeOrEquivalentObjectId = obj->variant.hardLinkVariant.equivalentObjectId;
+}
+
+static void yaffs_CheckpointObjectToObject( yaffs_Object *obj,yaffs_CheckpointObject *cp)
+{
+
+	yaffs_Object *parent;
+	
+	obj->objectId = cp->objectId;
+	
+	if(cp->parentId)
+		parent = yaffs_FindOrCreateObjectByNumber(
+					obj->myDev,
+					cp->parentId,
+					YAFFS_OBJECT_TYPE_DIRECTORY);
+	else
+		parent = NULL;
+		
+	if(parent)
+		yaffs_AddObjectToDirectory(parent, obj);
+		
+	obj->chunkId = cp->chunkId;
+	obj->variantType = cp->variantType;			
+	obj->deleted = cp->deleted;
+	obj->softDeleted = cp->softDeleted;
+	obj->unlinked = cp->unlinked;
+	obj->fake = cp->fake;
+	obj->renameAllowed = cp->renameAllowed;
+	obj->unlinkAllowed = cp->unlinkAllowed;
+	obj->serial = cp->serial;
+	obj->nDataChunks = cp->nDataChunks;
+	
+	if(obj->variantType == YAFFS_OBJECT_TYPE_FILE)
+		obj->variant.fileVariant.fileSize = cp->fileSizeOrEquivalentObjectId;
+	else if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)
+		obj->variant.hardLinkVariant.equivalentObjectId = cp->fileSizeOrEquivalentObjectId;
+		
+	if(obj->objectId >= YAFFS_NOBJECT_BUCKETS)
+		obj->lazyLoaded = 1;
+}
+
+
+
+static int yaffs_CheckpointTnodeWorker(yaffs_Object * in, yaffs_Tnode * tn,
+				  	__u32 level, int chunkOffset)
+{
+	int i;
+	yaffs_Device *dev = in->myDev;
+	int ok = 1;
+	int nTnodeBytes = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8;
+
+	if (tn) {
+		if (level > 0) {
+
+			for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++){
+				if (tn->internal[i]) {
+					ok = yaffs_CheckpointTnodeWorker(in,
+							tn->internal[i],
+							level - 1,
+							(chunkOffset<<YAFFS_TNODES_INTERNAL_BITS) + i);
+				}
+			}
+		} else if (level == 0) {
+			__u32 baseOffset = chunkOffset <<  YAFFS_TNODES_LEVEL0_BITS;
+			/* printf("write tnode at %d\n",baseOffset); */
+			ok = (yaffs_CheckpointWrite(dev,&baseOffset,sizeof(baseOffset)) == sizeof(baseOffset));
+			if(ok)
+				ok = (yaffs_CheckpointWrite(dev,tn,nTnodeBytes) == nTnodeBytes);
+		}
+	}
+
+	return ok;
+
+}
+
+static int yaffs_WriteCheckpointTnodes(yaffs_Object *obj)
+{
+	__u32 endMarker = ~0;
+	int ok = 1;
+	
+	if(obj->variantType == YAFFS_OBJECT_TYPE_FILE){
+		ok = yaffs_CheckpointTnodeWorker(obj,
+					    obj->variant.fileVariant.top,
+					    obj->variant.fileVariant.topLevel,
+					    0);
+		if(ok)
+			ok = (yaffs_CheckpointWrite(obj->myDev,&endMarker,sizeof(endMarker)) == 
+				sizeof(endMarker));
+	}
+	
+	return ok ? 1 : 0;
+}
+
+static int yaffs_ReadCheckpointTnodes(yaffs_Object *obj)
+{
+	__u32 baseChunk;
+	int ok = 1;
+	yaffs_Device *dev = obj->myDev;
+	yaffs_FileStructure *fileStructPtr = &obj->variant.fileVariant;
+	yaffs_Tnode *tn;
+	
+	ok = (yaffs_CheckpointRead(dev,&baseChunk,sizeof(baseChunk)) == sizeof(baseChunk));
+	
+	while(ok && (~baseChunk)){
+		/* Read level 0 tnode */
+		
+		/* printf("read  tnode at %d\n",baseChunk); */
+		tn = yaffs_GetTnodeRaw(dev);
+		if(tn)
+			ok = (yaffs_CheckpointRead(dev,tn,(dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8) ==
+			      (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8);
+		else
+			ok = 0;
+			
+		if(tn && ok){
+			ok = yaffs_AddOrFindLevel0Tnode(dev,
+					       		fileStructPtr,
+					       		baseChunk,
+					       		tn) ? 1 : 0;
+		}
+			
+		if(ok)
+			ok = (yaffs_CheckpointRead(dev,&baseChunk,sizeof(baseChunk)) == sizeof(baseChunk));
+		
+	}
+
+	return ok ? 1 : 0;	
+}
+ 
+
+static int yaffs_WriteCheckpointObjects(yaffs_Device *dev)
+{
+	yaffs_Object *obj;
+	yaffs_CheckpointObject cp;
+	int i;
+	int ok = 1;
+	struct list_head *lh;
+
+	
+	/* Iterate through the objects in each hash entry,
+	 * dumping them to the checkpointing stream.
+	 */
+	 
+	 for(i = 0; ok &&  i <  YAFFS_NOBJECT_BUCKETS; i++){
+	 	list_for_each(lh, &dev->objectBucket[i].list) {
+			if (lh) {
+				obj = list_entry(lh, yaffs_Object, hashLink);
+				if (!obj->deferedFree) {
+					yaffs_ObjectToCheckpointObject(&cp,obj);
+					cp.structType = sizeof(cp);
+
+					T(YAFFS_TRACE_CHECKPOINT,(
+						TSTR("Checkpoint write object %d parent %d type %d chunk %d obj addr %x" TENDSTR),
+						cp.objectId,cp.parentId,cp.variantType,cp.chunkId,(unsigned) obj));
+						
+					ok = (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp));
+					
+					if(ok && obj->variantType == YAFFS_OBJECT_TYPE_FILE){
+						ok = yaffs_WriteCheckpointTnodes(obj);
+					}
+				}
+			}
+		}
+	 }
+	 
+	 /* Dump end of list */
+	memset(&cp,0xFF,sizeof(yaffs_CheckpointObject));
+	cp.structType = sizeof(cp);
+	
+	if(ok)
+		ok = (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp));
+		
+	return ok ? 1 : 0;
+}
+
+static int yaffs_ReadCheckpointObjects(yaffs_Device *dev)
+{
+	yaffs_Object *obj;
+	yaffs_CheckpointObject cp;
+	int ok = 1;
+	int done = 0;
+	yaffs_Object *hardList = NULL;
+	
+	while(ok && !done) {
+		ok = (yaffs_CheckpointRead(dev,&cp,sizeof(cp)) == sizeof(cp));
+		if(cp.structType != sizeof(cp)) {
+			/* printf("structure parsing failed\n"); */
+			ok = 0;
+		}
+			
+		if(ok && cp.objectId == ~0)
+			done = 1;
+		else if(ok){
+			obj = yaffs_FindOrCreateObjectByNumber(dev,cp.objectId, cp.variantType);
+			T(YAFFS_TRACE_CHECKPOINT,(TSTR("Checkpoint read object %d parent %d type %d chunk %d obj addr %x" TENDSTR),
+				cp.objectId,cp.parentId,cp.variantType,cp.chunkId,(unsigned) obj));
+			if(obj) {
+				yaffs_CheckpointObjectToObject(obj,&cp);
+				if(obj->variantType == YAFFS_OBJECT_TYPE_FILE) {
+					ok = yaffs_ReadCheckpointTnodes(obj);
+				} else if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) {
+					obj->hardLinks.next =
+						    (struct list_head *)
+						    hardList;
+					hardList = obj;
+				}
+			   
+			}
+		}
+	}
+	
+	if(ok)
+		yaffs_HardlinkFixup(dev,hardList);
+	
+	return ok ? 1 : 0;
+}
+
+static int yaffs_WriteCheckpointData(yaffs_Device *dev)
+{
+
+	int ok;
+	
+	ok = yaffs_CheckpointOpen(dev,1);
+	
+	if(ok)
+		ok = yaffs_WriteCheckpointValidityMarker(dev,1);
+	if(ok)
+		ok = yaffs_WriteCheckpointDevice(dev);
+	if(ok)
+		ok = yaffs_WriteCheckpointObjects(dev);
+	if(ok)
+		ok = yaffs_WriteCheckpointValidityMarker(dev,0);
+		
+	if(!yaffs_CheckpointClose(dev))
+		 ok = 0;
+		 
+	if(ok)
+	    	dev->isCheckpointed = 1;
+	 else 
+	 	dev->isCheckpointed = 0;
+
+	return dev->isCheckpointed;
+}
+
+static int yaffs_ReadCheckpointData(yaffs_Device *dev)
+{
+	int ok;
+	
+	ok = yaffs_CheckpointOpen(dev,0); /* open for read */
+	
+	if(ok)
+		ok = yaffs_ReadCheckpointValidityMarker(dev,1);
+	if(ok)
+		ok = yaffs_ReadCheckpointDevice(dev);
+	if(ok)
+		ok = yaffs_ReadCheckpointObjects(dev);
+	if(ok)
+		ok = yaffs_ReadCheckpointValidityMarker(dev,0);
+		
+
+
+	if(!yaffs_CheckpointClose(dev))
+		ok = 0;
+
+	if(ok)
+	    	dev->isCheckpointed = 1;
+	 else 
+	 	dev->isCheckpointed = 0;
+
+	return ok ? 1 : 0;
+
+}
+
+static void yaffs_InvalidateCheckpoint(yaffs_Device *dev)
+{
+	if(dev->isCheckpointed || 
+	   dev->blocksInCheckpoint > 0){
+		dev->isCheckpointed = 0;
+		yaffs_CheckpointInvalidateStream(dev);
+		if(dev->superBlock && dev->markSuperBlockDirty)
+			dev->markSuperBlockDirty(dev->superBlock);
+	}
+}
+
+
+int yaffs_CheckpointSave(yaffs_Device *dev)
+{
+	yaffs_ReportOddballBlocks(dev);
+	T(YAFFS_TRACE_CHECKPOINT,(TSTR("save entry: isCheckpointed %d"TENDSTR),dev->isCheckpointed));
+
+	if(!dev->isCheckpointed)
+		yaffs_WriteCheckpointData(dev);
+	
+	T(YAFFS_TRACE_CHECKPOINT,(TSTR("save exit: isCheckpointed %d"TENDSTR),dev->isCheckpointed));
+
+	return dev->isCheckpointed;
+}
+
+int yaffs_CheckpointRestore(yaffs_Device *dev)
+{
+	int retval;
+	T(YAFFS_TRACE_CHECKPOINT,(TSTR("restore entry: isCheckpointed %d"TENDSTR),dev->isCheckpointed));
+	
+	retval = yaffs_ReadCheckpointData(dev);
+
+	T(YAFFS_TRACE_CHECKPOINT,(TSTR("restore exit: isCheckpointed %d"TENDSTR),dev->isCheckpointed));
+	
+	yaffs_ReportOddballBlocks(dev);
+	
+	return retval;
+}
+
+/*--------------------- File read/write ------------------------
+ * Read and write have very similar structures.
+ * In general the read/write has three parts to it
+ * An incomplete chunk to start with (if the read/write is not chunk-aligned)
+ * Some complete chunks
+ * An incomplete chunk to end off with
+ *
+ * Curve-balls: the first chunk might also be the last chunk.
+ */
+
+int yaffs_ReadDataFromFile(yaffs_Object * in, __u8 * buffer, loff_t offset,
+			   int nBytes)
+{
+
+	int chunk;
+	int start;
+	int nToCopy;
+	int n = nBytes;
+	int nDone = 0;
+	yaffs_ChunkCache *cache;
+
+	yaffs_Device *dev;
+
+	dev = in->myDev;
+
+	while (n > 0) {
+		//chunk = offset / dev->nDataBytesPerChunk + 1;
+		//start = offset % dev->nDataBytesPerChunk;
+		yaffs_AddrToChunk(dev,offset,&chunk,&start);
+		chunk++;
+
+		/* OK now check for the curveball where the start and end are in
+		 * the same chunk.      
+		 */
+		if ((start + n) < dev->nDataBytesPerChunk) {
+			nToCopy = n;
+		} else {
+			nToCopy = dev->nDataBytesPerChunk - start;
+		}
+
+		cache = yaffs_FindChunkCache(in, chunk);
+
+		/* If the chunk is already in the cache or it is less than a whole chunk
+		 * then use the cache (if there is caching)
+		 * else bypass the cache.
+		 */
+		if (cache || nToCopy != dev->nDataBytesPerChunk) {
+			if (dev->nShortOpCaches > 0) {
+
+				/* If we can't find the data in the cache, then load it up. */
+
+				if (!cache) {
+					cache = yaffs_GrabChunkCache(in->myDev);
+					cache->object = in;
+					cache->chunkId = chunk;
+					cache->dirty = 0;
+					cache->locked = 0;
+					yaffs_ReadChunkDataFromObject(in, chunk,
+								      cache->
+								      data);
+					cache->nBytes = 0;
+				}
+
+				yaffs_UseChunkCache(dev, cache, 0);
+
+				cache->locked = 1;
+
+#ifdef CONFIG_YAFFS_WINCE
+				yfsd_UnlockYAFFS(TRUE);
+#endif
+				memcpy(buffer, &cache->data[start], nToCopy);
+
+#ifdef CONFIG_YAFFS_WINCE
+				yfsd_LockYAFFS(TRUE);
+#endif
+				cache->locked = 0;
+			} else {
+				/* Read into the local buffer then copy..*/
+
+				__u8 *localBuffer =
+				    yaffs_GetTempBuffer(dev, __LINE__);
+				yaffs_ReadChunkDataFromObject(in, chunk,
+							      localBuffer);
+#ifdef CONFIG_YAFFS_WINCE
+				yfsd_UnlockYAFFS(TRUE);
+#endif
+				memcpy(buffer, &localBuffer[start], nToCopy);
+
+#ifdef CONFIG_YAFFS_WINCE
+				yfsd_LockYAFFS(TRUE);
+#endif
+				yaffs_ReleaseTempBuffer(dev, localBuffer,
+							__LINE__);
+			}
+
+		} else {
+#ifdef CONFIG_YAFFS_WINCE
+			__u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__);
+
+			/* Under WinCE can't do direct transfer. Need to use a local buffer.
+			 * This is because we otherwise screw up WinCE's memory mapper
+			 */
+			yaffs_ReadChunkDataFromObject(in, chunk, localBuffer);
+
+#ifdef CONFIG_YAFFS_WINCE
+			yfsd_UnlockYAFFS(TRUE);
+#endif
+			memcpy(buffer, localBuffer, dev->nDataBytesPerChunk);
+
+#ifdef CONFIG_YAFFS_WINCE
+			yfsd_LockYAFFS(TRUE);
+			yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__);
+#endif
+
+#else
+			/* A full chunk. Read directly into the supplied buffer. */
+			yaffs_ReadChunkDataFromObject(in, chunk, buffer);
+#endif
+		}
+
+		n -= nToCopy;
+		offset += nToCopy;
+		buffer += nToCopy;
+		nDone += nToCopy;
+
+	}
+
+	return nDone;
+}
+
+int yaffs_WriteDataToFile(yaffs_Object * in, const __u8 * buffer, loff_t offset,
+			  int nBytes, int writeThrough)
+{
+
+	int chunk;
+	int start;
+	int nToCopy;
+	int n = nBytes;
+	int nDone = 0;
+	int nToWriteBack;
+	int startOfWrite = offset;
+	int chunkWritten = 0;
+	int nBytesRead;
+
+	yaffs_Device *dev;
+
+	dev = in->myDev;
+
+	while (n > 0 && chunkWritten >= 0) {
+		//chunk = offset / dev->nDataBytesPerChunk + 1;
+		//start = offset % dev->nDataBytesPerChunk;
+		yaffs_AddrToChunk(dev,offset,&chunk,&start);
+		chunk++;
+
+		/* OK now check for the curveball where the start and end are in
+		 * the same chunk.
+		 */
+
+		if ((start + n) < dev->nDataBytesPerChunk) {
+			nToCopy = n;
+
+			/* Now folks, to calculate how many bytes to write back....
+			 * If we're overwriting and not writing to then end of file then
+			 * we need to write back as much as was there before.
+			 */
+
+			nBytesRead =
+			    in->variant.fileVariant.fileSize -
+			    ((chunk - 1) * dev->nDataBytesPerChunk);
+
+			if (nBytesRead > dev->nDataBytesPerChunk) {
+				nBytesRead = dev->nDataBytesPerChunk;
+			}
+
+			nToWriteBack =
+			    (nBytesRead >
+			     (start + n)) ? nBytesRead : (start + n);
+
+		} else {
+			nToCopy = dev->nDataBytesPerChunk - start;
+			nToWriteBack = dev->nDataBytesPerChunk;
+		}
+
+		if (nToCopy != dev->nDataBytesPerChunk) {
+			/* An incomplete start or end chunk (or maybe both start and end chunk) */
+			if (dev->nShortOpCaches > 0) {
+				yaffs_ChunkCache *cache;
+				/* If we can't find the data in the cache, then load the cache */
+				cache = yaffs_FindChunkCache(in, chunk);
+				
+				if (!cache
+				    && yaffs_CheckSpaceForAllocation(in->
+								     myDev)) {
+					cache = yaffs_GrabChunkCache(in->myDev);
+					cache->object = in;
+					cache->chunkId = chunk;
+					cache->dirty = 0;
+					cache->locked = 0;
+					yaffs_ReadChunkDataFromObject(in, chunk,
+								      cache->
+								      data);
+				}
+				else if(cache && 
+				        !cache->dirty &&
+					!yaffs_CheckSpaceForAllocation(in->myDev)){
+					/* Drop the cache if it was a read cache item and
+					 * no space check has been made for it.
+					 */ 
+					 cache = NULL;
+				}
+
+				if (cache) {
+					yaffs_UseChunkCache(dev, cache, 1);
+					cache->locked = 1;
+#ifdef CONFIG_YAFFS_WINCE
+					yfsd_UnlockYAFFS(TRUE);
+#endif
+
+					memcpy(&cache->data[start], buffer,
+					       nToCopy);
+
+#ifdef CONFIG_YAFFS_WINCE
+					yfsd_LockYAFFS(TRUE);
+#endif
+					cache->locked = 0;
+					cache->nBytes = nToWriteBack;
+
+					if (writeThrough) {
+						chunkWritten =
+						    yaffs_WriteChunkDataToObject
+						    (cache->object,
+						     cache->chunkId,
+						     cache->data, cache->nBytes,
+						     1);
+						cache->dirty = 0;
+					}
+
+				} else {
+					chunkWritten = -1;	/* fail the write */
+				}
+			} else {
+				/* An incomplete start or end chunk (or maybe both start and end chunk)
+				 * Read into the local buffer then copy, then copy over and write back.
+				 */
+
+				__u8 *localBuffer =
+				    yaffs_GetTempBuffer(dev, __LINE__);
+
+				yaffs_ReadChunkDataFromObject(in, chunk,
+							      localBuffer);
+
+#ifdef CONFIG_YAFFS_WINCE
+				yfsd_UnlockYAFFS(TRUE);
+#endif
+
+				memcpy(&localBuffer[start], buffer, nToCopy);
+
+#ifdef CONFIG_YAFFS_WINCE
+				yfsd_LockYAFFS(TRUE);
+#endif
+				chunkWritten =
+				    yaffs_WriteChunkDataToObject(in, chunk,
+								 localBuffer,
+								 nToWriteBack,
+								 0);
+
+				yaffs_ReleaseTempBuffer(dev, localBuffer,
+							__LINE__);
+
+			}
+
+		} else {
+
+#ifdef CONFIG_YAFFS_WINCE
+			/* Under WinCE can't do direct transfer. Need to use a local buffer.
+			 * This is because we otherwise screw up WinCE's memory mapper
+			 */
+			__u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__);
+#ifdef CONFIG_YAFFS_WINCE
+			yfsd_UnlockYAFFS(TRUE);
+#endif
+			memcpy(localBuffer, buffer, dev->nDataBytesPerChunk);
+#ifdef CONFIG_YAFFS_WINCE
+			yfsd_LockYAFFS(TRUE);
+#endif
+			chunkWritten =
+			    yaffs_WriteChunkDataToObject(in, chunk, localBuffer,
+							 dev->nDataBytesPerChunk,
+							 0);
+			yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__);
+#else
+			/* A full chunk. Write directly from the supplied buffer. */
+			chunkWritten =
+			    yaffs_WriteChunkDataToObject(in, chunk, buffer,
+							 dev->nDataBytesPerChunk,
+							 0);
+#endif
+			/* Since we've overwritten the cached data, we better invalidate it. */
+			yaffs_InvalidateChunkCache(in, chunk);
+		}
+
+		if (chunkWritten >= 0) {
+			n -= nToCopy;
+			offset += nToCopy;
+			buffer += nToCopy;
+			nDone += nToCopy;
+		}
+
+	}
+
+	/* Update file object */
+
+	if ((startOfWrite + nDone) > in->variant.fileVariant.fileSize) {
+		in->variant.fileVariant.fileSize = (startOfWrite + nDone);
+	}
+
+	in->dirty = 1;
+
+	return nDone;
+}
+
+
+/* ---------------------- File resizing stuff ------------------ */
+
+static void yaffs_PruneResizedChunks(yaffs_Object * in, int newSize)
+{
+
+	yaffs_Device *dev = in->myDev;
+	int oldFileSize = in->variant.fileVariant.fileSize;
+
+	int lastDel = 1 + (oldFileSize - 1) / dev->nDataBytesPerChunk;
+
+	int startDel = 1 + (newSize + dev->nDataBytesPerChunk - 1) /
+	    dev->nDataBytesPerChunk;
+	int i;
+	int chunkId;
+
+	/* Delete backwards so that we don't end up with holes if
+	 * power is lost part-way through the operation.
+	 */
+	for (i = lastDel; i >= startDel; i--) {
+		/* NB this could be optimised somewhat,
+		 * eg. could retrieve the tags and write them without
+		 * using yaffs_DeleteChunk
+		 */
+
+		chunkId = yaffs_FindAndDeleteChunkInFile(in, i, NULL);
+		if (chunkId > 0) {
+			if (chunkId <
+			    (dev->internalStartBlock * dev->nChunksPerBlock)
+			    || chunkId >=
+			    ((dev->internalEndBlock +
+			      1) * dev->nChunksPerBlock)) {
+				T(YAFFS_TRACE_ALWAYS,
+				  (TSTR("Found daft chunkId %d for %d" TENDSTR),
+				   chunkId, i));
+			} else {
+				in->nDataChunks--;
+				yaffs_DeleteChunk(dev, chunkId, 1, __LINE__);
+			}
+		}
+	}
+
+}
+
+int yaffs_ResizeFile(yaffs_Object * in, loff_t newSize)
+{
+
+	int oldFileSize = in->variant.fileVariant.fileSize;
+	int newSizeOfPartialChunk;
+	int newFullChunks;
+	
+	yaffs_Device *dev = in->myDev;
+	
+	yaffs_AddrToChunk(dev, newSize, &newFullChunks, &newSizeOfPartialChunk);
+
+	yaffs_FlushFilesChunkCache(in);
+	yaffs_InvalidateWholeChunkCache(in);
+
+	yaffs_CheckGarbageCollection(dev);
+
+	if (in->variantType != YAFFS_OBJECT_TYPE_FILE) {
+		return yaffs_GetFileSize(in);
+	}
+
+	if (newSize == oldFileSize) {
+		return oldFileSize;
+	}
+
+	if (newSize < oldFileSize) {
+
+		yaffs_PruneResizedChunks(in, newSize);
+
+		if (newSizeOfPartialChunk != 0) {
+			int lastChunk = 1 + newFullChunks;
+			
+			__u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__);
+
+			/* Got to read and rewrite the last chunk with its new size and zero pad */
+			yaffs_ReadChunkDataFromObject(in, lastChunk,
+						      localBuffer);
+
+			memset(localBuffer + newSizeOfPartialChunk, 0,
+			       dev->nDataBytesPerChunk - newSizeOfPartialChunk);
+
+			yaffs_WriteChunkDataToObject(in, lastChunk, localBuffer,
+						     newSizeOfPartialChunk, 1);
+
+			yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__);
+		}
+
+		in->variant.fileVariant.fileSize = newSize;
+
+		yaffs_PruneFileStructure(dev, &in->variant.fileVariant);
+	} else {
+		/* newsSize > oldFileSize */
+		in->variant.fileVariant.fileSize = newSize;
+	}
+
+		
+	
+	/* Write a new object header.
+	 * show we've shrunk the file, if need be
+	 * Do this only if the file is not in the deleted directories.
+	 */
+	if (in->parent->objectId != YAFFS_OBJECTID_UNLINKED &&
+	    in->parent->objectId != YAFFS_OBJECTID_DELETED) {
+		yaffs_UpdateObjectHeader(in, NULL, 0,
+					 (newSize < oldFileSize) ? 1 : 0, 0);
+	}
+
+	return newSize;
+}
+
+loff_t yaffs_GetFileSize(yaffs_Object * obj)
+{
+	obj = yaffs_GetEquivalentObject(obj);
+
+	switch (obj->variantType) {
+	case YAFFS_OBJECT_TYPE_FILE:
+		return obj->variant.fileVariant.fileSize;
+	case YAFFS_OBJECT_TYPE_SYMLINK:
+		return yaffs_strlen(obj->variant.symLinkVariant.alias);
+	default:
+		return 0;
+	}
+}
+
+
+
+int yaffs_FlushFile(yaffs_Object * in, int updateTime)
+{
+	int retVal;
+	if (in->dirty) {
+		yaffs_FlushFilesChunkCache(in);
+		if (updateTime) {
+#ifdef CONFIG_YAFFS_WINCE
+			yfsd_WinFileTimeNow(in->win_mtime);
+#else
+
+			in->yst_mtime = Y_CURRENT_TIME;
+
+#endif
+		}
+
+		retVal =
+		    (yaffs_UpdateObjectHeader(in, NULL, 0, 0, 0) >=
+		     0) ? YAFFS_OK : YAFFS_FAIL;
+	} else {
+		retVal = YAFFS_OK;
+	}
+
+	return retVal;
+
+}
+
+static int yaffs_DoGenericObjectDeletion(yaffs_Object * in)
+{
+
+	/* First off, invalidate the file's data in the cache, without flushing. */
+	yaffs_InvalidateWholeChunkCache(in);
+
+	if (in->myDev->isYaffs2 && (in->parent != in->myDev->deletedDir)) {
+		/* Move to the unlinked directory so we have a record that it was deleted. */
+		yaffs_ChangeObjectName(in, in->myDev->deletedDir, NULL, 0, 0);
+
+	}
+
+	yaffs_RemoveObjectFromDirectory(in);
+	yaffs_DeleteChunk(in->myDev, in->chunkId, 1, __LINE__);
+	in->chunkId = -1;
+
+	yaffs_FreeObject(in);
+	return YAFFS_OK;
+
+}
+
+/* yaffs_DeleteFile deletes the whole file data
+ * and the inode associated with the file.
+ * It does not delete the links associated with the file.
+ */
+static int yaffs_UnlinkFile(yaffs_Object * in)
+{
+
+	int retVal;
+	int immediateDeletion = 0;
+
+	if (1) {
+#ifdef __KERNEL__
+		if (!in->myInode) {
+			immediateDeletion = 1;
+
+		}
+#else
+		if (in->inUse <= 0) {
+			immediateDeletion = 1;
+
+		}
+#endif
+		if (immediateDeletion) {
+			retVal =
+			    yaffs_ChangeObjectName(in, in->myDev->deletedDir,
+						   NULL, 0, 0);
+			T(YAFFS_TRACE_TRACING,
+			  (TSTR("yaffs: immediate deletion of file %d" TENDSTR),
+			   in->objectId));
+			in->deleted = 1;
+			in->myDev->nDeletedFiles++;
+			if (0 && in->myDev->isYaffs2) {
+				yaffs_ResizeFile(in, 0);
+			}
+			yaffs_SoftDeleteFile(in);
+		} else {
+			retVal =
+			    yaffs_ChangeObjectName(in, in->myDev->unlinkedDir,
+						   NULL, 0, 0);
+		}
+
+	}
+	return retVal;
+}
+
+int yaffs_DeleteFile(yaffs_Object * in)
+{
+	int retVal = YAFFS_OK;
+
+	if (in->nDataChunks > 0) {
+		/* Use soft deletion if there is data in the file */
+		if (!in->unlinked) {
+			retVal = yaffs_UnlinkFile(in);
+		}
+		if (retVal == YAFFS_OK && in->unlinked && !in->deleted) {
+			in->deleted = 1;
+			in->myDev->nDeletedFiles++;
+			yaffs_SoftDeleteFile(in);
+		}
+		return in->deleted ? YAFFS_OK : YAFFS_FAIL;
+	} else {
+		/* The file has no data chunks so we toss it immediately */
+		yaffs_FreeTnode(in->myDev, in->variant.fileVariant.top);
+		in->variant.fileVariant.top = NULL;
+		yaffs_DoGenericObjectDeletion(in);
+
+		return YAFFS_OK;
+	}
+}
+
+static int yaffs_DeleteDirectory(yaffs_Object * in)
+{
+	/* First check that the directory is empty. */
+	if (list_empty(&in->variant.directoryVariant.children)) {
+		return yaffs_DoGenericObjectDeletion(in);
+	}
+
+	return YAFFS_FAIL;
+
+}
+
+static int yaffs_DeleteSymLink(yaffs_Object * in)
+{
+	YFREE(in->variant.symLinkVariant.alias);
+
+	return yaffs_DoGenericObjectDeletion(in);
+}
+
+static int yaffs_DeleteHardLink(yaffs_Object * in)
+{
+	/* remove this hardlink from the list assocaited with the equivalent
+	 * object
+	 */
+	list_del(&in->hardLinks);
+	return yaffs_DoGenericObjectDeletion(in);
+}
+
+static void yaffs_DestroyObject(yaffs_Object * obj)
+{
+	switch (obj->variantType) {
+	case YAFFS_OBJECT_TYPE_FILE:
+		yaffs_DeleteFile(obj);
+		break;
+	case YAFFS_OBJECT_TYPE_DIRECTORY:
+		yaffs_DeleteDirectory(obj);
+		break;
+	case YAFFS_OBJECT_TYPE_SYMLINK:
+		yaffs_DeleteSymLink(obj);
+		break;
+	case YAFFS_OBJECT_TYPE_HARDLINK:
+		yaffs_DeleteHardLink(obj);
+		break;
+	case YAFFS_OBJECT_TYPE_SPECIAL:
+		yaffs_DoGenericObjectDeletion(obj);
+		break;
+	case YAFFS_OBJECT_TYPE_UNKNOWN:
+		break;		/* should not happen. */
+	}
+}
+
+static int yaffs_UnlinkWorker(yaffs_Object * obj)
+{
+
+	if (obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) {
+		return yaffs_DeleteHardLink(obj);
+	} else if (!list_empty(&obj->hardLinks)) {
+		/* Curve ball: We're unlinking an object that has a hardlink.
+		 *
+		 * This problem arises because we are not strictly following
+		 * The Linux link/inode model.
+		 *
+		 * We can't really delete the object.
+		 * Instead, we do the following:
+		 * - Select a hardlink.
+		 * - Unhook it from the hard links
+		 * - Unhook it from its parent directory (so that the rename can work)
+		 * - Rename the object to the hardlink's name.
+		 * - Delete the hardlink
+		 */
+
+		yaffs_Object *hl;
+		int retVal;
+		YCHAR name[YAFFS_MAX_NAME_LENGTH + 1];
+
+		hl = list_entry(obj->hardLinks.next, yaffs_Object, hardLinks);
+
+		list_del_init(&hl->hardLinks);
+		list_del_init(&hl->siblings);
+
+		yaffs_GetObjectName(hl, name, YAFFS_MAX_NAME_LENGTH + 1);
+
+		retVal = yaffs_ChangeObjectName(obj, hl->parent, name, 0, 0);
+
+		if (retVal == YAFFS_OK) {
+			retVal = yaffs_DoGenericObjectDeletion(hl);
+		}
+		return retVal;
+
+	} else {
+		switch (obj->variantType) {
+		case YAFFS_OBJECT_TYPE_FILE:
+			return yaffs_UnlinkFile(obj);
+			break;
+		case YAFFS_OBJECT_TYPE_DIRECTORY:
+			return yaffs_DeleteDirectory(obj);
+			break;
+		case YAFFS_OBJECT_TYPE_SYMLINK:
+			return yaffs_DeleteSymLink(obj);
+			break;
+		case YAFFS_OBJECT_TYPE_SPECIAL:
+			return yaffs_DoGenericObjectDeletion(obj);
+			break;
+		case YAFFS_OBJECT_TYPE_HARDLINK:
+		case YAFFS_OBJECT_TYPE_UNKNOWN:
+		default:
+			return YAFFS_FAIL;
+		}
+	}
+}
+
+
+static int yaffs_UnlinkObject( yaffs_Object *obj)
+{
+
+	if (obj && obj->unlinkAllowed) {
+		return yaffs_UnlinkWorker(obj);
+	}
+
+	return YAFFS_FAIL;
+
+}
+int yaffs_Unlink(yaffs_Object * dir, const YCHAR * name)
+{
+	yaffs_Object *obj;
+
+	obj = yaffs_FindObjectByName(dir, name);
+	return yaffs_UnlinkObject(obj);
+}
+
+/*----------------------- Initialisation Scanning ---------------------- */
+
+static void yaffs_HandleShadowedObject(yaffs_Device * dev, int objId,
+				       int backwardScanning)
+{
+	yaffs_Object *obj;
+
+	if (!backwardScanning) {
+		/* Handle YAFFS1 forward scanning case
+		 * For YAFFS1 we always do the deletion
+		 */
+
+	} else {
+		/* Handle YAFFS2 case (backward scanning)
+		 * If the shadowed object exists then ignore.
+		 */
+		if (yaffs_FindObjectByNumber(dev, objId)) {
+			return;
+		}
+	}
+
+	/* Let's create it (if it does not exist) assuming it is a file so that it can do shrinking etc.
+	 * We put it in unlinked dir to be cleaned up after the scanning
+	 */
+	obj =
+	    yaffs_FindOrCreateObjectByNumber(dev, objId,
+					     YAFFS_OBJECT_TYPE_FILE);
+	yaffs_AddObjectToDirectory(dev->unlinkedDir, obj);
+	obj->variant.fileVariant.shrinkSize = 0;
+	obj->valid = 1;		/* So that we don't read any other info for this file */
+
+}
+
+typedef struct {
+	int seq;
+	int block;
+} yaffs_BlockIndex;
+
+
+static void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList)
+{
+	yaffs_Object *hl;
+	yaffs_Object *in;
+	
+	while (hardList) {
+		hl = hardList;
+		hardList = (yaffs_Object *) (hardList->hardLinks.next);
+
+		in = yaffs_FindObjectByNumber(dev,
+					      hl->variant.hardLinkVariant.
+					      equivalentObjectId);
+
+		if (in) {
+			/* Add the hardlink pointers */
+			hl->variant.hardLinkVariant.equivalentObject = in;
+			list_add(&hl->hardLinks, &in->hardLinks);
+		} else {
+			/* Todo Need to report/handle this better.
+			 * Got a problem... hardlink to a non-existant object
+			 */
+			hl->variant.hardLinkVariant.equivalentObject = NULL;
+			INIT_LIST_HEAD(&hl->hardLinks);
+
+		}
+
+	}
+
+}
+
+
+
+
+
+static int ybicmp(const void *a, const void *b){
+    register int aseq = ((yaffs_BlockIndex *)a)->seq;
+    register int bseq = ((yaffs_BlockIndex *)b)->seq;
+    register int ablock = ((yaffs_BlockIndex *)a)->block;
+    register int bblock = ((yaffs_BlockIndex *)b)->block;
+    if( aseq == bseq )
+        return ablock - bblock;
+    else
+        return aseq - bseq;
+
+}
+
+static int yaffs_Scan(yaffs_Device * dev)
+{
+	yaffs_ExtendedTags tags;
+	int blk;
+	int blockIterator;
+	int startIterator;
+	int endIterator;
+	int nBlocksToScan = 0;
+	int result;
+
+	int chunk;
+	int c;
+	int deleted;
+	yaffs_BlockState state;
+	yaffs_Object *hardList = NULL;
+	yaffs_Object *hl;
+	yaffs_BlockInfo *bi;
+	int sequenceNumber;
+	yaffs_ObjectHeader *oh;
+	yaffs_Object *in;
+	yaffs_Object *parent;
+	int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
+
+	__u8 *chunkData;
+
+	yaffs_BlockIndex *blockIndex = NULL;
+
+	if (dev->isYaffs2) {
+		T(YAFFS_TRACE_SCAN,
+		  (TSTR("yaffs_Scan is not for YAFFS2!" TENDSTR)));
+		return YAFFS_FAIL;
+	}
+	
+	//TODO  Throw all the yaffs2 stuuf out of yaffs_Scan since it is only for yaffs1 format.
+	
+	T(YAFFS_TRACE_SCAN,
+	  (TSTR("yaffs_Scan starts  intstartblk %d intendblk %d..." TENDSTR),
+	   dev->internalStartBlock, dev->internalEndBlock));
+
+	chunkData = yaffs_GetTempBuffer(dev, __LINE__);
+
+	dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER;
+
+	if (dev->isYaffs2) {
+		blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex));
+	}
+
+	/* Scan all the blocks to determine their state */
+	for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) {
+		bi = yaffs_GetBlockInfo(dev, blk);
+		yaffs_ClearChunkBits(dev, blk);
+		bi->pagesInUse = 0;
+		bi->softDeletions = 0;
+
+		yaffs_QueryInitialBlockState(dev, blk, &state, &sequenceNumber);
+
+		bi->blockState = state;
+		bi->sequenceNumber = sequenceNumber;
+
+		T(YAFFS_TRACE_SCAN_DEBUG,
+		  (TSTR("Block scanning block %d state %d seq %d" TENDSTR), blk,
+		   state, sequenceNumber));
+
+		if (state == YAFFS_BLOCK_STATE_DEAD) {
+			T(YAFFS_TRACE_BAD_BLOCKS,
+			  (TSTR("block %d is bad" TENDSTR), blk));
+		} else if (state == YAFFS_BLOCK_STATE_EMPTY) {
+			T(YAFFS_TRACE_SCAN_DEBUG,
+			  (TSTR("Block empty " TENDSTR)));
+			dev->nErasedBlocks++;
+			dev->nFreeChunks += dev->nChunksPerBlock;
+		} else if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) {
+
+			/* Determine the highest sequence number */
+			if (dev->isYaffs2 &&
+			    sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER &&
+			    sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER) {
+
+				blockIndex[nBlocksToScan].seq = sequenceNumber;
+				blockIndex[nBlocksToScan].block = blk;
+
+				nBlocksToScan++;
+
+				if (sequenceNumber >= dev->sequenceNumber) {
+					dev->sequenceNumber = sequenceNumber;
+				}
+			} else if (dev->isYaffs2) {
+				/* TODO: Nasty sequence number! */
+				T(YAFFS_TRACE_SCAN,
+				  (TSTR
+				   ("Block scanning block %d has bad sequence number %d"
+				    TENDSTR), blk, sequenceNumber));
+
+			}
+		}
+	}
+
+	/* Sort the blocks
+	 * Dungy old bubble sort for now...
+	 */
+	if (dev->isYaffs2) {
+		yaffs_BlockIndex temp;
+		int i;
+		int j;
+
+		for (i = 0; i < nBlocksToScan; i++)
+			for (j = i + 1; j < nBlocksToScan; j++)
+				if (blockIndex[i].seq > blockIndex[j].seq) {
+					temp = blockIndex[j];
+					blockIndex[j] = blockIndex[i];
+					blockIndex[i] = temp;
+				}
+	}
+
+	/* Now scan the blocks looking at the data. */
+	if (dev->isYaffs2) {
+		startIterator = 0;
+		endIterator = nBlocksToScan - 1;
+		T(YAFFS_TRACE_SCAN_DEBUG,
+		  (TSTR("%d blocks to be scanned" TENDSTR), nBlocksToScan));
+	} else {
+		startIterator = dev->internalStartBlock;
+		endIterator = dev->internalEndBlock;
+	}
+
+	/* For each block.... */
+	for (blockIterator = startIterator; blockIterator <= endIterator;
+	     blockIterator++) {
+
+		if (dev->isYaffs2) {
+			/* get the block to scan in the correct order */
+			blk = blockIndex[blockIterator].block;
+		} else {
+			blk = blockIterator;
+		}
+
+		bi = yaffs_GetBlockInfo(dev, blk);
+		state = bi->blockState;
+
+		deleted = 0;
+
+		/* For each chunk in each block that needs scanning....*/
+		for (c = 0; c < dev->nChunksPerBlock &&
+		     state == YAFFS_BLOCK_STATE_NEEDS_SCANNING; c++) {
+			/* Read the tags and decide what to do */
+			chunk = blk * dev->nChunksPerBlock + c;
+
+			result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, NULL,
+							&tags);
+
+			/* Let's have a good look at this chunk... */
+
+			if (!dev->isYaffs2 && tags.chunkDeleted) {
+				/* YAFFS1 only...
+				 * A deleted chunk
+				 */
+				deleted++;
+				dev->nFreeChunks++;
+				/*T((" %d %d deleted\n",blk,c)); */
+			} else if (!tags.chunkUsed) {
+				/* An unassigned chunk in the block
+				 * This means that either the block is empty or 
+				 * this is the one being allocated from
+				 */
+
+				if (c == 0) {
+					/* We're looking at the first chunk in the block so the block is unused */
+					state = YAFFS_BLOCK_STATE_EMPTY;
+					dev->nErasedBlocks++;
+				} else {
+					/* this is the block being allocated from */
+					T(YAFFS_TRACE_SCAN,
+					  (TSTR
+					   (" Allocating from %d %d" TENDSTR),
+					   blk, c));
+					state = YAFFS_BLOCK_STATE_ALLOCATING;
+					dev->allocationBlock = blk;
+					dev->allocationPage = c;
+					dev->allocationBlockFinder = blk;	
+					/* Set it to here to encourage the allocator to go forth from here. */
+					
+					/* Yaffs2 sanity check:
+					 * This should be the one with the highest sequence number
+					 */
+					if (dev->isYaffs2
+					    && (dev->sequenceNumber !=
+						bi->sequenceNumber)) {
+						T(YAFFS_TRACE_ALWAYS,
+						  (TSTR
+						   ("yaffs: Allocation block %d was not highest sequence id:"
+						    " block seq = %d, dev seq = %d"
+						    TENDSTR), blk,bi->sequenceNumber,dev->sequenceNumber));
+					}
+				}
+
+				dev->nFreeChunks += (dev->nChunksPerBlock - c);
+			} else if (tags.chunkId > 0) {
+				/* chunkId > 0 so it is a data chunk... */
+				unsigned int endpos;
+
+				yaffs_SetChunkBit(dev, blk, c);
+				bi->pagesInUse++;
+
+				in = yaffs_FindOrCreateObjectByNumber(dev,
+								      tags.
+								      objectId,
+								      YAFFS_OBJECT_TYPE_FILE);
+				/* PutChunkIntoFile checks for a clash (two data chunks with
+				 * the same chunkId).
+				 */
+				yaffs_PutChunkIntoFile(in, tags.chunkId, chunk,
+						       1);
+				endpos =
+				    (tags.chunkId - 1) * dev->nDataBytesPerChunk +
+				    tags.byteCount;
+				if (in->variantType == YAFFS_OBJECT_TYPE_FILE
+				    && in->variant.fileVariant.scannedFileSize <
+				    endpos) {
+					in->variant.fileVariant.
+					    scannedFileSize = endpos;
+					if (!dev->useHeaderFileSize) {
+						in->variant.fileVariant.
+						    fileSize =
+						    in->variant.fileVariant.
+						    scannedFileSize;
+					}
+
+				}
+				/* T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId));   */
+			} else {
+				/* chunkId == 0, so it is an ObjectHeader.
+				 * Thus, we read in the object header and make the object
+				 */
+				yaffs_SetChunkBit(dev, blk, c);
+				bi->pagesInUse++;
+
+				result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk,
+								chunkData,
+								NULL);
+
+				oh = (yaffs_ObjectHeader *) chunkData;
+
+				in = yaffs_FindObjectByNumber(dev,
+							      tags.objectId);
+				if (in && in->variantType != oh->type) {
+					/* This should not happen, but somehow
+					 * Wev'e ended up with an objectId that has been reused but not yet 
+					 * deleted, and worse still it has changed type. Delete the old object.
+					 */
+
+					yaffs_DestroyObject(in);
+
+					in = 0;
+				}
+
+				in = yaffs_FindOrCreateObjectByNumber(dev,
+								      tags.
+								      objectId,
+								      oh->type);
+
+				if (oh->shadowsObject > 0) {
+					yaffs_HandleShadowedObject(dev,
+								   oh->
+								   shadowsObject,
+								   0);
+				}
+
+				if (in->valid) {
+					/* We have already filled this one. We have a duplicate and need to resolve it. */
+
+					unsigned existingSerial = in->serial;
+					unsigned newSerial = tags.serialNumber;
+
+					if (dev->isYaffs2 ||
+					    ((existingSerial + 1) & 3) ==
+					    newSerial) {
+						/* Use new one - destroy the exisiting one */
+						yaffs_DeleteChunk(dev,
+								  in->chunkId,
+								  1, __LINE__);
+						in->valid = 0;
+					} else {
+						/* Use existing - destroy this one. */
+						yaffs_DeleteChunk(dev, chunk, 1,
+								  __LINE__);
+					}
+				}
+
+				if (!in->valid &&
+				    (tags.objectId == YAFFS_OBJECTID_ROOT ||
+				     tags.objectId == YAFFS_OBJECTID_LOSTNFOUND)) {
+					/* We only load some info, don't fiddle with directory structure */
+					in->valid = 1;
+					in->variantType = oh->type;
+
+					in->yst_mode = oh->yst_mode;
+#ifdef CONFIG_YAFFS_WINCE
+					in->win_atime[0] = oh->win_atime[0];
+					in->win_ctime[0] = oh->win_ctime[0];
+					in->win_mtime[0] = oh->win_mtime[0];
+					in->win_atime[1] = oh->win_atime[1];
+					in->win_ctime[1] = oh->win_ctime[1];
+					in->win_mtime[1] = oh->win_mtime[1];
+#else
+					in->yst_uid = oh->yst_uid;
+					in->yst_gid = oh->yst_gid;
+					in->yst_atime = oh->yst_atime;
+					in->yst_mtime = oh->yst_mtime;
+					in->yst_ctime = oh->yst_ctime;
+					in->yst_rdev = oh->yst_rdev;
+#endif
+					in->chunkId = chunk;
+
+				} else if (!in->valid) {
+					/* we need to load this info */
+
+					in->valid = 1;
+					in->variantType = oh->type;
+
+					in->yst_mode = oh->yst_mode;
+#ifdef CONFIG_YAFFS_WINCE
+					in->win_atime[0] = oh->win_atime[0];
+					in->win_ctime[0] = oh->win_ctime[0];
+					in->win_mtime[0] = oh->win_mtime[0];
+					in->win_atime[1] = oh->win_atime[1];
+					in->win_ctime[1] = oh->win_ctime[1];
+					in->win_mtime[1] = oh->win_mtime[1];
+#else
+					in->yst_uid = oh->yst_uid;
+					in->yst_gid = oh->yst_gid;
+					in->yst_atime = oh->yst_atime;
+					in->yst_mtime = oh->yst_mtime;
+					in->yst_ctime = oh->yst_ctime;
+					in->yst_rdev = oh->yst_rdev;
+#endif
+					in->chunkId = chunk;
+
+					yaffs_SetObjectName(in, oh->name);
+					in->dirty = 0;
+
+					/* directory stuff...
+					 * hook up to parent
+					 */
+
+					parent =
+					    yaffs_FindOrCreateObjectByNumber
+					    (dev, oh->parentObjectId,
+					     YAFFS_OBJECT_TYPE_DIRECTORY);
+					if (parent->variantType ==
+					    YAFFS_OBJECT_TYPE_UNKNOWN) {
+						/* Set up as a directory */
+						parent->variantType =
+						    YAFFS_OBJECT_TYPE_DIRECTORY;
+						INIT_LIST_HEAD(&parent->variant.
+							       directoryVariant.
+							       children);
+					} else if (parent->variantType !=
+						   YAFFS_OBJECT_TYPE_DIRECTORY)
+					{
+						/* Hoosterman, another problem....
+						 * We're trying to use a non-directory as a directory
+						 */
+
+						T(YAFFS_TRACE_ERROR,
+						  (TSTR
+						   ("yaffs tragedy: attempting to use non-directory as"
+						    " a directory in scan. Put in lost+found."
+						    TENDSTR)));
+						parent = dev->lostNFoundDir;
+					}
+
+					yaffs_AddObjectToDirectory(parent, in);
+
+					if (0 && (parent == dev->deletedDir ||
+						  parent == dev->unlinkedDir)) {
+						in->deleted = 1;	/* If it is unlinked at start up then it wants deleting */
+						dev->nDeletedFiles++;
+					}
+					/* Note re hardlinks.
+					 * Since we might scan a hardlink before its equivalent object is scanned
+					 * we put them all in a list.
+					 * After scanning is complete, we should have all the objects, so we run through this
+					 * list and fix up all the chains.              
+					 */
+
+					switch (in->variantType) {
+					case YAFFS_OBJECT_TYPE_UNKNOWN:	
+						/* Todo got a problem */
+						break;
+					case YAFFS_OBJECT_TYPE_FILE:
+						if (dev->isYaffs2
+						    && oh->isShrink) {
+							/* Prune back the shrunken chunks */
+							yaffs_PruneResizedChunks
+							    (in, oh->fileSize);
+							/* Mark the block as having a shrinkHeader */
+							bi->hasShrinkHeader = 1;
+						}
+
+						if (dev->useHeaderFileSize)
+
+							in->variant.fileVariant.
+							    fileSize =
+							    oh->fileSize;
+
+						break;
+					case YAFFS_OBJECT_TYPE_HARDLINK:
+						in->variant.hardLinkVariant.
+						    equivalentObjectId =
+						    oh->equivalentObjectId;
+						in->hardLinks.next =
+						    (struct list_head *)
+						    hardList;
+						hardList = in;
+						break;
+					case YAFFS_OBJECT_TYPE_DIRECTORY:
+						/* Do nothing */
+						break;
+					case YAFFS_OBJECT_TYPE_SPECIAL:
+						/* Do nothing */
+						break;
+					case YAFFS_OBJECT_TYPE_SYMLINK:	
+						in->variant.symLinkVariant.
+						    alias =
+						    yaffs_CloneString(oh->alias);
+						break;
+					}
+
+					if (parent == dev->deletedDir) {
+						yaffs_DestroyObject(in);
+						bi->hasShrinkHeader = 1;
+					}
+				}
+			}
+		}
+
+		if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) {
+			/* If we got this far while scanning, then the block is fully allocated.*/
+			state = YAFFS_BLOCK_STATE_FULL;
+		}
+
+		bi->blockState = state;
+
+		/* Now let's see if it was dirty */
+		if (bi->pagesInUse == 0 &&
+		    !bi->hasShrinkHeader &&
+		    bi->blockState == YAFFS_BLOCK_STATE_FULL) {
+			yaffs_BlockBecameDirty(dev, blk);
+		}
+
+	}
+
+	if (blockIndex) {
+		YFREE(blockIndex);
+	}
+	
+	
+	/* Ok, we've done all the scanning.
+	 * Fix up the hard link chains.
+	 * We should now have scanned all the objects, now it's time to add these 
+	 * hardlinks.
+	 */
+
+	yaffs_HardlinkFixup(dev,hardList);
+
+	/* Handle the unlinked files. Since they were left in an unlinked state we should
+	 * just delete them.
+	 */
+	{
+		struct list_head *i;
+		struct list_head *n;
+
+		yaffs_Object *l;
+		/* Soft delete all the unlinked files */
+		list_for_each_safe(i, n,
+				   &dev->unlinkedDir->variant.directoryVariant.
+				   children) {
+			if (i) {
+				l = list_entry(i, yaffs_Object, siblings);
+				yaffs_DestroyObject(l);
+			}
+		}
+	}
+
+	yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__);
+
+	T(YAFFS_TRACE_SCAN, (TSTR("yaffs_Scan ends" TENDSTR)));
+
+	return YAFFS_OK;
+}
+
+static void yaffs_CheckObjectDetailsLoaded(yaffs_Object *in)
+{
+	__u8 *chunkData;
+	yaffs_ObjectHeader *oh;
+	yaffs_Device *dev = in->myDev;
+	yaffs_ExtendedTags tags;
+	int result;
+	
+#if 0
+	T(YAFFS_TRACE_SCAN,(TSTR("details for object %d %s loaded" TENDSTR),
+		in->objectId,
+		in->lazyLoaded ? "not yet" : "already"));
+#endif
+		
+	if(in->lazyLoaded){
+		in->lazyLoaded = 0;
+		chunkData = yaffs_GetTempBuffer(dev, __LINE__);
+
+		result = yaffs_ReadChunkWithTagsFromNAND(dev,in->chunkId,chunkData,&tags);
+		oh = (yaffs_ObjectHeader *) chunkData;		
+
+		in->yst_mode = oh->yst_mode;
+#ifdef CONFIG_YAFFS_WINCE
+		in->win_atime[0] = oh->win_atime[0];
+		in->win_ctime[0] = oh->win_ctime[0];
+		in->win_mtime[0] = oh->win_mtime[0];
+		in->win_atime[1] = oh->win_atime[1];
+		in->win_ctime[1] = oh->win_ctime[1];
+		in->win_mtime[1] = oh->win_mtime[1];
+#else
+		in->yst_uid = oh->yst_uid;
+		in->yst_gid = oh->yst_gid;
+		in->yst_atime = oh->yst_atime;
+		in->yst_mtime = oh->yst_mtime;
+		in->yst_ctime = oh->yst_ctime;
+		in->yst_rdev = oh->yst_rdev;
+		
+#endif
+		yaffs_SetObjectName(in, oh->name);
+		
+		if(in->variantType == YAFFS_OBJECT_TYPE_SYMLINK)
+			 in->variant.symLinkVariant.alias =
+						    yaffs_CloneString(oh->alias);
+						    
+		yaffs_ReleaseTempBuffer(dev,chunkData, __LINE__);
+	}
+}
+
+static int yaffs_ScanBackwards(yaffs_Device * dev)
+{
+	yaffs_ExtendedTags tags;
+	int blk;
+	int blockIterator;
+	int startIterator;
+	int endIterator;
+	int nBlocksToScan = 0;
+
+	int chunk;
+	int result;
+	int c;
+	int deleted;
+	yaffs_BlockState state;
+	yaffs_Object *hardList = NULL;
+	yaffs_BlockInfo *bi;
+	int sequenceNumber;
+	yaffs_ObjectHeader *oh;
+	yaffs_Object *in;
+	yaffs_Object *parent;
+	int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
+	int itsUnlinked;
+	__u8 *chunkData;
+	
+	int fileSize;
+	int isShrink;
+	int foundChunksInBlock;
+	int equivalentObjectId;
+	
+
+	yaffs_BlockIndex *blockIndex = NULL;
+	int altBlockIndex = 0;
+
+	if (!dev->isYaffs2) {
+		T(YAFFS_TRACE_SCAN,
+		  (TSTR("yaffs_ScanBackwards is only for YAFFS2!" TENDSTR)));
+		return YAFFS_FAIL;
+	}
+
+	T(YAFFS_TRACE_SCAN,
+	  (TSTR
+	   ("yaffs_ScanBackwards starts  intstartblk %d intendblk %d..."
+	    TENDSTR), dev->internalStartBlock, dev->internalEndBlock));
+
+
+	dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER;
+
+	blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex));
+	
+	if(!blockIndex) {
+		blockIndex = YMALLOC_ALT(nBlocks * sizeof(yaffs_BlockIndex));
+		altBlockIndex = 1;
+	}
+	
+	if(!blockIndex) {
+		T(YAFFS_TRACE_SCAN,
+		  (TSTR("yaffs_Scan() could not allocate block index!" TENDSTR)));
+		return YAFFS_FAIL;
+	}
+	
+	chunkData = yaffs_GetTempBuffer(dev, __LINE__);
+
+	/* Scan all the blocks to determine their state */
+	for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) {
+		bi = yaffs_GetBlockInfo(dev, blk);
+		yaffs_ClearChunkBits(dev, blk);
+		bi->pagesInUse = 0;
+		bi->softDeletions = 0;
+
+		yaffs_QueryInitialBlockState(dev, blk, &state, &sequenceNumber);
+
+		bi->blockState = state;
+		bi->sequenceNumber = sequenceNumber;
+
+		if(bi->sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA)
+			bi->blockState = state = YAFFS_BLOCK_STATE_CHECKPOINT;
+			
+		T(YAFFS_TRACE_SCAN_DEBUG,
+		  (TSTR("Block scanning block %d state %d seq %d" TENDSTR), blk,
+		   state, sequenceNumber));
+
+		
+		if(state == YAFFS_BLOCK_STATE_CHECKPOINT){
+			/* todo .. fix free space ? */
+			
+		} else if (state == YAFFS_BLOCK_STATE_DEAD) {
+			T(YAFFS_TRACE_BAD_BLOCKS,
+			  (TSTR("block %d is bad" TENDSTR), blk));
+		} else if (state == YAFFS_BLOCK_STATE_EMPTY) {
+			T(YAFFS_TRACE_SCAN_DEBUG,
+			  (TSTR("Block empty " TENDSTR)));
+			dev->nErasedBlocks++;
+			dev->nFreeChunks += dev->nChunksPerBlock;
+		} else if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) {
+
+			/* Determine the highest sequence number */
+			if (dev->isYaffs2 &&
+			    sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER &&
+			    sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER) {
+
+				blockIndex[nBlocksToScan].seq = sequenceNumber;
+				blockIndex[nBlocksToScan].block = blk;
+
+				nBlocksToScan++;
+
+				if (sequenceNumber >= dev->sequenceNumber) {
+					dev->sequenceNumber = sequenceNumber;
+				}
+			} else if (dev->isYaffs2) {
+				/* TODO: Nasty sequence number! */
+				T(YAFFS_TRACE_SCAN,
+				  (TSTR
+				   ("Block scanning block %d has bad sequence number %d"
+				    TENDSTR), blk, sequenceNumber));
+
+			}
+		}
+	}
+
+	T(YAFFS_TRACE_SCAN,
+	(TSTR("%d blocks to be sorted..." TENDSTR), nBlocksToScan));
+
+
+
+	YYIELD();
+
+	/* Sort the blocks */
+#ifndef CONFIG_YAFFS_USE_OWN_SORT
+	{
+		/* Use qsort now. */
+		qsort(blockIndex, nBlocksToScan, sizeof(yaffs_BlockIndex), ybicmp);
+	}
+#else
+	{
+	 	/* Dungy old bubble sort... */
+	 	
+		yaffs_BlockIndex temp;
+		int i;
+		int j;
+
+		for (i = 0; i < nBlocksToScan; i++)
+			for (j = i + 1; j < nBlocksToScan; j++)
+				if (blockIndex[i].seq > blockIndex[j].seq) {
+					temp = blockIndex[j];
+					blockIndex[j] = blockIndex[i];
+					blockIndex[i] = temp;
+				}
+	}
+#endif
+
+	YYIELD();
+
+    	T(YAFFS_TRACE_SCAN, (TSTR("...done" TENDSTR)));
+
+	/* Now scan the blocks looking at the data. */
+	startIterator = 0;
+	endIterator = nBlocksToScan - 1;
+	T(YAFFS_TRACE_SCAN_DEBUG,
+	  (TSTR("%d blocks to be scanned" TENDSTR), nBlocksToScan));
+
+	/* For each block.... backwards */
+	for (blockIterator = endIterator; blockIterator >= startIterator;
+	     blockIterator--) {
+	        /* Cooperative multitasking! This loop can run for so
+		   long that watchdog timers expire. */
+	        YYIELD();
+
+		/* get the block to scan in the correct order */
+		blk = blockIndex[blockIterator].block;
+
+		bi = yaffs_GetBlockInfo(dev, blk);
+		state = bi->blockState;
+
+		deleted = 0;
+
+		/* For each chunk in each block that needs scanning.... */
+		foundChunksInBlock = 0;
+		for (c = dev->nChunksPerBlock - 1; c >= 0 &&
+		     (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING ||
+		      state == YAFFS_BLOCK_STATE_ALLOCATING); c--) {
+			/* Scan backwards... 
+			 * Read the tags and decide what to do
+			 */
+			chunk = blk * dev->nChunksPerBlock + c;
+
+			result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, NULL,
+							&tags);
+
+			/* Let's have a good look at this chunk... */
+
+			if (!tags.chunkUsed) {
+				/* An unassigned chunk in the block.
+				 * If there are used chunks after this one, then
+				 * it is a chunk that was skipped due to failing the erased
+				 * check. Just skip it so that it can be deleted.
+				 * But, more typically, We get here when this is an unallocated
+				 * chunk and his means that either the block is empty or 
+				 * this is the one being allocated from
+				 */
+
+				if(foundChunksInBlock)
+				{
+					/* This is a chunk that was skipped due to failing the erased check */
+					
+				} else if (c == 0) {
+					/* We're looking at the first chunk in the block so the block is unused */
+					state = YAFFS_BLOCK_STATE_EMPTY;
+					dev->nErasedBlocks++;
+				} else {
+					if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING ||
+					    state == YAFFS_BLOCK_STATE_ALLOCATING) {
+					    	if(dev->sequenceNumber == bi->sequenceNumber) {
+							/* this is the block being allocated from */
+					    	
+							T(YAFFS_TRACE_SCAN,
+							  (TSTR
+							   (" Allocating from %d %d"
+							    TENDSTR), blk, c));
+
+							state = YAFFS_BLOCK_STATE_ALLOCATING;
+							dev->allocationBlock = blk;
+							dev->allocationPage = c;
+							dev->allocationBlockFinder = blk;	
+						}
+						else {
+							/* This is a partially written block that is not
+							 * the current allocation block. This block must have
+							 * had a write failure, so set up for retirement.
+							 */
+						  
+							 bi->needsRetiring = 1;
+							 bi->gcPrioritise = 1;
+							 						 
+							 T(YAFFS_TRACE_ALWAYS,
+							 (TSTR("Partially written block %d being set for retirement" TENDSTR),
+							 blk));
+						}
+
+					}
+					 
+				}
+
+				dev->nFreeChunks++;
+				
+			} else if (tags.chunkId > 0) {
+				/* chunkId > 0 so it is a data chunk... */
+				unsigned int endpos;
+				__u32 chunkBase =
+				    (tags.chunkId - 1) * dev->nDataBytesPerChunk;
+								
+				foundChunksInBlock = 1;
+
+
+				yaffs_SetChunkBit(dev, blk, c);
+				bi->pagesInUse++;
+
+				in = yaffs_FindOrCreateObjectByNumber(dev,
+								      tags.
+								      objectId,
+								      YAFFS_OBJECT_TYPE_FILE);
+				if (in->variantType == YAFFS_OBJECT_TYPE_FILE
+				    && chunkBase <
+				    in->variant.fileVariant.shrinkSize) {
+					/* This has not been invalidated by a resize */
+					yaffs_PutChunkIntoFile(in, tags.chunkId,
+							       chunk, -1);
+
+					/* File size is calculated by looking at the data chunks if we have not 
+					 * seen an object header yet. Stop this practice once we find an object header.
+					 */
+					endpos =
+					    (tags.chunkId -
+					     1) * dev->nDataBytesPerChunk +
+					    tags.byteCount;
+					    
+					if (!in->valid &&	/* have not got an object header yet */
+					    in->variant.fileVariant.
+					    scannedFileSize < endpos) {
+						in->variant.fileVariant.
+						    scannedFileSize = endpos;
+						in->variant.fileVariant.
+						    fileSize =
+						    in->variant.fileVariant.
+						    scannedFileSize;
+					}
+
+				} else {
+					/* This chunk has been invalidated by a resize, so delete */
+					yaffs_DeleteChunk(dev, chunk, 1, __LINE__);
+
+				}
+			} else {
+				/* chunkId == 0, so it is an ObjectHeader.
+				 * Thus, we read in the object header and make the object
+				 */
+				foundChunksInBlock = 1;
+
+				yaffs_SetChunkBit(dev, blk, c);
+				bi->pagesInUse++;
+
+				oh = NULL;
+				in = NULL;
+
+				if (tags.extraHeaderInfoAvailable) {
+					in = yaffs_FindOrCreateObjectByNumber
+					    (dev, tags.objectId,
+					     tags.extraObjectType);
+				}
+
+				if (!in ||
+#ifdef CONFIG_YAFFS_DISABLE_LAZY_LOAD
+				    !in->valid ||
+#endif
+				    tags.extraShadows ||
+				    (!in->valid &&
+				    (tags.objectId == YAFFS_OBJECTID_ROOT ||
+				     tags.objectId == YAFFS_OBJECTID_LOSTNFOUND))
+				    ) {
+
+					/* If we don't have  valid info then we need to read the chunk
+					 * TODO In future we can probably defer reading the chunk and 
+					 * living with invalid data until needed.
+					 */
+
+					result = yaffs_ReadChunkWithTagsFromNAND(dev,
+									chunk,
+									chunkData,
+									NULL);
+
+					oh = (yaffs_ObjectHeader *) chunkData;
+
+					if (!in)
+						in = yaffs_FindOrCreateObjectByNumber(dev, tags.objectId, oh->type);
+
+				}
+
+				if (!in) {
+					/* TODO Hoosterman we have a problem! */
+					T(YAFFS_TRACE_ERROR,
+					  (TSTR
+					   ("yaffs tragedy: Could not make object for object  %d  "
+					    "at chunk %d during scan"
+					    TENDSTR), tags.objectId, chunk));
+
+				}
+
+				if (in->valid) {
+					/* We have already filled this one.
+					 * We have a duplicate that will be discarded, but 
+					 * we first have to suck out resize info if it is a file.
+					 */
+
+					if ((in->variantType == YAFFS_OBJECT_TYPE_FILE) && 
+					     ((oh && 
+					       oh-> type == YAFFS_OBJECT_TYPE_FILE)||
+					      (tags.extraHeaderInfoAvailable  &&
+					       tags.extraObjectType == YAFFS_OBJECT_TYPE_FILE))
+					    ) {
+						__u32 thisSize =
+						    (oh) ? oh->fileSize : tags.
+						    extraFileLength;
+						__u32 parentObjectId =
+						    (oh) ? oh->
+						    parentObjectId : tags.
+						    extraParentObjectId;
+						unsigned isShrink =
+						    (oh) ? oh->isShrink : tags.
+						    extraIsShrinkHeader;
+
+						/* If it is deleted (unlinked at start also means deleted)
+						 * we treat the file size as being zeroed at this point.
+						 */
+						if (parentObjectId ==
+						    YAFFS_OBJECTID_DELETED
+						    || parentObjectId ==
+						    YAFFS_OBJECTID_UNLINKED) {
+							thisSize = 0;
+							isShrink = 1;
+						}
+
+						if (isShrink &&
+						    in->variant.fileVariant.
+						    shrinkSize > thisSize) {
+							in->variant.fileVariant.
+							    shrinkSize =
+							    thisSize;
+						}
+
+						if (isShrink) {
+							bi->hasShrinkHeader = 1;
+						}
+
+					}
+					/* Use existing - destroy this one. */
+					yaffs_DeleteChunk(dev, chunk, 1, __LINE__);
+
+				}
+
+				if (!in->valid &&
+				    (tags.objectId == YAFFS_OBJECTID_ROOT ||
+				     tags.objectId ==
+				     YAFFS_OBJECTID_LOSTNFOUND)) {
+					/* We only load some info, don't fiddle with directory structure */
+					in->valid = 1;
+					
+					if(oh) {
+						in->variantType = oh->type;
+
+						in->yst_mode = oh->yst_mode;
+#ifdef CONFIG_YAFFS_WINCE
+						in->win_atime[0] = oh->win_atime[0];
+						in->win_ctime[0] = oh->win_ctime[0];
+						in->win_mtime[0] = oh->win_mtime[0];
+						in->win_atime[1] = oh->win_atime[1];
+						in->win_ctime[1] = oh->win_ctime[1];
+						in->win_mtime[1] = oh->win_mtime[1];
+#else
+						in->yst_uid = oh->yst_uid;
+						in->yst_gid = oh->yst_gid;
+						in->yst_atime = oh->yst_atime;
+						in->yst_mtime = oh->yst_mtime;
+						in->yst_ctime = oh->yst_ctime;
+						in->yst_rdev = oh->yst_rdev;
+		
+#endif
+					} else {
+						in->variantType = tags.extraObjectType;
+						in->lazyLoaded = 1;
+					}
+						
+					in->chunkId = chunk;
+
+				} else if (!in->valid) {
+					/* we need to load this info */
+
+					in->valid = 1;
+					in->chunkId = chunk;
+					
+					if(oh) {
+						in->variantType = oh->type;
+
+						in->yst_mode = oh->yst_mode;
+#ifdef CONFIG_YAFFS_WINCE
+						in->win_atime[0] = oh->win_atime[0];
+						in->win_ctime[0] = oh->win_ctime[0];
+						in->win_mtime[0] = oh->win_mtime[0];
+						in->win_atime[1] = oh->win_atime[1];
+						in->win_ctime[1] = oh->win_ctime[1];
+						in->win_mtime[1] = oh->win_mtime[1];
+#else
+						in->yst_uid = oh->yst_uid;
+						in->yst_gid = oh->yst_gid;
+						in->yst_atime = oh->yst_atime;
+						in->yst_mtime = oh->yst_mtime;
+						in->yst_ctime = oh->yst_ctime;
+						in->yst_rdev = oh->yst_rdev;
+#endif
+
+						if (oh->shadowsObject > 0) 
+							yaffs_HandleShadowedObject(dev,
+									   oh->
+									   shadowsObject,
+									   1);
+					
+
+						yaffs_SetObjectName(in, oh->name);
+						parent =
+						    yaffs_FindOrCreateObjectByNumber
+					    		(dev, oh->parentObjectId,
+					     		 YAFFS_OBJECT_TYPE_DIRECTORY);
+
+						 fileSize = oh->fileSize;
+ 						 isShrink = oh->isShrink;
+						 equivalentObjectId = oh->equivalentObjectId;
+
+					}
+					else {
+						in->variantType = tags.extraObjectType;
+						parent =
+						    yaffs_FindOrCreateObjectByNumber
+					    		(dev, tags.extraParentObjectId,
+					     		 YAFFS_OBJECT_TYPE_DIRECTORY);
+						 fileSize = tags.extraFileLength;
+						 isShrink = tags.extraIsShrinkHeader;
+						 equivalentObjectId = tags.extraEquivalentObjectId;
+						in->lazyLoaded = 1;
+
+					}
+					in->dirty = 0;
+
+					/* directory stuff...
+					 * hook up to parent
+					 */
+
+					if (parent->variantType ==
+					    YAFFS_OBJECT_TYPE_UNKNOWN) {
+						/* Set up as a directory */
+						parent->variantType =
+						    YAFFS_OBJECT_TYPE_DIRECTORY;
+						INIT_LIST_HEAD(&parent->variant.
+							       directoryVariant.
+							       children);
+					} else if (parent->variantType !=
+						   YAFFS_OBJECT_TYPE_DIRECTORY)
+					{
+						/* Hoosterman, another problem....
+						 * We're trying to use a non-directory as a directory
+						 */
+
+						T(YAFFS_TRACE_ERROR,
+						  (TSTR
+						   ("yaffs tragedy: attempting to use non-directory as"
+						    " a directory in scan. Put in lost+found."
+						    TENDSTR)));
+						parent = dev->lostNFoundDir;
+					}
+
+					yaffs_AddObjectToDirectory(parent, in);
+
+					itsUnlinked = (parent == dev->deletedDir) ||
+						      (parent == dev->unlinkedDir);
+
+					if (isShrink) {
+						/* Mark the block as having a shrinkHeader */
+						bi->hasShrinkHeader = 1;
+					}
+
+					/* Note re hardlinks.
+					 * Since we might scan a hardlink before its equivalent object is scanned
+					 * we put them all in a list.
+					 * After scanning is complete, we should have all the objects, so we run
+					 * through this list and fix up all the chains.              
+					 */
+
+					switch (in->variantType) {
+					case YAFFS_OBJECT_TYPE_UNKNOWN:	
+						/* Todo got a problem */
+						break;
+					case YAFFS_OBJECT_TYPE_FILE:
+
+						if (in->variant.fileVariant.
+						    scannedFileSize < fileSize) {
+							/* This covers the case where the file size is greater
+							 * than where the data is
+							 * This will happen if the file is resized to be larger 
+							 * than its current data extents.
+							 */
+							in->variant.fileVariant.fileSize = fileSize;
+							in->variant.fileVariant.scannedFileSize =
+							    in->variant.fileVariant.fileSize;
+						}
+
+						if (isShrink &&
+						    in->variant.fileVariant.shrinkSize > fileSize) {
+							in->variant.fileVariant.shrinkSize = fileSize;
+						}
+
+						break;
+					case YAFFS_OBJECT_TYPE_HARDLINK:
+						if(!itsUnlinked) {
+						  in->variant.hardLinkVariant.equivalentObjectId =
+						    equivalentObjectId;
+						  in->hardLinks.next =
+						    (struct list_head *) hardList;
+						  hardList = in;
+						}
+						break;
+					case YAFFS_OBJECT_TYPE_DIRECTORY:
+						/* Do nothing */
+						break;
+					case YAFFS_OBJECT_TYPE_SPECIAL:
+						/* Do nothing */
+						break;
+					case YAFFS_OBJECT_TYPE_SYMLINK:
+						if(oh)
+						   in->variant.symLinkVariant.alias =
+						    yaffs_CloneString(oh->
+								      alias);
+						break;
+					}
+
+				}
+			}
+		}
+
+		if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) {
+			/* If we got this far while scanning, then the block is fully allocated. */
+			state = YAFFS_BLOCK_STATE_FULL;
+		}
+
+		bi->blockState = state;
+
+		/* Now let's see if it was dirty */
+		if (bi->pagesInUse == 0 &&
+		    !bi->hasShrinkHeader &&
+		    bi->blockState == YAFFS_BLOCK_STATE_FULL) {
+			yaffs_BlockBecameDirty(dev, blk);
+		}
+
+	}
+
+	if (altBlockIndex) 
+		YFREE_ALT(blockIndex);
+	else
+		YFREE(blockIndex);
+	
+	/* Ok, we've done all the scanning.
+	 * Fix up the hard link chains.
+	 * We should now have scanned all the objects, now it's time to add these 
+	 * hardlinks.
+	 */
+	yaffs_HardlinkFixup(dev,hardList);
+	
+	
+	/*
+	*  Sort out state of unlinked and deleted objects.
+	*/
+	{
+		struct list_head *i;
+		struct list_head *n;
+
+		yaffs_Object *l;
+
+		/* Soft delete all the unlinked files */
+		list_for_each_safe(i, n,
+				   &dev->unlinkedDir->variant.directoryVariant.
+				   children) {
+			if (i) {
+				l = list_entry(i, yaffs_Object, siblings);
+				yaffs_DestroyObject(l);
+			}
+		}
+
+		/* Soft delete all the deletedDir files */
+		list_for_each_safe(i, n,
+				   &dev->deletedDir->variant.directoryVariant.
+				   children) {
+			if (i) {
+				l = list_entry(i, yaffs_Object, siblings);
+				yaffs_DestroyObject(l);
+
+			}
+		}
+	}
+
+	yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__);
+
+	T(YAFFS_TRACE_SCAN, (TSTR("yaffs_ScanBackwards ends" TENDSTR)));
+
+	return YAFFS_OK;
+}
+
+/*------------------------------  Directory Functions ----------------------------- */
+
+static void yaffs_RemoveObjectFromDirectory(yaffs_Object * obj)
+{
+	yaffs_Device *dev = obj->myDev;
+	
+	if(dev && dev->removeObjectCallback)
+		dev->removeObjectCallback(obj);
+	   
+	list_del_init(&obj->siblings);
+	obj->parent = NULL;
+}
+
+
+static void yaffs_AddObjectToDirectory(yaffs_Object * directory,
+				       yaffs_Object * obj)
+{
+
+	if (!directory) {
+		T(YAFFS_TRACE_ALWAYS,
+		  (TSTR
+		   ("tragedy: Trying to add an object to a null pointer directory"
+		    TENDSTR)));
+		YBUG();
+	}
+	if (directory->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) {
+		T(YAFFS_TRACE_ALWAYS,
+		  (TSTR
+		   ("tragedy: Trying to add an object to a non-directory"
+		    TENDSTR)));
+		YBUG();
+	}
+
+	if (obj->siblings.prev == NULL) {
+		/* Not initialised */
+		INIT_LIST_HEAD(&obj->siblings);
+
+	} else if (!list_empty(&obj->siblings)) {
+		/* If it is holed up somewhere else, un hook it */
+		yaffs_RemoveObjectFromDirectory(obj);
+	}
+	/* Now add it */
+	list_add(&obj->siblings, &directory->variant.directoryVariant.children);
+	obj->parent = directory;
+
+	if (directory == obj->myDev->unlinkedDir
+	    || directory == obj->myDev->deletedDir) {
+		obj->unlinked = 1;
+		obj->myDev->nUnlinkedFiles++;
+		obj->renameAllowed = 0;
+	}
+}
+
+yaffs_Object *yaffs_FindObjectByName(yaffs_Object * directory,
+				     const YCHAR * name)
+{
+	int sum;
+
+	struct list_head *i;
+	YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1];
+
+	yaffs_Object *l;
+
+	if (!name) {
+		return NULL;
+	}
+
+	if (!directory) {
+		T(YAFFS_TRACE_ALWAYS,
+		  (TSTR
+		   ("tragedy: yaffs_FindObjectByName: null pointer directory"
+		    TENDSTR)));
+		YBUG();
+	}
+	if (directory->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) {
+		T(YAFFS_TRACE_ALWAYS,
+		  (TSTR
+		   ("tragedy: yaffs_FindObjectByName: non-directory" TENDSTR)));
+		YBUG();
+	}
+
+	sum = yaffs_CalcNameSum(name);
+
+	list_for_each(i, &directory->variant.directoryVariant.children) {
+		if (i) {
+			l = list_entry(i, yaffs_Object, siblings);
+			
+			yaffs_CheckObjectDetailsLoaded(l);
+
+			/* Special case for lost-n-found */
+			if (l->objectId == YAFFS_OBJECTID_LOSTNFOUND) {
+				if (yaffs_strcmp(name, YAFFS_LOSTNFOUND_NAME) == 0) {
+					return l;
+				}
+			} else if (yaffs_SumCompare(l->sum, sum) || l->chunkId <= 0)	
+			{
+				/* LostnFound cunk called Objxxx
+				 * Do a real check
+				 */
+				yaffs_GetObjectName(l, buffer,
+						    YAFFS_MAX_NAME_LENGTH);
+				if (yaffs_strcmp(name, buffer) == 0) {
+					return l;
+				}
+
+			}
+		}
+	}
+
+	return NULL;
+}
+
+
+#if 0
+int yaffs_ApplyToDirectoryChildren(yaffs_Object * theDir,
+				   int (*fn) (yaffs_Object *))
+{
+	struct list_head *i;
+	yaffs_Object *l;
+
+	if (!theDir) {
+		T(YAFFS_TRACE_ALWAYS,
+		  (TSTR
+		   ("tragedy: yaffs_FindObjectByName: null pointer directory"
+		    TENDSTR)));
+		YBUG();
+	}
+	if (theDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) {
+		T(YAFFS_TRACE_ALWAYS,
+		  (TSTR
+		   ("tragedy: yaffs_FindObjectByName: non-directory" TENDSTR)));
+		YBUG();
+	}
+
+	list_for_each(i, &theDir->variant.directoryVariant.children) {
+		if (i) {
+			l = list_entry(i, yaffs_Object, siblings);
+			if (l && !fn(l)) {
+				return YAFFS_FAIL;
+			}
+		}
+	}
+
+	return YAFFS_OK;
+
+}
+#endif
+
+/* GetEquivalentObject dereferences any hard links to get to the
+ * actual object.
+ */
+
+yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object * obj)
+{
+	if (obj && obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) {
+		/* We want the object id of the equivalent object, not this one */
+		obj = obj->variant.hardLinkVariant.equivalentObject;
+	}
+	return obj;
+
+}
+
+int yaffs_GetObjectName(yaffs_Object * obj, YCHAR * name, int buffSize)
+{
+	memset(name, 0, buffSize * sizeof(YCHAR));
+	
+	yaffs_CheckObjectDetailsLoaded(obj);
+
+	if (obj->objectId == YAFFS_OBJECTID_LOSTNFOUND) {
+		yaffs_strncpy(name, YAFFS_LOSTNFOUND_NAME, buffSize - 1);
+	} else if (obj->chunkId <= 0) {
+		YCHAR locName[20];
+		/* make up a name */
+		yaffs_sprintf(locName, _Y("%s%d"), YAFFS_LOSTNFOUND_PREFIX,
+			      obj->objectId);
+		yaffs_strncpy(name, locName, buffSize - 1);
+
+	}
+#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
+	else if (obj->shortName[0]) {
+		yaffs_strcpy(name, obj->shortName);
+	}
+#endif
+	else {
+		int result;
+		__u8 *buffer = yaffs_GetTempBuffer(obj->myDev, __LINE__);
+
+		yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *) buffer;
+
+		memset(buffer, 0, obj->myDev->nDataBytesPerChunk);
+
+		if (obj->chunkId >= 0) {
+			result = yaffs_ReadChunkWithTagsFromNAND(obj->myDev,
+							obj->chunkId, buffer,
+							NULL);
+		}
+		yaffs_strncpy(name, oh->name, buffSize - 1);
+
+		yaffs_ReleaseTempBuffer(obj->myDev, buffer, __LINE__);
+	}
+
+	return yaffs_strlen(name);
+}
+
+int yaffs_GetObjectFileLength(yaffs_Object * obj)
+{
+
+	/* Dereference any hard linking */
+	obj = yaffs_GetEquivalentObject(obj);
+
+	if (obj->variantType == YAFFS_OBJECT_TYPE_FILE) {
+		return obj->variant.fileVariant.fileSize;
+	}
+	if (obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) {
+		return yaffs_strlen(obj->variant.symLinkVariant.alias);
+	} else {
+		/* Only a directory should drop through to here */
+		return obj->myDev->nDataBytesPerChunk;
+	}
+}
+
+int yaffs_GetObjectLinkCount(yaffs_Object * obj)
+{
+	int count = 0;
+	struct list_head *i;
+
+	if (!obj->unlinked) {
+		count++;	/* the object itself */
+	}
+	list_for_each(i, &obj->hardLinks) {
+		count++;	/* add the hard links; */
+	}
+	return count;
+
+}
+
+int yaffs_GetObjectInode(yaffs_Object * obj)
+{
+	obj = yaffs_GetEquivalentObject(obj);
+
+	return obj->objectId;
+}
+
+unsigned yaffs_GetObjectType(yaffs_Object * obj)
+{
+	obj = yaffs_GetEquivalentObject(obj);
+
+	switch (obj->variantType) {
+	case YAFFS_OBJECT_TYPE_FILE:
+		return DT_REG;
+		break;
+	case YAFFS_OBJECT_TYPE_DIRECTORY:
+		return DT_DIR;
+		break;
+	case YAFFS_OBJECT_TYPE_SYMLINK:
+		return DT_LNK;
+		break;
+	case YAFFS_OBJECT_TYPE_HARDLINK:
+		return DT_REG;
+		break;
+	case YAFFS_OBJECT_TYPE_SPECIAL:
+		if (S_ISFIFO(obj->yst_mode))
+			return DT_FIFO;
+		if (S_ISCHR(obj->yst_mode))
+			return DT_CHR;
+		if (S_ISBLK(obj->yst_mode))
+			return DT_BLK;
+		if (S_ISSOCK(obj->yst_mode))
+			return DT_SOCK;
+	default:
+		return DT_REG;
+		break;
+	}
+}
+
+YCHAR *yaffs_GetSymlinkAlias(yaffs_Object * obj)
+{
+	obj = yaffs_GetEquivalentObject(obj);
+	if (obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) {
+		return yaffs_CloneString(obj->variant.symLinkVariant.alias);
+	} else {
+		return yaffs_CloneString(_Y(""));
+	}
+}
+
+#ifndef CONFIG_YAFFS_WINCE
+
+int yaffs_SetAttributes(yaffs_Object * obj, struct iattr *attr)
+{
+	unsigned int valid = attr->ia_valid;
+
+	if (valid & ATTR_MODE)
+		obj->yst_mode = attr->ia_mode;
+	if (valid & ATTR_UID)
+		obj->yst_uid = attr->ia_uid;
+	if (valid & ATTR_GID)
+		obj->yst_gid = attr->ia_gid;
+
+	if (valid & ATTR_ATIME)
+		obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime);
+	if (valid & ATTR_CTIME)
+		obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime);
+	if (valid & ATTR_MTIME)
+		obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime);
+
+	if (valid & ATTR_SIZE)
+		yaffs_ResizeFile(obj, attr->ia_size);
+
+	yaffs_UpdateObjectHeader(obj, NULL, 1, 0, 0);
+
+	return YAFFS_OK;
+
+}
+int yaffs_GetAttributes(yaffs_Object * obj, struct iattr *attr)
+{
+	unsigned int valid = 0;
+
+	attr->ia_mode = obj->yst_mode;
+	valid |= ATTR_MODE;
+	attr->ia_uid = obj->yst_uid;
+	valid |= ATTR_UID;
+	attr->ia_gid = obj->yst_gid;
+	valid |= ATTR_GID;
+
+	Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime;
+	valid |= ATTR_ATIME;
+	Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime;
+	valid |= ATTR_CTIME;
+	Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime;
+	valid |= ATTR_MTIME;
+
+	attr->ia_size = yaffs_GetFileSize(obj);
+	valid |= ATTR_SIZE;
+
+	attr->ia_valid = valid;
+
+	return YAFFS_OK;
+
+}
+
+#endif
+
+#if 0
+int yaffs_DumpObject(yaffs_Object * obj)
+{
+	YCHAR name[257];
+
+	yaffs_GetObjectName(obj, name, 256);
+
+	T(YAFFS_TRACE_ALWAYS,
+	  (TSTR
+	   ("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d"
+	    " chunk %d type %d size %d\n"
+	    TENDSTR), obj->objectId, yaffs_GetObjectInode(obj), name,
+	   obj->dirty, obj->valid, obj->serial, obj->sum, obj->chunkId,
+	   yaffs_GetObjectType(obj), yaffs_GetObjectFileLength(obj)));
+
+	return YAFFS_OK;
+}
+#endif
+
+/*---------------------------- Initialisation code -------------------------------------- */
+
+static int yaffs_CheckDevFunctions(const yaffs_Device * dev)
+{
+
+	/* Common functions, gotta have */
+	if (!dev->eraseBlockInNAND || !dev->initialiseNAND)
+		return 0;
+
+#ifdef CONFIG_YAFFS_YAFFS2
+
+	/* Can use the "with tags" style interface for yaffs1 or yaffs2 */
+	if (dev->writeChunkWithTagsToNAND &&
+	    dev->readChunkWithTagsFromNAND &&
+	    !dev->writeChunkToNAND &&
+	    !dev->readChunkFromNAND &&
+	    dev->markNANDBlockBad && dev->queryNANDBlock)
+		return 1;
+#endif
+
+	/* Can use the "spare" style interface for yaffs1 */
+	if (!dev->isYaffs2 &&
+	    !dev->writeChunkWithTagsToNAND &&
+	    !dev->readChunkWithTagsFromNAND &&
+	    dev->writeChunkToNAND &&
+	    dev->readChunkFromNAND &&
+	    !dev->markNANDBlockBad && !dev->queryNANDBlock)
+		return 1;
+
+	return 0;		/* bad */
+}
+
+
+static void yaffs_CreateInitialDirectories(yaffs_Device *dev)
+{
+	/* Initialise the unlinked, deleted, root and lost and found directories */
+	
+	dev->lostNFoundDir = dev->rootDir =  NULL;
+	dev->unlinkedDir = dev->deletedDir = NULL;
+
+	dev->unlinkedDir =
+	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR);
+	dev->deletedDir =
+	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_DELETED, S_IFDIR);
+
+	dev->rootDir =
+	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_ROOT,
+				      YAFFS_ROOT_MODE | S_IFDIR);
+	dev->lostNFoundDir =
+	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_LOSTNFOUND,
+				      YAFFS_LOSTNFOUND_MODE | S_IFDIR);
+	yaffs_AddObjectToDirectory(dev->rootDir, dev->lostNFoundDir);
+}
+
+int yaffs_GutsInitialise(yaffs_Device * dev)
+{
+	unsigned x;
+	int bits;
+
+	T(YAFFS_TRACE_TRACING, (TSTR("yaffs: yaffs_GutsInitialise()" TENDSTR)));
+
+	/* Check stuff that must be set */
+
+	if (!dev) {
+		T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: Need a device" TENDSTR)));
+		return YAFFS_FAIL;
+	}
+
+	dev->internalStartBlock = dev->startBlock;
+	dev->internalEndBlock = dev->endBlock;
+	dev->blockOffset = 0;
+	dev->chunkOffset = 0;
+	dev->nFreeChunks = 0;
+
+	if (dev->startBlock == 0) {
+		dev->internalStartBlock = dev->startBlock + 1;
+		dev->internalEndBlock = dev->endBlock + 1;
+		dev->blockOffset = 1;
+		dev->chunkOffset = dev->nChunksPerBlock;
+	}
+
+	/* Check geometry parameters. */
+
+	if ((dev->isYaffs2 && dev->nDataBytesPerChunk < 1024) || 
+	    (!dev->isYaffs2 && dev->nDataBytesPerChunk != 512) || 
+	     dev->nChunksPerBlock < 2 || 
+	     dev->nReservedBlocks < 2 || 
+	     dev->internalStartBlock <= 0 || 
+	     dev->internalEndBlock <= 0 || 
+	     dev->internalEndBlock <= (dev->internalStartBlock + dev->nReservedBlocks + 2)	// otherwise it is too small
+	    ) {
+		T(YAFFS_TRACE_ALWAYS,
+		  (TSTR
+		   ("yaffs: NAND geometry problems: chunk size %d, type is yaffs%s "
+		    TENDSTR), dev->nDataBytesPerChunk, dev->isYaffs2 ? "2" : ""));
+		return YAFFS_FAIL;
+	}
+
+	if (yaffs_InitialiseNAND(dev) != YAFFS_OK) {
+		T(YAFFS_TRACE_ALWAYS,
+		  (TSTR("yaffs: InitialiseNAND failed" TENDSTR)));
+		return YAFFS_FAIL;
+	}
+
+	/* Got the right mix of functions? */
+	if (!yaffs_CheckDevFunctions(dev)) {
+		/* Function missing */
+		T(YAFFS_TRACE_ALWAYS,
+		  (TSTR
+		   ("yaffs: device function(s) missing or wrong\n" TENDSTR)));
+
+		return YAFFS_FAIL;
+	}
+
+	/* This is really a compilation check. */
+	if (!yaffs_CheckStructures()) {
+		T(YAFFS_TRACE_ALWAYS,
+		  (TSTR("yaffs_CheckStructures failed\n" TENDSTR)));
+		return YAFFS_FAIL;
+	}
+
+	if (dev->isMounted) {
+		T(YAFFS_TRACE_ALWAYS,
+		  (TSTR("yaffs: device already mounted\n" TENDSTR)));
+		return YAFFS_FAIL;
+	}
+
+	/* Finished with most checks. One or two more checks happen later on too. */
+
+	dev->isMounted = 1;
+
+
+
+	/* OK now calculate a few things for the device */
+	
+	/*
+	 *  Calculate all the chunk size manipulation numbers: 
+	 */
+	 /* Start off assuming it is a power of 2 */
+	 dev->chunkShift = ShiftDiv(dev->nDataBytesPerChunk);
+	 dev->chunkMask = (1<<dev->chunkShift) - 1;
+
+	 if(dev->nDataBytesPerChunk == (dev->chunkMask + 1)){
+	 	/* Yes it is a power of 2, disable crumbs */
+		dev->crumbMask = 0;
+		dev->crumbShift = 0;
+		dev->crumbsPerChunk = 0;
+	 } else {
+	 	/* Not a power of 2, use crumbs instead */
+		dev->crumbShift = ShiftDiv(sizeof(yaffs_PackedTags2TagsPart));
+		dev->crumbMask = (1<<dev->crumbShift)-1;
+		dev->crumbsPerChunk = dev->nDataBytesPerChunk/(1 << dev->crumbShift);
+		dev->chunkShift = 0;
+		dev->chunkMask = 0;
+	}
+	 	
+
+	/*
+	 * Calculate chunkGroupBits.
+	 * We need to find the next power of 2 > than internalEndBlock
+	 */
+
+	x = dev->nChunksPerBlock * (dev->internalEndBlock + 1);
+	
+	bits = ShiftsGE(x);
+	
+	/* Set up tnode width if wide tnodes are enabled. */
+	if(!dev->wideTnodesDisabled){
+		/* bits must be even so that we end up with 32-bit words */
+		if(bits & 1)
+			bits++;
+		if(bits < 16)
+			dev->tnodeWidth = 16;
+		else
+			dev->tnodeWidth = bits;
+	}
+	else
+		dev->tnodeWidth = 16;
+ 
+	dev->tnodeMask = (1<<dev->tnodeWidth)-1;
+		
+	/* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled),
+	 * so if the bitwidth of the
+	 * chunk range we're using is greater than 16 we need
+	 * to figure out chunk shift and chunkGroupSize
+	 */
+		 
+	if (bits <= dev->tnodeWidth)
+		dev->chunkGroupBits = 0;
+	else
+		dev->chunkGroupBits = bits - dev->tnodeWidth;
+		
+
+	dev->chunkGroupSize = 1 << dev->chunkGroupBits;
+
+	if (dev->nChunksPerBlock < dev->chunkGroupSize) {
+		/* We have a problem because the soft delete won't work if
+		 * the chunk group size > chunks per block.
+		 * This can be remedied by using larger "virtual blocks".
+		 */
+		T(YAFFS_TRACE_ALWAYS,
+		  (TSTR("yaffs: chunk group too large\n" TENDSTR)));
+
+		return YAFFS_FAIL;
+	}
+
+	/* OK, we've finished verifying the device, lets continue with initialisation */
+
+	/* More device initialisation */
+	dev->garbageCollections = 0;
+	dev->passiveGarbageCollections = 0;
+	dev->currentDirtyChecker = 0;
+	dev->bufferedBlock = -1;
+	dev->doingBufferedBlockRewrite = 0;
+	dev->nDeletedFiles = 0;
+	dev->nBackgroundDeletions = 0;
+	dev->nUnlinkedFiles = 0;
+	dev->eccFixed = 0;
+	dev->eccUnfixed = 0;
+	dev->tagsEccFixed = 0;
+	dev->tagsEccUnfixed = 0;
+	dev->nErasureFailures = 0;
+	dev->nErasedBlocks = 0;
+	dev->isDoingGC = 0;
+	dev->hasPendingPrioritisedGCs = 1; /* Assume the worst for now, will get fixed on first GC */
+
+	/* Initialise temporary buffers and caches. */
+	{
+		int i;
+		for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
+			dev->tempBuffer[i].line = 0;	/* not in use */
+			dev->tempBuffer[i].buffer =
+			    YMALLOC_DMA(dev->nDataBytesPerChunk);
+		}
+	}
+	
+	if (dev->nShortOpCaches > 0) {
+		int i;
+
+		if (dev->nShortOpCaches > YAFFS_MAX_SHORT_OP_CACHES) {
+			dev->nShortOpCaches = YAFFS_MAX_SHORT_OP_CACHES;
+		}
+
+		dev->srCache =
+		    YMALLOC(dev->nShortOpCaches * sizeof(yaffs_ChunkCache));
+
+		for (i = 0; i < dev->nShortOpCaches; i++) {
+			dev->srCache[i].object = NULL;
+			dev->srCache[i].lastUse = 0;
+			dev->srCache[i].dirty = 0;
+			dev->srCache[i].data = YMALLOC_DMA(dev->nDataBytesPerChunk);
+		}
+		dev->srLastUse = 0;
+	}
+
+	dev->cacheHits = 0;
+	
+	dev->gcCleanupList = YMALLOC(dev->nChunksPerBlock * sizeof(__u32));
+
+	if (dev->isYaffs2) {
+		dev->useHeaderFileSize = 1;
+	}
+
+	yaffs_InitialiseBlocks(dev);
+	yaffs_InitialiseTnodes(dev);
+	yaffs_InitialiseObjects(dev);
+
+	yaffs_CreateInitialDirectories(dev);
+
+
+	/* Now scan the flash. */
+	if (dev->isYaffs2) {
+		if(yaffs_CheckpointRestore(dev)) {
+			T(YAFFS_TRACE_ALWAYS,
+			  (TSTR("yaffs: restored from checkpoint" TENDSTR)));
+		} else {
+
+			/* Clean up the mess caused by an aborted checkpoint load 
+			 * and scan backwards. 
+			 */
+			yaffs_DeinitialiseBlocks(dev);
+			yaffs_DeinitialiseTnodes(dev);
+			yaffs_DeinitialiseObjects(dev);
+			yaffs_InitialiseBlocks(dev);
+			yaffs_InitialiseTnodes(dev);
+			yaffs_InitialiseObjects(dev);
+			yaffs_CreateInitialDirectories(dev);
+
+			yaffs_ScanBackwards(dev);
+		}
+	}else
+		yaffs_Scan(dev);
+
+	/* Zero out stats */
+	dev->nPageReads = 0;
+	dev->nPageWrites = 0;
+	dev->nBlockErasures = 0;
+	dev->nGCCopies = 0;
+	dev->nRetriedWrites = 0;
+
+	dev->nRetiredBlocks = 0;
+
+	yaffs_VerifyFreeChunks(dev);
+
+	T(YAFFS_TRACE_TRACING,
+	  (TSTR("yaffs: yaffs_GutsInitialise() done.\n" TENDSTR)));
+	return YAFFS_OK;
+
+}
+
+void yaffs_Deinitialise(yaffs_Device * dev)
+{
+	if (dev->isMounted) {
+		int i;
+
+		yaffs_DeinitialiseBlocks(dev);
+		yaffs_DeinitialiseTnodes(dev);
+		yaffs_DeinitialiseObjects(dev);
+		if (dev->nShortOpCaches > 0) {
+
+			for (i = 0; i < dev->nShortOpCaches; i++) {
+				YFREE(dev->srCache[i].data);
+			}
+
+			YFREE(dev->srCache);
+		}
+
+		YFREE(dev->gcCleanupList);
+
+		for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
+			YFREE(dev->tempBuffer[i].buffer);
+		}
+
+		dev->isMounted = 0;
+	}
+
+}
+
+static int yaffs_CountFreeChunks(yaffs_Device * dev)
+{
+	int nFree;
+	int b;
+
+	yaffs_BlockInfo *blk;
+
+	for (nFree = 0, b = dev->internalStartBlock; b <= dev->internalEndBlock;
+	     b++) {
+		blk = yaffs_GetBlockInfo(dev, b);
+
+		switch (blk->blockState) {
+		case YAFFS_BLOCK_STATE_EMPTY:
+		case YAFFS_BLOCK_STATE_ALLOCATING:
+		case YAFFS_BLOCK_STATE_COLLECTING:
+		case YAFFS_BLOCK_STATE_FULL:
+			nFree +=
+			    (dev->nChunksPerBlock - blk->pagesInUse +
+			     blk->softDeletions);
+			break;
+		default:
+			break;
+		}
+
+	}
+
+	return nFree;
+}
+
+int yaffs_GetNumberOfFreeChunks(yaffs_Device * dev)
+{
+	/* This is what we report to the outside world */
+
+	int nFree;
+	int nDirtyCacheChunks;
+	int blocksForCheckpoint;
+
+#if 1
+	nFree = dev->nFreeChunks;
+#else
+	nFree = yaffs_CountFreeChunks(dev);
+#endif
+
+	nFree += dev->nDeletedFiles;
+	
+	/* Now count the number of dirty chunks in the cache and subtract those */
+
+	{
+		int i;
+		for (nDirtyCacheChunks = 0, i = 0; i < dev->nShortOpCaches; i++) {
+			if (dev->srCache[i].dirty)
+				nDirtyCacheChunks++;
+		}
+	}
+
+	nFree -= nDirtyCacheChunks;
+
+	nFree -= ((dev->nReservedBlocks + 1) * dev->nChunksPerBlock);
+	
+	/* Now we figure out how much to reserve for the checkpoint and report that... */
+	blocksForCheckpoint = dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint;
+	if(blocksForCheckpoint < 0)
+		blocksForCheckpoint = 0;
+		
+	nFree -= (blocksForCheckpoint * dev->nChunksPerBlock);
+
+	if (nFree < 0)
+		nFree = 0;
+
+	return nFree;
+
+}
+
+static int yaffs_freeVerificationFailures;
+
+static void yaffs_VerifyFreeChunks(yaffs_Device * dev)
+{
+	int counted = yaffs_CountFreeChunks(dev);
+
+	int difference = dev->nFreeChunks - counted;
+
+	if (difference) {
+		T(YAFFS_TRACE_ALWAYS,
+		  (TSTR("Freechunks verification failure %d %d %d" TENDSTR),
+		   dev->nFreeChunks, counted, difference));
+		yaffs_freeVerificationFailures++;
+	}
+}
+
+/*---------------------------------------- YAFFS test code ----------------------*/
+
+#define yaffs_CheckStruct(structure,syze, name) \
+           if(sizeof(structure) != syze) \
+	       { \
+	         T(YAFFS_TRACE_ALWAYS,(TSTR("%s should be %d but is %d\n" TENDSTR),\
+		 name,syze,sizeof(structure))); \
+	         return YAFFS_FAIL; \
+		}
+
+static int yaffs_CheckStructures(void)
+{
+/*      yaffs_CheckStruct(yaffs_Tags,8,"yaffs_Tags") */
+/*      yaffs_CheckStruct(yaffs_TagsUnion,8,"yaffs_TagsUnion") */
+/*      yaffs_CheckStruct(yaffs_Spare,16,"yaffs_Spare") */
+#ifndef CONFIG_YAFFS_TNODE_LIST_DEBUG
+	yaffs_CheckStruct(yaffs_Tnode, 2 * YAFFS_NTNODES_LEVEL0, "yaffs_Tnode")
+#endif
+	    yaffs_CheckStruct(yaffs_ObjectHeader, 512, "yaffs_ObjectHeader")
+
+	    return YAFFS_OK;
+}
Index: fs/yaffs2/yaffs_guts.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yaffs_guts.h,v
retrieving revision 1.1
retrieving revision 1.4
diff -u -r1.1 -r1.4
--- fs/yaffs2/yaffs_guts.h	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/yaffs_guts.h	24 Apr 2007 19:30:25 -0000	1.4
@@ -1,8 +1,7 @@
 /*
- * YAFFS: Yet another FFS. A NAND-flash specific file system.
- * yaffs_guts.h: Configuration etc for yaffs_guts
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. 
  *
- * Copyright (C) 2002 Aleph One Ltd.
+ * Copyright (C) 2002-2007 Aleph One Ltd.
  *   for Toby Churchill Ltd and Brightstar Engineering
  *
  * Created by Charles Manning <charles@aleph1.co.uk>
@@ -11,10 +10,7 @@
  * it under the terms of the GNU Lesser General Public License version 2.1 as
  * published by the Free Software Foundation.
  *
- *
  * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
- *
- * $Id: yaffs_guts.h,v 1.1 2006/02/09 21:08:29 michael Exp $
  */
 
 #ifndef __YAFFS_GUTS_H__
@@ -34,6 +30,8 @@
  */
 #define YAFFS_MAGIC			0x5941FF53
 
+#define CONFIG_YAFFS_AUTO_YAFFS2        1
+
 #define YAFFS_NTNODES_LEVEL0	  	16
 #define YAFFS_TNODES_LEVEL0_BITS	4
 #define YAFFS_TNODES_LEVEL0_MASK	0xf
@@ -44,8 +42,13 @@
 #define YAFFS_TNODES_MAX_LEVEL		6
 
 #ifndef CONFIG_YAFFS_NO_YAFFS1
+#ifdef CONFIG_MTD2K
+#define YAFFS_BYTES_PER_SPARE		64
+#define YAFFS_BYTES_PER_CHUNK		2048
+#else
 #define YAFFS_BYTES_PER_SPARE		16
 #define YAFFS_BYTES_PER_CHUNK		512
+#endif
 #define YAFFS_CHUNK_SIZE_SHIFT		9
 #define YAFFS_CHUNKS_PER_BLOCK		32
 #define YAFFS_BYTES_PER_BLOCK		(YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK)
@@ -64,8 +67,13 @@
 
 #define YAFFS_NOBJECT_BUCKETS		256
 
+
 #define YAFFS_OBJECT_SPACE		0x40000
 
+#define YAFFS_NCHECKPOINT_OBJECTS	5000
+
+#define YAFFS_CHECKPOINT_VERSION 	2
+
 #ifdef CONFIG_YAFFS_UNICODE
 #define YAFFS_MAX_NAME_LENGTH		127
 #define YAFFS_MAX_ALIAS_LENGTH		79
@@ -76,12 +84,19 @@
 
 #define YAFFS_SHORT_NAME_LENGTH		15
 
-/* Some special object ids */
+/* Some special object ids for pseudo objects */
 #define YAFFS_OBJECTID_ROOT		1
 #define YAFFS_OBJECTID_LOSTNFOUND	2
 #define YAFFS_OBJECTID_UNLINKED		3
 #define YAFFS_OBJECTID_DELETED		4
 
+/* Sseudo object ids for checkpointing */
+#define YAFFS_OBJECTID_SB_HEADER	0x10
+#define YAFFS_OBJECTID_CHECKPOINT_DATA	0x20
+#define YAFFS_SEQUENCE_CHECKPOINT_DATA  0x21
+
+/* */
+
 #define YAFFS_MAX_SHORT_OP_CACHES	20
 
 #define YAFFS_N_TEMP_BUFFERS		4
@@ -231,13 +246,13 @@
 
 	YAFFS_BLOCK_STATE_ALLOCATING,
 	/* This block is partially allocated. 
+	 * At least one page holds valid data.
 	 * This is the one currently being used for page
 	 * allocation. Should never be more than one of these
 	 */
 
 	YAFFS_BLOCK_STATE_FULL,	
 	/* All the pages in this block have been allocated.
-	 * At least one page holds valid data.
 	 */
 
 	YAFFS_BLOCK_STATE_DIRTY,
@@ -245,6 +260,10 @@
 	 * Erase me, reuse me.
 	 */
 
+	YAFFS_BLOCK_STATE_CHECKPOINT,	
+	/* This block is assigned to holding checkpoint data.
+	 */
+
 	YAFFS_BLOCK_STATE_COLLECTING,	
 	/* This block is being garbage collected */
 
@@ -254,11 +273,16 @@
 
 typedef struct {
 
-	int softDeletions:12;	/* number of soft deleted pages */
-	int pagesInUse:12;	/* number of pages in use */
+	int softDeletions:10;	/* number of soft deleted pages */
+	int pagesInUse:10;	/* number of pages in use */
 	yaffs_BlockState blockState:4;	/* One of the above block states */
 	__u32 needsRetiring:1;	/* Data has failed on this block, need to get valid data off */
                         	/* and retire the block. */
+	__u32 skipErasedCheck: 1; /* If this is set we can skip the erased check on this block */
+	__u32 gcPrioritise: 1; 	/* An ECC check or bank check has failed on this block. 
+				   It should be prioritised for GC */
+        __u32 chunkErrorStrikes:3; /* How many times we've had ecc etc failures on this block and tried to reuse it */
+
 #ifdef CONFIG_YAFFS_YAFFS2
 	__u32 hasShrinkHeader:1; /* This block has at least one shrink object header */
 	__u32 sequenceNumber;	 /* block sequence number for yaffs2 */
@@ -386,15 +410,14 @@
 				 * object might be created before the data
 				 * is available (ie. file data records appear before the header).
 				 */
-	__u8 serial;		/* serial number of chunk in NAND. Cached here */
+	__u8 lazyLoaded:1;	/* This object has been lazy loaded and is missing some detail */
 
 	__u8 deferedFree:1;	/* For Linux kernel. Object is removed from NAND, but is
 				 * still in the inode cache. Free of object is defered.
 				 * until the inode is released.
 				 */
 
-	__u8 lazyLoaded;	/* Vital info has been loaded from tags. Not all info available. */
-
+	__u8 serial;		/* serial number of chunk in NAND. Cached here */
 	__u16 sum;		/* sum of the name to speed searching */
 
 	struct yaffs_DeviceStruct *myDev;	/* The device I'm on */
@@ -464,6 +487,31 @@
 	int count;
 } yaffs_ObjectBucket;
 
+
+/* yaffs_CheckpointObject holds the definition of an object as dumped 
+ * by checkpointing.
+ */
+
+typedef struct {
+        int structType;
+	__u32 objectId;		
+	__u32 parentId;
+	int chunkId;
+			
+	yaffs_ObjectType variantType:3;
+	__u8 deleted:1;		
+	__u8 softDeleted:1;	
+	__u8 unlinked:1;	
+	__u8 fake:1;		
+	__u8 renameAllowed:1;
+	__u8 unlinkAllowed:1;
+	__u8 serial;		
+	
+	int nDataChunks;	
+	__u32 fileSizeOrEquivalentObjectId;
+
+}yaffs_CheckpointObject;
+
 /*--------------------- Temporary buffers ----------------
  *
  * These are chunk-sized working buffers. Each device has a few
@@ -482,13 +530,25 @@
 	const char *name;
 
 	/* Entry parameters set up way early. Yaffs sets up the rest.*/
-	int nBytesPerChunk;	/* Should be a power of 2 >= 512 */
+	int nDataBytesPerChunk;	/* Should be a power of 2 >= 512 */
 	int nChunksPerBlock;	/* does not need to be a power of 2 */
 	int nBytesPerSpare;	/* spare area size */
 	int startBlock;		/* Start block we're allowed to use */
 	int endBlock;		/* End block we're allowed to use */
 	int nReservedBlocks;	/* We want this tuneable so that we can reduce */
 				/* reserved blocks on NOR and RAM. */
+	
+	/* Stuff used by the partitioned checkpointing mechanism */
+	int checkpointStartBlock;
+	int checkpointEndBlock;
+	
+	/* Stuff used by the shared space checkpointing mechanism */
+	/* If this value is zero, then this mechanism is disabled */
+	
+	int nCheckpointReservedBlocks; /* Blocks to reserve for checkpoint data */
+
+	
+
 
 	int nShortOpCaches;	/* If <= 0, then short op caching is disabled, else
 				 * the number of short op caches (don't use too many)
@@ -501,6 +561,8 @@
 	void *genericDevice;	/* Pointer to device context
 				 * On an mtd this holds the mtd pointer.
 				 */
+        void *superBlock;
+        
 	/* NAND access functions (Must be set before calling YAFFS)*/
 
 	int (*writeChunkToNAND) (struct yaffs_DeviceStruct * dev,
@@ -533,6 +595,9 @@
 	 */
 	void (*removeObjectCallback)(struct yaffs_ObjectStruct *obj);
 	
+	/* Callback to mark the superblock dirsty */
+	void (*markSuperBlockDirty)(void * superblock);
+	
 	int wideTnodesDisabled; /* Set to disable wide tnodes */
 	
 
@@ -547,6 +612,16 @@
 	__u32 tnodeWidth;
 	__u32 tnodeMask;
 	
+	/* Stuff to support various file offses to chunk/offset translations */
+	/* "Crumbs" for nDataBytesPerChunk not being a power of 2 */
+	__u32 crumbMask;
+	__u32 crumbShift;
+	__u32 crumbsPerChunk;
+	
+	/* Straight shifting for nDataBytesPerChunk being a power of 2 */
+	__u32 chunkShift;
+	__u32 chunkMask;
+	
 
 #ifdef __KERNEL__
 
@@ -559,13 +634,30 @@
 #endif
 
 	int isMounted;
+	
+	int isCheckpointed;
+
 
 	/* Stuff to support block offsetting to support start block zero */
 	int internalStartBlock;
 	int internalEndBlock;
 	int blockOffset;
 	int chunkOffset;
+	
 
+	/* Runtime checkpointing stuff */
+	int checkpointPageSequence;   /* running sequence number of checkpoint pages */
+	int checkpointByteCount;
+	int checkpointByteOffset;
+	__u8 *checkpointBuffer;
+	int checkpointOpenForWrite;
+	int blocksInCheckpoint;
+	int checkpointCurrentChunk;
+	int checkpointCurrentBlock;
+	int checkpointNextBlock;
+	int *checkpointBlockList;
+	int checkpointMaxBlocks;
+	
 	/* Block Info */
 	yaffs_BlockInfo *blockInfo;
 	__u8 *chunkBits;	/* bitmap of chunks in use */
@@ -618,6 +710,8 @@
 	int tagsEccUnfixed;
 	int nDeletions;
 	int nUnmarkedDeletions;
+	
+	int hasPendingPrioritisedGCs; /* We think this device might have pending prioritised gcs */
 
 	/* Special directories */
 	yaffs_Object *rootDir;
@@ -658,6 +752,45 @@
 
 typedef struct yaffs_DeviceStruct yaffs_Device;
 
+/* The static layout of bllock usage etc is stored in the super block header */
+typedef struct {
+        int StructType;
+	int version;
+	int checkpointStartBlock;
+	int checkpointEndBlock;
+	int startBlock;
+	int endBlock;
+	int rfu[100];
+} yaffs_SuperBlockHeader;
+	
+/* The CheckpointDevice structure holds the device information that changes at runtime and
+ * must be preserved over unmount/mount cycles.
+ */
+typedef struct {
+        int structType;
+	int nErasedBlocks;
+	int allocationBlock;	/* Current block being allocated off */
+	__u32 allocationPage;
+	int nFreeChunks;
+
+	int nDeletedFiles;		/* Count of files awaiting deletion;*/
+	int nUnlinkedFiles;		/* Count of unlinked files. */
+	int nBackgroundDeletions;	/* Count of background deletions. */
+
+	/* yaffs2 runtime stuff */
+	unsigned sequenceNumber;	/* Sequence number of currently allocating block */
+	unsigned oldestDirtySequence;
+
+} yaffs_CheckpointDevice;
+
+
+typedef struct {
+    int structType;
+    __u32 magic;
+    __u32 version;
+    __u32 head;
+} yaffs_CheckpointValidity;
+
 /* Function to manipulate block info */
 static Y_INLINE yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blk)
 {
@@ -694,16 +827,22 @@
 int yaffs_GetAttributes(yaffs_Object * obj, struct iattr *attr);
 
 /* File operations */
-int yaffs_ReadDataFromFile(yaffs_Object * obj, __u8 * buffer, __u32 offset,
+int yaffs_ReadDataFromFile(yaffs_Object * obj, __u8 * buffer, loff_t offset,
 			   int nBytes);
-int yaffs_WriteDataToFile(yaffs_Object * obj, const __u8 * buffer, __u32 offset,
+int yaffs_WriteDataToFile(yaffs_Object * obj, const __u8 * buffer, loff_t offset,
 			  int nBytes, int writeThrough);
-int yaffs_ResizeFile(yaffs_Object * obj, int newSize);
+int yaffs_ResizeFile(yaffs_Object * obj, loff_t newSize);
 
 yaffs_Object *yaffs_MknodFile(yaffs_Object * parent, const YCHAR * name,
 			      __u32 mode, __u32 uid, __u32 gid);
 int yaffs_FlushFile(yaffs_Object * obj, int updateTime);
 
+/* Flushing and checkpointing */
+void yaffs_FlushEntireDeviceCache(yaffs_Device *dev);
+
+int yaffs_CheckpointSave(yaffs_Device *dev);
+int yaffs_CheckpointRestore(yaffs_Device *dev);
+
 /* Directory operations */
 yaffs_Object *yaffs_MknodDirectory(yaffs_Object * parent, const YCHAR * name,
 				   __u32 mode, __u32 uid, __u32 gid);
@@ -752,5 +891,6 @@
 void yaffs_InitialiseTags(yaffs_ExtendedTags * tags);
 void yaffs_DeleteChunk(yaffs_Device * dev, int chunkId, int markNAND, int lyn);
 int yaffs_CheckFF(__u8 * buffer, int nBytes);
+void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi);
 
 #endif
Index: fs/yaffs2/yaffs_mtdif.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yaffs_mtdif.c,v
retrieving revision 1.1
retrieving revision 1.3
diff -u -r1.1 -r1.3
--- fs/yaffs2/yaffs_mtdif.c	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/yaffs_mtdif.c	24 Apr 2007 19:30:25 -0000	1.3
@@ -1,8 +1,7 @@
 /*
- * YAFFS: Yet another FFS. A NAND-flash specific file system. 
- * yaffs_mtdif.c  NAND mtd wrapper functions.
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
  *
- * Copyright (C) 2002 Aleph One Ltd.
+ * Copyright (C) 2002-2007 Aleph One Ltd.
  *   for Toby Churchill Ltd and Brightstar Engineering
  *
  * Created by Charles Manning <charles@aleph1.co.uk>
@@ -10,11 +9,10 @@
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
- *
  */
 
 const char *yaffs_mtdif_c_version =
-    "$Id: yaffs_mtdif.c,v 1.1 2006/02/09 21:08:29 michael Exp $";
+    "$Id: yaffs_mtdif.c,v 1.3 2007/04/24 19:30:25 eddie Exp $";
 
 #include "yportenv.h"
 
@@ -26,48 +24,110 @@
 #include "linux/time.h"
 #include "linux/mtd/nand.h"
 
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18))
 static struct nand_oobinfo yaffs_oobinfo = {
-        .useecc = 1,
+	.useecc = 1,
 	.eccbytes = 6,
 	.eccpos = {8, 9, 10, 13, 14, 15}
 };
 
 static struct nand_oobinfo yaffs_noeccinfo = {
-	.useecc =0,
+	.useecc = 0,
 };
+#endif
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+static inline void translate_spare2oob(const yaffs_Spare *spare, __u8 *oob)
+{
+	oob[0] = spare->tagByte0;
+	oob[1] = spare->tagByte1;
+	oob[2] = spare->tagByte2;
+	oob[3] = spare->tagByte3;
+	oob[4] = spare->tagByte4;
+	oob[5] = spare->tagByte5 & 0x3f;
+	oob[5] |= spare->blockStatus == 'Y' ? 0: 0x80;
+	oob[5] |= spare->pageStatus == 0 ? 0: 0x40;
+	oob[6] = spare->tagByte6;
+	oob[7] = spare->tagByte7;
+}
+
+static inline void translate_oob2spare(yaffs_Spare *spare, __u8 *oob)
+{
+	struct yaffs_NANDSpare *nspare = (struct yaffs_NANDSpare *)spare;
+	spare->tagByte0 = oob[0];
+	spare->tagByte1 = oob[1];
+	spare->tagByte2 = oob[2];
+	spare->tagByte3 = oob[3];
+	spare->tagByte4 = oob[4];
+	spare->tagByte5 = oob[5] == 0xff ? 0xff : oob[5] & 0x3f;
+	spare->blockStatus = oob[5] & 0x80 ? 0xff : 'Y';
+	spare->pageStatus = oob[5] & 0x40 ? 0xff : 0;
+	spare->ecc1[0] = spare->ecc1[1] = spare->ecc1[2] = 0xff;
+	spare->tagByte6 = oob[6];
+	spare->tagByte7 = oob[7];
+	spare->ecc2[0] = spare->ecc2[1] = spare->ecc2[2] = 0xff;
+
+	nspare->eccres1 = nspare->eccres2 = 0; /* FIXME */
+}
+#endif
 
 int nandmtd_WriteChunkToNAND(yaffs_Device * dev, int chunkInNAND,
 			     const __u8 * data, const yaffs_Spare * spare)
 {
 	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+	struct mtd_oob_ops ops;
+#endif
 	size_t dummy;
 	int retval = 0;
 
-	loff_t addr = ((loff_t) chunkInNAND) * dev->nBytesPerChunk;
-
+	loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk;
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+	__u8 spareAsBytes[8]; /* OOB */
+
+	if (data && !spare)
+		retval = mtd->write(mtd, addr, dev->nDataBytesPerChunk,
+				&dummy, data);
+	else if (spare) {
+		if (dev->useNANDECC) {
+			translate_spare2oob(spare, spareAsBytes);
+			ops.mode = MTD_OOB_AUTO;
+			ops.ooblen = 8; /* temp hack */
+		} else {
+			ops.mode = MTD_OOB_RAW;
+			ops.ooblen = YAFFS_BYTES_PER_SPARE;
+		}
+		ops.len = data ? dev->nDataBytesPerChunk : ops.ooblen;
+		ops.datbuf = (u8 *)data;
+		ops.ooboffs = 0;
+		ops.oobbuf = spareAsBytes;
+		retval = mtd->write_oob(mtd, addr, &ops);
+	}
+#else
 	__u8 *spareAsBytes = (__u8 *) spare;
 
 	if (data && spare) {
 		if (dev->useNANDECC)
 			retval =
-			    mtd->write_ecc(mtd, addr, dev->nBytesPerChunk,
+			    mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk,
 					   &dummy, data, spareAsBytes,
 					   &yaffs_oobinfo);
 		else
 			retval =
-			    mtd->write_ecc(mtd, addr, dev->nBytesPerChunk,
+			    mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk,
 					   &dummy, data, spareAsBytes,
 					   &yaffs_noeccinfo);
 	} else {
 		if (data)
 			retval =
-			    mtd->write(mtd, addr, dev->nBytesPerChunk, &dummy,
+			    mtd->write(mtd, addr, dev->nDataBytesPerChunk, &dummy,
 				       data);
 		if (spare)
 			retval =
 			    mtd->write_oob(mtd, addr, YAFFS_BYTES_PER_SPARE,
 					   &dummy, spareAsBytes);
 	}
+#endif
 
 	if (retval == 0)
 		return YAFFS_OK;
@@ -79,11 +139,36 @@
 			      yaffs_Spare * spare)
 {
 	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+	struct mtd_oob_ops ops;
+#endif
 	size_t dummy;
 	int retval = 0;
 
-	loff_t addr = ((loff_t) chunkInNAND) * dev->nBytesPerChunk;
-
+	loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk;
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
+	__u8 spareAsBytes[8]; /* OOB */
+
+	if (data && !spare)
+		retval = mtd->read(mtd, addr, dev->nDataBytesPerChunk,
+				&dummy, data);
+	else if (spare) {
+		if (dev->useNANDECC) {
+			ops.mode = MTD_OOB_AUTO;
+			ops.ooblen = 8; /* temp hack */
+		} else {
+			ops.mode = MTD_OOB_RAW;
+			ops.ooblen = YAFFS_BYTES_PER_SPARE;
+		}
+		ops.len = data ? dev->nDataBytesPerChunk : ops.ooblen;
+		ops.datbuf = data;
+		ops.ooboffs = 0;
+		ops.oobbuf = spareAsBytes;
+		retval = mtd->read_oob(mtd, addr, &ops);
+		if (dev->useNANDECC)
+			translate_oob2spare(spare, spareAsBytes);
+	}
+#else
 	__u8 *spareAsBytes = (__u8 *) spare;
 
 	if (data && spare) {
@@ -93,25 +178,26 @@
 			/* should allocate enough memory for spare, */
 			/* i.e. [YAFFS_BYTES_PER_SPARE+2*sizeof(int)]. */
 			retval =
-			    mtd->read_ecc(mtd, addr, dev->nBytesPerChunk,
+			    mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk,
 					  &dummy, data, spareAsBytes,
 					  &yaffs_oobinfo);
 		} else {
 			retval =
-			    mtd->read_ecc(mtd, addr, dev->nBytesPerChunk,
+			    mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk,
 					  &dummy, data, spareAsBytes,
 					  &yaffs_noeccinfo);
 		}
 	} else {
 		if (data)
 			retval =
-			    mtd->read(mtd, addr, dev->nBytesPerChunk, &dummy,
+			    mtd->read(mtd, addr, dev->nDataBytesPerChunk, &dummy,
 				      data);
 		if (spare)
 			retval =
 			    mtd->read_oob(mtd, addr, YAFFS_BYTES_PER_SPARE,
 					  &dummy, spareAsBytes);
 	}
+#endif
 
 	if (retval == 0)
 		return YAFFS_OK;
@@ -123,14 +209,14 @@
 {
 	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
 	__u32 addr =
-	    ((loff_t) blockNumber) * dev->nBytesPerChunk
+	    ((loff_t) blockNumber) * dev->nDataBytesPerChunk
 		* dev->nChunksPerBlock;
 	struct erase_info ei;
 	int retval = 0;
 
 	ei.mtd = mtd;
 	ei.addr = addr;
-	ei.len = dev->nBytesPerChunk * dev->nChunksPerBlock;
+	ei.len = dev->nDataBytesPerChunk * dev->nChunksPerBlock;
 	ei.time = 1000;
 	ei.retries = 2;
 	ei.callback = NULL;
Index: fs/yaffs2/yaffs_mtdif.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yaffs_mtdif.h,v
retrieving revision 1.1
retrieving revision 1.3
diff -u -r1.1 -r1.3
--- fs/yaffs2/yaffs_mtdif.h	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/yaffs_mtdif.h	24 Apr 2007 19:30:25 -0000	1.3
@@ -1,8 +1,7 @@
 /*
- * YAFFS: Yet another FFS. A NAND-flash specific file system. 
- * yaffs_mtdif.h  NAND mtd interface wrappers
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. 
  *
- * Copyright (C) 2002 Aleph One Ltd.
+ * Copyright (C) 2002-2007 Aleph One Ltd.
  *   for Toby Churchill Ltd and Brightstar Engineering
  *
  * Created by Charles Manning <charles@aleph1.co.uk>
@@ -11,10 +10,7 @@
  * it under the terms of the GNU Lesser General Public License version 2.1 as
  * published by the Free Software Foundation.
  *
- *
  * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
- *
- * $Id: yaffs_mtdif.h,v 1.1 2006/02/09 21:08:29 michael Exp $
  */
 
 #ifndef __YAFFS_MTDIF_H__
Index: fs/yaffs2/yaffs_mtdif2.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yaffs_mtdif2.c,v
retrieving revision 1.1
retrieving revision 1.4
diff -u -r1.1 -r1.4
--- fs/yaffs2/yaffs_mtdif2.c	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/yaffs_mtdif2.c	24 Apr 2007 19:30:25 -0000	1.4
@@ -1,260 +1,258 @@
-/*
- * YAFFS: Yet another FFS. A NAND-flash specific file system. 
- * yaffs_mtdif.c  NAND mtd wrapper functions.
- *
- * Copyright (C) 2002 Aleph One Ltd.
- *   for Toby Churchill Ltd and Brightstar Engineering
- *
- * Created by Charles Manning <charles@aleph1.co.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-/* mtd interface for YAFFS2 */
-
-const char *yaffs_mtdif2_c_version =
-    "$Id: yaffs_mtdif2.c,v 1.1 2006/02/09 21:08:29 michael Exp $";
-
-#include "yportenv.h"
-
-
-#include "yaffs_mtdif2.h"
-
-#include "linux/mtd/mtd.h"
-#include "linux/types.h"
-#include "linux/time.h"
-
-#include "yaffs_packedtags2.h"
-
-
-void nandmtd2_pt2buf(yaffs_Device *dev, yaffs_PackedTags2 *pt, int is_raw)
-{
-	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
-	__u8 *ptab = (__u8 *)pt; /* packed tags as bytes */
-	
-	int	i, j = 0, k, n;
-
-	/* Pack buffer with 0xff */
-	for (i = 0; i < mtd->oobsize; i++)
-		dev->spareBuffer[i] = 0xff;
-		
-	if(!is_raw){
-		memcpy(dev->spareBuffer,pt,sizeof(yaffs_PackedTags2));
-	} else {
-		j = 0;
-		k = mtd->oobinfo.oobfree[j][0];
-		n = mtd->oobinfo.oobfree[j][1];
-
-		if (n == 0) {
-			T(YAFFS_TRACE_ERROR, (TSTR("No OOB space for tags" TENDSTR)));
-			YBUG();
-		}
-
-		for (i = 0; i < sizeof(yaffs_PackedTags2); i++) {
-			if (n == 0) {
-				j++;
-				k = mtd->oobinfo.oobfree[j][0];
-				n = mtd->oobinfo.oobfree[j][1];
-				if (n == 0) {
-					T(YAFFS_TRACE_ERROR, (TSTR("No OOB space for tags" TENDSTR)));
-					YBUG();
-				}
-			}
-			dev->spareBuffer[k] = ptab[i];
-			k++;
-			n--;
-		}
-	}
-
-}
-
-void nandmtd2_buf2pt(yaffs_Device *dev, yaffs_PackedTags2 *pt, int is_raw)
-{
-	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
-	int	i, j = 0, k, n;
-	__u8 *ptab = (__u8 *)pt; /* packed tags as bytes */
-
-
-	if (!is_raw) {
-	
-		memcpy(pt,dev->spareBuffer,sizeof(yaffs_PackedTags2));
-	} else {
-		j = 0;
-		k = mtd->oobinfo.oobfree[j][0];
-		n = mtd->oobinfo.oobfree[j][1];
-
-		if (n == 0) {
-			T(YAFFS_TRACE_ERROR, (TSTR("No space in OOB for tags" TENDSTR)));
-			YBUG();
-		}
-
-		for (i = 0; i < sizeof(yaffs_PackedTags2); i++) {
-			if (n == 0) {
-				j++;
-				k = mtd->oobinfo.oobfree[j][0];
-				n = mtd->oobinfo.oobfree[j][1];
-				if (n == 0) {
-					T(YAFFS_TRACE_ERROR, (TSTR("No space in OOB for tags" TENDSTR)));
-					YBUG();
-				}
-			}
-			ptab[i] = dev->spareBuffer[k];
-			k++;
-			n--;
-		}
-	}
-		
-}
-
-int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device * dev, int chunkInNAND,
-				      const __u8 * data,
-				      const yaffs_ExtendedTags * tags)
-{
-	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
-	size_t dummy;
-	int retval = 0;
-
-	loff_t addr = ((loff_t) chunkInNAND) * dev->nBytesPerChunk;
-
-	yaffs_PackedTags2 pt;
-
-	T(YAFFS_TRACE_MTD,
-	  (TSTR
-	   ("nandmtd2_WriteChunkWithTagsToNAND chunk %d data %p tags %p"
-	    TENDSTR), chunkInNAND, data, tags));
-
-	if (tags) {
-		yaffs_PackTags2(&pt, tags);
-	}
-
-	if (data && tags) {
-	                nandmtd2_pt2buf(dev, &pt, 0);
-			retval =
-			    mtd->write_ecc(mtd, addr, dev->nBytesPerChunk,
-					   &dummy, data, dev->spareBuffer,
-					   NULL);
-
-	} else {
-	
-		T(YAFFS_TRACE_ALWAYS,
-		  (TSTR
-		  ("Write chunk with null tags or data!" TENDSTR)));
-		YBUG();
- 	}
-
-	if (retval == 0)
-		return YAFFS_OK;
-	else
-		return YAFFS_FAIL;
-}
-
-int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
-				       __u8 * data, yaffs_ExtendedTags * tags)
-{
-	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
-	size_t dummy;
-	int retval = 0;
-
-	loff_t addr = ((loff_t) chunkInNAND) * dev->nBytesPerChunk;
-
-	yaffs_PackedTags2 pt;
-
-	T(YAFFS_TRACE_MTD,
-	  (TSTR
-	   ("nandmtd2_ReadChunkWithTagsToNAND chunk %d data %p tags %p"
-	    TENDSTR), chunkInNAND, data, tags));
-
-	if (0 && data && tags) {
-			retval =
-			    mtd->read_ecc(mtd, addr, dev->nBytesPerChunk,
-					  &dummy, data, dev->spareBuffer,
-					  NULL);
-			nandmtd2_buf2pt(dev, &pt, 0);
-	} else {
-		if (data)
-			retval =
-			    mtd->read(mtd, addr, dev->nBytesPerChunk, &dummy,
-				      data);
-		if (tags) {
-			retval =
-			    mtd->read_oob(mtd, addr, mtd->oobsize, &dummy,
-					  dev->spareBuffer);
-			nandmtd2_buf2pt(dev, &pt, 1);
-		}
-	}
-
-	if (tags)
-		yaffs_UnpackTags2(tags, &pt);
-
-	if (retval == 0)
-		return YAFFS_OK;
-	else
-		return YAFFS_FAIL;
-}
-
-int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo)
-{
-	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
-	int retval;
-	T(YAFFS_TRACE_MTD,
-	  (TSTR("nandmtd2_MarkNANDBlockBad %d" TENDSTR), blockNo));
-
-	retval =
-	    mtd->block_markbad(mtd,
-			       blockNo * dev->nChunksPerBlock *
-			       dev->nBytesPerChunk);
-
-	if (retval == 0)
-		return YAFFS_OK;
-	else
-		return YAFFS_FAIL;
-
-}
-
-int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo,
-			    yaffs_BlockState * state, int *sequenceNumber)
-{
-	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
-	int retval;
-
-	T(YAFFS_TRACE_MTD,
-	  (TSTR("nandmtd2_QueryNANDBlock %d" TENDSTR), blockNo));
-	retval =
-	    mtd->block_isbad(mtd,
-			     blockNo * dev->nChunksPerBlock *
-			     dev->nBytesPerChunk);
-
-	if (retval) {
-		T(YAFFS_TRACE_MTD, (TSTR("block is bad" TENDSTR)));
-
-		*state = YAFFS_BLOCK_STATE_DEAD;
-		*sequenceNumber = 0;
-	} else {
-		yaffs_ExtendedTags t;
-		nandmtd2_ReadChunkWithTagsFromNAND(dev,
-						   blockNo *
-						   dev->nChunksPerBlock, NULL,
-						   &t);
-
-		if (t.chunkUsed) {
-			*sequenceNumber = t.sequenceNumber;
-			*state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
-		} else {
-			*sequenceNumber = 0;
-			*state = YAFFS_BLOCK_STATE_EMPTY;
-		}
-
-		T(YAFFS_TRACE_MTD,
-		  (TSTR("block is OK seq %d state %d" TENDSTR), *sequenceNumber,
-		   *state));
-	}
-
-	if (retval == 0)
-		return YAFFS_OK;
-	else
-		return YAFFS_FAIL;
-}
-
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* mtd interface for YAFFS2 */
+
+const char *yaffs_mtdif2_c_version =
+    "$Id: yaffs_mtdif2.c,v 1.4 2007/04/24 19:30:25 eddie Exp $";
+
+#include "yportenv.h"
+
+
+#include "yaffs_mtdif2.h"
+
+#include "linux/mtd/mtd.h"
+#include "linux/types.h"
+#include "linux/time.h"
+
+#include "yaffs_packedtags2.h"
+
+
+void nandmtd2_pt2buf(yaffs_Device *dev, yaffs_PackedTags2 *pt, int is_raw)
+{
+	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
+	__u8 *ptab = (__u8 *)pt; /* packed tags as bytes */
+	
+	int	i, j = 0, k, n;
+
+	/* Pack buffer with 0xff */
+	for (i = 0; i < mtd->oobsize; i++)
+		dev->spareBuffer[i] = 0xff;
+		
+	if(!is_raw){
+		memcpy(dev->spareBuffer,pt,sizeof(yaffs_PackedTags2));
+	} else {
+		j = 0;
+		k = mtd->oobinfo.oobfree[j][0];
+		n = mtd->oobinfo.oobfree[j][1];
+
+		if (n == 0) {
+			T(YAFFS_TRACE_ERROR, (TSTR("No OOB space for tags" TENDSTR)));
+			YBUG();
+		}
+
+		for (i = 0; i < sizeof(yaffs_PackedTags2); i++) {
+			if (n == 0) {
+				j++;
+				k = mtd->oobinfo.oobfree[j][0];
+				n = mtd->oobinfo.oobfree[j][1];
+				if (n == 0) {
+					T(YAFFS_TRACE_ERROR, (TSTR("No OOB space for tags" TENDSTR)));
+					YBUG();
+				}
+			}
+			dev->spareBuffer[k] = ptab[i];
+			k++;
+			n--;
+		}
+	}
+
+}
+
+void nandmtd2_buf2pt(yaffs_Device *dev, yaffs_PackedTags2 *pt, int is_raw)
+{
+	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
+	int	i, j = 0, k, n;
+	__u8 *ptab = (__u8 *)pt; /* packed tags as bytes */
+
+
+	if (!is_raw) {
+	
+		memcpy(pt,dev->spareBuffer,sizeof(yaffs_PackedTags2));
+	} else {
+		j = 0;
+		k = mtd->oobinfo.oobfree[j][0];
+		n = mtd->oobinfo.oobfree[j][1];
+
+		if (n == 0) {
+			T(YAFFS_TRACE_ERROR, (TSTR("No space in OOB for tags" TENDSTR)));
+			YBUG();
+		}
+
+		for (i = 0; i < sizeof(yaffs_PackedTags2); i++) {
+			if (n == 0) {
+				j++;
+				k = mtd->oobinfo.oobfree[j][0];
+				n = mtd->oobinfo.oobfree[j][1];
+				if (n == 0) {
+					T(YAFFS_TRACE_ERROR, (TSTR("No space in OOB for tags" TENDSTR)));
+					YBUG();
+				}
+			}
+			ptab[i] = dev->spareBuffer[k];
+			k++;
+			n--;
+		}
+	}
+		
+}
+
+int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device * dev, int chunkInNAND,
+				      const __u8 * data,
+				      const yaffs_ExtendedTags * tags)
+{
+	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
+	size_t dummy;
+	int retval = 0;
+
+	loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk;
+
+	yaffs_PackedTags2 pt;
+
+	T(YAFFS_TRACE_MTD,
+	  (TSTR
+	   ("nandmtd2_WriteChunkWithTagsToNAND chunk %d data %p tags %p"
+	    TENDSTR), chunkInNAND, data, tags));
+
+	if (tags) {
+		yaffs_PackTags2(&pt, tags);
+	}
+
+	if (data && tags) {
+	                nandmtd2_pt2buf(dev, &pt, 0);
+			retval =
+			    mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk,
+					   &dummy, data, dev->spareBuffer,
+					   NULL);
+
+	} else {
+	
+		T(YAFFS_TRACE_ALWAYS,
+		  (TSTR
+		  ("Write chunk with null tags or data!" TENDSTR)));
+		YBUG();
+ 	}
+
+	if (retval == 0)
+		return YAFFS_OK;
+	else
+		return YAFFS_FAIL;
+}
+
+int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
+				       __u8 * data, yaffs_ExtendedTags * tags)
+{
+	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
+	size_t dummy;
+	int retval = 0;
+
+	loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk;
+
+	yaffs_PackedTags2 pt;
+
+	T(YAFFS_TRACE_MTD,
+	  (TSTR
+	   ("nandmtd2_ReadChunkWithTagsToNAND chunk %d data %p tags %p"
+	    TENDSTR), chunkInNAND, data, tags));
+
+	if (0 && data && tags) {
+			retval =
+			    mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk,
+					  &dummy, data, dev->spareBuffer,
+					  NULL);
+			nandmtd2_buf2pt(dev, &pt, 0);
+	} else {
+		if (data)
+			retval =
+			    mtd->read(mtd, addr, dev->nDataBytesPerChunk, &dummy,
+				      data);
+		if (tags) {
+			retval =
+			    mtd->read_oob(mtd, addr, mtd->oobsize, &dummy,
+					  dev->spareBuffer);
+			nandmtd2_buf2pt(dev, &pt, 1);
+		}
+	}
+
+	if (tags)
+		yaffs_UnpackTags2(tags, &pt);
+
+	if (retval == 0)
+		return YAFFS_OK;
+	else
+		return YAFFS_FAIL;
+}
+
+int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo)
+{
+	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
+	int retval;
+	T(YAFFS_TRACE_MTD,
+	  (TSTR("nandmtd2_MarkNANDBlockBad %d" TENDSTR), blockNo));
+
+	retval =
+	    mtd->block_markbad(mtd,
+			       blockNo * dev->nChunksPerBlock *
+			       dev->nDataBytesPerChunk);
+
+	if (retval == 0)
+		return YAFFS_OK;
+	else
+		return YAFFS_FAIL;
+
+}
+
+int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo,
+			    yaffs_BlockState * state, int *sequenceNumber)
+{
+	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
+	int retval;
+
+	T(YAFFS_TRACE_MTD,
+	  (TSTR("nandmtd2_QueryNANDBlock %d" TENDSTR), blockNo));
+	retval =
+	    mtd->block_isbad(mtd,
+			     blockNo * dev->nChunksPerBlock *
+			     dev->nDataBytesPerChunk);
+
+	if (retval) {
+		T(YAFFS_TRACE_MTD, (TSTR("block is bad" TENDSTR)));
+
+		*state = YAFFS_BLOCK_STATE_DEAD;
+		*sequenceNumber = 0;
+	} else {
+		yaffs_ExtendedTags t;
+		nandmtd2_ReadChunkWithTagsFromNAND(dev,
+						   blockNo *
+						   dev->nChunksPerBlock, NULL,
+						   &t);
+
+		if (t.chunkUsed) {
+			*sequenceNumber = t.sequenceNumber;
+			*state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
+		} else {
+			*sequenceNumber = 0;
+			*state = YAFFS_BLOCK_STATE_EMPTY;
+		}
+
+		T(YAFFS_TRACE_MTD,
+		  (TSTR("block is OK seq %d state %d" TENDSTR), *sequenceNumber,
+		   *state));
+	}
+
+	if (retval == 0)
+		return YAFFS_OK;
+	else
+		return YAFFS_FAIL;
+}
+
Index: fs/yaffs2/yaffs_mtdif2.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yaffs_mtdif2.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- fs/yaffs2/yaffs_mtdif2.h	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/yaffs_mtdif2.h	24 Apr 2007 19:30:25 -0000	1.2
@@ -1,16 +1,16 @@
 /*
- * YAFFS: Yet another FFS. A NAND-flash specific file system. 
- * yaffs_mtdif.c  NAND mtd wrapper functions.
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. 
  *
- * Copyright (C) 2002 Aleph One Ltd.
+ * Copyright (C) 2002-2007 Aleph One Ltd.
  *   for Toby Churchill Ltd and Brightstar Engineering
  *
  * Created by Charles Manning <charles@aleph1.co.uk>
  *
  * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
  * published by the Free Software Foundation.
  *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
  */
 
 #ifndef __YAFFS_MTDIF2_H__
Index: fs/yaffs2/yaffs_nandemul2k.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yaffs_nandemul2k.h,v
retrieving revision 1.1
retrieving revision 1.3
diff -u -r1.1 -r1.3
--- fs/yaffs2/yaffs_nandemul2k.h	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/yaffs_nandemul2k.h	24 Apr 2007 19:30:25 -0000	1.3
@@ -1,7 +1,7 @@
 /*
- * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. 
  *
- * Copyright (C) 2002 Aleph One Ltd.
+ * Copyright (C) 2002-2007 Aleph One Ltd.
  *   for Toby Churchill Ltd and Brightstar Engineering
  *
  * Created by Charles Manning <charles@aleph1.co.uk>
@@ -10,14 +10,11 @@
  * it under the terms of the GNU Lesser General Public License version 2.1 as
  * published by the Free Software Foundation.
  *
- *
  * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
- *
- * yaffs_nandemul2k.h: Interface to emulated NAND functions (2k page size)
- *
- * $Id: yaffs_nandemul2k.h,v 1.1 2006/02/09 21:08:29 michael Exp $
  */
 
+/* Interface to emulated NAND functions (2k page size) */
+
 #ifndef __YAFFS_NANDEMUL2K_H__
 #define __YAFFS_NANDEMUL2K_H__
 
Index: fs/yaffs2/yaffs_packedtags1.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yaffs_packedtags1.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- fs/yaffs2/yaffs_packedtags1.c	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/yaffs_packedtags1.c	24 Apr 2007 19:30:25 -0000	1.2
@@ -1,3 +1,16 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2007 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
 #include "yaffs_packedtags1.h"
 #include "yportenv.h"
 
Index: fs/yaffs2/yaffs_packedtags1.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yaffs_packedtags1.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- fs/yaffs2/yaffs_packedtags1.h	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/yaffs_packedtags1.h	24 Apr 2007 19:30:25 -0000	1.2
@@ -1,4 +1,19 @@
-// This is used to pack YAFFS1 tags, not YAFFS2 tags.
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. 
+ *
+ * Copyright (C) 2002-2007 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
+/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */
 
 #ifndef __YAFFS_PACKEDTAGS1_H__
 #define __YAFFS_PACKEDTAGS1_H__
Index: fs/yaffs2/yaffs_packedtags2.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yaffs_packedtags2.c,v
retrieving revision 1.1
retrieving revision 1.3
diff -u -r1.1 -r1.3
--- fs/yaffs2/yaffs_packedtags2.c	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/yaffs_packedtags2.c	24 Apr 2007 19:30:25 -0000	1.3
@@ -1,24 +1,20 @@
 /*
- * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
  *
- * yaffs_packedtags2.c: Tags packing for YAFFS2
- *
- * Copyright (C) 2002 Aleph One Ltd.
+ * Copyright (C) 2002-2007 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
  *
  * Created by Charles Manning <charles@aleph1.co.uk>
  *
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * version 2.1 as published by the Free Software Foundation.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
  */
 
 #include "yaffs_packedtags2.h"
 #include "yportenv.h"
 #include "yaffs_tagsvalidity.h"
 
-//#define YAFFS_IGNORE_TAGS_ECC
-
 /* This code packs a set of extended tags into a binary structure for
  * NAND storage
  */
@@ -63,7 +59,6 @@
 
 void yaffs_PackTags2(yaffs_PackedTags2 * pt, const yaffs_ExtendedTags * t)
 {
-  //        pt->t.badBlockMarker = 0xFF;
 	pt->t.chunkId = t->chunkId;
 	pt->t.sequenceNumber = t->sequenceNumber;
 	pt->t.byteCount = t->byteCount;
@@ -117,23 +112,37 @@
 		/* Page is in use */
 #ifdef YAFFS_IGNORE_TAGS_ECC
 		{
-			t->eccResult = 0;
+			t->eccResult = YAFFS_ECC_RESULT_NO_ERROR;
 		}
 #else
 		{
 			yaffs_ECCOther ecc;
+			int result;
 			yaffs_ECCCalculateOther((unsigned char *)&pt->t,
 						sizeof
 						(yaffs_PackedTags2TagsPart),
 						&ecc);
-			t->eccResult =
+			result =
 			    yaffs_ECCCorrectOther((unsigned char *)&pt->t,
 						  sizeof
 						  (yaffs_PackedTags2TagsPart),
 						  &pt->ecc, &ecc);
+			switch(result){
+				case 0: 
+					t->eccResult = YAFFS_ECC_RESULT_NO_ERROR; 
+					break;
+				case 1: 
+					t->eccResult = YAFFS_ECC_RESULT_FIXED;
+					break;
+				case -1:
+					t->eccResult = YAFFS_ECC_RESULT_UNFIXED;
+					break;
+				default:
+					t->eccResult = YAFFS_ECC_RESULT_UNKNOWN;
+			}
 		}
 #endif
-		t->blockBad = 0; // pt->t.badBlockMarker != 0xFF;
+		t->blockBad = 0;
 		t->chunkUsed = 1;
 		t->objectId = pt->t.objectId;
 		t->chunkId = pt->t.chunkId;
Index: fs/yaffs2/yaffs_packedtags2.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yaffs_packedtags2.h,v
retrieving revision 1.1
retrieving revision 1.3
diff -u -r1.1 -r1.3
--- fs/yaffs2/yaffs_packedtags2.h	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/yaffs_packedtags2.h	24 Apr 2007 19:30:25 -0000	1.3
@@ -1,3 +1,18 @@
+/*
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. 
+ *
+ * Copyright (C) 2002-2007 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ */
+
 /* This is used to pack YAFFS2 tags, not YAFFS1tags. */
 
 #ifndef __YAFFS_PACKEDTAGS2_H__
@@ -7,7 +22,6 @@
 #include "yaffs_ecc.h"
 
 typedef struct {
-  //unsigned char badBlockMarker;
 	unsigned sequenceNumber;
 	unsigned objectId;
 	unsigned chunkId;
Index: fs/yaffs2/yaffs_tagscompat.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yaffs_tagscompat.c,v
retrieving revision 1.2
retrieving revision 1.4
diff -u -r1.2 -r1.4
--- fs/yaffs2/yaffs_tagscompat.c	11 Feb 2006 00:04:49 -0000	1.2
+++ fs/yaffs2/yaffs_tagscompat.c	24 Apr 2007 19:30:25 -0000	1.4
@@ -1,16 +1,14 @@
 /*
- * YAFFS: Yet another FFS. A NAND-flash specific file system. 
- * yaffs_tagscompat.h: Tags compatability layer to use YAFFS1 formatted NAND.
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
  *
- * Copyright (C) 2002 Aleph One Ltd.
+ * Copyright (C) 2002-2007 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
  *
  * Created by Charles Manning <charles@aleph1.co.uk>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
- *
- * $Id: yaffs_tagscompat.c,v 1.2 2006/02/11 00:04:49 michael Exp $
  */
 
 #include "yaffs_guts.h"
@@ -335,7 +333,6 @@
 
 static void yaffs_HandleReadDataError(yaffs_Device * dev, int chunkInNAND)
 {
-#ifdef PARANOID_ABOUT_READ_ERRORS
 	int blockInNAND = chunkInNAND / dev->nChunksPerBlock;
 
 	/* Mark the block for retirement */
@@ -348,7 +345,6 @@
 	 * then retire the block
 	 * NB recursion
 	 */
-#endif
 }
 
 #ifdef NOTYET
Index: fs/yaffs2/yaffs_tagscompat.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yaffs_tagscompat.h,v
retrieving revision 1.1
retrieving revision 1.3
diff -u -r1.1 -r1.3
--- fs/yaffs2/yaffs_tagscompat.h	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/yaffs_tagscompat.h	24 Apr 2007 19:30:25 -0000	1.3
@@ -1,22 +1,18 @@
 /*
- * YAFFS: Yet another FFS. A NAND-flash specific file system. 
- * yaffs_ramdisk.h: yaffs ram disk component
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. 
  *
- * Copyright (C) 2002 Aleph One Ltd.
+ * Copyright (C) 2002-2007 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
  *
  * Created by Charles Manning <charles@aleph1.co.uk>
  *
  * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
  * published by the Free Software Foundation.
  *
- * $Id: yaffs_tagscompat.h,v 1.1 2006/02/09 21:08:29 michael Exp $
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
  */
 
-/* This provides a ram disk under yaffs.
- * NB this is not intended for NAND emulation.
- * Use this with dev->useNANDECC enabled, then ECC overheads are not required.
- */
 #ifndef __YAFFS_TAGSCOMPAT_H__
 #define __YAFFS_TAGSCOMPAT_H__
 
Index: fs/yaffs2/yaffs_tagsvalidity.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yaffs_tagsvalidity.c,v
retrieving revision 1.1
retrieving revision 1.3
diff -u -r1.1 -r1.3
--- fs/yaffs2/yaffs_tagsvalidity.c	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/yaffs_tagsvalidity.c	24 Apr 2007 19:30:25 -0000	1.3
@@ -1,8 +1,7 @@
-
 /*
- * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
  *
- * Copyright (C) 2002 Aleph One Ltd.
+ * Copyright (C) 2002-2007 Aleph One Ltd.
  *   for Toby Churchill Ltd and Brightstar Engineering
  *
  * Created by Charles Manning <charles@aleph1.co.uk>
@@ -10,8 +9,6 @@
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
- *
- * $Id: yaffs_tagsvalidity.c,v 1.1 2006/02/09 21:08:29 michael Exp $
  */
 
 #include "yaffs_tagsvalidity.h"
Index: fs/yaffs2/yaffs_tagsvalidity.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yaffs_tagsvalidity.h,v
retrieving revision 1.1
retrieving revision 1.3
diff -u -r1.1 -r1.3
--- fs/yaffs2/yaffs_tagsvalidity.h	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/yaffs_tagsvalidity.h	24 Apr 2007 19:30:25 -0000	1.3
@@ -1,19 +1,18 @@
-
 /*
- * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. 
  *
- * Copyright (C) 2002 Aleph One Ltd.
+ * Copyright (C) 2002-2007 Aleph One Ltd.
  *   for Toby Churchill Ltd and Brightstar Engineering
  *
  * Created by Charles Manning <charles@aleph1.co.uk>
  *
  * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
  * published by the Free Software Foundation.
  *
- * $Id: yaffs_tagsvalidity.h,v 1.1 2006/02/09 21:08:29 michael Exp $
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
  */
-//yaffs_tagsvalidity.h
+
 
 #ifndef __YAFFS_TAGS_VALIDITY_H__
 #define __YAFFS_TAGS_VALIDITY_H__
Index: fs/yaffs2/yaffsinterface.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yaffsinterface.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- fs/yaffs2/yaffsinterface.h	9 Feb 2006 21:08:29 -0000	1.1
+++ fs/yaffs2/yaffsinterface.h	24 Apr 2007 19:30:25 -0000	1.2
@@ -1,8 +1,7 @@
 /*
- * YAFFS: Yet another FFS. A NAND-flash specific file system.
- * yaffsinterface.h: Interface to the guts of yaffs.
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. 
  *
- * Copyright (C) 2002 Aleph One Ltd.
+ * Copyright (C) 2002-2007 Aleph One Ltd.
  *   for Toby Churchill Ltd and Brightstar Engineering
  *
  * Created by Charles Manning <charles@aleph1.co.uk>
@@ -12,7 +11,6 @@
  * published by the Free Software Foundation.
  *
  * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
- *
  */
 
 #ifndef __YAFFSINTERFACE_H__
Index: fs/yaffs2/yportenv.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/fs/yaffs2/yportenv.h,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- fs/yaffs2/yportenv.h	14 Feb 2006 20:53:10 -0000	1.2
+++ fs/yaffs2/yportenv.h	24 Apr 2007 19:30:25 -0000	1.5
@@ -1,9 +1,7 @@
 /*
- * YAFFS: Yet another FFS. A NAND-flash specific file system. 
- * yportenv.h: Portable services used by yaffs. This is done to allow
- * simple migration from kernel space into app space for testing.
+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. 
  *
- * Copyright (C) 2002 Aleph One Ltd.
+ * Copyright (C) 2002-2007 Aleph One Ltd.
  *   for Toby Churchill Ltd and Brightstar Engineering
  *
  * Created by Charles Manning <charles@aleph1.co.uk>
@@ -12,13 +10,10 @@
  * it under the terms of the GNU Lesser General Public License version 2.1 as
  * published by the Free Software Foundation.
  *
- *
  * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
- *
- * $Id: yportenv.h,v 1.2 2006/02/14 20:53:10 eddie Exp $
- *
  */
 
+
 #ifndef __YPORTENV_H__
 #define __YPORTENV_H__
 
@@ -31,9 +26,11 @@
 #include "moduleconfig.h"
 
 /* Linux kernel */
+#include <linux/version.h>
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
 #include <linux/config.h>
+#endif
 #include <linux/kernel.h>
-#include <linux/version.h>
 #include <linux/mm.h>
 #include <linux/string.h>
 #include <linux/slab.h>
@@ -58,9 +55,13 @@
 #define YFREE(x)   kfree(x)
 #define YMALLOC_ALT(x) vmalloc(x)
 #define YFREE_ALT(x)   vfree(x)
+#define YMALLOC_DMA(x) YMALLOC(x)
+
+// KR - added for use in scan so processes aren't blocked indefinitely.
+#define YYIELD() schedule()
 
 #define YAFFS_ROOT_MODE				0755
-#define YAFFS_LOSTNFOUND_MODE		0755
+#define YAFFS_LOSTNFOUND_MODE			0755
 
 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
 #define Y_CURRENT_TIME CURRENT_TIME.tv_sec
@@ -119,8 +120,8 @@
 #define YAFFS_LOSTNFOUND_PREFIX		"obj"
 /* #define YPRINTF(x) printf x */
 
-#define YAFFS_ROOT_MODE				0666
-#define YAFFS_LOSTNFOUND_MODE		0666
+#define YAFFS_ROOT_MODE				0755
+#define YAFFS_LOSTNFOUND_MODE			0755
 
 #define yaffs_SumCompare(x,y) ((x) == (y))
 #define yaffs_strcmp(a,b) strcmp(a,b)
@@ -148,6 +149,7 @@
 #define YAFFS_TRACE_GC_DETAIL		0x00001000
 #define YAFFS_TRACE_SCAN_DEBUG		0x00002000
 #define YAFFS_TRACE_MTD			0x00004000
+#define YAFFS_TRACE_CHECKPOINT		0x00008000
 #define YAFFS_TRACE_ALWAYS		0x40000000
 #define YAFFS_TRACE_BUG			0x80000000
 
Index: include/asm-arm/arch-ep93xx/dma.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/include/asm-arm/arch-ep93xx/dma.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- include/asm-arm/arch-ep93xx/dma.h	22 Jul 2004 19:50:53 -0000	1.1.1.1
+++ include/asm-arm/arch-ep93xx/dma.h	29 Sep 2006 22:30:24 -0000	1.2
@@ -1,237 +1,246 @@
-/*****************************************************************************
- *  linux/include/asm-arm/arch-ep93xx/dma.h
- *
- *  Copyright (C) 2003 Cirrus Logic
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- ****************************************************************************/
-#ifndef __ASM_ARCH_DMA_H
-#define __ASM_ARCH_DMA_H
-
-#define MAX_DMA_ADDRESS     0xffffffff
-
-/*
- *  Not using the regular generic DMA interface for ep93xx.
- */
-#define MAX_DMA_CHANNELS   0
-
-/*
- * The ep93xx dma controller has 5 memory to peripheral (TX) channels, 5
- * peripheral to memory (RX) channels and 2 memory to memory channels.
- */
-#define MAX_EP93XX_DMA_M2P_CHANNELS 10
-#define MAX_EP93XX_DMA_M2M_CHANNELS 2
-
-/* 
- * The generic arm linux api does not support the ep93xx dma model, therefore
- * we use a set of dma support functions written specifically for this dma
- * controller.
- */
-#define MAX_EP93XX_DMA_CHANNELS (MAX_EP93XX_DMA_M2P_CHANNELS + MAX_EP93XX_DMA_M2M_CHANNELS)
-
-/*****************************************************************************
- *
- * Max DMA buffer size
- *
- ****************************************************************************/
-#define DMA_MAX_BUFFER_BYTES    0xFFFF
-
-/*****************************************************************************
- *
- * typedefs
- *
- ****************************************************************************/
-
-/*****************************************************************************
- *
- * All devices which can use a DMA channel
- *
- * NOTE: There exist two types of DMA channels, those that transfer
- *       between an internal peripheral and memory (M2P/P2M), and 
- *       those that transfer between an external peripheral and memory (M2M).
- *       This becomes a bit confusing when you take into account the fact
- *       that the M2M channels can also transfer between two specific 
- *       internal peripherals and memory. 
- *       The first 20 enumerated devices use the first type of channel (M2P/
- *       P2M).  The last six enumerations are specific to the M2M channels.
- *
- ****************************************************************************/
-typedef enum
-{
-    /*
-     *  Hardware device options for the 10 M2P/P2M DMA channels.
-     */
-    DMATx_I2S1 = 0x00000000,    /* TX peripheral ports can be allocated an  */
-    DMATx_I2S2 = 0x00000001,    /* even numbered DMA channel.               */
-    DMATx_AAC1 = 0x00000002,
-    DMATx_AAC2 = 0x00000003,
-    DMATx_AAC3 = 0x00000004,
-    DMATx_I2S3 = 0x00000005,
-    DMATx_UART1 = 0x00000006,
-    DMATx_UART2 = 0x00000007,
-    DMATx_UART3 = 0x00000008,
-    DMATx_IRDA = 0x00000009,
-    DMARx_I2S1 = 0x0000000A,    /* RX perhipheral ports can be allocated an */
-    DMARx_I2S2 = 0x0000000B,    /* odd numbered DMA channel.                */
-    DMARx_AAC1 = 0x0000000C,
-    DMARx_AAC2 = 0x0000000D,
-    DMARx_AAC3 = 0x0000000E,
-    DMARx_I2S3 = 0x0000000F,
-    DMARx_UART1 = 0x00000010,
-    DMARx_UART2 = 0x00000011,
-    DMARx_UART3 = 0x00000012,
-    DMARx_IRDA = 0x00000013,
-
-    /* 
-     *  Device options for the 2 M2M DMA channels
-     */
-    DMA_MEMORY = 0x00000014,
-    DMA_IDE = 0x00000015,
-    DMARx_SSP = 0x00000016,
-    DMATx_SSP = 0x00000017,
-    DMATx_EXT_DREQ = 0x00000018,
-    DMARx_EXT_DREQ = 0x00000019,
-    UNDEF = 0x0000001A
-} ep93xx_dma_dev_t;
-
-/*****************************************************************************
- *
- * Enumerated type used as a parameter for a callback function. 
- * Indicates the type of interrupt.
- *
- ****************************************************************************/
-typedef enum
-{
-    /*
-     *  Common interrupts
-     */
-    STALL,
-    NFB,
-
-    /*
-     *  Specific to M2P channels
-     */
-    CHERROR,
-
-    /*
-     *  Specific to M2M channels
-     */
-    DONE,
-    UNDEF_INT
-} ep93xx_dma_int_t;
-
-/*****************************************************************************
- *
- * Init flag bit defintions for M2P/P2M flags.
- *
- ****************************************************************************/
-
-/*
- *  Channel error interrupt enable.
- */
-#define CHANNEL_ERROR_INT_ENABLE    0x00000001
-/*
- *  Determines how the channel state machine behaves in the NEXT state and
- *  in receipt of a peripheral error.
- *  0 -> NEXT -> ON (ignore the peripheral error.)
- *  1 -> NEXT -> STALL (effectively disable the channel.)
- */
-#define ABORT                       0x00000002  
-/*
- *  Ignore channel error interrupt.
- */                                             
-#define IGNORE_CHANNEL_ERROR        0x00000004  
-                                                
-
-/*****************************************************************************
- *
- * Init flag bit defintions for M2M flags.
- *
- ****************************************************************************/
-
-/*
- *  Destination address hold.  This should be set for IDE write transfers
- */
-#define DESTINATION_HOLD            0x0000001
-/*
- *  Source Address hold. This should be set for IDE read transfers
- */
-#define SOURCE_HOLD                 0x0000002
-/*
- *  Transfer mode.
- *  00 - s/w initiated M2M transfer
- *  01 - h/w initiated external peripheral transfer - memory to external
- *       peripheral/IDE/SSP.
- *  10 - h/w initiated external peripheral transfer - external
- *        peripheral/IDE/SSP to memory.
- *  11 - not used.
- */
-#define TRANSFER_MODE_MASK          0x000000C
-#define TRANSFER_MODE_SW            0x0000000
-#define TRANSFER_MODE_HW_M2P        0x0000004
-#define TRANSFER_MODE_HW_P2M        0x0000008
-/*
- *  Peripheral wait states count. Latency in HCLK cycles needed by the 
- *  peripheral to de-assert its request line once the transfer is
- *  finished.
- *
- *  IDE Operation           Wait States
- *  --------------          ------------
- *  IDE MDMA read               0
- *  IDE MDMA write              0
- *  IDE UDMA read               1
- *  IDE UDMA write              2
- */
-#define WAIT_STATES_MASK            0x00007F0
-#define WAIT_STATES_SHIFT           4
-#define WS_IDE_MDMA_READ_WRITE      0 
-#define WS_IDE_UDMA_READ            1
-#define WS_IDE_UDMA_WRITE           2
-
-/*****************************************************************************
- *
- * Type definition for the callback function
- *
- ****************************************************************************/
-typedef void (* dma_callback)(ep93xx_dma_int_t dma_int,
-                              ep93xx_dma_dev_t device,
-                              unsigned int user_data);
-
-/*****************************************************************************
- *
- * API function prototypes
- *
- ****************************************************************************/
-extern int ep93xx_dma_request(int * handle, const char *device_id,
-                              ep93xx_dma_dev_t device);
-extern int ep93xx_dma_free(int handle);
-extern int ep93xx_dma_config(int handle, unsigned int flags_m2p,
-                             unsigned int flags_m2m,
-                             dma_callback callback, unsigned int user_data);
-extern int ep93xx_dma_add_buffer(int handle, unsigned int source,
-                                 unsigned int dest, unsigned int size,
-                                 unsigned int last, unsigned int buf_id);
-extern int ep93xx_dma_remove_buffer(int handle, unsigned int * buf_id);
-extern int ep93xx_dma_start(int handle, unsigned int channels,
-                            unsigned int * handles);
-extern int ep93xx_dma_pause(int handle, unsigned int channels,
-                            unsigned int * handles);
-extern int ep93xx_dma_flush(int handle);
-extern int ep93xx_dma_queue_full(int handle);
-extern int ep93xx_dma_get_position(int handle, unsigned int * buf_id,
-                                   unsigned int * total);                                 
-#endif /* _ASM_ARCH_DMA_H */
-
+/*****************************************************************************
+ *  linux/include/asm-arm/arch-ep93xx/dma.h
+ *
+ *  Copyright (C) 2003 Cirrus Logic
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ ****************************************************************************/
+#ifndef __ASM_ARCH_DMA_H
+#define __ASM_ARCH_DMA_H
+
+#define MAX_DMA_ADDRESS     0xffffffff
+
+/*
+ *  Not using the regular generic DMA interface for ep93xx.
+ */
+#define MAX_DMA_CHANNELS   0
+
+/*
+ * The ep93xx dma controller has 5 memory to peripheral (TX) channels, 5
+ * peripheral to memory (RX) channels and 2 memory to memory channels.
+ */
+#define MAX_EP93XX_DMA_M2P_CHANNELS 10
+#define MAX_EP93XX_DMA_M2M_CHANNELS 2
+
+/*
+ * The generic arm linux api does not support the ep93xx dma model, therefore
+ * we use a set of dma support functions written specifically for this dma
+ * controller.
+ */
+#define MAX_EP93XX_DMA_CHANNELS (MAX_EP93XX_DMA_M2P_CHANNELS + MAX_EP93XX_DMA_M2M_CHANNELS)
+
+/*****************************************************************************
+ *
+ * Max DMA buffer size
+ *
+ ****************************************************************************/
+#define DMA_MAX_BUFFER_BYTES    0xFFFF
+
+/*****************************************************************************
+ *
+ * typedefs
+ *
+ ****************************************************************************/
+
+/*****************************************************************************
+ *
+ * All devices which can use a DMA channel
+ *
+ * NOTE: There exist two types of DMA channels, those that transfer
+ *       between an internal peripheral and memory (M2P/P2M), and
+ *       those that transfer between an external peripheral and memory (M2M).
+ *       This becomes a bit confusing when you take into account the fact
+ *       that the M2M channels can also transfer between two specific
+ *       internal peripherals and memory.
+ *       The first 20 enumerated devices use the first type of channel (M2P/
+ *       P2M).  The last six enumerations are specific to the M2M channels.
+ *
+ ****************************************************************************/
+typedef enum
+{
+    /*
+     *  Hardware device options for the 10 M2P/P2M DMA channels.
+     */
+    DMATx_I2S1 = 0x00000000,    /* TX peripheral ports can be allocated an  */
+    DMATx_I2S2 = 0x00000001,    /* even numbered DMA channel.               */
+    DMATx_AAC1 = 0x00000002,
+    DMATx_AAC2 = 0x00000003,
+    DMATx_AAC3 = 0x00000004,
+    DMATx_I2S3 = 0x00000005,
+    DMATx_UART1 = 0x00000006,
+    DMATx_UART2 = 0x00000007,
+    DMATx_UART3 = 0x00000008,
+    DMATx_IRDA = 0x00000009,
+    DMARx_I2S1 = 0x0000000A,    /* RX perhipheral ports can be allocated an */
+    DMARx_I2S2 = 0x0000000B,    /* odd numbered DMA channel.                */
+    DMARx_AAC1 = 0x0000000C,
+    DMARx_AAC2 = 0x0000000D,
+    DMARx_AAC3 = 0x0000000E,
+    DMARx_I2S3 = 0x0000000F,
+    DMARx_UART1 = 0x00000010,
+    DMARx_UART2 = 0x00000011,
+    DMARx_UART3 = 0x00000012,
+    DMARx_IRDA = 0x00000013,
+
+    /*
+     *  Device options for the 2 M2M DMA channels
+     */
+    DMA_MEMORY = 0x00000014,
+    DMA_IDE = 0x00000015,
+    DMARx_SSP = 0x00000016,
+    DMATx_SSP = 0x00000017,
+    DMATx_EXT_DREQ = 0x00000018,
+    DMARx_EXT_DREQ = 0x00000019,
+    UNDEF = 0x0000001A
+} ep93xx_dma_dev_t;
+
+/*****************************************************************************
+ *
+ * Enumerated type used as a parameter for a callback function.
+ * Indicates the type of interrupt.
+ *
+ ****************************************************************************/
+typedef enum
+{
+    /*
+     *  Common interrupts
+     */
+    STALL,
+    NFB,
+
+    /*
+     *  Specific to M2P channels
+     */
+    CHERROR,
+
+    /*
+     *  Specific to M2M channels
+     */
+    DONE,
+    UNDEF_INT
+} ep93xx_dma_int_t;
+
+/*****************************************************************************
+ *
+ * Init flag bit defintions for M2P/P2M flags.
+ *
+ ****************************************************************************/
+
+/*
+ *  Channel error interrupt enable.
+ */
+#define CHANNEL_ERROR_INT_ENABLE    0x00000001
+
+/*
+ *  Determines how the channel state machine behaves in the NEXT state and
+ *  in receipt of a peripheral error.
+ *  0 -> NEXT -> ON (ignore the peripheral error.)
+ *  1 -> NEXT -> STALL (effectively disable the channel.)
+ */
+#define CHANNEL_ABORT               0x00000002
+
+/*
+ *  Ignore channel error interrupt.
+ */
+#define IGNORE_CHANNEL_ERROR        0x00000004
+
+/*****************************************************************************
+ *
+ * Init flag bit defintions for M2M flags.
+ *
+ ****************************************************************************/
+
+/*
+ *  Destination address hold.  This should be set for IDE write transfers
+ */
+#define DESTINATION_HOLD            0x0000001
+
+/*
+ *  Source Address hold. This should be set for IDE read transfers
+ */
+#define SOURCE_HOLD                 0x0000002
+
+/*
+ *  Transfer mode.
+ *  00 - s/w initiated M2M transfer
+ *  01 - h/w initiated external peripheral transfer - memory to external
+ *       peripheral/IDE/SSP.
+ *  10 - h/w initiated external peripheral transfer - external
+ *        peripheral/IDE/SSP to memory.
+ *  11 - not used.
+ */
+#define TRANSFER_MODE_MASK          0x000000C
+#define TRANSFER_MODE_SHIFT         2
+#define TRANSFER_MODE_SW            0x0000000
+#define TRANSFER_MODE_HW_M2P        0x0000004
+#define TRANSFER_MODE_HW_P2M        0x0000008
+
+/*
+ *  Peripheral wait states count. Latency in HCLK cycles needed by the
+ *  peripheral to de-assert its request line once the transfer is
+ *  finished.
+ *
+ *  IDE Operation           Wait States
+ *  --------------          ------------
+ *  IDE MDMA read               0
+ *  IDE MDMA write              3
+ *  IDE UDMA read               1
+ *  IDE UDMA write              2
+ */
+#define WAIT_STATES_MASK            0x00007F0
+#define WAIT_STATES_SHIFT           4
+#define WS_IDE_MDMA_READ            0
+#define WS_IDE_MDMA_WRITE           3
+#define WS_IDE_UDMA_READ            1
+#define WS_IDE_UDMA_WRITE           2
+
+/*****************************************************************************
+ *
+ * Type definition for the callback function
+ *
+ ****************************************************************************/
+typedef void (* dma_callback)(ep93xx_dma_int_t dma_int,
+                              ep93xx_dma_dev_t device,
+                              unsigned int user_data);
+
+/*****************************************************************************
+ *
+ * API function prototypes
+ *
+ ****************************************************************************/
+extern int ep93xx_dma_request(int * handle, const char *device_id,
+                              ep93xx_dma_dev_t device);
+extern int ep93xx_dma_free(int handle);
+extern int ep93xx_dma_config(int handle, unsigned int flags_m2p,
+                             unsigned int flags_m2m,
+                             dma_callback callback, unsigned int user_data);
+extern int ep93xx_dma_add_buffer(int handle, unsigned int source,
+                                 unsigned int dest, unsigned int size,
+                                 unsigned int last, unsigned int buf_id);
+extern int ep93xx_dma_remove_buffer(int handle, unsigned int * buf_id);
+extern int ep93xx_dma_start(int handle, unsigned int channels,
+                            unsigned int * handles);
+extern int ep93xx_dma_pause(int handle, unsigned int channels,
+                            unsigned int * handles);
+extern int ep93xx_dma_flush(int handle);
+extern int ep93xx_dma_queue_full(int handle);
+extern int ep93xx_dma_get_position(int handle, unsigned int * buf_id,
+                    unsigned int * total, unsigned int * current_frac);
+extern int ep93xx_dma_get_total(int handle);
+extern int ep93xx_dma_is_done(int handle);
+
+#endif /* _ASM_ARCH_DMA_H */
+
Index: include/asm-arm/arch-ep93xx/ide.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/include/asm-arm/arch-ep93xx/ide.h,v
retrieving revision 1.1.1.1
retrieving revision 1.5
diff -u -r1.1.1.1 -r1.5
--- include/asm-arm/arch-ep93xx/ide.h	22 Jul 2004 19:50:53 -0000	1.1.1.1
+++ include/asm-arm/arch-ep93xx/ide.h	25 Oct 2006 15:29:39 -0000	1.5
@@ -27,12 +27,11 @@
 #include <asm/irq.h>
 #include <asm/hardware.h>
 #include <asm/scatterlist.h>
-
 /*
  *  Maximum number of IDE interfaces for this architecture is 1.
  */
 #undef  MAX_HWIFS
-#define MAX_HWIFS	1 
+#define MAX_HWIFS	4
 
 /*
  *  Default PIO mode used for setting up DMA commands
@@ -272,7 +271,6 @@
 }
 #endif // CONFIG_BLK_DEV_EP93XX
 
-#ifdef CONFIG_BLK_DEV_TSCF
 extern void tscf_ide_init(struct hwif_s * hwif);
 /*****************************************************************************
  *
@@ -283,6 +281,17 @@
 static __inline__ void
 ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq)
 {
+  ide_ioreg_t reg = (ide_ioreg_t) data_port;
+  int i;
+
+  memset(hw, 0, sizeof(*hw));
+
+  hw->io_ports[IDE_DATA_OFFSET]=TS7XXX_IO16_BASE + TS7XXX_ISAIO + reg++;
+  for (i = IDE_DATA_OFFSET+1; i <= IDE_STATUS_OFFSET; i++) {
+    hw->io_ports[i] = TS7XXX_IO8_BASE + TS7XXX_ISAIO + reg++;
+  }
+  hw->io_ports[IDE_CONTROL_OFFSET] = 
+    TS7XXX_IO8_BASE + TS7XXX_ISAIO + (ide_ioreg_t) ctrl_port;
 }
 
 /*****************************************************************************
@@ -330,11 +339,12 @@
 
     
     /*
-     *  Set up a pointer to the tscf ideproc function.
+     *  Original Claim: Set up a pointer to the tscf ideproc function.
+     *  Actuality: original claim appears to be bogus
      */
-    tscf_ide_init(hwif);
+    hwif_s_setmmio(hwif,1);
     
-    printk("Technologic Systems TS-7XXX IDE initialization - driver version 1.3, 6/21/04. \n");
+    printk("Technologic Systems TS-7XXX IDE initialization - driver version 1.4, 10/20/06. \n");
 }
-#endif // CONFIG_BLK_DEV_TSCF
 #endif /* ASM_ARCH_IDE_H */
+
Index: include/asm-arm/arch-ep93xx/regmap.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/include/asm-arm/arch-ep93xx/regmap.h,v
retrieving revision 1.18
retrieving revision 1.21
diff -u -r1.18 -r1.21
--- include/asm-arm/arch-ep93xx/regmap.h	20 Oct 2005 22:38:05 -0000	1.18
+++ include/asm-arm/arch-ep93xx/regmap.h	11 Oct 2007 17:03:38 -0000	1.21
@@ -1373,10 +1373,13 @@
 #define TS7XXX_ENIO (TS7XXX_IO16_BASE + 0x01400000)
 #define TS7XXX_PLDREV (TS7XXX_IO16_BASE + 0x3400000)
 
-#define TS7XXX_RTCIDX (TS7XXX_IO8_BASE + 0xb00000)
-#define TS7XXX_RTCDATA (TS7XXX_IO8_BASE + 0x1700000)
+#define TS7XXX_RTCIDX (TS7XXX_IO8_BASE + 0xb00080)
+#define TS7XXX_RTCDATA (TS7XXX_IO8_BASE + 0x1700080)
 #define TS7XXX_STATUS (TS7XXX_IO8_BASE + 0x800000)
 
+#define BOC_RTCIDX (TS7XXX_IO8_BASE + 0x1e00070)
+#define BOC_RTCDATA (TS7XXX_IO8_BASE + 0x1e00071)
+
 #define TS7XXX_PC104IO8 (TS7XXX_IO8_BASE + 0x01e00000)
 #define TS7XXX_PC104IO16 (TS7XXX_IO16_BASE + 0x01e00000)
 #define TS7XXX_PC104IOMEM8 (TS7XXX_IO8_BASE + 0x01800000)
Index: include/asm-arm/arch-ep93xx/regs_dma.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/include/asm-arm/arch-ep93xx/regs_dma.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- include/asm-arm/arch-ep93xx/regs_dma.h	22 Jul 2004 19:50:53 -0000	1.1.1.1
+++ include/asm-arm/arch-ep93xx/regs_dma.h	29 Sep 2006 22:30:24 -0000	1.2
@@ -1,271 +1,276 @@
-/*****************************************************************************
- *  
+/*****************************************************************************
+ *  
  * linux/include/asm-arm/arch-ep93xx/regs_dma.h
  *
  *  Register definitions for the ep93xx dma channel registers.
  *
- *  Copyright (C) 2003 Cirrus Logic
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
+ *  Copyright (C) 2003 Cirrus Logic
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- ****************************************************************************/
+ *
+ ****************************************************************************/
 #ifndef _REGS_DMA_H_
 #define _REGS_DMA_H_
 
 /*****************************************************************************
- * 0x8000.0000 -> 0x8000.003C M2P Channel 0 Registers (Tx) 
- * 0x8000.0040 -> 0x8000.007C M2P Channel 1 Registers (Rx) 
- * 0x8000.0080 -> 0x8000.00BC M2P Channel 2 Registers (Tx)  
- * 0x8000.00C0 -> 0x8000.00FC M2P Channel 3 Registers (Rx) 
- * 0x8000.0100 -> 0x8000.013C M2M Channel 0 Registers      
- * 0x8000.0140 -> 0x8000.017C M2M Channel 1 Registers      
- * 0x8000.0180 -> 0x8000.01BC Not Used                     
- * 0x8000.01C0 -> 0x8000.01FC Not Used                     
- * 0x8000.0200 -> 0x8000.023C M2P Channel 5 Registers (Rx) 
- * 0x8000.0240 -> 0x8000.027C M2P Channel 4 Registers (Tx) 
- * 0x8000.0280 -> 0x8000.02BC M2P Channel 7 Registers (Rx) 
- * 0x8000.02C0 -> 0x8000.02FC M2P Channel 6 Registers (Tx) 
- * 0x8000.0300 -> 0x8000.033C M2P Channel 9 Registers (Rx) 
- * 0x8000.0340 -> 0x8000.037C M2P Channel 8 Registers (Tx) 
- * 0x8000.0380 DMA Channel Arbitration register            
- * 0x8000.03C0 DMA Global Interrupt register               
- * 0x8000.03C4 -> 0x8000.03FC Not Used                     
- *
- *
- * Internal M2P/P2M Channel Register Map                   
- *
- * Offset Name      Access  Bits Reset Value               
- * 0x00   CONTROL   R/W     6    0                         
- * 0x04   INTERRUPT R/W TC* 3    0                         
- * 0x08   PPALLOC   R/W     4    channel dependant         
- *                               (see reg description)     
- * 0x0C   STATUS    RO      8    0                         
- * 0x10   reserved                                         
- * 0x14   REMAIN    RO      16   0                         
- * 0X18   Reserved                                         
- * 0X1C   Reserved                                         
- * 0x20   MAXCNT0   R/W     16   0                         
- * 0x24   BASE0     R/W     32   0                         
- * 0x28   CURRENT0  RO      32   0                         
- * 0x2C   Reserved                                         
- * 0x30   MAXCNT1   R/W     16   0                         
- * 0x34   BASE1     R/W     32   0                         
- * 0X38   CURRENT1  RO      32   0                         
- * 0X3C   Reserved                                         
- *                                                         
- * M2M Channel Register Map                                
- * Offset Name         Access   Bits Reset Value           
- *                                                         
- * 0x00   CONTROL      R/W      22   0                     
- * 0x04   INTERRUPT    R/W TC*  3    0                     
- * 0x08   Reserved                                         
- * 0x0C   STATUS       R/W TC*  14   0                     
- * 0x10   BCR0         R/W      16   0                     
- * 0x14   BCR1         R/W      16   0                     
- * 0x18   SAR_BASE0    R/W      32   0                     
- * 0x1C   SAR_BASE1    R/W      32   0                     
- * 0x20   Reserved                                         
- * 0x24   SAR_CURRENT0 RO       32   0                     
- * 0x28   SAR_CURRENT1 RO       32   0                     
- * 0x2C   DAR_BASE0    R/W      32   0                     
- * 0x30   DAR_BASE1    R/W      32   0                     
- * 0x34   DAR_CURRENT0 RO       32   0                     
- * 0X38   Reserved                                         
- * 0X3C   DAR_CURRENT1 RO       32   0                          
- * * Write this location once to clear the bit (see        
- * Interrupt/Status register description for which bits    
+ * 0x8000.0000 -> 0x8000.003C M2P Channel 0 Registers (Tx) 
+ * 0x8000.0040 -> 0x8000.007C M2P Channel 1 Registers (Rx) 
+ * 0x8000.0080 -> 0x8000.00BC M2P Channel 2 Registers (Tx)  
+ * 0x8000.00C0 -> 0x8000.00FC M2P Channel 3 Registers (Rx) 
+ * 0x8000.0100 -> 0x8000.013C M2M Channel 0 Registers      
+ * 0x8000.0140 -> 0x8000.017C M2M Channel 1 Registers      
+ * 0x8000.0180 -> 0x8000.01BC Not Used                     
+ * 0x8000.01C0 -> 0x8000.01FC Not Used                     
+ * 0x8000.0200 -> 0x8000.023C M2P Channel 5 Registers (Rx) 
+ * 0x8000.0240 -> 0x8000.027C M2P Channel 4 Registers (Tx) 
+ * 0x8000.0280 -> 0x8000.02BC M2P Channel 7 Registers (Rx) 
+ * 0x8000.02C0 -> 0x8000.02FC M2P Channel 6 Registers (Tx) 
+ * 0x8000.0300 -> 0x8000.033C M2P Channel 9 Registers (Rx) 
+ * 0x8000.0340 -> 0x8000.037C M2P Channel 8 Registers (Tx) 
+ * 0x8000.0380 DMA Channel Arbitration register            
+ * 0x8000.03C0 DMA Global Interrupt register               
+ * 0x8000.03C4 -> 0x8000.03FC Not Used                     
+ *
+ *
+ * Internal M2P/P2M Channel Register Map                   
+ *
+ * Offset Name      Access  Bits Reset Value               
+ * 0x00   CONTROL   R/W     6    0                         
+ * 0x04   INTERRUPT R/W TC* 3    0                         
+ * 0x08   PPALLOC   R/W     4    channel dependant         
+ *                               (see reg description)     
+ * 0x0C   STATUS    RO      8    0                         
+ * 0x10   reserved                                         
+ * 0x14   REMAIN    RO      16   0                         
+ * 0X18   Reserved                                         
+ * 0X1C   Reserved                                         
+ * 0x20   MAXCNT0   R/W     16   0                         
+ * 0x24   BASE0     R/W     32   0                         
+ * 0x28   CURRENT0  RO      32   0                         
+ * 0x2C   Reserved                                         
+ * 0x30   MAXCNT1   R/W     16   0                         
+ * 0x34   BASE1     R/W     32   0                         
+ * 0X38   CURRENT1  RO      32   0                         
+ * 0X3C   Reserved                                         
+ *                                                         
+ * M2M Channel Register Map                                
+ * Offset Name         Access   Bits Reset Value           
+ *                                                         
+ * 0x00   CONTROL      R/W      22   0                     
+ * 0x04   INTERRUPT    R/W TC*  3    0                     
+ * 0x08   Reserved                                         
+ * 0x0C   STATUS       R/W TC*  14   0                     
+ * 0x10   BCR0         R/W      16   0                     
+ * 0x14   BCR1         R/W      16   0                     
+ * 0x18   SAR_BASE0    R/W      32   0                     
+ * 0x1C   SAR_BASE1    R/W      32   0                     
+ * 0x20   Reserved                                         
+ * 0x24   SAR_CURRENT0 RO       32   0                     
+ * 0x28   SAR_CURRENT1 RO       32   0                     
+ * 0x2C   DAR_BASE0    R/W      32   0                     
+ * 0x30   DAR_BASE1    R/W      32   0                     
+ * 0x34   DAR_CURRENT0 RO       32   0                     
+ * 0X38   Reserved                                         
+ * 0X3C   DAR_CURRENT1 RO       32   0                          
+ * * Write this location once to clear the bit (see        
+ * Interrupt/Status register description for which bits    
  * this rule applies to).
- *                                  
+ *                                  
  ****************************************************************************/
 
 #ifndef __ASSEMBLY__ 
-/*
- * DMA Register Base addresses
- */
-static unsigned int const DMAM2PChannelBase[10] =
-{
-    DMA_M2P_TX_0_BASE,
-    DMA_M2P_RX_1_BASE,
-    DMA_M2P_TX_2_BASE,
-    DMA_M2P_RX_3_BASE,
-    DMA_M2P_TX_4_BASE,
-    DMA_M2P_RX_5_BASE,
-    DMA_M2P_TX_6_BASE,
-    DMA_M2P_RX_7_BASE,
-    DMA_M2P_TX_8_BASE,
-    DMA_M2P_RX_9_BASE
-};
-
-static unsigned int const DMAM2MChannelBase[2] = 
-{
-    DMA_M2M_0_BASE,
-    DMA_M2M_1_BASE
-};
-
+/*
+ * DMA Register Base addresses
+ */
+static unsigned int const DMAM2PChannelBase[10] =
+{
+    DMA_M2P_TX_0_BASE,
+    DMA_M2P_RX_1_BASE,
+    DMA_M2P_TX_2_BASE,
+    DMA_M2P_RX_3_BASE,
+    DMA_M2P_TX_4_BASE,
+    DMA_M2P_RX_5_BASE,
+    DMA_M2P_TX_6_BASE,
+    DMA_M2P_RX_7_BASE,
+    DMA_M2P_TX_8_BASE,
+    DMA_M2P_RX_9_BASE
+};
+
+static unsigned int const DMAM2MChannelBase[2] = 
+{
+    DMA_M2M_0_BASE,
+    DMA_M2M_1_BASE
+};
+
 #endif /* __ASSEMBLY__ */
-
-/*----------------------------------------------------------------------------------*/
-/* M2P Registers                                                                    */
-/*----------------------------------------------------------------------------------*/
-/*
- * M2P CONTROL register bit defines 
- */
-#define CONTROL_M2P_STALLINTEN      0x00000001	    /* Enables the STALL interrupt  */
-#define CONTROL_M2P_NFBINTEN        0x00000002	    /* Enables the NFB interrupt    */
-#define CONTROL_M2P_CHERRORINTEN    0x00000008      /* Enables the ChError interrupt*/
-#define CONTROL_M2P_ENABLE		    0x00000010      /* Enables the channel          */
-#define CONTROL_M2P_ABRT		    0x00000020      /* Determines how DMA behaves in*/ 
-			                                        /* NEXT state with peripheral   */
-                                                    /* error                        */
-			                                        /* 0: NEXT -> ON, ignore error  */
-			                                        /* 1: NEXT -> STALL, disable ch.*/ 
-#define CONTROL_M2P_ICE			    0x00000040      /* Ignore Channel Error         */
-
-/*
- * M2P INTERRUPT register bit defines
- */
-#define INTERRUPT_M2P_STALLINT      0x00000001	    /* Indicates channel stalled.   */
-#define INTERRUPT_M2P_NFBINT        0x00000002		/* Indicates channel is hungry. */
-#define INTERRUPT_M2P_CHERRORINT    0x00000008	    /* Peripheral detects error     */
-
-
-/*
- * STATUS register bit defines
- */
-#define STATUS_M2P_STALL            0x00000001		/* A '1' indicates channel is       */
-                                                    /* stalled                          */
-#define STATUS_M2P_NFB			    0x00000002      /* A '1' indicates channel has moved*/
-			                                        /* from NEXT state to ON state, but */
-			                                        /* waiting for next buffer to be    */
-                                                    /* programmed.                      */
-#define STATUS_M2P_CHERROR		    0x00000008      /* Enables the ChError interrupt    */
-#define STATUS_M2P_CURRENT_MASK     0x00000030      /* Current state of the FSM         */
-#define STATUS_M2P_CURRENT_SHIFT    4
-#define STATUS_M2P_NEXTBUFFER	    0x00000040      /* Informs the int handler after an */
-			                                        /* NFB int which pair of maxcnt and */
-                                                    /* base regs to update.             */
-#define STATUS_M2P_BYTES_MASK       0x0000f800 		/* number of valid DMA data         */
-#define STATUS_M2P_BYTES_SHIFT      7               /* currently in                     */
-								        		    /* packer/unpacker                  */
-/*
- * Register masks to mask off reserved bits after reading register.
- */
-#define M2P_MASK_PPALLOC            0x0000000f
-#define M2P_MASK_REMAIN             0x0000ffff
-#define M2P_MASK_MAXCNT0            0x0000ffff
-#define M2P_MASK_BASE0              0xffffffff
-#define M2P_MASK_CURRENT0           0xffffffff
-#define M2P_MASK_MAXCNT1            0x0000ffff
-#define M2P_MASK_BASE1              0xffffffff
-#define M2P_MASK_CURRENT1           0xffffffff
-
-
-/*----------------------------------------------------------------------------------*/
-/* M2M Registers                                                                    */
-/*----------------------------------------------------------------------------------*/
-
-#define CONTROL_M2M_STALLINTEN	0x00000001  /* Enables the STALL interrupt                     */
-#define CONTROL_M2M_SCT			0x00000002  /* Source Copy Transfer. Setup a                   */
-										    /* block transfer from 1 memory source             */
-										    /* location.                                       */
-#define CONTROL_M2M_DONEINTEN	0x00000004  /* Enables the DONE interrupt which                */
-										    /* indicates if the xfer completed                 */
-										    /* successfully                                    */
-#define CONTROL_M2M_ENABLE		0x00000008  /* Enables the channel                             */
-#define CONTROL_M2M_START		0x00000010  /* Initiates the xfer. 'software trigger'          */
-#define CONTROL_M2M_BWC_MASK	0x000001e0  /* Bandwidth control. Indicate number of           */
-#define CONTROL_M2M_BWC_SHIFT   5			/* bytes in a transfer.                            */
-#define CONTROL_M2M_PW_MASK		0x00000600  /* Peripheral width. Used for xfers                */
-#define CONTROL_M2M_PW_SHIFT    9			/* between memory and external peripheral.         */
-										    /* 00: byte, 01: halfword, 10: word.               */
-#define CONTROL_M2M_DAH			0x00000800  /* Destination Address Hold                        */
-#define CONTROL_M2M_SAH			0x00001000  /* Source Address Hold                             */
-#define CONTROL_M2M_TM_MASK     0x00006000  /* Transfer Mode. 00: sw triggered,                */
-#define CONTROL_M2M_TM_SHIFT    13			/* 01: hw initiated M2P, 01: hw initiated P2M      */
-#define CONTROL_M2M_ETDP_MASK	0x00018000  /* End-of-Transfer/Terminal Count pin              */
-#define CONTROL_M2M_ETDP_SHIFT  15		    /* direction and polarity.                         */
-#define CONTROL_M2M_DACKP		0x00020000  /* DMA acknowledge pin polarity                    */
-
-#define CONTROL_M2M_DREQP_MASK  0x00180000	/* DMA request pin polarity. must be set           */
-#define CONTROL_M2M_DREQP_SHIFT 19			/* before enable bit.                              */
-#define CONTROL_M2M_NFBINTEN	0x00200000  /* Enables generation of the NFB interrupt.        */
-#define CONTROL_M2M_RSS_MASK    0x00c00000	/* Request source selection:                       */
-#define CONTROL_M2M_RSS_SHIFT	22			/*		000 - External DReq[0]                     */
-										    /*		001 - External DReq[1]                     */
-										    /*		01X - Internal SSPRx                       */
-										    /*		10X - Internal SSPTx                       */
-										    /*		11X - Internal IDE                         */
-#define CONTROL_M2M_NO_HDSK		0x01000000  /* No handshake.  When set the peripheral doesn't  */
-										    /* require the regular handshake protocal. Must    */
-									    	/* be set for SSP and IDE operations, optional     */
-										    /* for external peripherals.                       */
-#define CONTROL_M2M_PWSC_MASK   0xfe000000	/* Peripheral wait states count. Gives the latency */
-#define CONTROL_M2M_PWSC_SHIFT	25			/* (in PCLK cycles) needed by the peripheral to    */
-								    		/* deassert its' request once the M2M xfer w/ DMA  */
-									    	/* is complete.                                    */
-
-/*
- * M2M INTERRUPT register bit defines
- */
-#define INTERRUPT_M2M_STALLINT	0x00000001	/* Stall interrupt indicates channel stalled. */
-#define INTERRUPT_M2M_DONEINT	0x00000002	/* Transaction done.                          */
-#define INTERRUPT_M2M_NFBINT	0x00000004	/* Next frame buffer interrupt indicates      */
-											/* channel requires a new buffer              */
-
-
-
-/*
- * M2M STATUS register bit defines
- */
-#define STATUS_M2M_STALL		0x00000001  /* A '1' indicates channel is stalled           */
-#define STATUS_M2M_CURRENTSTATE_MASK  0x0000003e  /* Indicates state of M2M Channel control       */
-#define STATUS_M2M_CURRENTSTATE_SHIFT 1		/* FSM (0-2):                                   */
-										    /*	000 - IDLE, 001 - STALL, 010 - MEM_RD,      */
-										    /*  011 - MEM_WR, 100 - BWC_WAIT                */
-										    /* and M2M buffer FSM (3-2):                    */
-										    /* 	00 - NO_BUF, 01 - BUF_ON, 10 - BUF_NEXT     */
-#define STATUS_M2M_DONE		    0x00000040  /* Transfer completed successfully if 1.        */
-#define STATUS_M2M_TCS_MASK		0x00000180  /* Terminal Count status. Indicates whether or  */
-#define STATUS_M2M_TCS_SHIFT    7			/* or not the actual byte count reached         */
-								    		/* programmed limit for buffer descriptor       */
-#define STATUS_M2M_EOTS_MASK    0x00000600  /* End-of-Transfer status for buffer            */
-#define STATUS_M2M_EOTS_SHIFT   9
-#define STATUS_M2M_NFB			0x00000800  /* A '1' indicates channel has moved            */
-										    /* from NEXT state to ON state, but	the next    */
-										    /* byte count reg for next buffer has not been  */
-										    /* programmed yet.                              */
-#define STATUS_M2M_NB			0x00001000  /* NextBuffer status. Informs NFB service       */
-										    /* routine, after NFB int, which pair of buffer */
-										    /* descriptor registers is free to update.      */
-#define STATUS_M2M_DREQS		0x00002000  /* DREQ status.  Reflects the status of the     */
-										    /* synchronized external peripherals DMA        */
-										    /* request signal.                              */
-
-/*
- * Register masks to mask off reserved bits after reading register.
- */
-#define M2M_MASK_BCR0             0x0000ffff
-#define M2M_MASK_BCR1             0x0000ffff
-#define M2M_MASK_SAR_BASE0        0xffffffff
-#define M2M_MASK_SAR_BASE1        0xffffffff
-#define M2M_MASK_SAR_CURRENT0     0xffffffff
-#define M2M_MASK_SAR_CURRENT1     0xffffffff
-#define M2M_MASK_DAR_BASE0        0xffffffff
-#define M2M_MASK_DAR_BASE1        0xffffffff
-#define M2M_MASK_DAR_CURRENT0     0xffffffff
-#define M2M_MASK_DAR_CURRENT1     0xffffffff
-
-
+
+/*----------------------------------------------------------------------------------*/
+/* M2P Registers                                                                    */
+/*----------------------------------------------------------------------------------*/
+/*
+ * M2P CONTROL register bit defines 
+ */
+#define CONTROL_M2P_STALLINTEN      0x00000001	    /* Enables the STALL interrupt  */
+#define CONTROL_M2P_NFBINTEN        0x00000002	    /* Enables the NFB interrupt    */
+#define CONTROL_M2P_CHERRORINTEN    0x00000008      /* Enables the ChError interrupt*/
+#define CONTROL_M2P_ENABLE		    0x00000010      /* Enables the channel          */
+#define CONTROL_M2P_ABRT		    0x00000020      /* Determines how DMA behaves in*/ 
+			                                        /* NEXT state with peripheral   */
+                                                    /* error                        */
+			                                        /* 0: NEXT -> ON, ignore error  */
+			                                        /* 1: NEXT -> STALL, disable ch.*/ 
+#define CONTROL_M2P_ICE			    0x00000040      /* Ignore Channel Error         */
+
+/*
+ * M2P INTERRUPT register bit defines
+ */
+#define INTERRUPT_M2P_STALLINT      0x00000001	    /* Indicates channel stalled.   */
+#define INTERRUPT_M2P_NFBINT        0x00000002		/* Indicates channel is hungry. */
+#define INTERRUPT_M2P_CHERRORINT    0x00000008	    /* Peripheral detects error     */
+
+
+/*
+ * STATUS register bit defines
+ */
+#define STATUS_M2P_STALL            0x00000001		/* A '1' indicates channel is       */
+                                                    /* stalled                          */
+#define STATUS_M2P_NFB			    0x00000002      /* A '1' indicates channel has moved*/
+			                                        /* from NEXT state to ON state, but */
+			                                        /* waiting for next buffer to be    */
+                                                    /* programmed.                      */
+#define STATUS_M2P_CHERROR		    0x00000008      /* Enables the ChError interrupt    */
+#define STATUS_M2P_CURRENT_MASK     0x00000030      /* Current state of the FSM         */
+#define STATUS_M2P_CURRENT_SHIFT    4
+#define STATUS_M2P_NEXTBUFFER	    0x00000040      /* Informs the int handler after an */
+			                                        /* NFB int which pair of maxcnt and */
+                                                    /* base regs to update.             */
+#define STATUS_M2P_BYTES_MASK       0x0000f800 		/* number of valid DMA data         */
+#define STATUS_M2P_BYTES_SHIFT      7               /* currently in                     */
+								        		    /* packer/unpacker                  */
+
+#define STATUS_M2P_DMA_NO_BUF		0x00000000
+#define STATUS_M2P_DMA_BUF_ON		0x00000010
+#define STATUS_M2P_DMA_BUF_NEXT		0x00000020
+
+/*
+ * Register masks to mask off reserved bits after reading register.
+ */
+#define M2P_MASK_PPALLOC            0x0000000f
+#define M2P_MASK_REMAIN             0x0000ffff
+#define M2P_MASK_MAXCNT0            0x0000ffff
+#define M2P_MASK_BASE0              0xffffffff
+#define M2P_MASK_CURRENT0           0xffffffff
+#define M2P_MASK_MAXCNT1            0x0000ffff
+#define M2P_MASK_BASE1              0xffffffff
+#define M2P_MASK_CURRENT1           0xffffffff
+
+
+/*----------------------------------------------------------------------------------*/
+/* M2M Registers                                                                    */
+/*----------------------------------------------------------------------------------*/
+
+#define CONTROL_M2M_STALLINTEN	0x00000001  /* Enables the STALL interrupt                     */
+#define CONTROL_M2M_SCT			0x00000002  /* Source Copy Transfer. Setup a                   */
+										    /* block transfer from 1 memory source             */
+										    /* location.                                       */
+#define CONTROL_M2M_DONEINTEN	0x00000004  /* Enables the DONE interrupt which                */
+										    /* indicates if the xfer completed                 */
+										    /* successfully                                    */
+#define CONTROL_M2M_ENABLE		0x00000008  /* Enables the channel                             */
+#define CONTROL_M2M_START		0x00000010  /* Initiates the xfer. 'software trigger'          */
+#define CONTROL_M2M_BWC_MASK	0x000001e0  /* Bandwidth control. Indicate number of           */
+#define CONTROL_M2M_BWC_SHIFT   5			/* bytes in a transfer.                            */
+#define CONTROL_M2M_PW_MASK		0x00000600  /* Peripheral width. Used for xfers                */
+#define CONTROL_M2M_PW_SHIFT    9			/* between memory and external peripheral.         */
+										    /* 00: byte, 01: halfword, 10: word.               */
+#define CONTROL_M2M_DAH			0x00000800  /* Destination Address Hold                        */
+#define CONTROL_M2M_SAH			0x00001000  /* Source Address Hold                             */
+#define CONTROL_M2M_TM_MASK     0x00006000  /* Transfer Mode. 00: sw triggered,                */
+#define CONTROL_M2M_TM_SHIFT    13			/* 01: hw initiated M2P, 01: hw initiated P2M      */
+#define CONTROL_M2M_ETDP_MASK	0x00018000  /* End-of-Transfer/Terminal Count pin              */
+#define CONTROL_M2M_ETDP_SHIFT  15		    /* direction and polarity.                         */
+#define CONTROL_M2M_DACKP		0x00020000  /* DMA acknowledge pin polarity                    */
+
+#define CONTROL_M2M_DREQP_MASK  0x00180000	/* DMA request pin polarity. must be set           */
+#define CONTROL_M2M_DREQP_SHIFT 19			/* before enable bit.                              */
+#define CONTROL_M2M_NFBINTEN	0x00200000  /* Enables generation of the NFB interrupt.        */
+#define CONTROL_M2M_RSS_MASK    0x00c00000	/* Request source selection:                       */
+#define CONTROL_M2M_RSS_SHIFT	22			/*		000 - External DReq[0]                     */
+										    /*		001 - External DReq[1]                     */
+										    /*		01X - Internal SSPRx                       */
+										    /*		10X - Internal SSPTx                       */
+										    /*		11X - Internal IDE                         */
+#define CONTROL_M2M_NO_HDSK		0x01000000  /* No handshake.  When set the peripheral doesn't  */
+										    /* require the regular handshake protocal. Must    */
+									    	/* be set for SSP and IDE operations, optional     */
+										    /* for external peripherals.                       */
+#define CONTROL_M2M_PWSC_MASK   0xfe000000	/* Peripheral wait states count. Gives the latency */
+#define CONTROL_M2M_PWSC_SHIFT	25			/* (in PCLK cycles) needed by the peripheral to    */
+								    		/* deassert its' request once the M2M xfer w/ DMA  */
+									    	/* is complete.                                    */
+
+/*
+ * M2M INTERRUPT register bit defines
+ */
+#define INTERRUPT_M2M_STALLINT	0x00000001	/* Stall interrupt indicates channel stalled. */
+#define INTERRUPT_M2M_DONEINT	0x00000002	/* Transaction done.                          */
+#define INTERRUPT_M2M_NFBINT	0x00000004	/* Next frame buffer interrupt indicates      */
+											/* channel requires a new buffer              */
+
+
+
+/*
+ * M2M STATUS register bit defines
+ */
+#define STATUS_M2M_STALL		0x00000001  /* A '1' indicates channel is stalled           */
+#define STATUS_M2M_CURRENTSTATE_MASK  0x0000003e  /* Indicates state of M2M Channel control       */
+#define STATUS_M2M_CURRENTSTATE_SHIFT 1		/* FSM (0-2):                                   */
+										    /*	000 - IDLE, 001 - STALL, 010 - MEM_RD,      */
+										    /*  011 - MEM_WR, 100 - BWC_WAIT                */
+										    /* and M2M buffer FSM (3-2):                    */
+										    /* 	00 - NO_BUF, 01 - BUF_ON, 10 - BUF_NEXT     */
+#define STATUS_M2M_DONE		    0x00000040  /* Transfer completed successfully if 1.        */
+#define STATUS_M2M_TCS_MASK		0x00000180  /* Terminal Count status. Indicates whether or  */
+#define STATUS_M2M_TCS_SHIFT    7			/* or not the actual byte count reached         */
+								    		/* programmed limit for buffer descriptor       */
+#define STATUS_M2M_EOTS_MASK    0x00000600  /* End-of-Transfer status for buffer            */
+#define STATUS_M2M_EOTS_SHIFT   9
+#define STATUS_M2M_NFB			0x00000800  /* A '1' indicates channel has moved            */
+										    /* from NEXT state to ON state, but	the next    */
+										    /* byte count reg for next buffer has not been  */
+										    /* programmed yet.                              */
+#define STATUS_M2M_NB			0x00001000  /* NextBuffer status. Informs NFB service       */
+										    /* routine, after NFB int, which pair of buffer */
+										    /* descriptor registers is free to update.      */
+#define STATUS_M2M_DREQS		0x00002000  /* DREQ status.  Reflects the status of the     */
+										    /* synchronized external peripherals DMA        */
+										    /* request signal.                              */
+
+/*
+ * Register masks to mask off reserved bits after reading register.
+ */
+#define M2M_MASK_BCR0             0x0000ffff
+#define M2M_MASK_BCR1             0x0000ffff
+#define M2M_MASK_SAR_BASE0        0xffffffff
+#define M2M_MASK_SAR_BASE1        0xffffffff
+#define M2M_MASK_SAR_CURRENT0     0xffffffff
+#define M2M_MASK_SAR_CURRENT1     0xffffffff
+#define M2M_MASK_DAR_BASE0        0xffffffff
+#define M2M_MASK_DAR_BASE1        0xffffffff
+#define M2M_MASK_DAR_CURRENT0     0xffffffff
+#define M2M_MASK_DAR_CURRENT1     0xffffffff
+
+
 #endif /* _REGS_DMA_H_ */
Index: include/asm-arm/mach/irq.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/include/asm-arm/mach/irq.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- include/asm-arm/mach/irq.h	22 Jul 2004 19:50:53 -0000	1.1.1.1
+++ include/asm-arm/mach/irq.h	13 Jan 2009 19:28:09 -0000	1.2
@@ -36,6 +36,7 @@
 	unsigned int	 lck_jif;
 	int		 lck_warned;
 	struct timer_list	lck_timer;
+	struct proc_dir_entry   *dir;
 };
 
 extern struct irqdesc irq_desc[];
Index: include/linux/ide.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/include/linux/ide.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- include/linux/ide.h	4 Aug 2004 01:17:28 -0000	1.2
+++ include/linux/ide.h	20 Oct 2006 15:55:25 -0000	1.3
@@ -344,6 +344,8 @@
 #endif
 			int irq);
 
+static inline void hwif_s_setmmio(struct hwif_s *s,int val);
+
 #include <asm/ide.h>
 
 /* Currently only m68k, apus and m8xx need it */
@@ -1014,6 +1016,10 @@
 	void		*hwif_data;	/* extra hwif data */
 } ide_hwif_t;
 
+static inline void hwif_s_setmmio(struct hwif_s *s,int val) {
+  s->mmio = val;
+}
+
 /*
  *  internal ide interrupt handler type
  */
Index: include/linux/irq.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/include/linux/irq.h,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- include/linux/irq.h	22 Jul 2004 19:50:31 -0000	1.1.1.1
+++ include/linux/irq.h	16 Jan 2009 20:49:56 -0000	1.3
@@ -64,9 +64,10 @@
 	spinlock_t lock;
 } ____cacheline_aligned irq_desc_t;
 
-extern irq_desc_t irq_desc [NR_IRQS];
+//EWD: commented out due to conflicting types
+//extern irq_desc_t irq_desc [NR_IRQS];
 
-#include <asm/hw_irq.h> /* the arch dependent stuff */
+//#include <asm/hw_irq.h> /* the arch dependent stuff */
 
 extern int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
 extern int setup_irq(unsigned int , struct irqaction * );
Index: include/linux/netdevice.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/include/linux/netdevice.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- include/linux/netdevice.h	4 Aug 2004 01:17:30 -0000	1.2
+++ include/linux/netdevice.h	19 Oct 2006 17:43:03 -0000	1.3
@@ -294,7 +294,9 @@
 
 	/* List of functions to handle Wireless Extensions (instead of ioctl).
 	 * See <net/iw_handler.h> for details. Jean II */
-	struct iw_handler_def *	wireless_handlers;
+	const struct iw_handler_def *	wireless_handlers;
+	/* Instance data managed by the core of Wireless Extensions. */
+	struct iw_public_data *	wireless_data;
 
 	struct ethtool_ops *ethtool_ops;
 
Index: include/linux/sched.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/include/linux/sched.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- include/linux/sched.h	4 Aug 2004 01:17:32 -0000	1.2
+++ include/linux/sched.h	13 Jan 2009 19:28:09 -0000	1.3
@@ -125,6 +125,9 @@
 
 #include <linux/spinlock.h>
 
+/* Task command name length */
+#define TASK_COMM_LEN 16
+
 /*
  * This serializes "schedule()" and also protects
  * the run-queue from deletions/modifications (but
Index: include/linux/ts_sbc.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/include/linux/ts_sbc.h,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- include/linux/ts_sbc.h	8 Feb 2006 23:13:45 -0000	1.4
+++ include/linux/ts_sbc.h	7 Dec 2006 23:04:07 -0000	1.5
@@ -18,6 +18,7 @@
 #define TS7250  0x01
 #define TS7260  0x02
 #define TS7300  0x03
+#define TS7400  0x04
 
 #define IO_SBCID 0x74
 
Index: include/linux/wireless.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/include/linux/wireless.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- include/linux/wireless.h	4 Aug 2004 01:17:34 -0000	1.2
+++ include/linux/wireless.h	19 Oct 2006 17:43:03 -0000	1.3
@@ -1,10 +1,10 @@
 /*
  * This file define a set of standard wireless extensions
  *
- * Version :	16	2.4.03
+ * Version :	18	12.3.05
  *
  * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>
- * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved.
+ * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved.
  */
 
 #ifndef _LINUX_WIRELESS_H
@@ -47,12 +47,12 @@
  *	# include/net/iw_handler.h
  *
  * Note as well that /proc/net/wireless implementation has now moved in :
- *	# include/linux/wireless.c
+ *	# net/core/wireless.c
  *
  * Wireless Events (2002 -> onward) :
  * --------------------------------
  * Events are defined at the end of this file, and implemented in :
- *	# include/linux/wireless.c
+ *	# net/core/wireless.c
  *
  * Other comments :
  * --------------
@@ -82,7 +82,7 @@
  * (there is some stuff that will be added in the future...)
  * I just plan to increment with each new version.
  */
-#define WIRELESS_EXT	16
+#define WIRELESS_EXT	18
 
 /*
  * Changes :
@@ -175,6 +175,28 @@
  *	- Remove IW_MAX_GET_SPY because conflict with enhanced spy support
  *	- Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy"
  *	- Add IW_ENCODE_TEMP and iw_range->encoding_login_index
+ *
+ * V16 to V17
+ * ----------
+ *	- Add flags to frequency -> auto/fixed
+ *	- Document (struct iw_quality *)->updated, add new flags (INVALID)
+ *	- Wireless Event capability in struct iw_range
+ *	- Add support for relative TxPower (yick !)
+ *
+ * V17 to V18 (From Jouni Malinen <jkmaline@cc.hut.fi>)
+ * ----------
+ *	- Add support for WPA/WPA2
+ *	- Add extended encoding configuration (SIOCSIWENCODEEXT and
+ *	  SIOCGIWENCODEEXT)
+ *	- Add SIOCSIWGENIE/SIOCGIWGENIE
+ *	- Add SIOCSIWMLME
+ *	- Add SIOCSIWPMKSA
+ *	- Add struct iw_range bit field for supported encoding capabilities
+ *	- Add optional scan request parameters for SIOCSIWSCAN
+ *	- Add SIOCSIWAUTH/SIOCGIWAUTH for setting authentication and WPA
+ *	  related parameters (extensible up to 4096 parameter values)
+ *	- Add wireless events: IWEVGENIE, IWEVMICHAELMICFAILURE,
+ *	  IWEVASSOCREQIE, IWEVASSOCRESPIE, IWEVPMKIDCAND
  */
 
 /**************************** CONSTANTS ****************************/
@@ -249,9 +271,33 @@
 #define SIOCSIWPOWER	0x8B2C		/* set Power Management settings */
 #define SIOCGIWPOWER	0x8B2D		/* get Power Management settings */
 
+/* WPA : Generic IEEE 802.11 informatiom element (e.g., for WPA/RSN/WMM).
+ * This ioctl uses struct iw_point and data buffer that includes IE id and len
+ * fields. More than one IE may be included in the request. Setting the generic
+ * IE to empty buffer (len=0) removes the generic IE from the driver. Drivers
+ * are allowed to generate their own WPA/RSN IEs, but in these cases, drivers
+ * are required to report the used IE as a wireless event, e.g., when
+ * associating with an AP. */
+#define SIOCSIWGENIE	0x8B30		/* set generic IE */
+#define SIOCGIWGENIE	0x8B31		/* get generic IE */
+
+/* WPA : IEEE 802.11 MLME requests */
+#define SIOCSIWMLME	0x8B16		/* request MLME operation; uses
+					 * struct iw_mlme */
+/* WPA : Authentication mode parameters */
+#define SIOCSIWAUTH	0x8B32		/* set authentication mode params */
+#define SIOCGIWAUTH	0x8B33		/* get authentication mode params */
+
+/* WPA : Extended version of encoding configuration */
+#define SIOCSIWENCODEEXT 0x8B34		/* set encoding token & mode */
+#define SIOCGIWENCODEEXT 0x8B35		/* get encoding token & mode */
+
+/* WPA2 : PMKSA cache management */
+#define SIOCSIWPMKSA	0x8B36		/* PMKSA cache operation */
+
 /* -------------------- DEV PRIVATE IOCTL LIST -------------------- */
 
-/* These 16 ioctl are wireless device private.
+/* These 32 ioctl are wireless device private, for 16 commands.
  * Each driver is free to use them for whatever purpose it chooses,
  * however the driver *must* export the description of those ioctls
  * with SIOCGIWPRIV and *must* use arguments as defined below.
@@ -266,8 +312,8 @@
  * We now have 32 commands, so a bit more space ;-).
  * Also, all 'odd' commands are only usable by root and don't return the
  * content of ifr/iwr to user (but you are not obliged to use the set/get
- * convention, just use every other two command).
- * And I repeat : you are not obliged to use them with iwspy, but you
+ * convention, just use every other two command). More details in iwpriv.c.
+ * And I repeat : you are not forced to use them with iwpriv, but you
  * must be compliant with it.
  */
 
@@ -290,6 +336,34 @@
 #define IWEVCUSTOM	0x8C02		/* Driver specific ascii string */
 #define IWEVREGISTERED	0x8C03		/* Discovered a new node (AP mode) */
 #define IWEVEXPIRED	0x8C04		/* Expired a node (AP mode) */
+#define IWEVGENIE	0x8C05		/* Generic IE (WPA, RSN, WMM, ..)
+					 * (scan results); This includes id and
+					 * length fields. One IWEVGENIE may
+					 * contain more than one IE. Scan
+					 * results may contain one or more
+					 * IWEVGENIE events. */
+#define IWEVMICHAELMICFAILURE 0x8C06	/* Michael MIC failure
+					 * (struct iw_michaelmicfailure)
+					 */
+#define IWEVASSOCREQIE	0x8C07		/* IEs used in (Re)Association Request.
+					 * The data includes id and length
+					 * fields and may contain more than one
+					 * IE. This event is required in
+					 * Managed mode if the driver
+					 * generates its own WPA/RSN IE. This
+					 * should be sent just before
+					 * IWEVREGISTERED event for the
+					 * association. */
+#define IWEVASSOCRESPIE	0x8C08		/* IEs used in (Re)Association
+					 * Response. The data includes id and
+					 * length fields and may contain more
+					 * than one IE. This may be sent
+					 * between IWEVASSOCREQIE and
+					 * IWEVREGISTERED events for the
+					 * association. */
+#define IWEVPMKIDCAND	0x8C09		/* PMKID candidate for RSN
+					 * pre-authentication
+					 * (struct iw_pmkid_cand) */
 
 #define IWEVFIRST	0x8C00
 
@@ -352,6 +426,18 @@
 #define IW_MODE_SECOND	5	/* Secondary master/repeater (backup) */
 #define IW_MODE_MONITOR	6	/* Passive monitor (listen only) */
 
+/* Statistics flags (bitmask in updated) */
+#define IW_QUAL_QUAL_UPDATED	0x1	/* Value was updated since last read */
+#define IW_QUAL_LEVEL_UPDATED	0x2
+#define IW_QUAL_NOISE_UPDATED	0x4
+#define IW_QUAL_QUAL_INVALID	0x10	/* Driver doesn't provide value */
+#define IW_QUAL_LEVEL_INVALID	0x20
+#define IW_QUAL_NOISE_INVALID	0x40
+
+/* Frequency flags */
+#define IW_FREQ_AUTO		0x00	/* Let the driver decides */
+#define IW_FREQ_FIXED		0x01	/* Force a specific value */
+
 /* Maximum number of size of encoding token available
  * they are listed in the range structure */
 #define IW_MAX_ENCODING_SIZES	8
@@ -390,6 +476,7 @@
 #define IW_TXPOW_TYPE		0x00FF	/* Type of value */
 #define IW_TXPOW_DBM		0x0000	/* Value is in dBm */
 #define IW_TXPOW_MWATT		0x0001	/* Value is in mW */
+#define IW_TXPOW_RELATIVE	0x0002	/* Value is in arbitrary units */
 #define IW_TXPOW_RANGE		0x1000	/* Range of value between min/max */
 
 /* Retry limits and lifetime flags available */
@@ -412,12 +499,113 @@
 #define IW_SCAN_THIS_MODE	0x0020	/* Scan only this Mode */
 #define IW_SCAN_ALL_RATE	0x0040	/* Scan all Bit-Rates */
 #define IW_SCAN_THIS_RATE	0x0080	/* Scan only this Bit-Rate */
+/* struct iw_scan_req scan_type */
+#define IW_SCAN_TYPE_ACTIVE 0
+#define IW_SCAN_TYPE_PASSIVE 1
 /* Maximum size of returned data */
 #define IW_SCAN_MAX_DATA	4096	/* In bytes */
 
 /* Max number of char in custom event - use multiple of them if needed */
 #define IW_CUSTOM_MAX		256	/* In bytes */
 
+/* Generic information element */
+#define IW_GENERIC_IE_MAX	1024
+
+/* MLME requests (SIOCSIWMLME / struct iw_mlme) */
+#define IW_MLME_DEAUTH		0
+#define IW_MLME_DISASSOC	1
+
+/* SIOCSIWAUTH/SIOCGIWAUTH struct iw_param flags */
+#define IW_AUTH_INDEX		0x0FFF
+#define IW_AUTH_FLAGS		0xF000
+/* SIOCSIWAUTH/SIOCGIWAUTH parameters (0 .. 4095)
+ * (IW_AUTH_INDEX mask in struct iw_param flags; this is the index of the
+ * parameter that is being set/get to; value will be read/written to
+ * struct iw_param value field) */
+#define IW_AUTH_WPA_VERSION		0
+#define IW_AUTH_CIPHER_PAIRWISE		1
+#define IW_AUTH_CIPHER_GROUP		2
+#define IW_AUTH_KEY_MGMT		3
+#define IW_AUTH_TKIP_COUNTERMEASURES	4
+#define IW_AUTH_DROP_UNENCRYPTED	5
+#define IW_AUTH_80211_AUTH_ALG		6
+#define IW_AUTH_WPA_ENABLED		7
+#define IW_AUTH_RX_UNENCRYPTED_EAPOL	8
+#define IW_AUTH_ROAMING_CONTROL		9
+#define IW_AUTH_PRIVACY_INVOKED		10
+
+/* IW_AUTH_WPA_VERSION values (bit field) */
+#define IW_AUTH_WPA_VERSION_DISABLED	0x00000001
+#define IW_AUTH_WPA_VERSION_WPA		0x00000002
+#define IW_AUTH_WPA_VERSION_WPA2	0x00000004
+
+/* IW_AUTH_PAIRWISE_CIPHER and IW_AUTH_GROUP_CIPHER values (bit field) */
+#define IW_AUTH_CIPHER_NONE	0x00000001
+#define IW_AUTH_CIPHER_WEP40	0x00000002
+#define IW_AUTH_CIPHER_TKIP	0x00000004
+#define IW_AUTH_CIPHER_CCMP	0x00000008
+#define IW_AUTH_CIPHER_WEP104	0x00000010
+
+/* IW_AUTH_KEY_MGMT values (bit field) */
+#define IW_AUTH_KEY_MGMT_802_1X	1
+#define IW_AUTH_KEY_MGMT_PSK	2
+
+/* IW_AUTH_80211_AUTH_ALG values (bit field) */
+#define IW_AUTH_ALG_OPEN_SYSTEM	0x00000001
+#define IW_AUTH_ALG_SHARED_KEY	0x00000002
+#define IW_AUTH_ALG_LEAP	0x00000004
+
+/* IW_AUTH_ROAMING_CONTROL values */
+#define IW_AUTH_ROAMING_ENABLE	0	/* driver/firmware based roaming */
+#define IW_AUTH_ROAMING_DISABLE	1	/* user space program used for roaming
+					 * control */
+
+/* SIOCSIWENCODEEXT definitions */
+#define IW_ENCODE_SEQ_MAX_SIZE	8
+/* struct iw_encode_ext ->alg */
+#define IW_ENCODE_ALG_NONE	0
+#define IW_ENCODE_ALG_WEP	1
+#define IW_ENCODE_ALG_TKIP	2
+#define IW_ENCODE_ALG_CCMP	3
+/* struct iw_encode_ext ->ext_flags */
+#define IW_ENCODE_EXT_TX_SEQ_VALID	0x00000001
+#define IW_ENCODE_EXT_RX_SEQ_VALID	0x00000002
+#define IW_ENCODE_EXT_GROUP_KEY		0x00000004
+#define IW_ENCODE_EXT_SET_TX_KEY	0x00000008
+
+/* IWEVMICHAELMICFAILURE : struct iw_michaelmicfailure ->flags */
+#define IW_MICFAILURE_KEY_ID	0x00000003 /* Key ID 0..3 */
+#define IW_MICFAILURE_GROUP	0x00000004
+#define IW_MICFAILURE_PAIRWISE	0x00000008
+#define IW_MICFAILURE_STAKEY	0x00000010
+#define IW_MICFAILURE_COUNT	0x00000060 /* 1 or 2 (0 = count not supported)
+					    */
+
+/* Bit field values for enc_capa in struct iw_range */
+#define IW_ENC_CAPA_WPA		0x00000001
+#define IW_ENC_CAPA_WPA2	0x00000002
+#define IW_ENC_CAPA_CIPHER_TKIP	0x00000004
+#define IW_ENC_CAPA_CIPHER_CCMP	0x00000008
+
+/* Event capability macros - in (struct iw_range *)->event_capa
+ * Because we have more than 32 possible events, we use an array of
+ * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */
+#define IW_EVENT_CAPA_BASE(cmd)		((cmd >= SIOCIWFIRSTPRIV) ? \
+					 (cmd - SIOCIWFIRSTPRIV + 0x60) : \
+					 (cmd - SIOCSIWCOMMIT))
+#define IW_EVENT_CAPA_INDEX(cmd)	(IW_EVENT_CAPA_BASE(cmd) >> 5)
+#define IW_EVENT_CAPA_MASK(cmd)		(1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F))
+/* Event capability constants - event autogenerated by the kernel
+ * This list is valid for most 802.11 devices, customise as needed... */
+#define IW_EVENT_CAPA_K_0	(IW_EVENT_CAPA_MASK(0x8B04) | \
+				 IW_EVENT_CAPA_MASK(0x8B06) | \
+				 IW_EVENT_CAPA_MASK(0x8B1A))
+#define IW_EVENT_CAPA_K_1	(IW_EVENT_CAPA_MASK(0x8B2A))
+/* "Easy" macro to set events in iw_range (less efficient) */
+#define IW_EVENT_CAPA_SET(event_capa, cmd) (event_capa[IW_EVENT_CAPA_INDEX(cmd)] |= IW_EVENT_CAPA_MASK(cmd))
+#define IW_EVENT_CAPA_SET_KERNEL(event_capa) {event_capa[0] |= IW_EVENT_CAPA_K_0; event_capa[1] |= IW_EVENT_CAPA_K_1; }
+
+
 /****************************** TYPES ******************************/
 
 /* --------------------------- SUBTYPES --------------------------- */
@@ -456,7 +644,7 @@
 	__s32		m;		/* Mantissa */
 	__s16		e;		/* Exponent */
 	__u8		i;		/* List index (when in range struct) */
-	__u8		pad;		/* Unused - just for alignement */
+	__u8		flags;		/* Flags (fixed/auto) */
 };
 
 /*
@@ -507,6 +695,132 @@
 	struct iw_quality	high;		/* High threshold */
 };
 
+/*
+ *	Optional data for scan request
+ *
+ *	Note: these optional parameters are controlling parameters for the
+ *	scanning behavior, these do not apply to getting scan results
+ *	(SIOCGIWSCAN). Drivers are expected to keep a local BSS table and
+ *	provide a merged results with all BSSes even if the previous scan
+ *	request limited scanning to a subset, e.g., by specifying an SSID.
+ *	Especially, scan results are required to include an entry for the
+ *	current BSS if the driver is in Managed mode and associated with an AP.
+ */
+struct	iw_scan_req
+{
+	__u8		scan_type; /* IW_SCAN_TYPE_{ACTIVE,PASSIVE} */
+	__u8		essid_len;
+	__u8		num_channels; /* num entries in channel_list;
+				       * 0 = scan all allowed channels */
+	__u8		flags; /* reserved as padding; use zero, this may
+				* be used in the future for adding flags
+				* to request different scan behavior */
+	struct sockaddr	bssid; /* ff:ff:ff:ff:ff:ff for broadcast BSSID or
+				* individual address of a specific BSS */
+
+	/*
+	 * Use this ESSID if IW_SCAN_THIS_ESSID flag is used instead of using
+	 * the current ESSID. This allows scan requests for specific ESSID
+	 * without having to change the current ESSID and potentially breaking
+	 * the current association.
+	 */
+	__u8		essid[IW_ESSID_MAX_SIZE];
+
+	/*
+	 * Optional parameters for changing the default scanning behavior.
+	 * These are based on the MLME-SCAN.request from IEEE Std 802.11.
+	 * TU is 1.024 ms. If these are set to 0, driver is expected to use
+	 * reasonable default values. min_channel_time defines the time that
+	 * will be used to wait for the first reply on each channel. If no
+	 * replies are received, next channel will be scanned after this. If
+	 * replies are received, total time waited on the channel is defined by
+	 * max_channel_time.
+	 */
+	__u32		min_channel_time; /* in TU */
+	__u32		max_channel_time; /* in TU */
+
+	struct iw_freq	channel_list[IW_MAX_FREQUENCIES];
+};
+
+/* ------------------------- WPA SUPPORT ------------------------- */
+
+/*
+ *	Extended data structure for get/set encoding (this is used with
+ *	SIOCSIWENCODEEXT/SIOCGIWENCODEEXT. struct iw_point and IW_ENCODE_*
+ *	flags are used in the same way as with SIOCSIWENCODE/SIOCGIWENCODE and
+ *	only the data contents changes (key data -> this structure, including
+ *	key data).
+ *
+ *	If the new key is the first group key, it will be set as the default
+ *	TX key. Otherwise, default TX key index is only changed if
+ *	IW_ENCODE_EXT_SET_TX_KEY flag is set.
+ *
+ *	Key will be changed with SIOCSIWENCODEEXT in all cases except for
+ *	special "change TX key index" operation which is indicated by setting
+ *	key_len = 0 and ext_flags |= IW_ENCODE_EXT_SET_TX_KEY.
+ *
+ *	tx_seq/rx_seq are only used when respective
+ *	IW_ENCODE_EXT_{TX,RX}_SEQ_VALID flag is set in ext_flags. Normal
+ *	TKIP/CCMP operation is to set RX seq with SIOCSIWENCODEEXT and start
+ *	TX seq from zero whenever key is changed. SIOCGIWENCODEEXT is normally
+ *	used only by an Authenticator (AP or an IBSS station) to get the
+ *	current TX sequence number. Using TX_SEQ_VALID for SIOCSIWENCODEEXT and
+ *	RX_SEQ_VALID for SIOCGIWENCODEEXT are optional, but can be useful for
+ *	debugging/testing.
+ */
+struct	iw_encode_ext
+{
+	__u32		ext_flags; /* IW_ENCODE_EXT_* */
+	__u8		tx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */
+	__u8		rx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */
+	struct sockaddr	addr; /* ff:ff:ff:ff:ff:ff for broadcast/multicast
+			       * (group) keys or unicast address for
+			       * individual keys */
+	__u16		alg; /* IW_ENCODE_ALG_* */
+	__u16		key_len;
+	__u8		key[0];
+};
+
+/* SIOCSIWMLME data */
+struct	iw_mlme
+{
+	__u16		cmd; /* IW_MLME_* */
+	__u16		reason_code;
+	struct sockaddr	addr;
+};
+
+/* SIOCSIWPMKSA data */
+#define IW_PMKSA_ADD		1
+#define IW_PMKSA_REMOVE		2
+#define IW_PMKSA_FLUSH		3
+
+#define IW_PMKID_LEN	16
+
+struct	iw_pmksa
+{
+	__u32		cmd; /* IW_PMKSA_* */
+	struct sockaddr	bssid;
+	__u8		pmkid[IW_PMKID_LEN];
+};
+
+/* IWEVMICHAELMICFAILURE data */
+struct	iw_michaelmicfailure
+{
+	__u32		flags;
+	struct sockaddr	src_addr;
+	__u8		tsc[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */
+};
+
+/* IWEVPMKIDCAND data */
+#define IW_PMKID_CAND_PREAUTH	0x00000001 /* RNS pre-authentication enabled */
+struct	iw_pmkid_cand
+{
+	__u32		flags; /* IW_PMKID_CAND_* */
+	__u32		index; /* the smaller the index, the higher the
+				* priority */
+	struct sockaddr	bssid;
+};
+
 /* ------------------------ WIRELESS STATS ------------------------ */
 /*
  * Wireless statistics (used for /proc/net/wireless)
@@ -610,11 +924,12 @@
 	/* Old Frequency (backward compat - moved lower ) */
 	__u16		old_num_channels;
 	__u8		old_num_frequency;
-	/* Filler to keep "version" at the same offset */
-	__s32		old_freq[6];
+
+	/* Wireless event capability bitmasks */
+	__u32		event_capa[6];
 
 	/* signal level threshold range */
-	__s32	sensitivity;
+	__s32		sensitivity;
 
 	/* Quality of link & SNR stuff */
 	/* Quality range (link, level, noise)
@@ -685,6 +1000,8 @@
 	struct iw_freq	freq[IW_MAX_FREQUENCIES];	/* list */
 	/* Note : this frequency list doesn't need to fit channel numbers,
 	 * because each entry contain its channel index */
+
+	__u32		enc_capa; /* IW_ENC_CAPA_* bit field */
 };
 
 /*
Index: include/net/iw_handler.h
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/include/net/iw_handler.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- include/net/iw_handler.h	4 Aug 2004 01:17:35 -0000	1.2
+++ include/net/iw_handler.h	19 Oct 2006 17:43:03 -0000	1.3
@@ -1,10 +1,10 @@
 /*
  * This file define the new driver API for Wireless Extensions
  *
- * Version :	5	4.12.02
+ * Version :	6	21.6.04
  *
  * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>
- * Copyright (c) 2001-2002 Jean Tourrilhes, All Rights Reserved.
+ * Copyright (c) 2001-2004 Jean Tourrilhes, All Rights Reserved.
  */
 
 #ifndef _IW_HANDLER_H
@@ -206,7 +206,7 @@
  * will be needed...
  * I just plan to increment with each new version.
  */
-#define IW_HANDLER_VERSION	5
+#define IW_HANDLER_VERSION	6
 
 /*
  * Changes :
@@ -224,11 +224,18 @@
  * V4 to V5
  * --------
  *	- Add new spy support : struct iw_spy_data & prototypes
+ *
+ * V5 to V6
+ * --------
+ *	- Change the way we get to spy_data method for added safety
+ *	- Remove spy #ifdef, they are always on -> cleaner code
+ *	- Add IW_DESCR_FLAG_NOMAX flag for very large requests
+ *	- Start migrating get_wireless_stats to struct iw_handler_def
  */
 
 /**************************** CONSTANTS ****************************/
 
-/* Enable enhanced spy support. Disable to reduce footprint */
+/* Enhanced spy support available */
 #define IW_WIRELESS_SPY
 #define IW_WIRELESS_THRSPY
 
@@ -258,6 +265,7 @@
 #define IW_DESCR_FLAG_EVENT	0x0002	/* Generate an event on SET */
 #define IW_DESCR_FLAG_RESTRICT	0x0004	/* GET : request is ROOT only */
 				/* SET : Omit payload from generated iwevent */
+#define IW_DESCR_FLAG_NOMAX	0x0008	/* GET : no limit on request size */
 /* Driver level flags */
 #define IW_DESCR_FLAG_WAIT	0x0100	/* Wait for driver event */
 
@@ -311,23 +319,25 @@
 	/* Array of handlers for standard ioctls
 	 * We will call dev->wireless_handlers->standard[ioctl - SIOCSIWNAME]
 	 */
-	iw_handler *		standard;
+	const iw_handler *	standard;
 
 	/* Array of handlers for private ioctls
 	 * Will call dev->wireless_handlers->private[ioctl - SIOCIWFIRSTPRIV]
 	 */
-	iw_handler *		private;
+	const iw_handler *	private;
 
 	/* Arguments of private handler. This one is just a list, so you
 	 * can put it in any order you want and should not leave holes...
 	 * We will automatically export that to user space... */
-	struct iw_priv_args *	private_args;
+	const struct iw_priv_args *	private_args;
 
-	/* Driver enhanced spy support */
-	long			spy_offset;	/* Spy data offset */
+	/* This field will be *removed* in the next version of WE */
+	long			spy_offset;	/* DO NOT USE */
 
-	/* In the long term, get_wireless_stats will move from
-	 * 'struct net_device' to here, to minimise bloat. */
+	/* New location of get_wireless_stats, to de-bloat struct net_device.
+	 * The old pointer in struct net_device will be gradually phased
+	 * out, and drivers are encouraged to use this one... */
+	struct iw_statistics*	(*get_wireless_stats)(struct net_device *dev);
 };
 
 /* ---------------------- IOCTL DESCRIPTION ---------------------- */
@@ -374,18 +384,29 @@
  */
 struct iw_spy_data
 {
-#ifdef IW_WIRELESS_SPY
 	/* --- Standard spy support --- */
 	int			spy_number;
 	u_char			spy_address[IW_MAX_SPY][ETH_ALEN];
 	struct iw_quality	spy_stat[IW_MAX_SPY];
-#ifdef IW_WIRELESS_THRSPY
 	/* --- Enhanced spy support (event) */
 	struct iw_quality	spy_thr_low;	/* Low threshold */
 	struct iw_quality	spy_thr_high;	/* High threshold */
 	u_char			spy_thr_under[IW_MAX_SPY];
-#endif /* IW_WIRELESS_THRSPY */
-#endif /* IW_WIRELESS_SPY */
+};
+
+/* --------------------- DEVICE WIRELESS DATA --------------------- */
+/*
+ * This is all the wireless data specific to a device instance that
+ * is managed by the core of Wireless Extensions.
+ * We only keep pointer to those structures, so that a driver is free
+ * to share them between instances.
+ * This structure should be initialised before registering the device.
+ * Access to this data follow the same rules as any other struct net_device
+ * data (i.e. valid as long as struct net_device exist, same locking rules).
+ */
+struct iw_public_data {
+	/* Driver enhanced spy support */
+	struct iw_spy_data *	spy_data;
 };
 
 /**************************** PROTOTYPES ****************************/
Index: net/core/dev.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/net/core/dev.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- net/core/dev.c	4 Aug 2004 01:30:18 -0000	1.2
+++ net/core/dev.c	19 Oct 2006 17:43:03 -0000	1.3
@@ -2410,7 +2410,7 @@
 				/* Follow me in net/core/wireless.c */
 				ret = wireless_process_ioctl(&ifr, cmd);
 				rtnl_unlock();
-				if (!ret && IW_IS_GET(cmd) &&
+				if (IW_IS_GET(cmd) &&
 				    copy_to_user(arg, &ifr, sizeof(struct ifreq)))
 					return -EFAULT;
 				return ret;
Index: net/core/wireless.c
===================================================================
RCS file: /cvsroot/ts-7200/dist/linux24/net/core/wireless.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- net/core/wireless.c	4 Aug 2004 01:30:18 -0000	1.2
+++ net/core/wireless.c	19 Oct 2006 17:43:03 -0000	1.3
@@ -2,7 +2,7 @@
  * This file implement the Wireless Extensions APIs.
  *
  * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>
- * Copyright (c) 1997-2003 Jean Tourrilhes, All Rights Reserved.
+ * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved.
  *
  * (As all part of the Linux kernel, this file is GPL)
  */
@@ -48,6 +48,16 @@
  *	o Add common spy support : iw_handler_set_spy(), wireless_spy_update()
  *	o Add enhanced spy support : iw_handler_set_thrspy() and event.
  *	o Add WIRELESS_EXT version display in /proc/net/wireless
+ *
+ * v6 - 18.06.04 - Jean II
+ *	o Change get_spydata() method for added safety
+ *	o Remove spy #ifdef, they are always on -> cleaner code
+ *	o Allow any size GET request if user specifies length > max
+ *		and if request has IW_DESCR_FLAG_NOMAX flag or is SIOCGIWPRIV
+ *	o Start migrating get_wireless_stats to struct iw_handler_def
+ *	o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus
+ * Based on patch from Pavel Roskin <proski@gnu.org> :
+ *	o Fix kernel data leak to user space in private handler handling
  */
 
 /***************************** INCLUDES *****************************/
@@ -64,11 +74,7 @@
 
 /**************************** CONSTANTS ****************************/
 
-/* Enough lenience, let's make sure things are proper... */
-#define WE_STRICT_WRITE		/* Check write buffer size */
-/* I'll probably drop both the define and kernel message in the next version */
-
-/* Debuging stuff */
+/* Debugging stuff */
 #undef WE_IOCTL_DEBUG		/* Debug IOCTL API */
 #undef WE_EVENT_DEBUG		/* Debug Event dispatcher */
 #undef WE_SPY_DEBUG		/* Debug enhanced spy support */
@@ -131,14 +137,14 @@
 	{ IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0},
 	/* SIOCGIWAP */
 	{ IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP},
-	/* -- hole -- */
-	{ IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+	/* SIOCSIWMLME */
+	{ IW_HEADER_TYPE_POINT, 0, 1, sizeof(struct iw_mlme), sizeof(struct iw_mlme), 0},
 	/* SIOCGIWAPLIST */
-	{ IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_AP, 0},
+	{ IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_AP, IW_DESCR_FLAG_NOMAX},
 	/* SIOCSIWSCAN */
-	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+	{ IW_HEADER_TYPE_POINT, 0, 1, 0, sizeof(struct iw_scan_req), 0},
 	/* SIOCGIWSCAN */
-	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_SCAN_MAX_DATA, 0},
+	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_SCAN_MAX_DATA, IW_DESCR_FLAG_NOMAX},
 	/* SIOCSIWESSID */
 	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE + 1, IW_DESCR_FLAG_EVENT},
 	/* SIOCGIWESSID */
@@ -179,6 +185,25 @@
 	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
 	/* SIOCGIWPOWER */
 	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+	/* -- hole -- */
+	{ IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+	/* -- hole -- */
+	{ IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+	/* SIOCSIWGENIE */
+	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_GENERIC_IE_MAX, 0},
+	/* SIOCGIWGENIE */
+	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_GENERIC_IE_MAX, 0},
+	/* SIOCSIWAUTH */
+	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+	/* SIOCGIWAUTH */
+	{ IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+	/* SIOCSIWENCODEEXT */
+	{ IW_HEADER_TYPE_POINT, 0, 1, sizeof(struct iw_encode_ext), sizeof(struct iw_encode_ext) + IW_ENCODING_TOKEN_MAX, 0},
+	/* SIOCGIWENCODEEXT */
+	{ IW_HEADER_TYPE_POINT, 0, 1, sizeof(struct iw_encode_ext), sizeof(struct iw_encode_ext) + IW_ENCODING_TOKEN_MAX, 0},
+	/* SIOCSIWPMKSA */
+	{ IW_HEADER_TYPE_POINT, 0, 1, sizeof(struct iw_pmksa), sizeof(struct iw_pmksa), 0},
+	/* -- hole -- */
 };
 static const int standard_ioctl_num = (sizeof(standard_ioctl) /
 				       sizeof(struct iw_ioctl_description));
@@ -198,12 +223,22 @@
 	{ IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0},
 	/* IWEVEXPIRED */
 	{ IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0},
+	/* IWEVGENIE */
+	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_GENERIC_IE_MAX, 0},
+	/* IWEVMICHAELMICFAILURE */
+	{ IW_HEADER_TYPE_POINT, 0, 1, 0, sizeof(struct iw_michaelmicfailure), 0},
+	/* IWEVASSOCREQIE */
+	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_GENERIC_IE_MAX, 0},
+	/* IWEVASSOCRESPIE */
+	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_GENERIC_IE_MAX, 0},
+	/* IWEVPMKIDCAND */
+	{ IW_HEADER_TYPE_POINT, 0, 1, 0, sizeof(struct iw_pmkid_cand), 0},
 };
 static const int standard_event_num = (sizeof(standard_event) /
 				       sizeof(struct iw_ioctl_description));
 
 /* Size (in bytes) of the various private data types */
-static const char priv_type_size[] = {
+static const char iw_priv_type_size[] = {
 	0,				/* IW_PRIV_TYPE_NONE */
 	1,				/* IW_PRIV_TYPE_BYTE */
 	1,				/* IW_PRIV_TYPE_CHAR */
@@ -270,12 +305,15 @@
  */
 static inline struct iw_statistics *get_wireless_stats(struct net_device *dev)
 {
+	/* New location */
+	if((dev->wireless_handlers != NULL) &&
+	   (dev->wireless_handlers->get_wireless_stats != NULL))
+		return dev->wireless_handlers->get_wireless_stats(dev);
+
+	/* Old location, will be phased out in next WE */
 	return (dev->get_wireless_stats ?
 		dev->get_wireless_stats(dev) :
 		(struct iw_statistics *) NULL);
-	/* In the future, get_wireless_stats may move from 'struct net_device'
-	 * to 'struct iw_handler_def', to de-bloat struct net_device.
-	 * Definitely worse a thought... */
 }
 
 /* ---------------------------------------------------------------- */
@@ -310,14 +348,32 @@
 
 /* ---------------------------------------------------------------- */
 /*
- * Number of private arguments
+ * Calculate size of private arguments
  */
 static inline int get_priv_size(__u16	args)
 {
 	int	num = args & IW_PRIV_SIZE_MASK;
 	int	type = (args & IW_PRIV_TYPE_MASK) >> 12;
 
-	return num * priv_type_size[type];
+	return num * iw_priv_type_size[type];
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Re-calculate the size of private arguments
+ */
+static inline int adjust_priv_size(__u16		args,
+				   union iwreq_data *	wrqu)
+{
+	int	num = wrqu->data.length;
+	int	max = args & IW_PRIV_SIZE_MASK;
+	int	type = (args & IW_PRIV_TYPE_MASK) >> 12;
+
+	/* Make sure the driver doesn't goof up */
+	if (max < num)
+		num = max;
+
+	return num * iw_priv_type_size[type];
 }
 
 
@@ -350,11 +406,14 @@
 			       dev->name,
 			       stats->status,
 			       stats->qual.qual,
-			       stats->qual.updated & 1 ? '.' : ' ',
+			       stats->qual.updated & IW_QUAL_QUAL_UPDATED
+			       ? '.' : ' ',
 			       ((__u8) stats->qual.level),
-			       stats->qual.updated & 2 ? '.' : ' ',
+			       stats->qual.updated & IW_QUAL_LEVEL_UPDATED
+			       ? '.' : ' ',
 			       ((__u8) stats->qual.noise),
-			       stats->qual.updated & 4 ? '.' : ' ',
+			       stats->qual.updated & IW_QUAL_NOISE_UPDATED
+			       ? '.' : ' ',
 			       stats->discard.nwid,
 			       stats->discard.code,
 			       stats->discard.fragment,
@@ -470,13 +529,15 @@
 	/* Check NULL pointer */
 	if(iwr->u.data.pointer == NULL)
 		return -EFAULT;
-#ifdef WE_STRICT_WRITE
+
 	/* Check if there is enough buffer up there */
 	if(iwr->u.data.length < dev->wireless_handlers->num_private_args) {
-		printk(KERN_ERR "%s (WE) : Buffer for request SIOCGIWPRIV too small (%d<%d)\n", dev->name, iwr->u.data.length, dev->wireless_handlers->num_private_args);
+		/* User space can't know in advance how large the buffer
+		 * needs to be. Give it a hint, so that we can support
+		 * any size buffer we want somewhat efficiently... */
+		iwr->u.data.length = dev->wireless_handlers->num_private_args;
 		return -E2BIG;
 	}
-#endif	/* WE_STRICT_WRITE */
 
 	/* Set the number of available ioctls. */
 	iwr->u.data.length = dev->wireless_handlers->num_private_args;
@@ -505,7 +566,6 @@
 	const struct iw_ioctl_description *	descr;
 	struct iw_request_info			info;
 	int					ret = -EINVAL;
-	int					user_size = 0;
 
 	/* Get the description of the IOCTL */
 	if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
@@ -536,8 +596,14 @@
 #endif	/* WE_SET_EVENT */
 	} else {
 		char *	extra;
+		int	extra_size;
+		int	user_length = 0;
 		int	err;
 
+		/* Calculate space needed by arguments. Always allocate
+		 * for max space. Easier, and won't last long... */
+		extra_size = descr->max_tokens * descr->token_size;
+
 		/* Check what user space is giving us */
 		if(IW_IS_SET(cmd)) {
 			/* Check NULL pointer */
@@ -554,18 +620,33 @@
 			if(iwr->u.data.pointer == NULL)
 				return -EFAULT;
 			/* Save user space buffer size for checking */
-			user_size = iwr->u.data.length;
+			user_length = iwr->u.data.length;
+
+			/* Don't check if user_length > max to allow forward
+			 * compatibility. The test user_length < min is
+			 * implied by the test at the end. */
+
+			/* Support for very large requests */
+			if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
+			   (user_length > descr->max_tokens)) {
+				/* Allow userspace to GET more than max so
+				 * we can support any size GET requests.
+				 * There is still a limit : -ENOMEM. */
+				extra_size = user_length * descr->token_size;
+				/* Note : user_length is originally a __u16,
+				 * and token_size is controlled by us,
+				 * so extra_size won't get negative and
+				 * won't overflow... */
+			}
 		}
 
 #ifdef WE_IOCTL_DEBUG
 		printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
-		       dev->name, descr->max_tokens * descr->token_size);
+		       dev->name, extra_size);
 #endif	/* WE_IOCTL_DEBUG */
 
-		/* Always allocate for max space. Easier, and won't last
-		 * long... */
-		extra = kmalloc(descr->max_tokens * descr->token_size,
-				GFP_KERNEL);
+		/* Create the kernel buffer */
+		extra = kmalloc(extra_size, GFP_KERNEL);
 		if (extra == NULL) {
 			return -ENOMEM;
 		}
@@ -591,14 +672,11 @@
 
 		/* If we have something to return to the user */
 		if (!ret && IW_IS_GET(cmd)) {
-#ifdef WE_STRICT_WRITE
 			/* Check if there is enough buffer up there */
-			if(user_size < iwr->u.data.length) {
-				printk(KERN_ERR "%s (WE) : Buffer for request %04X too small (%d<%d)\n", dev->name, cmd, user_size, iwr->u.data.length);
+			if(user_length < iwr->u.data.length) {
 				kfree(extra);
 				return -E2BIG;
 			}
-#endif	/* WE_STRICT_WRITE */
 
 			err = copy_to_user(iwr->u.data.pointer, extra,
 					   iwr->u.data.length *
@@ -661,7 +739,7 @@
 				     iw_handler		handler)
 {
 	struct iwreq *			iwr = (struct iwreq *) ifr;
-	struct iw_priv_args *		descr = NULL;
+	const struct iw_priv_args *	descr = NULL;
 	struct iw_request_info		info;
 	int				extra_size = 0;
 	int				i;
@@ -701,7 +779,7 @@
 			   ((extra_size + offset) <= IFNAMSIZ))
 				extra_size = 0;
 		} else {
-			/* Size of set arguments */
+			/* Size of get arguments */
 			extra_size = get_priv_size(descr->get_args);
 
 			/* Does it fits in iwr ? */
@@ -771,6 +849,14 @@
 
 		/* If we have something to return to the user */
 		if (!ret && IW_IS_GET(cmd)) {
+
+			/* Adjust for the actual length if it's variable,
+			 * avoid leaking kernel bits outside. */
+			if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) {
+				extra_size = adjust_priv_size(descr->get_args,
+							      &(iwr->u));
+			}
+
 			err = copy_to_user(iwr->u.data.pointer, extra,
 					   extra_size);
 			if (err)
@@ -1042,9 +1128,25 @@
  * One of the main advantage of centralising spy support here is that
  * it becomes much easier to improve and extend it without having to touch
  * the drivers. One example is the addition of the Spy-Threshold events.
- * Note : IW_WIRELESS_SPY is defined in iw_handler.h
  */
 
+/* ---------------------------------------------------------------- */
+/*
+ * Return the pointer to the spy data in the driver.
+ * Because this is called on the Rx path via wireless_spy_update(),
+ * we want it to be efficient...
+ */
+static inline struct iw_spy_data * get_spydata(struct net_device *dev)
+{
+	/* This is the new way */
+	if(dev->wireless_data)
+		return(dev->wireless_data->spy_data);
+
+	/* This is the old way. Doesn't work for multi-headed drivers.
+	 * It will be removed in the next version of WE. */
+	return (dev->priv + dev->wireless_handlers->spy_offset);
+}
+
 /*------------------------------------------------------------------*/
 /*
  * Standard Wireless Handler : set Spy List
@@ -1054,16 +1156,26 @@
 		       union iwreq_data *	wrqu,
 		       char *			extra)
 {
-#ifdef IW_WIRELESS_SPY
-	struct iw_spy_data *	spydata = (dev->priv +
-					   dev->wireless_handlers->spy_offset);
+	struct iw_spy_data *	spydata = get_spydata(dev);
 	struct sockaddr *	address = (struct sockaddr *) extra;
 
+	/* Make sure driver is not buggy or using the old API */
+	if(!spydata)
+		return -EOPNOTSUPP;
+
 	/* Disable spy collection while we copy the addresses.
-	 * As we don't disable interrupts, we need to do this to avoid races.
-	 * As we are the only writer, this is good enough. */
+	 * While we copy addresses, any call to wireless_spy_update()
+	 * will NOP. This is OK, as anyway the addresses are changing. */
 	spydata->spy_number = 0;
 
+	/* We want to operate without locking, because wireless_spy_update()
+	 * most likely will happen in the interrupt handler, and therefore
+	 * have its own locking constraints and needs performance.
+	 * The rtnl_lock() make sure we don't race with the other iw_handlers.
+	 * This make sure wireless_spy_update() "see" that the spy list
+	 * is temporarily disabled. */
+	wmb();
+
 	/* Are there are addresses to copy? */
 	if(wrqu->data.length > 0) {
 		int i;
@@ -1089,13 +1201,14 @@
 			       spydata->spy_address[i][5]);
 #endif	/* WE_SPY_DEBUG */
 	}
+
+	/* Make sure above is updated before re-enabling */
+	wmb();
+
 	/* Enable addresses */
 	spydata->spy_number = wrqu->data.length;
 
 	return 0;
-#else /* IW_WIRELESS_SPY */
-	return -EOPNOTSUPP;
-#endif /* IW_WIRELESS_SPY */
 }
 
 /*------------------------------------------------------------------*/
@@ -1107,12 +1220,14 @@
 		       union iwreq_data *	wrqu,
 		       char *			extra)
 {
-#ifdef IW_WIRELESS_SPY
-	struct iw_spy_data *	spydata = (dev->priv +
-					   dev->wireless_handlers->spy_offset);
+	struct iw_spy_data *	spydata = get_spydata(dev);
 	struct sockaddr *	address = (struct sockaddr *) extra;
 	int			i;
 
+	/* Make sure driver is not buggy or using the old API */
+	if(!spydata)
+		return -EOPNOTSUPP;
+
 	wrqu->data.length = spydata->spy_number;
 
 	/* Copy addresses. */
@@ -1129,9 +1244,6 @@
 	for(i = 0; i < spydata->spy_number; i++)
 		spydata->spy_stat[i].updated = 0;
 	return 0;
-#else /* IW_WIRELESS_SPY */
-	return -EOPNOTSUPP;
-#endif /* IW_WIRELESS_SPY */
 }
 
 /*------------------------------------------------------------------*/
@@ -1143,11 +1255,13 @@
 			  union iwreq_data *	wrqu,
 			  char *		extra)
 {
-#ifdef IW_WIRELESS_THRSPY
-	struct iw_spy_data *	spydata = (dev->priv +
-					   dev->wireless_handlers->spy_offset);
+	struct iw_spy_data *	spydata = get_spydata(dev);
 	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;
 
+	/* Make sure driver is not buggy or using the old API */
+	if(!spydata)
+		return -EOPNOTSUPP;
+
 	/* Just do it */
 	memcpy(&(spydata->spy_thr_low), &(threshold->low),
 	       2 * sizeof(struct iw_quality));
@@ -1160,9 +1274,6 @@
 #endif	/* WE_SPY_DEBUG */
 
 	return 0;
-#else /* IW_WIRELESS_THRSPY */
-	return -EOPNOTSUPP;
-#endif /* IW_WIRELESS_THRSPY */
 }
 
 /*------------------------------------------------------------------*/
@@ -1174,22 +1285,20 @@
 			  union iwreq_data *	wrqu,
 			  char *		extra)
 {
-#ifdef IW_WIRELESS_THRSPY
-	struct iw_spy_data *	spydata = (dev->priv +
-					   dev->wireless_handlers->spy_offset);
+	struct iw_spy_data *	spydata = get_spydata(dev);
 	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;
 
+	/* Make sure driver is not buggy or using the old API */
+	if(!spydata)
+		return -EOPNOTSUPP;
+
 	/* Just do it */
 	memcpy(&(threshold->low), &(spydata->spy_thr_low),
 	       2 * sizeof(struct iw_quality));
 
 	return 0;
-#else /* IW_WIRELESS_THRSPY */
-	return -EOPNOTSUPP;
-#endif /* IW_WIRELESS_THRSPY */
 }
 
-#ifdef IW_WIRELESS_THRSPY
 /*------------------------------------------------------------------*/
 /*
  * Prepare and send a Spy Threshold event
@@ -1227,7 +1336,6 @@
 	/* Send event to user space */
 	wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
 }
-#endif /* IW_WIRELESS_THRSPY */
 
 /* ---------------------------------------------------------------- */
 /*
@@ -1240,12 +1348,14 @@
 			 unsigned char *	address,
 			 struct iw_quality *	wstats)
 {
-#ifdef IW_WIRELESS_SPY
-	struct iw_spy_data *	spydata = (dev->priv +
-					   dev->wireless_handlers->spy_offset);
+	struct iw_spy_data *	spydata = get_spydata(dev);
 	int			i;
 	int			match = -1;
 
+	/* Make sure driver is not buggy or using the old API */
+	if(!spydata)
+		return;
+
 #ifdef WE_SPY_DEBUG
 	printk(KERN_DEBUG "wireless_spy_update() :  offset %ld, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_handlers->spy_offset, spydata, address[0], address[1], address[2], address[3], address[4], address[5]);
 #endif	/* WE_SPY_DEBUG */
@@ -1257,7 +1367,7 @@
 			       sizeof(struct iw_quality));
 			match = i;
 		}
-#ifdef IW_WIRELESS_THRSPY
+
 	/* Generate an event if we cross the spy threshold.
 	 * To avoid event storms, we have a simple hysteresis : we generate
 	 * event only when we go under the low threshold or above the
@@ -1277,6 +1387,4 @@
 			}
 		}
 	}
-#endif /* IW_WIRELESS_THRSPY */
-#endif /* IW_WIRELESS_SPY */
 }
