diff --git a/cmt/Makefile b/cmt/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..950cb692304af59c0152aeafa7811e590d5ae3e9
--- /dev/null
+++ b/cmt/Makefile
@@ -0,0 +1,27 @@
+#
+KERNEL_VERSION := $(shell uname -r)
+KERNEL_MODLIB  := /lib/modules/$(KERNEL_VERSION)
+KERNEL_SOURCES := $(shell test -d $(KERNEL_MODLIB)/source && echo $(KERNEL_MODLIB)/source || echo $(KERNEL_MODLIB)/build)
+CPPFLAGS += -I/afs/cern.ch/atlas/project/tdaq/cmt/nightly/installed/include -DDRIVER_ERROR -DDRIVER_DEBUG -DCVSTAG=\"tagtag\" -DRELEASE_NAME=\"${CMTRELEASE}\" 
+
+ifneq ($(KERNELRELEASE),)
+obj-m	:= quest.o solar.o cmem_rcc.o io_rcc.o vme_rcc.o filar.o robin.o
+#obj-m	:= quest.o solar.o cmem_rcc.o io_rcc.o vme_rcc.o filar.o filar_dma.o robin.o
+else
+KDIR	:= $(KERNEL_SOURCES)
+PWD	:= $(shell pwd)
+default:
+	$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules 
+	@if [ ! -d ../$(CMTCONFIG) ]; then \
+	  echo Making directory ; \
+	  mkdir ../$(CMTCONFIG) ; \
+	fi	
+	cp quest.ko     ../$(CMTCONFIG)/quest-${KERNEL_VERSION}.ko
+	cp solar.ko     ../$(CMTCONFIG)/solar-${KERNEL_VERSION}.ko
+	cp io_rcc.ko    ../$(CMTCONFIG)/io_rcc-${KERNEL_VERSION}.ko
+	cp cmem_rcc.ko  ../$(CMTCONFIG)/cmem_rcc-${KERNEL_VERSION}.ko
+	cp vme_rcc.ko   ../$(CMTCONFIG)/vme_rcc-${KERNEL_VERSION}.ko
+	cp filar.ko     ../$(CMTCONFIG)/filar-${KERNEL_VERSION}.ko
+#	cp filar_dma.ko ../$(CMTCONFIG)/filar_dma-${KERNEL_VERSION}.ko
+	cp robin.ko     ../$(CMTCONFIG)/robin-${KERNEL_VERSION}.ko
+endif
diff --git a/cmt/cmem_rcc.c b/cmt/cmem_rcc.c
new file mode 100644
index 0000000000000000000000000000000000000000..cd59c1b2f2e23c01402048ebf708029bf4629ef8
--- /dev/null
+++ b/cmt/cmem_rcc.c
@@ -0,0 +1,889 @@
+/************************************************************************/
+/*									*/
+/*  This is the CMEM_RCC driver	 					*/
+/*  Its purpose is to provide user applications with contiguous data 	*/
+/*  buffers for DMA operations. 					*/
+/*									*/
+/*  12. Dec. 01  MAJO  created						*/
+/*									*/
+/*******C 2005 - The software with that certain something****************/
+
+/************************************************************************/
+/*NOTES:								*/
+/*- This driver should work on kernels from 2.6.9 onwards		*/
+/************************************************************************/
+ 
+
+#include <linux/init.h>           //MJ: for 2.6, p30
+#include <linux/module.h>
+#include <linux/moduleparam.h>    //MJ: for 2.6, p30
+#include <linux/kernel.h>
+#include <linux/stat.h>           //MJ: for 2.6, e.g. for module_param
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>           //e.g. for cdev_alloc
+#include <linux/proc_fs.h>
+#include <linux/bigphysarea.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include "cmem_rcc/cmem_rcc_drv.h"
+
+
+// Globals
+static int debug = 0;
+static char *proc_read_text;
+buffer_t *buffer_table;
+static u_long* sram_p_addr;
+static dev_t major_minor;
+static struct cdev *cmem_rcc_cdev;
+
+// Prototypes
+static void cmem_rcc_vmaClose(struct vm_area_struct *vma);
+static void cmem_rcc_vmaOpen(struct vm_area_struct *vma);
+static int cmem_rcc_open(struct inode *inode, struct file *file);
+static int cmem_rcc_release(struct inode *inode, struct file *file);
+static int cmem_rcc_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg);
+static int cmem_rcc_mmap(struct file *file, struct vm_area_struct *vma);
+static int cmem_rcc_proc_write(struct file *file, const char *buffer, u_long count, void *data);
+static int cmem_rcc_proc_read(char *buf, char **start, off_t offset, int count, int *eof, void *data);
+
+
+/***************************************************************/
+/* Use /sbin/modinfo <module name> to extract this information */
+/***************************************************************/
+module_param (debug, int, S_IRUGO | S_IWUSR);  //MJ: for 2.6 p37
+MODULE_PARM_DESC(debug, "1 = enable debugging   0 = disable debugging");
+MODULE_DESCRIPTION("Allocation of contiguous memory");
+MODULE_AUTHOR("Markus Joos, CERN/EP");
+MODULE_LICENSE("Private: Contact markus.joos@cern.ch");
+MODULE_VERSION("2.0");
+
+
+// The ordinary device operations
+static struct file_operations fops =
+{
+  .owner   = THIS_MODULE,
+  .ioctl   = cmem_rcc_ioctl,
+  .open    = cmem_rcc_open,
+  .mmap    = cmem_rcc_mmap,
+  .release = cmem_rcc_release,
+};
+
+// memory handler functions. MJ: Not actually required. Just for kdebug
+static struct vm_operations_struct cmem_rcc_vm_ops = 
+{
+  .close = cmem_rcc_vmaClose,   
+  .open  = cmem_rcc_vmaOpen,      //MJ: Note the comma at the end of the list!
+};
+  
+  
+/*****************************/
+/* Standard driver functions */
+/*****************************/
+
+/****************************/
+static int cmem_rcc_init(void)
+/****************************/
+{
+  int loop, ecode;
+  static u_long* sram_k_addr;
+  struct cmem_proc_data_t cmem_proc_data;
+  static struct proc_dir_entry *cmem_rcc_file;
+
+  ecode = alloc_chrdev_region (&major_minor, 0, 1, "cmem_rcc"); //MJ: for 2.6 p45
+  if (ecode)
+  {
+    kdebug(("cmem_rcc(cmem_rcc_init): failed to obtain device numbers\n"));
+    goto fail1;
+  }
+      
+  proc_read_text = (char *)kmalloc(MAX_PROC_TEXT_SIZE, GFP_KERNEL);
+  if (proc_read_text == NULL)
+  {
+    ecode = -ENOMEM;
+    kdebug(("cmem_rcc(cmem_rcc_init): error from kmalloc\n"));
+    goto fail2;
+  }
+      
+  // Install /proc entry
+  cmem_rcc_file = create_proc_entry("cmem_rcc", 0644, NULL);
+  if (cmem_rcc_file == NULL)
+  {
+    kdebug(("cmem_rcc(cmem_rcc_init): error from call to create_proc_entry\n"));
+    ecode = -EFAULT;
+    goto fail3;
+  }
+
+  strcpy(cmem_proc_data.name, "cmem_rcc");
+  strcpy(cmem_proc_data.value, "cmem_rcc");
+  cmem_rcc_file->data       = &cmem_proc_data;
+  cmem_rcc_file->read_proc  = cmem_rcc_proc_read;
+  cmem_rcc_file->write_proc = cmem_rcc_proc_write;
+  cmem_rcc_file->owner      = THIS_MODULE;
+
+  // Allocate memory for the buffer table
+  kdebug(("cmem_rcc(cmem_rcc_init): MAX_BUFFS        = %d\n", MAX_BUFFS));
+  kdebug(("cmem_rcc(cmem_rcc_init): sizeof(buffer_t) = %d\n", sizeof(buffer_t)));
+  kdebug(("cmem_rcc(cmem_rcc_init): need %d bytes\n", MAX_BUFFS * sizeof(buffer_t)));
+  buffer_table = (buffer_t *)kmalloc(MAX_BUFFS * sizeof(buffer_t), GFP_KERNEL);
+  if (buffer_table == NULL)
+  {
+    printk("cmem_rcc(cmem_rcc_init): unable to allocate memory for buffer table\n");
+    ecode = -EFAULT;
+    goto fail4;
+  }
+  
+  // Clear the buffer table
+  for(loop = 0; loop < MAX_BUFFS; loop++)
+  {
+    buffer_table[loop].paddr  = 0;
+    buffer_table[loop].size   = 0;
+    buffer_table[loop].used   = 0;
+    buffer_table[loop].locked = 0;
+    buffer_table[loop].type   = 0;
+    buffer_table[loop].pid    = 0;
+  }
+
+  // Temporarily allocate the first page to get the PCI base address for the auto-map feature in vmeconfig
+  sram_k_addr = (u_long *) bigphysarea_alloc_pages(1, 0, GFP_KERNEL);
+  if (sram_k_addr)
+  {
+    //MJ: Rubini does not like virt_to_bus but for the simple thing we want to do here it is probably OK 
+    sram_p_addr = (u_long *) virt_to_bus(sram_k_addr);
+    kdebug(("cmem_rcc(cmem_rcc_init): sram_k_addr = 0x%08lx\n", (u_long)sram_k_addr));
+    kdebug(("cmem_rcc(cmem_rcc_init): sram_p_addr = 0x%08lx\n", (u_long)sram_p_addr));
+    bigphysarea_free_pages((void *)sram_k_addr);
+  }
+  else
+  {
+    kdebug(("cmem_rcc(cmem_rcc_init): Failed to determine the BPA base address\n"));
+    kdebug(("cmem_rcc(cmem_rcc_init): Check if BPA memory has been reserved at boot time\n"));
+    sram_p_addr = 0;
+  }
+
+  cmem_rcc_cdev = (struct cdev *)cdev_alloc();      //MJ: for 2.6 p55
+  cmem_rcc_cdev->ops = &fops;
+  ecode = cdev_add(cmem_rcc_cdev, major_minor, 1);  //MJ: for 2.6 p56
+  if (ecode)
+  {
+    kdebug(("cmem_rcc(cmem_rcc_init): error from call to cdev_add.\n"));
+    goto fail5;
+  }
+  
+  kdebug(("cmem_rcc(cmem_rcc_init): driver loaded; major device number = %d\n", MAJOR(major_minor)));
+  return(0);
+
+  fail5:
+    kfree(buffer_table);
+
+  fail4:
+    remove_proc_entry("cmem_rcc", NULL);
+
+  fail3:
+    kfree(proc_read_text);
+
+  fail2:
+    unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45
+
+  fail1:
+    return(ecode);
+}
+
+
+/********************************/
+static void cmem_rcc_cleanup(void)
+/********************************/
+{
+  int loop, loop2;
+  struct page *page_ptr;
+
+  // Release orphaned buffers
+  for(loop = 0; loop < MAX_BUFFS; loop++)
+  {
+    if (buffer_table[loop].used)
+    {
+      if (buffer_table[loop].locked)
+      {
+        kdebug(("cmem_rcc(cmem_rcc_cleanup): releasing locked buffer: type=%d  paddr=0x%08lx  size=0x%08x  name=%s\n",
+        buffer_table[loop].type, buffer_table[loop].paddr, buffer_table[loop].size, buffer_table[loop].name));
+      }
+      else
+      {
+        kdebug(("cmem_rcc(cmem_rcc_cleanup): Releasing orphaned buffer: type=%d  paddr=0x%08lx  size=0x%08x  name=%s\n",
+        buffer_table[loop].type, buffer_table[loop].paddr, buffer_table[loop].size, buffer_table[loop].name));
+      } 	
+      
+      if (buffer_table[loop].type == TYPE_GFP)
+      {
+	// unreserve all pages
+	page_ptr = virt_to_page(buffer_table[loop].kaddr);
+
+	for (loop2 = (1 << buffer_table[loop].order); loop2 > 0; loop2--, page_ptr++)
+          clear_bit(PG_reserved, &page_ptr->flags);
+
+	// free the area 
+	free_pages(buffer_table[loop].kaddr, buffer_table[loop].order);
+      }
+      else
+	bigphysarea_free_pages((void *)buffer_table[loop].kaddr);
+    }
+  }
+  
+  cdev_del(cmem_rcc_cdev);                     //MJ: for 2.6 p56
+
+  // Remove /proc entry
+  remove_proc_entry("cmem_rcc", NULL);
+  kfree(proc_read_text);
+  
+  // Return the buffer table
+  kfree(buffer_table); 
+
+  // Unregister the device 
+  unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45
+}
+
+
+module_init(cmem_rcc_init);    //MJ: for 2.6 p16
+module_exit(cmem_rcc_cleanup); //MJ: for 2.6 p16
+
+
+/**************************************************************/
+static int cmem_rcc_open(struct inode *inode, struct file *file)
+/**************************************************************/
+{
+  int loop;
+  private_stuff *pptr; 
+  
+  kdebug(("cmem_rcc(cmem_rcc_open): function called for file at 0x%016lx\n", (u_long)file))
+  //reserve space to store information about the memory buffers managed by this "file"
+  pptr = (private_stuff *)kmalloc(sizeof(private_stuff), GFP_KERNEL);
+  if (pptr == NULL)
+  {
+    kdebug(("cmem_rcc(cmem_rcc_open): error from kmalloc\n"));
+    return(-EFAULT);
+  }
+
+  //Initialize the space
+  for (loop = 0; loop < MAX_BUFFS; loop++)
+    pptr->buffer[loop] = 0;
+
+  file->private_data = pptr;
+  kdebug(("cmem_rcc(cmem_rcc_open): private_data = 0x%08lx\n", (u_long)file->private_data));
+
+  return(0);
+}
+
+
+/*****************************************************************/
+static int cmem_rcc_release(struct inode *inode, struct file *file)
+/*****************************************************************/
+{
+  int loop, loop2;
+  struct page *page_ptr;
+  private_stuff *pptr; 
+
+  kdebug(("cmem_rcc(cmem_rcc_release): function called from process %d for file at 0x%016lx\n", current->pid, (u_long)file));
+  pptr = (private_stuff *) file->private_data;
+
+  // Release orphaned buffers of the current process
+//MJ-SMP: protect this fragment (preferrably with a spinlock)
+  for(loop = 0; loop < MAX_BUFFS; loop++)
+  {
+    if ((pptr->buffer[loop] == 1) && (!buffer_table[loop].locked == 1))
+    {
+      if (buffer_table[loop].type == TYPE_GFP)
+      {
+	// unreserve all pages
+	page_ptr = virt_to_page(buffer_table[loop].kaddr);
+
+	for (loop2 = (1 << buffer_table[loop].order); loop2 > 0; loop2--, page_ptr++)
+          clear_bit(PG_reserved, &page_ptr->flags);
+
+	// free the area 
+	free_pages(buffer_table[loop].kaddr, buffer_table[loop].order);
+      } 
+      else
+        bigphysarea_free_pages((void *)buffer_table[loop].kaddr);
+      
+      kdebug(("cmem_rcc(cmem_rcc_release): Releasing orphaned buffer of process %d: type=%d  paddr=0x%08lx  size=0x%08x  name=%s\n",
+      buffer_table[loop].pid, buffer_table[loop].type, buffer_table[loop].paddr, buffer_table[loop].size, buffer_table[loop].name));
+
+      // clear the entry in the buffer table
+      buffer_table[loop].paddr = 0;
+      buffer_table[loop].kaddr = 0;
+      buffer_table[loop].size  = 0;
+      buffer_table[loop].type  = 0;
+      buffer_table[loop].pid   = 0;
+      buffer_table[loop].order = 0;
+      buffer_table[loop].used  = 0;
+      pptr->buffer[loop] = 0;
+    }
+  }
+//MJ-SMP: end of protected zone
+    
+  kfree(pptr);  
+  return(0);
+}
+
+
+/**************************************************************************************/
+static int cmem_rcc_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg)
+/**************************************************************************************/
+{
+  private_stuff *pptr; 
+
+  pptr = (private_stuff *) file->private_data;
+
+  switch (cmd)
+  {
+    case CMEM_RCC_GET:
+    {
+      u_int loop, tnum, ok, pagecount;
+      cmem_rcc_t uio_desc;
+      struct page *page_ptr;
+            
+      if (copy_from_user(&uio_desc, (void *)arg, sizeof(cmem_rcc_t)) !=0)
+      {
+        printk("cmem_rcc(ioctl,CMEM_RCC_GET): error in from copy_from_user\n");
+        return(-CMEM_RCC_CFU);
+      }   
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): uio_desc.size = 0x%08x\n", uio_desc.size)); 
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): uio_desc.type = 0x%08x\n", uio_desc.type)); 
+
+//MJ-SMP: protect this fragment (preferrably with a spinlock)
+      // Look for a free slot in the buffer table
+      ok = 0;
+      for(tnum = 0; tnum < MAX_BUFFS; tnum++)
+      {
+        if (buffer_table[tnum].used == 0)
+        {
+          buffer_table[tnum].used = 1;  //This is to reserve the entry 
+          pptr->buffer[tnum] = 1;       //Remember which file this buffer will belong to
+          uio_desc.handle = tnum;
+          ok = 1;
+          kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): tnum = %d\n", tnum));
+          break;
+        }
+      }
+//MJ-SMP: end of protected zone
+
+      if (!ok)
+      {
+        printk("cmem_rcc(ioctl,CMEM_RCC_GET): all buffers are in use\n");
+        return(-CMEM_RCC_OVERFLOW);
+      }
+     
+      if(uio_desc.type == TYPE_GFP)
+      {
+	uio_desc.kaddr = 0;
+	kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): calling __get_free_pages\n"));
+	uio_desc.kaddr = __get_free_pages(GFP_ATOMIC, uio_desc.order);
+	kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): __get_free_pages returns address 0x%08lx\n", (u_long)uio_desc.kaddr));
+	if (!uio_desc.kaddr)
+	{
+          printk("cmem_rcc(ioctl,CMEM_RCC_GET): error from __get_free_pages for order=%d\n", uio_desc.order);
+//MJ-SMP: protect this fragment (preferrably with a spinlock)
+          buffer_table[tnum].used = 0;  // No longer required
+          pptr->buffer[tnum] = 0;
+//MJ-SMP: end of protected zone
+          return(-CMEM_RCC_GFP);
+	}
+
+	// Reserve all pages to make them remapable
+	page_ptr = virt_to_page(uio_desc.kaddr);
+
+	kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): reserving pages\n")); 
+	for (loop = (1 << uio_desc.order); loop > 0; loop--, page_ptr++)
+          set_bit(PG_reserved, &page_ptr->flags);            //MJ: have a look at ther kernel book
+
+	uio_desc.size = PAGE_SIZE * (1 << uio_desc.order);
+      } 
+      else
+      {
+	pagecount = (int)((uio_desc.size - 1) / PAGE_SIZE + 1); // pages
+	kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): requested number of BPA pages = %d\n", pagecount));
+
+	uio_desc.kaddr = (u_long)bigphysarea_alloc_pages(pagecount, 0, GFP_KERNEL);
+        uio_desc.size = PAGE_SIZE * pagecount;
+	if (uio_desc.kaddr == 0) 
+	{
+	 printk("cmem_rcc(ioctl,CMEM_RCC_GET): error on bigphysarea_alloc_pages\n"); 
+//MJ-SMP: protect this fragment (preferrably with a spinlock)
+         buffer_table[tnum].used = 0;  //Not required any more
+         pptr->buffer[tnum] = 0;
+//MJ-SMP: end of protected zone
+	 return(-CMEM_RCC_BPA);
+	} 
+      }
+      
+      uio_desc.paddr = virt_to_bus((void *) uio_desc.kaddr);
+
+      // Complete the entry in the buffer table
+      buffer_table[tnum].size   = uio_desc.size;
+      buffer_table[tnum].paddr  = uio_desc.paddr;
+      buffer_table[tnum].kaddr  = uio_desc.kaddr;
+      buffer_table[tnum].pid    = current->pid;
+      buffer_table[tnum].order  = uio_desc.order;
+      buffer_table[tnum].type   = uio_desc.type;
+      buffer_table[tnum].locked = 0;
+      strcpy(buffer_table[tnum].name, uio_desc.name);
+  
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): PAGE_SIZE       = 0x%08x\n", (u_int)PAGE_SIZE));
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): uio_desc.kaddr  = 0x%016lx\n", (u_long)uio_desc.kaddr));
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): uio_desc.paddr  = 0x%016lx\n", (u_long)uio_desc.paddr));
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): uio_desc.size   = 0x%08x\n", (u_int)uio_desc.size));
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): uio_desc.name   = %s\n", uio_desc.name));
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): uio_desc.order  = 0x%08x\n", (u_int)uio_desc.order));
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): uio_desc.type   = 0x%08x\n", (u_int)uio_desc.type));
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): uio_desc.handle = 0x%08x\n", (u_int)uio_desc.handle));
+
+      if (copy_to_user((void *)arg, &uio_desc, sizeof(cmem_rcc_t)) != 0)
+      {
+        printk("cmem_rcc(ioctl,CMEM_RCC_GET): error in from copy_to_user\n");
+        return(-CMEM_RCC_CTU);
+      }
+
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): done\n"));
+      break;
+    }    
+       
+    case CMEM_RCC_FREE:
+    {
+      u_int handle, loop;
+      struct page *page_ptr;
+      
+      if (copy_from_user(&handle, (void *)arg, sizeof(int)) !=0)
+      {
+        printk("cmem_rcc(ioctl,CMEM_RCC_FREE): error in from copy_from_user\n");
+        return(-CMEM_RCC_CFU);
+      } 
+         
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_FREE): handle = 0x%08x\n", handle));
+
+//MJ-SMP: protect this fragment (preferrably with a spinlock)
+      // Check if the handle makes sense
+      if (buffer_table[handle].used == 0)
+      {
+        printk("cmem_rcc(ioctl,CMEM_RCC_FREE): Invalid handle\n");
+        return(-CMEM_RCC_ILLHAND);
+      }
+      buffer_table[handle].used = 0;
+//MJ-SMP: end of protected zone
+      
+      if (buffer_table[handle].type == TYPE_GFP)
+      {
+	// unreserve all pages
+	kdebug(("cmem_rcc(ioctl,CMEM_RCC_FREE): unreserving GFP pages\n"));
+	page_ptr = virt_to_page(buffer_table[handle].kaddr);
+
+	for (loop = (1 << buffer_table[handle].order); loop > 0; loop--, page_ptr++)
+          clear_bit(PG_reserved, &page_ptr->flags);
+
+	// free the area 
+	free_pages(buffer_table[handle].kaddr, buffer_table[handle].order);
+	kdebug(("cmem_rcc(ioctl,CMEM_RCC_FREE): pages freed\n")); 
+      }
+      else
+      {   
+        bigphysarea_free_pages((void *)buffer_table[handle].kaddr);
+        kdebug(("cmem_rcc(ioctl,CMEM_RCC_FREE): BPA memory freed @ address 0x%8lx\n", buffer_table[handle].kaddr));
+      }            
+ 
+      // Delete the entry in the buffer table
+      buffer_table[handle].paddr  = 0;
+      buffer_table[handle].locked = 0;
+      buffer_table[handle].pid    = 0;
+      buffer_table[handle].kaddr  = 0;
+      buffer_table[handle].type   = 0;
+      buffer_table[handle].order  = 0;
+      buffer_table[handle].size   = 0;  //This enables the entry to be re-used
+      pptr->buffer[handle] = 0;
+
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_FREE): done\n"));
+      break;
+    }
+    
+    case CMEM_RCC_LOCK:
+    {
+      u_int handle;
+      
+      if (copy_from_user(&handle, (void *)arg, sizeof(int)) !=0)
+      {
+        printk("cmem_rcc(ioctl,CMEM_RCC_LOCK): error in from copy_from_user\n");
+        return(-CMEM_RCC_CFU);
+      } 
+      
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_LOCK): handle = 0x%08x\n", handle));
+      
+      // Check if the handle makes sense
+      if (buffer_table[handle].used == 0)
+      {
+        printk("cmem_rcc(ioctl,CMEM_RCC_LOCK): Invalid handle\n");
+        return(-CMEM_RCC_ILLHAND);
+      }
+
+      buffer_table[handle].locked = 1;
+      
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_LOCK): done\n"));
+      break;
+    }
+ 
+    case CMEM_RCC_UNLOCK:
+    {
+      u_int handle;
+      
+      if (copy_from_user(&handle, (void *)arg, sizeof(int)) !=0)
+      {
+        printk("cmem_rcc(ioctl,CMEM_RCC_UNLOCK): error in from copy_from_user\n");
+        return(-CMEM_RCC_CFU);
+      } 
+      
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_UNLOCK): handle = 0x%08x\n", handle));
+      
+      // Check if the handle makes sense
+      if (buffer_table[handle].used == 0)
+      {
+        printk("cmem_rcc(ioctl,CMEM_RCC_UNLOCK): Invalid handle\n");
+        return(-CMEM_RCC_ILLHAND);
+      }
+
+      buffer_table[handle].locked = 0;
+      
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_UNLOCK): done\n"));
+      break;
+    }
+    
+    case CMEM_RCC_GETPARAMS:
+    {
+      cmem_rcc_t uio_desc;
+            
+      if (copy_from_user(&uio_desc, (void *)arg, sizeof(cmem_rcc_t)) !=0)
+      {
+        printk("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS): error in from copy_from_user\n");
+        return(-CMEM_RCC_CFU);
+      }
+      
+      // Check if the handle makes sense
+      if (buffer_table[uio_desc.handle].used == 0)
+      {
+        printk("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS): Invalid handle\n");
+        return(-CMEM_RCC_ILLHAND);
+      }	
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS): called for handle %d\n", uio_desc.handle));
+      uio_desc.paddr  = buffer_table[uio_desc.handle].paddr;
+      uio_desc.uaddr  = buffer_table[uio_desc.handle].uaddr;
+      uio_desc.kaddr  = buffer_table[uio_desc.handle].kaddr;
+      uio_desc.size   = buffer_table[uio_desc.handle].size;
+      uio_desc.order  = buffer_table[uio_desc.handle].order;
+      uio_desc.locked = buffer_table[uio_desc.handle].locked;
+      uio_desc.type   = buffer_table[uio_desc.handle].type;
+      strcpy(uio_desc.name, buffer_table[uio_desc.handle].name);
+      
+      if (copy_to_user((void *)arg, &uio_desc, sizeof(cmem_rcc_t)) != 0)
+      {
+        printk("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS): error in from copy_to_user\n");
+        return(-CMEM_RCC_CTU);
+      }
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS): done\n"));
+      break;
+    } 
+       
+    case CMEM_RCC_SETUADDR:
+    {
+      cmem_rcc_t uio_desc;
+            
+      if (copy_from_user(&uio_desc, (void *)arg, sizeof(cmem_rcc_t)) !=0)
+      {
+        printk("cmem_rcc(ioctl,CMEM_RCC_SETUADDR): error in from copy_from_user\n");
+        return(-CMEM_RCC_CFU);
+      }
+      
+      // Check if the handle makes sense
+      if (buffer_table[uio_desc.handle].used == 0)
+      {
+        printk("cmem_rcc(ioctl,CMEM_RCC_SETUADDR): Invalid handle\n");
+        return(-CMEM_RCC_ILLHAND);
+      }	
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_SETUADDR): called for handle %d\n", uio_desc.handle));
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_SETUADDR): uaddr = 0x%08lx\n", uio_desc.uaddr));
+      buffer_table[uio_desc.handle].uaddr = uio_desc.uaddr;
+      
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_SETUADDR): done\n"));
+      break;
+    } 
+             
+    case CMEM_RCC_DUMP:
+    {
+      char *buf;
+      int len, loop;
+
+      kdebug(("cmem_rcc(ioctl,CMEM_RCC_DUMP): called\n"));
+
+      buf = (char *)kmalloc(TEXT_SIZE, GFP_KERNEL);
+      if (buf == NULL)
+      {
+        kdebug(("cmem_rcc(ioctl,CMEM_RCC_DUMP): error from kmalloc\n"));
+        return(-CMEM_RCC_KMALLOC);
+      }
+
+      len = 0;
+      len += sprintf(buf + len, "Memory allocated by __get_free_pages\n");
+      len += sprintf(buf + len, "  PID | Phys. address |          Size | Order | Locked | Name\n");
+//MJ-SMP: protect this fragment (preferrably with a spinlock)
+      for(loop = 0; loop < MAX_BUFFS; loop++)
+      {
+        if (buffer_table[loop].used && buffer_table[loop].type == TYPE_GFP)
+        {
+          len += sprintf(buf + len, "%5d |", buffer_table[loop].pid);
+          len += sprintf(buf + len, "    0x%08lx |", buffer_table[loop].paddr);
+          len += sprintf(buf + len, "    0x%08x |", buffer_table[loop].size);
+          len += sprintf(buf + len, "     %d |", buffer_table[loop].order);
+          len += sprintf(buf + len, "    %s |", buffer_table[loop].locked ? "yes" : " no");
+          len += sprintf(buf + len, " %s\n", buffer_table[loop].name);
+        }
+      }
+
+      len += sprintf(buf + len, "Memory allocated by BigPhysArea\n");
+      len += sprintf(buf + len, "  PID | Phys. address |          Size | Locked | Name\n");
+      for(loop = 0; loop < MAX_BUFFS; loop++)
+      {
+        if (buffer_table[loop].used && buffer_table[loop].type == TYPE_BPA)
+        {
+          len += sprintf(buf + len, "%5d |", buffer_table[loop].pid);
+          len += sprintf(buf + len, "    0x%08lx |", buffer_table[loop].paddr);
+          len += sprintf(buf + len, "    0x%08x |", buffer_table[loop].size);
+          len += sprintf(buf + len, "    %s |", buffer_table[loop].locked ? "yes" : " no");
+          len += sprintf(buf + len, " %s\n", buffer_table[loop].name);
+        }
+      }
+//MJ-SMP: end of protected zone
+
+      if (copy_to_user((void *)arg, buf, TEXT_SIZE * sizeof(char)) != 0)
+      {
+	kdebug(("cmem_rcc(ioctl,CMEM_RCC_DUMP): error from copy_to_user\n"));
+	return(-CMEM_RCC_CTU);
+      }
+            
+      kfree(buf);
+      break;
+    }
+  }
+  return(0);
+}
+
+
+/******************************************************/
+static void cmem_rcc_vmaOpen(struct vm_area_struct *vma)
+/******************************************************/
+{ 
+  kdebug(("cmem_rcc_vmaOpen: Called\n"));
+}
+
+
+/*******************************************************/
+static void cmem_rcc_vmaClose(struct vm_area_struct *vma)
+/*******************************************************/
+{  
+  kdebug(("cmem_rcc(cmem_rcc_vmaClose): Virtual address  = 0x%08lx\n", (u_long)vma->vm_start));
+  kdebug(("cmem_rcc(cmem_rcc_vmaClose): mmap released\n"));
+}
+
+
+/*********************************************************************/
+static int cmem_rcc_mmap(struct file *file, struct vm_area_struct *vma)
+/*********************************************************************/
+{
+  u_long offset, size;
+
+  kdebug(("cmem_rcc(cmem_rcc_mmap): cmem_rcc_mmap called\n"));
+  vma->vm_flags |= VM_RESERVED;
+  vma->vm_flags |= VM_LOCKED;
+  kdebug(("cmem_rcc(cmem_rcc_mmap): vma->vm_end    = 0x%016lx\n", (u_long)vma->vm_end));
+  kdebug(("cmem_rcc(cmem_rcc_mmap): vma->vm_start  = 0x%016lx\n", (u_long)vma->vm_start));
+  kdebug(("cmem_rcc(cmem_rcc_mmap): vma->vm_offset = 0x%016lx\n", (u_long)vma->vm_pgoff << PAGE_SHIFT));
+  kdebug(("cmem_rcc(cmem_rcc_mmap): vma->vm_flags  = 0x%08x\n", (u_int)vma->vm_flags));
+    
+  size = vma->vm_end - vma->vm_start;
+  offset = vma->vm_pgoff << PAGE_SHIFT;
+
+  // we only support shared mappings. "Copy on write" mappings are
+  // rejected here. A shared mapping that is writeable must have the
+  // shared flag set.
+//MJ: do we need this?  if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED))
+//MJ: do we need this?  {
+//MJ: do we need this?    printk("cmem_rcc(cmem_rcc_mmap): writeable mappings must be shared, rejecting\n");
+//MJ: do we need this?    return(-EINVAL);
+//MJ: do we need this?  }
+  
+  if (remap_page_range(vma, vma->vm_start, offset, size, vma->vm_page_prot))
+  {
+    kdebug(("cmem_rcc(cmem_rcc_mmap): function remap_page_range failed \n"));
+    return(-CMEM_RCC_MMAP);
+  }
+  kdebug(("cmem_rcc(cmem_rcc_mmap): vma->vm_start(2) = 0x%016lx\n", (u_long)vma->vm_start));
+
+  vma->vm_ops = &cmem_rcc_vm_ops;  
+  kdebug(("cmem_rcc(cmem_rcc_mmap): cmem_rcc_mmap done\n"));
+  return(0);
+}
+
+
+/*********************************************************************************************/
+static int cmem_rcc_proc_write(struct file *file, const char *buffer, u_long count, void *data)
+/*********************************************************************************************/
+{
+  int len, loop, loop2;
+  struct cmem_proc_data_t *fb_data = (struct cmem_proc_data_t *)data;
+  struct page *page_ptr;
+
+  kdebug(("cmem_rcc(cmem_rcc_proc_write): cmem_rcc_proc_write called\n"));
+
+  if(count > 99)
+    len = 99;
+  else
+    len = count;
+
+  if (copy_from_user(fb_data->value, buffer, len))
+  {
+    kdebug(("cmem_rcc(cmem_rcc_proc_write): error from copy_from_user\n"));
+    return(-EFAULT);
+  }
+
+  kdebug(("cmem_rcc(cmem_rcc_proc_write): len = %d\n", len));
+  fb_data->value[len - 1] = '\0';
+  kdebug(("cmem_rcc(cmem_rcc_proc_write): text passed = %s\n", fb_data->value));
+ 
+  if (!strcmp(fb_data->value, "debug"))
+  {
+    debug = 1;
+    kdebug(("cmem_rcc(cmem_rcc_proc_write): debugging enabled\n")); 
+  }
+ 
+  if (!strcmp(fb_data->value, "nodebug"))
+  {
+    kdebug(("cmem_rcc(cmem_rcc_proc_write): debugging disabled\n")); 
+    debug = 0; 
+  }
+
+  if (!strcmp(fb_data->value, "freelock"))
+  {
+    kdebug(("cmem_rcc(cmem_rcc_proc_write): releasing all locked segments\n")); 
+//MJ-SMP: protect this fragment (preferrably with a spinlock)
+    for(loop = 0; loop < MAX_BUFFS; loop++)
+    {
+      if (buffer_table[loop].used && buffer_table[loop].locked)
+      {
+        kdebug(("cmem_rcc(cmem_rcc_proc_write): releasing locked buffer: type=%d  paddr=0x%08lx  size=0x%08x  name=%s\n", buffer_table[loop].type, buffer_table[loop].paddr, buffer_table[loop].size, buffer_table[loop].name));
+	if (buffer_table[loop].type == TYPE_GFP)
+	{
+	  page_ptr = virt_to_page(buffer_table[loop].kaddr);  // unreserve all pages
+	  for (loop2 = (1 << buffer_table[loop].order); loop2 > 0; loop2--, page_ptr++)
+            clear_bit(PG_reserved, &page_ptr->flags);
+	  free_pages(buffer_table[loop].kaddr, buffer_table[loop].order);  // free the area 
+	}
+	else
+	  bigphysarea_free_pages((void *)buffer_table[loop].kaddr);
+
+	// clear the entry in the buffer table
+	buffer_table[loop].paddr = 0;
+	buffer_table[loop].kaddr = 0;
+	buffer_table[loop].size  = 0;
+	buffer_table[loop].type  = 0;
+	buffer_table[loop].pid   = 0;
+	buffer_table[loop].order = 0;
+	buffer_table[loop].used  = 0;
+      }
+    }
+//MJ-SMP: end of protected zone
+  }
+
+  return len;
+}
+
+
+/***************************************************************************************************/
+static int cmem_rcc_proc_read(char *buf, char **start, off_t offset, int count, int *eof, void *data)
+/***************************************************************************************************/
+{
+  int loop, nchars = 0;
+  static int len = 0;
+
+  kdebug(("cmem_rcc(cmem_rcc_proc_read): Called with buf    = 0x%08lx\n", (u_long)buf));
+  kdebug(("cmem_rcc(cmem_rcc_proc_read): Called with *start = 0x%08lx\n", (u_long)*start));
+  kdebug(("cmem_rcc(cmem_rcc_proc_read): Called with offset = %ld\n", (u_long)offset));
+  kdebug(("cmem_rcc(cmem_rcc_proc_read): Called with count  = %d\n", count));
+    
+  if (offset == 0)
+  {
+    kdebug(("cmem_rcc(cmem_rcc_proc_read): Creating text....\n"));
+    len = 0;
+
+    len += sprintf(proc_read_text, "\n");
+    len += sprintf(proc_read_text + len, "0x%016lx\n", (u_long)sram_p_addr);
+    len += sprintf(proc_read_text + len, "CMEM RCC driver for release %s (based on CVS tag %s)\n", RELEASE_NAME, CVSTAG);
+
+    len += sprintf(proc_read_text + len, "__get_free_pages\n");
+    len += sprintf(proc_read_text + len, "  PID | Handle | Phys. address |          Size | Locked | Order| Name\n");
+    for(loop = 0; loop < MAX_BUFFS; loop++)
+    {
+      if (buffer_table[loop].used && buffer_table[loop].type == TYPE_GFP)
+      {
+	len += sprintf(proc_read_text + len, "%5d |", buffer_table[loop].pid);
+	len += sprintf(proc_read_text + len, "%7d |", loop);
+	len += sprintf(proc_read_text + len, "    0x%08lx |", buffer_table[loop].paddr);
+	len += sprintf(proc_read_text + len, "    0x%08x |", buffer_table[loop].size);
+	len += sprintf(proc_read_text + len, "    %s |", buffer_table[loop].locked ? "yes" : " no");
+	len += sprintf(proc_read_text + len, "    %d |", buffer_table[loop].order);
+	len += sprintf(proc_read_text + len, " %s\n", buffer_table[loop].name);
+      }
+    }
+
+    len += sprintf(proc_read_text + len, "BigPhysArea\n");
+    len += sprintf(proc_read_text + len, "  PID | Handle | Phys. address |          Size | Locked | Name\n");
+    for(loop = 0; loop < MAX_BUFFS; loop++)
+    {
+      if (buffer_table[loop].used && buffer_table[loop].type == TYPE_BPA)
+      {
+	len += sprintf(proc_read_text + len, "%5d |", buffer_table[loop].pid);
+	len += sprintf(proc_read_text + len, "%7d |", loop);
+	len += sprintf(proc_read_text + len, "    0x%08lx |", buffer_table[loop].paddr);
+	len += sprintf(proc_read_text + len, "    0x%08x |", buffer_table[loop].size);
+	len += sprintf(proc_read_text + len, "    %s |", buffer_table[loop].locked ? "yes" : " no");
+	len += sprintf(proc_read_text + len, " %s\n", buffer_table[loop].name);
+      }
+    }
+
+    len += sprintf(proc_read_text + len, " \n");
+    len += sprintf(proc_read_text + len, "The command 'echo <action> > /proc/cmem_rcc', executed as root,\n");
+    len += sprintf(proc_read_text + len, "allows you to interact with the driver. Possible actions are:\n");
+    len += sprintf(proc_read_text + len, "debug    -> enable debugging\n");
+    len += sprintf(proc_read_text + len, "nodebug  -> disable debugging\n");
+    len += sprintf(proc_read_text + len, "freelock -> release all locked segments\n");
+  }
+  kdebug(("cmem_rcc(cmem_rcc_proc_read): number of characters in text buffer = %d\n", len));
+
+  if (count < (len - offset))
+    nchars = count;
+  else
+    nchars = len - offset;
+  kdebug(("cmem_rcc(cmem_rcc_proc_read): min nchars         = %d\n", nchars));
+  
+  if (nchars > 0)
+  {
+    for (loop = 0; loop < nchars; loop++)
+      buf[loop + (offset & (PAGE_SIZE - 1))] = proc_read_text[offset + loop];
+    *start = buf + (offset & (PAGE_SIZE - 1));
+  }
+  else
+  {
+    nchars = 0;
+    *eof = 1;
+  }
+ 
+  kdebug(("cmem_rcc(cmem_rcc_proc_read): returning *start   = 0x%08lx\n", (u_long)*start));
+  kdebug(("cmem_rcc(cmem_rcc_proc_read): returning nchars   = %d\n", nchars));
+  return(nchars);
+}
diff --git a/cmt/filar.c b/cmt/filar.c
new file mode 100644
index 0000000000000000000000000000000000000000..677c6259f6e6651b90cc0758656adb04d3d6753b
--- /dev/null
+++ b/cmt/filar.c
@@ -0,0 +1,1521 @@
+/************************************************************************/
+/*									*/
+/* File: filar_driver.c							*/
+/*									*/
+/* driver for the FILAR S-Link interface				*/
+/*									*/
+/* 25. Jun. 02  MAJO  created						*/
+/*									*/
+/************ C 2005 - The software with that certain something *********/
+
+/************************************************************************/
+/*NOTES:								*/
+/*- This driver should work on kernels from 2.6.9 onwards		*/
+/************************************************************************/
+
+/************************************************************************/
+/*Open issues:								*/
+/*- The ISR can have errors from the FIFOS (unlikely). It is not clear 	*/
+/*  how such errors would be reported. For now they are just logged	*/
+/*- The start and end control word patters passed via the INIT ioctl    */
+/*  are not yet used in the ISR for consistency checking.		*/
+/*- The ioctl fuctions copy large arrays into the driver and back to	*/
+/*  the user application. This is so because the amount of data in the	*/
+/*  arrays is not known. If possible one should only copy the valid 	*/
+/*  data. I don't know yet how to do this				*/ 
+/************************************************************************/
+
+
+#include <linux/init.h>           //MJ: for 2.6, p30
+#include <linux/module.h>
+#include <linux/moduleparam.h>    //MJ: for 2.6, p30
+#include <linux/kernel.h>
+#include <linux/stat.h>           //MJ: for 2.6, e.g. for module_param
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/cdev.h>           //e.g. for cdev_alloc
+#include <linux/slab.h>           //e.g. for kmalloc
+#include <linux/delay.h>          //e.g. for udelay
+#include <linux/interrupt.h>      //e.g. for request_irq
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>           //e.g. for local_irq_save
+#include "ROSfilar/filar_driver.h"
+
+
+/*********/
+/*Globals*/
+/*********/
+static int debug = 0, errorlog = 1;
+static char *proc_read_text;
+static u_int fuse = 1, scw, ecw;
+static u_int maxcard = 0, channels[MAXCARDS][MAXCHANNELS], served[MAXCARDS][MAXCHANNELS];
+static u_int irqlist[MAXIRQ], cmask[MAXCARDS], hwfifo[MAXROLS][MAXHWFIFO];
+static u_int opid, hwfifow[MAXROLS], hwfifor[MAXROLS], hwfifon[MAXROLS];
+static u_int infifor[MAXROLS], outfifow[MAXROLS];
+static u_int ccw, order, swfifobasephys, outfifodatasize, infifodatasize, fifoptrsize, tsize = 0;
+static u_int ackdata[MAXROLS][MAXOUTFIFO][4], acknum[MAXROLS]; 
+static u_int *outfifo, *outfifon, *infifo, *infifon, swfifobase;
+struct filar_proc_data_t filar_proc_data;
+static struct proc_dir_entry *filar_file;
+static struct cdev *filar_cdev;
+static T_filar_card filarcard[MAXCARDS];
+static dev_t major_minor;
+
+/************/
+/*Prototypes*/
+/************/
+static int hw_fifo_level(int ro, int *nfree);
+static int __devinit filarProbe(struct pci_dev *dev, const struct pci_device_id *id);
+static int filar_init(void);
+static void disable_input(void);
+static void enable_input(void);
+static void filar_cleanup(void);
+static void filar_vmaOpen(struct vm_area_struct *vma);
+static void filar_vmaClose(struct vm_area_struct *vma);
+static void filarRemove(struct pci_dev *dev); 
+static irqreturn_t filar_irq_handler (int irq, void *dev_id, struct pt_regs *regs); 
+
+MODULE_DESCRIPTION("S-Link FILAR");
+MODULE_AUTHOR("Markus Joos, CERN/PH");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("Private: Contact markus.joos@cern.ch");
+module_param (debug, int, S_IRUGO | S_IWUSR);  //MJ: for 2.6 p37
+MODULE_PARM_DESC(debug, "1 = enable debugging   0 = disable debugging"); 
+module_param (errorlog, int, S_IRUGO | S_IWUSR);  //MJ: for 2.6 p37
+MODULE_PARM_DESC(errorlog, "1 = enable debugging   0 = disable debugging"); 
+
+
+// memory handler functions
+static struct vm_operations_struct filar_vm_ops = 
+{
+  .close = filar_vmaClose,   //mmap-close
+  .open  = filar_vmaOpen,    //MJ: Note the comma at the end of the list!
+};
+
+// Standard file operations
+static struct file_operations fops = 
+{
+  .owner   = THIS_MODULE,
+  .ioctl   = filar_ioctl,
+  .open    = filar_open,    
+  .mmap    = filar_mmap,
+  .release = filar_release,
+};
+
+// PCI driver structures. See p311
+static struct pci_device_id filarIds[] = 
+{
+  {PCI_DEVICE(PCI_VENDOR_ID_CERN, 0x14)},      
+  {0,},
+};
+
+//PCI operations
+static struct pci_driver filarPciDriver = 
+{
+  .name     = "filar",
+  .id_table = filarIds,
+  .probe    = filarProbe,
+  .remove   = filarRemove,
+};
+
+
+/*****************************/
+/* Standard driver functions */
+/*****************************/
+/*************************/
+static int filar_init(void)
+/*************************/
+{
+  int rol, tfsize, result;
+  u_int loop;
+  struct page *page_ptr;
+
+  kerror(("FILAR (SC) driver version 3.0\n")); 
+
+  if (alloc_chrdev_region(&major_minor, 0, 1, "filar")) //MJ: for 2.6 p45
+  {
+    kdebug(("filar(filar_init): failed to obtain device numbers\n"));
+    result = -FILAR_EIO;
+    goto fail1;
+  }
+  
+  proc_read_text = (char *)kmalloc(MAX_PROC_TEXT_SIZE, GFP_KERNEL);
+  if (proc_read_text == NULL)
+  {
+    kdebug(("filar(filar_init): error from kmalloc\n"));
+    result = -EFAULT;
+    goto fail2;
+  }
+
+  filar_file = create_proc_entry("filar", 0644, NULL);
+  if (filar_file == NULL)
+  {
+    kerror(("filar(filar_init): error from call to create_proc_entry\n"));
+    result = -EFAULT;
+    goto fail3;
+  }  
+  
+  strcpy(filar_proc_data.name, "filar");
+  strcpy(filar_proc_data.value, "filar");
+  filar_file->data = &filar_proc_data;
+  filar_file->read_proc = filar_read_procmem;
+  filar_file->write_proc = filar_write_procmem;
+  filar_file->owner = THIS_MODULE;  
+  
+  //Clear the list of used interupts 
+  for(loop = 0; loop < MAXIRQ; loop++)
+    irqlist[loop] = 0;
+
+  kdebug(("filar(filar_init): registering PCIDriver\n"));
+  result = pci_register_driver(&filarPciDriver);  //MJ: See P312
+  if (result == 0)
+  {
+    kerror(("filar(filar_init): ERROR! no FILAR cards found!\n"));
+    result = -EIO;
+    goto fail4;
+  }
+  else 
+  {
+    kerror(("filar(filar_init): Found %d FILAR cards!\n", result));
+  }
+
+  // Allocate contiguous memory for the FIFOs and store the base addresses in a global structure
+  outfifodatasize = MAXROLS * MAXOUTFIFO * OUTFIFO_ELEMENTS * sizeof(u_int);    //outfifo
+  infifodatasize  = MAXROLS * MAXINFIFO * INFIFO_ELEMENTS * sizeof(u_int);      //infifo
+  fifoptrsize     = MAXROLS * sizeof(u_int);                                    //outfifo(w/r/n) + infifo(w/r/n)
+  kdebug(("filar(filar_init): 0x%08x bytes needed for the OUT FIFO\n", outfifodatasize));
+  kdebug(("filar(filar_init): 0x%08x bytes needed for the IN FIFO\n", infifodatasize));
+  kdebug(("filar(filar_init): 0x%08x bytes needed for the FIFO pointers\n", fifoptrsize));
+  
+  tsize = outfifodatasize + infifodatasize + 2 * fifoptrsize;
+  kdebug(("filar(filar_init): 0x%08x bytes needed for all FIFOs\n", tsize));
+
+  tfsize = (tsize + 4095) & 0xfffff000;  // round up to next 4K boundary
+  tfsize = tfsize >> 12;                 // compute number of pages
+  kdebug(("filar(filar_init): %d pages needed for the S/W FIFOs\n", tfsize));
+ 
+  order = 0;
+  while(tfsize)
+  {
+    order++;
+    tfsize = tfsize >> 1;                 // compute order
+  } 
+  kdebug(("filar(filar_init): order = %d\n", order));
+  
+  swfifobase = __get_free_pages(GFP_ATOMIC, order);
+  if (!swfifobase)
+  {
+    kerror(("filar(filar_init): error from __get_free_pages\n"));
+    result = -FILAR_EFAULT;
+    goto fail5;
+  }
+  kdebug(("filar(filar_init): swfifobase = 0x%08x\n", swfifobase));
+   
+  // Reserve all pages to make them remapable
+  page_ptr = virt_to_page(swfifobase);
+
+  for (loop = (1 << order); loop > 0; loop--, page_ptr++)
+    set_bit(PG_reserved, &page_ptr->flags);                   //MJ: have a look at the kernel book
+
+  swfifobasephys = virt_to_bus((void *)swfifobase);
+  kdebug(("filar(filar_init): swfifobasephys = 0x%08x\n", swfifobasephys));
+
+  // Assign base addresses to the FIFO arrays
+  outfifo  = (u_int *)swfifobase;
+  infifo   = (u_int *)(swfifobase + outfifodatasize);
+  outfifon = (u_int *)(swfifobase + outfifodatasize + infifodatasize);
+  infifon  = (u_int *)(swfifobase + outfifodatasize + infifodatasize + fifoptrsize);
+
+  kdebug(("filar(filar_init): outfifo  is at 0x%016lx\n", (u_long)outfifo));
+  kdebug(("filar(filar_init): infifo   is at 0x%016lx\n", (u_long)infifo));
+  kdebug(("filar(filar_init): outfifon is at 0x%016lx\n", (u_long)outfifon));
+  kdebug(("filar(filar_init): infifon  is at 0x%016lx\n", (u_long)infifon));
+
+  //Initialize the FIFOs
+  for(rol = 0; rol < MAXROLS; rol++)
+  {
+    infifor[rol] = 0;
+    infifon[rol] = 0;
+    hwfifow[rol] = 0;
+    hwfifor[rol] = 0;
+    hwfifon[rol] = 0;
+    outfifow[rol] = 0;
+    outfifon[rol] = 0;
+  }
+
+  opid = 0;
+  
+  filar_cdev = (struct cdev *)cdev_alloc();       //MJ: for 2.6 p55
+  filar_cdev->ops = &fops;
+  result = cdev_add(filar_cdev, major_minor, 1);  //MJ: for 2.6 p56
+  if (result)
+  {
+    kdebug(("filar(filar_init): error from call to cdev_add.\n"));
+    goto fail6;
+  }
+  
+  
+  kdebug(("filar(filar_init): driver loaded; major device number = %d\n", MAJOR(major_minor)));
+  return(0);  
+  
+  fail6:
+    page_ptr = virt_to_page(swfifobase);
+    for (loop = (1 << order); loop > 0; loop--, page_ptr++)
+      clear_bit(PG_reserved, &page_ptr->flags);
+    free_pages(swfifobase, order);
+
+  fail5:
+    pci_unregister_driver(&filarPciDriver);
+
+  fail4:
+    remove_proc_entry("io_rcc", NULL);
+    
+  fail3:
+    kfree(proc_read_text);
+
+  fail2:
+    unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45
+
+  fail1:
+    return(result);  
+}
+
+
+/*****************************/
+static void filar_cleanup(void)
+/*****************************/
+{
+  u_int loop;
+  struct page *page_ptr;
+      
+  cdev_del(filar_cdev);                     //MJ: for 2.6 p56
+  pci_unregister_driver(&filarPciDriver);
+  page_ptr = virt_to_page(swfifobase);      // unreserve all pages used for the S/W FIFOs
+
+  for (loop = (1 << order); loop > 0; loop--, page_ptr++)
+    clear_bit(PG_reserved, &page_ptr->flags);
+
+  free_pages(swfifobase, order);            // free the area 
+  remove_proc_entry("filar", NULL);
+  kfree(proc_read_text);
+  unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45
+
+  kdebug(("filar(filar_cleanup): driver removed\n"));
+}
+
+
+module_init(filar_init);    //MJ: for 2.6 p16
+module_exit(filar_cleanup); //MJ: for 2.6 p16
+
+
+/**********************************************************/
+static int filar_open(struct inode *ino, struct file *filep)
+/**********************************************************/
+{
+  kdebug(("filar(filar_open): nothing to be done\n"));
+  return(0);
+}
+
+
+/*************************************************************/
+static int filar_release(struct inode *ino, struct file *filep)
+/*************************************************************/
+{
+  kdebug(("filar(filar_release): (alomst) nothing to be done\n"));
+  opid = 0;
+  return(0);
+}
+
+
+/***********************************************************************************/
+static int filar_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg)
+/***********************************************************************************/
+{
+  switch (cmd)
+  {    
+    case OPEN:
+    {
+      FILAR_open_t params;
+      
+      kdebug(("filar(ioctl,OPEN): called from process %d\n", current->pid))
+      if (!opid)
+        opid = current->pid;
+      else
+      {
+	kerror(("filar(ioctl, OPEN): The FILAR is already used by process %d\n", opid));
+    	return(-FILAR_USED);
+      }
+	
+      params.size = tsize;
+      params.physbase = swfifobasephys;
+      params.outsize = outfifodatasize;
+      params.insize = infifodatasize;
+      params.ptrsize = fifoptrsize;
+      kdebug(("filar(ioctl, OPEN):  tsize           = 0x%08x\n", tsize));
+      kdebug(("filar(ioctl, OPEN):  swfifobasephys  = 0x%08x\n", swfifobasephys));
+      kdebug(("filar(ioctl, OPEN):  outfifodatasize = 0x%08x\n", outfifodatasize));
+      kdebug(("filar(ioctl, OPEN):  infifodatasize  = 0x%08x\n", infifodatasize));
+      kdebug(("filar(ioctl, OPEN):  fifoptrsize     = 0x%08x\n", fifoptrsize));
+            
+      if (copy_to_user((void *)arg, &params, sizeof(FILAR_open_t)) != 0)
+      {
+	kerror(("filar(ioctl, OPEN): error from copy_to_user\n"));
+	return(-FILAR_EFAULT);
+      }      
+      return(0);
+      break;
+    }
+    
+    case CLOSE:
+    {
+      kdebug(("filar(ioctl, CLOSE): called from process %d\n", current->pid))
+      opid = 0;
+  
+      return(0);
+      break;
+    }
+    
+    case CONFIG:
+    { 
+      FILAR_config_t params;
+      u_int card, channel, data, rol;
+      
+      if (copy_from_user(&params, (void *)arg, sizeof(FILAR_config_t)) !=0)
+      {
+	kerror(("filar(ioctl, CONFIG): error from copy_from_user\n"));
+	return(-FILAR_EFAULT);
+      }
+        
+      scw = params.scw;
+      ecw = params.ecw;
+      ccw = params.ccw;
+      kdebug(("filar(ioctl, CONFIG): params.bswap = %d\n", params.bswap));
+      kdebug(("filar(ioctl, CONFIG): params.wswap = %d\n", params.wswap));
+      kdebug(("filar(ioctl, CONFIG): params.psize = %d\n", params.psize));
+      kdebug(("filar(ioctl, CONFIG): params.ccw = %d\n", params.ccw));
+
+      for(card = 0; card < maxcard; card++)
+      {
+        data = 0;
+        for(channel = 0; channel < MAXCHANNELS; channel++)
+        { 
+          served[card][channel] = 0;
+          rol = (card << 2) + channel;          //works only for 4 channels per card
+          kdebug(("filar(ioctl, CONFIG): params.enable[%d] = %d\n", rol, params.enable[rol]));
+          if (params.enable[rol])
+            channels[card][channel] = 1; 
+          else
+          {
+            data += (1 << (6 * channel + 9));
+            channels[card][channel] = 0;
+          }
+          kdebug(("filar(ioctl, CONFIG): channels[%d][%d] = %d\n", card, channel, channels[card][channel]));
+          kdebug(("filar(ioctl, CONFIG): data = 0x%08x\n", data));
+        }
+        data += (params.bswap << 1) + (params.wswap << 2) + (params.psize << 3);
+        kdebug(("filar(ioctl, CONFIG): Writing 0x%08x to the OCR\n", data));
+        filarcard[card].regs->ocr = data;
+        cmask[card] = data;                    //remember which channels were enabled
+        
+        if (params.interrupt)
+          filarcard[card].regs->imask |= 0x2;  //Enable the ACK FIFO almost full interrupt 
+      }
+      break;
+    }
+    
+    case RESET:
+    {
+      u_int channel, data, card, rol;
+      
+      if (copy_from_user(&card, (void *)arg, sizeof(int)) !=0)
+      {
+	kerror(("filar(ioctl, RESET): error from copy_from_user\n"));
+	return(-FILAR_EFAULT);
+      }
+
+      for(channel = 0; channel < MAXCHANNELS; channel++)
+        served[card][channel] = 0;
+      
+      if (card <= maxcard)
+      {
+        kdebug(("filar(ioctl, RESET): Resetting card %d\n", card));
+        data = filarcard[card].regs->ocr;
+        data |= 0x1;
+        filarcard[card].regs->ocr = data;
+        udelay(2);  // Delay for at least one us
+        data &= 0xfffffffe;
+        filarcard[card].regs->ocr = data;
+
+        //reset the FIFOs
+        for(channel = 0; channel < MAXCHANNELS; channel++)
+        {
+          rol = (card << 2) + channel;
+          infifor[rol] = 0;
+          infifon[rol] = 0;
+          hwfifow[rol] = 0;
+          hwfifor[rol] = 0;
+          hwfifon[rol] = 0;
+          outfifow[rol] = 0;
+          outfifon[rol] = 0;
+        }
+      }
+      else
+      {
+       	kerror(("filar(ioctl,RESET): This card is unknown\n"));
+	return(-FILAR_NOCARD); 
+      }
+      
+      return(0);
+      break;
+    }
+    
+    case LINKRESET:
+    {   
+      u_int waited, rol, data, card, channel;
+ 
+      if (copy_from_user(&rol, (void *)arg, sizeof(int)) !=0)
+      {
+	kerror(("filar(ioctl,RESET): error from copy_from_user\n"));
+	return(-FILAR_EFAULT);
+      }
+      
+      card = rol >> 2;      
+      if (card <= maxcard)
+      {
+        channel = rol & 0x3;
+        kdebug(("filar(ioctl,LINKRESET): Resetting card %d, channel %d\n", card, channel));
+        served[card][channel] = 0;
+
+        data = 0x1 << (8 + channel * 6);     // Set the URESET bit
+        filarcard[card].regs->ocr |= data;
+        mdelay(30);                          // Delay for at least 30 ms
+        data = 0x1 << (16 + channel * 4);    // Now wait for LDOWN to come up again
+        waited = 0;
+        while(filarcard[card].regs->osr & data)
+        {
+          waited++;
+          if (waited > 10000)
+          {
+            kerror(("filar(ioctl,LINKRESET): channel %d of card %d does not come up again\n", channel, card));
+            data = ~(0x1 << (8 + channel * 6));  // Reset the URESET bit
+            filarcard[card].regs->ocr &= data;
+	    return(-FILAR_STUCK);
+          }          
+        }
+        data = ~(0x1 << (8 + channel * 6));  // Reset the URESET bit
+        filarcard[card].regs->ocr &= data;
+      }
+      else
+      {
+       	kerror(("filar(ioctl,LINKRESET): This card is unknown\n"));
+	return(-FILAR_NOCARD); 
+      }
+      
+      kdebug(("filar(ioctl,LINKRESET): Done\n"));
+      return(0);
+      break;
+    }
+    
+    case LINKSTATUS:
+    {   
+      u_int isup, data, card, rol, channel;
+      
+      if (copy_from_user(&rol, (void *)arg, sizeof(int)) !=0)
+      {
+	kerror(("filar(ioctl, RESET): error from copy_from_user\n"));
+	return(-FILAR_EFAULT);
+      }
+      
+      card = rol >> 2;
+      if (card <= maxcard)
+      {
+        channel = rol & 0x3;
+        data = 0x1 << (16 + channel * 4);
+        if (filarcard[card].regs->osr & data)
+          isup = 0;
+        else
+          isup = 1;
+          
+        if (copy_to_user((void *)arg, &isup, sizeof(int)) != 0)
+        {
+	  kerror(("filar(ioctl, LINKSTATUS): error from copy_to_user\n"));
+	  return(-FILAR_EFAULT);
+        } 
+      }
+      else
+      {
+       	kerror(("filar(ioctl, LINKSTATUS): This card is unknown\n"));
+	return(-FILAR_NOCARD); 
+      }
+      return(0);
+      break;
+    }
+
+    case INFO:
+    {
+      FILAR_info_t info;
+      u_int rol, card, data;
+     
+      //Initialise the array to 0
+      for(rol = 0; rol < MAXROLS; rol++)
+      {
+        info.channels[rol] = 0;
+        info.enabled[rol] = 0;
+      }
+      info.nchannels = 0;
+      
+      for(card = 0; card < maxcard; card++)
+      {
+        data = filarcard[card].regs->osr;
+        kdebug(("filar(ioctl, INFO): 0x%08x read from OSR\n", data));
+        if (data & 0x00080000)  info.channels[(card << 2)] = 0; 
+        else {info.channels[(card << 2)] = 1; info.nchannels++;}
+        
+        if (data & 0x00800000)  info.channels[(card << 2) + 1] = 0; 
+        else {info.channels[(card << 2) + 1] = 1; info.nchannels++;}
+        
+        if (data & 0x08000000)  info.channels[(card << 2) + 2] = 0; 
+        else {info.channels[(card << 2) + 2] = 1; info.nchannels++;}
+        
+        if (data & 0x80000000)  info.channels[(card << 2) + 3] = 0; 
+        else {info.channels[(card << 2) + 3] = 1; info.nchannels++;}
+                
+        data = filarcard[card].regs->ocr;
+        kdebug(("filar(ioctl, INFO): 0x%08x read from OCR\n", data));
+        
+        if (data & 0x00000200) info.enabled[(card << 2)] = 0;     else info.enabled[(card << 2)] = 1;
+        if (data & 0x00008000) info.enabled[(card << 2) + 1] = 0; else info.enabled[(card << 2) + 1] = 1;        
+        if (data & 0x00200000) info.enabled[(card << 2) + 2] = 0; else info.enabled[(card << 2) + 2] = 1;        
+        if (data & 0x08000000) info.enabled[(card << 2) + 3] = 0; else info.enabled[(card << 2) + 3] = 1;       
+      }  
+
+      if (copy_to_user((void *)arg, &info, sizeof(FILAR_info_t)) != 0)
+      {
+	kerror(("filar(ioctl, INFO): error from copy_to_user\n"));
+	return(-FILAR_EFAULT);
+      }      
+      return(0);
+      break;
+    }
+    
+    case FIFOIN:
+    { 
+      u_int card, channel, rol, loop, nfree, navail, min, data;
+      u_long flags;
+
+      if (copy_from_user(&rol, (void *)arg, sizeof(int)) !=0)
+      {
+	kerror(("filar(ioctl,FIFOIN): error from copy_from_user\n"));
+	return(-FILAR_EFAULT);
+      }
+      card = rol >> 2;
+      channel = rol & 0x3; 
+      kdebug(("filar(ioctl,FIFOIN): channel = %d\n", channel));
+      kdebug(("filar(ioctl,FIFOIN): card = %d\n", card));
+
+      //We have to make sure that this code does not get interrupted. Otherwhise there will be
+      //overflows in the IN FIFO which lead to incorrect PCI addresses being written to the FILAR
+      local_irq_save(flags);  //MJ: for 2.6 see p274 & p275
+
+      //Is there space in the FILAR
+      hw_fifo_level(rol, &nfree);
+      kdebug(("filar(ioctl,FIFOIN): There are %d slots available in the HW FIFO of ROL %d\n", nfree, rol));
+
+      //How many entries are in the IN FIFO
+      navail = infifon[rol]; 
+      kdebug(("filar(ioctl,FIFOIN): There are %d entries in the IN FIFO\n", navail));
+
+      if (nfree < navail)
+        min = nfree;
+      else
+        min = navail;
+              
+      kdebug(("filar(ioctl,FIFOIN): min = %d\n", min));
+                 
+      for(loop = 0; loop < min; loop++)
+      {
+        // We have checked how many entries are free / available in the FIFOS
+        // Therefore we can waive the return values
+        in_fifo_pop(rol, &data);
+        hw_fifo_push(rol, data);
+
+        // I don't check if there is space as this should be guaranteed by
+        // the coupling between the FILAR FIFO and the H/W FIFO which has the same depth
+             if (channel == 0) filarcard[card].regs->req1 = data;
+        else if (channel == 1) filarcard[card].regs->req2 = data;
+        else if (channel == 2) filarcard[card].regs->req3 = data;
+        else                   filarcard[card].regs->req4 = data; 
+	
+        kdebug(("filar(ioctl,FIFOIN): PCI addr. 0x%08x written to card %d, channel %d\n", data, card, channel));
+      }
+
+      local_irq_restore(flags);
+      return(0);
+      break;
+    }
+    
+    case FLUSH:
+    { 
+      flush_cards();
+      break;
+    }
+  }   
+  return(0);
+}
+
+
+/***************************************************/
+static void filar_vmaOpen(struct vm_area_struct *vma)
+/***************************************************/
+{ 
+  kdebug(("filar(filar_vmaOpen): Called\n"));
+}
+
+
+/****************************************************/
+static void filar_vmaClose(struct vm_area_struct *vma)
+/****************************************************/
+{  
+  kdebug(("filar(filar_vmaClose): Virtual address  = 0x%016lx\n",(u_long)vma->vm_start));
+  kdebug(("filar(filar_vmaClose): mmap released\n"));
+}
+
+
+/******************************************************************/
+static int filar_mmap(struct file *file, struct vm_area_struct *vma)
+/******************************************************************/
+{
+  u_long offset, size;
+
+  kdebug(("filar(filar_mmap): function called\n"));
+  vma->vm_flags |= VM_RESERVED;
+  vma->vm_flags |= VM_LOCKED;
+
+  kdebug(("filar(filar_mmap): vma->vm_end    = 0x%016lx\n", (u_long)vma->vm_end));
+  kdebug(("filar(filar_mmap): vma->vm_start  = 0x%016lx\n", (u_long)vma->vm_start));
+  kdebug(("filar(filar_mmap): vma->vm_offset = 0x%016lx\n", (u_long)vma->vm_pgoff << PAGE_SHIFT));
+  kdebug(("filar(filar_mmap): vma->vm_flags  = 0x%08x\n", (u_int)vma->vm_flags));
+
+  size = vma->vm_end - vma->vm_start;
+  offset = vma->vm_pgoff << PAGE_SHIFT;
+  kdebug(("filar(filar_mmap): offset = 0x%08x\n",(u_int)offset));
+  kdebug(("filar(filar_mmap): size   = 0x%08x\n",(u_int)size));
+
+//MJ: required?  if (offset & ~PAGE_MASK)
+//MJ: required?  {
+//MJ: required?    kerror(("filar(filar_mmap): offset not aligned: %ld\n", offset));
+//MJ: required?    return -ENXIO;
+//MJ: required?  }
+//MJ: required?  if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED))
+//MJ: required?  {
+//MJ: required?    kerror(("filar(filar_mmap): writeable mappings must be shared, rejecting\n"));
+//MJ: required?    return(-EINVAL);
+//MJ: required?  }
+
+  if (remap_page_range(vma, vma->vm_start, offset, size, vma->vm_page_prot))
+  {
+    kerror(("filar(filar_mmap): remap page range failed\n"));
+    return -ENXIO;
+  }
+  
+  vma->vm_ops = &filar_vm_ops;  
+  kdebug(("filar(filar_mmap): function done\n"));
+  return(0);
+}
+
+
+/*********************************************************************************************/
+static int filar_write_procmem(struct file *file, const char *buffer, u_long count, void *data)
+/*********************************************************************************************/
+{
+  int len;
+  u_int card;
+  struct filar_proc_data_t *fb_data = (struct filar_proc_data_t *)data;
+
+  kdebug(("filar(filar_write_procmem): filar_write_procmem called\n"));
+
+  if(count > 99)
+    len = 99;
+  else
+    len = count;
+
+  if (copy_from_user(fb_data->value, buffer, len))
+  {
+    kerror(("filar(filar_write_procmem): error from copy_from_user\n"));
+    return(-FILAR_EFAULT);
+  }
+
+  kdebug(("filar(filar_write_procmem): len = %d\n", len));
+  fb_data->value[len - 1] = '\0';
+  kdebug(("filar(filar_write_procmem): text passed = %s\n", fb_data->value));
+
+  if (!strcmp(fb_data->value, "debug"))
+  {
+    debug = 1;
+    kdebug(("filar(filar_write_procmem): debugging enabled\n"));
+  }
+
+  if (!strcmp(fb_data->value, "nodebug"))
+  {
+    kdebug(("filar(filar_write_procmem): debugging disabled\n"));
+    debug = 0;
+  }
+  
+  if (!strcmp(fb_data->value, "elog"))
+  {
+    kdebug(("filar(filar_write_procmem): Error logging enabled\n"))
+    errorlog = 1;
+  }
+
+  if (!strcmp(fb_data->value, "noelog"))
+  {
+    kdebug(("filar(filar_write_procmem): Error logging disabled\n"))
+    errorlog = 0;
+  }
+
+  if (!strcmp(fb_data->value, "disable"))
+  {
+    for(card = 0; card < maxcard; card++)
+      filarcard[card].regs->ocr |= 0x08208200;
+    kdebug(("filar(filar_write_procmem): All channels disabled\n"));
+  }
+  
+  if (!strcmp(fb_data->value, "reset"))
+  {
+    reset_cards(1);
+    kdebug(("filar(filar_write_procmem): All cards reset\n"));
+  }
+    
+  if (!strcmp(fb_data->value, "flush"))
+  {
+    flush_cards();
+    kdebug(("filar(filar_write_procmem): All cards flushed\n"));
+  }
+  
+  return len;
+}
+
+
+/***************************************************************************************************/
+static int filar_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data)
+/***************************************************************************************************/
+{
+  u_int rol, ret, card, ocr[MAXROLS], osr[MAXROLS], osr2[MAXROLS], imask[MAXROLS], fifostat[MAXROLS], value, value2, channel;
+  int loop, nchars = 0;
+  static int len = 0;
+
+  kdebug(("filar(filar_read_procmem): Called with buf    = 0x%016lx\n", (u_long)buf));
+  kdebug(("filar(filar_read_procmem): Called with *start = 0x%016lx\n", (u_long)*start));
+  kdebug(("filar(filar_read_procmem): Called with offset = %d\n", (u_int)offset));
+  kdebug(("filar(filar_read_procmem): Called with count  = %d\n", count));
+
+  if (offset == 0)
+  {
+    kdebug(("exp(exp_read_procmem): Creating text....\n"));
+    len = 0;
+    len += sprintf(proc_read_text + len, "\n\n\nFILAR driver (single cycle  protocol) for release %s (based on CVS tag %s)\n", RELEASE_NAME, CVSTAG);
+
+  if (opid)
+    len += sprintf(proc_read_text + len, "The drivers is currently used by PID %d\n", opid);
+    len += sprintf(proc_read_text + len, "=========================================================================\n");
+    len += sprintf(proc_read_text + len, "Card|IRQ line| ACK IRQ|revision|PCI MEM addr.|         page size|wswap|bswap|temperature\n");
+    len += sprintf(proc_read_text + len, "----|--------|--------|--------|-------------|------------------|-----|-----|-----------\n");
+
+    //Read the registers
+    for(card = 0; card < maxcard; card++)
+    {
+      ocr[card]   = filarcard[card].regs->ocr;
+      osr[card]   = filarcard[card].regs->osr;
+      osr2[card]  = filarcard[card].regs->osr;
+      imask[card] = filarcard[card].regs->imask;
+      fifostat[card] = filarcard[card].regs->fifostat;
+    }
+
+    for(card = 0; card < maxcard; card++)
+    {
+      len += sprintf(proc_read_text + len, "   %d|", card);
+      len += sprintf(proc_read_text + len, "      %2d|", filarcard[card].irq_line);
+      len += sprintf(proc_read_text + len, "%s|", (imask[card] & 0x2)?" enabled":"disabled"); 
+      len += sprintf(proc_read_text + len, "    0x%02x|", filarcard[card].pci_revision); 
+      len += sprintf(proc_read_text + len, "   0x%08x|", filarcard[card].pci_memaddr);
+      value = (ocr[card] >> 3) & 0x7;
+      if (value == 0) len += sprintf(proc_read_text + len, "         256 bytes|");
+      if (value == 1) len += sprintf(proc_read_text + len, "           1 Kbyte|");
+      if (value == 2) len += sprintf(proc_read_text + len, "          2 Kbytes|");
+      if (value == 3) len += sprintf(proc_read_text + len, "          4 Kbytes|");
+      if (value == 4) len += sprintf(proc_read_text + len, "         16 Kbytes|");
+      if (value == 5) len += sprintf(proc_read_text + len, "         64 Kbytes|");
+      if (value == 6) len += sprintf(proc_read_text + len, "        256 Kbytes|");
+      if (value == 7) len += sprintf(proc_read_text + len, "4 MBytes - 8 Bytes|");
+      len += sprintf(proc_read_text + len, "  %s|", (ocr[card] & 0x4)?"yes":" no");
+      len += sprintf(proc_read_text + len, "  %s|", (ocr[card] & 0x2)?"yes":" no");
+      len += sprintf(proc_read_text + len, " %3d deg. C\n", (osr[card] >>8) & 0xff);
+    }
+
+    for(card = 0; card < maxcard; card++)
+    {
+      len += sprintf(proc_read_text + len, "\nCard %d:\n", card);
+      len += sprintf(proc_read_text + len, "=======\n");
+      len += sprintf(proc_read_text + len, "       |       |       |          |free      |     |        |       |          |free      |\n");
+      len += sprintf(proc_read_text + len, "       |       |       |entries in|entries in|     |        |       |entries in|entries in|fragments\n");
+      len += sprintf(proc_read_text + len, "Channel|present|enabled|ACK FIFO  |REQ FIFO  |LDOWN|Overflow|   XOFF|OUT FIFO  |IN FIFO   |served\n");
+      len += sprintf(proc_read_text + len, "-------|-------|-------|----------|----------|-----|--------|-------|----------|----------|---------\n");
+
+      for(channel = 0; channel < MAXCHANNELS; channel++)
+      {
+	len += sprintf(proc_read_text + len, "      %d|", channel);
+
+	value = osr[card] & (1 << (19 + channel * 4));
+	len += sprintf(proc_read_text + len, "    %s|", value?" no":"yes");
+
+	value = ocr[card] & (1 << (9 + channel * 6));
+	len += sprintf(proc_read_text + len, "    %s|", value?" no":"yes");
+
+	value = (fifostat[card] >> (channel * 8)) & 0xf;
+	if (value < 15)
+          len += sprintf(proc_read_text + len, "        %2d|", value);
+	else
+          len += sprintf(proc_read_text + len, "       >14|");
+
+	value = (fifostat[card] >> ( 4 + channel * 8)) & 0xf;
+	if (value < 15)
+          len += sprintf(proc_read_text + len, "        %2d|", value);
+	else
+          len += sprintf(proc_read_text + len, "       >14|");
+
+	value = osr[card] & (1 << (16 + channel * 4));
+	len += sprintf(proc_read_text + len, "  %s|", value?"yes":" no"); 
+
+	value = osr[card] & (1 << (17 + channel * 4));
+	len += sprintf(proc_read_text + len, "     %s|", value?"yes":" no");
+
+	value = osr[card] & (1 << (18 + channel * 4));
+	value2 = osr2[card] & (1 << (18 + channel * 4));
+	if (!value && !value2)
+          len += sprintf(proc_read_text + len, " is off|");
+	else if (value && value2)
+          len += sprintf(proc_read_text + len, "  is on|");
+	else if (value && !value2)
+          len += sprintf(proc_read_text + len, " was on|");
+	else 
+          len += sprintf(proc_read_text + len, "went on|");
+
+	rol = (card << 2) + channel;
+	ret = out_fifo_level(rol, &value);
+	len += sprintf(proc_read_text + len, "      %4d|", MAXOUTFIFO - value);
+
+	ret = in_fifo_level(rol, &value);
+	len += sprintf(proc_read_text + len, "      %4d|", value);
+
+	len += sprintf(proc_read_text + len, " %8d\n", served[card][channel]);     
+      }
+    }
+
+    len += sprintf(proc_read_text + len, " \n");
+    len += sprintf(proc_read_text + len, "The command 'echo <action> > /proc/filar', executed as root,\n");
+    len += sprintf(proc_read_text + len, "allows you to interact with the driver. Possible actions are:\n");
+    len += sprintf(proc_read_text + len, "debug   -> enable debugging\n");
+    len += sprintf(proc_read_text + len, "nodebug -> disable debugging\n");
+    len += sprintf(proc_read_text + len, "elog    -> Log errors to /var/log/message\n");
+    len += sprintf(proc_read_text + len, "noelog  -> Do not log errors to /var/log/message\n");
+    len += sprintf(proc_read_text + len, "disable -> disable all FILAR channels\n");
+    len += sprintf(proc_read_text + len, "reset   -> Reset all FILAR channels\n");
+    len += sprintf(proc_read_text + len, "flush   -> Flush all FILAR channels\n");
+  }
+  kdebug(("filar(filar_read_procmem): number of characters in text buffer = %d\n", len));
+
+  if (count < (len - offset))
+    nchars = count;
+  else
+    nchars = len - offset;
+  kdebug(("filar(filar_read_procmem): min nchars         = %d\n", nchars));
+  
+  if (nchars > 0)
+  {
+    for(loop = 0; loop < nchars; loop++)
+      buf[loop + (offset & (PAGE_SIZE - 1))] = proc_read_text[offset + loop];
+    *start = buf + (offset & (PAGE_SIZE - 1));
+  }
+  else
+  {
+    nchars = 0;
+    *eof = 1;
+  }
+ 
+  kdebug(("filar(filar_read_procmem): returning *start   = 0x%016lx\n", (u_long)*start));
+  kdebug(("filar(filar_read_procmem): returning nchars   = %d\n", nchars));
+  return(nchars);
+}
+
+
+/********************************************************************************/
+static irqreturn_t filar_irq_handler (int irq, void *dev_id, struct pt_regs *regs)   //MJ: for 2.6 see p272 & p273 
+/********************************************************************************/
+{
+  u_int card;
+
+  // Note: the FILAR card(s) may have to share an interrupt line with other PCI devices
+  // It is therefore important to exit quickly if we are not concerned with an interrupt
+  // For now this is done by looking at the FIFOSTAT register. There may be a dedicated
+  // interrupt status bit in the future.
+  
+  for(card = 0; card < maxcard; card++)
+  {
+    if ((filarcard[card].irq_line == (u_int)irq) && (filarcard[card].regs->osr & 0x2))
+    {
+      if (fuse)
+        read_ack_and_write_out(card);
+      else
+      {    
+        filarcard[card].regs->imask = 0;  //Disable all interrupts
+        kerror(("filar(filar_irq_handler): CATASTROPHY: Interrupt received from card %d with fuse=0\n", card));
+      }
+      return(IRQ_HANDLED);  //MJ: for 2.6 see p273
+    }
+    else
+    {
+      kerror(("filar(filar_irq_handler): This was not a FILAR interrupt\n"));
+      return(IRQ_NONE);     
+    }
+  }
+  return(IRQ_NONE);     
+}  
+
+
+
+/*****************************/
+/* PCI driver functions      */
+/*****************************/
+
+/**********************************************************************************/
+static int __devinit filarProbe(struct pci_dev *dev, const struct pci_device_id *id)   //MJ: for _devinit see p31
+/**********************************************************************************/
+{
+  int i, card;
+  u_long flags;
+  u_int size;
+
+  kdebug(("filar(filarProbe): called for device: 0x%04x / 0x%04x\n", dev->vendor, dev->device));
+  kdebug(("filar(filarProbe): Bus: 0x%04x / Devfn 0x%04x\n", dev->bus->number, dev->devfn));
+
+  /* Find a new "slot" for the card */
+  card = -1;
+  for (i = 0; i < MAXCARDS; i++) 
+  {
+    if (filarcard[i].pciDevice == NULL) 
+    {
+      card = i;
+      filarcard[card].pciDevice = dev;
+      break;
+    }
+  }
+
+  if (card == -1) 
+  {
+    kerror(("filar(filarProbe): Could not find space in the filarcard array for this card."));
+    return(-FILAR_TOOMANYCARDS);
+  }
+  
+  flags = pci_resource_flags(dev, BAR0);  //MJ: See p317
+  if ((flags & IORESOURCE_MEM) != 0) 
+  {
+    filarcard[card].pci_memaddr = pci_resource_start(dev, BAR0) & PCI_BASE_ADDRESS_MEM_MASK;        //MJ: See p317
+    size = pci_resource_end(dev, BAR0) - pci_resource_start(dev, BAR0);                             //MJ: See p317
+    kdebug(("filar(filarProbe): Mapping %d bytes at PCI address 0x%08x\n", size, filarcard[card].pci_memaddr));
+    
+    filarcard[card].regs = (T_filar_regs *)ioremap(filarcard[card].pci_memaddr, size);
+    if (filarcard[card].regs == NULL)
+    {
+      kerror(("filar(filarProbe): error from ioremap for filarcard[%d].regs\n", card));
+      filarcard[card].pciDevice = NULL;
+      return(-FILAR_IOREMAP);
+    }
+    kdebug(("filar(filarProbe): filarcard[%d].regs is at 0x%016lx\n", card, (u_long)filarcard[card].regs));
+  }
+  else 
+  {
+    kerror(("filar(filarProbe): Bar 0 is not a memory resource"));
+    return(-FILAR_EIO);
+  }
+
+  // get revision directly from the Filar
+  pci_read_config_byte(filarcard[card].pciDevice, PCI_REVISION_ID, &filarcard[card].pci_revision);
+  kdebug(("filar(filarProbe): revision = %x \n", filarcard[card].pci_revision));
+  if (filarcard[card].pci_revision < MINREV)
+  {
+    kerror(("filar(filarProbe): Illegal Filar Revision\n"));
+    return(-FILAR_ILLREV);
+  }
+
+  pci_read_config_byte(filarcard[card].pciDevice, PCI_INTERRUPT_LINE, &filarcard[card].irq_line);
+  kdebug(("filar(filarProbe): interrupt line = %d \n", filarcard[card].irq_line));
+
+  //Several FILAR cards may use the same interrupt but we want to install the driver only once
+  if (irqlist[filarcard[card].irq_line] == 0) 
+  {
+    if(request_irq(filarcard[card].irq_line, filar_irq_handler, SA_SHIRQ, "filar", dev))	
+    {
+      kdebug(("filar(filarProbe): request_irq failed on IRQ = %d\n", filarcard[card].irq_line));
+      return(-FILAR_REQIRQ);
+    }
+    irqlist[filarcard[card].irq_line]++;
+    kdebug(("filar(filarProbe): Interrupt %d registered\n", filarcard[card].irq_line)); 
+  }  
+ 
+  maxcard++;
+  return(0);
+}
+
+
+/******************************************/
+static void filarRemove(struct pci_dev *dev) 
+/******************************************/
+{
+  int i, card;
+
+  /* Find the "slot" of the card */
+  card = -1;
+  for (i = 0; i < MAXCARDS; i++) 
+  {
+    if (filarcard[i].pciDevice == dev) 
+    {
+      card = i;
+      filarcard[i].pciDevice = 0;
+      break;
+    }  
+  }
+  
+  if (card == -1) 
+  {
+    kerror(("filar(filarRemove): Could not find device to remove."));
+    return;
+  }
+
+  if (--irqlist[filarcard[card].irq_line] == 0)
+  {
+    kerror(("filar(filarRemove): removing handler for interrupt %d", filarcard[card].irq_line));
+    free_irq(filarcard[card].irq_line, dev);	
+  }
+  
+  iounmap((void *)filarcard[card].regs);
+  kerror(("filar(filarRemove): Card %d removed", card));
+  maxcard--;
+}
+
+
+
+/*********************/
+/* Service functions */
+/*********************/
+
+/****************************************/
+static int hw_fifo_push(int rol, int data)
+/****************************************/
+{
+  if (hwfifon[rol] == MAXHWFIFO)
+  {
+    kerror(("filar(hw_fifo_push): The HW FIFO is full\n"));
+    blow_fuse();
+    return(-1);
+  }
+  hwfifo[rol][hwfifow[rol]] = data;
+  hwfifow[rol]++;
+  if (hwfifow[rol] == MAXHWFIFO)
+    hwfifow[rol] = 0;
+  
+  hwfifon[rol]++;
+  return(0);
+}
+
+
+/****************************************/
+static int hw_fifo_pop(int rol, int *data)
+/****************************************/
+{
+  if (hwfifon[rol] == 0)
+  {
+    kerror(("filar(hw_fifo_pop): The HW FIFO is empty\n"));
+    blow_fuse();
+    return(-1);
+  }
+  *data = hwfifo[rol][hwfifor[rol]];
+  hwfifor[rol]++;
+  if (hwfifor[rol] == MAXHWFIFO)
+    hwfifor[rol] = 0;
+  
+  hwfifon[rol]--;
+  return(0);
+}
+
+
+/*******************************************/
+static int hw_fifo_level(int rol, int *nfree)
+/*******************************************/
+{
+  *nfree = MAXHWFIFO - hwfifon[rol];
+  return(0);
+}
+
+
+/****************************************/
+static int in_fifo_pop(int rol, int *data)
+/****************************************/
+{
+  if (infifon[rol] == 0)
+  {
+    kdebug(("filar(in_fifo_pop): The IN FIFO is empty\n"));
+    return(-1);
+  }
+  *data = infifo[IN_INDEX(rol, infifor[rol])];
+
+  infifor[rol]++;
+  if (infifor[rol] == MAXINFIFO)
+    infifor[rol] = 0;
+
+  infifon[rol]--;
+
+  return(0);
+}
+
+
+/*******************************************/
+static int in_fifo_level(int rol, int *nfree)
+/*******************************************/
+{
+  *nfree = MAXINFIFO - infifon[rol];
+  return(0);
+}
+
+
+/******************************************************************************************/
+static int out_fifo_push(int rol, int pciaddr, int fragsize, int fragstat, int scw, int ecw)
+/******************************************************************************************/
+{
+  int oo;
+  
+  oo = OUT_INDEX(rol, outfifow[rol]);
+  kdebug(("filar(out_fifo_push): oo = %d\n", oo));
+  
+  if (outfifon[rol] == MAXOUTFIFO)
+  {
+    kerror(("filar(out_fifo_push): The OUT FIFO is full\n"));
+    return(-1);
+  }
+  
+  outfifo[oo]     = pciaddr;
+  outfifo[oo + 1] = fragsize;
+  outfifo[oo + 2] = fragstat;
+  outfifo[oo + 3] = scw;
+  outfifo[oo + 4] = ecw;
+
+  outfifow[rol]++;
+  if (outfifow[rol] == MAXOUTFIFO)
+    outfifow[rol] = 0;
+  outfifon[rol]++;
+  return(0);
+}
+
+
+/********************************************/
+static int out_fifo_level(int rol, int *nfree)
+/********************************************/
+{
+  *nfree = MAXOUTFIFO - outfifon[rol];
+  return(0);
+}
+
+
+/*****************************/
+static void disable_input(void)
+/*****************************/
+{
+  u_int card, data;
+
+  for(card = 0; card < maxcard; card++)
+  {
+    data = filarcard[card].regs->ocr;
+    data |= 0x08208200;
+    filarcard[card].regs->ocr = data;   //disable all channels
+  }
+}
+
+
+/****************************/
+static void enable_input(void)
+/****************************/
+{
+  u_int card;
+
+  for(card = 0; card < maxcard; card++)
+    filarcard[card].regs->ocr = cmask[card];  //reset cannels to initial state
+}
+
+
+/******************************************/
+static void read_ack_and_write_out(int card)
+/******************************************/
+{
+  u_int data2, fragstat, nfree, pciaddr, ret, dindex, rol, loop, nacks, channel;
+  volatile u_int data, *ackptr, *reqptr;
+
+  //---> Part 1: read / write FILAR registers
+  disable_input();
+  for(channel = 0; channel < MAXCHANNELS; channel++)
+  {
+    kdebug(("filar(read_ack_and_write_out): channels[%d][%d] = %d\n", card, channel, channels[card][channel]));
+    if (channels[card][channel])
+    {
+      dindex = 0;
+      rol = (card << 2) + channel;     
+      reqptr = (u_int *) (&filarcard[card].regs->req1 + 4 * channel);
+      ackptr = (u_int *) reqptr + 1;
+      out_fifo_level(rol, &nfree); 
+
+      while(1)
+      {   
+        nacks = (filarcard[card].regs->fifostat >> (channel * 8)) & 0xf;
+        kdebug(("filar(read_ack_and_write_out): card=%d  channel=%d  rol=%d  nacks=%d\n", card, channel, rol, nacks));
+        if (!nacks)
+          break;
+
+        for(loop = 0; loop < nacks; loop++)
+        {  
+	  ret = hw_fifo_pop(rol, &pciaddr);
+          if (ret)
+          {
+            kerror(("filar(read_ack_and_write_out): error from hw_fifo_pop\n"));
+	    blow_fuse();
+            return;
+          }
+          kdebug(("filar(read_ack_and_write_out): Read PCI address 0x%08x from HW FIFO\n", pciaddr));
+
+	  data = *ackptr;
+          kdebug(("filar(read_ack_and_write_out): Read 0x%08x from ACK FIFO\n", data));
+          ackdata[rol][dindex][0] = data;
+          if ((data & 0x40000000) || ccw)
+            ackdata[rol][dindex][1] = filarcard[card].regs->scw;
+          if ((data & 0x10000000) || ccw)
+            ackdata[rol][dindex][2] = filarcard[card].regs->ecw;
+	    
+          ackdata[rol][dindex][3] = pciaddr;
+          dindex++;
+	  nfree--;
+	  if (!nfree)
+          {
+            kerror(("filar(read_ack_and_write_out): nfree=0 , dindex=%d-> blowing fuse\n", dindex));
+            blow_fuse();
+            return;
+          }
+        }
+      }
+      
+      acknum[rol] = dindex;             
+      kdebug(("filar(read_ack_and_write_out): Received %d fragments from card=%d, channel=%d\n", dindex, card, channel));
+
+      for(loop = 0; loop < dindex; loop++)
+      {
+        ret = in_fifo_pop(rol, &pciaddr);
+        if (!ret)
+        {
+	  ret = hw_fifo_push(rol, pciaddr);
+          if (ret)
+          {
+            kerror(("filar(read_ack_and_write_out): error from hw_fifo_push\n"));
+            blow_fuse();
+            return;
+          }
+          *reqptr = pciaddr;
+          kdebug(("filar(read_ack_and_write_out): Wrote PCI address 0x%08x to REQ FIFO\n", pciaddr));
+	}  
+      }
+    }
+  }
+  enable_input();
+  
+  //---> Part 2: process data
+  for(channel = 0; channel < MAXCHANNELS; channel++)
+  {
+    if (channels[card][channel])
+    {
+      rol = (card << 2) + channel;
+      for(loop = 0; loop < acknum[rol]; loop++)
+      {        
+        fragstat = 0;
+        data = ackdata[rol][loop][0];
+        kdebug(("filar(read_ack_and_write_out): Analyzing card=%d  channel=%d  fragment=%d  ack=0x%08x\n", card, channel, loop, data));
+        if (data & 0x80000000)
+        {
+          fragstat |= NO_SCW;
+          kerror(("filar(read_ack_and_write_out): NO_SCW"));
+        }
+        else 
+        {
+          if (data & 0x40000000)
+          {
+            fragstat |= BAD_SCW;
+            kerror(("filar(read_ack_and_write_out): BAD_SCW"));
+
+            data2 = ackdata[rol][loop][1];
+            kerror(("filar(read_ack_and_write_out): Start Control Word              = 0x%08x\n", data2));
+
+            if (data2 & 0x1)
+            {
+              kerror(("filar(read_ack_and_write_out): Data transmission error in SCW\n"));
+              fragstat |= SCW_DTE;
+            }
+
+            if (data2 & 0x2)
+            {
+              kerror(("filar(read_ack_and_write_out): Control word transmission error in SCW\n"));
+              fragstat |= SCW_CTE;
+            }
+          }
+        }
+
+        if (data & 0x20000000)
+        {
+          fragstat |= NO_ECW;
+          kerror(("filar(read_ack_and_write_out): NO_ECW"));
+        }
+        else
+        {
+          if (data & 0x10000000)
+          {
+            fragstat |= BAD_ECW;
+            kerror(("filar(read_ack_and_write_out): BAD_ECW"));
+
+            data2 = ackdata[rol][loop][2];  
+            kerror(("filar(read_ack_and_write_out): End Control Word                = 0x%08x\n", data2));
+
+            if (data2 & 0x1)
+            {
+              kerror(("filar(read_ack_and_write_out): Data transmission error in ECW\n"));
+              fragstat |= ECW_DTE;
+            }
+
+            if (data2 & 0x2)
+            {
+              kerror(("filar(read_ack_and_write_out): Control word transmission error in ECW\n"));
+              fragstat |= ECW_CTE;
+            } 
+          }
+        }
+
+        if (ccw)
+          ret = out_fifo_push(rol, ackdata[rol][loop][3], data & 0x000fffff, fragstat, ackdata[rol][loop][1], ackdata[rol][loop][2]);
+	else
+          ret = out_fifo_push(rol, ackdata[rol][loop][3], data & 0x000fffff, fragstat, 0 ,0);
+    
+        if (ret)
+        {
+          kerror(("filar(read_ack_and_write_out): error from out_fifo_push\n"));
+          blow_fuse();
+	  return;
+        }        
+      }
+      served[card][channel] += acknum[rol];
+    }
+  } 
+}
+
+
+/*************************/
+static void blow_fuse(void)
+/*************************/
+{   
+  // If this fuse blows one has to re-configure the FILAR (to re-enable the interrupt)
+  u_int data, card, channel, rol, nacks, nfree;
+
+  fuse = 0;
+  kerror(("filar(blow_fuse): There was an internal error in the driver\n"));
+  kerror(("filar(blow_fuse): All interrupt have been disabled\n"));
+  kerror(("filar(blow_fuse): You have to reload the driver\n"));
+
+  for(card = 0; card < MAXCARDS; card++)
+  {
+    for(channel = 0; channel < MAXCHANNELS; channel++)
+    {
+      if (channels[card][channel])
+      {
+        rol = (card << 2) + channel;
+        nacks = (filarcard[card].regs->fifostat >> (channel * 8)) & 0xf;
+        out_fifo_level(rol, &nfree); 
+        kerror(("filar(blow_fuse): rol=%d  nacks=%d  nfree=%d\n", rol, nacks, nfree));
+      }
+    }
+  } 
+
+  for(card = 0; card < maxcard; card++)
+  {
+    filarcard[card].regs->imask = 0;  //Disable all interrupts
+    
+    data = filarcard[card].regs->ocr;
+    data |= 0x1;
+    filarcard[card].regs->ocr = data;
+    udelay(2);  // Delay for at least one us
+    data &= 0xfffffffe;
+    filarcard[card].regs->ocr = data;
+  }
+}
+
+
+/*******************************/
+static void reset_cards(int mode)
+/*******************************/
+{
+  u_int card, rol, channel, data;
+
+  kdebug(("filar(reset_cards): maxcard = %d   mode = %d\n", maxcard, mode));
+  for(card = 0; card < maxcard; card++)
+  {
+    data = filarcard[card].regs->ocr;
+    data |= 0x1;
+    filarcard[card].regs->ocr = data;
+    // Delay for at least one us
+    udelay(10);
+    data &= 0xfffffffe;
+    filarcard[card].regs->ocr = data;
+    //reset the FIFOs
+    if (mode)
+    {
+      for(channel = 0; channel < MAXCHANNELS; channel++)
+      {
+        served[card][channel] = 0;
+        rol = (card << 2) + channel;
+        infifor[rol] = 0;
+        infifon[rol] = 0;
+        outfifow[rol] = 0;
+        outfifon[rol] = 0;
+        hwfifow[rol] = 0;
+        hwfifor[rol] = 0;
+        hwfifon[rol] = 0;
+      }
+    }
+  }
+  kdebug(("filar(reset_cards): Done\n"));
+}
+
+
+/***************************/
+static void flush_cards(void)
+/***************************/
+{
+  u_long flags;
+  u_int card;
+
+  //We have to make sure that this code does not get interrupted.
+  //Otherwhise there can be a race condition.
+  local_irq_save(flags);
+  for(card = 0; card < maxcard; card++)
+  {
+    kdebug(("filar(flush_cards): Calling read_ack_and_write_out for card %d\n", card));
+    read_ack_and_write_out(card);
+  }
+
+  local_irq_restore(flags);
+}
+
diff --git a/cmt/filar_dma.c b/cmt/filar_dma.c
new file mode 100644
index 0000000000000000000000000000000000000000..c82858f7df63cbc5a83ba61fcb4b83ff8f52a878
--- /dev/null
+++ b/cmt/filar_dma.c
@@ -0,0 +1,1511 @@
+/************************************************************************/
+/*									*/
+/* File: filar_driver.c							*/
+/*									*/
+/* driver for the FILAR S-Link interface				*/
+/*									*/
+/* 25. Jun. 02  MAJO  created						*/
+/*									*/
+/************ C 2003 - The software with that certain something *********/
+
+/************************************************************************/
+/* NOTES:								*/
+/* - This driver should work on kernels from 2.4 onwards		*/
+/************************************************************************/
+
+/************************************************************************/
+/*Open issues:								*/
+/*- so far the errors in the ISR only printk to var/log/message. One 	*/
+/*  could use a global variable to remember the error and report it 	*/
+/*  via the next ioctl() call.						*/
+/*- The start and end control word patters passed via the INIT ioctl    */
+/*  are not yet used in the ISR for consistency checking.		*/
+/*- The FILAR does not return the actual control words via the ACK block*/
+/*- The ioctl fuctions copy large arrays into the driver and back to	*/
+/*  the user application. This is so because the amount of data in the	*/
+/*  arrays is not known. If possible one should only copy the valid 	*/
+/*  data. I don't know yet how to do this				*/ 
+/************************************************************************/
+
+#include <linux/init.h>           //MJ: for 2.6, p30
+#include <linux/module.h>
+#include <linux/moduleparam.h>    //MJ: for 2.6, p30
+#include <linux/kernel.h>
+#include <linux/stat.h>           //MJ: for 2.6, e.g. for module_param
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/cdev.h>           //e.g. for cdev_alloc
+#include <linux/slab.h>           //e.g. for kmalloc
+#include <linux/delay.h>          //e.g. for udelay
+#include <linux/interrupt.h>      //e.g. for request_irq
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>           //e.g. for local_irq_save
+#include "ROSfilar/filar_driver.h"
+
+
+/*********/
+/*Globals*/
+/*********/
+static int debug = 0, errorlog = 1;
+static u_int opid, scw, ecw, filar_irq_line[MAXCARDS];
+static u_int maxcards, channels[MAXCARDS][MAXCHANNELS], served[MAXCARDS][MAXCHANNELS], pci_memaddr[MAXCARDS];
+static u_char pci_revision[MAXCARDS];
+static u_int irq_in[MAXCARDS], hwfifo[MAXROLS][MAXHWFIFO];
+static u_int hwfifow[MAXROLS], hwfifor[MAXROLS], hwfifon[MAXROLS];
+static u_int *outfifo, *outfifon;
+static u_int *infifo, *infifon;
+static u_int irqlist[MAXIRQ], infifor[MAXROLS], outfifow[MAXROLS];
+static u_int order, ackadd[MAXCARDS], reqadd[MAXCARDS], *ackbuf[MAXCARDS], *reqbuf[MAXCARDS], swfifobasephys, swfifobase, outfifodatasize, infifodatasize, fifoptrsize, tsize = 0;
+static struct proc_dir_entry *filar_file;
+struct filar_proc_data_t filar_proc_data;
+static char *proc_read_text;
+static dev_t major_minor;
+static struct cdev *filar_cdev;
+static T_filar_card filarcard[MAXCARDS];
+
+/************/
+/*Prototypes*/
+/************/
+static int req_block(int card, int ackenable);
+static int __devinit filarProbe(struct pci_dev *dev, const struct pci_device_id *id);
+static int filar_init(void);
+static void filar_cleanup(void);
+static void filar_vmaOpen(struct vm_area_struct *vma);
+static void filar_vmaClose(struct vm_area_struct *vma);
+static void filarRemove(struct pci_dev *dev); 
+static irqreturn_t filar_irq_handler (int irq, void *dev_id, struct pt_regs *regs); 
+
+MODULE_DESCRIPTION("S-Link FILAR (DMA F/W)");
+MODULE_AUTHOR("Markus Joos, CERN/PH");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("Private: Contact markus.joos@cern.ch");
+module_param (debug, int, S_IRUGO | S_IWUSR);  //MJ: for 2.6 p37
+MODULE_PARM_DESC(debug, "1 = enable debugging   0 = disable debugging"); 
+module_param (errorlog, int, S_IRUGO | S_IWUSR);  //MJ: for 2.6 p37
+MODULE_PARM_DESC(errorlog, "1 = enable debugging   0 = disable debugging");
+
+// memory handler functions
+static struct vm_operations_struct filar_vm_ops = 
+{
+  .close = filar_vmaClose,   //mmap-close
+  .open  = filar_vmaOpen,    //MJ: Note the comma at the end of the list!
+};
+
+// Standard file operations
+static struct file_operations fops = 
+{
+  .owner   = THIS_MODULE,
+  .ioctl   = filar_ioctl,
+  .open    = filar_open,    
+  .mmap    = filar_mmap,
+  .release = filar_release,
+};
+
+// PCI driver structures. See p311
+static struct pci_device_id filarIds[] = 
+{
+  {PCI_DEVICE(PCI_VENDOR_ID_CERN, 0x14)},      
+  {0,},
+};
+
+//PCI operations
+static struct pci_driver filarPciDriver = 
+{
+  .name     = "filar_dma",
+  .id_table = filarIds,
+  .probe    = filarProbe,
+  .remove   = filarRemove,
+};
+
+
+/*****************************/
+/* Standard driver functions */
+/*****************************/
+
+/**************************/
+static int filar_init(void)
+/**************************/
+{
+  int rol, tfsize, result;
+  u_int card, loop;
+  struct page *page_ptr;
+
+  kerror(("FILAR (SC) driver version 3.0\n")); 
+
+  if (alloc_chrdev_region(&major_minor, 0, 1, "filar")) //MJ: for 2.6 p45
+  {
+    kdebug(("filar(filar_init): failed to obtain device numbers\n"));
+    result = -FILAR_EIO;
+    goto fail1;
+  }
+  
+  proc_read_text = (char *)kmalloc(MAX_PROC_TEXT_SIZE, GFP_KERNEL);
+  if (proc_read_text == NULL)
+  {
+    kdebug(("filar(filar_init): error from kmalloc\n"));
+    result = -EFAULT;
+    goto fail2;
+  }
+
+  filar_file = create_proc_entry("filar", 0644, NULL);
+  if (filar_file == NULL)
+  {
+    kerror(("filar(filar_init): error from call to create_proc_entry\n"));
+    result = -EFAULT;
+    goto fail3;
+  }  
+  
+  strcpy(filar_proc_data.name, "filar");
+  strcpy(filar_proc_data.value, "filar");
+  filar_file->data = &filar_proc_data;
+  filar_file->read_proc = filar_read_procmem;
+  filar_file->write_proc = filar_write_procmem;
+  filar_file->owner = THIS_MODULE;  
+  
+  //Clear the list of used interupts 
+  for(loop = 0; loop < MAXIRQ; loop++)
+    irqlist[loop] = 0;
+
+  kdebug(("filar(filar_init): registering PCIDriver\n"));
+  result = pci_register_driver(&filarPciDriver);  //MJ: See P312
+  if (result == 0)
+  {
+    kerror(("filar(filar_init): ERROR! no FILAR cards found!\n"));
+    result = -EIO;
+    goto fail4;
+  }
+  else 
+  {
+    kerror(("filar(filar_init): Found %d FILAR cards!\n", result));
+  }
+
+  // Allocate contiguous memory for the FIFOs and store the base addresses in a global structure
+  outfifodatasize = MAXROLS * MAXOUTFIFO * OUTFIFO_ELEMENTS * sizeof(u_int);    //outfifo
+  infifodatasize  = MAXROLS * MAXINFIFO * INFIFO_ELEMENTS * sizeof(u_int);      //infifo
+  fifoptrsize     = MAXROLS * sizeof(u_int);                                    //outfifo(w/r/n) + infifo(w/r/n)
+  kdebug(("filar(filar_init): 0x%08x bytes needed for the OUT FIFO\n", outfifodatasize));
+  kdebug(("filar(filar_init): 0x%08x bytes needed for the IN FIFO\n", infifodatasize));
+  kdebug(("filar(filar_init): 0x%08x bytes needed for the FIFO pointers\n", fifoptrsize));
+  
+  tsize = outfifodatasize + infifodatasize + 2 * fifoptrsize;
+  kdebug(("filar(filar_init): 0x%08x bytes needed for all FIFOs\n", tsize));
+
+  tfsize = (tsize + 4095) & 0xfffff000;  // round up to next 4K boundary
+  tfsize = tfsize >> 12;                 // compute number of pages
+  kdebug(("filar(filar_init): %d pages needed for the S/W FIFOs\n", tfsize));
+ 
+  order = 0;
+  while(tfsize)
+  {
+    order++;
+    tfsize = tfsize >> 1;                 // compute order
+  } 
+  kdebug(("filar(filar_init): order = %d\n", order));
+  
+  swfifobase = __get_free_pages(GFP_ATOMIC, order);
+  if (!swfifobase)
+  {
+    kerror(("filar(filar_init): error from __get_free_pages\n"));
+    result = -FILAR_EFAULT;
+    goto fail5;
+  }
+  kdebug(("filar(filar_init): swfifobase = 0x%08x\n", swfifobase));
+   
+  // Reserve all pages to make them remapable
+  page_ptr = virt_to_page(swfifobase);
+
+  for (loop = (1 << order); loop > 0; loop--, page_ptr++)
+    set_bit(PG_reserved, &page_ptr->flags);                   //MJ: have a look at the kernel book
+
+  swfifobasephys = virt_to_bus((void *)swfifobase);
+  kdebug(("filar(filar_init): swfifobasephys = 0x%08x\n", swfifobasephys));
+
+  // Assign base addresses to the FIFO arrays
+  outfifo  = (u_int *)swfifobase;
+  infifo   = (u_int *)(swfifobase + outfifodatasize);
+  outfifon = (u_int *)(swfifobase + outfifodatasize + infifodatasize);
+  infifon  = (u_int *)(swfifobase + outfifodatasize + infifodatasize + fifoptrsize);
+
+  kdebug(("filar(filar_init): outfifo  is at 0x%016lx\n", (u_long)outfifo));
+  kdebug(("filar(filar_init): infifo   is at 0x%016lx\n", (u_long)infifo));
+  kdebug(("filar(filar_init): outfifon is at 0x%016lx\n", (u_long)outfifon));
+  kdebug(("filar(filar_init): infifon  is at 0x%016lx\n", (u_long)infifon));
+
+  //Initialize the FIFOs
+  for(rol = 0; rol < MAXROLS; rol++)
+  {
+    infifor[rol] = 0;
+    infifon[rol] = 0;
+    hwfifow[rol] = 0;
+    hwfifor[rol] = 0;
+    hwfifon[rol] = 0;
+    outfifow[rol] = 0;
+    outfifon[rol] = 0;
+  }
+
+  //Allocate the buffers for the REQ and ACK blocks
+  //The max size of such a block is: 4 channels * (1 size word + 31 data words) = 128 words = 512 bytes
+  for(card = 0; card < maxcards; card++)
+  {
+    reqbuf[card] = (u_int *)__get_free_page(GFP_ATOMIC);
+    if (!reqbuf[card])
+    {
+      kerror(("filar(init_module): error from __get_free_pages for reqbuf\n"));
+      return(-FILAR_EFAULT);
+    }
+    reqadd[card] = virt_to_bus((void *) reqbuf[card]);
+    kdebug(("filar(init_module): reqbuf[%d] = 0x%08x (PCI = 0x%08x)\n", card, (u_int)reqbuf[card], reqadd[card]));
+  
+    ackbuf[card] = (u_int *)__get_free_page(GFP_ATOMIC);
+    if (!ackbuf[card])
+    {
+      kerror(("filar(init_module): error from __get_free_pages for ackbuf\n"));
+      return(-FILAR_EFAULT);
+    }
+    ackadd[card] = virt_to_bus((void *) ackbuf[card]);
+    kdebug(("filar(init_module): ackbuf[%d] = 0x%08x (PCI = 0x%08x)\n", card, (u_int)ackbuf[card], ackadd[card]));
+  }
+
+  opid = 0;
+
+  filar_cdev = (struct cdev *)cdev_alloc();       //MJ: for 2.6 p55
+  filar_cdev->ops = &fops;
+  result = cdev_add(filar_cdev, major_minor, 1);  //MJ: for 2.6 p56
+  if (result)
+  {
+    kdebug(("filar(filar_init): error from call to cdev_add.\n"));
+    goto fail6;
+  }
+  
+  
+  kdebug(("filar(filar_init): driver loaded; major device number = %d\n", MAJOR(major_minor)));
+  return(0);  
+  
+  fail6:
+    page_ptr = virt_to_page(swfifobase);
+    for (loop = (1 << order); loop > 0; loop--, page_ptr++)
+      clear_bit(PG_reserved, &page_ptr->flags);
+    free_pages(swfifobase, order);
+
+  fail5:
+    pci_unregister_driver(&filarPciDriver);
+
+  fail4:
+    remove_proc_entry("io_rcc", NULL);
+    
+  fail3:
+    kfree(proc_read_text);
+
+  fail2:
+    unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45
+
+  fail1:
+    return(result);  
+}
+
+
+/******************************/
+static void filar_cleanup(void)
+/******************************/
+{
+  u_int card, loop;
+  struct page *page_ptr;
+      
+  cdev_del(filar_cdev);                     //MJ: for 2.6 p56
+  pci_unregister_driver(&filarPciDriver);
+  page_ptr = virt_to_page(swfifobase);      // unreserve all pages used for the S/W FIFOs
+
+  for (loop = (1 << order); loop > 0; loop--, page_ptr++)
+    clear_bit(PG_reserved, &page_ptr->flags);
+
+  free_pages(swfifobase, order);            // free the area 
+
+  //Return the buffers for the REQ and ACK blocks
+  for(card = 0; card < maxcards; card++)
+  {
+    free_page((int)reqbuf[card]);
+    free_page((int)ackbuf[card]);
+  }
+
+  remove_proc_entry("filar", NULL);
+  kfree(proc_read_text);
+  unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45
+
+  kdebug(("filar(filar_cleanup): driver removed\n"));
+}
+
+
+module_init(filar_init);    //MJ: for 2.6 p16
+module_exit(filar_cleanup); //MJ: for 2.6 p16
+
+
+/**********************************************************/
+static int filar_open(struct inode *ino, struct file *filep)
+/**********************************************************/
+{
+  return(0);
+}
+
+
+/*************************************************************/
+static int filar_release(struct inode *ino, struct file *filep)
+/*************************************************************/
+{
+  opid = 0;
+  return(0);
+}
+
+
+/*************************************************************************************************/
+static int filar_ioctl(struct inode *inode, struct file *file, u_int cmd, unsigned long arg)
+/*************************************************************************************************/
+{
+  switch (cmd)
+  {    
+    case OPEN:
+    {
+      FILAR_open_t params;
+      
+      kdebug(("filar(ioctl,OPEN): called from process %d\n", current->pid))
+      if (!opid)
+        opid = current->pid;
+      else
+      {
+      	kerror(("filar(ioctl, OPEN): The FILAR is already used by process %d\n", opid));
+  	return(-FILAR_USED);
+      }
+      	
+      params.size = tsize;
+      params.physbase = swfifobasephys;
+      params.outsize = outfifodatasize;
+      params.insize = infifodatasize;
+      params.ptrsize = fifoptrsize;
+            
+      if (copy_to_user((void *)arg, &params, sizeof(FILAR_open_t)) != 0)
+      {
+	kerror(("filar(ioctl, OPEN): error from copy_to_user\n"));
+	return(-FILAR_EFAULT);
+      }      
+      return(0);
+      break;
+    }
+    
+    case CLOSE:
+    {
+      kdebug(("filar(ioctl,CLOSE): called from process %d\n", current->pid))
+      opid = 0;
+  
+      return(0);
+      break;
+    }
+    case CONFIG:
+    { 
+      FILAR_config_t params;
+      u_int card, channel, data, rol;
+      
+      kdebug(("filar(ioctl,CONFIG): \n"));
+      if (copy_from_user(&params, (void *)arg, sizeof(FILAR_config_t)) !=0 )
+      {
+	kerror(("filar(ioctl,CONFIG): error from copy_from_user\n"));
+	return(-FILAR_EFAULT);
+      }
+        
+      scw = params.scw;
+      ecw = params.ecw;
+        
+      kdebug(("filar(ioctl,CONFIG): params.bswap = %d\n", params.bswap));
+      kdebug(("filar(ioctl,CONFIG): params.wswap = %d\n", params.wswap));
+      kdebug(("filar(ioctl,CONFIG): params.psize = %d\n", params.psize));
+              
+      for(card = 0; card < maxcards; card++)
+      {
+        irq_in[card] = 0;
+        data = 0;
+        for(channel = 0; channel < MAXCHANNELS; channel++)
+        { 
+          served[card][channel] = 0;
+          rol = (card << 2) + channel;  //works only for 4 channels per card
+          kdebug(("filar(ioctl,CONFIG): params.enable[%d] = %d\n", rol, params.enable[rol]));
+          if (!params.enable[rol])
+          {
+            data += (1 << (6 * channel + 9));
+            channels[card][channel] = 0;
+            kdebug(("filar(ioctl,CONFIG): channels[%d][%d] = %d\n", card, channel, channels[card][channel]));
+          }
+          else
+          {
+            channels[card][channel] = 1; 
+            kdebug(("filar(ioctl,CONFIG): channels[%d][%d] = %d\n", card, channel, channels[card][channel]));
+          }
+          kdebug(("filar(ioctl,CONFIG): data = 0x%08x\n", data));
+        }
+        data += (params.bswap << 1) + (params.wswap << 2) + (params.psize << 3);
+        kdebug(("filar(ioctl,CONFIG): Writing 0x%08x to the OCR\n", data));
+        filarcard[card].regs->ocr = (data | 0x80);
+        filarcard[card].regs->ackadd = ackadd[card];
+        if (params.interrupt)
+          filarcard[card].regs->imask |= 0x8;  //Enable the ACKBLK_DONE interrupt
+      }
+      break;
+    }
+    
+    case RESET:
+    {
+      u_int channel, data, card, rol;
+      
+      if (copy_from_user(&card, (void *)arg, sizeof(int)) !=0 )
+      {
+	kerror(("filar(ioctl,RESET): error from copy_from_user\n"));
+	return(-FILAR_EFAULT);
+      }
+
+      irq_in[card] = 0;
+      for(channel = 0; channel < MAXCHANNELS; channel++)
+        served[card][channel] = 0;
+      
+      if (card <= maxcards)
+      {
+        kdebug(("filar(ioctl,RESET): Resetting card %d\n", card));
+        data = filarcard[card].regs->ocr;
+        data |= 0x1;
+        filarcard[card].regs->ocr = data;
+        // Delay for at least one us
+        udelay(10);
+        data &= 0xfffffffe;
+        filarcard[card].regs->ocr = data;
+
+        //reset the FIFOs
+        for(channel = 0; channel < MAXCHANNELS; channel++)
+        {
+          rol = (card << 2) + channel;
+          infifor[rol] = 0;
+          infifon[rol] = 0;
+          outfifow[rol] = 0;
+          outfifon[rol] = 0;
+          hwfifow[rol] = 0;
+          hwfifor[rol] = 0;
+          hwfifon[rol] = 0;
+        }
+      }
+      else
+      {
+       	kerror(("filar(ioctl,RESET): This card is unknown\n"));
+	return(-FILAR_NOCARD); 
+      }
+      
+      return(0);
+      break;
+    }
+    
+    case LINKRESET:
+    {   
+      u_int waited, rol, data, card, channel;
+ 
+      if (copy_from_user(&rol, (void *)arg, sizeof(int)) !=0 )
+      {
+	kerror(("filar(ioctl,LINKRESET): error from copy_from_user\n"));
+	return(-FILAR_EFAULT);
+      }
+      
+      card = rol >> 2;      
+      if (card <= maxcards)
+      {
+        channel = rol & 0x3;
+        kdebug(("filar(ioctl,LINKRESET): Resetting card %d, channel %d\n", card, channel));
+        served[card][channel] = 0;
+
+        // Set the URESET bit
+        data = 0x1 << (8 + channel * 6);
+        filarcard[card].regs->ocr |= data;
+        
+        // Delay for at least 30 ms
+        mdelay(30);
+
+        // Now wait for LDOWN to come up again
+        data = 0x1 << (16 + channel * 4);
+        waited = 0;
+        while(filarcard[card].regs->osr & data)
+        {
+          waited++;
+          if (waited > 10000)
+          {
+            kerror(("filar(ioctl,LINKRESET): channel %d of card %d does not come up again\n", channel, card));
+            // Reset the URESET bit
+            data = ~(0x1 << (8 + channel * 6));
+            filarcard[card].regs->ocr &= data;
+	    return(-FILAR_STUCK);
+          }          
+        }
+
+        // Reset the URESET bit
+        data = ~(0x1 << (8 + channel * 6));
+        filarcard[card].regs->ocr &= data;
+      }
+      else
+      {
+       	kerror(("filar(ioctl,LINKRESET): This card is unknown\n"));
+	return(-FILAR_NOCARD); 
+      }
+      
+      return(0);
+      break;
+    }
+    
+    case LINKSTATUS:
+    {   
+      u_int isup, data, card, rol, channel;
+      
+      if (copy_from_user(&rol, (void *)arg, sizeof(int)) !=0 )
+      {
+	kerror(("filar(ioctl, RESET): error from copy_from_user\n"));
+	return(-FILAR_EFAULT);
+      }
+      
+      card = rol >> 2;
+      if (card <= maxcards)
+      {
+        channel = rol & 0x3;
+        data = 0x1 << (16 + channel * 4);
+        if (filarcard[card].regs->osr & data)
+          isup = 0;
+        else
+          isup = 1;
+          
+        if (copy_to_user((void *)arg, &isup, sizeof(int)) != 0)
+        {
+	  kerror(("filar(ioctl, LINKSTATUS): error from copy_to_user\n"));
+	  return(-FILAR_EFAULT);
+        } 
+      }
+      else
+      {
+       	kerror(("filar(ioctl, LINKSTATUS): This card is unknown\n"));
+	return(-FILAR_NOCARD); 
+      }
+      
+      return(0);
+      break;
+    }
+
+    case INFO:
+    {
+      FILAR_info_t info;
+      u_int rol, card, data;
+     
+      //Initialise the array to 0
+      for(rol = 0; rol < MAXROLS; rol++)
+      {
+        info.channels[rol] = 0;
+        info.enabled[rol] = 0;
+      }
+      info.nchannels = 0;
+      
+      for(card = 0; card < maxcards; card++)
+      {
+        data = filarcard[card].regs->osr;
+        kdebug(("filar(ioctl, INFO): 0x%08x read from OSR\n", data));
+        if (data & 0x00080000)  info.channels[(card << 2)] = 0; 
+        else {info.channels[(card << 2)] = 1; info.nchannels++;}
+        
+        if (data & 0x00800000)  info.channels[(card << 2) + 1] = 0; 
+        else {info.channels[(card << 2) + 1] = 1; info.nchannels++;}
+        
+        if (data & 0x08000000)  info.channels[(card << 2) + 2] = 0; 
+        else {info.channels[(card << 2) + 2] = 1; info.nchannels++;}
+        
+        if (data & 0x80000000)  info.channels[(card << 2) + 3] = 0; 
+        else {info.channels[(card << 2) + 3] = 1; info.nchannels++;}
+                
+        data = filarcard[card].regs->ocr;
+        kdebug(("filar(ioctl, INFO): 0x%08x read from OCR\n", data));
+        
+        if (data & 0x00000200) info.enabled[(card << 2)] = 0;     else info.enabled[(card << 2)] = 1;
+        if (data & 0x00008000) info.enabled[(card << 2) + 1] = 0; else info.enabled[(card << 2) + 1] = 1;        
+        if (data & 0x00200000) info.enabled[(card << 2) + 2] = 0; else info.enabled[(card << 2) + 2] = 1;        
+        if (data & 0x08000000) info.enabled[(card << 2) + 3] = 0; else info.enabled[(card << 2) + 3] = 1;       
+      }  
+
+      if (copy_to_user((void *)arg, &info, sizeof(FILAR_info_t)) != 0)
+      {
+	kerror(("filar(ioctl, INFO): error from copy_to_user\n"));
+	return(-FILAR_EFAULT);
+      }      
+      return(0);
+      break;
+    }
+    
+    case FIFOIN:
+    { 
+      u_int ret, card, channel, rol;
+      unsigned long flags;
+
+      if (copy_from_user(&rol, (void *)arg, sizeof(int)) !=0)
+      {
+	kerror(("filar(ioctl,FIFOIN): error from copy_from_user\n"));
+	return(-FILAR_EFAULT);
+      }
+      card = rol >> 2;
+      channel = rol & 0x3; 
+      kdebug(("filar(ioctl,FIFOIN): channel = %d   card = %d\n", channel, card));
+
+      //We have to make sure that this code does not get interrupted. Otherwhise there will be
+      //inconsistencies in the REQ block which lead to incorrect PCI addresses being written to the FILAR
+      save_flags(flags);
+      cli();
+
+      ret = req_block(card, 0);
+      if (ret)
+      {
+        kerror(("filar(ioctl,FIFOIN): error from req_block\n"));
+	return(-FILAR_EREQ);
+      }
+
+      restore_flags(flags);
+      return(0);
+      break;
+    }
+    
+    case FLUSH:
+    { 
+      flush_cards();
+      break;
+    }
+  }   
+  return(0);
+}
+
+
+/****************************************************************************************************/
+static int filar_write_procmem(struct file *file, const char *buffer, unsigned long count, void *data)
+/****************************************************************************************************/
+{
+  struct filar_proc_data_t *fb_data = (struct filar_proc_data_t *)data;
+  int len;
+  u_int card;
+
+  kdebug(("filar(filar_write_procmem): filar_write_procmem called\n"));
+
+  if(count > 99)
+    len = 99;
+  else
+    len = count;
+
+  if (copy_from_user(fb_data->value, buffer, len)) 
+  {
+    kerror(("filar(filar_write_procmem): error from copy_from_user\n"));
+    return(-FILAR_EFAULT);
+  }
+ 
+  kdebug(("filar(filar_write_procmem): len = %d\n", len)); 
+  fb_data->value[len - 1] = '\0';
+  kdebug(("filar(filar_write_procmem): text passed = %s\n", fb_data->value));
+ 
+  if (!strcmp(fb_data->value, "debug"))
+  {
+    debug = 1;
+    kdebug(("filar(filar_write_procmem): debugging enabled\n")); 
+  }
+    
+  if (!strcmp(fb_data->value, "nodebug"))
+  {
+    kdebug(("filar(filar_write_procmem): debugging disabled\n")); 
+    debug = 0;   
+  }
+  
+  if (!strcmp(fb_data->value, "disable"))
+  {
+    for(card = 0; card < maxcards; card++)
+      filarcard[card].regs->ocr |= 0x08208200;
+    kdebug(("filar(filar_write_procmem): All channels disabled\n")); 
+  }
+
+  if (!strcmp(fb_data->value, "reset"))
+  {
+    reset_cards(1);
+    kdebug(("filar(filar_write_procmem): All cards reset\n"));
+  }
+  
+  if (!strcmp(fb_data->value, "elog"))
+  {
+    kdebug(("filar(filar_write_procmem): Error logging enabled\n"))
+    errorlog = 1;
+  }
+
+  if (!strcmp(fb_data->value, "noelog"))
+  {
+    kdebug(("filar(filar_write_procmem): Error logging disabled\n"))
+    errorlog = 0;
+  }
+  
+  if (!strcmp(fb_data->value, "flush"))
+  {
+    flush_cards();
+    kdebug(("filar(filar_write_procmem): All cards flushed\n"));
+  }
+  
+  if (!strcmp(fb_data->value, "dec"))
+  {
+    kdebug(("filar(filar_write_procmem): Use count decremented\n")); 
+  }
+  
+  if (!strcmp(fb_data->value, "inc"))
+  {
+    kdebug(("filar(filar_write_procmem): Use count incremented\n")); 
+  }
+  
+  return len;
+}
+
+
+/***************************************************************************************************/
+static int filar_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data)
+/***************************************************************************************************/
+{
+  u_int fdata, rol, ret, card, ocr[MAXROLS], osr[MAXROLS], osr2[MAXROLS], imask[MAXROLS], efstatr[MAXROLS], efstata[MAXROLS], value, value2, channel;
+  int loop, nchars = 0;
+  static int len = 0;
+
+  kdebug(("filar(filar_read_procmem): Called with buf    = 0x%08x\n", (u_int)buf));
+  kdebug(("filar(filar_read_procmem): Called with *start = 0x%08x\n", (u_int)*start));
+  kdebug(("filar(filar_read_procmem): Called with offset = %d\n", (u_int)offset));
+  kdebug(("filar(filar_read_procmem): Called with count  = %d\n", count));
+
+  if (offset == 0)
+  {
+    kdebug(("exp(exp_read_procmem): Creating text....\n"));
+    len = 0;
+  len += sprintf(proc_read_text + len, "\n\n\nFILAR driver (DMA protocol) for release %s (based on CVS tag %s)\n", RELEASE_NAME, CVSTAG);
+
+  if  (opid)
+    len += sprintf(proc_read_text + len, "The drivers is currently used by PID %d\n", opid);
+    len += sprintf(proc_read_text + len, "=================================================================\n");
+    len += sprintf(proc_read_text + len, "Card|IRQ| ACK IRQ|revision|PCI MEM addr.| page size|wswap|bswap|DMA|temperature|ACK address|REQ address|IRQs received\n");
+    len += sprintf(proc_read_text + len, "----|---|--------|--------|-------------|----------|-----|-----|---|-----------|-----------|-----------|-------------\n");
+
+    //Read the registers
+    for(card = 0; card < maxcards; card++)
+    {
+      ocr[card]     = filarcard[card].regs->ocr;
+      osr[card]     = filarcard[card].regs->osr;
+      osr2[card]    = filarcard[card].regs->osr;
+      imask[card]   = filarcard[card].regs->imask;
+      efstatr[card] = filarcard[card].regs->efstatr;
+      efstata[card] = filarcard[card].regs->efstata;
+    }
+
+    for(card = 0; card < maxcards; card++)
+    {
+      len += sprintf(proc_read_text + len, "   %d|", card);
+      len += sprintf(proc_read_text + len, "%3d|", filar_irq_line[card]);
+      len += sprintf(proc_read_text + len, "%s|", (imask[card] & 0x8)?" enabled":"disabled"); 
+      len += sprintf(proc_read_text + len, "    0x%02x|", pci_revision[card]); 
+      len += sprintf(proc_read_text + len, "   0x%08x|", pci_memaddr[card]);
+      value = (ocr[card] >> 3) & 0x7;
+      if (value == 0) len += sprintf(proc_read_text + len, " 256 bytes|");
+      if (value == 1) len += sprintf(proc_read_text + len, "   1 Kbyte|");
+      if (value == 2) len += sprintf(proc_read_text + len, "  2 Kbytes|");
+      if (value == 3) len += sprintf(proc_read_text + len, "  4 Kbytes|");
+      if (value == 4) len += sprintf(proc_read_text + len, " 16 Kbytes|");
+      if (value == 5) len += sprintf(proc_read_text + len, " 64 Kbytes|");
+      if (value == 6) len += sprintf(proc_read_text + len, "256 Kbytes|");
+      if (value == 7) len += sprintf(proc_read_text + len, "4 MB - 8 B|");
+      len += sprintf(proc_read_text + len, "  %s|", (ocr[card] & 0x4)?"yes":" no");
+      len += sprintf(proc_read_text + len, "  %s|", (ocr[card] & 0x2)?"yes":" no");
+      len += sprintf(proc_read_text + len, "%s|", (ocr[card] & 0x80)?"yes":" no");
+      len += sprintf(proc_read_text + len, " %3d deg. C|", (osr[card] >>8) & 0xff);
+      fdata = filarcard[card].regs->ackadd;
+      len += sprintf(proc_read_text + len, " 0x%08x|", fdata);
+      fdata = filarcard[card].regs->reqadd;
+      len += sprintf(proc_read_text + len, " 0x%08x|", fdata);
+      fdata = filarcard[card].regs->blkctl & 0xff;
+      len += sprintf(proc_read_text + len, "%13d\n", irq_in[card]);
+    }
+
+    for(card = 0; card < maxcards; card++)
+    {
+      len += sprintf(proc_read_text + len, "\nCard %d:\n", card);
+      len += sprintf(proc_read_text + len, "=======\n");
+      len += sprintf(proc_read_text + len, "       |       |       |          |free      |     |        |       |          |free      |         |fragments\n");
+      len += sprintf(proc_read_text + len, "       |       |       |entries in|entries in|     |        |       |entries in|entries in|fragments|per      \n");
+      len += sprintf(proc_read_text + len, "Channel|present|enabled|ACK FIFO  |REQ FIFO  |LDOWN|Overflow|   XOFF|OUT FIFO  |IN FIFO   |served   |interrupt\n");
+      len += sprintf(proc_read_text + len, "-------|-------|-------|----------|----------|-----|--------|-------|----------|----------|---------|---------\n");
+
+      for(channel  = 0; channel < MAXCHANNELS; channel++)
+      {
+	len += sprintf(proc_read_text + len, "      %d|", channel);
+
+	value = osr[card] & (1 << (19 + channel * 4));
+	len += sprintf(proc_read_text + len, "    %s|", value?" no":"yes");
+
+	value = ocr[card] & (1 << (9 + channel * 6));
+	len += sprintf(proc_read_text + len, "    %s|", value?" no":"yes");
+
+	value = (efstata[card] >> (channel * 8)) & 0xff;
+	len += sprintf(proc_read_text + len, "        %2d|", value);
+
+	value = (efstatr[card] >> (channel * 8)) & 0xff;
+	len += sprintf(proc_read_text + len, "        %2d|", value);
+
+	value = osr[card] & (1 << (16 + channel * 4));
+	len += sprintf(proc_read_text + len, "  %s|", value?"yes":" no"); 
+
+	value = osr[card] & (1 << (17 + channel * 4));
+	len += sprintf(proc_read_text + len, "     %s|", value?"yes":" no");
+
+	value = osr[card] & (1 << (18 + channel * 4));
+	value2 = osr2[card] & (1 << (18 + channel * 4));
+	if (!value && !value2)
+          len += sprintf(proc_read_text + len, " is off|");
+	else if (value && value2)
+          len += sprintf(proc_read_text + len, "  is on|");
+	else if (value && !value2)
+          len += sprintf(proc_read_text + len, " was on|");
+	else 
+          len += sprintf(proc_read_text + len, "went on|");
+
+	rol = (card << 2) + channel;
+	ret = out_fifo_level(rol, &value);
+	len += sprintf(proc_read_text + len, "      %4d|", MAXOUTFIFO - value);
+
+	ret = in_fifo_level(rol, &value);
+	len += sprintf(proc_read_text + len, "      %4d|", value);
+
+	len += sprintf(proc_read_text + len, " %8d|", served[card][channel]);     
+
+	if (irq_in[card]) 
+          len += sprintf(proc_read_text + len, "%f\n", (float)served[card][channel] / (float)irq_in[card]);
+	else
+          len += sprintf(proc_read_text + len, "undefined\n");
+      }
+    }
+
+    len += sprintf(proc_read_text + len, " \n");
+    len += sprintf(proc_read_text + len, "The command 'echo <action> > /proc/filar', executed as root,\n");
+    len += sprintf(proc_read_text + len, "allows you to interact with the driver. Possible actions are:\n");
+    len += sprintf(proc_read_text + len, "debug   -> Enable debugging\n");
+    len += sprintf(proc_read_text + len, "nodebug -> Disable debugging\n");
+    len += sprintf(proc_read_text + len, "elog    -> Log errors to /var/log/message\n");
+    len += sprintf(proc_read_text + len, "noelog  -> Do not log errors to /var/log/message\n");
+    len += sprintf(proc_read_text + len, "disable -> Disable all FILAR channels\n");
+    len += sprintf(proc_read_text + len, "reset   -> Reset all FILAR channels\n");
+    len += sprintf(proc_read_text + len, "flush   -> Flush all FILAR channels\n");
+    len += sprintf(proc_read_text + len, "dec     -> Decrement use count\n");
+    len += sprintf(proc_read_text + len, "inc     -> Increment use count\n");
+  }
+  kdebug(("filar(filar_read_procmem): number of characters in text buffer = %d\n", len));
+
+  if (count < (len - offset))
+    nchars = count;
+  else
+    nchars = len - offset;
+  kdebug(("filar(filar_read_procmem): min nchars         = %d\n", nchars));
+  
+  if (nchars > 0)
+  {
+    for (loop = 0; loop < nchars; loop++)
+      buf[loop + (offset & (PAGE_SIZE - 1))] = proc_read_text[offset + loop];
+    *start = buf + (offset & (PAGE_SIZE - 1));
+  }
+  else
+  {
+    nchars = 0;
+    *eof = 1;
+  }
+ 
+  kdebug(("filar(filar_read_procmem): returning *start   = 0x%08x\n", (u_int)*start));
+  kdebug(("filar(filar_read_procmem): returning nchars   = %d\n", nchars));
+  return(nchars);
+}
+
+
+
+
+/*************************************************************************/
+static irqreturn_t filar_irq_handler (int irq, void *dev_id, struct pt_regs *regs)
+/*************************************************************************/
+{
+  u_int card;
+
+  // Note: the FILAR card(s) may have to share an interrupt line with other PCI devices
+  // It is therefore important to exit quickly if we are not concerned with an interrupt
+
+  for(card = 0; card < maxcards; card++)
+  {
+    if (filar_irq_line[card] == (u_int)irq)
+    {
+      if (filarcard[card].regs->osr & 0x8)
+      {
+        irq_in[card]++;
+        read_ack_and_write_out(card);
+        return(IRQ_HANDLED);  //MJ: for 2.6 see p273
+      }
+    }
+  }
+  return(IRQ_NONE);     
+}
+
+/**********************************************************************************/
+static int __devinit filarProbe(struct pci_dev *dev, const struct pci_device_id *id)   //MJ: for _devinit see p31
+/**********************************************************************************/
+{
+  int i, card;
+  u_long flags;
+  u_int size;
+
+  kdebug(("filar(filarProbe): called for device: 0x%04x / 0x%04x\n", dev->vendor, dev->device));
+  kdebug(("filar(filarProbe): Bus: 0x%04x / Devfn 0x%04x\n", dev->bus->number, dev->devfn));
+
+  /* Find a new "slot" for the card */
+  card = -1;
+  for (i = 0; i < MAXCARDS; i++) 
+  {
+    if (filarcard[i].pciDevice == NULL) 
+    {
+      card = i;
+      filarcard[card].pciDevice = dev;
+      break;
+    }
+  }
+
+  if (card == -1) 
+  {
+    kerror(("filar(filarProbe): Could not find space in the filarcard array for this card."));
+    return(-FILAR_TOOMANYCARDS);
+  }
+  
+  flags = pci_resource_flags(dev, BAR0);  //MJ: See p317
+  if ((flags & IORESOURCE_MEM) != 0) 
+  {
+    filarcard[card].pci_memaddr = pci_resource_start(dev, BAR0) & PCI_BASE_ADDRESS_MEM_MASK;        //MJ: See p317
+    size = pci_resource_end(dev, BAR0) - pci_resource_start(dev, BAR0);                             //MJ: See p317
+    kdebug(("filar(filarProbe): Mapping %d bytes at PCI address 0x%08x\n", size, filarcard[card].pci_memaddr));
+    
+    filarcard[card].regs = (T_filar_regs *)ioremap(filarcard[card].pci_memaddr, size);
+    if (filarcard[card].regs == NULL)
+    {
+      kerror(("filar(filarProbe): error from ioremap for filarcard[%d].regs\n", card));
+      filarcard[card].pciDevice = NULL;
+      return(-FILAR_IOREMAP);
+    }
+    kdebug(("filar(filarProbe): filarcard[%d].regs is at 0x%016lx\n", card, (u_long)filarcard[card].regs));
+  }
+  else 
+  {
+    kerror(("filar(filarProbe): Bar 0 is not a memory resource"));
+    return(-FILAR_EIO);
+  }
+
+  // get revision directly from the Filar
+  pci_read_config_byte(filarcard[card].pciDevice, PCI_REVISION_ID, &filarcard[card].pci_revision);
+  kdebug(("filar(filarProbe): revision = %x \n", filarcard[card].pci_revision));
+  if (filarcard[card].pci_revision < MINREV)
+  {
+    kerror(("filar(filarProbe): Illegal Filar Revision\n"));
+    return(-FILAR_ILLREV);
+  }
+
+  pci_read_config_byte(filarcard[card].pciDevice, PCI_INTERRUPT_LINE, &filarcard[card].irq_line);
+  kdebug(("filar(filarProbe): interrupt line = %d \n", filarcard[card].irq_line));
+
+  //Several FILAR cards may use the same interrupt but we want to install the driver only once
+  if (irqlist[filarcard[card].irq_line] == 0) 
+  {
+    if(request_irq(filarcard[card].irq_line, filar_irq_handler, SA_SHIRQ, "filar", dev))	
+    {
+      kdebug(("filar(filarProbe): request_irq failed on IRQ = %d\n", filarcard[card].irq_line));
+      return(-FILAR_REQIRQ);
+    }
+    irqlist[filarcard[card].irq_line]++;
+    kdebug(("filar(filarProbe): Interrupt %d registered\n", filarcard[card].irq_line)); 
+  }  
+ 
+  maxcards++;
+  return(0);
+}
+
+
+/******************************************/
+static void filarRemove(struct pci_dev *dev) 
+/******************************************/
+{
+  int i, card;
+
+  /* Find the "slot" of the card */
+  card = -1;
+  for (i = 0; i < MAXCARDS; i++) 
+  {
+    if (filarcard[i].pciDevice == dev) 
+    {
+      card = i;
+      filarcard[i].pciDevice = 0;
+      break;
+    }  
+  }
+  
+  if (card == -1) 
+  {
+    kerror(("filar(filarRemove): Could not find device to remove."));
+    return;
+  }
+
+  if (--irqlist[filarcard[card].irq_line] == 0)
+  {
+    kerror(("filar(filarRemove): removing handler for interrupt %d", filarcard[card].irq_line));
+    free_irq(filarcard[card].irq_line, dev);	
+  }
+  
+  iounmap((void *)filarcard[card].regs);
+  kerror(("filar(filarRemove): Card %d removed", card));
+  maxcards--;
+}
+//------------------
+// Service functions
+//------------------
+
+
+/****************************************/
+static int hw_fifo_push(int rol, int data)
+/****************************************/
+{
+  if (hwfifon[rol] == MAXHWFIFO)
+  {
+    kerror(("filar(hw_fifo_push): The HW FIFO is full\n"));
+    return(-1);
+  }
+  hwfifo[rol][hwfifow[rol]] = data;
+  hwfifow[rol]++;
+  if (hwfifow[rol] == MAXHWFIFO)
+    hwfifow[rol] = 0;
+  
+  hwfifon[rol]++;
+  return(0);
+}
+
+
+/****************************************/
+static int hw_fifo_pop(int rol, int *data)
+/****************************************/
+{
+  if (hwfifon[rol] == 0)
+  {
+    kerror(("filar(hw_fifo_pop): The HW FIFO is empty\n"));
+    return(-1);
+  }
+  *data = hwfifo[rol][hwfifor[rol]];
+  hwfifor[rol]++;
+  if (hwfifor[rol] == MAXHWFIFO)
+    hwfifor[rol] = 0;
+  
+  hwfifon[rol]--;
+  return(0);
+}
+
+
+/****************************************/
+static int in_fifo_pop(int rol, int *data)
+/****************************************/
+{
+  if (infifon[rol] == 0)
+  {
+    kdebug(("filar(in_fifo_pop): The IN FIFO is empty\n"));
+    return(-1);
+  }
+  *data = infifo[IN_INDEX(rol, infifor[rol])];
+
+  infifor[rol]++;
+
+  if (infifor[rol] > (MAXINFIFO - 1))
+    infifor[rol] = 0;
+
+  infifon[rol]--;
+
+  return(0);
+}
+
+
+/*******************************************/
+static int in_fifo_level(int rol, int *nfree)
+/*******************************************/
+{
+  *nfree = MAXINFIFO - infifon[rol];
+  return(0);
+}
+
+
+/******************************************************************************************/
+static int out_fifo_push(int rol, int pciaddr, int fragsize, int fragstat, int scw, int ecw)
+/******************************************************************************************/
+{
+  int oo;
+  
+  oo = OUT_INDEX(rol, outfifow[rol]);
+  kdebug(("filar(out_fifo_push): oo = %d\n", oo));
+  
+  if (outfifon[rol] == MAXOUTFIFO)
+  {
+    kerror(("filar(out_fifo_push): The OUT FIFO is full\n"));
+    return(-1);
+  }
+  
+  outfifo[oo] = pciaddr;
+  outfifo[oo + 1] = fragsize;
+  outfifo[oo + 2] = fragstat;
+  outfifo[oo + 3] = scw;
+  outfifo[oo + 4] = ecw;
+
+  outfifow[rol]++;
+  if (outfifow[rol] == MAXOUTFIFO)
+    outfifow[rol] = 0;
+  
+  outfifon[rol]++;
+  return(0);
+}
+
+
+/********************************************/
+static int out_fifo_level(int rol, int *nfree)
+/********************************************/
+{
+  *nfree = MAXOUTFIFO - outfifon[rol];
+  return(0);
+}
+
+
+/*******************************************/
+static int req_block(int card, int ackenable)
+/*******************************************/
+{
+  u_int rol, ret, reqlen, reqfifo, loop, nreqs, navail, nmin, channel, pciaddr, *reqptr;
+
+  reqlen = 0;
+  reqptr = reqbuf[card];
+  reqfifo = filarcard[card].regs->efstatr;
+  for(channel = 0; channel < MAXCHANNELS; channel++)
+  {
+    rol = (card << 2) + channel;
+    nreqs = (reqfifo >> (8 * channel)) & 0xff;
+    navail = infifon[rol]; 
+    if (nreqs < navail)
+      nmin = nreqs;
+    else
+      nmin = navail;
+
+    kdebug(("filar(req_block): nreqs=%d  navail=%d\n", nreqs, navail));
+    *reqptr++ = nmin;
+    reqlen++;
+    kdebug(("filar(req_block): Wrote nmin=%d to REQ block for channel=%d\n", nmin, channel));
+    for(loop = 0; loop < nmin; loop++)
+    {
+      ret = in_fifo_pop(rol, &pciaddr);
+      if (ret)
+      {
+        kerror(("filar(req_block): error from in_fifo_pop\n"));
+        return(-1);
+      }
+ 
+      ret = hw_fifo_push(rol, pciaddr);
+      if (ret)
+      {
+        kerror(("filar(req_block): error from hw_fifo_push\n"));
+        return(-1);
+      }
+      *reqptr++ = pciaddr;
+      reqlen++;
+      kdebug(("filar(req_block): Wrote PCI address 0x%08x to REQ block\n", pciaddr));
+    }
+  }
+  
+  if (ackenable)
+    filarcard[card].regs->blkctl = 0x60000000 | reqlen;
+  else
+    filarcard[card].regs->blkctl = 0x20000000 | reqlen;
+
+  kdebug(("filar(req_block): Wrote reqlen=%d to FILAR\n", reqlen));
+  filarcard[card].regs->reqadd = reqadd[card];
+  kdebug(("filar(req_block): Wrote reqadd=0x%08x to FILAR card=%d\n", reqadd[card], card));
+  
+  //Wait until the card has read the REQ block
+  loop = 0;
+  while(1)
+  {
+    if (++loop == 1000) 
+    {
+      kerror(("filar(req_block) FILAR refuses to read REQ block\n"));
+      return(-1);
+    }
+    if (filarcard[card].regs->osr & 0x4) break;
+  }
+
+  return(0);
+}
+
+
+/***************************************************/
+static void filar_vmaOpen(struct vm_area_struct *vma)
+/***************************************************/
+{ 
+  kdebug(("filar(filar_vmaOpen): Called\n"));
+}
+
+
+/****************************************************/
+static void filar_vmaClose(struct vm_area_struct *vma)
+/****************************************************/
+{  
+  kdebug(("filar(filar_vmaClose): Virtual address  = 0x%016lx\n",(u_long)vma->vm_start));
+  kdebug(("filar(filar_vmaClose): mmap released\n"));
+}
+
+
+/******************************************************************/
+static int filar_mmap(struct file *file, struct vm_area_struct *vma)
+/******************************************************************/
+{
+  unsigned long offset, size;
+
+  kdebug(("filar(filar_mmap): function called\n"));
+  
+  offset = vma->vm_pgoff << PAGE_SHIFT;
+  
+  size = vma->vm_end - vma->vm_start;
+
+  kdebug(("filar(filar_mmap): offset = 0x%08x\n",(u_int)offset));
+  kdebug(("filar(filar_mmap): size   = 0x%08x\n",(u_int)size));
+
+  if (offset & ~PAGE_MASK)
+  {
+    kerror(("filar(filar_mmap): offset not aligned: %ld\n", offset));
+    return -ENXIO;
+  }
+
+  // we only support shared mappings. "Copy on write" mappings are
+  // rejected here. A shared mapping that is writeable must have the
+  // shared flag set.
+  if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED))
+  {
+    kerror(("filar(filar_mmap): writeable mappings must be shared, rejecting\n"));
+    return(-EINVAL);
+  }
+
+  vma->vm_flags |= VM_RESERVED;
+  
+  // we do not want to have this area swapped out, lock it
+  vma->vm_flags |= VM_LOCKED;
+
+  // we create a mapping between the physical pages and the virtual
+  // addresses of the application with remap_page_range.
+  // enter pages into mapping of application
+  kdebug(("filar(filar_mmap): Parameters of remap_page_range()\n"));
+  kdebug(("filar(filar_mmap): Virtual address  = 0x%08x\n",(u_int)vma->vm_start));
+  kdebug(("filar(filar_mmap): Physical address = 0x%08x\n",(u_int)offset));
+  kdebug(("filar(filar_mmap): Size             = 0x%08x\n",(u_int)size));
+  if (remap_page_range(vma, vma->vm_start, offset, size, vma->vm_page_prot))
+  {
+    kerror(("filar(filar_mmap): remap page range failed\n"));
+    return -ENXIO;
+  }
+  
+  vma->vm_ops = &filar_vm_ops;  
+  
+  kdebug(("filar(filar_mmap): function done\n"));
+  return(0);
+}
+
+
+/******************************************/
+static void read_ack_and_write_out(int card)
+/******************************************/
+{
+  u_int rol, ret, data, scw = 0, ecw = 0, loop, nacks, channel, fragstat, pciaddr, *ackptr;
+
+  //---> Part 1: process the ACK blocks
+  ackptr = ackbuf[card];
+  for(channel = 0; channel < MAXCHANNELS; channel++)
+  {
+    rol = (card << 2) + channel;
+    nacks = *ackptr++;
+    served[card][channel] += nacks;
+    kdebug(("filar(read_ack_and_write_out): card=%d  channel=%d  nacks=%d\n", card, channel, nacks));
+    for(loop = 0; loop < nacks; loop++)
+    {
+      data = *ackptr++;
+      kdebug(("filar(read_ack_and_write_out): Analyzing 0x%08x\n", data));
+      fragstat = 0;
+      
+      if (data & 0x50000000)
+      {
+        scw = *ackptr++;
+        ecw = *ackptr++;
+      }
+  
+      if (data & 0x80000000)
+      { 
+        fragstat |= NO_SCW; 
+        kerror(("filar(read_ack_and_write_out): NO_SCW")); 
+      }
+      else 
+      {
+        if (data & 0x40000000)
+        { 
+          fragstat |= BAD_SCW; 
+          kerror(("filar(read_ack_and_write_out): BAD_SCW")); 
+          kerror(("filar(read_ack_and_write_out): Start Control Word = 0x%08x\n", scw));
+          if (scw & 0x1)
+          {
+            kerror(("filar(read_ack_and_write_out): Data transmission error in SCW\n"));
+            fragstat |= SCW_DTE;
+          }
+          if (scw & 0x2)
+          {
+            kerror(("filar(read_ack_and_write_out): Control word transmission error in SCW\n"));
+            fragstat |= SCW_CTE;
+          } 
+        }
+      }
+  
+      if (data & 0x20000000)
+      { 
+        fragstat |= NO_ECW; 
+        kerror(("filar(read_ack_and_write_out): NO_ECW")); 
+      }
+      else
+      {
+        if (data & 0x10000000)
+        { 
+          fragstat |= BAD_ECW; 
+          kerror(("filar(read_ack_and_write_out): BAD_ECW"));             
+          kerror(("filar(read_ack_and_write_out): End Control Word = 0x%08x\n", ecw));
+
+          if (ecw & 0x1)
+          {
+            kerror(("filar(read_ack_and_write_out): Data transmission error in ECW\n"));
+            fragstat |= ECW_DTE;
+          }
+
+          if (ecw & 0x2)
+          {
+            kerror(("filar(read_ack_and_write_out): Control word transmission error in ECW\n"));
+            fragstat |= ECW_CTE;
+          } 
+        }
+      }
+  
+      ret = hw_fifo_pop(rol, &pciaddr);
+      if (ret)
+      {
+        kerror(("filar(read_ack_and_write_out): error from hw_fifo_pop\n"));
+        blow_fuse();
+        return;
+      }
+      kdebug(("filar(read_ack_and_write_out): Read PCI address 0x%08x from HW FIFO\n", pciaddr));
+
+      ret = out_fifo_push(rol, pciaddr, data & 0x000fffff, fragstat, 0 ,0);
+      if (ret)
+      {
+        kerror(("filar(read_ack_and_write_out): error from out_fifo_push\n"));
+        blow_fuse();
+        return;
+      }        
+    }
+  }
+      
+  //---> Part 2: write REQ blocks
+  ret = req_block(card, 1);
+  if (ret)
+    kerror(("filar(read_ack_and_write_out): error from req_block\n"));
+
+  kdebug(("filar(read_ack_and_write_out): End of ISR\n"));
+}
+
+
+/*************************/
+static void blow_fuse(void)
+/*************************/
+{
+  u_int card;
+
+  for(card = 0; card < maxcards; card++)
+    filarcard[card].regs->imask = 0;
+  kerror(("filar(blow_fuse): fuse blown\n"));
+}
+
+
+/*******************************/
+static void reset_cards(int mode)
+/*******************************/
+{
+  u_int card, rol, channel, data;
+
+  kdebug(("filar(reset_cards): maxcards = %d   mode = %d\n", maxcards, mode));
+  for(card = 0; card < maxcards; card++)
+  {
+    data = filarcard[card].regs->ocr;
+    data |= 0x1;
+    filarcard[card].regs->ocr = data;
+    // Delay for at least one us
+    udelay(10);
+    data &= 0xfffffffe;
+    filarcard[card].regs->ocr = data;
+    irq_in[card] = 0;
+    //reset the FIFOs
+    if (mode)
+    {
+      for(channel = 0; channel < MAXCHANNELS; channel++)
+      {
+        served[card][channel] = 0;
+        rol = (card << 2) + channel;
+        infifor[rol] = 0;
+        infifon[rol] = 0;
+        outfifow[rol] = 0;
+        outfifon[rol] = 0;
+        hwfifow[rol] = 0;
+        hwfifor[rol] = 0;
+        hwfifon[rol] = 0;
+      }
+    }
+  }
+  kdebug(("filar(reset_cards): Done\n"));
+}
+
+
+/***************************/
+static void flush_cards(void)
+/***************************/
+{
+  u_int loop, card, channel;
+  unsigned long flags;
+
+  //We have to make sure that this code does not get interrupted.
+  //Otherwhise there can be a race condition.
+  save_flags(flags);
+  cli();
+
+  for(card = 0; card < maxcards; card++)
+  {
+    filarcard[card].regs->blkctl = 0x80000000;
+    loop = 0;
+    //MJ: should I sleep a bit to wait for ACKBLK_DONE = 0 ?
+    while(1)
+    {
+      if (++loop == 1000)
+      {        
+	kerror(("filar(flush_cards): FILAR refuses to send ACK block\n"));
+	return;
+      }
+      if (filarcard[card].regs->osr & 0x8)  break;
+    }
+    read_ack_and_write_out(card);
+  }
+
+  for(channel = 0; channel < MAXCHANNELS; channel++)
+  {
+    kdebug(("filar(flush_cards): There are %d events in the OUT FIFO of channel %d\n", outfifon[channel], channel));
+  }
+  
+  restore_flags(flags);
+}
+      
diff --git a/cmt/io_rcc.c b/cmt/io_rcc.c
new file mode 100644
index 0000000000000000000000000000000000000000..8775409b24014d1f4995fb82910edc3c3f8f3774
--- /dev/null
+++ b/cmt/io_rcc.c
@@ -0,0 +1,785 @@
+/************************************************************************/
+/*									*/
+/* File: io_rcc.c							*/
+/*									*/
+/* IO RCC driver							*/
+/*									*/
+/*  7. Jun. 02  MAJO  created						*/
+/*									*/
+/************ C 2005 - The software with that certain something *********/
+
+/************************************************************************/
+/*NOTES:								*/
+/*- This driver should work on kernels from 2.6.9 onwards		*/
+/************************************************************************/
+
+//MJ: for 2.6//#include <linux/config.h>
+//MJ: for 2.6//#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
+//MJ: for 2.6//  #define MODVERSIONS
+//MJ: for 2.6//#endif
+//MJ-SMP:#ifdef CONFIG_SMP
+//MJ-SMP:  #define __SMP__
+//MJ-SMP:#endif
+
+//MJ: for 2.6//#if defined(MODVERSIONS)
+//MJ: for 2.6//  #include <linux/modversions.h>
+//MJ: for 2.6//#endif
+#include <linux/init.h>           //MJ: for 2.6, p30
+#include <linux/module.h>         //p30
+#include <linux/moduleparam.h>    //MJ: for 2.6, p30
+#include <linux/kernel.h>         //e.g. for printk
+#include <linux/stat.h>           //MJ: for 2.6, e.g. for module_param
+#include <linux/pci.h>            //e.g. for   pci_find_device
+#include <linux/errno.h>          //e.g. for EFAULT
+#include <linux/fs.h>             //e.g. for register_chrdev
+#include <linux/proc_fs.h>        //e.g. for create_proc_entry
+#include <linux/mm.h>             //e.g. for vm_operations_struct
+//MJ: for 2.6//#include <linux/string.h>         
+//MJ: obsolete?//#include <linux/vmalloc.h>         
+//MJ: for 2.6//#include <linux/mman.h>            
+//MJ: for 2.6//#include <linux/wrapper.h>        
+#include <linux/slab.h>           //e.g. for kmalloc
+#include <linux/cdev.h>           //e.g. for cdev_alloc
+#include <asm/uaccess.h>          //e.g. for copy_from_user
+#include <asm/io.h>               //e.g. for inl
+#include <asm/system.h>           //e.g. for wmb
+#include "io_rcc/io_rcc_driver.h"
+
+
+/*********************************** Notes **************************************/
+/*										*/
+/* In 2.6 driver parameters (such a "debug") can be accessed via /sys/modlue/   */
+/* Therefore it may be possible to remove them from the writable /proc file     */
+/*										*/
+/* Rubini (p83) recommends to replace /proc by /sysfs				*/
+/* The seq_file service could be used for proc_read				*/
+/* and /sysfs for proc_write. The details need to be understood 		*/
+/* (and can be tricky)								*/ 
+/*										*/
+/* It is not clear if SLC4 will provide support for the devfs. If though the	*/
+/* creation of nodes could be simplified. Seems devfs was replaced by udev.	*/
+/*										*/
+/* The book of Rubini is based on 2.6.10. SLC4 is 2.6.9				*/
+/********************************************************************************/
+
+
+/*********/
+/*Globals*/
+/*********/
+static int debug = 0;
+static u_int board_type;
+static char *proc_read_text;
+static struct proc_dir_entry *io_rcc_file;
+static pci_devices_t pcidev[IO_MAX_PCI];
+struct io_proc_data_t io_proc_data;
+static dev_t major_minor;
+static struct cdev *io_rcc_cdev;
+  
+
+/***************************************************************/
+/* Use /sbin/modinfo <module name> to extract this information */
+/***************************************************************/
+MODULE_DESCRIPTION("PCI IO driver");
+MODULE_AUTHOR("Markus Joos, CERN/PH");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("Private: Contact markus.joos@cern.ch");
+module_param (debug, int, S_IRUGO | S_IWUSR);  //MJ: for 2.6 p37
+MODULE_PARM_DESC(debug, "1 = enable debugging   0 = disable debugging"); 
+
+
+//MJ: Not actually required. Just for kdebug
+struct vm_operations_struct io_rcc_vm_ops =
+{       
+  .close = io_rcc_vmaClose,      
+  .open = io_rcc_vmaOpen,      //MJ: Note the comma at the end of the list!
+};
+
+
+static struct file_operations fops = 
+{
+  .owner   = THIS_MODULE,
+  .ioctl   = io_rcc_ioctl,
+  .open    = io_rcc_open,    
+  .mmap    = io_rcc_mmap,
+  .release = io_rcc_release,    
+};
+
+
+/*****************************/
+/* Standard driver functions */
+/*****************************/
+
+/*************************/
+static int io_rcc_init(void)  //MJ: Declared "static" because we do not want to export it
+/*************************/
+{
+  int loop, ecode = 0;
+  u_char reg;
+
+  //MJ: for 2.6//SET_MODULE_OWNER(&fops);
+
+  // Read the board identification (works only on VP110/31x). MJ: Do we still need this?
+  outb(BID1, CMOSA);
+  reg = inb(CMOSD);
+  kdebug(("io_rcc(io_rcc_init): BID1 = 0x%02x\n", reg));
+  
+  if (!(reg & 0x80))
+  {
+    kdebug(("io_rcc(io_rcc_init): Unable to determine board type\n"));
+    board_type = VP_UNKNOWN;
+  }
+  else
+  {
+    outb(BID2, CMOSA);
+    reg = inb(CMOSD);
+    kdebug(("io_rcc(io_rcc_init): BID2 = 0x%02x\n", reg));
+
+    reg &= 0x1f;  // Mask board ID bits
+    board_type = VP_UNKNOWN;
+         if (reg == 2) board_type = VP_PSE;  // VP-PSE
+    else if (reg == 3) board_type = VP_PSE;  // VP-PSE
+    else if (reg == 4) board_type = VP_PSE;  // VP-PSE
+    else if (reg == 5) board_type = VP_PMC;  // VP-PMC
+    else if (reg == 6) board_type = VP_100;  // VP-100
+    else if (reg == 7) board_type = VP_CP1;  // VP-CP1
+    else  board_type = 0;
+    if (board_type == VP_PSE)
+      {kdebug(("io_rcc(io_rcc_init): Board type = VP-PSE\n"));}
+    if (board_type == VP_PMC)
+      {kdebug(("io_rcc(io_rcc_init): Board type = VP-PMC\n"));}
+    if (board_type == VP_100)
+      {kdebug(("io_rcc(io_rcc_init): Board type = VP-100\n"));}
+    if (board_type == VP_CP1)
+      {kdebug(("io_rcc(io_rcc_init): Board type = VP-CP1\n"));}
+    if (!board_type)
+      {kdebug(("io_rcc(io_rcc_init): Unable to determine board type(2)\n"));}
+  }
+  
+  if(alloc_chrdev_region (&major_minor, 0, 1, "io_rcc")); //MJ: for 2.6 p45
+  {
+    kdebug(("io_rcc(io_rcc_init): failed to obtain device numbers\n"));
+    goto fail1;
+  }
+  
+  proc_read_text = (char *)kmalloc(MAX_PROC_TEXT_SIZE, GFP_KERNEL);
+  if (proc_read_text == NULL)
+  {
+    ecode = -ENOMEM;
+    kdebug(("io_rcc(io_rcc_init): error from kmalloc\n"));
+    goto fail2;
+  }
+
+  io_rcc_file = create_proc_entry("io_rcc", 0644, NULL);
+  if (io_rcc_file == NULL)
+  {
+    kdebug(("io_rcc(io_rcc_init): error from call to create_proc_entry\n"));
+    ecode = -EFAULT;
+    goto fail3;
+  }
+
+
+  //MJ: Rubini does not mention the block below in the 2.6 boot but he is in
+  //general not very verbose about /proc. Maybe we just keep the commands as they
+  //worked in 2.4 
+  strcpy(io_proc_data.name, "io_rcc");
+  strcpy(io_proc_data.value, "io_rcc");
+  io_rcc_file->data = &io_proc_data;
+  io_rcc_file->read_proc = io_rcc_read_procmem;
+  io_rcc_file->write_proc = io_rcc_write_procmem;
+  io_rcc_file->owner = THIS_MODULE;
+
+  for(loop = 0; loop < IO_MAX_PCI; loop++) 
+    pcidev[loop].pid = 0;
+
+  io_rcc_cdev = (struct cdev *)cdev_alloc();      //MJ: for 2.6 p55
+  io_rcc_cdev->ops = &fops;
+  ecode = cdev_add(io_rcc_cdev, major_minor, 1);  //MJ: for 2.6 p56
+  if (ecode)
+  {
+    kdebug(("io_rcc(io_rcc_init): error from call to cdev_add.\n"));
+    goto fail4;
+  }
+  
+  kdebug(("io_rcc(io_rcc_init): driver loaded; major device number = %d\n", MAJOR(major_minor)));
+  return(0);
+
+  fail4:
+    remove_proc_entry("io_rcc", NULL);
+
+  fail3:
+    kfree(proc_read_text);
+
+  fail2:
+    unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45
+
+  fail1:
+    return(ecode);
+}
+
+
+/******************************/
+static void io_rcc_cleanup(void)
+/******************************/
+{
+  cdev_del(io_rcc_cdev);                     //MJ: for 2.6 p56
+  remove_proc_entry("io_rcc", NULL);
+  kfree(proc_read_text);
+  unregister_chrdev_region(major_minor, 1);  //MJ: for 2.6 p45
+  kdebug(("io_rcc(io_rcc_cleanup): driver removed\n"));
+}
+
+
+module_init(io_rcc_init);    //MJ: for 2.6 p16
+module_exit(io_rcc_cleanup); //MJ: for 2.6 p16
+
+
+/***********************************************************/
+static int io_rcc_open(struct inode *ino, struct file *filep)
+/***********************************************************/
+{
+  int loop;
+  private_stuff *pptr; 
+
+  //reserve space to store information about the devices managed by this "file"
+  pptr = (private_stuff *)kmalloc(sizeof (private_stuff), GFP_KERNEL);
+  if (pptr == NULL)
+  {
+    kdebug(("io_rcc_drv(io_rcc_open): error from kmalloc\n"));
+    return(-EFAULT);
+  }
+
+  //Initialize the space
+  for (loop = 0; loop < IO_MAX_PCI; loop++)
+    pptr->linked[loop] = 0;
+
+  filep->private_data = pptr;
+  kdebug(("io_rcc_drv(io_rcc_open): private_data = 0x%08lx\n", (u_long)filep->private_data));
+  
+  return(0);
+}
+
+
+/**************************************************************/
+static int io_rcc_release(struct inode *ino, struct file *filep)
+/**************************************************************/
+{
+  int loop;
+  private_stuff *pptr; 
+
+  kdebug(("io_rcc(release): pid = %d\n", current->pid));
+  pptr = (private_stuff *) filep->private_data;
+
+  // Release orphaned links to PCI devices
+  for(loop = 0; loop < IO_MAX_PCI; loop++)
+  {
+    if (pptr->linked[loop] == 1)
+    {
+      kdebug(("io_rcc(release): Orphaned PCI device unlinked (pid=%d: vid=0x%08x did=0x%08x)\n", pcidev[loop].pid, pcidev[loop].vid, pcidev[loop].did));
+      pcidev[loop].pid = 0;
+      pptr->linked[loop] = 0;
+    } 
+  }
+
+  kfree(pptr);
+  return(0);
+}
+
+
+/************************************************************************************/
+static int io_rcc_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg)
+/************************************************************************************/
+{
+  private_stuff *pptr; 
+
+  pptr = (private_stuff *) file->private_data;
+
+  switch (cmd)
+  {        
+    case IOPEEK:
+    {
+      IO_RCC_IO_t params;
+      int ret;
+      
+      ret = copy_from_user(&params, (void *)arg, sizeof(IO_RCC_IO_t));
+      if (ret)
+      {
+	kdebug(("io_rcc(ioctl,IOPEEK): error %d from copy_from_user\n",ret));
+	return(-EFAULT);
+      }
+      
+      if (params.size == 4)
+      {
+        params.data = inl(params.offset);
+        kdebug(("io_rcc(ioctl,IOPEEK): 0x%08x (int) read from 0x%08x\n", params.data, params.offset));
+      }
+      if (params.size == 2)
+      {
+        params.data = inw(params.offset);      
+        kdebug(("io_rcc(ioctl,IOPEEK): 0x%08x (word) read from 0x%08x\n", params.data, params.offset));
+      }
+      if (params.size == 1)
+      {
+        params.data = inb(params.offset);
+        kdebug(("io_rcc(ioctl,IOPEEK): 0x%08x (byte) read from 0x%08x\n", params.data, params.offset));
+      }
+        
+      if (copy_to_user((void *)arg, &params, sizeof(IO_RCC_IO_t)) != 0)
+      {
+	kdebug(("io_rcc(ioctl,IOPEEK): error from copy_to_user\n"));
+	return(-EFAULT);
+      }
+      break;
+    }   
+        
+    case IOPOKE:
+    {
+      IO_RCC_IO_t params;
+      int ret;
+      
+      ret = copy_from_user(&params, (void *)arg, sizeof(IO_RCC_IO_t));
+      if (ret)
+      {
+	kdebug(("io_rcc(ioctl,IOPOKE): error %d from copy_from_user\n",ret));
+	return(-EFAULT);
+      }
+      
+      if (params.size == 4) 
+      {
+        kdebug(("io_rcc(ioctl,IOPOKE): writing 0x%08x (int) to 0x%08x\n", params.data, params.offset));
+        outl(params.data, params.offset);
+        wmb();    // Recommended by Rubini on p247
+      }
+      if (params.size == 2) 
+      {
+        kdebug(("io_rcc(ioctl,IOPOKE): writing 0x%08x (word) to 0x%08x\n", params.data, params.offset));
+        outw(params.data&0xffff, params.offset);
+        wmb();
+      }
+      if (params.size == 1) 
+      {
+        kdebug(("io_rcc(ioctl,IOPOKE): writing 0x%08x (byte) to 0x%08x\n", params.data, params.offset));
+        outb(params.data&0xff, params.offset);
+        wmb();
+      }
+              
+      if (copy_to_user((void *)arg, &params, sizeof(IO_RCC_IO_t)) != 0)
+      {
+	kdebug(("io_rcc(ioctl,IOPOKE): error from copy_to_user\n"));
+	return(-EFAULT);
+      }
+      break;
+    } 
+     
+    case IOGETID:
+    {
+      if (copy_to_user((void *)arg, &board_type, sizeof(int)) != 0)
+      {
+    	kdebug(("io_rcc(ioctl,IOGETID): error from copy_to_user\n"));
+        return(-EFAULT);
+      }
+      break;
+    }
+    
+    case IOPCILINK:
+    {
+      int ret, dnum;
+      u_int loop;
+      IO_PCI_FIND_t find;
+      struct pci_dev *dev = NULL;
+      
+      ret = copy_from_user(&find, (void *)arg, sizeof(IO_PCI_FIND_t));
+      if (ret)
+      {
+	kdebug(("io_rcc(ioctl,IOPCILINK): error %d from copy_from_user\n",ret));
+	return(-EFAULT);
+      }     
+      
+      //Find a free slot in the pcidev array
+      //MJ: for now I am not checking if the same device has already been opened by an other call
+      dnum = -1;
+//MJ-SMP: to be protected (spinlock)
+      for(loop = 0; loop < IO_MAX_PCI; loop++)
+      {
+        if (pcidev[loop].pid == 0)
+        {
+          dnum = loop;
+          pcidev[dnum].pid = current->pid;  //Reserve this array element
+          pptr->linked[dnum] = 1;           //Remember to which "file" this mapping belongs to
+          break;
+        }
+      }
+//MJ-SMP: end of protected zone      
+      
+      if (dnum == -1)
+      {
+        kdebug(("io_rcc(ioctl,IOPCILINK): Device table is full\n"));
+        return(-IO_PCI_TABLEFULL);
+      }
+          
+      for(loop = 0; loop < find.occ; loop++)
+      {
+        dev = pci_get_device(find.vid, find.did, dev);  //Find N-th device  //MJ: for 2.6 p313      
+        if (dev == NULL)
+        {
+          kdebug(("io_rcc(ioctl,IOPCILINK): No device found\n"));
+          pcidev[dnum].pid = 0;                         //array element no longer required
+          pptr->linked[dnum] = 0;
+          return(-IO_PCI_NOT_FOUND);
+        }
+      }
+  
+      pci_dev_put(dev);          //MJ: for 2.6 p314
+      pci_enable_device(dev);    //MJ: for 2.6 p314
+ 
+      kdebug(("io_rcc(ioctl,IOPCILINK):Device found\n"));
+      kdebug(("io_rcc(ioctl,IOPCILINK):devfn =0x%08x\n", dev->devfn));
+      kdebug(("io_rcc(ioctl,IOPCILINK):irq   =0x%08x\n", dev->irq));
+
+      pcidev[dnum].vid     = find.vid;
+      pcidev[dnum].did     = find.did;
+      pcidev[dnum].occ     = find.occ;
+      pcidev[dnum].dev_ptr = dev;
+      
+      find.handle = dnum;
+    
+      ret = copy_to_user((void *)arg, &find, sizeof(IO_PCI_FIND_t));
+      if (ret)
+      {
+    	kdebug(("io_rcc(ioctl,IOPCILINK): error from copy_to_user\n"));
+        return(-EFAULT);
+      }
+      break;
+    }
+    
+    case IOPCIINFO:
+    {
+      int ret, loop;
+      pci_info_t info;   
+
+      ret = copy_from_user(&info, (void *)arg, sizeof(pci_info_t));
+      if (ret)
+      {
+	kdebug(("io_rcc(ioctl,IOPCIINFO): error %d from copy_from_user\n", ret));
+	return(-EFAULT);
+      }  
+      
+      kdebug(("io_rcc(ioctl,IOPCIINFO): Info requested for handle %d\n", info.handle));
+      
+      for (loop = 0; loop < 6; loop++)
+      {
+        info.bar[loop].base  = pci_resource_start(pcidev[info.handle].dev_ptr, loop);
+        info.bar[loop].size  = pci_resource_len(pcidev[info.handle].dev_ptr, loop); 
+        info.bar[loop].flags = pci_resource_flags(pcidev[info.handle].dev_ptr, loop);
+        kdebug(("io_rcc(ioctl,IOPCIINFO): BAR %d:\n", loop));
+        kdebug(("io_rcc(ioctl,IOPCIINFO): base  = 0x%08x:\n", info.bar[loop].base));
+        kdebug(("io_rcc(ioctl,IOPCIINFO): end   = 0x%08x:\n", info.bar[loop].size));
+        kdebug(("io_rcc(ioctl,IOPCIINFO): flags = 0x%08x:\n", info.bar[loop].flags));
+      }
+
+      ret = copy_to_user((void *)arg, &info, sizeof(pci_info_t));
+      if (ret)
+      {
+	kdebug(("io_rcc(ioctl,IOPCIINFO): error %d from copy_to_user\n", ret));
+	return(-EFAULT);
+      }  
+
+      break;
+    }
+    
+    case IOPCIUNLINK:
+    { 
+      u_int dnum;
+      int ret;
+ 
+      ret = copy_from_user(&dnum, (void *)arg, sizeof(int));
+      if (ret)
+      {
+	kdebug(("io_rcc(ioctl,IOPCIUNLINK): error %d from copy_from_user\n",ret));
+	return(-EFAULT);
+      }
+      
+      if (pcidev[dnum].pid == 0)
+      {
+        kdebug(("io_rcc(ioctl,IOPCICONFR): Illegal handle\n"));
+	return(-IO_PCI_ILL_HANDLE);
+      }
+      
+      pcidev[dnum].pid = 0;
+      pptr->linked[dnum] = 0;
+
+      break;
+    }
+    
+    case IOPCICONFR:
+    {
+      int ret;
+      IO_PCI_CONF_t params;
+      u_int idata;
+      u_short sdata;
+      u_char cdata;
+    
+      ret = copy_from_user(&params, (void *)arg, sizeof(IO_PCI_CONF_t));
+      if (ret)
+      {
+	kdebug(("io_rcc(ioctl,IOPCICONFR): error %d from copy_from_user\n",ret));
+	return(-EFAULT);
+      }
+      
+      if (pcidev[params.handle].pid == 0)
+      {
+        kdebug(("io_rcc(ioctl,IOPCICONFR): Illegal handle\n"));
+	return(-IO_PCI_ILL_HANDLE);
+      }
+      
+      if (params.size == 4)
+      {
+        ret = pci_read_config_dword(pcidev[params.handle].dev_ptr, params.offs, &idata);
+        params.data = idata;
+      }
+      if (params.size == 2)
+      {
+        ret = pci_read_config_word(pcidev[params.handle].dev_ptr, params.offs, &sdata);
+        params.data = sdata;
+      }
+      if (params.size == 1)
+      {
+        ret = pci_read_config_byte(pcidev[params.handle].dev_ptr, params.offs, &cdata);
+        params.data = cdata;
+      }
+        
+      if (ret)
+      {
+        kdebug(("io_rcc(ioctl,IOPCICONFR): ERROR from pci_read_config_xxx\n"));
+	return(-IO_PCI_CONFIGRW);
+      }
+      
+      ret = copy_to_user((void *)arg, &params, sizeof(IO_PCI_CONF_t));
+      if (ret)
+      {
+    	kdebug(("io_rcc(ioctl,IOPCICONFR): error from copy_to_user\n"));
+        return(-EFAULT);
+      }
+      
+      break;
+    }
+
+    case IOPCICONFW:   
+    {
+      int ret;
+      IO_PCI_CONF_t params;
+    
+      ret = copy_from_user(&params, (void *)arg, sizeof(IO_PCI_CONF_t));
+      if (ret)
+      {
+	kdebug(("io_rcc(ioctl,IOPCICONFW): error %d from copy_from_user\n",ret));
+	return(-EFAULT);
+      }
+      
+      if (pcidev[params.handle].pid == 0)
+      {
+        kdebug(("io_rcc(ioctl,IOPCICONFW): Illegal handle\n"));
+	return(-IO_PCI_ILL_HANDLE);
+      }
+      
+      if (params.size == 4)
+        ret = pci_write_config_dword(pcidev[params.handle].dev_ptr, params.offs, params.data);
+      if (params.size == 2)
+        ret = pci_write_config_dword(pcidev[params.handle].dev_ptr, params.offs, params.data & 0xffff);
+      if (params.size == 1)
+        ret = pci_write_config_dword(pcidev[params.handle].dev_ptr, params.offs, params.data & 0xff);
+      
+      if (ret)
+      {
+        kdebug(("io_rcc(ioctl,IOPCICONFW): ERROR from pci_write_config_xxxx\n"));
+	return(-IO_PCI_CONFIGRW);
+      }
+      
+      break;
+    }
+  }
+  return(0);
+}
+
+
+/****************************************************/
+static void io_rcc_vmaOpen(struct vm_area_struct *vma)
+/****************************************************/
+{ 
+  kdebug(("io_rcc_vmaOpen: Called\n"));
+}
+
+
+/*****************************************************/
+static void io_rcc_vmaClose(struct vm_area_struct *vma)
+/*****************************************************/
+{ 
+  kdebug(("io_rcc_vmaClose: mmap released\n"));
+}
+
+
+/*******************************************************************/
+static int io_rcc_mmap(struct file *file, struct vm_area_struct *vma)
+/*******************************************************************/
+{
+//MJ FIXME: remap_pfn_range seems not to exist in /proc/kallsyms. Therefore:
+/*
+  int result;
+
+  vma->vm_flags |= VM_RESERVED;   // This memory must not be swapped out
+  vma->vm_flags |= VM_LOCKED;     // We also want to lock it   //MJ: for 2.6: this flag still exists but Rubini does not mention it
+  
+  kdebug(("io_rcc_mmap: vma->vm_end    = 0x%016lx\n", (u_long)vma->vm_end));
+  kdebug(("io_rcc_mmap: vma->vm_start  = 0x%016lx\n", (u_long)vma->vm_start));
+  kdebug(("io_rcc_mmap: vma->vm_pgoff  = 0x%016lx\n", (u_long)vma->vm_pgoff));
+  kdebug(("io_rcc_mmap: vma->vm_flags  = 0x%08x\n", (u_int)vma->vm_flags));
+
+  //MJ: is this OK or should we use the example from p429?
+  result = remap_page_range(vma, vma->vm_start, vma->vm_pgoff, vma->vm_end - vma->vm_start, vma->vm_page_prot);   //MJ: for 2.6 p424
+  if (result)
+  {
+    kdebug(("io_rcc_mmap: function remap_page_range failed \n"));
+    return(-IO_PCI_REMAP);
+  }
+  kdebug(("io_rcc_mmap: vma->vm_start(2) = 0x%016lx\n", (u_long)vma->vm_start));
+
+  vma->vm_ops = &io_rcc_vm_ops;
+  return(0);
+*/
+
+  u_long size, offset;  
+
+  vma->vm_flags |= VM_RESERVED;  // This memory must not be swapped out
+  vma->vm_flags |= VM_LOCKED;    // We also want to lock it   //MJ: for 2.6: this flag still exists but Rubini does not mention it
+
+  kdebug(("io_rcc_mmap: vma->vm_end    = 0x%016lx\n", (u_long)vma->vm_end));
+  kdebug(("io_rcc_mmap: vma->vm_start  = 0x%016lx\n", (u_long)vma->vm_start));
+  kdebug(("io_rcc_mmap: vma->vm_offset = 0x%016lx\n", (u_long)vma->vm_pgoff << PAGE_SHIFT));
+  kdebug(("io_rcc_mmap: vma->vm_flags  = 0x%08x\n", (u_int)vma->vm_flags));
+
+  size = vma->vm_end - vma->vm_start;
+  offset = vma->vm_pgoff << PAGE_SHIFT;
+
+  if (remap_page_range(vma, vma->vm_start, offset, size, vma->vm_page_prot))
+  {
+    kdebug(("io_rcc_mmap: function remap_page_range failed \n"));
+    return(-IO_PCI_REMAP);
+  }
+  kdebug(("io_rcc_mmap: vma->vm_start(2) = 0x%016lx\n", (u_long)vma->vm_start));
+
+  vma->vm_ops = &io_rcc_vm_ops;
+  return(0);
+}
+
+
+/**********************************************************************************************/
+static int io_rcc_write_procmem(struct file *file, const char *buffer, u_long count, void *data)
+/**********************************************************************************************/
+{
+  int len;
+  struct io_proc_data_t *fb_data = (struct io_proc_data_t *)data;
+
+  kdebug(("io_rcc_mmap(io_rcc_write_procmem): io_rcc_write_procmem called\n"));
+
+  if(count > 99)
+    len = 99;
+  else
+    len = count;
+
+  if (copy_from_user(fb_data->value, buffer, len))
+  {
+    kdebug(("io_rcc_mmap(io_rcc_write_procmem): error from copy_from_user\n"));
+    return(-EFAULT);
+  }
+
+  kdebug(("io_rcc_mmap(io_rcc_write_procmem): len = %d\n", len));
+  fb_data->value[len - 1] = '\0';
+  kdebug(("io_rcc_mmap(io_rcc_write_procmem): text passed = %s\n", fb_data->value));
+
+  if (!strcmp(fb_data->value, "debug"))
+  {
+    debug = 1;
+    kdebug(("io_rcc_mmap(io_rcc_write_procmem): debugging enabled\n"));
+  }
+
+  if (!strcmp(fb_data->value, "nodebug"))
+  {
+    kdebug(("io_rcc_mmap(io_rcc_write_procmem): debugging disabled\n"));
+    debug = 0;
+  }
+
+  return len;
+}
+
+
+/****************************************************************************************************/
+static int io_rcc_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data)
+/****************************************************************************************************/
+{
+  int loop, nchars = 0;
+  static int len = 0;
+
+  kdebug(("io_rcc_read_procmem: Called with buf    = 0x%08lx\n", (u_long)buf));
+  kdebug(("io_rcc_read_procmem: Called with *start = 0x%08lx\n", (u_long)*start));
+  kdebug(("io_rcc_read_procmem: Called with offset = %ld\n", (u_long)offset));
+  kdebug(("io_rcc_read_procmem: Called with count  = %d\n", count));
+
+  if (offset == 0)
+  {
+    kdebug(("io_rcc_read_procmem: Creating text....\n"));
+    len = 0;
+    len += sprintf(proc_read_text + len, "IO RCC driver for release %s (based on CVS tag %s)\n", RELEASE_NAME, CVSTAG);
+    len += sprintf(proc_read_text + len, "Dumping table of linked devices\n");
+    len += sprintf(proc_read_text + len, "Handle | Vendor ID | Device ID | Occurrence | Process ID\n");
+    for(loop = 0; loop < IO_MAX_PCI; loop++)
+    {
+      if (pcidev[loop].pid != 0)
+      {
+      len += sprintf(proc_read_text + len, "    %2d |", loop);
+      len += sprintf(proc_read_text + len, "0x%08x |", pcidev[loop].vid);
+      len += sprintf(proc_read_text + len, "0x%08x |", pcidev[loop].did);
+      len += sprintf(proc_read_text + len, " 0x%08x |", pcidev[loop].occ);
+      len += sprintf(proc_read_text + len, " 0x%08x\n", pcidev[loop].pid);
+      }
+    }
+
+    len += sprintf(proc_read_text + len, " \n");
+    len += sprintf(proc_read_text + len, "The command 'echo <action> > /proc/io_rcc', executed as root,\n");
+    len += sprintf(proc_read_text + len, "allows you to interact with the driver. Possible actions are:\n");
+    len += sprintf(proc_read_text + len, "debug   -> enable debugging\n");
+    len += sprintf(proc_read_text + len, "nodebug -> disable debugging\n");
+
+    len += sprintf(proc_read_text + len, "Current values of the parameter(s)\n");
+    len += sprintf(proc_read_text + len, "debug = %d\n", debug);
+  }
+  kdebug(("io_rcc_read_procmem: number of characters in text buffer = %d\n", len));
+
+  if (count < (len - offset))
+    nchars = count;
+  else
+    nchars = len - offset;
+  kdebug(("io_rcc_read_procmem: min nchars         = %d\n", nchars));
+  
+  if (nchars > 0)
+  {
+    for (loop = 0; loop < nchars; loop++)
+      buf[loop + (offset & (PAGE_SIZE - 1))] = proc_read_text[offset + loop];
+    *start = buf + (offset & (PAGE_SIZE - 1));
+  }
+  else
+  {
+    nchars = 0;
+    *eof = 1;
+  }
+ 
+  kdebug(("io_rcc_read_procmem: returning *start   = 0x%08lx\n", (u_long)*start));
+  kdebug(("io_rcc_read_procmem: returning nchars   = %d\n", nchars));
+  return(nchars);
+}
+
+
+
+
+
+
+
+
diff --git a/cmt/quest.c b/cmt/quest.c
new file mode 100644
index 0000000000000000000000000000000000000000..b4e5fdd6ee4ecb7977950a1968b1c60731b84006
--- /dev/null
+++ b/cmt/quest.c
@@ -0,0 +1,986 @@
+/************************************************************************/
+/*									*/
+/* File: quest_driver.c							*/
+/*									*/
+/* driver for the QUEST S-Link interface				*/
+/*									*/
+/* 9. Dec. 03  MAJO  created						*/
+/*									*/
+/************ C 2005 - The software with that certain something *********/
+
+/************************************************************************/
+/*NOTES:								*/
+/*- This driver should work on kernels from 2.6.9 onwards		*/
+/************************************************************************/
+
+#include <linux/init.h>           //MJ: for 2.6, p30
+#include <linux/module.h>         //p30
+#include <linux/moduleparam.h>    //MJ: for 2.6, p30
+#include <linux/kernel.h>         //e.g. for printk
+#include <linux/stat.h>           //MJ: for 2.6, e.g. for module_param
+#include <linux/pci.h>            //e.g. for   pci_find_device
+#include <linux/errno.h>          //e.g. for EFAULT
+#include <linux/fs.h>             //e.g. for register_chrdev
+#include <linux/proc_fs.h>        //e.g. for create_proc_entry
+#include <linux/mm.h>             //e.g. for vm_operations_struct
+#include <linux/slab.h>           //e.g. for kmalloc
+#include <linux/cdev.h>           //e.g. for cdev_alloc
+#include <linux/delay.h>
+#include <linux/interrupt.h>      //e.g. for request_irq
+#include <asm/uaccess.h>          //e.g. for copy_from_user
+#include <asm/io.h>               //e.g. for inl
+#include <asm/system.h>           //e.g. for wmb
+#include "ROSsolar/quest_driver.h"
+
+/*********/
+/*Globals*/
+/*********/
+static int debug = 0;
+static u_int maxcards = 0;
+static u_int opid, served[MAXQUESTROLS], irqlist[MAXIRQ];
+static u_int *infifo, *infifon;
+static u_int reqadd[MAXQUESTCARDS], reqbuf[MAXQUESTCARDS], infifor[MAXQUESTROLS], req_dma_active[MAXQUESTCARDS];
+static u_int order, swfifobasephys, infifodatasize, fifoptrsize, tsize = 0;
+static u_long swfifobase;
+static struct proc_dir_entry *quest_file;
+struct quest_proc_data_t quest_proc_data;
+static char *proc_read_text;
+static dev_t major_minor;
+static struct cdev *quest_cdev;
+static T_quest_card questcard[MAXQUESTCARDS];
+
+/************/
+/*Prototypes*/
+/************/
+static void quest_cleanup(void);
+static void quest_vmaOpen(struct vm_area_struct *vma);
+static void quest_vmaClose(struct vm_area_struct *vma);
+static void questRemove(struct pci_dev *dev); 
+static int __devinit questProbe(struct pci_dev *dev, const struct pci_device_id *id);
+static int quest_init(void);
+static irqreturn_t quest_irq_handler (int irq, void *dev_id, struct pt_regs *regs);
+
+
+/***************************************************************/
+/* Use /sbin/modinfo <module name> to extract this information */
+/***************************************************************/
+MODULE_DESCRIPTION("S-Link QUEST");
+MODULE_AUTHOR("Markus Joos, CERN/PH");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("Private: Contact markus.joos@cern.ch");
+module_param (debug, int, S_IRUGO | S_IWUSR);  //MJ: for 2.6 p37
+MODULE_PARM_DESC(debug, "1 = enable debugging   0 = disable debugging"); 
+
+
+//MJ: Not actually required. Just for kdebug
+struct vm_operations_struct quest_vm_ops =
+{       
+  .close = quest_vmaClose,      
+  .open  = quest_vmaOpen,      //MJ: Note the comma at the end of the list!
+};
+
+static struct file_operations fops = 
+{
+  .owner   = THIS_MODULE,
+  .ioctl   = quest_ioctl,
+  .open    = quest_open,    
+  .mmap    = quest_mmap,
+  .release = quest_release,
+};
+
+// PCI driver structures. See p311
+static struct pci_device_id questIds[] = 
+{
+  {PCI_DEVICE(PCI_VENDOR_ID_CERN, QUEST_PCI_DEVICE_ID)},      
+  {0,},
+};
+
+//PCI operations
+static struct pci_driver questPciDriver = 
+{
+  .name     = "quest",
+  .id_table = questIds,
+  .probe    = questProbe,
+  .remove   = questRemove,
+};
+
+/*****************************/
+/* Standard driver functions */
+/*****************************/
+
+/*************************/
+static int quest_init(void)
+/*************************/
+{
+  int tfsize, result;
+  u_int channel, card, loop;
+  struct page *page_ptr;
+     
+  if (alloc_chrdev_region(&major_minor, 0, 1, "quest")) //MJ: for 2.6 p45
+  {
+    kdebug(("quest(quest_init): failed to obtain device numbers\n"));
+    result = -QUEST_EIO;
+    goto fail1;
+  }
+  
+  proc_read_text = (char *)kmalloc(MAX_PROC_TEXT_SIZE, GFP_KERNEL);
+  if (proc_read_text == NULL)
+  {
+    kdebug(("quest(quest_init): error from kmalloc\n"));
+    result = -EFAULT;
+    goto fail2;
+  }
+
+  quest_file = create_proc_entry("quest", 0644, NULL);
+  if (quest_file == NULL)
+  {
+    kdebug(("quest(quest_init): error from call to create_proc_entry\n"));
+    result = -EFAULT;
+    goto fail3;
+  }  
+  
+  strcpy(quest_proc_data.name, "quest");
+  strcpy(quest_proc_data.value, "quest");
+  quest_file->data = &quest_proc_data;
+  quest_file->read_proc = quest_read_procmem;
+  quest_file->write_proc = quest_write_procmem;
+  quest_file->owner = THIS_MODULE;  
+
+  //Clear the list of used interupts 
+  for(loop = 0; loop < MAXIRQ; loop++)
+    irqlist[loop] = 0;
+      
+  kdebug(("quest(quest_init): registering PCIDriver\n"));
+  result = pci_register_driver(&questPciDriver);  //MJ: See P312
+  if (result == 0)
+  {
+    kdebug(("quest(quest_init): ERROR! no QUEST cards found!\n"));
+    result = -EIO;
+    goto fail4;
+  }
+  else 
+  {
+    kdebug(("quest(quest_init): Found %d QUEST cards!\n", result));
+  }
+
+  // Allocate contiguous memory for the FIFOs and store the base addresses in a global structure
+  infifodatasize = MAXQUESTROLS * MAXQUESTINFIFO * sizeof(u_int) * 2;  //infifo
+  fifoptrsize = MAXQUESTROLS * sizeof(u_int);                     //infifo counter
+  kdebug(("quest(quest_init): 0x%08x bytes needed for the IN FIFOs\n", infifodatasize));
+  kdebug(("quest(quest_init): 0x%08x bytes needed for the FIFO counters\n", fifoptrsize));
+  
+  tsize = infifodatasize + fifoptrsize;
+  kdebug(("quest(quest_init): 0x%08x bytes needed for all FIFOs\n", tsize));
+
+  tfsize = (tsize + 4095) & 0xfffff000;  // round up to next 4K boundary
+  tfsize = tfsize >> 12;                 // compute number of pages
+  kdebug(("quest(quest_init): %d pages needed for the S/W FIFOs\n", tfsize));
+ 
+  order = 0;
+  while(tfsize)
+  {
+    order++;
+    tfsize = tfsize >> 1;                 // compute order
+  } 
+  kdebug(("quest(quest_init): order = %d\n", order));
+  
+  swfifobase = __get_free_pages(GFP_ATOMIC, order);
+  if (!swfifobase)
+  {
+    kdebug(("rquest(quest_init): error from __get_free_pages\n"));
+    result = -QUEST_EFAULT;
+    goto fail5;
+  }
+  kdebug(("quest(quest_init): swfifobase = 0x%016lx\n", swfifobase));
+   
+  // Reserve all pages to make them remapable
+  page_ptr = virt_to_page(swfifobase);
+  for (loop = (1 << order); loop > 0; loop--, page_ptr++)
+    set_bit(PG_reserved, &page_ptr->flags);
+
+  swfifobasephys = virt_to_bus((void *) swfifobase);
+  kdebug(("quest(quest_init): swfifobasephys = 0x%08x\n", swfifobasephys));
+
+  // Assign base addresses to the FIFO arrays
+  infifo  = (u_int *)swfifobase;
+  infifon = (u_int *)(swfifobase + infifodatasize);
+  kdebug(("quest(quest_init): infifo   is at 0x%016lx\n", (u_long)infifo));
+  kdebug(("quest(quest_init): infifon  is at 0x%016lx\n", (u_long)infifon));
+
+  //Initialize the FIFOs
+  for(channel = 0; channel < MAXQUESTROLS; channel++)
+  {
+    infifor[channel] = 0;
+    infifon[channel] = 0;
+  }
+
+  //Allocate the buffers for the REQ blocks
+  //The max size of such a block is: 4 channels * (1 size word + 126 data words) = 504 words = 2032 bytes
+  //It is therefore safe if we get one page (4k) per buffer
+  for(card = 0; card < maxcards; card++)
+  {
+    req_dma_active[card] = 0;
+    reqbuf[card] = __get_free_page(GFP_ATOMIC);
+    if (!reqbuf[card])
+    {
+      kdebug(("quest(quest_init): error from __get_free_pages for reqbuf\n"));
+      result = -QUEST_EFAULT;
+      goto fail6;
+    }
+    reqadd[card] = virt_to_bus((void *) reqbuf[card]);
+    kdebug(("quest(quest_init): reqbuf[%d] = 0x%08x (PCI = 0x%08x)\n", card, reqbuf[card], reqadd[card]));
+  }
+  
+  opid = 0; 
+  quest_cdev = (struct cdev *)cdev_alloc();       //MJ: for 2.6 p55
+  quest_cdev->ops = &fops;
+  result = cdev_add(quest_cdev, major_minor, 1);  //MJ: for 2.6 p56
+  if (result)
+  {
+    kdebug(("quest(quest_init): error from call to cdev_add.\n"));
+    goto fail7;
+  }
+
+  kdebug(("quest(quest_init): driver loaded; major device number = %d\n", MAJOR(major_minor)));
+  return(0);
+
+  fail7:
+    for(card = 0; card < maxcards; card++)
+      free_page((u_long)reqbuf[card]);
+   
+  fail6:
+    page_ptr = virt_to_page(swfifobase);
+    for (loop = (1 << order); loop > 0; loop--, page_ptr++)
+      clear_bit(PG_reserved, &page_ptr->flags);
+    free_pages(swfifobase, order);
+
+  fail5:
+    pci_unregister_driver(&questPciDriver);
+
+  fail4:
+    remove_proc_entry("io_rcc", NULL);
+    
+  fail3:
+    kfree(proc_read_text);
+
+  fail2:
+    unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45
+
+  fail1:
+    return(result);  
+}
+
+
+/*****************************/
+static void quest_cleanup(void)
+/*****************************/
+{
+  u_int card, loop;
+  struct page *page_ptr;
+      
+  cdev_del(quest_cdev);                     //MJ: for 2.6 p56
+  pci_unregister_driver(&questPciDriver);
+
+  // unreserve all pages used for the S/W FIFOs
+  page_ptr = virt_to_page(swfifobase);
+  for (loop = (1 << order); loop > 0; loop--, page_ptr++)
+    clear_bit(PG_reserved, &page_ptr->flags);
+
+  // free the area 
+  free_pages(swfifobase, order);
+  
+  // Return the buffers for the REQ blocks
+  for(card = 0; card < maxcards; card++)
+    free_page(reqbuf[card]);
+  
+  remove_proc_entry("quest", NULL);
+  kfree(proc_read_text);
+  unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45
+
+  kdebug(("quest(quest_cleanup): driver removed\n"));
+}
+
+
+module_init(quest_init);    //MJ: for 2.6 p16
+module_exit(quest_cleanup); //MJ: for 2.6 p16
+
+
+/**********************************************************/
+static int quest_open(struct inode *ino, struct file *filep)
+/**********************************************************/
+{
+  kdebug(("quest(quest_open): nothing to be done\n"));
+  return(0);
+}
+
+
+/*************************************************************/
+static int quest_release(struct inode *ino, struct file *filep)
+/*************************************************************/
+{
+  kdebug(("quest(quest_release): (almost) nothing to be done\n"));
+  opid = 0;
+  return(0);
+}
+
+
+/***********************************************************************************/
+static int quest_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg)
+/***********************************************************************************/
+{
+  switch (cmd)
+  {    
+    case OPEN:
+    {
+      QUEST_open_t params;
+      
+      kdebug(("quest(ioctl,OPEN): called from process %d\n", current->pid))
+      if (!opid)
+        opid = current->pid;
+      else
+      {
+        kdebug(("quest(ioctl,OPEN): The QUEST cards are already used by process %d\n", opid))
+  	return(-QUEST_USED);
+      }
+      
+      params.size = tsize;
+      params.insize = infifodatasize;
+      params.physbase = swfifobasephys;
+      kdebug(("quest(ioctl,OPEN): params.size     = 0x%08x\n", params.size));
+      kdebug(("quest(ioctl,OPEN): params.physbase = 0x%08x\n", params.physbase));
+      kdebug(("quest(ioctl,OPEN): params.insize   = 0x%08x\n", params.insize));
+  
+      if (copy_to_user((void *)arg, &params, sizeof(QUEST_open_t)))
+      {
+	kdebug(("quest(ioctl, OPEN): error from copy_to_user\n"));
+	return(-QUEST_EFAULT);
+      }      
+      return(0);
+      break;
+    }
+    
+    case CLOSE:
+    {
+      kdebug(("quest(ioctl,CLOSE): called from process %d\n", current->pid))
+      opid = 0;
+  
+      return(0);
+      break;
+    }
+    
+    case CONFIG:
+    { 
+      QUEST_config_t params;
+      u_int channel, card, data;
+      
+      if (copy_from_user(&params, (void *)arg, sizeof(QUEST_config_t)))
+      {
+	kdebug(("quest(ioctl,CONFIG): error from copy_from_user\n"));
+	return(-QUEST_EFAULT);
+      }
+      kdebug(("quest(ioctl,CONFIG): \n"));        
+      kdebug(("quest(ioctl,CONFIG): params.bswap  = %d\n", params.bswap));
+      kdebug(("quest(ioctl,CONFIG): params.wswap  = %d\n", params.wswap));
+      kdebug(("quest(ioctl,CONFIG): params.scw    = 0x%08x\n", params.scw));
+      kdebug(("quest(ioctl,CONFIG): params.ecw    = 0x%08x\n", params.ecw));
+
+      for(card = 0; card < maxcards; card++)
+      {
+        data = 0;  
+        questcard[card].regs->opctl = (params.wswap << 2) + (params.bswap << 1);
+        questcard[card].regs->sctl  = params.scw & 0xfffffffc;
+        questcard[card].regs->ectl  = params.ecw & 0xfffffffc;
+        
+        //The LFF and LDOWN interrupts are not yet supported
+	questcard[card].regs->intctl = 0xFBEFBE05; // threshold fixed at 62 for all channels, clear any outstanding interrupts
+	questcard[card].regs->intmask = 0x1;
+      }
+
+      for(channel = 0; channel < MAXQUESTROLS; channel++)
+        served[channel] = 0;
+	
+      break;
+    }
+    
+    case RESET:
+    {
+      u_int rol, data, card, channel;
+      
+      if (copy_from_user(&card, (void *)arg, sizeof(int)))
+      {
+	kdebug(("quest(ioctl,RESET): error from copy_from_user\n"));
+	return(-QUEST_EFAULT);
+      }
+     
+      kdebug(("quest(ioctl,RESET): Resetting card %d\n", card));
+      data = questcard[card].regs->opctl;
+      data |= 0x1;
+      questcard[card].regs->opctl = data;
+      udelay(2);    // Delay for at least one us
+      data &= 0xfffffffe;
+      questcard[card].regs->opctl = data;
+      req_dma_active[card] = 0;
+
+      for (channel = 0; channel < MAXQUESTCHANNELS; channel++) //reset the FIFOs
+      {
+        rol = (card << 2) + channel;     
+        infifor[rol] = 0;
+        infifon[rol] = 0;
+      }
+      
+      return(0);
+      break;
+    }
+    
+    case LINKRESET:
+    {   
+      u_int data1, data2, waited = 0, card, channel;
+ 
+      if (copy_from_user(&channel, (void *)arg, sizeof(int)))
+      {
+	kdebug(("quest(ioctl,LINKRESET): error from copy_from_user\n"));
+	return(-QUEST_EFAULT);
+      }
+
+      card = channel >> 2;      
+      channel &= 0x3;
+      kdebug(("quest(ioctl,LINKRESET): Resetting channel %d of card %d\n", channel, card));
+
+      data1 = 1 << (16 + channel * 4);
+      data2 = 1 << (4 + channel * 7);
+      questcard[card].regs->opctl |= data1;        // Set the URESET bit
+      udelay(2000);                       // Delay for at least two ms
+     
+      while(questcard[card].regs->opstat & data2)  // Now wait for LDOWN to come up again
+      {
+        waited++;
+        if (waited > 10000)
+        {
+          kdebug(("quest(ioctl,LINKRESET): card %d does not come up again\n", card));
+          questcard[card].regs->opctl &= ~data1;   // Reset the URESET bit
+	  return(-QUEST_STUCK);
+        }          
+      }
+
+      questcard[card].regs->opctl &= ~data1; // Reset the URESET bit
+      served[4* card + channel] = 0;
+      return(0);
+      break;
+    }
+    
+    case LINKSTATUS:
+    {              
+      u_long flags;
+      u_int data, card;
+      QUEST_status_t params;
+      
+      if (copy_from_user(&params, (void *)arg, sizeof(QUEST_status_t)))
+      {
+	kdebug(("quest(ioctl, LINKSTATUS): error from copy_from_user\n"));
+	return(-QUEST_EFAULT);
+      }
+       
+      card = params.channel >> 2;
+      data = 1 << (4 + (params.channel & 0x3) * 7);
+      if (questcard[card].regs->opstat & data)
+        params.ldown = 1;
+      else
+        params.ldown = 0;
+      
+      //We have to make sure that this code does not get interrupted. 
+      local_irq_save(flags);  //MJ: for 2.6 see p274 & p275
+      params.infifo = infifon[params.channel];
+      params.reqfifo = REQFIFODEPTH - ((questcard[card].regs->fstat >> (8 * (params.channel & 0x3))) & 0xff); 
+      kdebug(("quest(ioctl, LINKSTATUS): params.ldown = %d\n", params.ldown));
+      kdebug(("quest(ioctl, LINKSTATUS): params.infifo = %d\n", params.infifo));
+      kdebug(("quest(ioctl, LINKSTATUS): params.reqfifo = %d\n", params.reqfifo));
+      local_irq_restore(flags);  //MJ: for 2.6 see p274 & p275
+
+      if (copy_to_user((void *)arg, &params, sizeof(QUEST_status_t)))
+      {
+	kdebug(("quest(ioctl, LINKSTATUS): error from copy_to_user\n"));
+	return(-QUEST_EFAULT);
+      } 
+      
+      return(0);
+      break;
+    }
+
+    case INFO:
+    {
+      QUEST_info_t info;
+      u_int data, card, channel;
+            
+      info.ncards = maxcards;  
+      kdebug(("quest(ioctl, INFO): maxcards = %d\n", maxcards));
+      
+      for(card = 0; card < MAXQUESTCARDS; card++)
+      {
+        if (card < maxcards)
+	  info.cards[card] = 1;
+	else  
+	  info.cards[card] = 0;
+        kdebug(("quest(ioctl, INFO): info.cards[%d] = %d\n", card, info.cards[card]));
+      }
+      
+      for(channel = 0; channel < MAXQUESTCHANNELS; channel++)
+      {
+        card = channel >> 2;
+	if (info.cards[card] == 1)
+	{
+	  data = questcard[card].regs->opstat & (1 << (10 + 7 * (channel & 0x3))); 
+	  if (data)
+	    info.channel[channel] = 0;
+	  else
+	    info.channel[channel] = 1;
+	}
+	else
+	  info.channel[channel] = 0;
+        kdebug(("quest(ioctl, INFO): info.channel[%d] = %d\n", channel, info.channel[channel]));
+      } 
+
+      if (copy_to_user((void *)arg, &info, sizeof(QUEST_info_t)))
+      {
+	kdebug(("quest(ioctl, INFO): error from copy_to_user\n"));
+	return(-QUEST_EFAULT);
+      }      
+      return(0);
+      break;
+    }
+    
+    case FIFOIN:
+    { 
+      u_int channel;
+
+      if (copy_from_user(&channel, (void *)arg, sizeof(int)) !=0)
+      {
+	kdebug(("quest(ioctl,FIFOIN): error from copy_from_user\n"));
+	return(-QUEST_EFAULT);
+      }
+      kdebug(("quest(ioctl,FIFOIN): channel = %d\n", channel));
+
+      //Try to post a new request block on the card of this channel
+      refill_req_fifo_dma(channel >> 2);
+
+      return(0);
+      break;
+    }
+  }   
+  return(0);
+}
+
+
+/***************************************************/
+static void quest_vmaOpen(struct vm_area_struct *vma)
+/***************************************************/
+{
+  kdebug(("quest(quest_vmaOpen): Called\n"));
+}
+
+
+/****************************************************/
+static void quest_vmaClose(struct vm_area_struct *vma)
+/****************************************************/
+{  
+  kdebug(("quest(quest_vmaClose): Virtual address  = 0x%016lx\n",(u_long)vma->vm_start));
+  kdebug(("quest(quest_vmaClose): mmap released\n"));
+}
+
+
+/******************************************************************/
+static int quest_mmap(struct file *file, struct vm_area_struct *vma)
+/******************************************************************/
+{
+  u_long offset, size;
+
+  kdebug(("quest(quest_mmap): function called\n"));
+
+  vma->vm_flags |= VM_RESERVED;
+  vma->vm_flags |= VM_LOCKED;
+ 
+  kdebug(("quest(quest_mmap): vma->vm_end    = 0x%016lx\n", (u_long)vma->vm_end));
+  kdebug(("quest(quest_mmap): vma->vm_start  = 0x%016lx\n", (u_long)vma->vm_start));
+  kdebug(("quest(quest_mmap): vma->vm_offset = 0x%016lx\n", (u_long)vma->vm_pgoff << PAGE_SHIFT));
+  kdebug(("quest(quest_mmap): vma->vm_flags  = 0x%08x\n", (u_int)vma->vm_flags));
+
+  size = vma->vm_end - vma->vm_start;
+  offset = vma->vm_pgoff << PAGE_SHIFT;  
+
+  kdebug(("quest(quest_mmap): offset = 0x%08x\n",(u_int)offset));
+  kdebug(("quest(quest_mmap): size   = 0x%08x\n",(u_int)size));
+
+  if (remap_page_range(vma, vma->vm_start, offset, size, vma->vm_page_prot))
+  {
+    kdebug(("quest(quest_mmap): remap page range failed\n"));
+    return -ENXIO;
+  }
+  
+  vma->vm_ops = &quest_vm_ops;  
+  kdebug(("quest(quest_mmap): function done\n"));
+  return(0);
+}
+
+
+/*********************************************************************************************/
+static int quest_write_procmem(struct file *file, const char *buffer, u_long count, void *data)
+/*********************************************************************************************/
+{
+  int len;
+  struct quest_proc_data_t *fb_data = (struct quest_proc_data_t *)data;
+
+  kdebug(("quest(quest_write_procmem): quest_write_procmem called\n"));
+
+  if(count > 99)
+    len = 99;
+  else
+    len = count;
+
+  if (copy_from_user(fb_data->value, buffer, len))
+  {
+    kdebug(("quest(quest_write_procmem): error from copy_from_user\n"));
+    return(-QUEST_EFAULT);
+  }
+
+  kdebug(("quest(quest_write_procmem): len = %d\n", len));
+  fb_data->value[len - 1] = '\0';
+  kdebug(("quest(quest_write_procmem): text passed = %s\n", fb_data->value));
+
+  if (!strcmp(fb_data->value, "debug"))
+  {
+    debug = 1;
+    kdebug(("quest(quest_write_procmem): debugging enabled\n"));
+  }
+
+  if (!strcmp(fb_data->value, "nodebug"))
+  {
+    kdebug(("quest(quest_write_procmem): debugging disabled\n"));
+    debug = 0;
+  }
+  
+  return len;
+}
+
+
+/***************************************************************************************************/
+static int quest_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data)
+/***************************************************************************************************/
+{
+  u_int card, channel, fstat, ocr, osr, imask, estat, value;
+  int loop, nchars = 0;
+  static int len = 0;
+  
+  kdebug(("quest(quest_read_procmem): Called with buf    = 0x%016lx\n", (u_long)buf));
+  kdebug(("quest(quest_read_procmem): Called with *start = 0x%016lx\n", (u_long)*start));
+  kdebug(("quest(quest_read_procmem): Called with offset = %d\n", (u_int)offset));
+  kdebug(("quest(quest_read_procmem): Called with count  = %d\n", count));
+
+  if (offset == 0)
+  {
+    kdebug(("exp(exp_read_procmem): Creating text....\n"));
+    len = 0;
+    len += sprintf(proc_read_text + len, "\n\n\nQUEST driver for release %s (based on CVS tag %s)\n", RELEASE_NAME, CVSTAG);
+
+    len += sprintf(proc_read_text + len, "==================================================\n");
+    len += sprintf(proc_read_text + len, "Card|IRQ line|  REQ IRQ| DONE IRQ|revision|PCI MEM addr.|wswap|bswap|Temperature\n");
+    len += sprintf(proc_read_text + len, "----|--------|---------|---------|--------|-------------|-----|-----|-----------\n");
+    for(card = 0; card < maxcards; card++)
+    {
+      ocr = questcard[card].regs->opctl;
+      osr = questcard[card].regs->opstat;
+      imask = questcard[card].regs->intmask;
+      estat = questcard[card].regs->estat;
+      len += sprintf(proc_read_text + len, "   %d|", card);
+      len += sprintf(proc_read_text + len, "      %2d|", questcard[card].irq_line);
+      len += sprintf(proc_read_text + len, " %s|", (imask & 0x4)?"enabled":"disabled"); 
+      len += sprintf(proc_read_text + len, " %s|", (imask & 0x1)?"enabled":"disabled"); 
+      len += sprintf(proc_read_text + len, "    0x%02x|", questcard[card].pci_revision); 
+      len += sprintf(proc_read_text + len, "   0x%08x|", questcard[card].pci_memaddr);
+      len += sprintf(proc_read_text + len, "  %s|", (ocr & 0x4)?"yes":" no");
+      len += sprintf(proc_read_text + len, "  %s", (ocr & 0x2)?"yes":" no");
+      len += sprintf(proc_read_text + len, "%9d C", estat & 0xff);
+    }
+
+    for(card = 0; card < maxcards; card++)
+    {
+      len += sprintf(proc_read_text + len, "\nCard %d:\n", card);
+      len += sprintf(proc_read_text + len, "=======\n");
+      len += sprintf(proc_read_text + len, "Channel|present|LDOWN|#REQ slots avail.|#fragments sent\n");
+      len += sprintf(proc_read_text + len, "-------|-------|-----|-----------------|---------------\n");
+
+      osr =  questcard[card].regs->opstat;
+      fstat = questcard[card].regs->fstat;
+      for(channel  = 0; channel < MAXQUESTCHANNELS; channel++)
+      {
+	len += sprintf(proc_read_text + len, "      %1d|", channel);
+	value = osr & (1 << (10 + channel * 7));
+	len += sprintf(proc_read_text + len, "    %s|", value?"Yes":" No");
+	value = osr & (1 << (4 + channel * 7));
+	len += sprintf(proc_read_text + len, "  %s|", value?"Yes":" No");  
+	value = (fstat >> (8 * channel)) & 0xff;
+	len += sprintf(proc_read_text + len, "%17d|", value);
+	len += sprintf(proc_read_text + len, "%15d\n", served[(card << 2) + channel]);     
+      }
+    }
+
+    len += sprintf(proc_read_text + len, " \n");
+    len += sprintf(proc_read_text + len, "The command 'echo <action> > /proc/quest', executed as root,\n");
+    len += sprintf(proc_read_text + len, "allows you to interact with the driver. Possible actions are:\n");
+    len += sprintf(proc_read_text + len, "debug   -> enable debugging\n");
+    len += sprintf(proc_read_text + len, "nodebug -> disable debugging\n");
+  }
+  kdebug(("quest(quest_read_procmem): number of characters in text buffer = %d\n", len));
+
+  if (count < (len - offset))
+    nchars = count;
+  else
+    nchars = len - offset;
+  kdebug(("quest(quest_read_procmem): min nchars         = %d\n", nchars));
+  
+  if (nchars > 0)
+  {
+    for (loop = 0; loop < nchars; loop++)
+      buf[loop + (offset & (PAGE_SIZE - 1))] = proc_read_text[offset + loop];
+    *start = buf + (offset & (PAGE_SIZE - 1));
+  }
+  else
+  {
+    nchars = 0;
+    *eof = 1;
+  }
+ 
+  kdebug(("quest(quest_read_procmem): returning *start   = 0x%016lx\n", (u_long)*start));
+  kdebug(("quest(quest_read_procmem): returning nchars   = %d\n", nchars));
+  return(nchars);
+}
+
+
+/********************************************************************************/
+static irqreturn_t quest_irq_handler (int irq, void *dev_id, struct pt_regs *regs)
+/********************************************************************************/
+{
+  u_int card;
+
+  // Note: the QUEST card(s) may have to share an interrupt line with other PCI devices.
+  // It is therefore important to exit quickly if we are not concerned with an interrupt.
+  // This is done by looking at REQBLK_DONE bit in the OPSTAT register. 
+  
+  for(card = 0; card < maxcards; card++)
+  {
+    if ((questcard[card].irq_line == (u_int)irq) && questcard[card].regs->opstat & 0x1)
+    {
+      kdebug(("quest(quest_irq_handler): Interrupt received\n"));
+      questcard[card].regs->intctl |= 0x5;   //Acknowledge (and clear) the inetrrupt
+      req_dma_active[card] = 0;
+      refill_req_fifo_dma(card); 
+      return(IRQ_HANDLED);          //MJ: for 2.6 see p273
+    }
+    else
+    {
+      kdebug(("quest(quest_irq_handler): This was not a quest interrupt\n"));
+      return(IRQ_NONE);     
+    }
+  }  
+  return(IRQ_HANDLED);          //MJ: for 2.6 see p273
+}
+
+
+/*****************************/
+/* PCI driver functions      */
+/*****************************/
+
+/**********************************************************************************/
+static int __devinit questProbe(struct pci_dev *dev, const struct pci_device_id *id)   //MJ: for _devinit see p31
+/**********************************************************************************/
+{
+  int i, card;
+  u_long flags;
+  u_int size;
+
+  kdebug(("quest(questProbe): called for device: 0x%04x / 0x%04x\n", dev->vendor, dev->device));
+  kdebug(("quest(questProbe): Bus: 0x%04x / Devfn 0x%04x\n", dev->bus->number, dev->devfn));
+
+  /* Find a new "slot" for the card */
+  card = -1;
+  for (i = 0; i < MAXQUESTCARDS; i++) 
+  {
+    if (questcard[i].pciDevice == NULL) 
+    {
+      card = i;
+      questcard[card].pciDevice = dev;
+      break;
+    }
+  }
+
+  if (card == -1) 
+  {
+    kdebug(("quest(questProbe): Could not find space in the questcard array for this card."));
+    return(-QUEST_TOOMANYCARDS);
+  }
+  
+  flags = pci_resource_flags(dev, BAR0);  //MJ: See p317
+  if ((flags & IORESOURCE_MEM) != 0) 
+  {
+    questcard[card].pci_memaddr = pci_resource_start(dev, BAR0) & PCI_BASE_ADDRESS_MEM_MASK;        //MJ: See p317
+    size = pci_resource_end(dev, BAR0) - pci_resource_start(dev, BAR0);                             //MJ: See p317
+    kdebug(("quest(questProbe): Mapping %d bytes at PCI address 0x%08x\n", size, questcard[card].pci_memaddr));
+    
+    questcard[card].regs = (T_quest_regs *)ioremap(questcard[card].pci_memaddr, size);
+    if (questcard[card].regs == NULL)
+    {
+      kdebug(("quest(questProbe): error from ioremap for questcard[%d].regs\n", card));
+      questcard[card].pciDevice = NULL;
+      return(-QUEST_IOREMAP);
+    }
+    kdebug(("quest(questProbe): questcard[%d].regs is at 0x%016lx\n", card, (u_long)questcard[card].regs));
+  }
+  else 
+  {
+    kdebug(("quest(questProbe): Bar 0 is not a memory resource"));
+    return(-QUEST_EIO);
+  }  
+
+  // get revision directly from the QUEST
+  pci_read_config_byte(questcard[card].pciDevice, PCI_REVISION_ID, &questcard[card].pci_revision);
+  kdebug(("quest(questProbe): revision = %x \n", questcard[card].pci_revision));
+  if (questcard[card].pci_revision < MINREV)
+  {
+    kdebug(("quest(questProbe): Illegal quest Revision\n"));
+    return(-QUEST_ILLREV);
+  }  
+  
+  pci_read_config_byte(questcard[card].pciDevice, PCI_INTERRUPT_LINE, &questcard[card].irq_line);
+  kdebug(("quest(questProbe): interrupt line = %d \n", questcard[card].irq_line));
+
+  //Several quest cards may use the same interrupt but we want to install the driver only once
+  if (irqlist[questcard[card].irq_line] == 0) 
+  {
+    if(request_irq(questcard[card].irq_line, quest_irq_handler, SA_SHIRQ, "quest", dev))	
+    {
+      kdebug(("quest(questProbe): request_irq failed on IRQ = %d\n", questcard[card].irq_line));
+      return(-QUEST_REQIRQ);
+    }
+    irqlist[questcard[card].irq_line]++;
+    kdebug(("quest(questProbe): Interrupt %d registered\n", questcard[card].irq_line)); 
+  }  
+ 
+  maxcards++;
+  return(0);
+}
+
+
+/******************************************/
+static void questRemove(struct pci_dev *dev) 
+/******************************************/
+{
+  int i, card;
+
+  /* Find the "slot" of the card */
+  card = -1;
+  for (i = 0; i < MAXQUESTCARDS; i++) 
+  {
+    if (questcard[i].pciDevice == dev) 
+    {
+      card = i;
+      questcard[i].pciDevice = 0;
+      break;
+    }  
+  }
+  
+  if (card == -1) 
+  {
+    kdebug(("quest(questRemove): Could not find device to remove."));
+    return;
+  }
+
+  questcard[card].regs->intctl |= 0x5; // clear any outstanding interrupts
+  questcard[card].regs->intmask = 0;   // mask the interrupts
+ 
+  if (--irqlist[questcard[card].irq_line] == 0)
+  {
+    kdebug(("quest(questRemove): removing handler for interrupt %d", questcard[card].irq_line));
+    free_irq(questcard[card].irq_line, dev);	
+  }
+  
+  iounmap((void *)questcard[card].regs);
+  kdebug(("quest(questRemove): Card %d removed", card));
+  maxcards--;
+}
+
+
+
+
+/*********************/
+/* Service functions */
+/*********************/
+
+/*************************************************************/
+static int in_fifo_pop(int channel, u_int *data1, u_int *data2)
+/*************************************************************/
+{
+  int index;
+  
+  if (infifon[channel] == 0)
+  {
+    kdebug(("quest(in_fifo_pop): The IN FIFO is empty\n"));
+    return(-1);
+  }
+  
+  index = QUEST_IN_INDEX(channel, infifor[channel]);
+  *data1 = infifo[index];
+  *data2 = infifo[index + 1];
+  kdebug(("quest(in_fifo_pop): index=%d data1=0x%08x  data2=0x%08x\n", index, *data1, *data2));
+
+  infifor[channel]++;
+
+  if (infifor[channel] > (MAXQUESTINFIFO - 1))
+    infifor[channel] = 0;
+
+  infifon[channel]--;
+
+  return(0);
+}
+
+
+/***************************************/
+static void refill_req_fifo_dma(int card)
+/***************************************/
+{
+  int channel;
+  int reqlen = 2; // 4 32-bit words for the entry count -> 2 64-bit words
+  int rol = 4 * card;
+  u_int *reqptr = (u_int *) reqbuf[card];
+  u_int fstat = questcard[card].regs->fstat;
+  
+  if (req_dma_active[card]) return;
+  for (channel = 0; channel < MAXQUESTCHANNELS; channel++)
+  {
+    int loop;
+    int numfree = fstat & 0xff;
+    int numreq = infifon[rol];
+    int numcopy = (numfree < numreq) ? numfree : numreq;
+    
+    *reqptr++ = numcopy << 1; // number of requests x 2
+    
+    for (loop = 0; loop < numcopy; loop++)
+    {
+      u_int addr, size;
+      in_fifo_pop(rol, &addr, &size);
+      served[rol]++;
+      *reqptr++ = addr; // address of the data block
+      *reqptr++ = size; // block size and control bits
+    }
+    reqlen += numcopy;
+    fstat >>= 8;
+    rol++;
+  }
+  
+  // Post the request
+  if (reqlen > 2) // at least one request
+  {
+    req_dma_active[card]++;
+    questcard[card].regs->blklen = reqlen; // reqlen is in 64 bit words
+    questcard[card].regs->reqadd = reqadd[card];
+  }  
+}
+
+
diff --git a/cmt/requirements b/cmt/requirements
new file mode 100644
index 0000000000000000000000000000000000000000..78f07a48e078cf4b18a5624c285db6fd2c72a636
--- /dev/null
+++ b/cmt/requirements
@@ -0,0 +1,19 @@
+package ROSRCDdrivers
+
+author  markus.joos@cern.ch
+manager markus.joos@cern.ch
+
+#==========================================================
+public
+#==========================================================
+use TDAQPolicy
+use cmem_rcc
+use io_rcc
+use vme_rcc
+use ROSsolar
+use ROSfilar
+use ROSRobin
+
+#action driver "cd ../$CMTCONFIG; gmake -f ../driver2.6/Makefile"
+action driver "gmake -f Makefile"
+
diff --git a/cmt/robin.c b/cmt/robin.c
new file mode 100644
index 0000000000000000000000000000000000000000..cb72771d3188b7362af37d5c708569d51791ae17
--- /dev/null
+++ b/cmt/robin.c
@@ -0,0 +1,1182 @@
+/****************************************************************/
+/* file:   robindriver.c					*/
+/* author: Markus Joos, CERN-PH/ESS				*/
+/****** C 2005 - The software with that certain something *******/
+
+
+//  Description:
+//  Driver for the ATLAS ROBIN PCI card
+//  NOTES:
+//  - This driver has been developed with SLC3 
+
+//  Strings like P311 refer to pages (e.g.311) in the third edition of the 
+//  Linux Device Drivers book
+
+#include <linux/init.h>           //MJ: for 2.6, p30
+#include <linux/module.h>
+#include <linux/moduleparam.h>    //MJ: for 2.6, p30
+#include <linux/kernel.h>
+#include <linux/stat.h>           //MJ: for 2.6, e.g. for module_param
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/cdev.h>           //e.g. for cdev_alloc
+#include <linux/slab.h>           //e.g. for kmalloc
+#include <linux/delay.h>          //e.g. for udelay
+#include <linux/interrupt.h>      //e.g. for request_irq
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>           //e.g. for local_irq_save
+#include "ROSRobin/robindriver_common.h"
+#include "ROSRobin/robindriver.h"
+
+#ifndef EXPORT_SYMTAB   //MJ: For 2.6: symbol seems not to be required
+  #define EXPORT_SYMTAB
+#endif
+
+
+/*********/
+/*Globals*/
+/*********/
+static int debug = 0, errorlog = 1, msg_fifo_avail[MAXCARDS];
+static int robin_major = 0; // use dynamic allocation
+static u_int maxcard;
+static T_card robincard[MAXCARDS];
+static struct proc_dir_entry *robin_file;
+struct robin_proc_data_t robin_proc_data;
+static T_blocklet blocklets[MAXCARDS][BLOCK_TYPES][MAX_BLOCKLETS];
+char *proc_read_text; 
+
+/*****************/
+/*Some prototypes*/
+/*****************/
+static int robin_write_procmem(struct file *file, const char *buffer, u_long count, void *data);
+static int robin_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data);
+static int robinProbe(struct pci_dev *dev, const struct pci_device_id *id);
+static void robinRemove(struct pci_dev *dev);
+module_init(robindriver_init);  //MJ: See P39
+module_exit(robindriver_exit);
+
+MODULE_DESCRIPTION("ATLAS ROBIN");
+MODULE_AUTHOR("Markus Joos, CERN/PH");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("Private: Contact markus.joos@cern.ch");
+module_param (debug, int, S_IRUGO | S_IWUSR);  //MJ: for 2.6 p37
+MODULE_PARM_DESC(debug, "1 = enable debugging   0 = disable debugging"); 
+module_param (errorlog, int, S_IRUGO | S_IWUSR);  //MJ: for 2.6 p37
+MODULE_PARM_DESC(errorlog, "1 = enable debugging   0 = disable debugging"); 
+
+static struct file_operations fops = 
+{
+  owner:   THIS_MODULE,
+  mmap:    robin_mmap,
+  ioctl:   robin_ioctl,
+  open:    robin_open,    
+  release: robin_release
+};
+
+
+// memory handler functions
+static struct vm_operations_struct robin_vm_ops = 
+{
+  close:  robin_vclose,   // mmap-close
+};
+
+
+/* PCI driver structures */
+
+/* ID tables */  //MJ: See P311
+static struct pci_device_id robinIds[] = 
+{
+  { PCI_DEVICE(PCI_VENDOR_ID_ROBIN3, PCI_DEVICE_ID_ROBIN3) },      //ROBIN2, correct VID
+  { PCI_DEVICE(PCI_VENDOR_ID_ROBIN3, PCI_DEVICE_ID_ROBIN3_bad) },  //ROBIN2, wrong VID
+  { PCI_DEVICE(PCI_VENDOR_ID_ROBIN2, PCI_DEVICE_ID_ROBIN2) },      //ROBIN prototype, correct VID
+  { 0, },
+};
+
+
+/* The driver struct */  //MJ: See P311
+static struct pci_driver robinPciDriver = 
+{
+  .name     = "robindriver",
+  .id_table = robinIds,
+  .probe    = robinProbe,
+  .remove   = robinRemove,
+};
+
+
+/*******************************/
+static int robindriver_init(void)
+/*******************************/
+{
+  int result, i;
+
+  kerror(("robin driver version 2.1\n")); 
+
+  // Initialise the robin card map. This is used to assign the slot parameter to a ROBIN board when calling ioctl(link)
+  
+  for (i = 0; i < MAXCARDS; i++) 
+  {
+    robincard[i].pciDevice = NULL;
+    robincard[i].pci_memaddr_plx = 0;
+    robincard[i].pci_memaddr_bus = 0;
+    robincard[i].pci_memsize_plx = 0; 
+    robincard[i].pci_memsize_bus = 0;
+    robincard[i].mem_base = 0;
+    robincard[i].mem_size = 0;
+    robincard[i].plx_regs = NULL;
+    robincard[i].fpga_regs = NULL;
+    robincard[i].nrols = 0;
+    robincard[i].bar2 = 0;
+    robincard[i].fpgaConfigured = 0;  //Will be set in INIT_DMA
+  }
+
+  maxcard = 0;
+
+  kdebug(("robin(robindriver_init): registering PCIDriver\n"));
+  result = pci_register_driver(&robinPciDriver);  //MJ: See P312
+  if (result == 0)
+  {
+    kerror(("robin(robindriver_init): Warning! no PCI devices found!\n"));
+    return(-RD_EIO);
+  }
+  else 
+  {
+    kerror(("robin(robindriver_init): Found %d devices!\n", result));
+  }
+
+  result = register_chrdev(robin_major, "robin", &fops); //MJ: This is old fashioned. Update for 2.6. See P57
+  if (result < 1)
+  {
+    kerror(("robin(robindriver_init): registering robin driver failed.\n"));
+    return(-RD_EIO2);
+  }
+  robin_major = result;
+
+  proc_read_text = (char *)kmalloc(PROC_MAX_CHARS, GFP_KERNEL);
+  if (proc_read_text == NULL)
+  {
+    kdebug(("robin(robindriver_init): error from kmalloc\n"));
+    return(-RD_KMALLOC);
+  }
+
+  robin_file = create_proc_entry("robin", 0644, NULL);  //MJ: See P87 (seq_file) for a better 2.6 implementation 
+  if (robin_file == NULL)
+  {
+    kerror(("robin(robindriver_init): error from call to create_proc_entry\n"));
+    return(-RD_PROC);
+  }
+
+  strcpy(robin_proc_data.name, "robin");
+  strcpy(robin_proc_data.value, "robin");
+  robin_file->data = &robin_proc_data;
+  robin_file->read_proc = robin_read_procmem;
+  robin_file->write_proc = robin_write_procmem;
+  robin_file->owner = THIS_MODULE;
+
+  kdebug(("robin(robindriver_init): driver loaded; major device number = %d\n", robin_major));
+  return(0);
+}
+
+
+/********************************/
+static void robindriver_exit(void)
+/********************************/
+{
+  if (unregister_chrdev(robin_major, "robin") != 0) 
+  {
+    kerror(("robin(cleanup_module): cleanup_module failed\n"));
+  }
+  
+  kfree(proc_read_text);
+  remove_proc_entry("robin", NULL);
+  pci_unregister_driver(&robinPciDriver);
+  kdebug(("robin(cleanup_module): driver removed\n"));
+}
+
+
+/*****************************/
+/* PCI driver functions      */
+/*****************************/
+
+/**********************************************************************************/
+static int __devinit robinProbe(struct pci_dev *dev, const struct pci_device_id *id) //MJ: for _devinit see P31
+/**********************************************************************************/
+{
+  int i;
+  u_long flags;
+  u_int baddr, size, card, bar2Available;
+
+  // Check which type of ROBIN is installed
+  kdebug(("robin(robinProbe): called for device: 0x%04x / 0x%04x\n", dev->vendor, dev->device));
+  kdebug(("robin(robinProbe): Bus: 0x%04x / Devfn 0x%04x\n", dev->bus->number, dev->devfn));
+  
+  //MJ: Should we check if the FPGA is loaded and exit if not?
+  
+  /* Find a new "slot" for the card */
+  for (i = 0, card = -1; (i < MAXCARDS) && (card == -1); i++) 
+  {
+    if (robincard[i].pciDevice == NULL) 
+    {
+      card = i;
+      robincard[card].pciDevice = dev;
+    }
+  }
+
+  if (card == -1) 
+  {
+    kerror(("robin(robinProbe): Could not find space in the robincard array for this card."));
+    return(-RD_TOOMANYCARDS);
+  }
+
+  flags = pci_resource_flags(dev, PLX_BAR);  //MJ: See P317
+  if ((flags & IORESOURCE_MEM) != 0) 
+  {
+    baddr = pci_resource_start(dev, PLX_BAR) & PCI_BASE_ADDRESS_MEM_MASK;  //MJ: See P317
+    size = pci_resource_end(dev, PLX_BAR) - pci_resource_start(dev, PLX_BAR);    //MJ: See P317
+    kdebug(("robin(robinProbe): PLX: Mapping %d bytes at PCI address 0x%08x\n", size, baddr));
+    robincard[card].plx_regs = (u_int *)ioremap(baddr, size);
+    if (robincard[card].plx_regs == NULL)
+    {
+      kerror(("robin(robinProbe): error from ioremap for robincard[%d].plx_regs\n", card));
+      robincard[card].pciDevice = NULL;
+      return(-RD_REMAP);
+    }
+    kdebug(("robin(robinProbe): robincard[%d].plx_regs is at 0x%08x\n", card, (u_int)robincard[card].plx_regs));
+
+    robincard[card].pci_memaddr_plx = baddr;
+    robincard[card].pci_memsize_plx = size;
+  }
+  else 
+  {
+    kerror(("robin(robinProbe): Bar 0 is not a memory resource"));
+    return(-RD_BAR);
+  }
+
+  flags = pci_resource_flags(dev, FPGA_BAR);
+  bar2Available = 1;
+  if ((flags & IORESOURCE_MEM) != 0) 
+  {
+    baddr = pci_resource_start(dev, FPGA_BAR) & PCI_BASE_ADDRESS_MEM_MASK;
+    size = pci_resource_end(dev, FPGA_BAR) - pci_resource_start(dev, FPGA_BAR);
+    kdebug(("robin(robinProbe): FPGA: Mapping %d bytes at PCI address 0x%08x\n", size, baddr));
+    robincard[card].fpga_regs = (u_int *)ioremap(baddr, size);
+    robincard[card].bar2 = baddr;
+    if (robincard[card].fpga_regs == NULL)
+    {
+      kerror(("robin(robinProbe): error from ioremap for robincard[%d].fpga_regs\n", card));
+      return(-RD_REMAP);
+    }
+    kdebug(("robin(robinProbe): robincard[%d].fpga_regs is at 0x%08x\n", card, (u_int)robincard[card].fpga_regs));
+    
+    robincard[card].pci_memaddr_bus = baddr;
+    robincard[card].pci_memsize_bus = size;
+  }
+  else 
+  {
+    kerror(("robin(robinProbe): Warning! Bar 2 is not available"));
+    bar2Available = 0;
+  }
+
+  //MJ: bar2Available is there for the rare case that a Robin is used un-initialized for production tests
+  if ((dev->device == 0x144) || (dev->device == 0x324)) 
+  {
+    /* This is a ROBIN */
+    robincard[card].nrols = 3;
+  }
+  else if ((dev->device == 0x9656) && (bar2Available = 1)) 
+  {
+    /* This is a ROBIN Prototype */
+    robincard[card].nrols = 2;
+  }
+  
+  maxcard += 1;
+  return(0);
+}
+
+
+/******************************************/
+static void robinRemove(struct pci_dev *dev) 
+/******************************************/
+{
+  int i;
+  u_int card;
+
+  /* Find the "slot" of the card */
+  for (i = 0, card = -1; (i < MAXCARDS) && (card == -1); i++) 
+  {
+    if (robincard[i].pciDevice == dev) 
+      card = i;
+  }
+  
+  if (card == -1) 
+  {
+    kerror(("robin(robinRemove): Could not find device to remove."));
+    return;
+  }
+
+  iounmap((void *)robincard[card].plx_regs);
+  iounmap((void *)robincard[card].fpga_regs);
+
+  robincard[card].pciDevice = NULL;
+  robincard[card].pci_memaddr_plx = 0;
+  robincard[card].pci_memaddr_bus = 0;
+  robincard[card].pci_memsize_plx = 0; 
+  robincard[card].pci_memsize_bus = 0;
+  robincard[card].mem_base = 0;
+  robincard[card].mem_size = 0;
+  robincard[card].plx_regs = NULL;
+  robincard[card].fpga_regs = NULL;
+  robincard[card].nrols = 0;
+  robincard[card].bar2 = 0;
+  robincard[i].fpgaConfigured = 0;
+
+  maxcard -= 1;
+}
+
+
+
+/*****************************/
+/* Standard driver functions */
+/*****************************/
+
+
+/*********************************************************/
+static int robin_open(struct inode *ino, struct file *file)
+/*********************************************************/
+{
+  u_int loop;
+  T_private_data *pdata;
+
+  kdebug(("robin(robin_open): called from process %d\n", current->pid))
+  pdata = (T_private_data *)kmalloc(sizeof(T_private_data), GFP_KERNEL);
+  if (pdata == NULL)
+  {
+    kerror(("robin(robin_open): error from kmalloc\n"))
+    return(-RD_KMALLOC);
+  }
+
+  //I guess we have to protect code from being executed by parallel threads on a SMP machine. Therefore:
+  sema_init(&pdata->thread_sema, 1);
+
+  for(loop = 0; loop < MAXCARDS; loop++)
+  {
+    pdata->dpm_handle[loop] = -1;
+    pdata->mem_handle[loop] = -1;
+    pdata->fifo_handle[loop] = 0;
+  }
+
+  pdata->slot = -1;
+   
+  file->private_data = (char *)pdata;
+  return(0);
+}
+
+
+/************************************************************/
+static int robin_release(struct inode *ino, struct file *file)
+/************************************************************/
+{
+  T_private_data *pdata;
+  u_int ret;
+  
+  kdebug(("robin(robin_release): called from process %d\n", current->pid))
+
+  pdata = (T_private_data *)file->private_data;
+  kdebug(("robin(robin_release): Releasing orphaned resources for slot %d\n", pdata->slot))
+
+  if (pdata->slot != -1) 
+  {
+    if (pdata->fifo_handle[pdata->slot] != 0)
+    {  
+      kdebug(("robin(robin_release): Releasing %d FIFO slots for card %d\n", pdata->fifo_handle[pdata->slot], pdata->slot))
+      msg_fifo_avail[pdata->slot] += pdata->fifo_handle[pdata->slot];
+    }
+    
+    if (pdata->dpm_handle[pdata->slot] != -1)
+    {  
+      kdebug(("robin(robin_release): Freeing handle %d of the DPM\n", pdata->dpm_handle[pdata->slot]))
+      ret = retmem(pdata->slot, 0, pdata->dpm_handle[pdata->slot]);
+      if (ret)
+	kerror(("robin(robin_release): retmem for DPM returned %d\n", ret));
+    }
+    
+    if (pdata->mem_handle[pdata->slot] != -1)
+    { 
+      kdebug(("robin(robin_release): Freeing handle %d of the PC MEM\n", pdata->mem_handle[pdata->slot]))
+      ret = retmem(pdata->slot, 1, pdata->mem_handle[pdata->slot]);
+      if (ret)
+	kerror(("robin(robin_release): retmem for PC MEM returned %d\n", ret));
+    }
+  }
+
+  kfree(file->private_data);
+  kdebug(("robin(robin_release): kfreed file private data @ %x\n", (u_int)file->private_data))
+  file->private_data = NULL;	// Required?
+  return(0);
+}
+
+
+/***********************************************************************************/
+static int robin_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg)
+/***********************************************************************************/
+{
+  T_private_data *pdata;
+  int ret, slot;
+  
+  pdata = (T_private_data *)file->private_data;
+  kdebug(("robin(ioctl): file at 0x%08x, file private data at 0x%08x\n", (u_int)file, (u_int)pdata))
+
+  switch (cmd)
+  { 
+    case LINK:
+    {
+      kdebug(("robin(ioctl,LINK): called from process %d\n", current->pid))
+      
+      //As pdata->link_params has to be shared by all threads that are derived from the same
+      //open(/dev/robin) call we have to avoid that two threads use it at the same time. Therefore:
+      down(&pdata->thread_sema);
+      
+      if (copy_from_user(&pdata->link_params, (void *)arg, sizeof(T_link_params)) !=0)
+      {
+	kerror(("robin(ioctl,LINK): error from copy_from_user\n"));
+        up(&pdata->thread_sema);
+	return(-RD_CFU);
+      }      
+
+      if (robincard[pdata->link_params.slot].pciDevice == NULL) 
+      {
+        up(&pdata->thread_sema);
+	return(-RD_NOCARD);
+      }
+      
+      kdebug(("robin(ioctl,LINK): params.slot      = %d\n", pdata->link_params.slot))
+      pdata->link_params.plx_base  = robincard[pdata->link_params.slot].pci_memaddr_plx;  
+      pdata->link_params.plx_size  = robincard[pdata->link_params.slot].pci_memsize_plx;
+      pdata->link_params.fpga_base = robincard[pdata->link_params.slot].pci_memaddr_bus;
+      pdata->link_params.fpga_size = robincard[pdata->link_params.slot].pci_memsize_bus;
+      pdata->link_params.nrols = robincard[pdata->link_params.slot].nrols;
+      kdebug(("robin(ioctl,LINK): params.nrols     = 0x%08x\n", robincard[pdata->link_params.slot].nrols))
+      kdebug(("robin(ioctl,LINK): params.plx_base  = 0x%08x\n", pdata->link_params.plx_base))
+      kdebug(("robin(ioctl,LINK): params.plx_size  = 0x%08x\n", pdata->link_params.plx_size))
+      kdebug(("robin(ioctl,LINK): params.fpga_base = 0x%08x\n", pdata->link_params.fpga_base))
+      kdebug(("robin(ioctl,LINK): params.fpga_size = 0x%08x\n", pdata->link_params.fpga_size))
+
+      if (copy_to_user((void *)arg, &pdata->link_params, sizeof(T_link_params)) != 0)
+      {
+	kerror(("robin(ioctl,LINK): error from copy_to_user\n"));
+        up(&pdata->thread_sema);
+	return(-RD_CTU);
+      }
+
+      up(&pdata->thread_sema);
+      kdebug(("robin(ioctl,LINK): OK\n"))
+      break;
+    }
+
+    case INIT_DMA:
+    {
+      kdebug(("robin(ioctl,INIT_DMA): called from process %d\n", current->pid))
+      //This ioctl will only be called one from "robinconfigure". Therefore SMP and race conditions are not an issue            
+
+      if (copy_from_user(&pdata->init_dma_params, (void *)arg, sizeof(T_init_dma_params)) !=0)
+      {
+	kerror(("robin(ioctl,INIT_DMA): error from copy_from_user\n"));
+	return(-RD_CFU);
+      } 
+
+      //MJ: This is a bit of a trick. Before calling INIT_DMA "robinconfigure" checks via Jtag if the FPGA is loaded.
+      //Therefore proc_read and the TTY driver will only work once "robinconfigure" has been run
+      robincard[pdata->init_dma_params.slot].fpgaConfigured = 1;
+
+      kdebug(("robin(ioctl,INIT_DMA): link_params.slot      = %d\n", pdata->init_dma_params.slot))
+      kdebug(("robin(ioctl,INIT_DMA): link_params.mem_base  = 0x%08x\n", pdata->init_dma_params.mem_base))
+      kdebug(("robin(ioctl,INIT_DMA): link_params.mem_size  = 0x%08x\n", pdata->init_dma_params.mem_size))
+      robincard[pdata->init_dma_params.slot].mem_base = pdata->init_dma_params.mem_base;
+      robincard[pdata->init_dma_params.slot].mem_size = pdata->init_dma_params.mem_size;
+
+      //Initialize the memory manager
+      initmem(pdata->init_dma_params.slot, pdata->init_dma_params.mem_size, pdata->init_dma_params.mem_base);
+
+      break;
+    }
+
+    case MSG_REG:
+    {
+      kdebug(("robin(ioctl,MSG_REG): called from process %d\n", current->pid));
+      
+      //As pdata->msg_req_params has to be shared by all threads that are derived from the same
+      //open(/dev/robin) call we have to avoid that two threads use it at the same time. Therefore:
+      down(&pdata->thread_sema);
+
+      if (copy_from_user(&pdata->msg_req_params, (void *)arg, sizeof(T_msg_req_params)) !=0)
+      {
+	kerror(("robin(ioctl,LINK): error from copy_from_user\n"));
+        up(&pdata->thread_sema);
+	return(-RD_CFU);
+      }    
+      
+      kdebug(("robin(ioctl,MSG_REG): msg_req_params.slot     = %d\n", pdata->msg_req_params.slot))
+      kdebug(("robin(ioctl,MSG_REG): msg_req_params.n_fifo   = %d\n", pdata->msg_req_params.n_fifo))
+      kdebug(("robin(ioctl,MSG_REG): msg_req_params.dpm_size = %d\n", pdata->msg_req_params.dpm_size))
+      kdebug(("robin(ioctl,MSG_REG): msg_req_params.mem_size = %d\n", pdata->msg_req_params.mem_size))
+      
+      //MJ: If pdata->slot already has a value assigned we should return an error
+      pdata->slot = pdata->msg_req_params.slot;
+      
+      if ((pdata->dpm_handle[pdata->msg_req_params.slot] != -1) || (pdata->mem_handle[pdata->msg_req_params.slot] != -1))
+      {
+        kerror(("robin(ioctl,MSG_REG): This process has already requested message resources for card %d\n", pdata->msg_req_params.slot));
+        kerror(("robin(ioctl,MSG_REG): pdata->dpm_handle[%d] = %d\n", pdata->msg_req_params.slot, pdata->dpm_handle[pdata->msg_req_params.slot]));
+        kerror(("robin(ioctl,MSG_REG): pdata->mem_handle[%d] = %d\n", pdata->msg_req_params.slot, pdata->mem_handle[pdata->msg_req_params.slot]));
+        up(&pdata->thread_sema);
+	return(-RD_ALREADYDONE);
+      }
+      
+      //Get a buffer in the DPM
+      ret = getmem(pdata->msg_req_params.slot, 0, pdata->msg_req_params.dpm_size, &pdata->msg_req_params.dpm_base, &pdata->dpm_handle[pdata->msg_req_params.slot]);
+      if (ret)
+      {
+        kerror(("robin(ioctl,MSG_REG): getmem for DPM returned %d\n", ret));
+        up(&pdata->thread_sema);
+	return(ret);
+      } 
+
+      //Add the PCI base address of the DPM
+      pdata->msg_req_params.dpm_offset = pdata->msg_req_params.dpm_base;
+      pdata->msg_req_params.dpm_base += (robincard[pdata->msg_req_params.slot].pci_memaddr_bus + MESSAGE_RAM_OFFSET);
+      kdebug(("robin(ioctl,MSG_REG): getmem returns for DPM: base=0x%08x  handle=%d\n", pdata->msg_req_params.dpm_base, pdata->dpm_handle[pdata->msg_req_params.slot]))
+      kdebug(("robin(ioctl,MSG_REG): pdata->msg_req_params.dpm_offset = 0x%08x\n", pdata->msg_req_params.dpm_offset))
+
+      //Get a buffer in the PC MEM
+      ret = getmem(pdata->msg_req_params.slot, 1, pdata->msg_req_params.mem_size, &pdata->msg_req_params.mem_base, &pdata->mem_handle[pdata->msg_req_params.slot]);
+      if (ret)
+      {
+        kerror(("robin(ioctl,MSG_REG): getmem for PC MEM returned %d\n", ret));
+        up(&pdata->thread_sema);
+	return(ret);
+      } 
+      kdebug(("robin(ioctl,MSG_REG): getmem returns for MEM: base=0x%08x  handle=%d\n", pdata->msg_req_params.mem_base, pdata->mem_handle[pdata->msg_req_params.slot]))
+
+      pdata->msg_req_params.mem_offset = pdata->msg_req_params.mem_base - robincard[pdata->msg_req_params.slot].mem_base;
+      kdebug(("robin(ioctl,MSG_REG): pdata->msg_req_params.mem_base   = 0x%08x\n", pdata->msg_req_params.mem_base))
+      kdebug(("robin(ioctl,MSG_REG): robincard[%d].mem_base           = 0x%08x\n", pdata->msg_req_params.slot, robincard[pdata->msg_req_params.slot].mem_base))
+      kdebug(("robin(ioctl,MSG_REG): pdata->msg_req_params.mem_offset = 0x%08x\n", pdata->msg_req_params.mem_offset))
+
+      //Get FIFO slots
+      kdebug(("robin(ioctl,MSG_REG): card=%d   FIFO entries requested=%d   FIFO entries available=%d\n", pdata->msg_req_params.slot, msg_fifo_avail[pdata->msg_req_params.slot], pdata->msg_req_params.n_fifo));
+      if (msg_fifo_avail[pdata->msg_req_params.slot] < pdata->msg_req_params.n_fifo)
+      {
+        kerror(("robin(ioctl,MSG_REG): card=%d   FIFO entries requested=%d   FIFO entries available=%d\n", pdata->msg_req_params.slot, msg_fifo_avail[pdata->msg_req_params.slot], pdata->msg_req_params.n_fifo));
+        up(&pdata->thread_sema);
+	return(-RD_FIFOEMPTY);
+      }
+      else
+      {
+	pdata->fifo_handle[pdata->msg_req_params.slot] = pdata->msg_req_params.n_fifo;
+	msg_fifo_avail[pdata->msg_req_params.slot] -= pdata->msg_req_params.n_fifo;
+	kdebug(("robin(ioctl,MSG_REG): %d entries in MSG FIFO allocated on card %d\n", pdata->msg_req_params.n_fifo, pdata->msg_req_params.slot))
+      }
+
+      if (copy_to_user((void *)arg, &pdata->msg_req_params, sizeof(T_msg_req_params)) != 0)
+      {
+	kerror(("robin(ioctl,MSG_REG): error from copy_to_user\n"));
+        up(&pdata->thread_sema);
+	return(-RD_CTU);
+      }
+      
+      up(&pdata->thread_sema);
+      break;
+    }
+
+    case MSG_FREE:
+    {
+      kdebug(("robin(ioctl,MSG_FREE): called from process %d\n", current->pid))
+
+      if (copy_from_user(&slot, (void *)arg, sizeof(int)) !=0)
+      {
+	kerror(("robin(ioctl,MSG_FREE): error from copy_from_user\n"));
+	return(-RD_CFU);
+      } 
+            
+      kdebug(("robin(ioctl,MSG_FREE): card = %d\n", slot))
+      kdebug(("robin(ioctl,MSG_FREE): Freeing %d entries in the FIFO\n", pdata->fifo_handle[slot]))
+      msg_fifo_avail[slot] += pdata->fifo_handle[slot];
+      pdata->fifo_handle[slot] = 0;
+      
+      kdebug(("robin(ioctl,MSG_FREE): Freeing handle %d of the DPM\n", pdata->dpm_handle[slot]))
+      ret = retmem(slot, 0, pdata->dpm_handle[slot]);
+      if (ret)
+      {
+        kerror(("robin(ioctl,MSG_FREE): retmem for DPM returned %d\n", ret));
+	return(ret);
+      } 
+      
+      kdebug(("robin(ioctl,MSG_FREE): Freeing handle %d of the PC MEM\n", pdata->mem_handle[slot]))
+      ret = retmem(slot, 1, pdata->mem_handle[slot]);
+      if (ret)
+      {
+        kerror(("robin(ioctl,MSG_FREE): retmem for PC MEM returned %d\n", ret));
+	return(ret);
+      } 
+    
+      pdata->dpm_handle[slot] = -1;
+      pdata->mem_handle[slot] = -1;
+      kdebug(("robin(ioctl,MSG_FREE): pdata->dpm_handle[%d] = %d\n", slot, pdata->dpm_handle[slot]))
+      kdebug(("robin(ioctl,MSG_FREE): pdata->mem_handle[%d] = %d\n", slot, pdata->mem_handle[slot]))
+
+      break;
+    }
+
+    case LINK_IRQ:
+    {
+      kdebug(("robin(ioctl,LINK_IRQ): called from process %d\n", current->pid))
+      break;
+    }
+  }   
+  return(0);
+}
+
+
+/*********************************************************************************************/
+static int robin_write_procmem(struct file *file, const char *buffer, u_long count, void *data)
+/*********************************************************************************************/
+{
+  int len;
+  struct robin_proc_data_t *fb_data = (struct robin_proc_data_t *)data;
+
+  kdebug(("robin(robin_write_procmem): robin_write_procmem called\n"));
+
+  if(count > 99)
+    len = 99;
+  else
+    len = count;
+
+  if (copy_from_user(fb_data->value, buffer, len))
+  {
+    kerror(("robin(robin_write_procmem): error from copy_from_user\n"));
+    return(-RD_CFU);
+  }
+
+  kdebug(("robin(robin_write_procmem): len = %d\n", len));
+  fb_data->value[len - 1] = '\0';
+  kdebug(("robin(robin_write_procmem): text passed = %s\n", fb_data->value));
+
+  if (!strcmp(fb_data->value, "debug"))
+  {
+    debug = 1;
+    kdebug(("robin(robin_write_procmem): debugging enabled\n"));
+  }
+
+  if (!strcmp(fb_data->value, "nodebug"))
+  {
+    kdebug(("robin(robin_write_procmem): debugging disabled\n"));
+    debug = 0;
+  }
+
+  if (!strcmp(fb_data->value, "elog"))
+  {
+    kdebug(("robin(robin_write_procmem): Error logging enabled\n"))
+    errorlog = 1;
+  }
+
+  if (!strcmp(fb_data->value, "noelog"))
+  {
+    kdebug(("robin(robin_write_procmem): Error logging disabled\n"))
+    errorlog = 0;
+  }
+  
+  return(len);
+}
+
+
+/***************************************************************************************************/
+static int robin_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data)
+/***************************************************************************************************/
+{
+  u_int card, value, v1, v2, v3, v4, v5, fpgadesign;
+  int ret, nchars = 0, loop1, loop2;
+  static int len = 0;
+
+  kdebug(("robin(robin_read_procmem): Called with buf    = 0x%08x\n", (unsigned int)buf));
+  kdebug(("robin(robin_read_procmem): Called with *start = 0x%08x\n", (unsigned int)*start));
+  kdebug(("robin(robin_read_procmem): Called with offset = %d\n", (unsigned int)offset));
+  kdebug(("robin(robin_read_procmem): Called with count  = %d\n", count));
+
+  if (offset == 0)
+  {
+    kdebug(("robin(robin_read_procmem): Creating text....\n"));
+    len = 0;
+    len += sprintf(proc_read_text + len, "\n\n\nROBIN driver for release %s (based on CVS tag %s)\n", RELEASE_NAME, CVSTAG);
+    len += sprintf(proc_read_text + len, "Number of cards detected = %d\n", maxcard);
+    len += sprintf(proc_read_text + len, "\nSome PLX registers\n");
+    len += sprintf(proc_read_text + len, "card |     LAS0RR |     LAS0BA |       DMRR |     DMLBAM |     DMPBAM |      CNTRL |     INTCSR |\n");
+    for (card = 0; card < maxcard; card++)
+    {
+      len += sprintf(proc_read_text + len, "%4d |", card);
+      len += sprintf(proc_read_text + len, " 0x%08x |", robincard[card].plx_regs[PLX_LAS0RR]);
+      len += sprintf(proc_read_text + len, " 0x%08x |", robincard[card].plx_regs[PLX_LAS0BA]);
+      len += sprintf(proc_read_text + len, " 0x%08x |", robincard[card].plx_regs[PLX_DMRR]);
+      len += sprintf(proc_read_text + len, " 0x%08x |", robincard[card].plx_regs[PLX_DMLBAM]);
+      len += sprintf(proc_read_text + len, " 0x%08x |", robincard[card].plx_regs[PLX_DMPBAM]);
+      len += sprintf(proc_read_text + len, " 0x%08x |", robincard[card].plx_regs[PLX_CNTRL]);
+      len += sprintf(proc_read_text + len, " 0x%08x |\n", robincard[card].plx_regs[PLX_INTCSR]);
+    }
+
+    len += sprintf(proc_read_text + len, "\nSome other registers\n");
+    len += sprintf(proc_read_text + len, "card | FPGA design ID |       BAR2 |\n");
+    for (card = 0; card < maxcard; card++)
+    {
+      len += sprintf(proc_read_text + len, "%4d |", card);
+      ret = fpgaLoaded(card, &fpgadesign);
+      if(ret == 0)
+      {
+	len += sprintf(proc_read_text + len, "     0x%08x |", robincard[card].fpga_regs[0]);
+	len += sprintf(proc_read_text + len, " 0x%08x |\n", robincard[card].bar2);
+      }
+      else if (ret == -1)
+	len += sprintf(proc_read_text + len, "robinconfig has not yet been run on this Robin\n");
+      else
+	len += sprintf(proc_read_text + len, "The FPGA of this card (0x%08x) is out of date\n", fpgadesign);
+    }
+
+    len += sprintf(proc_read_text + len, "\nS-Link status bits\n");
+    len += sprintf(proc_read_text + len, "Card | ROL | Link err. | Link up | Xoff | Link active | ROL Emulation |\n");
+    for (card = 0; card < maxcard; card++)
+    {
+      ret = fpgaLoaded(card, &fpgadesign);
+      if(ret == 0)
+      {
+	value = robincard[card].fpga_regs[SLINK_REG_OFFSET >> 2];
+
+	v1 = (value >> 0) & 0x1;
+	v2 = (value >> 1) & 0x1;
+	v3 = (value >> 2) & 0x1;
+	v4 = (value >> 3) & 0x1;
+	v5 = (value >> 12) & 0x1;
+	len += sprintf(proc_read_text + len, "   %d |   0 |       %s |     %s |  %s |         %s |           %s |\n", card, v1?" No":"Yes",  v2?" No":"Yes", v3?" No":"Yes", v4?" No":"Yes", v5?"Yes":" No");
+
+	v1 = (value >> 4) & 0x1;
+	v2 = (value >> 5) & 0x1;
+	v3 = (value >> 6) & 0x1;
+	v4 = (value >> 7) & 0x1;
+	v5 = (value >> 13) & 0x1;    
+	len += sprintf(proc_read_text + len, "   %d |   1 |       %s |     %s |  %s |         %s |           %s |\n", card, v1?" No":"Yes",  v2?" No":"Yes", v3?" No":"Yes", v4?" No":"Yes", v5?"Yes":" No");
+
+	v1 = (value >> 8) & 0x1;
+	v2 = (value >> 9) & 0x1;
+	v3 = (value >> 10) & 0x1;
+	v4 = (value >> 11) & 0x1;
+	v5 = (value >> 14) & 0x1;
+	len += sprintf(proc_read_text + len, "   %d |   2 |       %s |     %s |  %s |         %s |           %s |\n", card, v1?" No":"Yes",  v2?" No":"Yes", v3?" No":"Yes", v4?" No":"Yes", v5?"Yes":" No");
+      }
+      else if (ret == -1)
+	len += sprintf(proc_read_text + len, "robinconfig has not yet been run on this Robin\n");
+      else
+	len += sprintf(proc_read_text + len, "The FPGA of this card (0x%08x) is out of date\n", fpgadesign);
+     }
+
+    len += sprintf(proc_read_text + len, "\nDumping status of Message FIFOs\n");
+    for(loop1 = 0; loop1 < maxcard; loop1++)
+      len += sprintf(proc_read_text + len,"There are %d FIFO slots available on card %d\n", msg_fifo_avail[loop1], loop1);
+
+    len += sprintf(proc_read_text + len, "\nDumping status of Dual Ported Memory\n");
+    len += sprintf(proc_read_text + len,"Index | Status |      Start |       Size\n");
+    for(loop1 = 0; loop1 < maxcard; loop1++)
+    {
+      len += sprintf(proc_read_text + len, "Card %d\n", loop1);
+      for(loop2 = 0; loop2 < MAX_BLOCKLETS; loop2++)
+      {
+	len += sprintf(proc_read_text + len,"%5d |", loop2);
+	len += sprintf(proc_read_text + len,"%7d |", blocklets[loop1][0][loop2].used);
+	len += sprintf(proc_read_text + len," 0x%08x |", blocklets[loop1][0][loop2].start);
+	len += sprintf(proc_read_text + len," 0x%08x\n", blocklets[loop1][0][loop2].size);
+      }
+    }
+
+    len += sprintf(proc_read_text + len,"\nDumping status of PC direct-DMA Memory\n");
+    len += sprintf(proc_read_text + len,"Index | Status |      Start |       Size\n");
+    for(loop1 = 0; loop1 < maxcard; loop1++)
+    {
+      len += sprintf(proc_read_text + len, "Card %d\n", loop1);
+      for(loop2 = 0; loop2 < MAX_BLOCKLETS; loop2++)
+      {
+	len += sprintf(proc_read_text + len,"%5d |", loop2);
+	len += sprintf(proc_read_text + len,"%7d |", blocklets[loop1][1][loop2].used);
+	len += sprintf(proc_read_text + len," 0x%08x |", blocklets[loop1][1][loop2].start);
+	len += sprintf(proc_read_text + len," 0x%08x\n", blocklets[loop1][1][loop2].size);
+      }
+    }
+
+    len += sprintf(proc_read_text + len, " \n");
+    len += sprintf(proc_read_text + len, "The command 'echo <action> > /proc/robin', executed as root,\n");
+    len += sprintf(proc_read_text + len, "allows you to interact with the driver. Possible actions are:\n");
+    len += sprintf(proc_read_text + len, "debug   -> enable debugging\n");
+    len += sprintf(proc_read_text + len, "nodebug -> disable debugging\n");
+    len += sprintf(proc_read_text + len, "elog    -> Log errors to /var/log/message\n");
+    len += sprintf(proc_read_text + len, "noelog  -> Do not log errors to /var/log/message\n");
+    kdebug(("robin(robin_read_procmem): Number of characters created = %d\n", len));
+  }
+
+
+  if (count < (len - offset))
+    nchars = count;
+  else
+    nchars = len - offset;
+  kdebug(("robin(robin_read_procmem): min nchars         = %d\n", nchars));
+  kdebug(("robin(robin_read_procmem): position           = %ld\n", (offset & (PAGE_SIZE - 1))));
+  
+  if (nchars > 0)
+  {
+    for (loop1 = 0; loop1 < nchars; loop1++)
+      buf[loop1 + (offset & (PAGE_SIZE - 1))] = proc_read_text[offset + loop1];
+    *start = buf + (offset & (PAGE_SIZE - 1));
+  }
+  else
+  {
+    nchars = 0;
+    *eof = 1;
+  }
+ 
+  kdebug(("robin(robin_read_procmem): returning *start   = 0x%08x\n", (unsigned int)*start));
+  kdebug(("robin(robin_read_procmem): returning nchars   = %d\n", nchars));
+  return(nchars);
+}
+
+
+//------------------
+// Service functions
+//------------------
+
+
+/******************************************************************/
+static int robin_mmap(struct file *file, struct vm_area_struct *vma)
+/******************************************************************/
+{
+  u_long offset, size;
+
+  kdebug(("robin(robin_mmap): function called\n"));
+  
+  offset = vma->vm_pgoff << PAGE_SHIFT;  
+  size = vma->vm_end - vma->vm_start;
+  kdebug(("robin(robin_mmap): offset = 0x%08x\n",(u_int)offset));
+  kdebug(("robin(robin_mmap): size   = 0x%08x\n",(u_int)size));
+
+  if (offset & ~PAGE_MASK)
+  {
+    kerror(("robin(robin_mmap): offset not aligned: %ld\n", offset));
+    return(-RD_MMAP);
+  }
+
+  // we only support shared mappings. "Copy on write" mappings are
+  // rejected here. A shared mapping that is writeable must have the
+  // shared flag set.
+  if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED))
+  {
+    kerror(("robin(robin_mmap): writeable mappings must be shared, rejecting\n"));
+    return(-RD_NOSHARE);
+  }
+
+  vma->vm_flags |= VM_RESERVED;
+  
+  // we do not want to have this area swapped out, lock it
+  vma->vm_flags |= VM_LOCKED;
+
+  // we create a mapping between the physical pages and the virtual
+  // addresses of the application with remap_page_range.
+  // enter pages into mapping of application
+  kdebug(("robin(robin_mmap): Parameters of remap_page_range()\n"));
+  kdebug(("robin(robin_mmap): Virtual address  = 0x%08x\n",(u_int)vma->vm_start));
+  kdebug(("robin(robin_mmap): Physical address = 0x%08x\n",(u_int)offset));
+  kdebug(("robin(robin_mmap): Size             = 0x%08x\n",(u_int)size));
+  if (remap_page_range(vma, vma->vm_start, offset, size, vma->vm_page_prot))
+  {
+    kerror(("robin(robin_mmap): remap page range failed\n"));
+    return(-RD_RPR);
+  }
+  
+  vma->vm_ops = &robin_vm_ops;  
+  
+  kdebug(("robin(robin_mmap): function done\n"));
+  return(0);
+}
+
+
+/**************************************************/
+static void robin_vclose(struct vm_area_struct *vma)
+/**************************************************/
+{  
+  kdebug(("robin(robin_vclose): Virtual address  = 0x%08x\n",(u_int)vma->vm_start));
+  kdebug(("robin(robin_vclose): mmap released.\n"));
+}
+
+
+/****************************************************/
+static int initmem(u_int card, u_int size, u_int base)
+/****************************************************/
+{
+  int loop1, loop2;
+
+  kdebug(("robin(initmem): Initializing memory management structures for card %d\n", card));
+
+  for(loop1 = 0; loop1 < BLOCK_TYPES; loop1++)
+  {
+    for(loop2 = 0; loop2 < MAX_BLOCKLETS; loop2++)
+    {
+      blocklets[card][loop1][loop2].used = BLOCKLET_UNUSED;
+      blocklets[card][loop1][loop2].start = 0;
+      blocklets[card][loop1][loop2].size = 0;
+    }
+  }
+
+  //Initialise the DPM buffer
+  blocklets[card][0][0].used = BLOCKLET_FREE;
+  blocklets[card][0][0].start = 0;
+  blocklets[card][0][0].size = MESSAGE_RAM_SIZE;  
+
+  //Initialise the PC MEM buffer
+  blocklets[card][1][0].used = BLOCKLET_FREE;
+  blocklets[card][1][0].start = base;
+  blocklets[card][1][0].size = size;
+  
+  msg_fifo_avail[card] = MESSAGE_FIFO_DEPTH;
+  kdebug(("robin(initmem):  msg_fifo_avail has been initialised to %d\n", MESSAGE_FIFO_DEPTH));
+
+  return(0);
+}
+
+
+/************************************************************************************/
+static int getmem(u_int card, u_int type, u_int size, u_int *start_add, u_int *handle)
+/************************************************************************************/
+{
+  int loop, b1 = -1 , b2 = -1;
+
+  kdebug(("robin(getmem): called with card=%d  type=%d  size=%d\n", card, type, size));
+
+  kdebug(("robin(getmem): looking for space...\n"));
+  //look for a large enough free blocklet  
+  for(loop = 0; loop < MAX_BLOCKLETS; loop++)
+  {
+    kdebug(("robin(getmem): blocklets[%d][%d][%d].used = %d\n", card, type, loop, blocklets[card][type][loop].used));
+    kdebug(("robin(getmem): blocklets[%d][%d][%d].size = %d\n", card, type, loop, blocklets[card][type][loop].size));
+    if (blocklets[card][type][loop].used == BLOCKLET_FREE && blocklets[card][type][loop].size >= size)
+    {
+      b1 = loop;
+      break;
+    }  
+  }
+  
+  kdebug(("robin(getmem): looking for free slot...\n"));
+  //look for an empty slot in the array to hold the new blocklet
+  for(loop = 0; loop < MAX_BLOCKLETS; loop++)
+  {
+    kdebug(("robin(getmem): blocklets[%d][%d][%d].used = %d\n", card, type, loop, blocklets[card][type][loop].used));
+    if (blocklets[card][type][loop].used == BLOCKLET_UNUSED)
+    {
+      b2 = loop;
+      break;
+    }  
+  } 
+
+  if (b1 == -1)
+  {
+    kerror(("robin(getmem): failed to find free memory\n"))
+    return(-RD_NOMEMORY);
+  }
+    
+  if (b2 == -1)
+  {
+    kerror(("robin(getmem): failed to find an enpty slot\n"))
+    return(-RD_NOSLOT);     
+  }
+ 
+  blocklets[card][type][b2].used = BLOCKLET_USED;
+  blocklets[card][type][b2].size = size;
+  blocklets[card][type][b2].start = blocklets[card][type][b1].start;  
+
+  blocklets[card][type][b1].size -= size;
+  if (blocklets[card][type][b1].size == 0)
+  {
+    blocklets[card][type][b1].used = BLOCKLET_UNUSED;
+    blocklets[card][type][b1].start = 0;
+  }
+  else
+    blocklets[card][type][b1].start += size;
+  
+  *start_add = blocklets[card][type][b2].start;
+  *handle = b2;
+
+  return(0);
+}
+
+
+/*****************************************************/
+static int retmem(u_int card, u_int type, u_int handle)
+/*****************************************************/
+{
+  int loop, bprev = -1,  bnext = -1;
+  
+  kdebug(("robin(retmem): called with card=%d  type=%d  handle=%d\n", card, type, handle));
+  if (blocklets[card][type][handle].used != BLOCKLET_USED)
+    return(-RD_ILLHANDLE);
+ 
+  blocklets[card][type][handle].used = BLOCKLET_FREE;
+    
+  //Check if the blocklet can be merged with a predecessor   
+  for(loop = 0; loop < MAX_BLOCKLETS; loop++)
+  {
+    if (blocklets[card][type][loop].used == BLOCKLET_FREE && (blocklets[card][type][loop].start + blocklets[card][type][loop].size) == blocklets[card][type][handle].start)
+    {
+      bprev = loop;
+      break;
+    }
+  }
+  
+  //Check if the blocklet can be merged with a sucessor   
+  for(loop = 0; loop < MAX_BLOCKLETS; loop++)
+  {
+    if (blocklets[card][type][loop].used == BLOCKLET_FREE && (blocklets[card][type][handle].start + blocklets[card][type][handle].size) == blocklets[card][type][loop].start)
+    {
+      bnext = loop;
+      break;
+    }
+  }
+  
+  if (bprev != -1)
+  {
+    kdebug(("robin(retmem): merging %d with predecessor %d\n", handle, bprev)); 
+    blocklets[card][type][handle].start = blocklets[card][type][bprev].start;
+    blocklets[card][type][handle].size += blocklets[card][type][bprev].size;
+    blocklets[card][type][bprev].used = BLOCKLET_UNUSED;
+    blocklets[card][type][bprev].start = 0;
+    blocklets[card][type][bprev].size = 0;
+  }
+  
+  if (bnext != -1)
+  {
+    kdebug(("robin(retmem): merging %d with sucessor %d\n", handle, bnext)); 
+    blocklets[card][type][handle].size += blocklets[card][type][bnext].size;
+    blocklets[card][type][bnext].used = BLOCKLET_UNUSED;
+    blocklets[card][type][bnext].start = 0;
+    blocklets[card][type][bnext].size = 0;
+  }
+  return(0);
+}
+
+
+
+/**************************************************/
+static int readFpga(u_int card, u_int addressOffset)
+/**************************************************/
+{
+  u_int fpgadesign;
+  int value = 0;
+  void *address;
+  
+  kdebug(("robin(readFpga): function called\n"))
+
+  if ((robincard[card].fpga_regs != NULL) && (addressOffset < robincard[card].pci_memsize_bus)) 
+  {
+    address = (void *)(robincard[card].fpga_regs + addressOffset);
+    if (fpgaLoaded(card, &fpgadesign) == 0) 
+    {
+      value = ioread32(address);
+      kdebug(("robin(readFpga): 0x%08x read from FPGA of card %d at offset 0x%08x\n", value, card, addressOffset))
+    }
+    return value;
+  }
+  else 
+    return 0;
+}
+
+
+/**************************************************************/
+static void writeFpga(u_int card, u_int addressOffset, int data)
+/**************************************************************/
+{
+  u_int fpgadesign;
+  void *address;
+  
+  kdebug(("robin(writeFpga): function called\n"))
+
+  if ((robincard[card].fpga_regs != NULL) && (addressOffset < robincard[card].pci_memsize_bus)) 
+  {
+    address = (void *)(robincard[card].fpga_regs + addressOffset);
+    if (fpgaLoaded(card, &fpgadesign) == 0) 
+      iowrite32(data, robincard[card].fpga_regs + addressOffset);
+  }
+}
+
+
+/**************************************************/
+static int fpgaLoaded(u_int card, u_int *fpgadesign)  
+/**************************************************/
+{
+  int value, version, majorRevision, minorRevision;
+  int eversion, emajorRevision, eminorRevision;
+
+  kdebug(("robin(fpgaLoaded): function called\n"))
+  
+  eversion       = DESIGN_ID >> 24;
+  emajorRevision = (DESIGN_ID >> 16) & 0xff;
+  eminorRevision = DESIGN_ID & 0x0fff;
+  
+  if ((card < maxcard) && (robincard[card].fpgaConfigured == 1)) 
+  {
+    value = ioread32((void *) (robincard[card].fpga_regs));
+    *fpgadesign = value;
+    
+    version = value >> 24;
+    majorRevision = (value >> 16) & 0xff;
+    minorRevision = value & 0x0fff;
+
+    kdebug(("robin(fpgaLoaded): Expected = 0x%08x\n", DESIGN_ID));
+    kdebug(("robin(fpgaLoaded): eversion: 0x%x\n", eversion));
+    kdebug(("robin(fpgaLoaded): emajorRevision: 0x%x\n", emajorRevision));
+    kdebug(("robin(fpgaLoaded): eminorRevision: 0x%x\n", eminorRevision));
+    kdebug(("robin(fpgaLoaded): Detected = 0x%08x\n", value));
+    kdebug(("robin(fpgaLoaded): version: 0x%x\n", version));
+    kdebug(("robin(fpgaLoaded): majorRevision: 0x%x\n", majorRevision));
+    kdebug(("robin(fpgaLoaded): minorRevision: 0x%x\n", minorRevision));
+
+    if ((eversion != version) || (emajorRevision != majorRevision))
+    {
+      kerror(("robin(fpgaLoaded): Incompatible FPGA version. Can't continue!\n"));
+      return -2;
+    } 
+    else 
+    {
+      if (eminorRevision > minorRevision)
+      {
+        kerror(("robin(fpgaLoaded): Incompatible FPGA revision (too small). Can't continue!\n"));
+        return -2;
+      }
+      if (eminorRevision < minorRevision)
+        kdebug(("robin(fpgaLoaded): Actual FPGA revision too high. Not all FPGA features might be exploited\n"));
+    }
+    return 0;
+  }
+  else
+  { 
+    kdebug(("robin(fpgaLoaded): robinconfig has not yet been called on this Robin\n"));
+    *fpgadesign = 0;
+    return -1;
+  }
+}
+
+
+/*************************/
+static int getCardNum(void) 
+/*************************/
+{
+  return(maxcard);
+}
+
+
+EXPORT_SYMBOL(readFpga);
+EXPORT_SYMBOL(writeFpga);
+EXPORT_SYMBOL(fpgaLoaded);
+EXPORT_SYMBOL(getCardNum);
diff --git a/cmt/solar.c b/cmt/solar.c
new file mode 100644
index 0000000000000000000000000000000000000000..d34045550d1a116d69af262bfb3252867189ad86
--- /dev/null
+++ b/cmt/solar.c
@@ -0,0 +1,893 @@
+/************************************************************************/
+/*									*/
+/* File: solar_driver.c							*/
+/*									*/
+/* driver for the SOLAR S-Link interface				*/
+/*									*/
+/* 2. Apr. 03  MAJO  created						*/
+/*									*/
+/************ C 2005 - The software with that certain something *********/
+
+/************************************************************************/
+/*NOTES:								*/
+/*- This driver should work on kernels from 2.6.9 onwards		*/
+/************************************************************************/
+
+
+#include <linux/init.h>           //MJ: for 2.6, p30
+#include <linux/module.h>         //p30
+#include <linux/moduleparam.h>    //MJ: for 2.6, p30
+#include <linux/kernel.h>         //e.g. for printk
+#include <linux/stat.h>           //MJ: for 2.6, e.g. for module_param
+#include <linux/pci.h>            //e.g. for   pci_find_device
+#include <linux/errno.h>          //e.g. for EFAULT
+#include <linux/fs.h>             //e.g. for register_chrdev
+#include <linux/proc_fs.h>        //e.g. for create_proc_entry
+#include <linux/mm.h>             //e.g. for vm_operations_struct
+#include <linux/slab.h>           //e.g. for kmalloc
+#include <linux/cdev.h>           //e.g. for cdev_alloc
+#include <linux/delay.h>
+#include <linux/interrupt.h>      //e.g. for request_irq
+#include <asm/uaccess.h>          //e.g. for copy_from_user
+#include <asm/io.h>               //e.g. for inl
+#include <asm/system.h>           //e.g. for wmb
+#include "ROSsolar/solar_driver.h"
+
+
+/*********/
+/*Globals*/
+/*********/
+static int debug = 0;
+static char *proc_read_text;
+static u_int irqlist[MAXIRQ], infifor[MAXCARDS], served[MAXCARDS];
+static u_int maxcards, *infifo, *infifon, order, swfifobasephys, infifodatasize, fifoptrsize, tsize = 0;
+static u_long swfifobase;
+static struct proc_dir_entry *solar_file;
+static struct cdev *solar_cdev;
+struct solar_proc_data_t solar_proc_data;
+static dev_t major_minor;
+static T_solar_card solarcard[MAXCARDS];
+
+/************/
+/*Prototypes*/
+/************/
+static int __devinit solarProbe(struct pci_dev *dev, const struct pci_device_id *id);
+static void solar_cleanup(void);
+static void solar_vmaOpen(struct vm_area_struct *vma);
+static void solar_vmaClose(struct vm_area_struct *vma);
+static void solarRemove(struct pci_dev *dev); 
+static int solar_init(void);
+static irqreturn_t solar_irq_handler (int irq, void *dev_id, struct pt_regs *regs);
+
+/***************************************************************/
+/* Use /sbin/modinfo <module name> to extract this information */
+/***************************************************************/
+MODULE_DESCRIPTION("S-Link SOLAR");
+MODULE_AUTHOR("Markus Joos, CERN/PH");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("Private: Contact markus.joos@cern.ch");
+module_param (debug, int, S_IRUGO | S_IWUSR);  //MJ: for 2.6 p37
+MODULE_PARM_DESC(debug, "1 = enable debugging   0 = disable debugging"); 
+
+
+//MJ: Not actually required. Just for kdebug
+struct vm_operations_struct solar_vm_ops =
+{       
+  .close = solar_vmaClose,      
+  .open  = solar_vmaOpen,      //MJ: Note the comma at the end of the list!
+};
+
+
+static struct file_operations fops = 
+{
+  .owner   = THIS_MODULE,
+  .ioctl   = solar_ioctl,
+  .open    = solar_open,    
+  .mmap    = solar_mmap,
+  .release = solar_release,
+};
+
+// PCI driver structures. See p311
+static struct pci_device_id solarIds[] = 
+{
+  {PCI_DEVICE(PCI_VENDOR_ID_CERN, SOLAR_PCI_DEVICE_ID)},      
+  {0,},
+};
+
+//PCI operations
+static struct pci_driver solarPciDriver = 
+{
+  .name     = "solar",
+  .id_table = solarIds,
+  .probe    = solarProbe,
+  .remove   = solarRemove,
+};
+
+
+
+/*****************************/
+/* Standard driver functions */
+/*****************************/
+
+/*************************/
+static int solar_init(void)
+/*************************/
+{
+  int card, tfsize, result;
+  u_int loop;
+  struct page *page_ptr;
+     
+  if (alloc_chrdev_region(&major_minor, 0, 1, "solar")) //MJ: for 2.6 p45
+  {
+    kdebug(("solar(solar_init): failed to obtain device numbers\n"));
+    result = -EIO;
+    goto fail1;
+  }
+
+  proc_read_text = (char *)kmalloc(MAX_PROC_TEXT_SIZE, GFP_KERNEL);
+  if (proc_read_text == NULL)
+  {
+    kdebug(("solar(solar_init): error from kmalloc\n"));
+    result = -EFAULT;
+    goto fail2;
+  }
+
+  solar_file = create_proc_entry("solar", 0644, NULL);
+  if (solar_file == NULL)
+  {
+    kdebug(("solar(solar_init): error from call to create_proc_entry\n"));
+    result = -EFAULT;
+    goto fail3;
+  }
+
+  strcpy(solar_proc_data.name, "solar");
+  strcpy(solar_proc_data.value, "solar");
+  solar_file->data = &solar_proc_data;
+  solar_file->read_proc = solar_read_procmem;
+  solar_file->write_proc = solar_write_procmem;
+  solar_file->owner = THIS_MODULE;    
+  
+  kdebug(("solar(solar_init): registering PCIDriver\n"));
+  result = pci_register_driver(&solarPciDriver);  //MJ: See P312
+  if (result == 0)
+  {
+    kdebug(("solar(solar_init): ERROR! no SOLAR cards found!\n"));
+    result = -EIO;
+    goto fail4;
+  }
+  else 
+  {
+    kdebug(("solar(solar_init): Found %d SOLAR cards!\n", result));
+  }
+    
+  // Allocate contiguous memory for the FIFOs and store the base addresses in a global structure
+  infifodatasize = MAXCARDS * MAXINFIFO * sizeof(u_int) * 2;  //infifo
+  fifoptrsize = MAXCARDS * sizeof(u_int);                     //infifo(w/r/n)
+  kdebug(("solar(solar_init): 0x%08x bytes needed for the IN FIFO\n", infifodatasize));
+  kdebug(("solar(solar_init): 0x%08x bytes needed for the FIFO pointers\n", fifoptrsize));
+  
+  tsize = infifodatasize + fifoptrsize;
+  kdebug(("solar(solar_init): 0x%08x bytes needed for all FIFOs\n", tsize));
+
+  tfsize = (tsize + 4095) & 0xfffff000;  // round up to next 4K boundary
+  tfsize = tfsize >> 12;                 // compute number of pages
+  kdebug(("solar(solar_init): %d pages needed for the S/W FIFOs\n", tfsize));
+ 
+  order = 0;
+  while(tfsize)
+  {
+    order++;
+    tfsize = tfsize >> 1;                 // compute order
+  } 
+  kdebug(("solar(solar_init): order = %d\n", order));
+  
+  swfifobase = __get_free_pages(GFP_ATOMIC, order);
+  if (!swfifobase)
+  {
+    kdebug(("rsolar(solar_init): error from __get_free_pages\n"));
+    result = -EFAULT;
+    goto fail5;
+  }
+  kdebug(("solar(solar_init): swfifobase = 0x%016lx\n", swfifobase));
+   
+  // Reserve all pages to make them remapable
+  page_ptr = virt_to_page(swfifobase);
+  for (loop = (1 << order); loop > 0; loop--, page_ptr++)
+    set_bit(PG_reserved, &page_ptr->flags);
+
+  swfifobasephys = virt_to_bus((void *) swfifobase);
+  kdebug(("solar(solar_init): swfifobasephys = 0x%08x\n", swfifobasephys));
+
+  // Assign base addresses to the FIFO arrays
+  infifo  = (u_int *)swfifobase;
+  infifon = (u_int *)(swfifobase + infifodatasize);
+  kdebug(("solar(solar_init): infifo   is at 0x%016lx\n", (u_long)infifo));
+  kdebug(("solar(solar_init): infifon  is at 0x%016lx\n", (u_long)infifon));
+
+  //Initialize the FIFOs
+  for(card = 0; card < MAXCARDS; card++)
+  {
+    infifor[card] = 0;
+    infifon[card] = 0;
+  }
+
+  solar_cdev = (struct cdev *)cdev_alloc();       //MJ: for 2.6 p55
+  solar_cdev->ops = &fops;
+  result = cdev_add(solar_cdev, major_minor, 1);  //MJ: for 2.6 p56
+  if (result)
+  {
+    kdebug(("solar(solar_init): error from call to cdev_add.\n"));
+    goto fail6;
+  }
+
+
+  kdebug(("solar(solar_init): driver loaded; major device number = %d\n", MAJOR(major_minor)));
+  return(0);
+
+  fail6:
+    page_ptr = virt_to_page(swfifobase);
+    for (loop = (1 << order); loop > 0; loop--, page_ptr++)
+      clear_bit(PG_reserved, &page_ptr->flags);
+    free_pages(swfifobase, order);
+     
+  fail5:
+    pci_unregister_driver(&solarPciDriver);
+
+  fail4:
+    remove_proc_entry("io_rcc", NULL);
+    
+  fail3:
+    kfree(proc_read_text);
+
+  fail2:
+    unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45
+
+  fail1:
+    return(result);    
+}
+
+
+/*****************************/
+static void solar_cleanup(void)
+/*****************************/
+{
+  u_int loop;
+  struct page *page_ptr;
+      
+  cdev_del(solar_cdev);                     //MJ: for 2.6 p56
+  pci_unregister_driver(&solarPciDriver);
+
+  // unreserve all pages used for the S/W FIFOs
+  page_ptr = virt_to_page(swfifobase);
+  for (loop = (1 << order); loop > 0; loop--, page_ptr++)
+    clear_bit(PG_reserved, &page_ptr->flags);
+
+  // free the area 
+  free_pages(swfifobase, order);
+  remove_proc_entry("solar", NULL);
+  kfree(proc_read_text);
+  unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45
+
+  kdebug(("solar(solar_cleanup): driver removed\n"));
+}
+
+
+module_init(solar_init);    //MJ: for 2.6 p16
+module_exit(solar_cleanup); //MJ: for 2.6 p16
+
+
+/**********************************************************/
+static int solar_open(struct inode *ino, struct file *filep)
+/**********************************************************/
+{
+  kdebug(("solar(solar_open): nothing to be done\n"));
+  return(0);
+}
+
+
+/*************************************************************/
+static int solar_release(struct inode *ino, struct file *filep)
+/*************************************************************/
+{
+  kdebug(("solar(solar_release): nothing to be done\n"));
+  return(0);
+}
+
+/***********************************************************************************/
+static int solar_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg)
+/***********************************************************************************/
+{
+  switch (cmd)
+  {    
+    case OPEN:
+    {
+      SOLAR_open_t params;
+      
+      params.size = tsize;
+      params.physbase = swfifobasephys;
+      params.insize = infifodatasize;
+      kdebug(("solar(ioctl,OPEN): params.size     = 0x%08x\n", params.size));
+      kdebug(("solar(ioctl,OPEN): params.physbase = 0x%08x\n", params.physbase));
+      kdebug(("solar(ioctl,OPEN): params.insize   = 0x%08x\n", params.insize));
+  
+      if (copy_to_user((void *)arg, &params, sizeof(SOLAR_open_t)))
+      {
+	kdebug(("solar(ioctl, OPEN): error from copy_to_user\n"));
+	return(-SOLAR_EFAULT);
+      }      
+      return(0);
+      break;
+    }
+
+    case CONFIG:
+    { 
+      SOLAR_config_t params;
+      u_int card, data;
+      
+      if (copy_from_user(&params, (void *)arg, sizeof(SOLAR_config_t)))
+      {
+	kdebug(("solar(ioctl,CONFIG): error from copy_from_user\n"));
+	return(-SOLAR_EFAULT);
+      }
+      kdebug(("solar(ioctl,CONFIG): \n"));        
+      kdebug(("solar(ioctl,CONFIG): params.bswap  = %d\n", params.bswap));
+      kdebug(("solar(ioctl,CONFIG): params.wswap  = %d\n", params.wswap));
+      kdebug(("solar(ioctl,CONFIG): params.scw    = 0x%08x\n", params.scw));
+      kdebug(("solar(ioctl,CONFIG): params.ecw    = 0x%08x\n", params.ecw));
+      kdebug(("solar(ioctl,CONFIG): params.udw    = %d\n", params.udw));
+      kdebug(("solar(ioctl,CONFIG): params.clksel = %d\n", params.clksel));
+      kdebug(("solar(ioctl,CONFIG): params.clkdiv = %d\n", params.clkdiv));
+
+      for(card = 0; card < maxcards; card++)
+      {
+        data = 0;  
+        solarcard[card].regs->opctrl = (params.udw << 18) + (params.wswap << 2) + (params.bswap << 1);
+        solarcard[card].regs->bctrlw = params.scw & 0xfffffffc;
+        solarcard[card].regs->ectrlw = params.ecw & 0xfffffffc;
+        solarcard[card].regs->opfeat = (params.clksel << 31) + (params.clkdiv << 29);
+        
+        //The LFF and LDOWN interrupts are not yet supported
+        //If enabled the REQ interrupt has a fixed threshold of 32
+        //This is to reduce the IRQ frequency
+        solarcard[card].regs->intmask = INTERRUPT_THRESHOLD;
+        served[card] = 0;
+      }
+      break;
+    }
+    
+    case RESET:
+    {
+      u_int data, card;
+      
+      if (copy_from_user(&card, (void *)arg, sizeof(int)))
+      {
+	kdebug(("solar(ioctl,RESET): error from copy_from_user\n"));
+	return(-SOLAR_EFAULT);
+      }
+     
+      kdebug(("solar(ioctl,RESET): Resetting card %d\n", card));
+      data = solarcard[card].regs->opctrl;
+      data |= 0x1;
+      solarcard[card].regs->opctrl = data;
+      udelay(2);  // Delay for at least one us
+      data &= 0xfffffffe;
+      solarcard[card].regs->opctrl = data;
+
+      //reset the FIFOs
+      infifor[card] = 0;
+      infifon[card] = 0;
+
+      return(0);
+      break;
+    }
+    
+    case LINKRESET:
+    {   
+      u_int waited = 0, card;
+ 
+      if (copy_from_user(&card, (void *)arg, sizeof(int)))
+      {
+	kdebug(("solar(ioctl,LINKRESET): error from copy_from_user\n"));
+	return(-SOLAR_EFAULT);
+      }
+      
+      kdebug(("solar(ioctl,LINKRESET): Resetting link of card %d\n", card));
+      
+      solarcard[card].regs->opctrl |= 0x00020000;        // Set the URESET bit
+      udelay(2000);                             // Delay for at least two ms
+      while(solarcard[card].regs->opstat & 0x00040000)   // Now wait for LDOWN to come up again
+      {
+        waited++;
+        if (waited > 10000)
+        {
+          kdebug(("solar(ioctl,LINKRESET): card %d does not come up again\n", card));
+          solarcard[card].regs->opctrl &= 0xfffdffff;    // Reset the URESET bit
+	  return(-SOLAR_STUCK);
+        }          
+      }
+
+      // Reset the URESET bit
+      solarcard[card].regs->opctrl &= 0xfffdffff;
+      served[card] = 0;
+      return(0);
+      break;
+    }
+    
+    case LINKSTATUS:
+    {              
+      u_long flags;
+      SOLAR_status_t params;
+      
+      if (copy_from_user(&params, (void *)arg, sizeof(SOLAR_status_t)))
+      {
+	kdebug(("solar(ioctl, LINKSTATUS): error from copy_from_user\n"));
+	return(-SOLAR_EFAULT);
+      }
+      
+      if (solarcard[params.card].regs->opstat & 0x00040000)
+        params.ldown = 1;
+      else
+        params.ldown = 0;
+      
+      //We have to make sure that this code does not get interrupted. 
+      local_irq_save(flags);  //MJ: for 2.6 see p274 & p275
+      params.infifo = infifon[params.card];
+      params.reqfifo = REQFIFODEPTH - ((solarcard[params.card].regs->opstat) & 0x3f); 
+      kdebug(("solar(ioctl, LINKSTATUS): params.ldown = %d\n", params.ldown));
+      kdebug(("solar(ioctl, LINKSTATUS): params.infifo = %d\n", params.infifo));
+      kdebug(("solar(ioctl, LINKSTATUS): params.reqfifo = %d\n", params.reqfifo));
+      local_irq_restore(flags);  //MJ: for 2.6 see p274 & p275
+
+      if (copy_to_user((void *)arg, &params, sizeof(SOLAR_status_t)))
+      {
+	kdebug(("solar(ioctl, LINKSTATUS): error from copy_to_user\n"));
+	return(-SOLAR_EFAULT);
+      } 
+      
+      return(0);
+      break;
+    }
+
+    case INFO:
+    {
+      SOLAR_info_t info;
+      u_int card;
+     
+      //Initialise the array to 1 (it is not possible to disable a SOLAR card via a register)
+      for(card = 0; card < MAXCARDS; card++)
+      {
+        info.cards[card] = 0;
+        info.enabled[card] = 0;
+      }
+      
+      for(card = 0; card < maxcards; card++)
+      {
+        info.cards[card] = 1;
+        info.enabled[card] = 1;
+      }
+      
+      info.ncards = maxcards;  
+      kdebug(("solar(ioctl, INFO): maxcards = %d\n", maxcards));
+
+      if (copy_to_user((void *)arg, &info, sizeof(SOLAR_info_t)))
+      {
+	kdebug(("solar(ioctl, INFO): error from copy_to_user\n"));
+	return(-SOLAR_EFAULT);
+      }      
+      return(0);
+      break;
+    }
+    
+    case FIFOIN:
+    { 
+      u_int card;
+
+      if (copy_from_user(&card, (void *)arg, sizeof(int)) !=0)
+      {
+	kdebug(("solar(ioctl,FIFOIN): error from copy_from_user\n"));
+	return(-SOLAR_EFAULT);
+      }
+      kdebug(("solar(ioctl,FIFOIN): card = %d\n", card));
+
+      // (re)enable the interupt. Once the REQ FIFO is empty the ISR will fill it with the new requests
+      solarcard[card].regs->intmask = INTERRUPT_THRESHOLD;
+
+      return(0);
+      break;
+    }
+  }   
+  return(0);
+}
+
+
+/***************************************************/
+static void solar_vmaOpen(struct vm_area_struct *vma)
+/***************************************************/
+{
+  kdebug(("solar(solar_vmaOpen): Called\n"));
+}
+
+
+/****************************************************/
+static void solar_vmaClose(struct vm_area_struct *vma)
+/****************************************************/
+{  
+  kdebug(("solar(solar_vmaClose): Virtual address  = 0x%016lx\n",(u_long)vma->vm_start));
+  kdebug(("solar(solar_vmaClose): mmap released\n"));
+}
+
+/******************************************************************/
+static int solar_mmap(struct file *file, struct vm_area_struct *vma)
+/******************************************************************/
+{
+  u_long offset, size;
+
+  kdebug(("solar(solar_mmap): function called\n"));
+  vma->vm_flags |= VM_RESERVED;
+  vma->vm_flags |= VM_LOCKED;
+
+  kdebug(("solar(solar_mmap): vma->vm_end    = 0x%016lx\n", (u_long)vma->vm_end));
+  kdebug(("solar(solar_mmap): vma->vm_start  = 0x%016lx\n", (u_long)vma->vm_start));
+  kdebug(("solar(solar_mmap): vma->vm_offset = 0x%016lx\n", (u_long)vma->vm_pgoff << PAGE_SHIFT));
+  kdebug(("solar(solar_mmap): vma->vm_flags  = 0x%08x\n", (u_int)vma->vm_flags));
+  
+  size = vma->vm_end - vma->vm_start;
+  offset = vma->vm_pgoff << PAGE_SHIFT;  
+
+  kdebug(("solar(solar_mmap): offset = 0x%08x\n",(u_int)offset));
+  kdebug(("solar(solar_mmap): size   = 0x%08x\n",(u_int)size));
+
+  if (remap_page_range(vma, vma->vm_start, offset, size, vma->vm_page_prot))
+  {
+    kdebug(("solar(solar_mmap): remap page range failed\n"));
+    return -ENXIO;
+  }
+  
+  vma->vm_ops = &solar_vm_ops;  
+  kdebug(("solar(solar_mmap): function done\n"));
+  return(0);
+}
+
+
+/*********************************************************************************************/
+static int solar_write_procmem(struct file *file, const char *buffer, u_long count, void *data)
+/*********************************************************************************************/
+{
+  int len;
+  struct solar_proc_data_t *fb_data = (struct solar_proc_data_t *)data;
+
+  kdebug(("solar(solar_write_procmem): solar_write_procmem called\n"));
+
+  if(count > 99)
+    len = 99;
+  else
+    len = count;
+
+  if (copy_from_user(fb_data->value, buffer, len))
+  {
+    kdebug(("solar(solar_write_procmem): error from copy_from_user\n"));
+    return(-SOLAR_EFAULT);
+  }
+
+  kdebug(("solar(solar_write_procmem): len = %d\n", len));
+  fb_data->value[len - 1] = '\0';
+  kdebug(("solar(solar_write_procmem): text passed = %s\n", fb_data->value));
+
+  if (!strcmp(fb_data->value, "debug"))
+  {
+    debug = 1;
+    kdebug(("solar(solar_write_procmem): debugging enabled\n"));
+  }
+
+  if (!strcmp(fb_data->value, "nodebug"))
+  {
+    kdebug(("solar(solar_write_procmem): debugging disabled\n"));
+    debug = 0;
+  }
+  
+  return len;
+}
+
+
+/***************************************************************************************************/
+static int solar_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data)
+/***************************************************************************************************/
+{
+  u_int card, ocr, osr, imask;
+  int loop, nchars = 0;
+  static int len = 0;
+
+  kdebug(("solar(solar_read_procmem): Called with buf    = 0x%016lx\n", (u_long)buf));
+  kdebug(("solar(solar_read_procmem): Called with *start = 0x%016lx\n", (u_long)*start));
+  kdebug(("solar(solar_read_procmem): Called with offset = %d\n", (u_int)offset));
+  kdebug(("solar(solar_read_procmem): Called with count  = %d\n", count));
+
+  if (offset == 0)
+  {
+    kdebug(("exp(exp_read_procmem): Creating text....\n"));
+    len = 0;
+    len += sprintf(proc_read_text + len, "\n\n\nSOLAR driver for release %s (based on CVS tag %s)\n", RELEASE_NAME, CVSTAG);
+
+    len += sprintf(proc_read_text + len, "==================================================\n");
+    len += sprintf(proc_read_text + len, "Card|IRQ line| REQ IRQ|revision|PCI MEM addr.|wswap|bswap|LDOWN|REQ available|REQs posted|\n");
+    len += sprintf(proc_read_text + len, "----|--------|--------|--------|-------------|-----|-----|-----|-------------|-----------|\n");
+    for(card = 0; card < maxcards; card++)
+    {
+      ocr = solarcard[card].regs->opctrl;
+      osr = solarcard[card].regs->opstat;
+      imask = solarcard[card].regs->intmask;
+      len += sprintf(proc_read_text + len, "   %d|", card);
+      len += sprintf(proc_read_text + len, "      %2d|", solarcard[card].irq_line);
+      len += sprintf(proc_read_text + len, "      %2d|", (imask & 0xf)); 
+      len += sprintf(proc_read_text + len, "    0x%02x|", solarcard[card].pci_revision); 
+      len += sprintf(proc_read_text + len, "   0x%08x|", solarcard[card].pci_memaddr);
+      len += sprintf(proc_read_text + len, "  %s|", (ocr & 0x4)?"yes":" no");
+      len += sprintf(proc_read_text + len, "  %s|", (ocr & 0x2)?"yes":" no");
+      len += sprintf(proc_read_text + len, "  %s|", (osr & 0x00040000)?"yes":" no");
+      len += sprintf(proc_read_text + len, "           %2d|", (osr & 0x3f));
+      len += sprintf(proc_read_text + len, "%11d|\n", served[card]);
+    }
+
+    len += sprintf(proc_read_text + len, " \n");
+    len += sprintf(proc_read_text + len, "The command 'echo <action> > /proc/solar', executed as root,\n");
+    len += sprintf(proc_read_text + len, "allows you to interact with the driver. Possible actions are:\n");
+    len += sprintf(proc_read_text + len, "debug   -> enable debugging\n");
+    len += sprintf(proc_read_text + len, "nodebug -> disable debugging\n");
+  }
+  kdebug(("solar(solar_read_procmem): number of characters in text buffer = %d\n", len));
+
+  if (count < (len - offset))
+    nchars = count;
+  else
+    nchars = len - offset;
+  kdebug(("solar(solar_read_procmem): min nchars         = %d\n", nchars));
+  
+  if (nchars > 0)
+  {
+    for (loop = 0; loop < nchars; loop++)
+      buf[loop + (offset & (PAGE_SIZE - 1))] = proc_read_text[offset + loop];
+    *start = buf + (offset & (PAGE_SIZE - 1));
+  }
+  else
+  {
+    nchars = 0;
+    *eof = 1;
+  }
+ 
+  kdebug(("solar(solar_read_procmem): returning *start   = 0x%016lx\n", (u_long)*start));
+  kdebug(("solar(solar_read_procmem): returning nchars   = %d\n", nchars));
+  return(nchars);
+}
+
+
+/********************************************************************************/
+static irqreturn_t solar_irq_handler (int irq, void *dev_id, struct pt_regs *regs)
+/********************************************************************************/
+{
+  u_int card, for_us;
+
+  // Note: the SOLAR card(s) may have to share an interrupt line with other PCI devices.
+  // It is therefore important to exit quickly if we are not concerned with an interrupt.
+  // For now this is done by looking at the OPSTAT register. There may be a dedicated
+  // interrupt status bit in the future.
+  
+  for_us = 0;
+  for(card = 0; card < maxcards; card++)
+    for_us += solarcard[card].regs->opstat & 0x3f;
+
+  if (for_us)
+  {
+    kdebug(("solar(solar_irq_handler): Interrupt received\n"));
+    refill_req_fifo();
+    return(IRQ_HANDLED);          //MJ: for 2.6 see p273
+  }
+  else
+  {
+    kdebug(("solar(solar_irq_handler): This was not a SOLAR interrupt\n"));
+    return(IRQ_NONE);     
+  }
+}
+
+
+
+/*****************************/
+/* PCI driver functions      */
+/*****************************/
+
+/**********************************************************************************/
+static int __devinit solarProbe(struct pci_dev *dev, const struct pci_device_id *id)   //MJ: for _devinit see p31
+/**********************************************************************************/
+{
+  int i, card;
+  u_long flags;
+  u_int size;
+
+  kdebug(("solar(solarProbe): called for device: 0x%04x / 0x%04x\n", dev->vendor, dev->device));
+  kdebug(("solar(solarProbe): Bus: 0x%04x / Devfn 0x%04x\n", dev->bus->number, dev->devfn));
+
+  /* Find a new "slot" for the card */
+  card = -1;
+  for (i = 0; i < MAXCARDS; i++) 
+  {
+    if (solarcard[i].pciDevice == NULL) 
+    {
+      card = i;
+      solarcard[card].pciDevice = dev;
+      break;
+    }
+  }
+
+  if (card == -1) 
+  {
+    kdebug(("solar(solarProbe): Could not find space in the solarcard array for this card."));
+    return(-EIO);
+  }
+  
+  flags = pci_resource_flags(dev, BAR0);  //MJ: See p317
+  if ((flags & IORESOURCE_MEM) != 0) 
+  {
+    solarcard[card].pci_memaddr = pci_resource_start(dev, BAR0) & PCI_BASE_ADDRESS_MEM_MASK;        //MJ: See p317
+    size = pci_resource_end(dev, BAR0) - pci_resource_start(dev, BAR0);                             //MJ: See p317
+    kdebug(("solar(solarProbe): Mapping %d bytes at PCI address 0x%08x\n", size, solarcard[card].pci_memaddr));
+    
+    solarcard[card].regs = (T_solar_regs *)ioremap(solarcard[card].pci_memaddr, size);
+    if (solarcard[card].regs == NULL)
+    {
+      kdebug(("solar(solarProbe): error from ioremap for solarcard[%d].regs\n", card));
+      solarcard[card].pciDevice = NULL;
+      return(-SOLAR_IOREMAP);
+    }
+    kdebug(("solar(solarProbe): solarcard[%d].regs is at 0x%016lx\n", card, (u_long)solarcard[card].regs));
+  }
+  else 
+  {
+    kdebug(("solar(solarProbe): Bar 0 is not a memory resource"));
+    return(-SOLAR_EIO);
+  }  
+
+  // get revision directly from the SOLAR
+  pci_read_config_byte(solarcard[card].pciDevice, PCI_REVISION_ID, &solarcard[card].pci_revision);
+  kdebug(("solar(solarProbe): revision = %x \n", solarcard[card].pci_revision));
+  if (solarcard[card].pci_revision < MINREV)
+  {
+    kdebug(("solar(solarProbe): Illegal SOLAR Revision\n"));
+    return(-SOLAR_ILLREV);
+  }  
+  
+  pci_read_config_byte(solarcard[card].pciDevice, PCI_INTERRUPT_LINE, &solarcard[card].irq_line);
+  kdebug(("solar(solarProbe): interrupt line = %d \n", solarcard[card].irq_line));
+
+  //Several solar cards may use the same interrupt but we want to install the driver only once
+  if (irqlist[solarcard[card].irq_line] == 0) 
+  {
+    if(request_irq(solarcard[card].irq_line, solar_irq_handler, SA_SHIRQ, "solar", dev))	
+    {
+      kdebug(("solar(solarProbe): request_irq failed on IRQ = %d\n", solarcard[card].irq_line));
+      return(-SOLAR_REQIRQ);
+    }
+    irqlist[solarcard[card].irq_line]++;
+    kdebug(("solar(solarProbe): Interrupt %d registered\n", solarcard[card].irq_line)); 
+  }  
+ 
+  maxcards++;
+  return(0);
+}
+
+
+/******************************************/
+static void solarRemove(struct pci_dev *dev) 
+/******************************************/
+{
+  int i, card;
+
+  /* Find the "slot" of the card */
+  card = -1;
+  for (i = 0; i < MAXCARDS; i++) 
+  {
+    if (solarcard[i].pciDevice == dev) 
+    {
+      card = i;
+      solarcard[i].pciDevice = 0;
+      break;
+    }  
+  }
+  
+  if (card == -1) 
+  {
+    kdebug(("solar(solarRemove): Could not find device to remove."));
+    return;
+  }
+ 
+  if (--irqlist[solarcard[card].irq_line] == 0)
+  {
+    kdebug(("solar(solarRemove): removing handler for interrupt %d", solarcard[card].irq_line));
+    free_irq(solarcard[card].irq_line, dev);	
+  }
+  
+  iounmap((void *)solarcard[card].regs);
+  kdebug(("solar(solarRemove): Card %d removed", card));
+  maxcards--;
+}
+
+
+
+
+/*********************/
+/* Service functions */
+/*********************/
+
+/**********************************************************/
+static int in_fifo_pop(int card, u_int *data1, u_int *data2)
+/**********************************************************/
+{
+  int index;
+  
+  if (infifon[card] == 0)
+  {
+    kdebug(("solar(in_fifo_pop): The IN FIFO is empty\n"));
+    return(-1);
+  }
+  
+  index = IN_INDEX(card, infifor[card]);
+  *data1 = infifo[index];
+  *data2 = infifo[index + 1];
+  kdebug(("solar(in_fifo_pop): index=%d data1=0x%08x  data2=0x%08x\n", index, *data1, *data2));
+
+  infifor[card]++;
+
+  if (infifor[card] > (MAXINFIFO - 1))
+    infifor[card] = 0;
+
+  infifon[card]--;
+
+  return(0);
+}
+
+
+/*******************************/
+static void refill_req_fifo(void)
+/*******************************/
+{
+  u_int data1, data2, loop, card, numfree, numreq, numcopy;
+
+  // We got here because one of the cards generated an interrupt.
+  // As other cards may also have space in ther REQ FIFOs we process all of them
+  // to reduce the interrupt frequency.
+
+  for(card = 0; card < maxcards; card++)
+  {       
+    // Is there space in the REQ FIFO?
+    numfree = (solarcard[card].regs->opstat) & 0x3f;
+
+    // Have we got new requests?
+    numreq = infifon[card];
+    kdebug(("solar(refill_req_fifo): numfree=%d  numreq=%d\n", numfree, numreq));
+
+    if (numfree && !numreq)
+    {
+      //We have to disable the interrupt as we can not clear it by filling the REQ FIFO
+      kdebug(("solar(refill_req_fifo): Disabling interrupt\n"));
+      solarcard[card].regs->intmask = 0;
+    }
+    else
+    {
+      if (numfree < numreq)
+        numcopy = numfree;
+      else
+        numcopy = numreq;
+      kdebug(("solar(refill_req_fifo): numcopy=%d \n", numcopy));
+
+      for (loop = 0; loop < numcopy; loop++)
+      {
+        // As we have checked the number of entries in the IN FIFO we do not have to worry about errors
+        in_fifo_pop(card, &data1, &data2);
+        solarcard[card].regs->reqfifo1 = data1;
+        solarcard[card].regs->reqfifo2 = data2;
+        served[card]++;
+        kdebug(("solar(refill_req_fifo): data1=0x%08x  data2=0x%08x\n", data1, data2));
+      }
+    }
+  }
+}
+
+
diff --git a/cmt/vme_rcc.c b/cmt/vme_rcc.c
new file mode 100644
index 0000000000000000000000000000000000000000..3dbe3920c6679de03f767a012d49ccd37db37c63
--- /dev/null
+++ b/cmt/vme_rcc.c
@@ -0,0 +1,2874 @@
+// $Id$
+/************************************************************************/
+/*									*/
+/* File: vme_rcc_driver.c						*/
+/*									*/
+/* RCC VMEbus driver							*/
+/*									*/
+/* 25. Oct. 01  MAJO  created						*/
+/* 01. Nov. 01  JOP  prepare for interrupts				*/
+/* 26. Nov. 01  JOP  bus errors						*/
+/* 15. Jan. 02  JOP  clean link & berr structures in release		*/
+/* 26. Feb. 02  JOP  update to Linux 2.4				*/
+/* 17. Apr. 02  JOP  RORA interrupts					*/
+/* 27. May. 02  JOP  modify API for IRs					*/
+/*									*/
+/************ C 2005 - The software with that certain something *********/
+
+
+//MJ - FIXME - We have to decide what to do with the pid
+
+
+/************************************************************************/
+/*NOTES:								*/
+/*- This driver should work on kernels from 2.6.9 onwards		*/
+/*- This driver requires a UniverseII. Universe I is not supported.	*/
+/*- The proper way of accessing the Universe would be through 		*/
+/*  writel/readl functions. For a first implementation I have chosen to */
+/*  use pointer based I/O as this simplifies the re-use of old code	*/ 
+/************************************************************************/
+
+#include <linux/init.h>           //MJ: for 2.6, p30
+#include <linux/module.h>
+#include <linux/moduleparam.h>    //MJ: for 2.6, p30
+#include <linux/kernel.h>
+#include <linux/stat.h>           //MJ: for 2.6, e.g. for module_param
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>           //e.g. for cdev_alloc
+#include <linux/interrupt.h>      //e.g. for request_irq
+//MJ: obsolete? #include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include "vme_rcc/universe.h"
+#include "vme_rcc/cct_berr.h"
+#include "vme_rcc/vme_rcc_driver.h"
+
+
+/*********/
+/*Globals*/
+/*********/
+static int berr_interlock = 0, vmetab_ok = 0, debug = 0, errorlog = 1;
+static int dma_sem_busy = 0, sem_positive[2] = {0,0};
+static char *proc_read_text;
+static u_char vme_irq_line;
+static u_int maxcard = 0;
+static u_int board_type, dma_to;
+static u_int irq_level_table[7];
+static u_int irq_sysfail;
+static u_int berr_int_count = 0;
+static u_int txfifo_wait_1;		        // # waits on TXFIFO empty
+static u_int txfifo_wait_2;
+static all_mstslvmap_t stmap;
+static VME_DMAhandle_t dma_donelist[VME_DMADONESIZE];
+static master_map_t master_map_table;
+static link_dsc_table_t link_table;		// link descriptors
+static berr_proc_table_t berr_proc_table;	// processes with BERR handling
+static sysfail_link_dsc_t sysfail_link;	        // processes with SYSFAIL handling
+static berr_dsc_t berr_dsc;			// last BERR
+#if defined (VMEDEBUG) || defined (INTDEBUG)
+  static u_int *marker;
+#endif
+static InterruptCounters_t Interrupt_counters = {0,0,0,0,0,0,0, {0,0,0,0,0,0,0}, 0};
+static dev_t major_minor;
+static universe_regs_t *uni;
+static volatile VME_DMAhandle_t current_dmahandle;
+static struct vme_proc_data_t vme_proc_data;
+static struct proc_dir_entry *vme_rcc_file;
+static struct timer_list dma_timer;
+static struct semaphore dma_semaphore;
+static struct cdev *vme_rcc_cdev;
+static struct pci_dev *universedev;
+
+/************/
+/*Prototypes*/
+/************/
+static irqreturn_t universe_irq_handler (int irq, void *dev_id, struct pt_regs *regs);
+static void vme_rcc_Remove(struct pci_dev *dev); 
+static void vme_rcc_vmaOpen(struct vm_area_struct *vma);
+static void vme_rcc_cleanup(void);
+static int __devinit vme_rcc_Probe(struct pci_dev *dev, const struct pci_device_id *id);
+static int vme_rcc_init(void);
+
+/***************************************************************/
+/* Use /sbin/modinfo <module name> to extract this information */
+/***************************************************************/
+MODULE_DESCRIPTION("VMEbus driver for the Tundra Universe on SBCs from CCT");
+MODULE_AUTHOR("Markus Joos & Jorgen Petersen, CERN/PH");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("Private: Contact markus.joos@cern.ch");
+module_param (debug, int, S_IRUGO | S_IWUSR);  //MJ: for 2.6 p37
+MODULE_PARM_DESC(debug, "1 = enable debugging   0 = disable debugging");
+module_param (errorlog, int, S_IRUGO | S_IWUSR);  //MJ: for 2.6 p37
+MODULE_PARM_DESC(errorlog, "1 = enable error logging   0 = disable error logging");
+
+
+//MJ: Not actually required. Just for kdebug
+struct vm_operations_struct vme_rcc_vm_ops =
+{       
+  .close = vme_rcc_vmaClose,      
+  .open  = vme_rcc_vmaOpen,      //MJ: Note the comma at the end of the list!
+};
+
+static struct file_operations fops = 
+{
+  .owner   = THIS_MODULE,
+  .ioctl   = vme_rcc_ioctl,
+  .open    = vme_rcc_open,    
+  .mmap    = vme_rcc_mmap,
+  .release = vme_rcc_release,
+};
+
+// PCI driver structures. See p311
+static struct pci_device_id vme_rcc_Ids[] = 
+{
+  {PCI_DEVICE(PCI_VENDOR_ID_TUNDRA, PCI_DEVICE_ID_TUNDRA_CA91C042)},      
+  {0,},
+};
+
+//PCI operations
+static struct pci_driver vme_rcc_PciDriver = 
+{
+  .name     = "vme_rcc",
+  .id_table = vme_rcc_Ids,
+  .probe    = vme_rcc_Probe,
+  .remove   = vme_rcc_Remove,
+};
+
+
+/****************************/
+/* Standard driver function */
+/****************************/
+
+/***************************/
+static int vme_rcc_init(void)
+/***************************/
+{
+  u_int i;
+  int result, loop;
+  u_char reg;
+ 
+  kerror(("vme_rcc(vme_rcc_init): Error debug is active\n"))
+
+  if(alloc_chrdev_region (&major_minor, 0, 1, "vme_rcc")) //MJ: for 2.6 p45
+  {
+    kdebug(("vme_rcc(vme_rcc_init): failed to obtain device numbers\n"));
+    result = -EIO;
+    goto fail1;
+  }
+  
+  proc_read_text = (char *)kmalloc(MAX_PROC_TEXT_SIZE, GFP_KERNEL);
+  if (proc_read_text == NULL)
+  {
+    kdebug(("vme_rcc(vme_rcc_init): error from kmalloc\n"));
+    result = -ENOMEM;
+    goto fail2;
+  }
+
+  vme_rcc_file = create_proc_entry("vme_rcc", 0644, NULL);
+  if (vme_rcc_file == NULL)
+  {
+    kdebug(("vme_rcc(vme_rcc_init): error from call to create_proc_entry\n"));
+    result = -EFAULT;
+    goto fail3;
+  }
+
+  strcpy(vme_proc_data.name, "vme_rcc");
+  strcpy(vme_proc_data.value, "vme_rcc");
+  vme_rcc_file->data = &vme_proc_data;
+  vme_rcc_file->read_proc = vme_rcc_read_procmem;
+  vme_rcc_file->write_proc = vme_rcc_write_procmem;
+  vme_rcc_file->owner = THIS_MODULE;
+
+  kdebug(("vme_rcc(vme_rcc_init): registering PCIDriver\n"));
+  result = pci_register_driver(&vme_rcc_PciDriver);  //MJ: See P312
+  if (result == 0)
+  {
+    kerror(("vme_rcc(vme_rcc_init): ERROR! no UNIVERSE chip found!\n"));
+    result = -EIO;
+    goto fail3;
+  }
+  else 
+  {
+    kerror(("vme_rcc(vme_rcc_init): Found %d UNIVERSE chips!\n", result));
+  }
+  
+  // Read the board identification
+  outb(BID1, CMOSA);
+  reg = inb(CMOSD);
+  kdebug(("vme_rcc(vme_rcc_init): BID1 = 0x%02x\n", reg))
+  
+  if (!(reg & 0x80))
+  {
+    kerror(("vme_rcc(vme_rcc_init): Unable to determine board type\n"))
+    result = -VME_UNKNOWN_BOARD;
+    goto fail3;
+  }
+
+  outb(BID2, CMOSA);
+  reg = inb(CMOSD);
+  kdebug(("vme_rcc(vme_rcc_init): BID2 = 0x%02x\n", reg))
+
+  reg &= 0x1f;  // Mask board ID bits
+  board_type = VP_UNKNOWN;
+       if (reg == 2)  {board_type = VP_PSE; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-PSE\n"));}
+  else if (reg == 3)  {board_type = VP_PSE; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-PSE\n"));}
+  else if (reg == 4)  {board_type = VP_PSE; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-PSE\n"));}
+  else if (reg == 5)  {board_type = VP_PMC; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-PMC\n"));}
+  else if (reg == 6)  {board_type = VP_CP1; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-CP1\n"));}
+  else if (reg == 7)  {board_type = VP_100; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-100\n"));}
+  else if (reg == 8)  {board_type = VP_110; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-110\n"));}
+  else if (reg == 11) {board_type = VP_315; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-315\n"));}
+  else if (reg == 13) {board_type = VP_317; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-317\n"));}
+  else if (reg == 14) {board_type = VP_325; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-325\n"));}
+
+  if (!board_type)
+  {
+    kerror(("vme_rcc(vme_rcc_init): Unable to determine board type(2). Board seems to be of type %d\n", reg))
+    return(-VME_UNKNOWN_BOARD);
+  } 
+      
+  if (board_type == VP_100 || board_type == VP_110 || board_type == VP_315 || board_type == VP_317 || board_type == VP_325)
+    outb(0x1, BERR_VP100);                          // Enable VMEbus BERR capturing
+
+  init_cct_berr();	                            // Enable CCT bus errors
+  sema_init(&dma_semaphore, 1);                     // Initialize the DMA semaphore
+  sema_init(&link_table.link_dsc_sem, 1);           // Initialize the Link Descriptor protection semaphore
+  init_timer(&dma_timer);                           // Initialise the watchdog timer for DMA transactions
+  
+  for (loop = 0; loop < VME_DMADONESIZE; loop++)    // Initialize the list of completed DMA transaction
+  {
+    dma_donelist[loop].handle = 0xffffffff;
+    dma_donelist[loop].pid = 0;
+  }
+  
+  for (loop = 0; loop < VME_MAX_MASTERMAP; loop++)  //Initialize the list of active master mappings
+  {
+    master_map_table.map[loop].pid = 0;
+    master_map_table.map[loop].vaddr = 0;
+  }
+  sema_init(&master_map_table.sem, 1);
+  
+  for (i = 0; i < VME_VCTSIZE; i++)                 // Reset link and BERR tables
+  {
+    link_table.link_dsc[i].pid = 0;
+    link_table.link_dsc[i].vct = 0;
+    link_table.link_dsc[i].lvl = 0;
+    link_table.link_dsc[i].pending = 0;
+    link_table.link_dsc[i].total_count = 0;
+    link_table.link_dsc[i].group_ptr = 0;
+    sema_init(&link_table.link_dsc[i].sem, 0);
+    link_table.link_dsc[i].sig = 0;
+  }
+
+  for (i = 0; i < VME_MAX_BERR_PROCS; i++)
+  {
+    berr_proc_table.berr_link_dsc[i].pid =  0;
+    berr_proc_table.berr_link_dsc[i].sig =  0;
+    sema_init(&berr_proc_table.proc_sem, 1);
+  }
+
+  berr_dsc.vmeadd = 0;
+  berr_dsc.am = 0;
+  berr_dsc.iack = 0;
+  berr_dsc.lword = 0;
+  berr_dsc.ds0 = 0;
+  berr_dsc.ds1 = 0;
+  berr_dsc.wr = 0;
+  berr_dsc.multiple = 0;
+  berr_dsc.flag = 0;
+
+  sysfail_link.pid =  0;
+  sysfail_link.sig =  0;
+  sema_init(&sysfail_link.proc_sem, 1);
+
+  vme_rcc_cdev = (struct cdev *)cdev_alloc();      //MJ: for 2.6 p55
+  vme_rcc_cdev->ops = &fops;
+  result = cdev_add(vme_rcc_cdev, major_minor, 1);  //MJ: for 2.6 p56
+  if (result)
+  {
+    kdebug(("vme_rcc(vme_rcc_init): error from call to cdev_add.\n"));
+    goto fail4;
+  }
+  
+  kdebug(("vme_rcc(vme_rcc_init): driver loaded; major device number = %d\n", MAJOR(major_minor)));
+  return(0);
+
+  fail4:
+    remove_proc_entry("vme_rcc", NULL);
+
+  fail3:
+    kfree(proc_read_text);
+
+  fail2:
+    unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45
+
+  fail1:
+    return(result);
+}
+
+
+/********************************/
+static void vme_rcc_cleanup(void)
+/********************************/
+{
+  pci_unregister_driver(&vme_rcc_PciDriver);
+
+  cdev_del(vme_rcc_cdev);                     //MJ: for 2.6 p56
+  remove_proc_entry("vme_rcc", NULL);
+  kfree(proc_read_text);
+  unregister_chrdev_region(major_minor, 1);  //MJ: for 2.6 p45
+  kdebug(("vme_rcc(vme_rcc_cleanup): driver removed\n"))
+}
+
+
+module_init(vme_rcc_init);    //MJ: for 2.6 p16
+module_exit(vme_rcc_cleanup); //MJ: for 2.6 p16
+
+
+/************************************************************/
+static int vme_rcc_open(struct inode *ino, struct file *filep)
+/************************************************************/
+{
+  int vct, berr, loop;
+  private_data_t *pdata;
+
+  pdata = (private_data_t *)kmalloc(sizeof(private_data_t), GFP_KERNEL);
+  if (pdata == NULL)
+  {
+    kerror(("vme_rcc(open): error from kmalloc\n"))
+    return(-VME_EFAULT);
+  }
+
+  kdebug(("vme_rcc(open): pointer to link flags in file descriptor = %x\n", (u_int)&pdata->link_dsc_flag[0]))
+  for (vct = 0; vct < VME_VCTSIZE; vct++)
+    pdata->link_dsc_flag[vct] = 0;
+
+  kdebug(("vme_rcc(open): pointer to berr flags in file descriptor = %x\n", (u_int)&pdata->berr_dsc_flag[0]))
+  for (berr = 0; berr < VME_MAX_BERR_PROCS; berr++)
+    pdata->berr_dsc_flag[berr] = 0;
+
+  pdata->sysfail_dsc_flag = 0;
+  
+  for (loop = 0; loop < VME_DMADONESIZE; loop++)
+    pdata->dma[loop] = 0;
+  
+  for (loop = 0; loop < VME_MAX_MASTERMAP; loop++)
+    pdata->mastermap[loop] = 0;
+  
+  filep->private_data = pdata;
+  return(0);
+}
+
+
+/***************************************************************/
+static int vme_rcc_release(struct inode *ino, struct file *filep)
+/***************************************************************/
+{
+  int vct, no_freed, isave, free_flag, loop;
+  u_int i;
+  VME_IntHandle_t *group_ptr;
+  VME_IntHandle_t *group_ptr_save[VME_VCTSIZE];
+  private_data_t *pdata;
+
+  pdata = (private_data_t *)filep->private_data;
+
+  down(&link_table.link_dsc_sem);
+
+  no_freed = 0;
+  group_ptr_save[0] = (VME_IntHandle_t*)0xffffffff;
+
+  kdebug(("vme_rcc(release): pid = %d\n", current->pid))
+
+  for (vct = 0; vct < VME_VCTSIZE; vct++)
+  {
+    if (pdata->link_dsc_flag[vct] == 1)
+    {
+      if (link_table.link_dsc[vct].group_ptr)	// grouped vector
+      {
+        group_ptr = link_table.link_dsc[vct].group_ptr;
+        kdebug(("vme_rcc(release): vector = %d, group_ptr = 0x%p\n", vct, group_ptr))
+        free_flag = 1;
+        for (isave = 0; isave <= no_freed; isave++)
+        {
+          if (group_ptr == group_ptr_save[isave])
+          {
+            free_flag = 0;	// already freed
+            break;
+          }
+        }
+
+        kdebug(("vme_rcc(release): free_flag = %d\n", free_flag))
+        if (free_flag)
+        {
+          kdebug(("vme_rcc(release): group_ptr = 0x%p is now kfree'd\n", group_ptr))
+          kfree(group_ptr);                            //MJ: where is the corresponding kmalloc?
+          group_ptr_save[no_freed] = group_ptr;
+          no_freed++;
+          kdebug(("vme_rcc(release): no_freed = %d \n", no_freed))
+        }
+      }
+
+      link_table.link_dsc[vct].pid = 0;
+      link_table.link_dsc[vct].vct = 0;
+      link_table.link_dsc[vct].lvl = 0;
+      link_table.link_dsc[vct].pending = 0;
+      link_table.link_dsc[vct].total_count = 0;
+      link_table.link_dsc[vct].group_ptr = 0;
+      sema_init(&link_table.link_dsc[vct].sem,0);
+      link_table.link_dsc[vct].sig = 0;
+
+      pdata->link_dsc_flag[vct] = 0;
+    }
+  }
+  up(&link_table.link_dsc_sem);
+
+  // and now the BERR links
+  down(&berr_proc_table.proc_sem);
+  for (i = 0; i < VME_MAX_BERR_PROCS; i++)
+  {
+    if (pdata->berr_dsc_flag[i] == 1)
+    {
+      kdebug(("vme_rcc(release): clearing BERR entry %d \n", i))
+      berr_proc_table.berr_link_dsc[i].pid = 0;
+      berr_proc_table.berr_link_dsc[i].sig = 0;
+      pdata->link_dsc_flag[i] = 0;
+    }
+  }
+  up(&berr_proc_table.proc_sem);
+  
+  down(&sysfail_link.proc_sem);
+  if (pdata->sysfail_dsc_flag)
+  {
+    kdebug(("vme_rcc(release): clearing SYSFAIL entry that has been left open by process %d\n", sysfail_link.pid))
+    sysfail_link.pid = 0;
+    sysfail_link.sig = 0;
+    pdata->sysfail_dsc_flag = 0;
+  }
+  up(&sysfail_link.proc_sem);  
+
+  // Release orphaned master mappings
+  down(&master_map_table.sem);
+  for (loop = 0; loop < VME_MAX_MASTERMAP; loop++)
+  {
+    if (pdata->mastermap[loop])
+    {
+      kdebug(("vme_rcc(release): Unmapping orphaned virtual address 0x%08x for process %d\n", master_map_table.map[loop].vaddr, master_map_table.map[loop].pid))
+      iounmap((void *)master_map_table.map[loop].vaddr);
+      master_map_table.map[loop].vaddr = 0;
+      master_map_table.map[loop].pid = 0;
+      pdata->mastermap[loop] = 0;
+    }
+  }
+  up(&master_map_table.sem);
+
+  // and finally the DMA done list
+  for (loop = 0; loop < VME_DMADONESIZE; loop++)
+  {
+    if (pdata->dma[loop])
+    {
+      kdebug(("vme_rcc(release): entry %d in dma_done list released (was owned by process %d)\n", loop, dma_donelist[loop].pid))
+      dma_donelist[loop].pid = 0;
+      dma_donelist[loop].handle = 0xffffffff;
+      pdata->dma[loop] = 0;
+    }
+  }
+
+  kfree(pdata);
+  kdebug(("vme_rcc(release): kfreed file private data @ %x\n", (u_int)pdata))
+  return(0);
+}
+
+
+/**************************************************************************************/
+static int vme_rcc_ioctl(struct inode *inode, struct file *filep, u_int cmd, u_long arg)
+/**************************************************************************************/
+{
+  private_data_t *pdata;
+
+  kdebug(("vme_rcc(ioctl): filep at 0x%08x\n", (u_int)filep))
+  pdata = (private_data_t *)filep->private_data;
+
+  switch (cmd)
+  {    
+    case VMEMASTERMAP:
+    {
+      int ok2, ok = 0, loop, loop2;
+      u_int vaddr;
+      
+      if (copy_from_user(&pdata->mm, (void *)arg, sizeof(VME_MasterMapInt_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMEMASTERMAP): error from copy_from_user\n"))
+	return(-VME_EFAULT);
+      }
+      kdebug(("vme_rcc(ioctl,VMEMASTERMAP): mm.in.vmebus_address   = 0x%08x\n", pdata->mm.in.vmebus_address))
+      kdebug(("vme_rcc(ioctl,VMEMASTERMAP): mm.in.window_size      = 0x%08x\n", pdata->mm.in.window_size))
+      kdebug(("vme_rcc(ioctl,VMEMASTERMAP): mm.in.address_modifier = 0x%08x\n", pdata->mm.in.address_modifier))
+      kdebug(("vme_rcc(ioctl,VMEMASTERMAP): mm.in.options          = 0x%08x\n", pdata->mm.in.options))
+
+      for (loop = 0; loop < 8; loop++)
+      {
+	if (pdata->mm.in.vmebus_address >= stmap.master[loop].vbase && 
+           (pdata->mm.in.vmebus_address + pdata->mm.in.window_size) <= stmap.master[loop].vtop && 
+	   stmap.master[loop].enab == 1 && 
+	   stmap.master[loop].am == pdata->mm.in.address_modifier &&
+           stmap.master[loop].options == pdata->mm.in.options)
+        {
+          kdebug(("vme_rcc(ioctl,VMEMASTERMAP): Decoder %d matches parameters\n", loop))
+          pdata->mm.pci_address = stmap.master[loop].pbase + (pdata->mm.in.vmebus_address-stmap.master[loop].vbase); 
+          kdebug(("vme_rcc(ioctl,VMEMASTERMAP): PCI address = 0x%08x\n", pdata->mm.pci_address))
+
+          //find a free slot in the master map table
+          down(&master_map_table.sem);
+          ok2 = 0;
+          for (loop2 = 0; loop2 < VME_MAX_MASTERMAP; loop2++)
+          {
+            if (master_map_table.map[loop2].pid == 0)
+            {
+              kdebug(("vme_rcc(ioctl,VMEMASTERMAP): Will use entry %d in master_map_table\n", loop2))
+              ok2 = 1;
+              break;
+            }
+          }
+          if (!ok2)
+          {
+            kdebug(("vme_rcc(ioctl,VMEMASTERMAP): master_map_table is full\n"))
+            return(-VME_NOSTATMAP2);
+          }
+
+	  vaddr = (u_int)ioremap(pdata->mm.pci_address, pdata->mm.in.window_size);
+          if (!vaddr)
+          {
+            kerror(("vme_rcc(ioctl,VMEMASTERMAP): error from ioremap\n"))
+            up(&master_map_table.sem);
+            return(-VME_EFAULT);
+          }
+          master_map_table.map[loop2].vaddr = vaddr;
+          master_map_table.map[loop2].pid = current->pid;
+          pdata->mastermap[loop2] = 1;
+          up(&master_map_table.sem);
+
+          kdebug(("vme_rcc(ioctl,VMEMASTERMAP): kernel virtual address = 0x%08x   pid = %d\n", vaddr, current->pid))
+          pdata->mm.kvirt_address = vaddr;
+	  ok = 1;
+	  break;
+        }
+        else
+        {
+          kdebug(("vme_rcc(ioctl,VMEMASTERMAP): stmap.master[%d].vbase   = 0x%08x\n", loop, stmap.master[loop].vbase))
+          kdebug(("vme_rcc(ioctl,VMEMASTERMAP): stmap.master[%d].vtop    = 0x%08x\n", loop, stmap.master[loop].vtop))
+          kdebug(("vme_rcc(ioctl,VMEMASTERMAP): stmap.master[%d].enab    = %d\n", loop, stmap.master[loop].enab))
+          kdebug(("vme_rcc(ioctl,VMEMASTERMAP): stmap.master[%d].am      = %d\n", loop, stmap.master[loop].am))
+          kdebug(("vme_rcc(ioctl,VMEMASTERMAP): stmap.master[%d].options = 0x%08x\n", loop, stmap.master[loop].options))
+        }
+      }
+      if (!ok)
+	return(-VME_NOSTATMAP);
+
+      if (copy_to_user((void *)arg, &pdata->mm, sizeof(VME_MasterMapInt_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMEMASTERMAP): error from copy_to_user\n"))
+	return(-VME_EFAULT);
+      }
+      break;
+    }
+
+    case VMEMASTERUNMAP:
+    {    
+      u_int ok, mm, loop;
+
+      if (copy_from_user(&mm, (void *)arg, sizeof(u_int)))
+      {
+	kerror(("vme_rcc(ioctl,VMEMASTERUNMAP): error from copy_from_user\n"))
+	return(-VME_EFAULT);
+      }
+
+      down(&master_map_table.sem);
+      ok = 0;
+      for (loop = 0; loop < VME_MAX_MASTERMAP; loop++)
+      {
+	kdebug(("vme_rcc(ioctl,VMEMASTERUNMAP): master_map_table.map[%d].vaddr = 0x%08x  <-> mm = 0x%08x\n", loop, master_map_table.map[loop].vaddr, mm));
+	kdebug(("vme_rcc(ioctl,VMEMASTERUNMAP): master_map_table.map[%d].pid = %d <-> current->pid = %d\n", loop, master_map_table.map[loop].pid, current->pid));
+      
+        if (master_map_table.map[loop].vaddr == mm) 
+        {
+          kdebug(("vme_rcc(ioctl,VMEMASTERUNMAP): Entry %d in master_map_table matches\n", loop))
+          ok = 1;
+          iounmap((void *)mm);
+          kdebug(("vme_rcc(ioctl,VMEMASTERUNMAP): unmapping address 0x%08x\n", mm))
+          master_map_table.map[loop].vaddr = 0;
+          master_map_table.map[loop].pid = 0;
+          pdata->mastermap[loop] = 0;
+          break;
+        }
+      }
+      up(&master_map_table.sem);
+  
+      if (!ok)
+      {
+        kdebug(("vme_rcc(ioctl,VMEMASTERUNMAP): No entry in master_map_table matched for address 0x%08x\n", mm))
+        return(-VME_IOUNMAP);
+      }
+      break;
+    }
+
+    case VMEMASTERMAPDUMP:
+    {
+      char *buf;
+      int len, loop;
+
+      buf = (char *)kmalloc(TEXT_SIZE1, GFP_KERNEL);
+      if (buf == NULL)
+      {
+        kerror(("vme_rcc(ioctl,VMEMASTERMAPDUMP): error from kmalloc\n"))
+        return(-VME_KMALLOC);
+      }
+
+      len = 0;
+      len += sprintf(buf + len, "Master mapping:\n");
+      len += sprintf(buf + len, "LSI  VME address range  PCI address range   EN   WP   AM\n");
+      for (loop = 0; loop < 8; loop++)
+	len += sprintf(buf + len, "%d    %08x-%08x  %08x-%08x    %d    %d    %d\n"
+        	      ,loop
+	 	      ,stmap.master[loop].vbase
+		      ,stmap.master[loop].vtop
+		      ,stmap.master[loop].pbase
+		      ,stmap.master[loop].ptop
+		      ,stmap.master[loop].enab
+		      ,stmap.master[loop].wp
+		      ,stmap.master[loop].am);
+      if (copy_to_user((void *)arg, buf, TEXT_SIZE1 * sizeof(char)))
+      {
+	kerror(("vme_rcc(ioctl,VMEMASTERMAPDUMP): error from copy_to_user\n"))
+	return(-VME_EFAULT);
+      }
+            
+      kfree(buf);
+      break;
+    }
+     
+    case VMESYSFAILLINK:
+    {
+      down(&sysfail_link.proc_sem);
+
+      if (sysfail_link.pid)  // Already linked by any other process?
+      {
+        up(&sysfail_link.proc_sem);
+        return(-VME_SYSFAILTBLFULL);
+      }
+
+      sysfail_link.pid = current->pid;
+      pdata->sysfail_dsc_flag = 1;
+      sema_init(&sysfail_link.sem, 0);     // initialise semaphore count
+      up(&sysfail_link.proc_sem);
+      break;
+    }
+
+    case VMESYSFAILUNLINK:
+    {
+      down(&sysfail_link.proc_sem);
+
+      if (pdata->sysfail_dsc_flag == 0)          //Check if the current process (group) has linked it
+      {
+        up(&sysfail_link.proc_sem);
+        return(-VME_SYSFAILTBLNOTLINKED);
+      }
+
+      sysfail_link.pid = 0;
+      sysfail_link.sig = 0;
+      pdata->sysfail_dsc_flag = 0;
+      up(&sysfail_link.proc_sem);
+
+      break;
+    }
+
+    case VMESYSFAILREGISTERSIGNAL:
+    {
+      if (copy_from_user(&pdata->VME_SysfailRegSig, (void *)arg, sizeof(VME_RegSig_t)))
+      {
+        kerror(("vme_rcc(ioctl,VMESYSFAILREGISTERSIGNAL): error from copy_from_user\n"))
+        return(-VME_EFAULT);
+      }
+      kdebug(("vme_rcc(ioctl,VMESYSFAILREGISTERSIGNAL): signal # = %d\n", pdata->VME_SysfailRegSig.signum))
+      down(&sysfail_link.proc_sem);
+
+      if (sysfail_link.pid != current->pid)
+      {
+        up(&sysfail_link.proc_sem);
+        return(-VME_SYSFAILNOTLINKED);
+      }
+      if (pdata->VME_SysfailRegSig.signum)  // register
+        sysfail_link.sig = pdata->VME_SysfailRegSig.signum;
+      else                                  // unregister
+        sysfail_link.sig = 0;
+      up(&sysfail_link.proc_sem);
+      break;
+    }
+    
+    case VMESYSFAILWAIT:
+    {
+      struct timer_list int_timer;
+      int use_timer = 0, result = 0;
+
+      if (copy_from_user(&pdata->VME_WaitInt, (void *)arg, sizeof(VME_WaitInt_t)))
+      {
+        kerror(("vme_rcc(ioctl,VMESYSFAILWAIT): error from copy_from_user\n"))
+ 	return(-VME_EFAULT);
+      }
+      kdebug(("vme_rcc(ioctl,VMESYSFAILWAIT): timeout = %d\n", pdata->VME_WaitInt.timeout))
+
+      if (pdata->VME_WaitInt.timeout > 0) 
+      {
+        init_timer(&int_timer);
+        int_timer.function = vme_intSysfailTimeout;
+        int_timer.data = (u_long)&pdata->VME_WaitInt;
+        int_timer.expires = jiffies + pdata->VME_WaitInt.timeout;
+        add_timer(&int_timer);
+        use_timer = 1;
+      }
+
+      if (pdata->VME_WaitInt.timeout) 		// Wait : with timeout or not
+      {
+        kdebug(("vme_rcc(ioctl,VMESYSFAILWAIT): Before Down, semaphore = %d\n", sysfail_link.sem.count.counter))
+        down_interruptible(&sysfail_link.sem);	// wait ..
+        if (signal_pending(current))
+        {					// interrupted system call
+          kdebug(("vme_rcc(ioctl,VMESYSFAILWAIT): Interrupted by signal\n"))
+          result = -VME_INTBYSIGNAL;		// or ERESTARTSYS ?
+        }
+        else if ((use_timer == 1) && (pdata->VME_WaitInt.timeout == 0))
+          result = -VME_TIMEOUT;
+      }
+      
+      if (use_timer == 1)
+        del_timer(&int_timer);	// OK also if timer ran out
+
+      if (copy_to_user((void *)arg, &pdata->VME_WaitInt, sizeof(VME_WaitInt_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMESYSFAILWAIT): error from copy_to_user\n"))
+        return(-VME_EFAULT);
+      }
+
+      return (result);
+      break;	// dummy ..
+    }
+
+    // SYSFAIL is latched in lint_stat (I believe). Therefore, even if SYSFAIL is removed from VMEbus
+    // the interrupt status bit has to be cleared in LINT_STAT
+    // this will only work if SYSFAIL is not on the bus
+    case VMESYSFAILREENABLE:
+    {
+      u_int lint_stat, lint_en;
+
+      lint_stat = readl((char*)uni + LINT_STAT);	// read the interrupt bits
+      lint_stat |= 0x4000;
+      kdebug(("vme_rcc(ioctl,VMESYSFAILREENABLE): lint_stat = %x\n", lint_stat))
+      writel(lint_stat, (char*)uni + LINT_STAT);	// only success if SYSFAIL is not asserted on VMEbus
+      lint_en = readl((char*)uni + LINT_EN);	        // read enabled irq bits
+      kdebug(("vme_rcc(ioctl,VMESYSFAILREENABLE): lint_en = 0x%x\n", lint_en))
+      lint_en |= 0x4000;				
+      kdebug(("vme_rcc(ioctl,VMESYSFAILREENABLE): lint_en after masking = 0x%x\n", lint_en))
+      writel(lint_en, (char*)uni + LINT_EN);	        //reenable SYSFAIL
+      break;
+    }
+
+    // reset the SYSFAIL interrupt bit
+    // if it comes back on, the SYSFAIL is on VMEbus
+    case VMESYSFAILPOLL:
+    {
+      int flag;
+      u_int lint_stat;
+
+      lint_stat = readl((char*)uni + LINT_STAT);	// read the interrupt bits
+      lint_stat |= 0x4000;
+      kdebug(("vme_rcc(ioctl,VMESYSFAILPOLL): lint_stat = %x\n", lint_stat))
+      writel(lint_stat, (char*)uni + LINT_STAT);	// only success if SYSFAIL is not asserted on VMEbus
+      lint_stat = readl((char*)uni + LINT_STAT);	// read the interrupt bits
+      kdebug(("vme_rcc(ioctl,VMESYSFAILPOLL): lint_stat = %x\n", lint_stat))
+      if (lint_stat & 0x4000)	// the SYSFAIL bit
+        flag = 1;
+      else
+        flag = 0;
+
+      if (copy_to_user((void *)arg, &flag, sizeof(int)))
+      {
+	kerror(("vme_rcc(ioctl,VMESYSFAILPOLL): error from copy_to_user\n"))
+        return(-VME_EFAULT);
+      }
+      break;
+    }
+    
+    case VMESYSFAILSET:
+    {   
+      u_int vcsr_set;
+
+      vcsr_set = readl((char*)uni + VCSR_SET);
+      vcsr_set |= 0x40000000;				// bit SYSFAIL
+      kdebug(("vme_rcc(ioctl,VMESYSFAILSET): vcsr_set = %x\n", vcsr_set))
+      writel(vcsr_set, (char*)uni + VCSR_SET);
+      break;
+    }
+    
+  
+    case VMESYSFAILRESET:
+    {   
+      u_int vcsr_clr;
+
+      vcsr_clr = readl((char*)uni + VCSR_CLR);
+      vcsr_clr |= 0x40000000;				// bit SYSFAIL
+      kdebug(("vme_rcc(ioctl,VMESYSFAILRESET): vcsr_clr = %x\n", vcsr_clr))
+      writel(vcsr_clr, (char*)uni + VCSR_CLR);
+      break;
+    }
+
+    case VMEBERRREGISTERSIGNAL:
+    {
+      u_int i;
+      int index;
+
+      if (copy_from_user(&pdata->VME_BerrRegSig, (void *)arg, sizeof(VME_RegSig_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMEBERRREGISTERSIGNAL): error from copy_from_user\n"))
+	return(-VME_EFAULT);
+      }
+
+      kdebug(("vme_rcc(ioctl,VMEBERRREGISTERSIGNAL): signal # = %d\n", pdata->VME_BerrRegSig.signum))
+      down(&berr_proc_table.proc_sem);
+
+      if (pdata->VME_BerrRegSig.signum)
+      {
+        // find a free slot
+        index = -1;
+        for (i = 0; i < VME_MAX_BERR_PROCS; i++)
+        {
+          if (berr_proc_table.berr_link_dsc[i].pid == 0)
+          {
+            index = i;
+            break;
+          }
+        }
+        if (index == -1)
+        {
+          up(&berr_proc_table.proc_sem);
+          return(-VME_BERRTBLFULL);
+        }
+
+        berr_proc_table.berr_link_dsc[index].pid = current->pid;
+        berr_proc_table.berr_link_dsc[index].sig = pdata->VME_BerrRegSig.signum;
+        berr_dsc.flag = 0; 	                // enable BERR latching via signal
+				                // race: could occur while a(nother) bus error is latched and not yet serviced ..
+        pdata->berr_dsc_flag[index] = 1;	// remember which file berr belongs to
+      }
+      else		                        // unregister
+      {
+        index = -1;
+        for (i = 0; i < VME_MAX_BERR_PROCS; i++)
+        {
+          if (pdata->berr_dsc_flag[i] == 1)
+          {
+            index = i;
+            break;
+          }
+        }
+        if (index == -1)
+        {
+          up(&berr_proc_table.proc_sem);
+          return(-VME_BERRNOTFOUND);
+        }
+
+        berr_proc_table.berr_link_dsc[index].pid = 0;
+        berr_proc_table.berr_link_dsc[index].sig = 0;
+        pdata->berr_dsc_flag[index] = 0;
+      }
+
+      up(&berr_proc_table.proc_sem);
+      break;
+    }
+
+    case VMEBERRINFO:
+    {
+      u_long flags;
+
+      if (berr_dsc.flag == 0)		// slight risk of race with ISR ..
+        return(-VME_NOBUSERROR);
+
+      local_irq_save(flags);            //MJ: for 2.6 see p274 & p275
+
+      pdata->VME_BerrInfo.vmebus_address = berr_dsc.vmeadd;
+      pdata->VME_BerrInfo.address_modifier = berr_dsc.am;
+      pdata->VME_BerrInfo.multiple = berr_dsc.flag;
+      pdata->VME_BerrInfo.lword = berr_dsc.lword;
+      pdata->VME_BerrInfo.iack = berr_dsc.iack;
+      pdata->VME_BerrInfo.ds0 = berr_dsc.ds0;
+      pdata->VME_BerrInfo.ds1 = berr_dsc.ds1;
+      pdata->VME_BerrInfo.wr = berr_dsc.wr;
+
+      berr_dsc.vmeadd = 0;
+      berr_dsc.am = 0;
+      berr_dsc.multiple = 0;
+      berr_dsc.iack = 0;
+      berr_dsc.flag = 0;
+      berr_dsc.lword = 0;
+      berr_dsc.ds0 = 0;
+      berr_dsc.ds1 = 0;
+      berr_dsc.wr = 0;
+
+      local_irq_restore(flags);
+
+      if (copy_to_user((void *)arg, &pdata->VME_BerrInfo, sizeof(VME_BusErrorInfo_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMEBERRINFO): error from copy_to_user\n"))
+	return(-VME_EFAULT);
+      }
+      break;
+    }
+		
+    case VMESLAVEMAP:
+    {
+      int loop, ok = 0;
+
+      if (copy_from_user(&pdata->sm, (void *)arg, sizeof(VME_SlaveMapInt_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMESLAVEMAP): error from copy_from_user\n"))
+	return(-VME_EFAULT);
+      }
+      kdebug(("vme_rcc(ioctl,VMESLAVEMAP): mm.in.system_iobus_address = 0x%08x\n", pdata->sm.in.system_iobus_address))
+      kdebug(("vme_rcc(ioctl,VMESLAVEMAP): mm.in.window_size          = 0x%08x\n", pdata->sm.in.window_size))
+      kdebug(("vme_rcc(ioctl,VMESLAVEMAP): mm.in.address_width        = 0x%08x\n", pdata->sm.in.address_width))
+      kdebug(("vme_rcc(ioctl,VMESLAVEMAP): mm.in.options              = 0x%08x\n", pdata->sm.in.options))
+
+      for(loop = 0; loop < 8; loop++)
+      {
+	kdebug(("vme_rcc(ioctl,VMESLAVEMAP): stmap.slave[%d].pbase = 0x%08x\n", loop, stmap.slave[loop].pbase))
+	kdebug(("vme_rcc(ioctl,VMESLAVEMAP): stmap.slave[%d].ptop  = 0x%08x\n", loop, stmap.slave[loop].ptop))
+	kdebug(("vme_rcc(ioctl,VMESLAVEMAP): stmap.slave[%d].enab  = 0x%08x\n", loop, stmap.slave[loop].enab))
+	kdebug(("vme_rcc(ioctl,VMESLAVEMAP): stmap.slave[%d].space = 0x%08x\n", loop, stmap.slave[loop].space))
+	if (pdata->sm.in.system_iobus_address >= stmap.slave[loop].pbase && 
+           (pdata->sm.in.system_iobus_address + pdata->sm.in.window_size) <= stmap.slave[loop].ptop && 
+	   stmap.slave[loop].enab == 1 && 
+	   stmap.slave[loop].am == pdata->sm.in.address_width)
+        {
+	  kdebug(("vme_rcc(ioctl,VMESLAVEMAP): Decoder %d matches parameters\n", loop))
+	  pdata->sm.vme_address = stmap.slave[loop].vbase + (pdata->sm.in.system_iobus_address - stmap.slave[loop].pbase);
+          kdebug(("vme_rcc(ioctl,VMESLAVEMAP): VME address = 0x%08x\n", pdata->sm.vme_address))
+	  ok = 1;
+	  break;
+        }
+      }
+      if (!ok)
+      {
+	kerror(("vme_rcc(ioctl,VMESLAVEMAP): No matching mapping found\n"))
+	return(-VME_NOSTATMAP);   
+      }
+      if (copy_to_user((void *)arg, &pdata->sm, sizeof(VME_SlaveMapInt_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMESLAVEMAP): error from copy_to_user\n"))
+	return(-VME_EFAULT);
+      }
+      break;
+    } 
+
+    case VMESLAVEMAPDUMP: 
+    {  
+      char *buf;
+      int len, loop;
+
+      buf = (char *)kmalloc(TEXT_SIZE1, GFP_KERNEL);
+      if (buf == NULL)
+      {
+        kerror(("vme_rcc(ioctl,VMESLAVEMAPDUMP): error from kmalloc\n"))
+        return(-VME_KMALLOC);
+      }
+      len = 0;
+      len += sprintf(buf + len, "Slave mapping:\n");
+      len += sprintf(buf + len, "VSI  VME address range  PCI address range   EN   WP   RP   AM PCI Space\n");
+      for (loop = 0 ;loop < 8; loop++)
+	len += sprintf(buf + len, "%d    %08x-%08x  %08x-%08x    %d    %d    %d    %d         %d\n"
+          	      ,loop
+		      ,stmap.slave[loop].vbase
+	  	      ,stmap.slave[loop].vtop
+		      ,stmap.slave[loop].pbase
+		      ,stmap.slave[loop].ptop
+		      ,stmap.slave[loop].enab
+		      ,stmap.slave[loop].wp
+		      ,stmap.slave[loop].rp
+		      ,stmap.slave[loop].am
+		      ,stmap.slave[loop].space);
+      if (copy_to_user((void *)arg, buf, TEXT_SIZE1*sizeof(char)))
+      {
+	kerror(("vme_rcc(ioctl,VMESLAVEMAPDUMP): error from copy_to_user\n"))
+	return(-VME_EFAULT);
+      }
+      kfree(buf);
+      break;
+    }
+
+    case VMESCSAFE:
+    {    
+      u_int addr, multi, am, *lptr, ldata;
+      u_short *sptr, sdata;
+      u_char *bptr, bdata;
+      int ret;
+
+      if (copy_from_user(&pdata->sc, (void *)arg, sizeof(VME_SingleCycle_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMESCSAFE): error from copy_from_user\n"))
+	return(-VME_EFAULT);
+      }
+      kdebug(("vme_rcc(ioctl,VMESCSAFE): sc.kvirt_address = 0x%08x\n", pdata->sc.kvirt_address))
+      kdebug(("vme_rcc(ioctl,VMESCSAFE): sc.offset        = 0x%08x\n", pdata->sc.offset))
+      kdebug(("vme_rcc(ioctl,VMESCSAFE): sc.nbytes        = %d\n", pdata->sc.nbytes))
+      kdebug(("vme_rcc(ioctl,VMESCSAFE): sc.rw            = %d\n", pdata->sc.rw))
+
+      //Wait until the Universe has emptied the write posting buffer
+      while (!(uni->misc_stat & 0x40000))
+      {
+        txfifo_wait_1++;
+        kdebug(("vme_rcc(ioctl,VMESCSAFE): TXFIFO still not empty\n"))
+      }
+        
+      //Disable the CCT BERR logic for the duration of the safe single cycle
+      mask_cct_berr();
+        
+      if (pdata->sc.rw == 1)             //read
+      {
+	if (pdata->sc.nbytes == 4)       //D32
+	{
+	  lptr = (u_int *)(pdata->sc.kvirt_address + pdata->sc.offset);
+	  ldata = *lptr;
+	  pdata->sc.data = (u_int)ldata;
+	}
+	else if (pdata->sc.nbytes == 2)  //D16
+        {
+	  sptr = (u_short *)(pdata->sc.kvirt_address + pdata->sc.offset);
+	  sdata = *sptr;
+	  pdata->sc.data = (u_int)sdata;
+	}
+	else                      //D8
+	{
+	  bptr = (u_char *)(pdata->sc.kvirt_address + pdata->sc.offset);
+	  bdata = *bptr;
+	  pdata->sc.data = (u_int)bdata;
+	}
+      }
+      else                        //write
+      {
+	if (pdata->sc.nbytes == 4)       //D32
+	{
+	  lptr = (u_int *)(pdata->sc.kvirt_address + pdata->sc.offset);
+	  ldata = pdata->sc.data;
+	  *lptr = ldata;
+	}
+	else if (pdata->sc.nbytes == 2)  //D16
+	{
+	  sptr = (u_short *)(pdata->sc.kvirt_address + pdata->sc.offset);
+	  sdata = (u_short)pdata->sc.data;
+	  *sptr = sdata;
+	}
+	else                      //D8
+	{
+	  bptr = (u_char *)(pdata->sc.kvirt_address + pdata->sc.offset);
+	  bdata = (u_char)pdata->sc.data;
+	  *bptr = bdata;
+	}
+        
+        //Wait again until the Universe has emptied the write posting buffer
+        while (!(uni->misc_stat & 0x40000))
+        {
+          txfifo_wait_2++;
+          kdebug(("vme_rcc(ioctl,VMESCSAFE): TXFIFO still not empty (2)\n"))
+        }
+      }
+
+      //MJ: Do we have to wait a bit for bus errors to arrive?
+      //MJ: What to do with addr, multi and am in case of BERR?
+      ret = berr_check(&addr, &multi, &am);
+      if (ret)
+      {
+	kerror(("vme_rcc(ioctl,VMESCSAFE): Bus error received\n"))
+	init_cct_berr();
+	return(-VME_BUSERROR);
+      }
+      
+      //Reenable the CCT BERR logic and clear the BERR flag in the CCT logic
+      init_cct_berr();
+
+      if (copy_to_user((void *)arg, &pdata->sc, sizeof(VME_SingleCycle_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMESCSAFE): error from copy_to_user\n"))
+	return(-VME_EFAULT);
+      }
+      break;
+    } 
+
+    case VMEDMASTART:
+    {
+      int ret, loop, ok;
+      u_long flags;
+     
+      //Check if there is space in the done list
+      ok = 0;
+      for (loop = 0; loop < VME_DMADONESIZE; loop++)
+        if (dma_donelist[loop].handle == 0xffffffff && dma_donelist[loop].pid == 0)
+        {
+          ok = 1;
+          break;
+        }
+      if (!ok)
+      {
+        kerror(("vme_rcc(ioctl,VMEDMASTART): no memory left in done list\n"))
+        return(-VME_NODOMEMEM);
+      }
+      else
+        kdebug(("vme_rcc(ioctl,VMEDMASTART): using entry %d in done list\n", loop))
+
+      current_dmahandle.index = loop;
+      pdata->dma[loop] = 1;
+      
+      //Get the DMA semaphore
+      //MJ: The API document says that the function should return VME_BUSY if the DMA controller
+      //    is in use (semaphore=0). There seems to be a function (down_trylock) but it is so badly
+      //    documented (i.e. not at all) that I don't dare to use it. Therefore I do it the forbidden way.
+      
+      #ifdef VMEDEBUG 
+        *marker=((current->pid) << 8) | 2; 
+      #endif
+
+      local_irq_save(flags);  //MJ: for 2.6 see p274 & p275
+      if (dma_semaphore.count.counter < 1)
+      {
+        local_irq_restore(flags);
+        dma_sem_busy++;
+        kerror(("vme_rcc(ioctl,VMEDMASTART): failed to get semaphore\n"))
+        return(-VME_DMABUSY);
+      }
+      
+      ret = down_interruptible(&dma_semaphore);        //we should now have the garantee to get the semaphore
+      local_irq_restore(flags);
+      if (ret)
+	return(-VME_ERESTARTSYS);
+
+      #ifdef VMEDEBUG 
+        *marker=((current->pid) << 8) | 3; 
+      #endif
+
+      if (copy_from_user(&pdata->dma_params, (void *)arg, sizeof(VME_DMAstart_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMEDMASTART): error from copy_from_user\n"))
+	return(-VME_EFAULT);
+      }
+
+      kdebug(("vme_rcc(ioctl,VMEDMASTART): PCI address of chain = 0x%08x\n", pdata->dma_params.paddr))
+      kdebug(("vme_rcc(ioctl,VMEDMASTART): Handle chain         = 0x%08x\n", pdata->dma_params.handle))
+
+      current_dmahandle.handle = pdata->dma_params.handle;
+      current_dmahandle.pid    = current->pid;
+      kdebug(("vme_rcc(ioctl,VMEDMASTART): current_dmahandle.handle = %d\n", current_dmahandle.handle))
+      kdebug(("vme_rcc(ioctl,VMEDMASTART): current_dmahandle.pid    = %d\n", current_dmahandle.pid)) 
+
+      //start the chained DMA
+      uni->dgcs |= 0x00006f00;  //clear all status and error flags
+      uni->dcpp = pdata->dma_params.paddr; 
+      uni->dgcs = 0x88000008;   //start chaind DMA, enable EOT interrupt
+      kdebug(("vme_rcc(ioctl,VMEDMASTART): Transfer started\n"))
+      
+      #ifdef VMEDEBUG 
+        *marker=((current->pid) << 8) | 4; 
+      #endif
+
+      break;        
+    }
+
+    case VMEDMAPOLL:
+    {
+      u_int ok, loop = 0;
+      
+      if (copy_from_user(&pdata->dma_poll_params, (void *)arg, sizeof(VME_DMAhandle_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMEDMAPOLL): error from copy_from_user\n"))
+	return(-VME_EFAULT);
+      }
+        
+      #ifdef VMEDEBUG 
+        *marker=((current->pid) << 8) | 5; 
+      #endif
+
+      current_dmahandle.timeout = 1;
+      
+      if (pdata->dma_poll_params.timeoutval > 0)
+      {
+        dma_timer.function = vme_dmaTimeout;
+        dma_timer.data = (u_long)&dma_to;
+        dma_timer.expires = jiffies + pdata->dma_poll_params.timeoutval;
+        add_timer(&dma_timer);
+      }
+
+      ok = 0;
+
+      #ifdef VMEDEBUG 
+        *marker=((current->pid) << 8) | 7; 
+      #endif
+
+      while (!ok && current_dmahandle.timeout == 1)
+      {
+        for (loop = 0; loop < VME_DMADONESIZE; loop++)
+        {
+          kdebug(("vme_rcc(ioctl,VMEDMAPOLL): dma_donelist[%d].handle = %d  params.handle = %d\n", loop, dma_donelist[loop].handle, pdata->dma_poll_params.handle))
+          kdebug(("vme_rcc(ioctl,VMEDMAPOLL): dma_donelist[%d].pid    = %d  pid           = %d\n", loop, dma_donelist[loop].pid,  current->pid))
+
+          if (dma_donelist[loop].handle == pdata->dma_poll_params.handle && dma_donelist[loop].pid == (u_int) current->pid)
+          {
+            ok = 1;
+            kdebug(("vme_rcc(ioctl,VMEDMAPOLL): DMA confirmation found under index %d\n", loop))
+            #ifdef VMEDEBUG 
+              *marker=((current->pid) << 8) | 0xa; 
+              *marker=pdata->dma_poll_params.handle;
+              *marker=loop;
+            #endif
+            break;
+          }
+        }
+        if (pdata->dma_poll_params.timeoutval == 0)
+          break;  //Don't poll, just probe
+      }
+      
+      #ifdef VMEDEBUG 
+        *marker=((current->pid) << 8) | 0x60 | current_dmahandle.timeout; 
+      #endif
+      
+      if (!ok)  //This DMA has not yet completed
+      {
+        #ifdef VMEDEBUG 
+          *marker=0x4444; 
+        #endif
+      
+        kdebug(("vme_rcc(ioctl,VMEDMAPOLL): Still busy\n"))
+        pdata->dma_poll_params.ctrl    = 0;        
+        pdata->dma_poll_params.counter = 0x80000000;  // Just to avoid 0
+        pdata->dma_poll_params.timeout = current_dmahandle.timeout;
+      }
+      else
+      {
+        #ifdef VMEDEBUG 
+          *marker=((current->pid) << 8) | 9; 
+          *marker=loop;
+        #endif
+
+        kdebug(("vme_rcc(ioctl,VMEDMAPOLL): loop = %d\n", loop))
+        pdata->dma_poll_params.ctrl    = dma_donelist[loop].ctrl;
+        pdata->dma_poll_params.counter = dma_donelist[loop].counter;
+        pdata->dma_poll_params.timeout = current_dmahandle.timeout;
+        dma_donelist[loop].handle = 0xffffffff;
+        dma_donelist[loop].pid = 0;
+        pdata->dma[loop] = 0;
+        kdebug(("vme_rcc(ioctl,VMEDMAPOLL): params.ctrl    = 0x%08x\n", pdata->dma_poll_params.ctrl))
+        kdebug(("vme_rcc(ioctl,VMEDMAPOLL): params.counter = 0x%08x\n", pdata->dma_poll_params.counter))
+        kdebug(("vme_rcc(ioctl,VMEDMAPOLL): params.timeout = %d\n", pdata->dma_poll_params.timeout))
+      }
+      
+      //Stop the timer
+      del_timer(&dma_timer);	// OK also if timer ran out
+      #ifdef VMEDEBUG 
+        *marker=((current->pid) << 8) | 8; 
+      #endif
+  
+      if (copy_to_user((void *)arg, &pdata->dma_poll_params, sizeof(VME_DMAhandle_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMEDMAPOLL): error from copy_to_user\n"))
+	return(-VME_EFAULT);
+      }
+      break;
+    }
+
+    case VMEDMADUMP: 
+    {  
+      char *buf;
+      int loop,len;
+      u_int data, value;
+      
+      buf = (char *)kmalloc(TEXT_SIZE2, GFP_KERNEL);
+      if (buf == NULL)
+      {
+        kerror(("vme_rcc(ioctl,VMESLAVEMAPDUMP): error from kmalloc\n"))
+        return(-VME_KMALLOC);
+      }
+      len = 0;
+      len += sprintf(buf + len, "PCI_CSR register               = 0x%08x\n", uni->pci_csr);
+      len += sprintf(buf + len, "DMA registers:\n");
+      len += sprintf(buf + len, "PCI address              (DLA) = 0x%08x\n", uni->dla);
+      len += sprintf(buf + len, "VME address              (DVA) = 0x%08x\n", uni->dva);
+      len += sprintf(buf + len, "Byte count              (DTBC) = 0x%08x\n", uni->dtbc);
+      len += sprintf(buf + len, "Chain pointer           (DCPP) = 0x%08x\n", uni->dcpp);
+      data = uni->dctl;
+      len += sprintf(buf + len, "Transfer control reg.   (DCTL) = 0x%08x\n", data);
+      len += sprintf(buf + len, "64 bit PCI is                    %s\n", data & 0x80 ? "Enabled" : "Disabled");
+      len += sprintf(buf + len, "VMEbus cycle type:               %s\n", data & 0x100 ? "Block cycles" : "Single cycles");
+      len += sprintf(buf + len, "AM code tpye:                    %s\n", data & 0x1000 ? "Supervisor" : "User");
+      len += sprintf(buf + len, "AM code tpye:                    %s\n", data & 0x4000 ? "Programm" : "Data");
+      len += sprintf(buf + len, "VMEbus address space:            ");
+      value = (data >> 16) & 0x7;
+      if (value == 0) len += sprintf(buf + len, "A16\n");
+      if (value == 1) len += sprintf(buf + len, "A24\n");
+      if (value == 2) len += sprintf(buf + len, "A32\n");
+      if (value == 3) len += sprintf(buf + len, "Reserved\n");
+      if (value == 4) len += sprintf(buf + len, "Reserved\n");
+      if (value == 5) len += sprintf(buf + len, "Reserved\n");
+      if (value == 6) len += sprintf(buf + len, "User 1\n");
+      if (value == 7) len += sprintf(buf + len, "User 2\n");
+      len += sprintf(buf + len, "VMEbus max data width:           ");
+      value = (data >> 22) & 0x3;
+      if (value == 0) len += sprintf(buf + len, "D8\n");
+      if (value == 1) len += sprintf(buf + len, "D16\n");
+      if (value == 2) len += sprintf(buf + len, "D32\n");
+      if (value == 3) len += sprintf(buf + len, "D64\n");
+      len += sprintf(buf + len, "Transfer direction:              VMEbus %s\n", data & 0x80000000 ? "Write":  "Read");
+      data = uni->dgcs;
+      len += sprintf(buf + len, "General control reg.    (DGCS) = 0x%08x\n", data);
+      len += sprintf(buf + len, "Interrupt on protocol error:     %s\n", data & 0x1 ? "Enabled" : "Disabled");
+      len += sprintf(buf + len, "Interrupt on VME error:          %s\n", data & 0x2 ? "Enabled" : "Disabled");
+      len += sprintf(buf + len, "Interrupt on PCI error:          %s\n", data & 0x4 ? "Enabled" : "Disabled");
+      len += sprintf(buf + len, "Interrupt when done:             %s\n", data & 0x8 ? "Enabled" : "Disabled");
+      len += sprintf(buf + len, "Interrupt when halted:           %s\n", data & 0x20 ? "Enabled" : "Disabled");
+      len += sprintf(buf + len, "Interrupt when stopped:          %s\n", data & 0x40 ? "Enabled" : "Disabled");
+      len += sprintf(buf + len, "Protocol error:                  %s\n", data & 0x100 ? "Yes" : "No");
+      len += sprintf(buf + len, "VMEbus error:                    %s\n", data & 0x200 ? "Yes" : "No");
+      len += sprintf(buf + len, "PCI Bus error:                   %s\n", data & 0x400 ? "Yes" : "No");
+      len += sprintf(buf + len, "Transfer complete:               %s\n", data & 0x800 ? "Yes" : "No");
+      len += sprintf(buf + len, "DMA halted:                      %s\n",data&0x2000?"Yes":"No");
+      len += sprintf(buf + len, "DMA stopped:                     %s\n",data&0x4000?"Yes":"No");
+      len += sprintf(buf + len, "DMA active:                      %s\n",data&0x8000?"Yes":"No");
+      value = (data >> 16) & 0xf;
+      len += sprintf(buf + len, "Minimum inter tenure gap:        ");
+      if (value == 0) len += sprintf(buf + len, "0 us\n");
+      else if (value == 1) len += sprintf(buf + len, "16 us\n");
+      else if (value == 2) len += sprintf(buf + len, "32 us\n");
+      else if (value == 3) len += sprintf(buf + len, "64 us\n");
+      else if (value == 4) len += sprintf(buf + len, "128 us\n");
+      else if (value == 5) len += sprintf(buf + len, "256 us\n");
+      else if (value == 6) len += sprintf(buf + len, "512 us\n");
+      else if (value == 7) len += sprintf(buf + len, "1024 us\n");
+      else len += sprintf(buf + len, "Out of range\n");
+      len += sprintf(buf + len, "Bytes transfered per bus tenure: ");
+      value = (data >> 20) & 0xf;
+      if (value == 0) len += sprintf(buf + len, "Until done\n");
+      else if (value == 1) len += sprintf(buf + len, "256 bytes\n");
+      else if (value == 2) len += sprintf(buf + len, "512 bytes\n");
+      else if (value == 3) len += sprintf(buf + len, "1 Kbye\n");
+      else if (value == 4) len += sprintf(buf + len, "2 Kbye\n");
+      else if (value == 5) len += sprintf(buf + len, "4 Kbye\n");
+      else if (value == 6) len += sprintf(buf + len, "8 Kbye\n");
+      else if (value == 7) len += sprintf(buf + len, "16 Kbye\n");
+      else len += sprintf(buf + len, "Out of range\n");
+      len += sprintf(buf + len, "DMA chaining:                    %s\n",data & 0x8000000 ? "Yes" : "No");
+      len += sprintf(buf + len, "DMA halt request:                %s\n",data & 0x20000000 ? "Halt DMA after current package" : "No");
+      len += sprintf(buf + len, "DMA stop request:                %s\n",data & 0x40000000 ? "process buffered data and stop" : "No");
+      len += sprintf(buf + len, "DMA go:                          %s\n",data & 0x80000000 ? "Yes" : "No");
+
+      len += sprintf(buf + len, "\nDumping active entries in dma_donelist:\n");
+      for (loop = 0; loop < VME_DMADONESIZE; loop++)
+      { 
+        if (dma_donelist[loop].handle != 0xffffffff && dma_donelist[loop].pid)
+        {
+          len += sprintf(buf + len, "dma_donelist[%d].handle  = %d\n", loop, dma_donelist[loop].handle);
+          len += sprintf(buf + len, "dma_donelist[%d].pid     = %d\n", loop, dma_donelist[loop].pid);
+          len += sprintf(buf + len, "dma_donelist[%d].ctrl    = 0x%08x\n", loop, dma_donelist[loop].ctrl);
+          len += sprintf(buf + len, "dma_donelist[%d].counter = 0x%08x\n", loop, dma_donelist[loop].counter);
+          len += sprintf(buf + len, "dma_donelist[%d].timeout = %d\n\n", loop, dma_donelist[loop].timeout);
+        }
+      }
+
+      if (copy_to_user((void *)arg, buf, TEXT_SIZE2*sizeof(char)))
+      {
+	kerror(("vme_rcc(ioctl,VMEDMADUMP): error from copy_to_user\n"))
+	return(-VME_EFAULT);
+      }
+      kfree(buf);
+      break;
+    }
+    
+    case VMELINK:
+    {
+      VME_IntHandle_t *group_ptr;
+      u_int i;
+      int vct;
+
+      if (copy_from_user(&pdata->VME_int_handle, (void *)arg, sizeof(VME_IntHandle_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMELINK): error from copy_from_user\n"))
+	return(-VME_EFAULT);
+      }
+      kdebug(("vme_rcc(ioctl,VMELINK): # vectors = %d\n", pdata->VME_int_handle.nvectors))
+      for (i = 0; i < pdata->VME_int_handle.nvectors; i++)
+        kdebug(("vme_rcc(ioctl,VMELINK): vector # %d = 0x%8x\n", i, pdata->VME_int_handle.vector[i]))
+
+      if (irq_level_table[pdata->VME_int_handle.level - 1] == 0)        // is this level enabled?
+      {
+        kdebug(("vme_rcc(ioctl,VMELINK): level %d is disabled\n", pdata->VME_int_handle.level))
+        return(-VME_INTDISABLED);
+      }
+      
+      kdebug(("vme_rcc(ioctl,VMELINK): level = %d, type = %d\n", pdata->VME_int_handle.level, pdata->VME_int_handle.type))
+
+      if (pdata->VME_int_handle.type != irq_level_table[pdata->VME_int_handle.level - 1])        // check type
+      {
+        kdebug(("vme_rcc(ioctl,VMELINK): level %d, user mode = %d, VMEconfig mode = %d\n",
+		   pdata->VME_int_handle.level, pdata->VME_int_handle.type,  irq_level_table[pdata->VME_int_handle.level - 1]))
+        return(-VME_INTCONF);
+      }
+
+      down(&link_table.link_dsc_sem);
+
+      for (i = 0; i < pdata->VME_int_handle.nvectors; i++)
+      {
+        vct = pdata->VME_int_handle.vector[i]; 
+        if (link_table.link_dsc[vct].pid) 
+        {
+          kdebug(("vme_rcc(ioctl,VMELINK): vector busy, pid = %d\n", link_table.link_dsc[vct].pid))
+          up(&link_table.link_dsc_sem);
+          return(-VME_INTUSED);
+        }
+      }
+
+      // allocate aux. structure for multi vector handles and fill it
+      group_ptr = (VME_IntHandle_t*) NULL;
+      if (pdata->VME_int_handle.nvectors > 1)
+      {
+        group_ptr = kmalloc(sizeof(VME_IntHandle_t), GFP_KERNEL);
+        if (!group_ptr)
+        {  
+          up(&link_table.link_dsc_sem);
+          return(-VME_ENOMEM);
+        }
+        group_ptr->nvectors = pdata->VME_int_handle.nvectors;
+        for (i = 0; i < pdata->VME_int_handle.nvectors; i++)
+          group_ptr->vector[i] = pdata->VME_int_handle.vector[i];
+      }
+
+      for (i = 0; i < pdata->VME_int_handle.nvectors; i++)
+      {
+        vct = pdata->VME_int_handle.vector[i]; 
+
+        link_table.link_dsc[vct].pid = current->pid;		       // current process
+        link_table.link_dsc[vct].vct = vct;	        	       // vector
+        link_table.link_dsc[vct].lvl = pdata->VME_int_handle.level;    // level
+        link_table.link_dsc[vct].type = pdata->VME_int_handle.type;    // type
+        link_table.link_dsc[vct].pending = 0;	        	       // no interrupt pending
+        link_table.link_dsc[vct].total_count = 0;       	       // total # interrupts
+        link_table.link_dsc[vct].group_ptr = group_ptr; 	       // the group vectors
+        sema_init(&link_table.link_dsc[vct].sem,0); 		       // initialise semaphore count
+        link_table.link_dsc[vct].sig = 0;	        	       // no signal
+      
+        pdata->link_dsc_flag[vct] = 1;			               // remember which file it belongs to
+      }
+
+      up(&link_table.link_dsc_sem);
+      break;
+    }
+
+    case VMESETLEVEL:  //MJ: It seems that this entry point is obsolete 
+    {
+      if (copy_from_user(&pdata->VME_IntEnable, (void *)arg, sizeof(VME_IntEnable_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMESETLEVEL): error from copy_from_user\n"))
+	return(-VME_EFAULT);
+      }
+      kdebug(("vme_rcc(ioctl,VMESETLEVEL): level = %d, type = %d\n", pdata->VME_IntEnable.level, pdata->VME_IntEnable.type))
+
+      if (irq_level_table[pdata->VME_IntEnable.level-1] == VME_LEVELISDISABLED)
+        return(-VME_LVLDISABLED);
+
+      irq_level_table[pdata->VME_IntEnable.level-1] = pdata->VME_IntEnable.type;
+      break;
+    }
+
+    case VMEINTENABLE:
+    {
+      u_int lint_en;
+      u_long flags;
+
+      if (copy_from_user(&pdata->VME_IntEnable2, (void *)arg, sizeof(VME_IntEnable_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMEINTENABLE): error from copy_from_user\n"))
+	return(-VME_EFAULT);
+      }
+      kdebug(("vme_rcc(ioctl,VMEINTENABLE): level = %d\n", pdata->VME_IntEnable2.level))
+
+      // ONLY allowed if RORA level
+      if (irq_level_table[pdata->VME_IntEnable2.level-1] != VME_INT_RORA)
+        return(-VME_LVLISNOTRORA);
+
+      local_irq_save(flags);                            //MJ: for 2.6 see p274 & p275
+
+      lint_en = readl((char*)uni + LINT_EN);    	// read enabled irq bits
+      lint_en |= (1 << pdata->VME_IntEnable2.level);	// BM_LINT_VIRQxxx
+      kdebug(("vme_rcc(ioctl,VMEINTENABLE): lint_en =0x %x\n", lint_en))
+      writel(lint_en, (char*)uni + LINT_EN);	        // write it back
+
+      local_irq_restore(flags);
+      break;
+    }
+
+    case VMEINTDISABLE:
+    {
+      u_int lint_en;
+      u_long flags;
+
+      if (copy_from_user(&pdata->VME_IntEnable3, (void *)arg, sizeof(VME_IntEnable_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMEINTDISABLE): error from copy_from_user\n"))
+	return(-VME_EFAULT);
+      }
+      kdebug(("vme_rcc(ioctl,VMEINTDISABLE): level = %d\n", pdata->VME_IntEnable3.level))
+
+      local_irq_save(flags);                            //MJ: for 2.6 see p274 & p275
+
+      lint_en = readl((char*)uni + LINT_EN);	        // read enabled irq bits
+      lint_en &= ~(1 << pdata->VME_IntEnable3.level);	// BM_LINT_VIRQxxx
+      kdebug(("vme_rcc(ioctl,VMEINTDISABLE): lint_en =0x %x\n", lint_en))
+      writel(lint_en, (char*)uni + LINT_EN);	        // write it back
+
+      local_irq_restore(flags);
+      break;
+    }
+
+    case VMEWAIT:
+    {
+      int first_vct, cur_vct = 0, use_timer = 0, result = 0;
+      u_int i;
+      u_long flags;
+      struct timer_list int_timer;
+      VME_IntHandle_t *group_ptr;
+
+      if (copy_from_user(&pdata->VME_WaitInt2, (void *)arg, sizeof(VME_WaitInt_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMEWAIT): error from copy_from_user\n"))
+	return(-VME_EFAULT);
+      }
+      kdebug(("vme_rcc(ioctl,VMEWAIT): # vectors = %d, timeout = %d\n", pdata->VME_WaitInt2.int_handle.nvectors, pdata->VME_WaitInt2.timeout))
+      for (i = 0; i < pdata->VME_WaitInt2.int_handle.nvectors; i++)
+        kdebug(("vme_rcc(ioctl,VMEWAIT): vector # %d = 0x%8x\n", i, pdata->VME_WaitInt2.int_handle.vector[i]))
+
+      first_vct = pdata->VME_WaitInt2.int_handle.vector[0];
+
+      if (pdata->VME_WaitInt2.timeout > 0) 
+      {
+        init_timer(&int_timer);
+        int_timer.function = vme_intTimeout;
+        int_timer.data = (u_long)&pdata->VME_WaitInt2;
+        int_timer.expires = jiffies + pdata->VME_WaitInt2.timeout;
+        add_timer(&int_timer);
+        use_timer = 1;
+      }
+
+      if (pdata->VME_WaitInt2.timeout) 		// Wait : with timeout or not
+      {
+        kdebug(("vme_rcc(ioctl,VMEWAIT): Before Down, semaphore = %d\n", link_table.link_dsc[first_vct].sem.count.counter))
+
+        // IR handled before we wait. Not quite RACE safe but only for statistics  .. 
+        if (link_table.link_dsc[first_vct].sem.count.counter > 0) 
+        {
+          if (link_table.link_dsc[first_vct].sem.count.counter == 1)
+            sem_positive[0]++;
+          else
+            sem_positive[1]++;
+        }
+
+        #ifdef INTDEBUG
+          *marker=((current->pid) << 8) | 0x3;
+        #endif
+
+        down_interruptible(&link_table.link_dsc[first_vct].sem);	// wait ..
+
+        #ifdef INTDEBUG
+          *marker=((current->pid) << 8) | 0x4;
+          *marker=first_vct;
+          *marker=link_table.link_dsc[first_vct].sem.count.counter;
+        #endif
+
+        if (signal_pending(current))
+        {						// interrupted system call
+          kdebug(("vme_rcc(ioctl,VMEWAIT): Interrupted by signal\n"))
+          result = -VME_INTBYSIGNAL;		        // or ERESTARTSYS ?
+        }
+
+        else if ((use_timer == 1) && (pdata->VME_WaitInt2.timeout == 0))
+          result = -VME_TIMEOUT;
+      }
+
+      pdata->VME_WaitInt2.vector = 0;                   // return 0 if NO pending
+      pdata->VME_WaitInt2.level = 0;
+      pdata->VME_WaitInt2.type = 0;
+      pdata->VME_WaitInt2.multiple = 0;
+
+      if (result == 0)		                                // for all values of timeout & no early returns: get IR info
+      {
+        if (link_table.link_dsc[first_vct].group_ptr == NULL)	// single vector
+        {
+          if (link_table.link_dsc[first_vct].pending > 0)
+          {
+            pdata->VME_WaitInt2.level = link_table.link_dsc[first_vct].lvl;
+            pdata->VME_WaitInt2.type = link_table.link_dsc[first_vct].type;
+            pdata->VME_WaitInt2.vector = first_vct;
+            pdata->VME_WaitInt2.multiple = link_table.link_dsc[first_vct].pending;
+            link_table.link_dsc[first_vct].pending = 0;		// done
+          }
+        }
+        else		                // scan vectors in group and return FIRST one pending 
+			                // the scan starts from the first: an issue ?
+        {
+          local_irq_save(flags);  // may not really be needed //MJ: for 2.6 see p274 & p275
+
+          group_ptr = link_table.link_dsc[first_vct].group_ptr;
+          kdebug(("vme_rcc(ioctl,VMEWAIT): group pointer = 0x%p\n",group_ptr))
+          for (i = 0; i < group_ptr->nvectors; i++)
+          {
+            cur_vct = group_ptr->vector[i];
+            kdebug(("vme_rcc(ioctl,VMEWAIT): current vector = %d\n",cur_vct))
+            if (link_table.link_dsc[cur_vct].pending > 0)
+            {
+              pdata->VME_WaitInt2.level = link_table.link_dsc[cur_vct].lvl;
+              pdata->VME_WaitInt2.type = link_table.link_dsc[cur_vct].type;
+              pdata->VME_WaitInt2.vector = cur_vct;
+              pdata->VME_WaitInt2.multiple = link_table.link_dsc[cur_vct].pending;
+              link_table.link_dsc[cur_vct].pending = 0;		// done
+              break;
+            }
+          }
+          local_irq_restore(flags);
+        }
+      }
+
+      if (use_timer == 1)
+        del_timer(&int_timer);	// OK also if timer ran out
+
+      if (copy_to_user((void *)arg, &pdata->VME_WaitInt2, sizeof(VME_WaitInt_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMEWAIT): error from copy_to_user\n"))
+	return(-VME_EFAULT);
+      }
+
+      return (result);
+      break;	// dummy ..
+    }
+
+    case VMEREGISTERSIGNAL:
+    {
+      int vct;
+      u_int i;
+
+      if (copy_from_user(&pdata->VME_RegSig, (void *)arg, sizeof(VME_RegSig_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMEREGISTERSIGNAL): error from copy_from_user\n"))
+	return(-VME_EFAULT);
+      }
+
+      pdata->VME_int_handle2 = pdata->VME_RegSig.int_handle;
+
+      kdebug(("vme_rcc(ioctl,VMEREGISTERSIGNAL): # vectors = %d, signal # = %d\n", pdata->VME_int_handle2.nvectors, pdata->VME_RegSig.signum))
+      for (i = 0; i < pdata->VME_int_handle2.nvectors; i++)
+        kdebug(("vme_rcc(ioctl,VMEREGISTERSIGNAL): vector # %d = 0x%8x\n", i, pdata->VME_int_handle2.vector[i]));
+
+      down(&link_table.link_dsc_sem);
+
+      for (i = 0; i < pdata->VME_int_handle2.nvectors; i++)
+      {
+        vct = pdata->VME_int_handle2.vector[i]; 
+        link_table.link_dsc[vct].sig = pdata->VME_RegSig.signum;
+      }
+
+      up(&link_table.link_dsc_sem);
+      break;
+    }
+
+    case VMEINTERRUPTINFOGET:
+    {
+      int first_vct, cur_vct = 0;
+      u_int i;
+      u_long flags;
+      VME_IntHandle_t *group_ptr;
+
+      if (copy_from_user(&pdata->VME_WaitInt3, (void *)arg, sizeof(VME_WaitInt_t)))
+      {
+	kdebug(("vme_rcc(ioctl,VMEINTERRUPTINFOGET): error from copy_from_user\n"))
+	return(-VME_EFAULT);
+      }
+      kdebug(("vme_rcc(ioctl,VMEINTERRUPTINFOGET): # vectors = %d \n", pdata->VME_WaitInt3.int_handle.nvectors))
+      for (i = 0; i < pdata->VME_WaitInt3.int_handle.nvectors; i++)
+        kdebug(("vme_rcc(ioctl,VMEINTERRUPTINFOGET): vector # %d = 0x%8x\n", i, pdata->VME_WaitInt3.int_handle.vector[i]))
+
+      first_vct = pdata->VME_WaitInt3.int_handle.vector[0];
+
+      pdata->VME_WaitInt3.vector = 0;      // return 0 if NO pending
+      pdata->VME_WaitInt3.level = 0;
+      pdata->VME_WaitInt3.type = 0;
+      pdata->VME_WaitInt3.multiple = 0;
+
+      if (link_table.link_dsc[first_vct].group_ptr == NULL)
+      {
+        if (link_table.link_dsc[first_vct].pending > 0)
+        {
+          pdata->VME_WaitInt3.level = link_table.link_dsc[first_vct].lvl;
+          pdata->VME_WaitInt3.type = link_table.link_dsc[first_vct].type;
+          pdata->VME_WaitInt3.vector = first_vct;
+          pdata->VME_WaitInt3.multiple = link_table.link_dsc[first_vct].pending;	// TBD
+          link_table.link_dsc[first_vct].pending = 0;		// done
+        }
+      }
+      else		// scan vectors in group and return FIRST one pending 
+      {
+        local_irq_save(flags);  // may not really be needed //MJ: for 2.6 see p274 & p275
+
+        group_ptr = link_table.link_dsc[first_vct].group_ptr;
+        kdebug(("vme_rcc(ioctl,VMEINTERRUPTINFOGET): group pointer = 0x%p\n",group_ptr))
+        for (i = 0; i < group_ptr->nvectors; i++)
+        {
+          cur_vct = group_ptr->vector[i];
+          kdebug(("vme_rcc(ioctl,VMEINTERRUPTINFOGET): current vector = %d\n",cur_vct))
+          if (link_table.link_dsc[cur_vct].pending > 0)
+          {
+            pdata->VME_WaitInt3.level = link_table.link_dsc[cur_vct].lvl;
+            pdata->VME_WaitInt3.type = link_table.link_dsc[cur_vct].type;
+            pdata->VME_WaitInt3.vector = cur_vct;
+            pdata->VME_WaitInt3.multiple = link_table.link_dsc[cur_vct].pending;
+            link_table.link_dsc[cur_vct].pending = 0;		// done
+            break;
+          }
+        }
+        local_irq_restore(flags);
+      }
+
+      if (copy_to_user((void *)arg, &pdata->VME_WaitInt3, sizeof(VME_WaitInt_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMEINTERRUPTINFOGET): error from copy_to_user\n"))
+	return(-VME_EFAULT);
+      }
+      break;
+    }
+
+    case VMEUNLINK:
+    {
+      u_int i;
+      int vct, first_vector;
+
+      if (copy_from_user(&pdata->VME_int_handle3, (void *)arg, sizeof(VME_IntHandle_t)))
+      {
+	kerror(("vme_rcc(ioctl,VMEUNLINK): error from copy_from_user\n"))
+	return(-VME_EFAULT);
+      }
+      kdebug(("vme_rcc(ioctl,VMEUNLINK): # vectors = %d\n", pdata->VME_int_handle3.nvectors))
+      for (i = 0; i < pdata->VME_int_handle3.nvectors; i++)
+        kdebug(("vme_rcc(ioctl,VMEUNLINK): vector # %d = 0x%x\n", i, pdata->VME_int_handle3.vector[i]));
+
+      down(&link_table.link_dsc_sem);
+
+      if (pdata->VME_int_handle3.nvectors > 1)
+      {
+        first_vector = pdata->VME_int_handle3.vector[0];
+        kfree(link_table.link_dsc[first_vector].group_ptr);
+        kdebug(("vme_rcc(ioctl,VMEUNLINK): first vector = %d\n", first_vector))
+        kdebug(("vme_rcc(ioctl,VMEUNLINK): group_ptr = 0x%p\n", link_table.link_dsc[first_vector].group_ptr))
+      }
+
+      for (i = 0; i < pdata->VME_int_handle3.nvectors; i++)
+      {
+        vct = pdata->VME_int_handle3.vector[i]; 
+
+        link_table.link_dsc[vct].pid = 0;
+        link_table.link_dsc[vct].vct = 0;
+        link_table.link_dsc[vct].lvl = 0;
+        link_table.link_dsc[vct].pending = 0;
+        link_table.link_dsc[vct].total_count = 0;
+        link_table.link_dsc[vct].group_ptr = 0;
+        sema_init(&link_table.link_dsc[vct].sem,0);
+        link_table.link_dsc[vct].sig = 0;
+
+        pdata->link_dsc_flag[vct] = 0;
+      }
+      up(&link_table.link_dsc_sem);
+      break;
+    }
+ 
+    // called from vmeconfig when Universe registers are updated
+    case VMEUPDATE:
+    {      
+      int ret, i;
+
+      kdebug(("vme_rcc(ioctl,VMEUPDATE): Function called\n")) 
+        
+      ret = copy_from_user(&pdata->update_data, (void *)arg, sizeof(VME_Update_t));
+      if (ret)
+      {
+	kerror(("vme_rcc(ioctl,VMEUPDATE): error %d from copy_from_user\n",ret))
+	return(-VME_EFAULT);
+      }
+      
+      fill_mstmap(uni->lsi0_ctl, uni->lsi0_bs, uni->lsi0_bd, uni->lsi0_to, &stmap.master[0]);
+      fill_mstmap(uni->lsi1_ctl, uni->lsi1_bs, uni->lsi1_bd, uni->lsi1_to, &stmap.master[1]);
+      fill_mstmap(uni->lsi2_ctl, uni->lsi2_bs, uni->lsi2_bd, uni->lsi2_to, &stmap.master[2]);
+      fill_mstmap(uni->lsi3_ctl, uni->lsi3_bs, uni->lsi3_bd, uni->lsi3_to, &stmap.master[3]);
+      fill_mstmap(uni->lsi4_ctl, uni->lsi4_bs, uni->lsi4_bd, uni->lsi4_to, &stmap.master[4]);
+      fill_mstmap(uni->lsi5_ctl, uni->lsi5_bs, uni->lsi5_bd, uni->lsi5_to, &stmap.master[5]);
+      fill_mstmap(uni->lsi6_ctl, uni->lsi6_bs, uni->lsi6_bd, uni->lsi6_to, &stmap.master[6]);
+      fill_mstmap(uni->lsi7_ctl, uni->lsi7_bs, uni->lsi7_bd, uni->lsi7_to, &stmap.master[7]);
+
+      fill_slvmap(uni->vsi0_ctl, uni->vsi0_bs, uni->vsi0_bd, uni->vsi0_to, &stmap.slave[0]);
+      fill_slvmap(uni->vsi1_ctl, uni->vsi1_bs, uni->vsi1_bd, uni->vsi1_to, &stmap.slave[1]);
+      fill_slvmap(uni->vsi2_ctl, uni->vsi2_bs, uni->vsi2_bd, uni->vsi2_to, &stmap.slave[2]);
+      fill_slvmap(uni->vsi3_ctl, uni->vsi3_bs, uni->vsi3_bd, uni->vsi3_to, &stmap.slave[3]);
+      fill_slvmap(uni->vsi4_ctl, uni->vsi4_bs, uni->vsi4_bd, uni->vsi4_to, &stmap.slave[4]);
+      fill_slvmap(uni->vsi5_ctl, uni->vsi5_bs, uni->vsi5_bd, uni->vsi5_to, &stmap.slave[5]);
+      fill_slvmap(uni->vsi6_ctl, uni->vsi6_bs, uni->vsi6_bd, uni->vsi6_to, &stmap.slave[6]);
+      fill_slvmap(uni->vsi7_ctl, uni->vsi7_bs, uni->vsi7_bd, uni->vsi7_to, &stmap.slave[7]); 
+ 
+      // update level table
+      if (pdata->update_data.irq_mode[0])
+      {
+        kdebug(("vme_rcc(ioctl,VMEUPDATE): IRQ mode parameters are valid\n"))
+        for (i = 0; i < 7; i++)	
+        {
+          irq_level_table[i] = pdata->update_data.irq_mode[i + 1];
+          kdebug(("vme_rcc(ioctl,VMEUPDATE): IRQ mode for level %d = %d\n", i, pdata->update_data.irq_mode[i + 1]))
+        }
+        irq_sysfail =  pdata->update_data.irq_mode[8];
+        kdebug(("vme_rcc(ioctl,VMEUPDATE): IRQ mode for SYSFAIL = %d\n", pdata->update_data.irq_mode[8]))
+      }
+      else
+        kdebug(("vme_rcc(ioctl,VMEUPDATE): IRQ mode parameters are not valid\n"))
+      vmetab_ok = 1;
+      break;
+    } 
+    
+    case VMETEST:
+    {
+      int ret, count;
+
+      ret = copy_from_user(&count, (void *)arg, sizeof(int));
+      if (ret)
+      {
+	kerror(("vme_rcc(ioctl,VMETEST): error %d from copy_from_user\n",ret))
+	return(-VME_EFAULT);
+      }
+
+      count++;
+
+      if (copy_to_user((void *)arg, &count, sizeof(int)))
+      {
+	kerror(("vme_rcc(ioctl,VMETEST): error from copy_to_user\n"))
+	return(-VME_EFAULT);
+      }
+
+      kdebug(("vme_rcc(ioctl,VMETEST): VME_ENOSYS=0x%08x\n",VME_ENOSYS))
+      return(-VME_ENOSYS);
+      break;
+    }
+  }
+  return(0);
+}
+
+
+/*****************************************************/
+static void vme_rcc_vmaOpen(struct vm_area_struct *vma)
+/*****************************************************/
+{ 
+  kdebug(("vme_rcc_vmaOpen: Called\n"));
+}
+
+
+/******************************************************/
+static void vme_rcc_vmaClose(struct vm_area_struct *vma)
+/******************************************************/
+{ 
+  kdebug(("vme_rcc_vmaClose: mmap released\n"));
+}
+
+
+/********************************************************************/
+static int vme_rcc_mmap(struct file *file, struct vm_area_struct *vma)
+/********************************************************************/
+{
+  u_int size, offset;  
+
+  vma->vm_flags |= VM_RESERVED;
+  vma->vm_flags |= VM_LOCKED;
+
+  kdebug(("vme_rcc(mmap): vma->vm_end    = 0x%08x\n", (u_int)vma->vm_end))
+  kdebug(("vme_rcc(mmap): vma->vm_start  = 0x%08x\n", (u_int)vma->vm_start))
+  kdebug(("vme_rcc(mmap): vma->vm_offset = 0x%08x\n", (u_int)vma->vm_pgoff << PAGE_SHIFT))
+  kdebug(("vme_rcc(mmap): vma->vm_flags  = 0x%08x\n", (u_int)vma->vm_flags))
+
+  size = vma->vm_end - vma->vm_start;
+  offset = vma->vm_pgoff << PAGE_SHIFT;
+
+  if(remap_page_range(vma, vma->vm_start, offset, size, vma->vm_page_prot))
+  {
+    kerror(("vme_rcc(mmap): function remap_page_range failed \n"))
+    return(-VME_REMAP);
+  }
+  kdebug(("vme_rcc(mmap): vma->vm_start(2) = 0x%08x\n", (u_int)vma->vm_start))
+
+  vma->vm_ops = &vme_rcc_vm_ops;
+  return(0);
+}
+
+
+/***********************************************************************************************/
+static int vme_rcc_write_procmem(struct file *file, const char *buffer, u_long count, void *data)
+/***********************************************************************************************/
+{
+  int len;
+  struct vme_proc_data_t *fb_data = (struct vme_proc_data_t *)data;
+
+  kdebug(("vme_rcc(write_procmem): vme_rcc_write_procmem called\n"))
+
+  if(count > 99)
+    len = 99;
+  else
+    len = count;
+
+  if (copy_from_user(fb_data->value, buffer, len))
+  {
+    kdebug(("vme_rcc(write_procmem): error from copy_from_user\n"))
+    return(-VME_EFAULT);
+  }
+
+  kdebug(("vme_rcc(write_procmem): len = %d\n", len))
+  fb_data->value[len - 1] = '\0';
+  kdebug(("vme_rcc(write_procmem): text passed = %s\n", fb_data->value))
+
+  if (!strcmp(fb_data->value, "debug"))
+  {
+    debug = 1;
+    kdebug(("vme_rcc(write_procmem): debugging enabled\n"))
+  }
+
+  if (!strcmp(fb_data->value, "nodebug"))
+  {
+    kdebug(("vme_rcc(write_procmem): debugging disabled\n"))
+    debug = 0;
+  }
+
+  if (!strcmp(fb_data->value, "elog"))
+  {
+    kdebug(("vme_rcc(write_procmem): error logging enabled\n"))
+    errorlog = 1;
+  }
+
+  if (!strcmp(fb_data->value, "noelog"))
+  {
+    kdebug(("vme_rcc(write_procmem): error logging disabled\n"))
+    errorlog = 0;
+  }
+  return len;
+}
+
+
+/*****************************************************************************************************/
+static int vme_rcc_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data)
+/*****************************************************************************************************/
+{
+  int nchars = 0, cnt, loop;
+  u_int lint_stat, lint_en, lint_mask;
+  static int len = 0;
+  
+  kdebug(("vme_rcc(vme_rcc_read_procmem): Called with buf    = 0x%08x\n", (u_int)buf));
+  kdebug(("vme_rcc(vme_rcc_read_procmem): Called with *start = 0x%08x\n", (u_int)*start));
+  kdebug(("vme_rcc(vme_rcc_read_procmem): Called with offset = %d\n", (u_int)offset));
+  kdebug(("vme_rcc(vme_rcc_read_procmem): Called with count  = %d\n", count));
+  
+  if (offset == 0)
+  {
+    kdebug(("exp(exp_read_procmem): Creating text....\n"));
+    len = 0;
+    len += sprintf(proc_read_text + len, "RCC VMEbus driver for release %s (based on CVS tag %s)\n", RELEASE_NAME, CVSTAG);
+
+    if (board_type == VP_PSE) len += sprintf(proc_read_text + len, "Board type: VP_PSE\n");
+    if (board_type == VP_PMC) len += sprintf(proc_read_text + len, "Board type: VP_PMC\n");
+    if (board_type == VP_100) len += sprintf(proc_read_text + len, "Board type: VP_100\n");
+    if (board_type == VP_CP1) len += sprintf(proc_read_text + len, "Board type: VP_CP1\n");
+    if (board_type == VP_110) len += sprintf(proc_read_text + len, "Board type: VP_110\n");
+    if (board_type == VP_315) len += sprintf(proc_read_text + len, "Board type: VP_315\n");
+    if (board_type == VP_317) len += sprintf(proc_read_text + len, "Board type: VP_317\n");
+    if (board_type == VP_325) len += sprintf(proc_read_text + len, "Board type: VP_325\n");
+
+    len += sprintf(proc_read_text + len, "\nThe Universe chip %s been initialized with vmeconfig\n", vmetab_ok ? "has" : "has NOT");
+    len += sprintf(proc_read_text + len, "\n SAFE function statistics\n");
+    len += sprintf(proc_read_text + len, " TXFIFO not empty before safe cycle : %d times\n", txfifo_wait_1);
+    len += sprintf(proc_read_text + len, " TXFIFO not empty after safe cycle  : %d times\n", txfifo_wait_2);
+    len += sprintf(proc_read_text + len, "\n VMEbus interrupts\n");
+    len += sprintf(proc_read_text + len, "\n  level  state     enabled  pending  # interrupts\n");
+
+    lint_stat = readl((char*)uni + LINT_STAT);	// read the interrupt bits
+    lint_en = readl((char*)uni + LINT_EN);	// read enabled irq bitsa
+
+    for (cnt = 0; cnt < 7; cnt++)
+    {
+      lint_mask = 1<<(cnt+1);			// BM_Lxxx_VIRQxxx
+
+      if (irq_level_table[cnt] == VME_LEVELISDISABLED)
+	len += sprintf(proc_read_text + len, "      %d  %s  %s  %s\n", cnt+1,"disabled",
+                       "           ", (lint_stat & lint_mask) ? "yes":" no") ;
+      else if (irq_level_table[cnt] == VME_INT_ROAK)
+	len += sprintf(proc_read_text + len, "      %d  %s  %11s  %7s      %8d\n", cnt+1,"ROAK",
+                       (lint_en & lint_mask) ? "yes":" no", (lint_stat & lint_mask) ? "yes":" no",
+                	Interrupt_counters.virq[cnt]);
+      else if (irq_level_table[cnt] == VME_INT_RORA)
+	len += sprintf(proc_read_text + len, "      %d  %s  %11s  %7s      %8d\n", cnt+1,"RORA",
+                       (lint_en & lint_mask) ? "yes":" no", (lint_stat & lint_mask) ? "yes":" no",
+                	Interrupt_counters.virq[cnt]);
+    }
+
+    if (irq_sysfail == VME_LEVELISDISABLED)
+      len += sprintf(proc_read_text + len, "      %d  %s  %s  %s\n", cnt+1, "disabled", "           ", (lint_stat & lint_mask) ? "yes":" no") ;
+		       
+    else 
+      len += sprintf(proc_read_text + len, "SYSFAIL  %s  %11s  %7s      %8d\n", "RORA", (lint_en & 0x4000) ? "yes":" no", (lint_stat & 0x4000) ? "yes":" no", Interrupt_counters.sysfail);
+
+    len += sprintf(proc_read_text + len, "   Bus Error                    %8d\n", berr_int_count);
+    len += sprintf(proc_read_text + len, "   DMA                          %8d\n", Interrupt_counters.dma);
+    len += sprintf(proc_read_text + len, "\n Link Descriptors: \n");
+
+    for (cnt = 0; cnt< VME_VCTSIZE; cnt++) 
+    {
+      if (link_table.link_dsc[cnt].pid)
+      {
+	len += sprintf(proc_read_text + len, "\n Array index     = %02d\n", cnt);
+	len += sprintf(proc_read_text + len, " Pid             = %02d\n", link_table.link_dsc[cnt].pid);
+	len += sprintf(proc_read_text + len, " Vector          = %02d\n", link_table.link_dsc[cnt].vct);
+	len += sprintf(proc_read_text + len, " Level           = %02d\n", link_table.link_dsc[cnt].lvl);
+	len += sprintf(proc_read_text + len, " Type            = %02d\n", link_table.link_dsc[cnt].type);
+	len += sprintf(proc_read_text + len, " Pending         = %02d\n", link_table.link_dsc[cnt].pending);
+	len += sprintf(proc_read_text + len, " # interrupts    = %02d\n", link_table.link_dsc[cnt].total_count);
+	len += sprintf(proc_read_text + len, " group pointer   = 0x%p\n", link_table.link_dsc[cnt].group_ptr);
+	len += sprintf(proc_read_text + len, " Semaphore count = 0x%x\n", link_table.link_dsc[cnt].sem.count.counter);
+	len += sprintf(proc_read_text + len, " Signal #        = %d\n", link_table.link_dsc[cnt].sig);
+      }
+    }
+
+    len += sprintf(proc_read_text + len, " semaphore statistics\n");
+    len += sprintf(proc_read_text + len, " semaphore count = 1 : %d times\n", sem_positive[0]);
+    len += sprintf(proc_read_text + len, " semaphore count > 1 : %d times\n", sem_positive[1]);
+
+    len += sprintf(proc_read_text + len, "\n BERR Links: \n");
+    for (cnt = 0; cnt< VME_MAX_BERR_PROCS; cnt++) 
+    {
+      if (berr_proc_table.berr_link_dsc[cnt].pid)
+      {
+	len += sprintf(proc_read_text + len, "\n Array index     = %02d\n", cnt);
+	len += sprintf(proc_read_text + len, " Pid             = %02d\n", berr_proc_table.berr_link_dsc[cnt].pid);
+	len += sprintf(proc_read_text + len, " Signal #        = %d\n", berr_proc_table.berr_link_dsc[cnt].sig);
+      }
+    }
+
+    len += sprintf(proc_read_text + len, "\n SYSFAIL Link: \n");
+    if (sysfail_link.pid)
+    {
+      len += sprintf(proc_read_text + len, " Pid             = %02d\n", sysfail_link.pid);
+      len += sprintf(proc_read_text + len, " Signal #        = %d\n", sysfail_link.sig);
+    }
+
+    len += sprintf(proc_read_text + len, "\n BERR Descriptor: \n");
+    len += sprintf(proc_read_text + len, " VMEbus address   = 0x%8x\n", berr_dsc.vmeadd);
+    len += sprintf(proc_read_text + len, " address modifier = 0x%8x\n", berr_dsc.am);
+    len += sprintf(proc_read_text + len, " IACK             = %d\n", berr_dsc.iack);
+    len += sprintf(proc_read_text + len, " LWORD*           = %d\n", berr_dsc.lword);
+    len += sprintf(proc_read_text + len, " DS0*             = %d\n", berr_dsc.ds0);
+    len += sprintf(proc_read_text + len, " DS1*             = %d\n", berr_dsc.ds1);
+    len += sprintf(proc_read_text + len, " WRITE*           = %d\n", berr_dsc.wr);
+    len += sprintf(proc_read_text + len, " multiple bit     = 0x%8x\n", berr_dsc.multiple);
+    len += sprintf(proc_read_text + len, " bus error flag   = 0x%8x\n", berr_dsc.flag);
+
+    len += sprintf(proc_read_text + len, "\n Block transfer status\n");
+    len += sprintf(proc_read_text + len, " # of unsuccessful semaphore requests: %d\n",dma_sem_busy);
+    len += sprintf(proc_read_text + len, " List of completed DMA transactions:\n");
+    len += sprintf(proc_read_text + len, " process ID |     handle |   DMA ctrl | DMA counter\n");
+
+    for (loop = 0; loop < VME_DMADONESIZE; loop++)
+    {
+      if (dma_donelist[loop].pid)
+      {
+	len += sprintf(proc_read_text + len, " 0x%08x |", dma_donelist[loop].pid);
+	len += sprintf(proc_read_text + len, " 0x%08x |", dma_donelist[loop].handle);
+	len += sprintf(proc_read_text + len, " 0x%08x |", dma_donelist[loop].ctrl);
+	len += sprintf(proc_read_text + len, "  0x%08x\n", dma_donelist[loop].counter);
+      }
+    }
+
+    len += sprintf(proc_read_text + len, " \n");
+    len += sprintf(proc_read_text + len, "The command 'echo <action> > /proc/vme_rcc', executed as root,\n");
+    len += sprintf(proc_read_text + len, "allows you to interact with the driver. Possible actions are:\n");
+    len += sprintf(proc_read_text + len, "debug   -> Enable debugging\n");
+    len += sprintf(proc_read_text + len, "nodebug -> Disable debugging\n");
+    len += sprintf(proc_read_text + len, "elog    -> Log errors to /var/log/messages\n");
+    len += sprintf(proc_read_text + len, "noelog  -> Do not log errors to /var/log/messages\n");
+  }
+  kdebug(("vme_rcc(vme_rcc_read_procmem): number of characters in text buffer = %d\n", len));
+
+  if (count < (len - offset))
+    nchars = count;
+  else
+    nchars = len - offset;
+  kdebug(("vme_rcc(vme_rcc_read_procmem): min nchars         = %d\n", nchars));
+  
+  if (nchars > 0)
+  {
+    for (loop = 0; loop < nchars; loop++)
+      buf[loop + (offset & (PAGE_SIZE - 1))] = proc_read_text[offset + loop];
+    *start = buf + (offset & (PAGE_SIZE - 1));
+  }
+  else
+  {
+    nchars = 0;
+    *eof = 1;
+  }
+ 
+  kdebug(("vme_rcc(vme_rcc_read_procmem): returning *start   = 0x%08x\n", (u_int)*start));
+  kdebug(("vme_rcc(vme_rcc_read_procmem): returning nchars   = %d\n", nchars));
+  return(nchars);
+}
+
+
+/*****************************/
+/* PCI driver functions      */
+/*****************************/
+
+/*************************************************************************************/
+static int __devinit vme_rcc_Probe(struct pci_dev *dev, const struct pci_device_id *id)   //MJ: for _devinit see p31
+/*************************************************************************************/
+{
+  u_char pci_revision;
+  u_int size, pci_ioaddr = 0;
+  u_long flags;
+  
+  kdebug(("vme_rcc(vme_rcc_Probe): called for device: 0x%04x / 0x%04x\n", dev->vendor, dev->device));
+  kdebug(("vme_rcc(vme_rcc_Probe): Bus: 0x%04x / Devfn 0x%04x\n", dev->bus->number, dev->devfn));
+
+  if (maxcard == 0)
+  {
+    kdebug(("vme_rcc(vme_rcc_Probe): First (and hopefully only) Universe found\n"));
+    universedev = dev;
+    maxcard = 1;
+  }
+  else
+  {
+    kdebug(("vme_rcc(vme_rcc_Probe): Another Universe found (Parallel Universes are not supported)\n"));
+    return(-VME_EIO);
+  }
+
+  // get revision directly from the Tundra
+  pci_read_config_byte(universedev, PCI_REVISION_ID, &pci_revision);
+  kdebug(("vme_rcc(vme_rcc_Probe): revision = %x\n", pci_revision))
+  if (pci_revision < 1)
+  {
+    kerror(("vme_rcc(vme_rcc_Probe): Illegal Universe Revision %d\n", pci_revision))
+    return(-VME_ILLREV);
+  }
+  
+  flags = pci_resource_flags(dev, BAR0);  //MJ: See p317
+  if ((flags & IORESOURCE_MEM) != 0) 
+  {
+    pci_ioaddr = pci_resource_start(dev, BAR0) & PCI_BASE_ADDRESS_MEM_MASK;        //MJ: See p317
+    size = pci_resource_end(dev, BAR0) - pci_resource_start(dev, BAR0);                             //MJ: See p317
+    kdebug(("vme_rcc(vme_rcc_Probe): Mapping %d bytes at PCI address 0x%08x\n", size, pci_ioaddr));
+    uni = (universe_regs_t *)ioremap(pci_ioaddr, size);
+    if (uni == NULL)
+    {
+      kerror(("vme_rcc(vme_rcc_Probe): error from ioremap\n"));
+      return(-VME_EFAULT);
+    }
+    kdebug(("vme_rcc(vme_rcc_Probe): uni is at 0x%08x\n", (u_int)uni));
+  }
+  else 
+  {
+    kerror(("vme_rcc(vme_rcc_Probe): Bar 0 is not a memory resource"));
+    return(-EIO);
+  }
+
+  pci_read_config_byte(universedev, PCI_INTERRUPT_LINE, &vme_irq_line);
+  kdebug(("vme_rcc(vme_rcc_Probe): interrupt line = %d \n", vme_irq_line))
+
+  if (request_irq(vme_irq_line, universe_irq_handler, SA_SHIRQ, "vme_rcc", universe_irq_handler))	
+  {
+    kerror(("vme_rcc(vme_rcc_Probe): request_irq failed on IRQ = %d\n", vme_irq_line))
+    return(-VME_REQIRQ);
+  }
+  kdebug(("vme_rcc(vme_rcc_Probe): request_irq OK\n"))
+
+  // Clear and enable the DMA interrupt
+  uni->lint_stat |= 0x100;
+  uni->lint_en |= 0x100;
+
+  // Read the static master and slave mappings once
+  fill_mstmap(uni->lsi0_ctl, uni->lsi0_bs, uni->lsi0_bd, uni->lsi0_to, &stmap.master[0]);
+  fill_mstmap(uni->lsi1_ctl, uni->lsi1_bs, uni->lsi1_bd, uni->lsi1_to, &stmap.master[1]);
+  fill_mstmap(uni->lsi2_ctl, uni->lsi2_bs, uni->lsi2_bd, uni->lsi2_to, &stmap.master[2]);
+  fill_mstmap(uni->lsi3_ctl, uni->lsi3_bs, uni->lsi3_bd, uni->lsi3_to, &stmap.master[3]);
+  fill_mstmap(uni->lsi4_ctl, uni->lsi4_bs, uni->lsi4_bd, uni->lsi4_to, &stmap.master[4]);
+  fill_mstmap(uni->lsi5_ctl, uni->lsi5_bs, uni->lsi5_bd, uni->lsi5_to, &stmap.master[5]);
+  fill_mstmap(uni->lsi6_ctl, uni->lsi6_bs, uni->lsi6_bd, uni->lsi6_to, &stmap.master[6]);
+  fill_mstmap(uni->lsi7_ctl, uni->lsi7_bs, uni->lsi7_bd, uni->lsi7_to, &stmap.master[7]);
+
+  fill_slvmap(uni->vsi0_ctl, uni->vsi0_bs, uni->vsi0_bd, uni->vsi0_to, &stmap.slave[0]);
+  fill_slvmap(uni->vsi1_ctl, uni->vsi1_bs, uni->vsi1_bd, uni->vsi1_to, &stmap.slave[1]);
+  fill_slvmap(uni->vsi2_ctl, uni->vsi2_bs, uni->vsi2_bd, uni->vsi2_to, &stmap.slave[2]);
+  fill_slvmap(uni->vsi3_ctl, uni->vsi3_bs, uni->vsi3_bd, uni->vsi3_to, &stmap.slave[3]);
+  fill_slvmap(uni->vsi4_ctl, uni->vsi4_bs, uni->vsi4_bd, uni->vsi4_to, &stmap.slave[4]);
+  fill_slvmap(uni->vsi5_ctl, uni->vsi5_bs, uni->vsi5_bd, uni->vsi5_to, &stmap.slave[5]);
+  fill_slvmap(uni->vsi6_ctl, uni->vsi6_bs, uni->vsi6_bd, uni->vsi6_to, &stmap.slave[6]);
+  fill_slvmap(uni->vsi7_ctl, uni->vsi7_bs, uni->vsi7_bd, uni->vsi7_to, &stmap.slave[7]);       
+
+#if defined (VMEDEBUG) || defined (INTDEBUG)
+  marker = (u_int *)ioremap(0x80008000, 1024);
+  *marker=1;
+#endif
+
+  return(0);
+}
+
+
+/*********************************************/
+static void vme_rcc_Remove(struct pci_dev *dev) 
+/*********************************************/
+{
+  u_int i, reg;
+
+  for (i = 0; i < 7; i++)				// restore initial enable bits from vmetab 
+  {
+    kdebug(("vme_rcc(vme_rcc_Remove): irq_level_table[%d] =0x%x\n", i, irq_level_table[i]))
+    if (irq_level_table[i] != VME_LEVELISDISABLED)
+    {
+      reg = readl((char*)uni + LINT_EN);	// read enabled irq bits
+      kdebug(("vme_rcc(vme_rcc_Remove): reg =0x%x\n", reg))
+      reg |= (1<<(i+1)); 			// BM_LINT_VIRQxxx
+      kdebug(("vme_rcc(vme_rcc_Remove): reg =0x%x\n", reg))
+      writel(reg, (char*)uni + LINT_EN);
+    }
+  }
+
+  kdebug(("vme_rcc(vme_rcc_Remove): irqsysfail = 0x%x\n", irq_sysfail))
+  if (irq_sysfail != VME_LEVELISDISABLED)
+  {
+    reg = readl((char*)uni + LINT_EN);	// read enabled irq bits
+    kdebug(("vme_rcc(vme_rcc_Remove): reg =0x%x\n", reg))
+    reg |= 0x4000; 			// SYSFAIL
+    kdebug(("vme_rcc(vme_rcc_Remove): reg =0x%x\n", reg))
+    writel(reg, (char*)uni + LINT_EN);
+  }
+  
+  free_irq(vme_irq_line, universe_irq_handler);	
+  kdebug(("vme_rcc(vme_rcc_Remove): interrupt line %d released\n", vme_irq_line))
+ 
+  iounmap((void *)uni);
+  kerror(("vme_rcc(vme_rcc_Remove): Universe removed\n"));
+  maxcard = 0;
+}
+
+
+/*********************/
+/* Service functions */
+/*********************/
+
+// Interrupt timeout function. Called when an interrupt fails to occur following
+// a call to VME_InterruptWait()
+/************************************/
+static void vme_intTimeout(u_long arg)
+/************************************/
+{
+  VME_WaitInt_t *iPtr;
+  int first_vct;
+
+  iPtr = (VME_WaitInt_t *) arg;
+  kdebug(("vme_rcc(vme_intTimeout): timeout = %d\n",iPtr->timeout))
+  iPtr->timeout = 0;                            // zero to indicate timeout has occurred
+  first_vct = iPtr->int_handle.vector[0];
+  kdebug(("vme_rcc(vme_intTimeout): first vector = 0x%x\n",first_vct))
+  up(&link_table.link_dsc[first_vct].sem);	// wake up WAIT
+}
+
+
+// Interrupt timeout function. Called when an interrupt fails to occur following
+// a call to VME_SysfailInterruptWait()
+/*******************************************/
+static void vme_intSysfailTimeout(u_long arg)
+/*******************************************/
+{
+  VME_WaitInt_t *iPtr;
+
+  iPtr = (VME_WaitInt_t *) arg;
+  kdebug(("vme_rcc(vme_intSysfailTimeout): timeout = %d\n",iPtr->timeout))
+  iPtr->timeout = 0;                            // zero to indicate timeout has occurred
+  up(&sysfail_link.sem);			// wake up WAIT
+}
+
+
+/************************************/
+static void vme_dmaTimeout(u_long arg)
+/************************************/
+{  
+  //MJ:  It is very unclear if there will ever be a time-out
+  //     This function should be removed unless we find a good reason for keeping it
+
+  current_dmahandle.ctrl    = uni->dgcs & 0xf00;
+  current_dmahandle.counter = uni->dtbc;
+  current_dmahandle.timeout = 0;
+
+  kdebug(("vme_rcc(vme_dmaTimeout): current_dmahandle.ctrl    = 0x%08x\n", current_dmahandle.ctrl))
+  kdebug(("vme_rcc(vme_dmaTimeout): current_dmahandle.counter = 0x%08x\n", current_dmahandle.counter))
+  
+  //time-out: reset the DMA controller
+  uni->dctl = 0;
+  uni->dtbc = 0;
+  uni->dla  = 0;
+  uni->dva  = 0;
+  uni->dcpp = 0;
+  uni->dgcs = 0x6f00;
+  
+  if (dma_donelist[current_dmahandle.index].handle != 0xffffffff && dma_donelist[current_dmahandle.index].pid) 
+    kerror(("vme_rcc(vme_dmaTimeout): ERROR: Entry in dma_donelist is not empty\n"))
+ 
+  kdebug(("vme_rcc(vme_dmaTimeout): entering data into array at index %d\n", current_dmahandle.index))
+  dma_donelist[current_dmahandle.index].handle  = current_dmahandle.handle;
+  dma_donelist[current_dmahandle.index].pid     = current_dmahandle.pid;
+  dma_donelist[current_dmahandle.index].ctrl    = current_dmahandle.ctrl;
+  dma_donelist[current_dmahandle.index].counter = current_dmahandle.counter;
+  dma_donelist[current_dmahandle.index].timeout = 0;  //time out
+   
+  up(&dma_semaphore); //DMA reset, we can release the lock        
+}
+
+
+/*******************************/
+static void vme_dma_handler(void)
+/*******************************/
+{  
+  current_dmahandle.ctrl    = uni->dgcs & 0xf00;
+  current_dmahandle.counter = uni->dtbc;
+  
+  kdebug(("vme_rcc(vme_dma_handler): current_dmahandle.ctrl    = 0x%08x\n", current_dmahandle.ctrl))
+  kdebug(("vme_rcc(vme_dma_handler): current_dmahandle.counter = 0x%08x\n", current_dmahandle.counter))
+      
+  if (dma_donelist[current_dmahandle.index].handle != 0xffffffff && dma_donelist[current_dmahandle.index].pid) 
+    kerror(("vme_rcc(vme_dma_handler): ERROR: Entry in dma_donelist is not empty\n"))
+    
+  kdebug(("vme_rcc(vme_dma_handler): entering data into array at index %d\n", current_dmahandle.index))
+  kdebug(("vme_rcc(vme_dma_handler): current_dmahandle.handle = %d\n", current_dmahandle.handle))
+  kdebug(("vme_rcc(vme_dma_handler): current_dmahandle.pid    = %d\n", current_dmahandle.pid))
+  dma_donelist[current_dmahandle.index].handle  = current_dmahandle.handle;
+  dma_donelist[current_dmahandle.index].pid     = current_dmahandle.pid;
+  dma_donelist[current_dmahandle.index].ctrl    = current_dmahandle.ctrl;
+  dma_donelist[current_dmahandle.index].counter = current_dmahandle.counter;
+  dma_donelist[current_dmahandle.index].timeout = 1;  //normal termination, no time out 
+
+  up(&dma_semaphore); //EOT, we can release the lock        
+}
+
+
+/*****************************/
+static void init_cct_berr(void)
+/*****************************/
+{
+  u_char reg;
+
+  reg = inb(BERR_INT_PORT);
+  kdebug(("vme_rcc(init_cct_berr): CCT BERR register = 0x%x\n", reg))
+
+  reg &= ~BERR_INT_MASK;      //Clear the error flag 
+  outb(reg, BERR_INT_PORT);
+  reg |= BERR_INT_ENABLE;     // Enable the BERR interrupt
+  outb(reg, BERR_INT_PORT);
+  
+  berr_interlock = 0;         // For cards with shared interrupts such as the VP325
+  kdebug(("vme_rcc(init_cct_berr): CCT BERR register = 0x%x\n", reg))
+}
+
+
+/*****************************/
+static void mask_cct_berr(void)
+/*****************************/
+{
+  u_char reg;
+
+  berr_interlock = 1;   // For cards with shared interrupts such as the VP325
+  reg = inb(BERR_INT_PORT);
+  reg &= ~BERR_INT_ENABLE;
+  outb(reg, BERR_INT_PORT);
+
+  kdebug(("vme_rcc(mask_cct_berr): CCT BERR register = 0x%x\n", reg))
+}
+
+
+/* VME bus error interrupt function for boards which have the hardware support.
+*  e.g VP PMC/C1x and VP CPI/Pxx boards
+*
+*  Do nothing if the BERR was caused by a DMA operation
+*
+*  If there is a BERR interrupt: 
+*  If the BERR descriptor is empty, fill it and set the count = 1, else just count++
+*  re-enable the latching of the BERR registers in the Universe for evry interrupt.*/
+/**************************/
+static int cct_berrInt(void)
+/**************************/
+{
+  int result = 0;
+  u_char reg;
+  u_int data;
+
+  reg = inb(BERR_INT_PORT);
+  kdebug(("vme_rcc(cct_berrInt): CCT BERR register = 0x%x\n", reg))
+
+  if ((uni->dgcs & 0x700))                      // was there a DMA BERR
+  {
+    kerror(("vme_rcc(cct_berrInt): DMA BERR detected\n"))
+    reg &= ~BERR_INT_MASK;		        // clear VME bus error
+    outb(reg, BERR_INT_PORT);
+
+    // fill the dma_done list
+    current_dmahandle.ctrl    = uni->dgcs & 0xf00;
+    current_dmahandle.counter = uni->dtbc;
+    current_dmahandle.timeout = 0;
+
+    kdebug(("vme_rcc(cct_berrInt): current_dmahandle.ctrl    = 0x%08x\n", current_dmahandle.ctrl))
+    kdebug(("vme_rcc(cct_berrInt): current_dmahandle.counter = 0x%08x\n", current_dmahandle.counter))
+ 
+    //error: reset the DMA controller
+    uni->dctl=0;
+    uni->dtbc=0;
+    uni->dla =0;
+    uni->dva =0;
+    uni->dcpp=0;
+    uni->dgcs=0x6f00;
+    
+    if (berr_dsc.flag == 0)       // copy info from Universe into BERR descriptor
+    { 
+      if (board_type == VP_100 || board_type == VP_110 || board_type == VP_315 || board_type == VP_317 || board_type == VP_325)
+        read_berr_capture();
+      else
+      {
+        berr_dsc.vmeadd = 0xffffffff;
+        berr_dsc.am = 0xffffffff;
+        berr_dsc.iack = 0xffffffff;
+        berr_dsc.multiple = 0xffffffff;
+        berr_dsc.lword = 0xffffffff;
+        berr_dsc.ds0 = 0xffffffff;
+        berr_dsc.ds1 = 0xffffffff;
+        berr_dsc.wr = 0xffffffff;
+      }
+      // log the error
+      kerror(("vme_rcc(cct_berrInt): bus error on VMEbus block transfer, current pid = %d\n", current->pid))
+    }
+          
+    if (dma_donelist[current_dmahandle.index].handle != 0xffffffff && dma_donelist[current_dmahandle.index].pid) 
+      kerror(("vme_rcc(cct_berrInt): ERROR: Entry in dma_donelist is not empty\n"))
+
+    kdebug(("vme_rcc(cct_berrInt): entering data into array at index %d\n", current_dmahandle.index))
+    dma_donelist[current_dmahandle.index].handle  = current_dmahandle.handle;
+    dma_donelist[current_dmahandle.index].pid     = current_dmahandle.pid;
+    dma_donelist[current_dmahandle.index].ctrl    = current_dmahandle.ctrl;
+    dma_donelist[current_dmahandle.index].counter = current_dmahandle.counter;
+    dma_donelist[current_dmahandle.index].timeout = 1;
+
+    up(&dma_semaphore); //Error handled, DMA reset, we can release the lock
+
+    return(result);
+  }
+  
+  if ((reg & BERR_INT_MASK))		        // if VME bus error has occurred
+  {
+    data = readl((char*)uni + PCI_CSR);		// error with coupled cycle ?
+    kdebug(("vme_rcc(cct_berrInt): PCI_CSR = 0x%x\n", data))
+    if (data & 0x08000000)			// check S_TA bit
+    {
+      if (berr_dsc.flag == 0)		        // copy info from Universe into BERR descriptor
+      {
+        if (board_type == VP_100 || board_type == VP_110 || board_type == VP_315 || board_type == VP_317 || board_type == VP_325)
+          read_berr_capture();
+        else
+        {
+          berr_dsc.vmeadd = 0xffffffff;
+          berr_dsc.am = 0xffffffff;
+          berr_dsc.iack = 0xffffffff;
+          berr_dsc.multiple = 0xffffffff;
+          berr_dsc.lword = 0xffffffff;
+          berr_dsc.ds0 = 0xffffffff;
+          berr_dsc.ds1 = 0xffffffff;
+          berr_dsc.wr = 0xffffffff;
+        }
+        // log the error
+        kerror(("vme_rcc(cct_berrInt): bus error on coupled VMEbus cycle, current pid = %d\n", current->pid))
+        // send signals, if anybody registered
+        send_berr_signals();
+        berr_dsc.flag = 1;		                // reset by BusErrorInfoGet or RegisterSignal
+      }
+      writel(data | 0x08000000, (char*)uni + PCI_CSR);	// unlock error in Universe
+    }
+
+    data = readl((char*)uni + V_AMERR);	           	// error with posted cycle ?
+    kdebug(("vme_rcc(cct_berrInt): V_AMERR = 0x%x\n", data))
+    if (data & 0x00800000)			        // V_STAT
+    {
+      if (berr_dsc.flag == 0)		                // copy info from Universe into BERR descriptor
+      {
+        if (board_type == VP_100 || board_type == VP_110 || board_type == VP_315 || board_type == VP_317 || board_type == VP_325)
+          read_berr_capture();
+        else
+        {
+          berr_dsc.am = (data >> 26) & 0x3f;
+          berr_dsc.multiple = (data >> 24) & 0x1;
+          berr_dsc.vmeadd = readl((char*)uni + VAERR);
+          berr_dsc.iack = (data >> 25) & 0x1;
+          berr_dsc.lword = 0xffffffff;
+          berr_dsc.ds0 = 0xffffffff;
+          berr_dsc.ds1 = 0xffffffff;
+          berr_dsc.wr = 0xffffffff;
+        }
+        kdebug(("vme_rcc(cct_berrInt): VAERR = 0x%x\n", berr_dsc.vmeadd))
+
+        // log the error
+        kerror(("vme_rcc(cct_berrInt): VMEbus error: address = 0x%8x, AM code = 0x%8x\n", berr_dsc.vmeadd, berr_dsc.am))
+        // send signals, if anybody registered
+        send_berr_signals();
+        berr_dsc.flag = 1;	// reset by BusErrorInfoGet or RegisterSignal
+      }
+      writel(0x00800000, (char*)uni + V_AMERR);	// unlock error
+    }
+    
+    reg &= ~BERR_INT_MASK;		        // clear VME bus error
+    outb(reg, BERR_INT_PORT);
+    result = 1;		                        // BERR
+    berr_int_count++;
+  }
+  return(result);
+}
+
+
+/***********************************/
+static void sysfail_irq_handler(void)
+/***********************************/
+{
+  u_int lint_en;
+
+  // The SYSFAIL interupt is active as long as the SYSFAIL signal is asserted on the bus
+  // In order to prevent a deadlock we disable the interrupt here and provide an ioctl() to re-enable it
+  lint_en = readl((char*)uni + LINT_EN);		// read enabled irq bits
+  kdebug(("vme_rcc(sysfail_irq_handler): lint_en = 0x%x\n", lint_en))
+  lint_en &= 0xffffbfff;				
+  kdebug(("vme_rcc(sysfail_irq_handler): lint_en after masking = 0x%x\n", lint_en))
+  writel(lint_en, (char*)uni + LINT_EN);	 	//disable SYSFAIL
+
+  if (sysfail_link.pid == 0)	// not linked: "spurious" interrupt
+  {
+    kerror(("vme_rcc(sysfail_irq_handler): SYSFAIL interrupt not linked\n"))
+    kerror(("vme_rcc(sysfail_irq_handler): SYSFAIL interrupt disabled\n"))
+  }
+  else
+  {
+    if (sysfail_link.sig > 0)
+    {
+      kdebug(("vme_rcc(sysfail_irq_handler): pid = %d, signal = %d\n", sysfail_link.pid, sysfail_link.sig))
+      kill_proc (sysfail_link.pid, sysfail_link.sig, 1);
+    }
+    else
+      up(&sysfail_link.sem);
+  } 
+}
+
+
+/************************************/
+static void vme_irq_handler(int level)
+/************************************/
+{
+  int statid, vector, first_vector;
+  u_int lint_en;
+  VME_IntHandle_t* group_ptr;
+
+  #ifdef VMEDEBUG 
+    *marker=((current->pid) << 8) | 0x40; 
+  #endif
+
+  Interrupt_counters.virq[level-1]++;
+  statid = readl((char*)uni + STATID + 4 * level);
+
+  if (statid & 0x00000100) // bus error during IACK cycle
+    kerror(("vme_rcc(vme_irq_handler): bus error during IACK cycle on level %d\n", level))
+  else
+  {
+    vector = statid & 0x000000FF;
+    kdebug(("vme_rcc(vme_irq_handler): vector = 0x%x\n",vector))
+
+    if (link_table.link_dsc[vector].pid == 0)	// not linked: "spurious" interrupt
+      kerror(("vme_rcc(vme_irq_handler): interrupt with vector %d not linked\n", vector))
+    else		// handle it
+    {
+      if (irq_level_table[level-1] == VME_INT_RORA)
+      {
+        lint_en = readl((char*)uni + LINT_EN);		// read enabled irq bits
+        kdebug(("vme_rcc(vme_irq_handler): level = %d, lint_en = 0x%x\n", level, lint_en))
+        lint_en &= ~(1<<level);				// BM_LINT_VIRQxxx
+        kdebug(("vme_rcc(vme_irq_handler): lint_en after masking = 0x%x\n", lint_en))
+        writel(lint_en, (char*)uni + LINT_EN);	 	//disable  SHOULD BE MARKED??
+      }
+
+      link_table.link_dsc[vector].lvl = level;	// store in link descriptor
+      link_table.link_dsc[vector].pending = 1;
+      link_table.link_dsc[vector].total_count++;
+
+      // signal FIRST(common) semaphore in a group
+      // for signals: use signal in the link descriptor for "vector"
+      group_ptr = link_table.link_dsc[vector].group_ptr;
+      if (group_ptr == NULL)
+        first_vector = vector;
+      else 
+        first_vector = group_ptr->vector[0];
+      kdebug(("vme_rcc(vme_irq_handler): first vector = 0x%x\n",first_vector))
+
+      if (link_table.link_dsc[vector].sig > 0)
+      {
+        kdebug(("vme_rcc(vme_irq_handler): pid = %d, signal = %d\n", link_table.link_dsc[vector].pid, link_table.link_dsc[vector].sig))
+        kill_proc (link_table.link_dsc[vector].pid, link_table.link_dsc[vector].sig,1);
+      }
+      else
+      {
+        up(&link_table.link_dsc[first_vector].sem);
+        #ifdef INTDEBUG
+          *marker=((current->pid) << 8) | 0x2;
+          *marker=first_vector;
+        #endif
+      }
+    } 
+  }
+  #ifdef VMEDEBUG 
+    *marker=((current->pid) << 8) | 0x4f; 
+  #endif
+}
+
+
+// This function handles Universe Interrupts & CCT direct BUS error interrupts.
+// The latter are handled first and if found, the function returns.
+/***********************************************************************************/
+static irqreturn_t universe_irq_handler (int irq, void *dev_id, struct pt_regs *regs)
+/***********************************************************************************/
+{
+  u_int lint_stat, lint_estat, lint_en;
+
+  #ifdef VMEDEBUG 
+    *marker=0x10000000 | (current->pid); 
+  #endif
+
+  lint_stat = readl((char*)uni + LINT_STAT);	// read the interrupt bits
+  lint_en = readl((char*)uni + LINT_EN);	// read enabled irq bits
+  lint_estat = lint_stat & lint_en;		// and look only at enabled irqs
+  kdebug(("vme_rcc(universe_irq_handler): lint_estat = 0x%x\n", lint_estat))
+
+  if (!berr_interlock && cct_berrInt())	        // first check and handle CCT VMEbus errors
+    return(IRQ_HANDLED);                        //MJ: to be checked
+
+  //handle all the interrupts sequentially
+
+  if (lint_estat & BM_LINT_VIRQ7) vme_irq_handler(7);
+  if (lint_estat & BM_LINT_VIRQ6) vme_irq_handler(6);
+  if (lint_estat & BM_LINT_VIRQ5) vme_irq_handler(5);
+  if (lint_estat & BM_LINT_VIRQ4) vme_irq_handler(4);
+  if (lint_estat & BM_LINT_VIRQ3) vme_irq_handler(3);
+  if (lint_estat & BM_LINT_VIRQ2) vme_irq_handler(2);
+  if (lint_estat & BM_LINT_VIRQ1) vme_irq_handler(1);
+  
+  if (lint_estat & BM_LINT_SYSFAIL)
+  {
+    Interrupt_counters.sysfail++;    
+    sysfail_irq_handler();
+  }
+  
+  // ERROR CHECKING
+  if (lint_estat & BM_LINT_VOWN)    Interrupt_counters.vown++;
+  if (lint_estat & BM_LINT_ACFAIL)  Interrupt_counters.acfail++;
+  if (lint_estat & BM_LINT_SW_INT)  Interrupt_counters.sw_int++;
+  if (lint_estat & BM_LINT_SW_IACK) Interrupt_counters.sw_iack++;
+  if (lint_estat & BM_LINT_VERR)    Interrupt_counters.verr++;
+  if (lint_estat & BM_LINT_LERR)    Interrupt_counters.lerr++;
+
+  if (lint_estat & BM_LINT_DMA)
+  {
+    #ifdef VMEDEBUG 
+      *marker=0x30000000 | (current->pid); 
+    #endif
+    Interrupt_counters.dma++;
+    vme_dma_handler();
+  }
+    
+  #ifdef VMEDEBUG 
+    *marker=0x40000000 | (current->pid); 
+  #endif
+  
+  writel(lint_estat, (char*)uni + LINT_STAT);   // clear only the enabled bits
+  return(IRQ_HANDLED);   //MJ: foe 2.6 see p 273   //MJ: we also have to treat IRQ_NONE
+}
+
+
+/*************************************************************************************/
+static void fill_mstmap(u_int d1, u_int d2, u_int d3, u_int d4, mstslvmap_t *mstslvmap)
+/*************************************************************************************/
+{
+  if (d3 == 0) 
+    d3 = 0xffffffff;
+  d2 = d2 & 0xfffff000;
+  d4 = d4 & 0xfffff000;
+  mstslvmap->vbase   = d2 + d4;
+  mstslvmap->vtop    = d3 + d4;
+  mstslvmap->pbase   = d2;
+  mstslvmap->ptop    = d3;
+  mstslvmap->am      = (d1 >> 16) & 0x7;   //0 = A16, 1 = A24, 2 = A32
+  mstslvmap->options = d1 & 0x0000f000;
+  mstslvmap->enab    = (d1 >> 31) & 0x1;
+  mstslvmap->wp      = (d1 >> 30) & 0x1;
+  mstslvmap->rp      = 0;
+  kdebug(("vme_rcc(fill_mstmap): mstslvmap->vbase   = 0x%08x @  0x%08x\n", mstslvmap->vbase, (u_int) &mstslvmap->vbase))
+  kdebug(("vme_rcc(fill_mstmap): mstslvmap->vtop    = 0x%08x @  0x%08x\n", mstslvmap->vtop, (u_int) &mstslvmap->vtop))
+  kdebug(("vme_rcc(fill_mstmap): mstslvmap->am      = %d @  0x%08x\n", mstslvmap->am, (u_int) &mstslvmap->am))
+  kdebug(("vme_rcc(fill_mstmap): mstslvmap->options = %d @  0x%08x\n", mstslvmap->options, (u_int) &mstslvmap->options))
+  kdebug(("vme_rcc(fill_mstmap): mstslvmap->enab    = %d @  0x%08x\n", mstslvmap->enab, (u_int) &mstslvmap->enab))
+}
+
+
+/*************************************************************************************/
+static void fill_slvmap(u_int d1, u_int d2, u_int d3, u_int d4, mstslvmap_t *mstslvmap)
+/*************************************************************************************/
+{
+  d2 = d2 & 0xfffff000;
+  d4 = d4 & 0xfffff000;
+  mstslvmap->vbase = d2;
+  mstslvmap->vtop  = d3;
+  mstslvmap->pbase = d2 + d4;
+  mstslvmap->ptop  = d3 + d4;
+  mstslvmap->space = d1 & 0x3;
+  mstslvmap->am    = (d1 >> 16) & 0x7;   //0 = A16, 1 = A24, 2 = A32
+  mstslvmap->enab  = (d1 >> 31) & 0x1;
+  mstslvmap->wp    = (d1 >> 30) & 0x1;
+  mstslvmap->rp    = (d1 >> 29) & 0x1;
+}
+
+
+/*********************************/
+static void send_berr_signals(void)
+/*********************************/
+{
+  u_int i;
+
+  for (i = 0; i < VME_MAX_BERR_PROCS; i++)
+  {
+    if (berr_proc_table.berr_link_dsc[i].pid)
+    {
+      kdebug(("vme_rcc(send_berr_signals): index = %d, pid (current)= %d, signal = %d\n", i, berr_proc_table.berr_link_dsc[i].pid, berr_proc_table.berr_link_dsc[i].sig))
+      kill_proc(berr_proc_table.berr_link_dsc[i].pid, berr_proc_table.berr_link_dsc[i].sig,1);
+    }
+  }
+}
+
+
+/*********************************************************/
+static int berr_check(u_int *addr, u_int *multi, u_int *am)
+/*********************************************************/
+{ 
+  //This function is to look for single cycle related bus errors
+  //It does not handle BERRs of DMAs or IACK cycles
+  u_int data;
+
+  kdebug(("vme_rcc(berr_check): called\n"))
+
+  //lets see if it there was an error with a coupled cycle
+  data = uni->pci_csr;                 //read the target abort status bit
+  if (data & 0x08000000)               //check S_TA bit
+  { 
+    uni->pci_csr = data | 0x08000000;  //clear the bit
+    *addr  = 0xffffffff;
+    *multi = 0xffffffff;
+    *am    = 0xffffffff;
+    kdebug(("vme_rcc(berr_check): BERR on coupled cycle detected\n"))
+    return(VME_BUSERROR);              //bus error
+  }
+
+  //lets see if it there was a VMEbus error with a posted cycle
+  data = uni->v_amerr;
+  if (data & 0x00800000)
+  {
+    *am    = (data >> 26) & 0x3f;
+    *multi = (data >> 24) & 0x1;
+    *addr  = uni->vaerr;
+    uni->v_amerr = 0x00800000;
+    kdebug(("vme_rcc(berr_check): BERR on coupled posted detected\n"))
+    return(VME_BUSERROR);
+  }
+  else
+  {
+    kdebug(("vme_rcc(berr_check): No BERR detected\n"))
+    return(0);                //no error
+  }
+}
+
+
+/*********************************/
+static void read_berr_capture(void)
+/*********************************/
+{  
+  u_char reg, berr_info[16];
+  u_int data, loop;
+  
+  reg = inb(BERR_VP100);
+  if (reg & 0x80)
+  {
+  
+    berr_dsc.vmeadd   = 0xffffffff;
+    berr_dsc.am       = 0xffffffff;
+    berr_dsc.iack     = 0xffffffff;
+    berr_dsc.multiple = 0xffffffff;
+    berr_dsc.lword    = 0xffffffff;
+    berr_dsc.ds0      = 0xffffffff;
+    berr_dsc.ds1      = 0xffffffff;
+    berr_dsc.wr       = 0xffffffff;
+    kerror(("vme_rcc(read_berr_capture): The BERR capture logic was busy. Information could not be read\n"))
+    return;
+  }
+  
+  outb(0x2, BERR_VP100);   // Reset the read sequence
+  for(loop = 0; loop < 16; loop++)
+    berr_info[loop] = inb(BERR_VP100) & 0xf;   // Read the raw data
+  
+  // Assemble the values
+  berr_dsc.vmeadd = (berr_info[0] << 28) +
+                    (berr_info[1] << 24) +
+                    (berr_info[2] << 20) +
+                    (berr_info[3] << 16) +
+                    (berr_info[4] << 12) +
+                    (berr_info[5] << 8) +
+                    (berr_info[6] << 4) +
+                    (berr_info[7] & 0xe);
+  berr_dsc.am       = ((berr_info[8] & 0x3) << 4) + berr_info[9];
+  berr_dsc.multiple = 0xffffffff;
+  berr_dsc.lword    = berr_info[7] & 0x1;
+  berr_dsc.ds0      = (berr_info[8] & 0x4) >> 2;
+  berr_dsc.ds1      = (berr_info[8] & 0x8) >> 3;
+  berr_dsc.wr       = (berr_info[10] & 0x8) >> 3;
+  
+  data = readl((char*)uni + V_AMERR);		// error with IACK cycle ?
+  kdebug(("vme_rcc(read_berr_capture): V_AMERR = 0x%x\n", data))
+  if (data & 0x00800000)			// V_STAT
+    berr_dsc.iack = (data >> 25) & 0x1;
+  else
+    berr_dsc.iack = 0xffffffff;
+
+  kdebug(("vme_rcc(read_berr_capture): berr_dsc.vmeadd = 0x%08x\n", berr_dsc.vmeadd))
+  kdebug(("vme_rcc(read_berr_capture): berr_dsc.am     = 0x%02x\n", berr_dsc.am))
+  kdebug(("vme_rcc(read_berr_capture): berr_dsc.iack   = 0x%02x\n", berr_dsc.iack))
+  kdebug(("vme_rcc(read_berr_capture): berr_dsc.lword  = %d\n", berr_dsc.lword))
+  kdebug(("vme_rcc(read_berr_capture): berr_dsc.ds0    = %d\n", berr_dsc.ds0))
+  kdebug(("vme_rcc(read_berr_capture): berr_dsc.ds1    = %d\n", berr_dsc.ds1))
+  kdebug(("vme_rcc(read_berr_capture): berr_dsc.write  = %d\n", berr_dsc.wr))
+
+  // Reset the capture buffer
+  outb(0x4, BERR_VP100);
+
+  //Reenable the capture
+  outb(0x1, BERR_VP100);
+}