--- /dev/null
+
+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
--- /dev/null
+
+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.
+
+
+
--- /dev/null
+/*
+ * 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;
+}
+
+
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+
+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 */
--- /dev/null
+#! /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
+
--- /dev/null
+/*
+
+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
+}
+
--- /dev/null
+/*
+
+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
--- /dev/null
+/*
+
+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 */
+}
--- /dev/null
+/*
+
+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
--- /dev/null
+/*
+
+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;
+}
--- /dev/null
+/*
+
+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
--- /dev/null
+
+/* 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_ */
--- /dev/null
+/*
+ * 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_ */