Staging: add dt3155 driver
authorScott Smedley <ss@aao.gov.au>
Fri, 18 Dec 2009 18:54:26 +0000 (10:54 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 4 Mar 2010 00:42:31 +0000 (16:42 -0800)
This is a driver for the DT3155 Digitizer

Signed-off-by: Scott Smedley <ss@aao.gov.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
14 files changed:
drivers/staging/dt3155/Makefile [new file with mode: 0644]
drivers/staging/dt3155/allocator.README [new file with mode: 0644]
drivers/staging/dt3155/allocator.c [new file with mode: 0644]
drivers/staging/dt3155/allocator.h [new file with mode: 0644]
drivers/staging/dt3155/dt3155.h [new file with mode: 0644]
drivers/staging/dt3155/dt3155.sysvinit [new file with mode: 0644]
drivers/staging/dt3155/dt3155_drv.c [new file with mode: 0644]
drivers/staging/dt3155/dt3155_drv.h [new file with mode: 0644]
drivers/staging/dt3155/dt3155_io.c [new file with mode: 0644]
drivers/staging/dt3155/dt3155_io.h [new file with mode: 0644]
drivers/staging/dt3155/dt3155_isr.c [new file with mode: 0644]
drivers/staging/dt3155/dt3155_isr.h [new file with mode: 0644]
drivers/staging/dt3155/pci-compat.h [new file with mode: 0644]
drivers/staging/dt3155/sysdep.h [new file with mode: 0644]

diff --git a/drivers/staging/dt3155/Makefile b/drivers/staging/dt3155/Makefile
new file mode 100644 (file)
index 0000000..1717ffb
--- /dev/null
@@ -0,0 +1,27 @@
+
+ifeq ($(shell [[ `uname -r | cut -f 1,2 -d\.` < 2.6 ]] && echo pre2.6),pre2.6)
+# system with a pre 2.6 kernel _don't_ use kbuild.
+all:
+       $(MAKE) -f Makefile.pre-2.6
+
+clean:
+       rm -f *.o
+
+else
+# systems with a 2.6 or later kernel use kbuild.
+ifneq ($(KERNELRELEASE),)
+obj-m  := dt3155.o
+dt3155-objs := dt3155_drv.o dt3155_isr.o dt3155_io.o allocator.o
+
+else
+KDIR   := /lib/modules/$(shell uname -r)/build
+PWD            := $(shell pwd)
+
+all:
+       $(MAKE) -C $(KDIR) M=$(PWD) modules
+
+clean:
+       rm -rf *.o *.mod *.mod.c *.ko  .dt3155* .allocator.o.cmd  .tmp_versions
+
+endif
+endif
diff --git a/drivers/staging/dt3155/allocator.README b/drivers/staging/dt3155/allocator.README
new file mode 100644 (file)
index 0000000..05700b6
--- /dev/null
@@ -0,0 +1,98 @@
+
+The allocator shown here  exploits high memory. This document explains
+how  a user can  deal   with drivers uses   this  allocator and how  a
+programmer can link in the module.
+
+The module is being used by my pxc and pxdrv device drivers (as well as
+other ones), available from ftp.systemy.it/pub/develop and
+ftp.linux.it/pub/People/Rubini
+
+       User's manual
+       =============
+
+
+One of the most compelling problems with any DMA-capable device is the
+allocation  of a suitable  memory buffer. The "allocator" module tries
+to deal with  the problem in  a clean way.  The module is  able to use
+high   memory  (above the  one   used in  normal   operation)  for DMA
+allocation.
+
+To prevent  the  kernel for using   high memory,  so  that it  remains
+available for  DMA, you should  pass a  command  line argument to  the
+kernel.  Command line arguments  can be passed to  Lilo, to Loadlin or
+to whichever loader  you are using  (unless it's very poor in design).
+For Lilo, either use  "append=" in  /etc/lilo.conf or add  commandline
+arguments to the  interactive prompt. For  example, I have a 32MB  box
+and reserve two megs for DMA:
+
+In lilo.conf:
+       image = /zImage
+       label = linux
+       append = "mem=30M"
+
+Or, interactively:
+       LILO: linux mem=30M
+
+Once  the kernel is booted  with the  right command-line argument, any
+driver  linked   with  the  allocator   module  will  be able   to get
+DMA-capable memory without  much  trouble (unless the  various drivers
+need more memory than available).
+
+The module implements an alloc/free  mechanism,  so that it can  serve
+multiple drivers  at the  same time. Note  however that  the allocator
+uses all of  high memory and assumes to  be the only piece of software
+using such memory.
+
+
+       Programmer's manual
+       ===================
+
+The allocator,  as  released, is designed  to  be linked  to  a device
+driver.  In this  case, the driver  must call allocator_init()  before
+using   the  allocator   and  must  call   allocator_cleanup()  before
+unloading.  This is  usually  done   from within  init_module()    and
+cleanup_module(). If the allocator is linked to  a driver, it won't be
+possible for several drivers to allocate high DMA memory, as explained
+above.
+
+It is possible, on the other hand, to compile the module as a standalone
+module, so that several modules can rely on the allocator for they DMA
+buffers. To compile the allocator as a standalone module, do the
+following in this directory (or provide a suitable Makefile, or edit
+the source code):
+
+       make allocator.o CC="gcc -Dallocator_init=init_module -Dallocator_cleanup=cleanup_module -include /usr/include/linux/module.h"
+
+The previous commandline  tells   to include <linux/module.h>  in  the
+first place,  and to rename the init  and cleanup function to the ones
+needed for  module loading and  unloading.  Drivers using a standalone
+allocator won't need to call allocator_init() nor allocator_cleanup().
+
+The allocator exports the following functions (declared in allocator.h):
+
+   unsigned long allocator_allocate_dma (unsigned long kilobytes,
+                                        int priority);
+
+       This function returns a physical address, over high_memory,
+       which corresponds to an area of at least "kilobytes" kilobytes.
+       The area will be owned by the module calling the function.
+       The returned address can be passed to device boards, to instruct
+       their DMA controllers, via phys_to_bus(). The address can be used
+       by C code after vremap()/ioremap(). The "priority" argument should
+       be GFP_KERNEL or GFP_ATOMIC, according to the context of the
+       caller; it is used to call kmalloc(), as the allocator must keep
+       track of any region it gives away. In case of error the function
+       returns 0, and the caller is expected to issue a -ENOMEM error.
+
+
+   void allocator_free_dma (unsigned long address);
+
+       This function is the reverse of the previous one. If a driver
+       doesn't free the DMA memory it allocated, the allocator will
+       consider such memory as busy. Note, however, that
+       allocator_cleanup() calls kfree() on every region it reclaimed,
+       so that a driver with the allocator linked in can avoid calling
+       allocator_free_dma() at unload time.
+
+
+
diff --git a/drivers/staging/dt3155/allocator.c b/drivers/staging/dt3155/allocator.c
new file mode 100644 (file)
index 0000000..90429db
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * allocator.c -- allocate after high_memory, if available
+ *
+ * NOTE: this is different from my previous allocator, the one that
+ *       assembles pages, which revealed itself both slow and unreliable.
+ *
+ * Copyright (C) 1998   rubini@linux.it (Alessandro Rubini)
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+
+-- Changes --
+
+  Date       Programmer  Description of changes made
+  -------------------------------------------------------------------
+  02-Aug-2002 NJC         allocator now steps in 1MB increments, rather
+                          than doubling its size each time.
+                         Also, allocator_init(u_int *) now returns
+                         (in the first arg) the size of the free
+                         space.  This is no longer consistent with
+                         using the allocator as a module, and some changes
+                         may be necessary for that purpose.  This was
+                         designed to work with the DT3155 driver, in
+                         stand alone mode only!!!
+  26-Oct-2009 SS         Port to 2.6.30 kernel.
+ */
+
+
+#ifndef __KERNEL__
+#  define __KERNEL__
+#endif
+#ifndef MODULE
+#  define MODULE
+#endif
+
+#include <linux/version.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mm.h>  /* PAGE_ALIGN() */
+
+#include <asm/page.h>
+
+#include "sysdep.h"
+
+/*#define ALL_DEBUG*/
+#define ALL_MSG "allocator: "
+
+#undef PDEBUG             /* undef it, just in case */
+#ifdef ALL_DEBUG
+#  define __static
+#  define DUMP_LIST() dump_list()
+#  ifdef __KERNEL__
+     /* This one if debugging is on, and kernel space */
+#    define PDEBUG(fmt, args...) printk( KERN_DEBUG ALL_MSG fmt, ## args)
+#  else
+     /* This one for user space */
+#    define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
+#  endif
+#else
+#  define PDEBUG(fmt, args...) /* not debugging: nothing */
+#  define DUMP_LIST()
+#  define __static static
+#endif
+
+#undef PDEBUGG
+#define PDEBUGG(fmt, args...)
+/*#define PDEBUGG(fmt, args...) printk( KERN_DEBUG ALL_MSG fmt, ## args)*/
+
+
+int allocator_himem = 1; /* 0 = probe, pos. = megs, neg. = disable   */
+int allocator_step = 1;  /* This is the step size in MB              */
+int allocator_probe = 1; /* This is a flag -- 1=probe, 0=don't probe */
+
+static unsigned long allocator_buffer      = 0;  /* physical address */
+static unsigned long allocator_buffer_size = 0;  /* kilobytes */
+
+/*
+ * The allocator keeps a list of DMA areas, so multiple devices
+ * can coexist. The list is kept sorted by address
+ */
+
+struct allocator_struct {
+    unsigned long address;
+    unsigned long size;
+    struct allocator_struct *next;
+};
+
+struct allocator_struct *allocator_list = NULL;
+
+
+#ifdef ALL_DEBUG
+static int dump_list(void)
+{
+    struct allocator_struct *ptr;
+
+    PDEBUG("Current list:\n");
+    for (ptr = allocator_list; ptr; ptr = ptr->next) {
+        PDEBUG("0x%08lx (size %likB)\n",ptr->address,ptr->size>>10);
+    }
+    return 0;
+}
+#endif
+
+/* ========================================================================
+ * This function is the actual allocator.
+ *
+ * If space is available in high memory (as detected at load time), that
+ * one is returned. The return value is a physical address (i.e., it can
+ * be used straight ahead for DMA, but needs remapping for program use).
+ */
+
+unsigned long allocator_allocate_dma (unsigned long kilobytes, int prio)
+{
+    struct allocator_struct *ptr = allocator_list, *newptr;
+    unsigned long bytes = kilobytes << 10;
+
+    /* check if high memory is available */
+    if (!allocator_buffer)
+        return 0;
+
+     /* Round it to a multiple of the pagesize */
+     bytes = PAGE_ALIGN(bytes);
+    PDEBUG("request for %li bytes\n", bytes);
+
+    while (ptr && ptr->next) {
+        if (ptr->next->address - (ptr->address + ptr->size) >= bytes)
+            break; /* enough space */
+        ptr = ptr->next;
+    }
+    if (!ptr->next) {
+        DUMP_LIST();
+        PDEBUG("alloc failed\n");
+        return 0; /* end of list */
+    }
+    newptr = kmalloc(sizeof(struct allocator_struct),prio);
+    if (!newptr)
+        return 0;
+
+    /* ok, now stick it after ptr */
+    newptr->address = ptr->address + ptr->size;
+    newptr->size = bytes;
+    newptr->next = ptr->next;
+    ptr->next = newptr;
+
+    DUMP_LIST();
+    PDEBUG("returning 0x%08lx\n",newptr->address);
+    return newptr->address;
+}
+
+int allocator_free_dma (unsigned long address)
+{
+    struct allocator_struct *ptr = allocator_list, *prev;
+
+    while (ptr && ptr->next) {
+        if (ptr->next->address == address)
+            break;
+       ptr = ptr->next;
+       }
+    /* the one being freed is ptr->next */
+    prev = ptr; ptr = ptr->next;
+
+    if (!ptr) {
+        printk(KERN_ERR ALL_MSG "free_dma(0x%08lx) but add. not allocated\n",
+               ptr->address);
+        return -EINVAL;
+    }
+    PDEBUGG("freeing: %08lx (%li) next %08lx\n",ptr->address,ptr->size,
+          ptr->next->address);
+    prev->next = ptr->next;
+    kfree(ptr);
+
+    /* dump_list(); */
+    return 0;
+}
+
+/* ========================================================================
+ * Init and cleanup
+ *
+ * On cleanup everything is released. If the list is not empty, that a
+ * problem of our clients
+ */
+int allocator_init(u_long *allocator_max)
+{
+    /* check how much free memory is there */
+
+    volatile void *remapped;
+    unsigned long max;
+    unsigned long trial_size = allocator_himem<<20;
+    unsigned long last_trial = 0;
+    unsigned long step = allocator_step<<20;
+    unsigned long i=0;
+    struct allocator_struct *head, *tail;
+    char test_string[]="0123456789abcde"; /* 16 bytes */
+
+    PDEBUGG("himem = %i\n",allocator_himem);
+    if (allocator_himem < 0) /* don't even try */
+        return -EINVAL;
+
+    if (!trial_size) trial_size = 1<<20; /* not specified: try one meg */
+
+    while (1) {
+        remapped = ioremap(__pa(high_memory), trial_size);
+        if (!remapped)
+         {
+           PDEBUGG("%li megs failed!\n",trial_size>>20);
+            break;
+         }
+        PDEBUGG("Trying %li megs (at %p, %p)\n",trial_size>>20,
+               (void *)__pa(high_memory), remapped);
+        for (i=last_trial; i<trial_size; i+=16) {
+            strcpy((char *)(remapped)+i, test_string);
+            if (strcmp((char *)(remapped)+i, test_string))
+                break;
+        }
+        iounmap((void *)remapped);
+        schedule();
+        last_trial = trial_size;
+        if (i==trial_size)
+            trial_size += step; /* increment, if all went well */
+        else
+         {
+           PDEBUGG("%li megs copy test failed!\n",trial_size>>20);
+            break;
+         }
+        if (!allocator_probe) break;
+    }
+    PDEBUG("%li megs (%li k, %li b)\n",i>>20,i>>10,i);
+    allocator_buffer_size = i>>10; /* kilobytes */
+    allocator_buffer = __pa(high_memory);
+    if (!allocator_buffer_size) {
+        printk(KERN_WARNING ALL_MSG "no free high memory to use\n");
+        return -ENOMEM;
+    }
+
+    /*
+     * to simplify things, always have two cells in the list:
+     * the first and the last. This avoids some conditionals and
+     * extra code when allocating and deallocating: we only play
+     * in the middle of the list
+     */
+    head = kmalloc(sizeof(struct allocator_struct),GFP_KERNEL);
+    if (!head)
+        return -ENOMEM;
+    tail = kmalloc(sizeof(struct allocator_struct),GFP_KERNEL);
+    if (!tail) {
+        kfree(head);
+        return -ENOMEM;
+    }
+
+    max = allocator_buffer_size<<10;
+
+    head->size = tail->size = 0;
+    head->address = allocator_buffer;
+    tail->address = allocator_buffer + max;
+    head->next = tail;
+    tail->next = NULL;
+    allocator_list = head;
+
+    *allocator_max = allocator_buffer_size; /* Back to the user code, in KB */
+
+    return 0; /* ok, ready */
+}
+
+void allocator_cleanup(void)
+{
+    struct allocator_struct *ptr, *next;
+
+    for (ptr = allocator_list; ptr; ptr = next) {
+        next = ptr->next;
+        PDEBUG("freeing list: 0x%08lx\n",ptr->address);
+        kfree(ptr);
+    }
+
+    allocator_buffer      = 0;
+    allocator_buffer_size = 0;
+    allocator_list = NULL;
+}
+
+
diff --git a/drivers/staging/dt3155/allocator.h b/drivers/staging/dt3155/allocator.h
new file mode 100644 (file)
index 0000000..ecc8680
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * allocator.h -- prototypes for allocating high memory
+ *
+ * NOTE: this is different from my previous allocator, the one that
+ *       assembles pages, which revealed itself both slow and unreliable.
+ *
+ * Copyright (C) 1998   rubini@linux.it (Alessandro Rubini)
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+void allocator_free_dma(unsigned long address);
+unsigned long allocator_allocate_dma (unsigned long kilobytes, int priority);
+int allocator_init(u_long *);
+void allocator_cleanup(void);
diff --git a/drivers/staging/dt3155/dt3155.h b/drivers/staging/dt3155/dt3155.h
new file mode 100644 (file)
index 0000000..2e15815
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+
+Copyright 1996,2002,2005 Gregory D. Hager, Alfred A. Rizzi, Noah J. Cowan,
+                         Jason Lapenta, Scott Smedley
+
+This file is part of the DT3155 Device Driver.
+
+The DT3155 Device Driver is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The DT3155 Device Driver is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the DT3155 Device Driver; if not, write to the Free
+Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+MA 02111-1307 USA
+
+  $Id: dt3155.h,v 1.11 2005/08/09 06:08:51 ssmedley Exp $
+
+-- Changes --
+
+  Date     Programmer  Description of changes made
+  -------------------------------------------------------------------
+  03-Jul-2000 JML     n/a
+  10-Oct-2001 SS      port to 2.4 kernel.
+  24-Jul-2002 SS      remove unused code & added GPL licence.
+  05-Aug-2005 SS      port to 2.6 kernel; make CCIR mode default.
+
+*/
+
+#ifndef _DT3155_INC
+#define _DT3155_INC
+
+#ifdef __KERNEL__
+#include <linux/types.h>       /* u_int etc. */
+#include <linux/time.h>                /* struct timeval */
+#else
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+
+
+#define TRUE  1
+#define FALSE 0
+
+/* Uncomment this for 50Hz CCIR */
+#define CCIR 1
+
+/* Can be 1 or 2 */
+#define MAXBOARDS 1
+
+#define BOARD_MAX_BUFFS 3
+#define MAXBUFFERS BOARD_MAX_BUFFS*MAXBOARDS
+
+#define PCI_PAGE_SIZE (1 << 12)
+
+#ifdef CCIR
+#define DT3155_MAX_ROWS 576
+#define DT3155_MAX_COLS 768
+#define FORMAT50HZ TRUE
+#else
+#define DT3155_MAX_ROWS 480
+#define DT3155_MAX_COLS 640
+#define FORMAT50HZ FALSE
+#endif
+
+/* Configuration structure */
+struct dt3155_config_s {
+  u_int acq_mode;
+  u_int cols, rows;
+  u_int continuous;
+};
+
+
+/* hold data for each frame */
+typedef struct
+{
+  u_long addr;          /* address of the buffer with the frame */
+  u_long tag;           /* unique number for the frame */
+  struct timeval time;  /* time that capture took place */
+} frame_info_t;
+
+/* Structure for interrupt and buffer handling.  */
+/* This is the setup for 1 card                  */
+struct dt3155_fbuffer_s {
+  int    nbuffers;
+
+  frame_info_t frame_info[ BOARD_MAX_BUFFS ];
+
+  int empty_buffers[ BOARD_MAX_BUFFS ];    /* indexes empty frames */
+  int empty_len;                /* Number of empty buffers  */
+                                /* Zero means empty */
+
+  int active_buf;               /* Where data is currently dma'ing */
+  int locked_buf;               /* Buffers used by user */
+
+  int ready_que[ BOARD_MAX_BUFFS ];
+  u_long ready_head;  /* The most recent buffer located here */
+  u_long ready_len;   /* The number of ready buffers */
+
+  int even_happened;
+  int even_stopped;
+
+  int stop_acquire;             /* Flag to stop interrupts */
+  u_long frame_count;           /* Counter for frames acquired by this card */
+
+};
+
+
+
+#define DT3155_MODE_FRAME 1
+#define DT3155_MODE_FIELD 2
+
+#define DT3155_SNAP 1
+#define DT3155_ACQ  2
+
+/* There is one status structure for each card. */
+typedef struct dt3155_status_s
+{
+  int fixed_mode;              /* if 1, we are in fixed frame mode */
+  u_long reg_addr;             /* Register address for a single card */
+  u_long mem_addr;             /* Buffer start addr for this card */
+  u_long mem_size;             /* This is the amount of mem available  */
+  u_int  irq;                  /* this card's irq */
+  struct dt3155_config_s config;  /* configuration struct */
+  struct dt3155_fbuffer_s fbuffer;/* frame buffer state struct */
+  u_long state;                /* this card's state */
+  u_int device_installed;      /* Flag if installed. 1=installed */
+} dt3155_status_t;
+
+/* Reference to global status structure */
+extern struct dt3155_status_s dt3155_status[MAXBOARDS];
+
+#define DT3155_STATE_IDLE    0x00
+#define DT3155_STATE_FRAME   0x01
+#define DT3155_STATE_FLD     0x02
+#define DT3155_STATE_STOP   0x100
+#define DT3155_STATE_ERROR  0x200
+#define DT3155_STATE_MODE   0x0ff
+
+#define DT3155_IOC_MAGIC     '!'
+
+#define DT3155_SET_CONFIG  _IOW( DT3155_IOC_MAGIC, 1, struct dt3155_config_s )
+#define DT3155_GET_CONFIG  _IOR( DT3155_IOC_MAGIC, 2, struct dt3155_status_s )
+#define DT3155_STOP        _IO(  DT3155_IOC_MAGIC, 3 )
+#define DT3155_START       _IO(  DT3155_IOC_MAGIC, 4 )
+#define DT3155_FLUSH       _IO(  DT3155_IOC_MAGIC, 5 )
+#define DT3155_IOC_MAXNR   5
+
+/* Error codes */
+
+#define DT_ERR_NO_BUFFERS   0x10000 /* not used but it might be one day - SS */
+#define DT_ERR_CORRUPT      0x20000
+#define DT_ERR_OVERRUN      0x30000
+#define DT_ERR_I2C_TIMEOUT  0x40000
+#define DT_ERR_MASK         0xff0000/* not used but it might be one day - SS */
+
+/* User code will probably want to declare one of these for each card */
+typedef struct dt3155_read_s
+{
+  u_long offset;
+  u_long frame_seq;
+  u_long state;
+
+  frame_info_t frame_info;
+} dt3155_read_t;
+
+#endif /* _DT3155_inc */
diff --git a/drivers/staging/dt3155/dt3155.sysvinit b/drivers/staging/dt3155/dt3155.sysvinit
new file mode 100644 (file)
index 0000000..92ec093
--- /dev/null
@@ -0,0 +1,60 @@
+#! /bin/sh
+#
+# Module load/unload script for use with SysV-style /etc/init.d/ systems.
+# On a Debian system, copy this to /etc/init.d/dt3155 and then run
+#      /usr/sbin/update-rc.d dt3155 defaults 55
+# to create the appropriate /etc/rc?.d/[SK]55dt3155 start/stop links.
+# (The "55" is arbitrary but is what I use to load this rather late.)
+#
+#    Andy Dougherty   Feb 22 2000      doughera@lafayette.edu
+#    Dept. of Physics
+#    Lafayette College, Easton PA 18042
+#
+
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+
+# Edit to point to your local copy.
+FILE=/usr/local/lib/modules/dt3155/dt3155.o
+NAME="dt3155"
+DESC="dt3155 Frame Grabber module"
+DEV="dt3155"
+
+if test ! -f $FILE; then
+    echo "Unable to locate $FILE"
+    exit 0
+fi
+
+set -e
+
+case "$1" in
+  start)
+    echo -n "Loading $DESC "
+    if /sbin/insmod -v -f $FILE; then
+       major=`grep $DEV /proc/devices | awk "{print \\$1}"`
+       rm -f /dev/dt3155?
+       mknod /dev/dt3155a c $major 0
+       mknod /dev/dt3155b c $major 1
+       chmod go+rw /dev/dt3155?
+       echo
+    else
+       echo "$FILE not loaded."
+    fi
+    ;;
+  stop)
+    echo -n "Unloading $DESC: "
+    if /sbin/rmmod $NAME ; then
+       echo
+    else
+       echo "$DEV not removed"
+       exit 0
+    fi
+    rm -f /dev/dt3155?
+    ;;
+  *)
+    echo "Usage: /etc/init.d/$NAME {start|stop}"
+    exit 1
+    ;;
+esac
+
+exit 0
+
diff --git a/drivers/staging/dt3155/dt3155_drv.c b/drivers/staging/dt3155/dt3155_drv.c
new file mode 100644 (file)
index 0000000..f3ae8e8
--- /dev/null
@@ -0,0 +1,1203 @@
+/*
+
+Copyright 1996,2002,2005 Gregory D. Hager, Alfred A. Rizzi, Noah J. Cowan,
+                         Jason Lapenta, Scott Smedley, Greg Sharp
+
+This file is part of the DT3155 Device Driver.
+
+The DT3155 Device Driver is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The DT3155 Device Driver is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the DT3155 Device Driver; if not, write to the Free
+Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+MA 02111-1307 USA
+
+-- Changes --
+
+  Date     Programmer  Description of changes made
+  -------------------------------------------------------------------
+  03-Jul-2000 JML       n/a
+  10-Oct-2001 SS        port to 2.4 kernel
+  02-Apr-2002 SS        Mods to use allocator as a standalone module;
+                        Merged John Roll's changes (john@cfa.harvard.edu)
+                        to make work with multiple boards.
+  02-Jul-2002 SS        Merged James Rose's chages (rosejr@purdue.edu) to:
+                         * fix successive interrupt-driven captures
+                         * add select/poll support.
+  10-Jul-2002 GCS       Add error check when ndevices > MAXBOARDS.
+  02-Aug-2002 GCS       Fix field mode so that odd (lower) field is stored
+                        in lower half of buffer.
+  05-Aug-2005 SS        port to 2.6 kernel.
+  26-Oct-2009 SS       port to 2.6.30 kernel.
+
+-- Notes --
+
+** appended "mem=124" in lilo.conf to allow for 4megs free on my 128meg system.
+ * using allocator.c and allocator.h from o'reilly book (alessandro rubini)
+    ftp://ftp.systemy.it/pub/develop (see README.allocator)
+
+ + might want to get rid of MAXboards for allocating initial buffer.
+    confusing and not necessary
+
+ + in cleanup_module the MOD_IN_USE looks like it is check after it should
+
+ * GFP_DMA should not be set with a PCI system (pg 291)
+
+ - NJC why are only two buffers allowed? (see isr, approx line 358)
+
+*/
+
+extern void printques(int);
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/interrupt.h>
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10)
+MODULE_LICENSE("GPL");
+#endif
+
+#endif
+
+#ifndef CONFIG_PCI
+#error  "DT3155 :  Kernel PCI support not enabled (DT3155 drive requires PCI)"
+#endif
+
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/poll.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include "dt3155.h"
+#include "dt3155_drv.h"
+#include "dt3155_isr.h"
+#include "dt3155_io.h"
+#include "allocator.h"
+
+/* Error variable.  Zero means no error. */
+int dt3155_errno = 0;
+
+#ifndef PCI_DEVICE_ID_INTEL_7116
+#define PCI_DEVICE_ID_INTEL_7116 0x1223
+#endif
+
+#define DT3155_VENDORID    PCI_VENDOR_ID_INTEL
+#define DT3155_DEVICEID    PCI_DEVICE_ID_INTEL_7116
+#define MAXPCI    16
+
+#ifdef DT_DEBUG
+#define DT_3155_DEBUG_MSG(x,y) printk(x,y)
+#else
+#define DT_3155_DEBUG_MSG(x,y)
+#endif
+
+/* wait queue for interrupts */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1)
+wait_queue_head_t dt3155_read_wait_queue[ MAXBOARDS ];
+#else
+struct wait_queue *dt3155_read_wait_queue[ MAXBOARDS ];
+#endif
+
+#define DT_3155_SUCCESS 0
+#define DT_3155_FAILURE -EIO
+
+/* set to dynamicaly allocate, but it is tunable: */
+/* insmod DT_3155 dt3155 dt3155_major=XX */
+int dt3155_major = 0;
+
+/* The minor numbers are 0 and 1 ... they are not tunable.
+ * They are used as the indices for the structure vectors,
+ * and register address vectors
+ */
+
+/* Global structures and variables */
+
+/* Status of each device */
+struct dt3155_status_s dt3155_status[ MAXBOARDS ];
+
+/* kernel logical address of the board */
+u_char *dt3155_lbase[ MAXBOARDS ] = { NULL
+#if MAXBOARDS == 2
+                                     , NULL
+#endif
+};
+/* DT3155 registers              */
+u_char *dt3155_bbase = NULL;             /* kernel logical address of the *
+                                          * buffer region                 */
+u_int  dt3155_dev_open[ MAXBOARDS ] = {0
+#if MAXBOARDS == 2
+                                      , 0
+#endif
+};
+
+u_int  ndevices = 0;
+u_long unique_tag = 0;;
+
+
+/*
+ * Stops interrupt generation right away and resets the status
+ * to idle.  I don't know why this works and the other way doesn't.
+ * (James Rose)
+ */
+static void quick_stop (int minor)
+{
+  // TODO: scott was here
+#if 1
+  ReadMReg((dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg);
+  /* disable interrupts */
+  int_csr_r.fld.FLD_END_EVE_EN = 0;
+  int_csr_r.fld.FLD_END_ODD_EN = 0;
+  WriteMReg((dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg );
+
+  dt3155_status[ minor ].state &= ~(DT3155_STATE_STOP|0xff);
+  /* mark the system stopped: */
+  dt3155_status[ minor ].state |= DT3155_STATE_IDLE;
+  dt3155_fbuffer[ minor ]->stop_acquire = 0;
+  dt3155_fbuffer[ minor ]->even_stopped = 0;
+#else
+  dt3155_status[minor].state |= DT3155_STATE_STOP;
+  dt3155_status[minor].fbuffer.stop_acquire = 1;
+#endif
+
+}
+
+
+/*****************************************************
+ *  dt3155_isr() Interrupt service routien
+ *
+ * - looks like this isr supports IRQ sharing (or could) JML
+ * - Assumes irq's are disabled, via SA_INTERRUPT flag
+ * being set in request_irq() call from init_module()
+ *****************************************************/
+static inline void dt3155_isr( int irq, void *dev_id, struct pt_regs *regs )
+{
+  int    minor = -1;
+  int    index;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+  unsigned long flags;
+#else
+  int    flags;
+#endif
+  u_long buffer_addr;
+
+  /* find out who issued the interrupt */
+  for ( index = 0; index < ndevices; index++ ) {
+    if( dev_id == (void*) &dt3155_status[ index ])
+      {
+       minor = index;
+       break;
+      }
+  }
+
+  /* hopefully we should not get here */
+  if ( minor < 0 || minor >= MAXBOARDS ) {
+    printk(KERN_ERR "dt3155_isr called with invalid dev_id\n");
+    return;
+  }
+
+  /* Check for corruption and set a flag if so */
+  ReadMReg( (dt3155_lbase[ minor ] + CSR1), csr1_r.reg );
+
+  if ( (csr1_r.fld.FLD_CRPT_EVE) || (csr1_r.fld.FLD_CRPT_ODD) )
+    {
+      /* TODO: this should probably stop acquisition */
+      /* and set some flags so that dt3155_read      */
+      /* returns an error next time it is called     */
+      dt3155_errno = DT_ERR_CORRUPT;
+      printk("dt3155:  corrupt field\n");
+      return;
+    }
+
+  ReadMReg((dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg);
+
+  /* Handle the even field ... */
+  if (int_csr_r.fld.FLD_END_EVE)
+    {
+      if ( (dt3155_status[ minor ].state & DT3155_STATE_MODE) ==
+          DT3155_STATE_FLD )
+       {
+         dt3155_fbuffer[ minor ]->frame_count++;
+       }
+
+      ReadI2C(dt3155_lbase[ minor ], EVEN_CSR, &i2c_even_csr.reg);
+
+      /* Clear the interrupt? */
+      int_csr_r.fld.FLD_END_EVE = 1;
+
+      /* disable the interrupt if last field */
+      if (dt3155_fbuffer[ minor ]->stop_acquire)
+       {
+         printk("dt3155:  even stopped.\n");
+         dt3155_fbuffer[ minor ]->even_stopped = 1;
+         if (i2c_even_csr.fld.SNGL_EVE)
+           {
+             int_csr_r.fld.FLD_END_EVE_EN = 0;
+           }
+         else
+           {
+             i2c_even_csr.fld.SNGL_EVE  = 1;
+           }
+       }
+
+      WriteMReg( (dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg );
+
+      /* Set up next DMA if we are doing FIELDS */
+      if ( (dt3155_status[ minor ].state & DT3155_STATE_MODE ) ==
+          DT3155_STATE_FLD)
+       {
+         /* GCS (Aug 2, 2002) -- In field mode, dma the odd field
+            into the lower half of the buffer */
+         const u_long stride =  dt3155_status[ minor ].config.cols;
+         buffer_addr = dt3155_fbuffer[ minor ]->
+           frame_info[ dt3155_fbuffer[ minor ]->active_buf ].addr
+           + (DT3155_MAX_ROWS / 2) * stride;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+         local_save_flags(flags);
+         local_irq_disable();
+#else
+         save_flags( flags );
+         cli();
+#endif
+         wake_up_interruptible( &dt3155_read_wait_queue[ minor ] );
+
+         /* Set up the DMA address for the next field */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+         local_irq_restore(flags);
+#else
+         restore_flags( flags );
+#endif
+         WriteMReg((dt3155_lbase[ minor ] + ODD_DMA_START), buffer_addr);
+       }
+
+      /* Check for errors. */
+      i2c_even_csr.fld.DONE_EVE = 1;
+      if ( i2c_even_csr.fld.ERROR_EVE )
+       dt3155_errno = DT_ERR_OVERRUN;
+
+      WriteI2C( dt3155_lbase[ minor ], EVEN_CSR, i2c_even_csr.reg );
+
+      /* Note that we actually saw an even field meaning  */
+      /* that subsequent odd field complete the frame     */
+      dt3155_fbuffer[ minor ]->even_happened = 1;
+
+      /* recording the time that the even field finished, this should be */
+      /* about time in the middle of the frame */
+      do_gettimeofday( &(dt3155_fbuffer[ minor ]->
+                        frame_info[ dt3155_fbuffer[ minor ]->
+                                    active_buf ].time) );
+      return;
+    }
+
+  /* ... now handle the odd field */
+  if ( int_csr_r.fld.FLD_END_ODD )
+    {
+      ReadI2C( dt3155_lbase[ minor ], ODD_CSR, &i2c_odd_csr.reg );
+
+      /* Clear the interrupt? */
+      int_csr_r.fld.FLD_END_ODD = 1;
+
+      if (dt3155_fbuffer[ minor ]->even_happened ||
+         (dt3155_status[ minor ].state & DT3155_STATE_MODE) ==
+         DT3155_STATE_FLD)
+       {
+         dt3155_fbuffer[ minor ]->frame_count++;
+       }
+
+      if ( dt3155_fbuffer[ minor ]->stop_acquire &&
+          dt3155_fbuffer[ minor ]->even_stopped )
+       {
+         printk(KERN_DEBUG "dt3155:  stopping odd..\n");
+         if ( i2c_odd_csr.fld.SNGL_ODD )
+           {
+             /* disable interrupts */
+             int_csr_r.fld.FLD_END_ODD_EN = 0;
+             dt3155_status[ minor ].state &= ~(DT3155_STATE_STOP|0xff);
+
+             /* mark the system stopped: */
+             dt3155_status[ minor ].state |= DT3155_STATE_IDLE;
+             dt3155_fbuffer[ minor ]->stop_acquire = 0;
+             dt3155_fbuffer[ minor ]->even_stopped = 0;
+
+             printk(KERN_DEBUG "dt3155:  state is now %lx\n",
+                    dt3155_status[minor].state);
+           }
+         else
+           {
+             i2c_odd_csr.fld.SNGL_ODD  = 1;
+           }
+       }
+
+      WriteMReg( (dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg );
+
+      /* if the odd field has been acquired, then     */
+      /* change the next dma location for both fields */
+      /* and wake up the process if sleeping          */
+      if ( dt3155_fbuffer[ minor ]->even_happened ||
+          (dt3155_status[ minor ].state & DT3155_STATE_MODE) ==
+          DT3155_STATE_FLD )
+       {
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+         local_save_flags(flags);
+         local_irq_disable();
+#else
+         save_flags( flags );
+         cli();
+#endif
+
+#ifdef DEBUG_QUES_B
+         printques( minor );
+#endif
+         if ( dt3155_fbuffer[ minor ]->nbuffers > 2 )
+           {
+             if ( !are_empty_buffers( minor ) )
+               {
+                 /* The number of active + locked buffers is
+                  * at most 2, and since there are none empty, there
+                  * must be at least nbuffers-2 ready buffers.
+                  * This is where we 'drop frames', oldest first. */
+                 push_empty( pop_ready( minor ),  minor );
+               }
+
+             /* The ready_que can't be full, since we know
+              * there is one active buffer right now, so it's safe
+              * to push the active buf on the ready_que. */
+             push_ready( minor, dt3155_fbuffer[ minor ]->active_buf );
+             /* There's at least 1 empty -- make it active */
+             dt3155_fbuffer[ minor ]->active_buf = pop_empty( minor );
+             dt3155_fbuffer[ minor ]->
+               frame_info[ dt3155_fbuffer[ minor ]->
+                           active_buf ].tag = ++unique_tag;
+           }
+         else /* nbuffers == 2, special case */
+           { /* There is 1 active buffer.
+              * If there is a locked buffer, keep the active buffer
+              * the same -- that means we drop a frame.
+              */
+             if ( dt3155_fbuffer[ minor ]->locked_buf < 0 )
+               {
+                 push_ready( minor,
+                             dt3155_fbuffer[ minor ]->active_buf );
+                 if (are_empty_buffers( minor ) )
+                   {
+                     dt3155_fbuffer[ minor ]->active_buf =
+                       pop_empty( minor );
+                   }
+                 else
+                   { /* no empty or locked buffers, so use a readybuf */
+                     dt3155_fbuffer[ minor ]->active_buf =
+                       pop_ready( minor );
+                   }
+               }
+           }
+
+#ifdef DEBUG_QUES_B
+         printques( minor );
+#endif
+
+         dt3155_fbuffer[ minor ]->even_happened = 0;
+
+         wake_up_interruptible( &dt3155_read_wait_queue[ minor ] );
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+         local_irq_restore(flags);
+#else
+         restore_flags( flags );
+#endif
+       }
+
+
+      /* Set up the DMA address for the next frame/field */
+      buffer_addr = dt3155_fbuffer[ minor ]->
+       frame_info[ dt3155_fbuffer[ minor ]->active_buf ].addr;
+      if ( (dt3155_status[ minor ].state & DT3155_STATE_MODE) ==
+          DT3155_STATE_FLD )
+       {
+         WriteMReg((dt3155_lbase[ minor ] + EVEN_DMA_START), buffer_addr);
+       }
+      else
+       {
+         WriteMReg((dt3155_lbase[ minor ] + EVEN_DMA_START), buffer_addr);
+
+         WriteMReg((dt3155_lbase[ minor ] + ODD_DMA_START), buffer_addr
+                   + dt3155_status[ minor ].config.cols);
+       }
+
+      /* Do error checking */
+      i2c_odd_csr.fld.DONE_ODD = 1;
+      if ( i2c_odd_csr.fld.ERROR_ODD )
+       dt3155_errno = DT_ERR_OVERRUN;
+
+      WriteI2C(dt3155_lbase[ minor ], ODD_CSR, i2c_odd_csr.reg );
+
+      return;
+    }
+  /* If we get here, the Odd Field wasn't it either... */
+  printk( "neither even nor odd.  shared perhaps?\n");
+}
+
+/*****************************************************
+ * init_isr(int minor)
+ *   turns on interupt generation for the card
+ *   designated by "minor".
+ *   It is called *only* from inside ioctl().
+ *****************************************************/
+static void dt3155_init_isr(int minor)
+{
+  const u_long stride =  dt3155_status[ minor ].config.cols;
+
+  switch (dt3155_status[ minor ].state & DT3155_STATE_MODE)
+    {
+    case DT3155_STATE_FLD:
+      {
+       even_dma_start_r  = dt3155_status[ minor ].
+         fbuffer.frame_info[ dt3155_status[ minor ].fbuffer.active_buf ].addr;
+       even_dma_stride_r = 0;
+       odd_dma_stride_r  = 0;
+
+       WriteMReg((dt3155_lbase[ minor ] + EVEN_DMA_START),
+                 even_dma_start_r);
+       WriteMReg((dt3155_lbase[ minor ] + EVEN_DMA_STRIDE),
+                 even_dma_stride_r);
+       WriteMReg((dt3155_lbase[ minor ] + ODD_DMA_STRIDE),
+                 odd_dma_stride_r);
+       break;
+      }
+
+    case DT3155_STATE_FRAME:
+    default:
+      {
+       even_dma_start_r  = dt3155_status[ minor ].
+         fbuffer.frame_info[ dt3155_status[ minor ].fbuffer.active_buf ].addr;
+       odd_dma_start_r   =  even_dma_start_r + stride;
+       even_dma_stride_r =  stride;
+       odd_dma_stride_r  =  stride;
+
+       WriteMReg((dt3155_lbase[ minor ] + EVEN_DMA_START),
+                 even_dma_start_r);
+       WriteMReg((dt3155_lbase[ minor ] + ODD_DMA_START),
+                 odd_dma_start_r);
+       WriteMReg((dt3155_lbase[ minor ] + EVEN_DMA_STRIDE),
+                 even_dma_stride_r);
+       WriteMReg((dt3155_lbase[ minor ] + ODD_DMA_STRIDE),
+                 odd_dma_stride_r);
+       break;
+      }
+    }
+
+  /* 50/60 Hz should be set before this point but let's make sure it is */
+  /* right anyway */
+
+  ReadI2C(dt3155_lbase[ minor ], CONFIG, &i2c_csr2.reg);
+  i2c_csr2.fld.HZ50 = FORMAT50HZ;
+  WriteI2C(dt3155_lbase[ minor ], CONFIG, i2c_config.reg);
+
+  /* enable busmaster chip, clear flags */
+
+  /*
+   * TODO:
+   * shouldn't we be concered with continuous values of
+   * DT3155_SNAP & DT3155_ACQ here? (SS)
+   */
+
+  csr1_r.reg                = 0;
+  csr1_r.fld.CAP_CONT_EVE   = 1; /* use continuous capture bits to */
+  csr1_r.fld.CAP_CONT_ODD   = 1; /* enable */
+  csr1_r.fld.FLD_DN_EVE     = 1; /* writing a 1 clears flags */
+  csr1_r.fld.FLD_DN_ODD     = 1;
+  csr1_r.fld.SRST           = 1; /* reset        - must be 1 */
+  csr1_r.fld.FIFO_EN        = 1; /* fifo control - must be 1 */
+  csr1_r.fld.FLD_CRPT_EVE   = 1; /* writing a 1 clears flags */
+  csr1_r.fld.FLD_CRPT_ODD   = 1;
+
+  WriteMReg((dt3155_lbase[ minor ] + CSR1),csr1_r.reg);
+
+  /* Enable interrupts at the end of each field */
+
+  int_csr_r.reg = 0;
+  int_csr_r.fld.FLD_END_EVE_EN = 1;
+  int_csr_r.fld.FLD_END_ODD_EN = 1;
+  int_csr_r.fld.FLD_START_EN = 0;
+
+  WriteMReg((dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg);
+
+  /* start internal BUSY bits */
+
+  ReadI2C(dt3155_lbase[ minor ], CSR2, &i2c_csr2.reg);
+  i2c_csr2.fld.BUSY_ODD  = 1;
+  i2c_csr2.fld.BUSY_EVE  = 1;
+  WriteI2C(dt3155_lbase[ minor ], CSR2, i2c_csr2.reg);
+
+  /* Now its up to the interrupt routine!! */
+
+  return;
+}
+
+
+/*****************************************************
+ * ioctl()
+ *
+ *****************************************************/
+static int dt3155_ioctl (
+                        struct inode   *inode,
+                        struct file            *file,
+                        u_int                  cmd,
+                        u_long                 arg)
+{
+  int minor = MINOR(inode->i_rdev); /* What device are we ioctl()'ing? */
+
+  if ( minor >= MAXBOARDS || minor < 0 )
+    return -ENODEV;
+
+  /* make sure it is valid command */
+  if (_IOC_NR(cmd) > DT3155_IOC_MAXNR)
+    {
+      printk("DT3155: invalid IOCTL(0x%x)\n",cmd);
+      printk("DT3155: Valid commands (0x%x), (0x%x), (0x%x), (0x%x), (0x%x)\n",
+            DT3155_GET_CONFIG, DT3155_SET_CONFIG,
+            DT3155_START, DT3155_STOP, DT3155_FLUSH);
+      return -EINVAL;
+    }
+
+  switch (cmd)
+    {
+    case DT3155_SET_CONFIG:
+      {
+       if (dt3155_status[minor].state != DT3155_STATE_IDLE)
+         return -EBUSY;
+
+       {
+         struct dt3155_config_s tmp;
+         if (copy_from_user((void *)&tmp, (void *) arg, sizeof(tmp)))
+             return -EFAULT;
+         /* check for valid settings */
+         if (tmp.rows > DT3155_MAX_ROWS ||
+             tmp.cols > DT3155_MAX_COLS ||
+             (tmp.acq_mode != DT3155_MODE_FRAME &&
+              tmp.acq_mode != DT3155_MODE_FIELD) ||
+             (tmp.continuous != DT3155_SNAP &&
+              tmp.continuous != DT3155_ACQ))
+           {
+             return -EINVAL;
+           }
+         dt3155_status[minor].config = tmp;
+       }
+       return 0;
+      }
+    case DT3155_GET_CONFIG:
+      {
+       if (copy_to_user((void *) arg, (void *) &dt3155_status[minor],
+                    sizeof(dt3155_status_t) ))
+           return -EFAULT;
+       return 0;
+      }
+    case DT3155_FLUSH: /* Flushes the buffers -- ensures fresh data */
+      {
+       if (dt3155_status[minor].state != DT3155_STATE_IDLE)
+         return -EBUSY;
+       return dt3155_flush(minor);
+      }
+    case DT3155_STOP:
+      {
+       if (dt3155_status[minor].state & DT3155_STATE_STOP ||
+           dt3155_status[minor].fbuffer.stop_acquire)
+         return -EBUSY;
+
+       if (dt3155_status[minor].state == DT3155_STATE_IDLE)
+         return 0;
+
+       quick_stop(minor);
+       if (copy_to_user((void *) arg, (void *) &dt3155_status[minor],
+                    sizeof(dt3155_status_t)))
+           return -EFAULT;
+       return 0;
+      }
+    case DT3155_START:
+      {
+       if (dt3155_status[minor].state != DT3155_STATE_IDLE)
+         return -EBUSY;
+
+       dt3155_status[minor].fbuffer.stop_acquire = 0;
+       dt3155_status[minor].fbuffer.frame_count = 0;
+
+       /* Set the MODE in the status -- we default to FRAME */
+       if (dt3155_status[minor].config.acq_mode == DT3155_MODE_FIELD)
+         {
+           dt3155_status[minor].state = DT3155_STATE_FLD;
+         }
+       else
+         {
+           dt3155_status[minor].state = DT3155_STATE_FRAME;
+         }
+
+       dt3155_init_isr(minor);
+       if (copy_to_user( (void *) arg, (void *) &dt3155_status[minor],
+                     sizeof(dt3155_status_t)))
+           return -EFAULT;
+       return 0;
+      }
+    default:
+      {
+       printk("DT3155: invalid IOCTL(0x%x)\n",cmd);
+      printk("DT3155: Valid commands (0x%x), (0x%x), (0x%x), (0x%x), (0x%x)\n",
+            DT3155_GET_CONFIG, DT3155_SET_CONFIG,
+            DT3155_START, DT3155_STOP, DT3155_FLUSH);
+       return -ENOSYS;
+      }
+    }
+  return -ENOSYS;
+}
+
+/*****************************************************
+ * mmap()
+ *
+ * only allow the user to mmap the registers and buffer
+ * It is quite possible that this is broken, since the
+ * addition of of the capacity for two cards!!!!!!!!
+ * It *looks* like it should work but since I'm not
+ * sure how to use it, I'm not actually sure. (NJC? ditto by SS)
+ *****************************************************/
+static int dt3155_mmap (struct file * file, struct vm_area_struct * vma)
+{
+  /* which device are we mmapping? */
+  int                          minor = MINOR(file->f_dentry->d_inode->i_rdev);
+  unsigned long        offset;
+
+  /* not actually sure when vm_area_struct changed,
+     but it was in 2.3 sometime */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,20)
+
+  offset = vma->vm_pgoff << PAGE_SHIFT;
+
+  if (offset >= __pa(high_memory) || (file->f_flags & O_SYNC))
+    vma->vm_flags |= VM_IO;
+
+  /* Don't try to swap out physical pages.. */
+  vma->vm_flags |= VM_RESERVED;
+
+#else
+
+  if (vma->vm_offset & ~PAGE_MASK)
+    return -ENXIO;
+
+  offset = vma->vm_offset;
+
+#endif
+
+  /* they are mapping the registers or the buffer */
+  if ((offset == dt3155_status[minor].reg_addr &&
+       vma->vm_end - vma->vm_start == PCI_PAGE_SIZE) ||
+      (offset == dt3155_status[minor].mem_addr &&
+       vma->vm_end - vma->vm_start == dt3155_status[minor].mem_size))
+    {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+      if (remap_pfn_range(vma,
+                       vma->vm_start,
+                       offset >> PAGE_SHIFT,
+                       vma->vm_end - vma->vm_start,
+                       vma->vm_page_prot))
+#else
+      if (remap_page_range(vma->vm_start,
+                          offset,
+                          vma->vm_end - vma->vm_start,
+                          vma->vm_page_prot))
+#endif
+       {
+         printk("DT3155: remap_page_range() failed.\n");
+         return -EAGAIN;
+       }
+    }
+  else
+    {
+      printk("DT3155: dt3155_mmap() bad call.\n");
+      return -ENXIO;
+    }
+
+  return 0;
+}
+
+
+/*****************************************************
+ * open()
+ *
+ * Our special open code.
+ * MOD_INC_USE_COUNT make sure that the driver memory is not freed
+ * while the device is in use.
+ *****************************************************/
+static int dt3155_open( struct inode* inode, struct file* filep)
+{
+  int minor = MINOR(inode->i_rdev); /* what device are we opening? */
+  if (dt3155_dev_open[ minor ]) {
+    printk ("DT3155:  Already opened by another process.\n");
+    return -EBUSY;
+  }
+
+  if (dt3155_status[ minor ].device_installed==0)
+    {
+      printk("DT3155 Open Error: No such device dt3155 minor number %d\n",
+            minor);
+      return -EIO;
+    }
+
+  if (dt3155_status[ minor ].state != DT3155_STATE_IDLE) {
+    printk ("DT3155:  Not in idle state (state = %lx)\n",
+           dt3155_status[ minor ].state);
+    return -EBUSY;
+  }
+
+  printk("DT3155: Device opened.\n");
+
+  dt3155_dev_open[ minor ] = 1 ;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+  MOD_INC_USE_COUNT;
+#endif
+
+  dt3155_flush( minor );
+
+  /* Disable ALL interrupts */
+  int_csr_r.reg = 0;
+  WriteMReg( (dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg );
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1)
+  init_waitqueue_head(&(dt3155_read_wait_queue[minor]));
+#else
+  dt3155_read_wait_queue[minor] = NULL;
+#endif
+
+  return 0;
+}
+
+
+/*****************************************************
+ * close()
+ *
+ * Now decrement the use count.
+ *
+ *****************************************************/
+static int dt3155_close( struct inode *inode, struct file *filep)
+{
+  int minor;
+
+  minor = MINOR(inode->i_rdev); /* which device are we closing */
+  if (!dt3155_dev_open[ minor ])
+    {
+      printk("DT3155: attempt to CLOSE a not OPEN device\n");
+    }
+  else
+    {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+      MOD_DEC_USE_COUNT;
+#endif
+      dt3155_dev_open[ minor ] = 0;
+
+      if (dt3155_status[ minor ].state != DT3155_STATE_IDLE)
+       {
+         quick_stop(minor);
+       }
+    }
+  return 0;
+}
+
+/*****************************************************
+ * read()
+ *
+ *****************************************************/
+static int dt3155_read (
+                       struct file     *filep,
+                       char            *buf,
+                       size_t          count,
+                       loff_t          *ppos)
+{
+  /* which device are we reading from? */
+  int          minor = MINOR(filep->f_dentry->d_inode->i_rdev);
+  u_long               offset;
+  int          frame_index;
+  frame_info_t *frame_info_p;
+
+  /* TODO: this should check the error flag and */
+  /*   return an error on hardware failures */
+  if (count != sizeof(dt3155_read_t))
+    {
+      printk("DT3155 ERROR (NJC): count is not right\n");
+      return -EINVAL;
+    }
+
+
+  /* Hack here -- I'm going to allow reading even when idle.
+   * this is so that the frames can be read after STOP has
+   * been called.  Leaving it here, commented out, as a reminder
+   * for a short while to make sure there are no problems.
+   * Note that if the driver is not opened in non_blocking mode,
+   * and the device is idle, then it could sit here forever! */
+
+  /*  if (dt3155_status[minor].state == DT3155_STATE_IDLE)*/
+  /*    return -EBUSY;*/
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1)
+
+  /* non-blocking reads should return if no data */
+  if (filep->f_flags & O_NDELAY)
+    {
+      if ((frame_index = dt3155_get_ready_buffer(minor)) < 0) {
+       /*printk( "dt3155:  no buffers available (?)\n");*/
+       /*              printques(minor); */
+       return -EAGAIN;
+      }
+    }
+  else
+    {
+      /*
+       * sleep till data arrives , or we get interrupted.
+       * Note that wait_event_interruptible() does not actually
+       * sleep/wait if it's condition evaluates to true upon entry.
+       */
+      wait_event_interruptible(dt3155_read_wait_queue[minor],
+                              (frame_index = dt3155_get_ready_buffer(minor))
+                              >= 0);
+
+      if (frame_index < 0)
+       {
+         printk ("DT3155: read: interrupted\n");
+         quick_stop (minor);
+         printques(minor);
+         return -EINTR;
+       }
+    }
+
+#else
+  while ((frame_index = dt3155_get_ready_buffer(minor)) < 0 )
+    {
+      int index;
+      if (filep->f_flags & O_NDELAY)
+       return 0;
+
+      /* sleep till data arrives , or we get interrupted */
+      interruptible_sleep_on(&dt3155_read_wait_queue[minor]);
+      for (index = 0; index < _NSIG_WORDS; index++)
+       {
+         /*
+          * Changing the next line of code below to this:
+          * if (current->pending.signal.sig[index] &
+          *                           ~current->blocked.sig[index])
+          * would also work on a 2.4 kernel, however, the above
+          * method is preferred & more portable.
+          */
+         if (current->signal.sig[index] & ~current->blocked.sig[index])
+           {
+             printk ("DT3155: read: interrupted\n");
+             return -EINTR;
+           }
+       }
+    }
+
+#endif
+
+  frame_info_p = &dt3155_status[minor].fbuffer.frame_info[frame_index];
+
+  /* make this an offset */
+  offset = frame_info_p->addr - dt3155_status[minor].mem_addr;
+
+  put_user(offset, (unsigned int *) buf);
+  buf += sizeof(u_long);
+  put_user( dt3155_status[minor].fbuffer.frame_count, (unsigned int *) buf);
+  buf += sizeof(u_long);
+  put_user(dt3155_status[minor].state, (unsigned int *) buf);
+  buf += sizeof(u_long);
+  if (copy_to_user(buf, frame_info_p, sizeof(frame_info_t)))
+      return -EFAULT;
+
+  return sizeof(dt3155_read_t);
+}
+
+static unsigned int dt3155_poll (struct file * filp, poll_table *wait)
+{
+  int minor = MINOR(filp->f_dentry->d_inode->i_rdev);
+
+  if (!is_ready_buf_empty(minor))
+    return POLLIN | POLLRDNORM;
+
+  poll_wait (filp, &dt3155_read_wait_queue[minor], wait);
+
+  return 0;
+}
+
+
+/*****************************************************
+ * file operations supported by DT3155 driver
+ *  needed by init_module
+ *  register_chrdev
+ *****************************************************/
+static struct file_operations dt3155_fops = {
+  read:                dt3155_read,
+  ioctl:               dt3155_ioctl,
+  mmap:                dt3155_mmap,
+  poll:           dt3155_poll,
+  open:                dt3155_open,
+  release:     dt3155_close
+};
+
+
+/*****************************************************
+ * find_PCI();
+ *
+ * PCI has been totally reworked in 2.1..
+ *****************************************************/
+static int find_PCI (void)
+{
+  struct pci_dev *pci_dev = NULL;
+  int error, pci_index = 0;
+  unsigned short rev_device;
+  unsigned long base;
+  unsigned char irq;
+
+  while ((pci_dev = pci_find_device
+         (DT3155_VENDORID, DT3155_DEVICEID, pci_dev)) != NULL)
+    {
+      pci_index ++;
+
+      /* Is it really there? */
+      if ((error =
+          pci_read_config_word(pci_dev, PCI_CLASS_DEVICE, &rev_device)))
+       continue;
+
+      /* Found a board */
+      DT_3155_DEBUG_MSG("DT3155: Device number %d \n", pci_index);
+
+      /* Make sure the driver was compiled with enough buffers to handle
+        this many boards */
+      if (pci_index > MAXBOARDS) {
+       printk("DT3155: ERROR - found %d devices, but driver only configured "
+              "for %d devices\n"
+              "DT3155: Please change MAXBOARDS in dt3155.h\n",
+              pci_index, MAXBOARDS);
+       return DT_3155_FAILURE;
+      }
+
+      /* Now, just go out and make sure that this/these device(s) is/are
+        actually mapped into the kernel address space */
+      if ((error = pci_read_config_dword( pci_dev, PCI_BASE_ADDRESS_0,
+                                         (u_int *) &base)))
+       {
+         printk("DT3155: Was not able to find device \n");
+         return DT_3155_FAILURE;
+       }
+
+      DT_3155_DEBUG_MSG("DT3155: Base address 0 for device is %lx \n", base);
+      dt3155_status[pci_index-1].reg_addr = base;
+
+      /* Remap the base address to a logical address through which we
+       * can access it. */
+      dt3155_lbase[ pci_index - 1 ] = ioremap(base,PCI_PAGE_SIZE);
+      dt3155_status[ pci_index - 1 ].reg_addr = base;
+      DT_3155_DEBUG_MSG("DT3155: New logical address is x%x \n",
+                       (u_int)dt3155_lbase[pci_index-1]);
+      if ( !dt3155_lbase[pci_index-1] )
+       {
+         printk("DT3155: Unable to remap control registers\n");
+         return DT_3155_FAILURE;
+       }
+
+      if ( (error = pci_read_config_byte( pci_dev, PCI_INTERRUPT_LINE, &irq)) )
+       {
+         printk("DT3155: Was not able to find device \n");
+         return DT_3155_FAILURE;
+       }
+
+      DT_3155_DEBUG_MSG("DT3155: IRQ is %d \n",irq);
+      dt3155_status[ pci_index-1 ].irq = irq;
+      /* Set flag: kth device found! */
+      dt3155_status[ pci_index-1 ].device_installed = 1;
+      printk("DT3155: Installing device %d w/irq %d and address 0x%x\n",
+            pci_index,
+            (u_int)dt3155_status[pci_index-1].irq,
+            (u_int)dt3155_lbase[pci_index-1]);
+
+    }
+  ndevices = pci_index;
+
+  return DT_3155_SUCCESS;
+}
+
+u_long allocatorAddr = 0;
+
+/*****************************************************
+ * init_module()
+ *****************************************************/
+int init_module(void)
+{
+  int index;
+  int rcode = 0;
+  char *devname[ MAXBOARDS ];
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,1)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+  SET_MODULE_OWNER(&dt3155_fops);
+#endif
+#endif
+
+  devname[ 0 ] = "dt3155a";
+#if MAXBOARDS == 2
+  devname[ 1 ] = "dt3155b";
+#endif
+
+  printk("DT3155: Loading module...\n");
+
+  /* Register the device driver */
+  rcode = register_chrdev( dt3155_major, "dt3155", &dt3155_fops );
+  if( rcode < 0 )
+    {
+      printk( KERN_INFO "DT3155: register_chrdev failed \n");
+      return rcode;
+    }
+
+  if( dt3155_major == 0 )
+    dt3155_major = rcode; /* dynamic */
+
+
+  /* init the status variables.                     */
+  /* DMA memory is taken care of in setup_buffers() */
+  for ( index = 0; index < MAXBOARDS; index++ )
+    {
+      dt3155_status[ index ].config.acq_mode   = DT3155_MODE_FRAME;
+      dt3155_status[ index ].config.continuous = DT3155_ACQ;
+      dt3155_status[ index ].config.cols       = DT3155_MAX_COLS;
+      dt3155_status[ index ].config.rows       = DT3155_MAX_ROWS;
+      dt3155_status[ index ].state = DT3155_STATE_IDLE;
+
+      /* find_PCI() will check if devices are installed; */
+      /* first assume they're not:                       */
+      dt3155_status[ index ].mem_addr          = 0;
+      dt3155_status[ index ].mem_size          = 0;
+      dt3155_status[ index ].state             = DT3155_STATE_IDLE;
+      dt3155_status[ index ].device_installed  = 0;
+    }
+
+  /* Now let's find the hardware.  find_PCI() will set ndevices to the
+   * number of cards found in this machine. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+  if ( !(pcibios_present()) )
+    {
+      printk("DT3155: Error: No PCI bios on this machine \n");
+      if( unregister_chrdev( dt3155_major, "dt3155" ) != 0 )
+       printk("DT3155: cleanup_module failed\n");
+
+      return DT_3155_FAILURE;
+    }
+  else
+#endif
+    {
+      if ( (rcode = find_PCI()) !=  DT_3155_SUCCESS )
+       {
+         printk("DT3155 error: find_PCI() failed to find dt3155 board(s)\n");
+         unregister_chrdev( dt3155_major, "dt3155" );
+         return rcode;
+       }
+    }
+
+  /* Ok, time to setup the frame buffers */
+  if( (rcode = dt3155_setup_buffers(&allocatorAddr)) < 0 )
+    {
+      printk("DT3155: Error: setting up buffer not large enough.");
+      unregister_chrdev( dt3155_major, "dt3155" );
+      return rcode;
+    }
+
+  /* If we are this far, then there is enough RAM */
+  /* for the buffers: Print the configuration.    */
+  for(  index = 0;  index < ndevices;  index++ )
+    {
+      printk("DT3155: Device = %d; acq_mode = %d; "
+            "continuous = %d; cols = %d; rows = %d;\n",
+            index ,
+            dt3155_status[ index ].config.acq_mode,
+            dt3155_status[ index ].config.continuous,
+            dt3155_status[ index ].config.cols,
+            dt3155_status[ index ].config.rows);
+      printk("DT3155: m_addr = 0x%x; m_size = %ld; "
+            "state = %ld; device_installed = %d\n",
+            (u_int)dt3155_status[ index ].mem_addr,
+            dt3155_status[ index ].mem_size,
+            dt3155_status[ index ].state,
+            dt3155_status[ index ].device_installed);
+    }
+
+  /* Disable ALL interrupts */
+  int_csr_r.reg = 0;
+  for(  index = 0;  index < ndevices;  index++ )
+    {
+      WriteMReg( (dt3155_lbase[ index ] + INT_CSR), int_csr_r.reg );
+      if( dt3155_status[ index ].device_installed )
+       {
+         /*
+          * This driver *looks* like it can handle sharing interrupts,
+          * but I can't actually test myself. I've had reports that it
+          * DOES work so I'll enable it for now. This comment will remain
+          * as a reminder in case any problems arise. (SS)
+          */
+         /* in older kernels flags are: SA_SHIRQ | SA_INTERRUPT */
+         rcode = request_irq( dt3155_status[ index ].irq, (void *)dt3155_isr,
+                              IRQF_SHARED | IRQF_DISABLED, devname[ index ],
+                              (void*) &dt3155_status[index]);
+         if( rcode < 0 )
+           {
+             printk("DT3155: minor %d request_irq failed for IRQ %d\n",
+                    index, dt3155_status[index].irq);
+             unregister_chrdev( dt3155_major, "dt3155" );
+             return rcode;
+           }
+       }
+    }
+
+  printk("DT3155: finished loading\n");
+
+  return 0;
+}
+
+/*****************************************************
+ * cleanup_module(void)
+ *
+ *****************************************************/
+void cleanup_module(void)
+{
+  int index;
+
+  printk("DT3155:  cleanup_module called\n");
+
+  /* removed DMA allocated with the allocator */
+#ifdef STANDALONE_ALLOCATOR
+  if (allocatorAddr != 0)
+    allocator_free_dma(allocatorAddr);
+#else
+  allocator_cleanup();
+#endif
+
+  unregister_chrdev( dt3155_major, "dt3155" );
+
+  for( index = 0; index < ndevices; index++ )
+    {
+      if( dt3155_status[ index ].device_installed == 1 )
+       {
+         printk( "DT3155: Freeing irq %d for device %d\n",
+                 dt3155_status[ index ].irq, index );
+         free_irq( dt3155_status[ index ].irq, (void*)&dt3155_status[index] );
+       }
+    }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+  if (MOD_IN_USE)
+    printk("DT3155: device busy, remove delayed\n");
+#endif
+}
+
diff --git a/drivers/staging/dt3155/dt3155_drv.h b/drivers/staging/dt3155/dt3155_drv.h
new file mode 100644 (file)
index 0000000..92a92dc
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+
+Copyright 1996,2002 Gregory D. Hager, Alfred A. Rizzi, Noah J. Cowan,
+                    Scott Smedley
+
+This file is part of the DT3155 Device Driver.
+
+The DT3155 Device Driver is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The DT3155 Device Driver is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the DT3155 Device Driver; if not, write to the Free
+Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+MA 02111-1307 USA
+*/
+
+#ifndef DT3155_DRV_INC
+#define DT3155_DRV_INC
+
+/* kernel logical address of the frame grabbers */
+extern u_char *dt3155_lbase[ MAXBOARDS ];
+
+/* kernel logical address of ram buffer */
+extern u_char *dt3155_bbase;
+
+#ifdef __KERNEL__
+#include <linux/wait.h>
+
+#include <linux/version.h>     /* need access to LINUX_VERSION_CODE */
+/* wait queue for reads */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1)
+extern wait_queue_head_t dt3155_read_wait_queue[MAXBOARDS];
+#else
+extern struct wait_queue *dt3155_read_wait_queue[MAXBOARDS];
+#endif
+#endif
+
+/* number of devices */
+extern u_int ndevices;
+
+extern int dt3155_errno;
+
+#endif
diff --git a/drivers/staging/dt3155/dt3155_io.c b/drivers/staging/dt3155/dt3155_io.c
new file mode 100644 (file)
index 0000000..6d51686
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+
+Copyright 1996,2002,2005 Gregory D. Hager, Alfred A. Rizzi, Noah J. Cowan,
+                         Jason Lapenta, Scott Smedley
+
+This file is part of the DT3155 Device Driver.
+
+The DT3155 Device Driver is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The DT3155 Device Driver is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the DT3155 Device Driver; if not, write to the Free
+Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+MA 02111-1307 USA
+
+
+-- Changes --
+
+  Date     Programmer   Description of changes made
+  -------------------------------------------------------------------
+  10-Oct-2001 SS       port to 2.4 kernel.
+  24-Jul-2002 SS       GPL licence.
+  26-Jul-2002 SS       Bug fix: timing logic was wrong.
+  08-Aug-2005 SS       port to 2.6 kernel.
+
+*/
+
+/* This file provides some basic register io routines.  It is modified
+   from demo code provided by Data Translations. */
+
+#ifdef __KERNEL__
+#include <asm/delay.h>
+#endif
+
+#if 0
+#include <sys/param.h>
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+
+#include "dt3155.h"
+#include "dt3155_io.h"
+#include "dt3155_drv.h"
+
+#ifndef __KERNEL__
+#include <stdio.h>
+#endif
+
+
+/****** local copies of board's 32 bit registers ******/
+u_long            even_dma_start_r;     /*  bit 0 should always be 0 */
+u_long            odd_dma_start_r;      /*               .. */
+u_long            even_dma_stride_r;    /*  bits 0&1 should always be 0 */
+u_long            odd_dma_stride_r;     /*               .. */
+u_long            even_pixel_fmt_r;
+u_long            odd_pixel_fmt_r;
+
+FIFO_TRIGGER_R      fifo_trigger_r;
+XFER_MODE_R         xfer_mode_r;
+CSR1_R              csr1_r;
+RETRY_WAIT_CNT_R    retry_wait_cnt_r;
+INT_CSR_R           int_csr_r;
+
+u_long              even_fld_mask_r;
+u_long              odd_fld_mask_r;
+
+MASK_LENGTH_R       mask_length_r;
+FIFO_FLAG_CNT_R     fifo_flag_cnt_r;
+IIC_CLK_DUR_R       iic_clk_dur_r;
+IIC_CSR1_R          iic_csr1_r;
+IIC_CSR2_R          iic_csr2_r;
+DMA_UPPER_LMT_R     even_dma_upper_lmt_r;
+DMA_UPPER_LMT_R     odd_dma_upper_lmt_r;
+
+
+
+/******** local copies of board's 8 bit I2C registers ******/
+I2C_CSR2               i2c_csr2;
+I2C_EVEN_CSR           i2c_even_csr;
+I2C_ODD_CSR            i2c_odd_csr;
+I2C_CONFIG             i2c_config;
+u_char                 i2c_dt_id;
+u_char                 i2c_x_clip_start;
+u_char                 i2c_y_clip_start;
+u_char                 i2c_x_clip_end;
+u_char                 i2c_y_clip_end;
+u_char                 i2c_ad_addr;
+u_char                 i2c_ad_lut;
+I2C_AD_CMD             i2c_ad_cmd;
+u_char                 i2c_dig_out;
+u_char                 i2c_pm_lut_addr;
+u_char                 i2c_pm_lut_data;
+
+
+// return the time difference (in microseconds) b/w <a> & <b>.
+long elapsed2 (const struct timeval *pStart, const struct timeval *pEnd)
+{
+       long i = (pEnd->tv_sec - pStart->tv_sec) * 1000000;
+       i += pEnd->tv_usec - pStart->tv_usec;
+       return i;
+}
+
+/***********************************************************************
+ wait_ibsyclr()
+
+ This function handles read/write timing and r/w timeout error
+
+     Returns TRUE  if NEW_CYCLE clears
+     Returns FALSE if NEW_CYCLE doesn't clear in roughly 3 msecs,
+             otherwise returns 0
+
+***********************************************************************/
+int wait_ibsyclr(u_char * lpReg)
+{
+  /* wait 100 microseconds */
+
+#ifdef __KERNEL__
+  udelay(100L);
+  /*    __delay(loops_per_sec/10000); */
+  if (iic_csr2_r.fld.NEW_CYCLE )
+    { /*  if NEW_CYCLE didn't clear */
+      /*  TIMEOUT ERROR */
+      dt3155_errno = DT_ERR_I2C_TIMEOUT;
+      return FALSE;
+    }
+  else
+    return TRUE;        /*  no  error */
+#else
+  struct timeval StartTime;
+  struct timeval EndTime;
+
+  const int to_3ms = 3000;  /* time out of 3ms = 3000us */
+
+  gettimeofday( &StartTime, NULL );
+  do {
+    /* get new iic_csr2 value: */
+    ReadMReg((lpReg + IIC_CSR2), iic_csr2_r.reg);
+    gettimeofday( &EndTime, NULL );
+  }
+  while ((elapsed2(&StartTime, &EndTime) < to_3ms) && iic_csr2_r.fld.NEW_CYCLE);
+
+  if (iic_csr2_r.fld.NEW_CYCLE )
+    { /*  if NEW_CYCLE didn't clear */
+      printf("Timed out waiting for NEW_CYCLE to clear!");
+      return FALSE;
+    }
+  else
+    return TRUE;        /*  no  error */
+#endif
+}
+
+/***********************************************************************
+ WriteI2C()
+
+ This function handles writing to 8-bit DT3155 registers
+
+   1st parameter is pointer to 32-bit register base address
+   2nd parameter is reg. index;
+   3rd is value to be written
+
+   Returns    TRUE   -  Successful completion
+              FALSE  -  Timeout error - cycle did not complete!
+***********************************************************************/
+int WriteI2C (u_char * lpReg, u_short wIregIndex, u_char byVal)
+{
+    int writestat;     /* status for return */
+
+    /*  read 32 bit IIC_CSR2 register data into union */
+
+    ReadMReg((lpReg + IIC_CSR2), iic_csr2_r.reg);
+
+    iic_csr2_r.fld.DIR_RD    = 0;           /*  for write operation */
+    iic_csr2_r.fld.DIR_ADDR  = wIregIndex;  /*  I2C address of I2C register: */
+    iic_csr2_r.fld.DIR_WR_DATA = byVal;     /*  8 bit data to be written to I2C reg */
+    iic_csr2_r.fld.NEW_CYCLE   = 1;         /*  will start a direct I2C cycle: */
+
+    /*  xfer union data into 32 bit IIC_CSR2 register */
+
+    WriteMReg((lpReg + IIC_CSR2), iic_csr2_r.reg);
+
+    /* wait for IIC cycle to finish */
+
+    writestat = wait_ibsyclr( lpReg );
+    return writestat;                  /* return with status */
+}
+
+/***********************************************************************
+ ReadI2C()
+
+ This function handles reading from 8-bit DT3155 registers
+
+   1st parameter is pointer to 32-bit register base address
+   2nd parameter is reg. index;
+   3rd is adrs of value to be read
+
+   Returns    TRUE   -  Successful completion
+              FALSE  -  Timeout error - cycle did not complete!
+***********************************************************************/
+int ReadI2C (u_char * lpReg, u_short wIregIndex, u_char * byVal)
+{
+  int writestat;     /* status for return */
+
+  /*  read 32 bit IIC_CSR2 register data into union */
+  ReadMReg((lpReg + IIC_CSR2), iic_csr2_r.reg);
+
+  /*  for read operation */
+  iic_csr2_r.fld.DIR_RD     = 1;
+
+  /*  I2C address of I2C register: */
+  iic_csr2_r.fld.DIR_ADDR   = wIregIndex;
+
+  /*  will start a direct I2C cycle: */
+  iic_csr2_r.fld.NEW_CYCLE  = 1;
+
+  /*  xfer union's data into 32 bit IIC_CSR2 register */
+  WriteMReg((lpReg + IIC_CSR2), iic_csr2_r.reg);
+
+  /* wait for IIC cycle to finish */
+  writestat = wait_ibsyclr(lpReg);
+
+  /* Next 2 commands read 32 bit IIC_CSR1 register's data into union */
+  /* first read data is in IIC_CSR1 */
+  ReadMReg((lpReg + IIC_CSR1), iic_csr1_r.reg);
+
+  /* now get data u_char out of register */
+  *byVal = (u_char) iic_csr1_r.fld.RD_DATA;
+
+  return writestat;   /*  return with status */
+}
diff --git a/drivers/staging/dt3155/dt3155_io.h b/drivers/staging/dt3155/dt3155_io.h
new file mode 100644 (file)
index 0000000..c1174ab
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+
+Copyright 1996,2002 Gregory D. Hager, Alfred A. Rizzi, Noah J. Cowan,
+                    Jason Lapenta, Scott Smedley
+
+This file is part of the DT3155 Device Driver.
+
+The DT3155 Device Driver is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The DT3155 Device Driver is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the DT3155 Device Driver; if not, write to the Free
+Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+MA 02111-1307 USA
+
+
+-- Changes --
+
+  Date     Programmer  Description of changes made
+  -------------------------------------------------------------------
+  24-Jul-2002 SS       GPL licence.
+
+*/
+
+/* This code is a modified version of examples provided by Data Translations.*/
+
+#ifndef DT3155_IO_INC
+#define DT3155_IO_INC
+
+/* macros to access registers */
+
+#define   WriteMReg(Address, Data)        * ((u_long *) (Address)) = Data
+#define   ReadMReg(Address, Data)         Data = * ((u_long *) (Address))
+
+/***************** 32 bit register globals  **************/
+
+/*  offsets for 32-bit memory mapped registers */
+
+#define  EVEN_DMA_START     0x000
+#define  ODD_DMA_START      0x00C
+#define  EVEN_DMA_STRIDE    0x018
+#define  ODD_DMA_STRIDE     0x024
+#define  EVEN_PIXEL_FMT     0x030
+#define  ODD_PIXEL_FMT      0x034
+#define  FIFO_TRIGGER       0x038
+#define  XFER_MODE          0x03C
+#define  CSR1               0x040
+#define  RETRY_WAIT_CNT     0x044
+#define  INT_CSR            0x048
+#define  EVEN_FLD_MASK      0x04C
+#define  ODD_FLD_MASK       0x050
+#define  MASK_LENGTH        0x054
+#define  FIFO_FLAG_CNT      0x058
+#define  IIC_CLK_DUR        0x05C
+#define  IIC_CSR1           0x060
+#define  IIC_CSR2           0x064
+#define  EVEN_DMA_UPPR_LMT  0x08C
+#define  ODD_DMA_UPPR_LMT   0x090
+
+#define  CLK_DUR_VAL        0x01010101
+
+
+
+/******** Assignments and Typedefs for 32 bit Memory Mapped Registers ********/
+
+/**********************************
+ * fifo_trigger_tag
+ */
+typedef union fifo_trigger_tag {
+  u_long   reg;
+  struct
+  {
+    u_long   PACKED     :   6;
+    u_long              :   9;
+    u_long   PLANER     :   7;
+    u_long              :   9;
+  } fld;
+} FIFO_TRIGGER_R;
+
+/**********************************
+ * xfer_mode_tag
+ */
+typedef union xfer_mode_tag {
+  u_long   reg;
+  struct
+  {
+    u_long                 :   2;
+    u_long   FIELD_TOGGLE  :   1;
+    u_long                 :   5;
+    u_long                 :   2;
+    u_long                 :   22;
+  } fld;
+} XFER_MODE_R;
+
+/**********************************
+ * csr1_tag
+ */
+typedef union csr1_tag {
+  u_long   reg;
+  struct
+  {
+    u_long   CAP_CONT_EVE    :   1;
+    u_long   CAP_CONT_ODD    :   1;
+    u_long   CAP_SNGL_EVE    :   1;
+    u_long   CAP_SNGL_ODD    :   1;
+    u_long   FLD_DN_EVE      :   1;
+    u_long   FLD_DN_ODD      :   1;
+    u_long   SRST            :   1;
+    u_long   FIFO_EN         :   1;
+    u_long   FLD_CRPT_EVE    :   1;
+    u_long   FLD_CRPT_ODD    :   1;
+    u_long   ADDR_ERR_EVE    :   1;
+    u_long   ADDR_ERR_ODD    :   1;
+    u_long   CRPT_DIS        :   1;
+    u_long   RANGE_EN        :   1;
+    u_long                   :   16;
+  } fld;
+} CSR1_R;
+
+/**********************************
+ * retry_wait_cnt_tag
+ */
+typedef union retry_wait_cnt_tag {
+  u_long   reg;
+  struct
+  {
+    u_long   RTRY_WAIT_CNT   :   8;
+    u_long                   :   24;
+  } fld;
+} RETRY_WAIT_CNT_R;
+
+/**********************************
+ * int_csr_tag
+ */
+typedef union int_csr_tag {
+  u_long   reg;
+  struct
+  {
+    u_long   FLD_END_EVE           :   1;
+    u_long   FLD_END_ODD           :   1;
+    u_long   FLD_START             :   1;
+    u_long                         :   5;
+    u_long   FLD_END_EVE_EN        :   1;
+    u_long   FLD_END_ODD_EN        :   1;
+    u_long   FLD_START_EN          :   1;
+    u_long                         :   21;
+  } fld;
+} INT_CSR_R;
+
+/**********************************
+ * mask_length_tag
+ */
+typedef union mask_length_tag {
+  u_long   reg;
+  struct
+  {
+    u_long   MASK_LEN_EVE    :   5;
+    u_long                   :   11;
+    u_long   MASK_LEN_ODD    :   5;
+    u_long                   :   11;
+  } fld;
+} MASK_LENGTH_R;
+
+/**********************************
+ * fifo_flag_cnt_tag
+ */
+typedef union fifo_flag_cnt_tag {
+  u_long   reg;
+  struct
+  {
+    u_long   AF_COUNT    :   7;
+    u_long               :   9;
+    u_long   AE_COUNT    :   7;
+    u_long               :   9;
+  } fld;
+} FIFO_FLAG_CNT_R;
+
+/**********************************
+ * iic_clk_dur
+ */
+typedef union iic_clk_dur {
+  u_long   reg;
+  struct
+  {
+    u_long   PHASE_1     :   8;
+    u_long   PHASE_2     :   8;
+    u_long   PHASE_3     :   8;
+    u_long   PHASE_4     :   8;
+  } fld;
+} IIC_CLK_DUR_R;
+
+/**********************************
+ * iic_csr1_tag
+ */
+typedef union iic_csr1_tag {
+  u_long   reg;
+  struct
+  {
+    u_long   AUTO_EN        :   1;
+    u_long   BYPASS         :   1;
+    u_long   SDA_OUT        :   1;
+    u_long   SCL_OUT        :   1;
+    u_long                  :   4;
+    u_long   AUTO_ABORT     :   1;
+    u_long   DIRECT_ABORT   :   1;
+    u_long   SDA_IN         :   1;
+    u_long   SCL_IN         :   1;
+    u_long                  :   4;
+    u_long   AUTO_ADDR      :   8;
+    u_long   RD_DATA        :   8;
+  } fld;
+} IIC_CSR1_R;
+
+/**********************************
+ * iic_csr2_tag
+ */
+typedef union iic_csr2_tag {
+  u_long   reg;
+  struct
+  {
+    u_long   DIR_WR_DATA   :   8;
+    u_long   DIR_SUB_ADDR  :   8;
+    u_long   DIR_RD        :   1;
+    u_long   DIR_ADDR      :   7;
+    u_long   NEW_CYCLE     :   1;
+    u_long                 :   7;
+  } fld;
+}  IIC_CSR2_R;
+
+/*  use for both EVEN and ODD DMA UPPER LIMITS */
+
+/**********************************
+ * dma_upper_lmt_tag
+ */
+typedef union dma_upper_lmt_tag   {
+  u_long   reg;
+  struct
+  {
+    u_long   DMA_UPPER_LMT_VAL       :   24;
+    u_long                           :   8;
+  } fld;
+} DMA_UPPER_LMT_R;
+
+
+/***************************************
+ * Global declarations of local copies
+ * of boards' 32 bit registers
+ ***************************************/
+extern u_long            even_dma_start_r;   /*  bit 0 should always be 0 */
+extern u_long            odd_dma_start_r;    /*               .. */
+extern u_long            even_dma_stride_r;  /*  bits 0&1 should always be 0 */
+extern u_long            odd_dma_stride_r;   /*               .. */
+extern u_long            even_pixel_fmt_r;
+extern u_long            odd_pixel_fmt_r;
+
+extern FIFO_TRIGGER_R      fifo_trigger_r;
+extern XFER_MODE_R         xfer_mode_r;
+extern CSR1_R              csr1_r;
+extern RETRY_WAIT_CNT_R    retry_wait_cnt_r;
+extern INT_CSR_R           int_csr_r;
+
+extern u_long              even_fld_mask_r;
+extern u_long              odd_fld_mask_r;
+
+extern MASK_LENGTH_R       mask_length_r;
+extern FIFO_FLAG_CNT_R     fifo_flag_cnt_r;
+extern IIC_CLK_DUR_R       iic_clk_dur_r;
+extern IIC_CSR1_R          iic_csr1_r;
+extern IIC_CSR2_R          iic_csr2_r;
+extern DMA_UPPER_LMT_R     even_dma_upper_lmt_r;
+extern DMA_UPPER_LMT_R     odd_dma_upper_lmt_r;
+
+
+
+/***************** 8 bit I2C register globals  ***********/
+
+#define  CSR2                   0x010 /* indices of 8-bit I2C mapped reg's*/
+#define  EVEN_CSR               0x011
+#define  ODD_CSR                0x012
+#define  CONFIG                 0x013
+#define  DT_ID                  0x01F
+#define  X_CLIP_START           0x020
+#define  Y_CLIP_START           0x022
+#define  X_CLIP_END             0x024
+#define  Y_CLIP_END             0x026
+#define  AD_ADDR                0x030
+#define  AD_LUT                 0x031
+#define  AD_CMD                 0x032
+#define  DIG_OUT                0x040
+#define  PM_LUT_ADDR            0x050
+#define  PM_LUT_DATA            0x051
+
+
+/******** Assignments and Typedefs for 8 bit I2C Registers********************/
+
+typedef union i2c_csr2_tag {
+  u_char   reg;
+  struct
+  {
+    u_char    CHROM_FIL         :   1;
+    u_char    SYNC_SNTL         :   1;
+    u_char    HZ50              :   1;
+    u_char    SYNC_PRESENT      :   1;
+    u_char    BUSY_EVE          :   1;
+    u_char    BUSY_ODD          :   1;
+    u_char    DISP_PASS         :   1;
+  } fld;
+} I2C_CSR2;
+
+typedef union i2c_even_csr_tag {
+  u_char    reg;
+  struct
+  {
+    u_char    DONE_EVE       :   1;
+    u_char    SNGL_EVE       :   1;
+    u_char    ERROR_EVE      :   1;
+    u_char                   :   5;
+  } fld;
+} I2C_EVEN_CSR;
+
+typedef union i2c_odd_csr_tag {
+  u_char    reg;
+  struct
+  {
+    u_char    DONE_ODD     :   1;
+    u_char    SNGL_ODD     :   1;
+    u_char    ERROR_ODD    :   1;
+    u_char                 :   5;
+  } fld;
+} I2C_ODD_CSR;
+
+typedef union i2c_config_tag {
+  u_char   reg;
+  struct
+  {
+    u_char    ACQ_MODE        :   2;
+    u_char    EXT_TRIG_EN     :   1;
+    u_char    EXT_TRIG_POL    :   1;
+    u_char    H_SCALE         :   1;
+    u_char    CLIP            :   1;
+    u_char    PM_LUT_SEL      :   1;
+    u_char    PM_LUT_PGM      :   1;
+  } fld;
+}  I2C_CONFIG;
+
+
+typedef union i2c_ad_cmd_tag {         /* bits can have 3 different meanings
+                                        depending on value of AD_ADDR */
+  u_char   reg;
+  struct
+  {
+    u_char                         :   2;
+    u_char    SYNC_LVL_SEL         :   2;
+    u_char    SYNC_CNL_SEL         :   2;
+    u_char    DIGITIZE_CNL_SEL1    :   2;
+  } bt252_command;                        /* Bt252 Command Register */
+  struct                                  /* if AD_ADDR = 00h */
+  {
+    u_char    IOUT_DATA            :   8;
+  } bt252_iout0;                            /* Bt252 IOUT0 register */
+  struct                                    /* if AD_ADDR = 01h */
+  {
+    u_char    IOUT_DATA            :   8;
+  } bt252_iout1;                            /* BT252 IOUT1 register */
+}  I2C_AD_CMD;                                /* if AD_ADDR = 02h */
+
+
+/***** Global declarations of local copies of boards' 8 bit I2C registers ***/
+
+extern I2C_CSR2               i2c_csr2;
+extern I2C_EVEN_CSR           i2c_even_csr;
+extern I2C_ODD_CSR            i2c_odd_csr;
+extern I2C_CONFIG             i2c_config;
+extern u_char                 i2c_dt_id;
+extern u_char                 i2c_x_clip_start;
+extern u_char                 i2c_y_clip_start;
+extern u_char                 i2c_x_clip_end;
+extern u_char                 i2c_y_clip_end;
+extern u_char                 i2c_ad_addr;
+extern u_char                 i2c_ad_lut;
+extern I2C_AD_CMD             i2c_ad_cmd;
+extern u_char                 i2c_dig_out;
+extern u_char                 i2c_pm_lut_addr;
+extern u_char                 i2c_pm_lut_data;
+
+/* Functions for Global use */
+
+/* access 8-bit IIC registers */
+
+extern int ReadI2C (u_char * lpReg, u_short wIregIndex, u_char * byVal);
+extern int WriteI2C (u_char * lpReg, u_short wIregIndex, u_char byVal);
+
+#endif
diff --git a/drivers/staging/dt3155/dt3155_isr.c b/drivers/staging/dt3155/dt3155_isr.c
new file mode 100644 (file)
index 0000000..339aee9
--- /dev/null
@@ -0,0 +1,545 @@
+/*
+
+Copyright 1996,2002,2005 Gregory D. Hager, Alfred A. Rizzi, Noah J. Cowan,
+                         Jason Lapenta, Scott Smedley, Greg Sharp
+
+This file is part of the DT3155 Device Driver.
+
+The DT3155 Device Driver is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The DT3155 Device Driver is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the DT3155 Device Driver; if not, write to the Free
+Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+MA 02111-1307 USA
+
+   File: dt3155_isr.c
+Purpose: Buffer management routines, and other routines for the ISR
+         (the actual isr is in dt3155_drv.c)
+
+-- Changes --
+
+  Date       Programmer  Description of changes made
+  -------------------------------------------------------------------
+  03-Jul-2000 JML       n/a
+  02-Apr-2002 SS        Mods to make work with separate allocator
+                        module; Merged John Roll's mods to make work with
+                        multiple boards.
+  10-Jul-2002 GCS       Complete rewrite of setup_buffers to disallow
+                        buffers which span a 4MB boundary.
+  24-Jul-2002 SS        GPL licence.
+  30-Jul-2002 NJC       Added support for buffer loop.
+  31-Jul-2002 NJC       Complete rewrite of buffer management
+  02-Aug-2002 NJC       Including slab.h instead of malloc.h (no warning).
+                        Also, allocator_init() now returns allocator_max
+                        so cleaned up allocate_buffers() accordingly.
+  08-Aug-2005 SS        port to 2.6 kernel.
+
+*/
+
+#include <asm/system.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+
+#include "dt3155.h"
+#include "dt3155_drv.h"
+#include "dt3155_io.h"
+#include "dt3155_isr.h"
+#include "allocator.h"
+
+#define FOUR_MB         (0x0400000)  /* Can't DMA accross a 4MB boundary!*/
+#define UPPER_10_BITS   (0x3FF<<22)  /* Can't DMA accross a 4MB boundary!*/
+
+
+/* Pointer into global structure for handling buffers */
+struct dt3155_fbuffer_s *dt3155_fbuffer[MAXBOARDS] = {NULL
+#if MAXBOARDS == 2
+                                                     , NULL
+#endif
+};
+
+/******************************************************************************
+ * Simple array based que struct
+ *
+ * Some handy functions using the buffering structure.
+ *****************************************************************************/
+
+
+/***************************
+ * are_empty_buffers
+ * m is minor # of device
+ ***************************/
+inline bool are_empty_buffers( int m )
+{
+  return ( dt3155_fbuffer[ m ]->empty_len );
+}
+
+/**************************
+ * push_empty
+ * m is minor # of device
+ *
+ * This is slightly confusing.  The number empty_len is the literal #
+ * of empty buffers.  After calling, empty_len-1 is the index into the
+ * empty buffer stack.  So, if empty_len == 1, there is one empty buffer,
+ * given by dt3155_fbuffer[m]->empty_buffers[0].
+ * empty_buffers should never fill up, though this is not checked.
+ **************************/
+inline void push_empty( int index, int m )
+{
+  dt3155_fbuffer[m]->empty_buffers[ dt3155_fbuffer[m]->empty_len ] = index;
+  dt3155_fbuffer[m]->empty_len++;
+}
+
+/**************************
+ * pop_empty( m )
+ * m is minor # of device
+ **************************/
+inline int pop_empty( int m )
+{
+  dt3155_fbuffer[m]->empty_len--;
+  return dt3155_fbuffer[m]->empty_buffers[ dt3155_fbuffer[m]->empty_len ];
+}
+
+/*************************
+ * is_ready_buf_empty( m )
+ * m is minor # of device
+ *************************/
+inline bool is_ready_buf_empty( int m )
+{
+  return ((dt3155_fbuffer[ m ]->ready_len) == 0);
+}
+
+/*************************
+ * is_ready_buf_full( m )
+ * m is minor # of device
+ * this should *never* be true if there are any active, locked or empty
+ * buffers, since it corresponds to nbuffers ready buffers!!
+ * 7/31/02: total rewrite. --NJC
+ *************************/
+inline bool is_ready_buf_full( int m )
+{
+  return ( dt3155_fbuffer[ m ]->ready_len == dt3155_fbuffer[ m ]->nbuffers );
+}
+
+/*****************************************************
+ * push_ready( m, buffer )
+ * m is minor # of device
+ *
+ *****************************************************/
+inline void push_ready( int m, int index )
+{
+  int head = dt3155_fbuffer[m]->ready_head;
+
+  dt3155_fbuffer[ m ]->ready_que[ head ] = index;
+  dt3155_fbuffer[ m ]->ready_head = ( (head + 1) %
+                                     (dt3155_fbuffer[ m ]->nbuffers) );
+  dt3155_fbuffer[ m ]->ready_len++;
+
+}
+
+/*****************************************************
+ * get_tail()
+ * m is minor # of device
+ *
+ * Simply comptutes the tail given the head and the length.
+ *****************************************************/
+static inline int get_tail( int m )
+{
+  return ((dt3155_fbuffer[ m ]->ready_head -
+          dt3155_fbuffer[ m ]->ready_len +
+          dt3155_fbuffer[ m ]->nbuffers)%
+         (dt3155_fbuffer[ m ]->nbuffers));
+}
+
+
+
+/*****************************************************
+ * pop_ready()
+ * m is minor # of device
+ *
+ * This assumes that there is a ready buffer ready... should
+ * be checked (e.g. with is_ready_buf_empty()  prior to call.
+ *****************************************************/
+inline int pop_ready( int m )
+{
+  int tail;
+  tail = get_tail(m);
+  dt3155_fbuffer[ m ]->ready_len--;
+  return dt3155_fbuffer[ m ]->ready_que[ tail ];
+}
+
+
+/*****************************************************
+ * printques
+ * m is minor # of device
+ *****************************************************/
+inline void printques( int m )
+{
+  int head = dt3155_fbuffer[ m ]->ready_head;
+  int tail;
+  int num = dt3155_fbuffer[ m ]->nbuffers;
+  int frame_index;
+  int index;
+
+  tail = get_tail(m);
+
+  printk("\n R:");
+  for ( index = tail; index != head; index++, index = index % (num) )
+    {
+      frame_index = dt3155_fbuffer[ m ]->ready_que[ index ];
+      printk(" %d ", frame_index );
+    }
+
+  printk("\n E:");
+  for ( index = 0; index < dt3155_fbuffer[ m ]->empty_len; index++ )
+    {
+      frame_index = dt3155_fbuffer[ m ]->empty_buffers[ index ];
+      printk(" %d ", frame_index );
+    }
+
+  frame_index = dt3155_fbuffer[ m ]->active_buf;
+  printk("\n A: %d", frame_index);
+
+  frame_index = dt3155_fbuffer[ m ]->locked_buf;
+  printk("\n L: %d \n", frame_index );
+
+}
+
+/*****************************************************
+ * adjust_4MB
+ *
+ *  If a buffer intersects the 4MB boundary, push
+ *  the start address up to the beginning of the
+ *  next 4MB chunk (assuming bufsize < 4MB).
+ *****************************************************/
+u_long adjust_4MB (u_long buf_addr, u_long bufsize) {
+  if (((buf_addr+bufsize) & UPPER_10_BITS) != (buf_addr & UPPER_10_BITS))
+    return (buf_addr+bufsize) & UPPER_10_BITS;
+  else
+    return buf_addr;
+}
+
+
+/*****************************************************
+ * allocate_buffers
+ *
+ *  Try to allocate enough memory for all requested
+ *  buffers.  If there is not enough free space
+ *  try for less memory.
+ *****************************************************/
+void allocate_buffers (u_long *buf_addr, u_long* total_size_kbs,
+                      u_long bufsize)
+{
+  /* Compute the minimum amount of memory guaranteed to hold all
+     MAXBUFFERS such that no buffer crosses the 4MB boundary.
+     Store this value in the variable "full_size" */
+
+  u_long allocator_max;
+  u_long bufs_per_chunk = (FOUR_MB / bufsize);
+  u_long filled_chunks = (MAXBUFFERS-1) / bufs_per_chunk;
+  u_long leftover_bufs = MAXBUFFERS - filled_chunks * bufs_per_chunk;
+
+  u_long full_size = bufsize      /* possibly unusable part of 1st chunk */
+    + filled_chunks * FOUR_MB   /* max # of completely filled 4mb chunks */
+    + leftover_bufs * bufsize;  /* these buffs will be in a partly filled
+                                  chunk at beginning or end */
+
+  u_long full_size_kbs = 1 + (full_size-1) / 1024;
+  u_long min_size_kbs = 2*ndevices*bufsize / 1024;
+  u_long size_kbs;
+
+  /* Now, try to allocate full_size.  If this fails, keep trying for
+     less & less memory until it succeeds. */
+#ifndef STANDALONE_ALLOCATOR
+  /* initialize the allocator            */
+  allocator_init(&allocator_max);
+#endif
+  size_kbs = full_size_kbs;
+  *buf_addr = 0;
+  printk ("DT3155: We would like to get: %d KB\n", (u_int)(full_size_kbs));
+  printk ("DT3155: ...but need at least: %d KB\n", (u_int)(min_size_kbs));
+  printk ("DT3155: ...the allocator has: %d KB\n", (u_int)(allocator_max));
+  size_kbs = (full_size_kbs <= allocator_max ? full_size_kbs : allocator_max);
+  if (size_kbs > min_size_kbs) {
+    if ((*buf_addr = allocator_allocate_dma (size_kbs, GFP_KERNEL)) != 0) {
+      printk ("DT3155:  Managed to allocate: %d KB\n", (u_int)size_kbs);
+      *total_size_kbs = size_kbs;
+      return;
+    }
+  }
+  /* If we got here, the allocation failed */
+  printk ("DT3155: Allocator failed!\n");
+  *buf_addr = 0;
+  *total_size_kbs = 0;
+  return;
+
+}
+
+
+/*****************************************************
+ * dt3155_setup_buffers
+ *
+ *  setup_buffers just puts the buffering system into
+ *  a consistent state before the start of interrupts
+ *
+ * JML : it looks like all the buffers need to be
+ * continuous. So I'm going to try and allocate one
+ * continuous buffer.
+ *
+ * GCS : Fix DMA problems when buffer spans
+ * 4MB boundary.  Also, add error checking.  This
+ * function will return -ENOMEM when not enough memory.
+ *****************************************************/
+u_long dt3155_setup_buffers(u_long *allocatorAddr)
+
+{
+  u_long index;
+  u_long rambuff_addr; /* start of allocation */
+  u_long rambuff_size; /* total size allocated to driver */
+  u_long rambuff_acm;  /* accumlator, keep track of how much
+                         is left after being split up*/
+  u_long rambuff_end;  /* end of rambuff */
+  u_long numbufs;      /* number of useful buffers allocated (per device) */
+  u_long bufsize      = DT3155_MAX_ROWS * DT3155_MAX_COLS;
+  int m;               /* minor # of device, looped for all devs */
+
+  /* zero the fbuffer status and address structure */
+  for ( m = 0; m < ndevices; m++)
+    {
+      dt3155_fbuffer[ m ] = &(dt3155_status[ m ].fbuffer);
+
+      /* Make sure the buffering variables are consistent */
+      {
+       u_char *ptr = (u_char *) dt3155_fbuffer[ m ];
+       for( index = 0; index < sizeof(struct dt3155_fbuffer_s); index++)
+         *(ptr++)=0;
+      }
+    }
+
+  /* allocate a large contiguous chunk of RAM */
+  allocate_buffers (&rambuff_addr, &rambuff_size, bufsize);
+  printk( "DT3155: mem info\n" );
+  printk( "  - rambuf_addr = 0x%x \n", (u_int)rambuff_addr );
+  printk( "  - length (kb) = %u \n",  (u_int)rambuff_size );
+  if( rambuff_addr == 0 )
+    {
+      printk( KERN_INFO
+             "DT3155: Error setup_buffers() allocator dma failed \n" );
+      return -ENOMEM;
+    }
+  *allocatorAddr = rambuff_addr;
+  rambuff_end = rambuff_addr + 1024 * rambuff_size;
+
+  /* after allocation, we need to count how many useful buffers there
+     are so we can give an equal number to each device */
+  rambuff_acm = rambuff_addr;
+  for ( index = 0; index < MAXBUFFERS; index++) {
+    rambuff_acm = adjust_4MB (rambuff_acm, bufsize);/*avoid spanning 4MB bdry*/
+    if (rambuff_acm + bufsize > rambuff_end)
+      break;
+    rambuff_acm += bufsize;
+  }
+  /* Following line is OK, will waste buffers if index
+   * not evenly divisible by ndevices -NJC*/
+  numbufs = index / ndevices;
+  printk ("  - numbufs = %u\n", (u_int) numbufs);
+  if (numbufs < 2) {
+    printk( KERN_INFO
+           "DT3155: Error setup_buffers() couldn't allocate 2 bufs/board\n" );
+    return -ENOMEM;
+  }
+
+  /* now that we have board memory we spit it up */
+  /* between the boards and the buffers          */
+  rambuff_acm = rambuff_addr;
+  for ( m = 0; m < ndevices; m ++)
+    {
+      rambuff_acm = adjust_4MB (rambuff_acm, bufsize);
+
+      /* Save the start of this boards buffer space (for mmap).  */
+      dt3155_status[ m ].mem_addr = rambuff_acm;
+
+      for (index = 0; index < numbufs; index++)
+       {
+         rambuff_acm = adjust_4MB (rambuff_acm, bufsize);
+         if (rambuff_acm + bufsize > rambuff_end) {
+           /* Should never happen */
+           printk ("DT3155 PROGRAM ERROR (GCS)\n"
+                   "Error distributing allocated buffers\n");
+           return -ENOMEM;
+         }
+
+         dt3155_fbuffer[ m ]->frame_info[ index ].addr = rambuff_acm;
+         push_empty( index, m );
+         /* printk("  - Buffer : %lx\n",
+          * dt3155_fbuffer[ m ]->frame_info[ index ].addr );
+          */
+         dt3155_fbuffer[ m ]->nbuffers += 1;
+         rambuff_acm += bufsize;
+       }
+
+      /* Make sure there is an active buffer there. */
+      dt3155_fbuffer[ m ]->active_buf    = pop_empty( m );
+      dt3155_fbuffer[ m ]->even_happened = 0;
+      dt3155_fbuffer[ m ]->even_stopped  = 0;
+
+      /* make sure there is no locked_buf JML 2/28/00 */
+      dt3155_fbuffer[ m ]->locked_buf = -1;
+
+      dt3155_status[ m ].mem_size =
+       rambuff_acm - dt3155_status[ m ].mem_addr;
+
+      /* setup the ready queue */
+      dt3155_fbuffer[ m ]->ready_head = 0;
+      dt3155_fbuffer[ m ]->ready_len = 0;
+      printk("Available buffers for device %d: %d\n",
+            m, dt3155_fbuffer[ m ]->nbuffers);
+    }
+
+  return 1;
+}
+
+/*****************************************************
+ * internal_release_locked_buffer
+ *
+ * The internal function for releasing a locked buffer.
+ * It assumes interrupts are turned off.
+ *
+ * m is minor number of device
+ *****************************************************/
+static inline void internal_release_locked_buffer( int m )
+{
+  /* Pointer into global structure for handling buffers */
+  if ( dt3155_fbuffer[ m ]->locked_buf >= 0 )
+    {
+      push_empty( dt3155_fbuffer[ m ]->locked_buf, m );
+      dt3155_fbuffer[ m ]->locked_buf = -1;
+    }
+}
+
+
+/*****************************************************
+ * dt3155_release_locked_buffer()
+ * m is minor # of device
+ *
+ * The user function of the above.
+ *
+ *****************************************************/
+inline void dt3155_release_locked_buffer( int m )
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+       unsigned long int flags;
+       local_save_flags(flags);
+       local_irq_disable();
+       internal_release_locked_buffer(m);
+       local_irq_restore(flags);
+#else
+  int flags;
+
+  save_flags( flags );
+  cli();
+  internal_release_locked_buffer( m );
+  restore_flags( flags );
+#endif
+}
+
+
+/*****************************************************
+ * dt3155_flush()
+ * m is minor # of device
+ *
+ *****************************************************/
+inline int dt3155_flush( int m )
+{
+  int index;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+       unsigned long int flags;
+       local_save_flags(flags);
+       local_irq_disable();
+#else
+  int flags;
+  save_flags( flags );
+  cli();
+#endif
+
+  internal_release_locked_buffer( m );
+  dt3155_fbuffer[ m ]->empty_len = 0;
+
+  for ( index = 0; index < dt3155_fbuffer[ m ]->nbuffers; index++ )
+    push_empty( index,  m );
+
+  /* Make sure there is an active buffer there. */
+  dt3155_fbuffer[ m ]->active_buf = pop_empty( m );
+
+  dt3155_fbuffer[ m ]->even_happened = 0;
+  dt3155_fbuffer[ m ]->even_stopped  = 0;
+
+  /* setup the ready queue  */
+  dt3155_fbuffer[ m ]->ready_head = 0;
+  dt3155_fbuffer[ m ]->ready_len = 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+  local_irq_restore(flags);
+#else
+  restore_flags( flags );
+#endif
+
+  return 0;
+}
+
+/*****************************************************
+ * dt3155_get_ready_buffer()
+ * m is minor # of device
+ *
+ * get_ready_buffer will grab the next chunk of data
+ * if it is already there, otherwise it returns 0.
+ * If the user has a buffer locked it will unlock
+ * that buffer before returning the new one.
+ *****************************************************/
+inline int dt3155_get_ready_buffer( int m )
+{
+  int frame_index;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+       unsigned long int flags;
+       local_save_flags(flags);
+       local_irq_disable();
+#else
+  int flags;
+  save_flags( flags );
+  cli();
+#endif
+
+#ifdef DEBUG_QUES_A
+  printques( m );
+#endif
+
+  internal_release_locked_buffer( m );
+
+  if (is_ready_buf_empty( m ))
+    frame_index = -1;
+  else
+    {
+      frame_index = pop_ready( m );
+      dt3155_fbuffer[ m ]->locked_buf = frame_index;
+    }
+
+#ifdef DEBUG_QUES_B
+  printques( m );
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+  local_irq_restore(flags);
+#else
+  restore_flags( flags );
+#endif
+
+  return frame_index;
+}
diff --git a/drivers/staging/dt3155/dt3155_isr.h b/drivers/staging/dt3155/dt3155_isr.h
new file mode 100644 (file)
index 0000000..b117b50
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+
+Copyright 1996,2002 Gregory D. Hager, Alfred A. Rizzi, Noah J. Cowan,
+                    Jason Lapenta, Scott Smedley
+
+This file is part of the DT3155 Device Driver.
+
+The DT3155 Device Driver is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The DT3155 Device Driver is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the DT3155 Device Driver; if not, write to the Free
+Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+MA 02111-1307 USA
+
+
+-- Changes --
+
+  Date     Programmer   Description of changes made
+  -------------------------------------------------------------------
+  03-Jul-2000 JML       n/a
+  24-Jul-2002 SS        GPL licence.
+  26-Oct-2009 SS       Porting to 2.6.30 kernel.
+
+-- notes --
+
+*/
+
+#ifndef  DT3155_ISR_H
+#define  DT3155_ISR_H
+
+extern struct dt3155_fbuffer_s *dt3155_fbuffer[MAXBOARDS];
+
+/* User functions for buffering */
+/* Initialize the buffering system.  This should */
+/* be called prior to enabling interrupts */
+
+u_long dt3155_setup_buffers(u_long *allocatorAddr);
+
+/* Get the next frame of data if it is ready.  Returns */
+/* zero if no data is ready.  If there is data but */
+/* the user has a locked buffer, it will unlock that */
+/* buffer and return it to the free list. */
+
+int dt3155_get_ready_buffer(int minor);
+
+/* Return a locked buffer to the free list */
+
+void dt3155_release_locked_buffer(int minor);
+
+/* Flush the buffer system */
+int dt3155_flush(int minor);
+
+/**********************************
+ * Simple array based que struct
+ **********************************/
+
+bool are_empty_buffers( int minor );
+void push_empty( int index, int minor );
+
+int  pop_empty( int minor );
+
+bool is_ready_buf_empty( int minor );
+bool is_ready_buf_full( int minor );
+
+void push_ready( int minor, int index );
+int  pop_ready( int minor );
+
+
+#endif
diff --git a/drivers/staging/dt3155/pci-compat.h b/drivers/staging/dt3155/pci-compat.h
new file mode 100644 (file)
index 0000000..5f47d5a
--- /dev/null
@@ -0,0 +1,97 @@
+
+/* This header only makes send when included in a 2.0 compile */
+
+#ifndef _PCI_COMPAT_H_
+#define _PCI_COMPAT_H_
+
+#ifdef __KERNEL__
+
+#include <linux/bios32.h> /* pcibios_* */
+#include <linux/pci.h> /* pcibios_* */
+#include <linux/malloc.h> /* kmalloc */
+
+/* fake the new pci interface based on the old one: encapsulate bus/devfn */
+struct pci_fake_dev {
+    u8 bus;
+    u8 devfn;
+    int index;
+};
+#define pci_dev pci_fake_dev /* the other pci_dev is unused by 2.0 drivers */
+
+extern inline struct pci_dev *pci_find_device(unsigned int vendorid,
+                                             unsigned int devid,
+                                             struct pci_dev *from)
+{
+    struct pci_dev *pptr = kmalloc(sizeof(*pptr), GFP_KERNEL);
+    int index = 0;
+    int ret;
+
+    if (!pptr) return NULL;
+    if (from) index = pptr->index + 1;
+    ret = pcibios_find_device(vendorid, devid, index,
+                             &pptr->bus, &pptr->devfn);
+    if (ret) { kfree(pptr); return NULL; }
+    return pptr;
+}
+
+extern inline struct pci_dev *pci_find_class(unsigned int class,
+                                            struct pci_dev *from)
+{
+    return NULL; /* FIXME */
+}
+
+extern inline void pci_release_device(struct pci_dev *dev)
+{
+    kfree(dev);
+}
+
+/* struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn); */
+
+#define pci_present pcibios_present
+
+extern inline int
+pci_read_config_byte(struct pci_dev *dev, u8 where, u8 *val)
+{
+    return pcibios_read_config_byte(dev->bus, dev->devfn, where, val);
+}
+
+extern inline int
+pci_read_config_word(struct pci_dev *dev, u8 where, u16 *val)
+{
+    return pcibios_read_config_word(dev->bus, dev->devfn, where, val);
+}
+
+extern inline int
+pci_read_config_dword(struct pci_dev *dev, u8 where, u32 *val)
+{
+    return pcibios_read_config_dword(dev->bus, dev->devfn, where, val);
+}
+
+extern inline int
+pci_write_config_byte(struct pci_dev *dev, u8 where, u8 val)
+{
+    return pcibios_write_config_byte(dev->bus, dev->devfn, where, val);
+}
+
+extern inline int
+pci_write_config_word(struct pci_dev *dev, u8 where, u16 val)
+{
+    return pcibios_write_config_word(dev->bus, dev->devfn, where, val);
+}
+
+extern inline int
+pci_write_config_dword(struct pci_dev *dev, u8 where, u32 val)
+{
+    return pcibios_write_config_dword(dev->bus, dev->devfn, where, val);
+}
+
+extern inline void pci_set_master(struct pci_dev *dev)
+{
+    u16 cmd;
+    pcibios_read_config_word(dev->bus, dev->devfn, PCI_COMMAND, &cmd);
+    cmd |= PCI_COMMAND_MASTER;
+    pcibios_write_config_word(dev->bus, dev->devfn, PCI_COMMAND, cmd);
+}
+
+#endif /* __KERNEL__ */
+#endif /* _PCI_COMPAT_H_ */
diff --git a/drivers/staging/dt3155/sysdep.h b/drivers/staging/dt3155/sysdep.h
new file mode 100644 (file)
index 0000000..1d575d4
--- /dev/null
@@ -0,0 +1,820 @@
+/*
+ * sysdep.h -- centralizing compatibility issues between 2.0, 2.2, 2.4
+ *
+ * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
+ * Copyright (C) 2001 O'Reilly & Associates
+ *
+ * The source code in this file can be freely used, adapted,
+ * and redistributed in source or binary form, so long as an
+ * acknowledgment appears in derived source files.  The citation
+ * should list that the code comes from the book "Linux Device
+ * Drivers" by Alessandro Rubini and Jonathan Corbet, published
+ * by O'Reilly & Associates.   No warranty is attached;
+ * we cannot take responsibility for errors or fitness for use.
+ *
+ * $Id: sysdep.h,v 1.2 2005/08/09 06:08:51 ssmedley Exp $
+ */
+
+
+#ifndef _SYSDEP_H_
+#define _SYSDEP_H_
+
+#ifndef LINUX_VERSION_CODE
+#  include <linux/version.h>
+#endif
+
+#ifndef KERNEL_VERSION /* pre-2.1.90 didn't have it */
+#  define KERNEL_VERSION(vers,rel,seq) ( ((vers)<<16) | ((rel)<<8) | (seq) )
+#endif
+
+/* only allow 2.0.x  2.2.y and 2.4.z */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,0,0) /* not < 2.0 */
+#  error "This kernel is too old: not supported by this file"
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,7,0) /* not > 2.7, by now */
+#  error "This kernel is too recent: not supported by this file"
+#endif
+#if (LINUX_VERSION_CODE & 0xff00) == 1 /* not 2.1 */
+#  error "Please don't use linux-2.1, use 2.2, 2.4 or 2.6 instead"
+#endif
+#if (LINUX_VERSION_CODE & 0xff00) == 3 /* not 2.3 */
+#  error "Please don't use linux-2.3, use 2.4 or 2.6 instead"
+#endif
+
+/* remember about the current version */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+#  define LINUX_20
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+#  define LINUX_22
+#else
+#  define LINUX_24
+#endif
+
+/* we can't support versioning in pre-2.4 because we #define some functions */
+#if !defined(LINUX_24) && defined(CONFIG_MODVERSIONS)
+#  error "This sysdep.h can't support CONFIG_MODVERSIONS"
+#  error "and old kernels at the same time."
+#  error "Either use 2.4 or avoid using versioning"
+#endif
+
+#ifndef LINUX_20 /* include vmalloc.h if this is 2.2/2.4 */
+#  ifdef VM_READ /* a typical flag defined by mm.h */
+#    include <linux/vmalloc.h>
+#  endif
+#endif
+
+#include <linux/sched.h>
+
+/* Modularization issues */
+#ifdef LINUX_20
+#  define __USE_OLD_SYMTAB__
+#  define EXPORT_NO_SYMBOLS register_symtab(NULL);
+#  define REGISTER_SYMTAB(tab) register_symtab(tab)
+#else
+#  define REGISTER_SYMTAB(tab) /* nothing */
+#endif
+
+#ifdef __USE_OLD_SYMTAB__
+#  define __MODULE_STRING(s)         /* nothing */
+#  define MODULE_PARM(v,t)           /* nothing */
+#  define MODULE_PARM_DESC(v,t)      /* nothing */
+#  define MODULE_AUTHOR(n)           /* nothing */
+#  define MODULE_DESCRIPTION(d)      /* nothing */
+#  define MODULE_SUPPORTED_DEVICE(n) /* nothing */
+#endif
+
+/*
+ * In version 2.2 (up to 2.2.19, at least), the macro for request_module()
+ * when no kmod is there is wrong. It's a "do {} while 0" but it shouldbe int
+ */
+#ifdef LINUX_22
+#  ifndef CONFIG_KMOD
+#    undef request_module
+#    define request_module(name) -ENOSYS
+#  endif
+#endif
+
+
+#ifndef LINUX_20
+#  include <linux/init.h>     /* module_init/module_exit */
+#endif
+
+#ifndef module_init
+#  define module_init(x)        int init_module(void) { return x(); }
+#  define module_exit(x)        void cleanup_module(void) { x(); }
+#endif
+
+#ifndef SET_MODULE_OWNER
+#  define SET_MODULE_OWNER(structure) /* nothing */
+#endif
+
+/*
+ * "select" changed in 2.1.23. The implementation is twin, but this
+ * header is new
+ *
+ */
+#ifdef LINUX_20
+#  define __USE_OLD_SELECT__
+#else
+#  include <linux/poll.h>
+#endif
+
+#ifdef LINUX_20
+#  define INODE_FROM_F(filp) ((filp)->f_inode)
+#else
+#  define INODE_FROM_F(filp) ((filp)->f_dentry->d_inode)
+#endif
+
+/* Other changes in the fops are solved using wrappers */
+
+/*
+ * Wait queues changed with 2.3
+ */
+#ifndef DECLARE_WAIT_QUEUE_HEAD
+#  define DECLARE_WAIT_QUEUE_HEAD(head) struct wait_queue *head = NULL
+   typedef  struct wait_queue *wait_queue_head_t;
+#  define init_waitqueue_head(head) (*(head)) = NULL
+
+/* offer wake_up_sync as an alias for wake_up */
+#  define wake_up_sync(head) wake_up(head)
+#  define wake_up_interruptible_sync(head) wake_up_interruptible(head)
+
+/* Pretend we have add_wait_queue_exclusive */
+#  define add_wait_queue_exclusive(q,entry) add_wait_queue ((q), (entry))
+
+#endif /* no DECLARE_WAIT_QUEUE_HEAD */
+
+/*
+ * Define wait_event for 2.0 kernels.  (This ripped off directly from
+ * the 2.2.18 sched.h)
+ */
+#ifdef LINUX_20
+
+#define __wait_event(wq, condition)                                    \
+do {                                                                   \
+       struct wait_queue __wait;                                       \
+                                                                       \
+       __wait.task = current;                                          \
+       add_wait_queue(&wq, &__wait);                                   \
+       for (;;) {                                                      \
+               current->state = TASK_UNINTERRUPTIBLE;                  \
+               mb();                                                   \
+               if (condition)                                          \
+                       break;                                          \
+               schedule();                                             \
+       }                                                               \
+       current->state = TASK_RUNNING;                                  \
+       remove_wait_queue(&wq, &__wait);                                \
+} while (0)
+
+#define wait_event(wq, condition)                                      \
+do {                                                                   \
+       if (condition)                                                  \
+               break;                                                  \
+       __wait_event(wq, condition);                                    \
+} while (0)
+
+#define __wait_event_interruptible(wq, condition, ret)                 \
+do {                                                                   \
+       struct wait_queue __wait;                                       \
+                                                                       \
+       __wait.task = current;                                          \
+       add_wait_queue(&wq, &__wait);                                   \
+       for (;;) {                                                      \
+               current->state = TASK_INTERRUPTIBLE;                    \
+               mb();                                                   \
+               if (condition)                                          \
+                       break;                                          \
+               if (!signal_pending(current)) {                         \
+                       schedule();                                     \
+                       continue;                                       \
+               }                                                       \
+               ret = -ERESTARTSYS;                                     \
+               break;                                                  \
+       }                                                               \
+       current->state = TASK_RUNNING;                                  \
+       remove_wait_queue(&wq, &__wait);                                \
+} while (0)
+
+#define wait_event_interruptible(wq, condition)                                \
+({                                                                     \
+       int __ret = 0;                                                  \
+       if (!(condition))                                               \
+               __wait_event_interruptible(wq, condition, __ret);       \
+       __ret;                                                          \
+})
+#endif
+
+
+/*
+ * 2.3 added tasklets
+ */
+#ifdef LINUX_24
+#  define HAVE_TASKLETS
+#endif
+
+
+
+
+/* FIXME: implement the other versions of wake_up etc */
+
+
+/*
+ * access to user space: use the 2.2 functions,
+ * and implement them as macros for 2.0
+ */
+
+#ifdef LINUX_20
+#  include <asm/segment.h>
+#  define access_ok(t,a,sz)           (verify_area((t),(void *) (a),(sz)) ? 0 : 1)
+#  define verify_area_20              verify_area
+#  define   copy_to_user(t,f,n)         (memcpy_tofs((t), (f), (n)), 0)
+#  define copy_from_user(t,f,n)       (memcpy_fromfs((t), (f), (n)), 0)
+#  define   __copy_to_user(t,f,n)       copy_to_user((t), (f), (n))
+#  define __copy_from_user(t,f,n)     copy_from_user((t), (f), (n))
+
+#  define PUT_USER(val,add)           (put_user((val),(add)), 0)
+#  define __PUT_USER(val,add)         PUT_USER((val),(add))
+
+#  define GET_USER(dest,add)          ((dest)=get_user((add)), 0)
+#  define __GET_USER(dest,add)        GET_USER((dest),(add))
+#else
+#  include <asm/uaccess.h>
+#  include <asm/io.h>
+#  define verify_area_20(t,a,sz) (0) /* == success */
+#  define   PUT_USER   put_user
+#  define __PUT_USER __put_user
+#  define   GET_USER   get_user
+#  define __GET_USER __get_user
+#endif
+
+/*
+ * Allocation issues
+ */
+#ifdef GFP_USER /* only if mm.h has been included */
+#  ifdef LINUX_20
+#    define __GFP_DMA GFP_DMA /* 2.0 didn't have the leading __ */
+#  endif
+#  ifndef LINUX_24
+#    define __GFP_HIGHMEM  0  /* was not there */
+#    define GFP_HIGHUSER   0   /* idem */
+#  endif
+
+#  ifdef LINUX_20
+#    define __get_free_pages(a,b) __get_free_pages((a),(b),0)
+#  endif
+#  ifndef LINUX_24
+#    define get_zeroed_page get_free_page
+#  endif
+#endif
+
+/* ioremap */
+#if defined(LINUX_20) && defined(_LINUX_MM_H)
+#  define ioremap_nocache ioremap
+#  ifndef __i386__
+   /* This simple approach works for non-PC platforms. */
+#    define ioremap vremap
+#    define iounmap vfree
+#  else /* the PC has <expletive> ISA; 2.2 and 2.4 remap it, 2.0 needs not */
+extern inline void *ioremap(unsigned long phys_addr, unsigned long size)
+{
+    if (phys_addr >= 0xA0000 && phys_addr + size <= 0x100000)
+        return (void *)phys_addr;
+    return vremap(phys_addr, size);
+}
+
+extern inline void iounmap(void *addr)
+{
+    if ((unsigned long)addr >= 0xA0000
+            && (unsigned long)addr < 0x100000)
+        return;
+    vfree(addr);
+}
+#  endif
+#endif
+
+/* Also, define check_mem_region etc */
+#ifndef LINUX_24
+#  define check_mem_region(a,b)     0 /* success */
+#  define request_mem_region(a,b,c) /* nothing */
+#  define release_mem_region(a,b)   /* nothing */
+#endif
+
+/* implement capable() for 2.0 */
+#ifdef LINUX_20
+#  define capable(anything)  suser()
+#endif
+
+/* The use_count of exec_domain and binfmt changed in 2.1.23 */
+
+#ifdef LINUX_20
+#  define INCRCOUNT(p)  ((p)->module ? __MOD_INC_USE_COUNT((p)->module) : 0)
+#  define DECRCOUNT(p)  ((p)->module ? __MOD_DEC_USE_COUNT((p)->module) : 0)
+#  define CURRCOUNT(p)  ((p)->module && (p)->module->usecount)
+#else
+#  define INCRCOUNT(p)  ((p)->use_count++)
+#  define DECRCOUNT(p)  ((p)->use_count--)
+#  define CURRCOUNT(p)  ((p)->use_count)
+#endif
+
+/*
+ * /proc has changed a lot across the versions...
+ */
+#ifdef LINUX_20
+#  define USE_PROC_REGISTER
+#endif
+
+
+/*
+ * 2.2 didn't have create_proc_{read|info}_entry yet.
+ * And it looks like there are no other "interesting" entry point, as
+ * the rest is somehow esotique (mknod, symlink, ...)
+ */
+#ifdef LINUX_22
+#  ifdef PROC_SUPER_MAGIC  /* Only if procfs is being used */
+extern inline struct proc_dir_entry *create_proc_read_entry(const char *name,
+                          mode_t mode, struct proc_dir_entry *base,
+                          read_proc_t *read_proc, void * data)
+{
+    struct proc_dir_entry *res=create_proc_entry(name,mode,base);
+    if (res) {
+        res->read_proc=read_proc;
+        res->data=data;
+    }
+    return res;
+}
+
+#    ifndef create_proc_info_entry /* added in 2.2.18 */
+typedef int (get_info_t)(char *, char **, off_t, int, int);
+extern inline struct proc_dir_entry *create_proc_info_entry(const char *name,
+        mode_t mode, struct proc_dir_entry *base, get_info_t *get_info)
+{
+        struct proc_dir_entry *res=create_proc_entry(name,mode,base);
+        if (res) res->get_info=get_info;
+        return res;
+}
+#    endif  /* no create_proc_info_entry */
+#  endif
+#endif
+
+#ifdef LINUX_20
+#  define test_and_set_bit(nr,addr)  test_bit((nr),(addr))
+#  define test_and_clear_bit(nr,addr) clear_bit((nr),(addr))
+#  define test_and_change_bit(nr,addr) change_bit((nr),(addr))
+#endif
+
+
+/* 2.0 had no read and write memory barriers, and 2.2 lacks the
+   set_ functions */
+#ifndef LINUX_24
+#  ifdef LINUX_20
+#    define wmb() mb() /* this is a big penalty on non-reordering platfs */
+#    define rmb() mb() /* this is a big penalty on non-reordering platfs */
+#  endif /* LINUX_20 */
+
+#define set_mb() do { var = value; mb(); } while (0)
+#define set_wmb() do { var = value; wmb(); } while (0)
+#endif /* ! LINUX_24 */
+
+
+
+/* 2.1.30 removed these functions. Let's define them, just in case */
+#ifndef LINUX_20
+#  define queue_task_irq      queue_task
+#  define queue_task_irq_off  queue_task
+#endif
+
+/* 2.1.10 and 2.1.43 introduced new functions. They are worth using */
+
+#ifdef LINUX_20
+
+#  include <asm/byteorder.h>
+#  ifdef __LITTLE_ENDIAN
+#    define cpu_to_le16(x) (x)
+#    define cpu_to_le32(x) (x)
+#    define cpu_to_be16(x) htons((x))
+#    define cpu_to_be32(x) htonl((x))
+#  else
+#    define cpu_to_be16(x) (x)
+#    define cpu_to_be32(x) (x)
+     extern inline __u16 cpu_to_le16(__u16 x) { return (x<<8) | (x>>8);}
+     extern inline __u32 cpu_to_le32(__u32 x) { return (x>>24) |
+             ((x>>8)&0xff00) | ((x<<8)&0xff0000) | (x<<24);}
+#  endif
+
+#  define le16_to_cpu(x)  cpu_to_le16(x)
+#  define le32_to_cpu(x)  cpu_to_le32(x)
+#  define be16_to_cpu(x)  cpu_to_be16(x)
+#  define be32_to_cpu(x)  cpu_to_be32(x)
+
+#  define cpu_to_le16p(addr) (cpu_to_le16(*(addr)))
+#  define cpu_to_le32p(addr) (cpu_to_le32(*(addr)))
+#  define cpu_to_be16p(addr) (cpu_to_be16(*(addr)))
+#  define cpu_to_be32p(addr) (cpu_to_be32(*(addr)))
+
+   extern inline void cpu_to_le16s(__u16 *a) {*a = cpu_to_le16(*a);}
+   extern inline void cpu_to_le32s(__u16 *a) {*a = cpu_to_le32(*a);}
+   extern inline void cpu_to_be16s(__u16 *a) {*a = cpu_to_be16(*a);}
+   extern inline void cpu_to_be32s(__u16 *a) {*a = cpu_to_be32(*a);}
+
+#  define le16_to_cpup(x) cpu_to_le16p(x)
+#  define le32_to_cpup(x) cpu_to_le32p(x)
+#  define be16_to_cpup(x) cpu_to_be16p(x)
+#  define be32_to_cpup(x) cpu_to_be32p(x)
+
+#  define le16_to_cpus(x) cpu_to_le16s(x)
+#  define le32_to_cpus(x) cpu_to_le32s(x)
+#  define be16_to_cpus(x) cpu_to_be16s(x)
+#  define be32_to_cpus(x) cpu_to_be32s(x)
+
+#endif
+
+#ifdef LINUX_20
+#  define __USE_OLD_REBUILD_HEADER__
+#endif
+
+/*
+ * 2.0 didn't include sema_init, so we make our own - but only if it
+ * looks like semaphore.h got included.
+ */
+#ifdef LINUX_20
+#  ifdef MUTEX_LOCKED   /* Only if semaphore.h included */
+     extern inline void sema_init (struct semaphore *sem, int val)
+     {
+         sem->count = val;
+         sem->waking = sem->lock = 0;
+         sem->wait = NULL;
+     }
+#  endif
+#endif /* LINUX_20 */
+
+/*
+ * In 2.0, there is no real need for spinlocks, and they weren't really
+ * implemented anyway.
+ *
+ * XXX the _irqsave variant should be defined eventually to do the
+ * right thing.
+ */
+#ifdef LINUX_20
+typedef int spinlock_t;
+#  define spin_lock(lock)
+#  define spin_unlock(lock)
+#  define spin_lock_init(lock)
+
+#  define spin_lock_irqsave(lock,flags) do { \
+        save_flags(flags); cli(); } while (0);
+#  define spin_unlock_irqrestore(lock,flags) restore_flags(flags);
+#endif
+
+/*
+ * 2.1 stuffed the "flush" method into the middle of the file_operations
+ * structure.  The FOP_NO_FLUSH symbol is for drivers that do not implement
+ * flush (most of them), it can be inserted in initializers for all 2.x
+ * kernel versions.
+ */
+#ifdef LINUX_20
+#  define FOP_NO_FLUSH   /* nothing */
+#  define TAG_LLSEEK    lseek
+#  define TAG_POLL      select
+#else
+#  define FOP_NO_FLUSH  NULL,
+#  define TAG_LLSEEK    llseek
+#  define TAG_POLL      poll
+#endif
+
+
+
+/*
+ * fasync changed in 2.2.
+ */
+#ifdef LINUX_20
+/*  typedef struct inode *fasync_file; */
+#  define fasync_file struct inode *
+#else
+  typedef int fasync_file;
+#endif
+
+/* kill_fasync had less arguments, and a different indirection in the first */
+#ifndef LINUX_24
+#  define kill_fasync(ptrptr,sig,band)  kill_fasync(*(ptrptr),(sig))
+#endif
+
+/* other things that are virtualized: define the new functions for the old k */
+#ifdef LINUX_20
+#  define in_interrupt() (intr_count!=0)
+#  define mdelay(x) udelay((x)*1000)
+#  define signal_pending(current)  ((current)->signal & ~(current)->blocked)
+#endif
+
+#ifdef LINUX_PCI_H /* only if PCI stuff is being used */
+#  ifdef LINUX_20
+#    include "pci-compat.h" /* a whole set of replacement functions */
+#  else
+#    define  pci_release_device(d) /* placeholder, used in 2.0 to free stuff */
+#  endif
+#endif
+
+
+
+/*
+ * Some task state stuff
+ */
+
+#ifndef set_current_state
+#  define set_current_state(s) current->state = (s);
+#endif
+
+#ifdef LINUX_20
+extern inline void schedule_timeout(int timeout)
+{
+    current->timeout = jiffies + timeout;
+    current->state = TASK_INTERRUPTIBLE;
+    schedule();
+    current->timeout = 0;
+}
+
+extern inline long sleep_on_timeout(wait_queue_head_t *q, signed long timeout)
+{
+    signed long early = 0;
+
+    current->timeout = jiffies + timeout;
+    sleep_on (q);
+    if (current->timeout > 0) {
+        early = current->timeout - jiffies;
+        current->timeout = 0;
+    }
+    return early;
+}
+
+
+extern inline long interruptible_sleep_on_timeout(wait_queue_head_t *q,
+                signed long timeout)
+{
+    signed long early = 0;
+
+    current->timeout = jiffies + timeout;
+    interruptible_sleep_on (q);
+    if (current->timeout > 0) {
+        early = current->timeout - jiffies;
+        current->timeout = 0;
+    }
+    return early;
+}
+
+#endif /* LINUX_20 */
+
+/*
+ * Schedule_task was a late 2.4 addition.
+ */
+#ifndef LINUX_24
+extern inline int schedule_task(struct tq_struct *task)
+{
+        queue_task(task, &tq_scheduler);
+        return 1;
+}
+#endif
+
+
+/*
+ * Timing issues
+ */
+#ifdef LINUX_20
+#  define get_fast_time do_gettimeofday
+#endif
+
+#ifdef _LINUX_DELAY_H /* only if linux/delay.h is included */
+#  ifndef mdelay /* linux-2.0 */
+#    ifndef MAX_UDELAY_MS
+#      define MAX_UDELAY_MS   5
+#    endif
+#    define mdelay(n) (\
+        (__builtin_constant_p(n) && (n)<=MAX_UDELAY_MS) ? udelay((n)*1000) : \
+        ({unsigned long msec=(n); while (msec--) udelay(1000);}))
+#  endif /* mdelay */
+#endif /* _LINUX_DELAY_H */
+
+
+/*
+ * No del_timer_sync before 2.4
+ */
+#ifndef LINUX_24
+#  define del_timer_sync(timer) del_timer(timer)  /* and hope */
+#endif
+
+/*
+ * mod_timer wasn't present in 2.0
+ */
+#ifdef LINUX_20
+static inline int mod_timer(struct timer_list *timer, unsigned long expires)
+{
+    int pending = del_timer(timer);
+    if (pending) {
+        timer->expires = expires;
+        add_timer(timer);
+    }
+    return pending;
+}
+#endif
+/*
+ * Various changes in mmap and friends.
+ */
+
+#ifndef NOPAGE_SIGBUS
+#  define NOPAGE_SIGBUS  NULL  /* return value of the nopage memory method */
+#  define NOPAGE_OOM     NULL  /* No real equivalent in older kernels */
+#endif
+
+#ifndef VM_RESERVED            /* Added 2.4.0-test10 */
+#  define VM_RESERVED 0
+#endif
+
+#ifdef LINUX_24 /* use "vm_pgoff" to get an offset */
+#define VMA_OFFSET(vma)  ((vma)->vm_pgoff << PAGE_SHIFT)
+#else /* use "vm_offset" */
+#define VMA_OFFSET(vma)  ((vma)->vm_offset)
+#endif
+
+#ifdef MAP_NR
+#define virt_to_page(page) (mem_map + MAP_NR(page))
+#endif
+
+#ifndef get_page
+#  define get_page(p) atomic_inc(&(p)->count)
+#endif
+
+/*
+ * No DMA lock in 2.0.
+ */
+#ifdef LINUX_20
+static inline unsigned long claim_dma_lock(void)
+{
+    unsigned long flags;
+    save_flags(flags);
+    cli();
+    return flags;
+}
+
+static inline void release_dma_lock(unsigned long flags)
+{
+    restore_flags(flags);
+}
+#endif
+
+
+/*
+ * I/O memory was not managed by ealier kernels, define them as success
+ */
+
+#if 0 /* FIXME: what is the right way to do request_mem_region? */
+#ifndef LINUX_24
+#  define check_mem_region(start, len)          0
+#  define request_mem_region(start, len, name)  0
+#  define release_mem_region(start, len)        0
+
+   /*
+    * Also, request_ and release_ region used to return void. Return 0 instead
+    */
+#  define request_region(s, l, n)  ({request_region((s),(l),(n));0;})
+#  define release_region(s, l)     ({release_region((s),(l));0;})
+
+#endif /* not LINUX_24 */
+#endif
+
+/*
+ * Block layer stuff.
+ */
+#ifndef LINUX_24
+
+/* BLK_DEFAULT_QUEUE for use with these macros only!!!! */
+#define BLK_DEFAULT_QUEUE(major) blk_dev[(major)].request_fn
+#define blk_init_queue(where,request_fn) where = request_fn;
+#define blk_cleanup_queue(where) where = NULL;
+
+/* No QUEUE_EMPTY in older kernels */
+#ifndef QUEUE_EMPTY  /* Driver can redefine it too */
+#  define QUEUE_EMPTY (CURRENT != NULL)
+#endif
+
+#ifdef RO_IOCTLS
+static inline int blk_ioctl(kdev_t dev, unsigned int cmd, unsigned long arg)
+{
+    int err;
+
+    switch (cmd) {
+      case BLKRAGET: /* return the readahead value */
+        if (!arg)  return -EINVAL;
+        err = ! access_ok(VERIFY_WRITE, arg, sizeof(long));
+        if (err) return -EFAULT;
+        PUT_USER(read_ahead[MAJOR(dev)],(long *) arg);
+        return 0;
+
+      case BLKRASET: /* set the readahead value */
+        if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+        if (arg > 0xff) return -EINVAL; /* limit it */
+        read_ahead[MAJOR(dev)] = arg;
+        return 0;
+
+      case BLKFLSBUF: /* flush */
+        if (! capable(CAP_SYS_ADMIN)) return -EACCES; /* only root */
+        fsync_dev(dev);
+        invalidate_buffers(dev);
+        return 0;
+
+        RO_IOCTLS(dev, arg);
+    }
+    return -ENOTTY;
+}
+#endif  /* RO_IOCTLS */
+
+#ifdef LINUX_EXTENDED_PARTITION /* defined in genhd.h */
+static inline void register_disk(struct gendisk *gdev, kdev_t dev,
+                unsigned minors, struct file_operations *ops, long size)
+{
+    if (! gdev)
+        return;
+    resetup_one_dev(gdev, MINOR(dev) >> gdev->minor_shift);
+}
+#endif /* LINUX_EXTENDED_PARTITION */
+
+
+#else  /* it is Linux 2.4 */
+#define HAVE_BLKPG_H
+#endif /* LINUX_24 */
+
+
+
+#ifdef LINUX_20 /* physical and virtual addresses had the same value */
+#  define __pa(a) (a)
+#  define __va(a) (a)
+#endif
+
+/*
+ * Network driver compatibility
+ */
+
+/*
+ * 2.0 dev_kfree_skb had an extra arg.  The following is a little dangerous
+ * in that it assumes that FREE_WRITE is always wanted.  Very few 2.0 drivers
+ * use FREE_READ, but the number is *not* zero...
+ *
+ * Also: implement the non-checking versions of a couple skb functions -
+ * but they still check in 2.0.
+ */
+#ifdef LINUX_20
+#  define dev_kfree_skb(skb) dev_kfree_skb((skb), FREE_WRITE);
+
+#  define __skb_push(skb, len) skb_push((skb), (len))
+#  define __skb_put(skb, len)  skb_put((skb), (len))
+#endif
+
+/*
+ * Softnet changes in 2.4
+ */
+#ifndef LINUX_24
+#  ifdef _LINUX_NETDEVICE_H /* only if netdevice.h was included */
+#  define netif_start_queue(dev) clear_bit(0, (void *) &(dev)->tbusy);
+#  define netif_stop_queue(dev)  set_bit(0, (void *) &(dev)->tbusy);
+
+static inline void netif_wake_queue(struct device *dev)
+{
+    clear_bit(0, (void *) &(dev)->tbusy);
+    mark_bh(NET_BH);
+}
+
+/* struct device became struct net_device */
+#  define net_device device
+#  endif /* netdevice.h */
+#endif /* ! LINUX_24 */
+
+/*
+ * Memory barrier stuff, define what's missing from older kernel versions
+ */
+#ifdef switch_to /* this is always a macro, defined in <asm/sysstem.h> */
+
+#  ifndef set_mb
+#    define set_mb(var, value) do {(var) = (value); mb();}  while 0
+#  endif
+#  ifndef set_rmb
+#    define set_rmb(var, value) do {(var) = (value); rmb();}  while 0
+#  endif
+#  ifndef set_wmb
+#    define set_wmb(var, value) do {(var) = (value); wmb();}  while 0
+#  endif
+
+/* The hw barriers are defined as sw barriers. A correct thing if this
+   specific kernel/platform is supported but has no specific instruction */
+#  ifndef mb
+#    define mb barrier
+#  endif
+#  ifndef rmb
+#    define rmb barrier
+#  endif
+#  ifndef wmb
+#    define wmb barrier
+#  endif
+
+#endif /* switch to (i.e. <asm/system.h>) */
+
+
+#endif /* _SYSDEP_H_ */