From aa337ef1fbc9650f8b5b9087af1dac33f71eb01c Mon Sep 17 00:00:00 2001 From: Scott Smedley Date: Fri, 18 Dec 2009 10:54:26 -0800 Subject: [PATCH] Staging: add dt3155 driver This is a driver for the DT3155 Digitizer Signed-off-by: Scott Smedley Signed-off-by: Greg Kroah-Hartman --- drivers/staging/dt3155/Makefile | 27 + drivers/staging/dt3155/allocator.README | 98 +++ drivers/staging/dt3155/allocator.c | 296 ++++++++ drivers/staging/dt3155/allocator.h | 28 + drivers/staging/dt3155/dt3155.h | 175 +++++ drivers/staging/dt3155/dt3155.sysvinit | 60 ++ drivers/staging/dt3155/dt3155_drv.c | 1203 +++++++++++++++++++++++++++++++ drivers/staging/dt3155/dt3155_drv.h | 50 ++ drivers/staging/dt3155/dt3155_io.c | 236 ++++++ drivers/staging/dt3155/dt3155_io.h | 400 ++++++++++ drivers/staging/dt3155/dt3155_isr.c | 545 ++++++++++++++ drivers/staging/dt3155/dt3155_isr.h | 77 ++ drivers/staging/dt3155/pci-compat.h | 97 +++ drivers/staging/dt3155/sysdep.h | 820 +++++++++++++++++++++ 14 files changed, 4112 insertions(+) create mode 100644 drivers/staging/dt3155/Makefile create mode 100644 drivers/staging/dt3155/allocator.README create mode 100644 drivers/staging/dt3155/allocator.c create mode 100644 drivers/staging/dt3155/allocator.h create mode 100644 drivers/staging/dt3155/dt3155.h create mode 100644 drivers/staging/dt3155/dt3155.sysvinit create mode 100644 drivers/staging/dt3155/dt3155_drv.c create mode 100644 drivers/staging/dt3155/dt3155_drv.h create mode 100644 drivers/staging/dt3155/dt3155_io.c create mode 100644 drivers/staging/dt3155/dt3155_io.h create mode 100644 drivers/staging/dt3155/dt3155_isr.c create mode 100644 drivers/staging/dt3155/dt3155_isr.h create mode 100644 drivers/staging/dt3155/pci-compat.h create mode 100644 drivers/staging/dt3155/sysdep.h diff --git a/drivers/staging/dt3155/Makefile b/drivers/staging/dt3155/Makefile new file mode 100644 index 0000000..1717ffb --- /dev/null +++ b/drivers/staging/dt3155/Makefile @@ -0,0 +1,27 @@ + +ifeq ($(shell [[ `uname -r | cut -f 1,2 -d\.` < 2.6 ]] && echo pre2.6),pre2.6) +# system with a pre 2.6 kernel _don't_ use kbuild. +all: + $(MAKE) -f Makefile.pre-2.6 + +clean: + rm -f *.o + +else +# systems with a 2.6 or later kernel use kbuild. +ifneq ($(KERNELRELEASE),) +obj-m := dt3155.o +dt3155-objs := dt3155_drv.o dt3155_isr.o dt3155_io.o allocator.o + +else +KDIR := /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +all: + $(MAKE) -C $(KDIR) M=$(PWD) modules + +clean: + rm -rf *.o *.mod *.mod.c *.ko .dt3155* .allocator.o.cmd .tmp_versions + +endif +endif diff --git a/drivers/staging/dt3155/allocator.README b/drivers/staging/dt3155/allocator.README new file mode 100644 index 0000000..05700b6 --- /dev/null +++ b/drivers/staging/dt3155/allocator.README @@ -0,0 +1,98 @@ + +The allocator shown here exploits high memory. This document explains +how a user can deal with drivers uses this allocator and how a +programmer can link in the module. + +The module is being used by my pxc and pxdrv device drivers (as well as +other ones), available from ftp.systemy.it/pub/develop and +ftp.linux.it/pub/People/Rubini + + User's manual + ============= + + +One of the most compelling problems with any DMA-capable device is the +allocation of a suitable memory buffer. The "allocator" module tries +to deal with the problem in a clean way. The module is able to use +high memory (above the one used in normal operation) for DMA +allocation. + +To prevent the kernel for using high memory, so that it remains +available for DMA, you should pass a command line argument to the +kernel. Command line arguments can be passed to Lilo, to Loadlin or +to whichever loader you are using (unless it's very poor in design). +For Lilo, either use "append=" in /etc/lilo.conf or add commandline +arguments to the interactive prompt. For example, I have a 32MB box +and reserve two megs for DMA: + +In lilo.conf: + image = /zImage + label = linux + append = "mem=30M" + +Or, interactively: + LILO: linux mem=30M + +Once the kernel is booted with the right command-line argument, any +driver linked with the allocator module will be able to get +DMA-capable memory without much trouble (unless the various drivers +need more memory than available). + +The module implements an alloc/free mechanism, so that it can serve +multiple drivers at the same time. Note however that the allocator +uses all of high memory and assumes to be the only piece of software +using such memory. + + + Programmer's manual + =================== + +The allocator, as released, is designed to be linked to a device +driver. In this case, the driver must call allocator_init() before +using the allocator and must call allocator_cleanup() before +unloading. This is usually done from within init_module() and +cleanup_module(). If the allocator is linked to a driver, it won't be +possible for several drivers to allocate high DMA memory, as explained +above. + +It is possible, on the other hand, to compile the module as a standalone +module, so that several modules can rely on the allocator for they DMA +buffers. To compile the allocator as a standalone module, do the +following in this directory (or provide a suitable Makefile, or edit +the source code): + + make allocator.o CC="gcc -Dallocator_init=init_module -Dallocator_cleanup=cleanup_module -include /usr/include/linux/module.h" + +The previous commandline tells to include in the +first place, and to rename the init and cleanup function to the ones +needed for module loading and unloading. Drivers using a standalone +allocator won't need to call allocator_init() nor allocator_cleanup(). + +The allocator exports the following functions (declared in allocator.h): + + unsigned long allocator_allocate_dma (unsigned long kilobytes, + int priority); + + This function returns a physical address, over high_memory, + which corresponds to an area of at least "kilobytes" kilobytes. + The area will be owned by the module calling the function. + The returned address can be passed to device boards, to instruct + their DMA controllers, via phys_to_bus(). The address can be used + by C code after vremap()/ioremap(). The "priority" argument should + be GFP_KERNEL or GFP_ATOMIC, according to the context of the + caller; it is used to call kmalloc(), as the allocator must keep + track of any region it gives away. In case of error the function + returns 0, and the caller is expected to issue a -ENOMEM error. + + + void allocator_free_dma (unsigned long address); + + This function is the reverse of the previous one. If a driver + doesn't free the DMA memory it allocated, the allocator will + consider such memory as busy. Note, however, that + allocator_cleanup() calls kfree() on every region it reclaimed, + so that a driver with the allocator linked in can avoid calling + allocator_free_dma() at unload time. + + + diff --git a/drivers/staging/dt3155/allocator.c b/drivers/staging/dt3155/allocator.c new file mode 100644 index 0000000..90429db --- /dev/null +++ b/drivers/staging/dt3155/allocator.c @@ -0,0 +1,296 @@ +/* + * allocator.c -- allocate after high_memory, if available + * + * NOTE: this is different from my previous allocator, the one that + * assembles pages, which revealed itself both slow and unreliable. + * + * Copyright (C) 1998 rubini@linux.it (Alessandro Rubini) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + +-- Changes -- + + Date Programmer Description of changes made + ------------------------------------------------------------------- + 02-Aug-2002 NJC allocator now steps in 1MB increments, rather + than doubling its size each time. + Also, allocator_init(u_int *) now returns + (in the first arg) the size of the free + space. This is no longer consistent with + using the allocator as a module, and some changes + may be necessary for that purpose. This was + designed to work with the DT3155 driver, in + stand alone mode only!!! + 26-Oct-2009 SS Port to 2.6.30 kernel. + */ + + +#ifndef __KERNEL__ +# define __KERNEL__ +#endif +#ifndef MODULE +# define MODULE +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include /* PAGE_ALIGN() */ + +#include + +#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>20); + break; + } + if (!allocator_probe) break; + } + PDEBUG("%li megs (%li k, %li b)\n",i>>20,i>>10,i); + allocator_buffer_size = i>>10; /* kilobytes */ + allocator_buffer = __pa(high_memory); + if (!allocator_buffer_size) { + printk(KERN_WARNING ALL_MSG "no free high memory to use\n"); + return -ENOMEM; + } + + /* + * to simplify things, always have two cells in the list: + * the first and the last. This avoids some conditionals and + * extra code when allocating and deallocating: we only play + * in the middle of the list + */ + head = kmalloc(sizeof(struct allocator_struct),GFP_KERNEL); + if (!head) + return -ENOMEM; + tail = kmalloc(sizeof(struct allocator_struct),GFP_KERNEL); + if (!tail) { + kfree(head); + return -ENOMEM; + } + + max = allocator_buffer_size<<10; + + head->size = tail->size = 0; + head->address = allocator_buffer; + tail->address = allocator_buffer + max; + head->next = tail; + tail->next = NULL; + allocator_list = head; + + *allocator_max = allocator_buffer_size; /* Back to the user code, in KB */ + + return 0; /* ok, ready */ +} + +void allocator_cleanup(void) +{ + struct allocator_struct *ptr, *next; + + for (ptr = allocator_list; ptr; ptr = next) { + next = ptr->next; + PDEBUG("freeing list: 0x%08lx\n",ptr->address); + kfree(ptr); + } + + allocator_buffer = 0; + allocator_buffer_size = 0; + allocator_list = NULL; +} + + diff --git a/drivers/staging/dt3155/allocator.h b/drivers/staging/dt3155/allocator.h new file mode 100644 index 0000000..ecc8680 --- /dev/null +++ b/drivers/staging/dt3155/allocator.h @@ -0,0 +1,28 @@ +/* + * allocator.h -- prototypes for allocating high memory + * + * NOTE: this is different from my previous allocator, the one that + * assembles pages, which revealed itself both slow and unreliable. + * + * Copyright (C) 1998 rubini@linux.it (Alessandro Rubini) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +void allocator_free_dma(unsigned long address); +unsigned long allocator_allocate_dma (unsigned long kilobytes, int priority); +int allocator_init(u_long *); +void allocator_cleanup(void); diff --git a/drivers/staging/dt3155/dt3155.h b/drivers/staging/dt3155/dt3155.h new file mode 100644 index 0000000..2e15815 --- /dev/null +++ b/drivers/staging/dt3155/dt3155.h @@ -0,0 +1,175 @@ +/* + +Copyright 1996,2002,2005 Gregory D. Hager, Alfred A. Rizzi, Noah J. Cowan, + Jason Lapenta, Scott Smedley + +This file is part of the DT3155 Device Driver. + +The DT3155 Device Driver is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The DT3155 Device Driver is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty +of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the DT3155 Device Driver; if not, write to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, +MA 02111-1307 USA + + $Id: dt3155.h,v 1.11 2005/08/09 06:08:51 ssmedley Exp $ + +-- Changes -- + + Date Programmer Description of changes made + ------------------------------------------------------------------- + 03-Jul-2000 JML n/a + 10-Oct-2001 SS port to 2.4 kernel. + 24-Jul-2002 SS remove unused code & added GPL licence. + 05-Aug-2005 SS port to 2.6 kernel; make CCIR mode default. + +*/ + +#ifndef _DT3155_INC +#define _DT3155_INC + +#ifdef __KERNEL__ +#include /* u_int etc. */ +#include /* struct timeval */ +#else +#include +#include +#include +#include +#endif + + +#define TRUE 1 +#define FALSE 0 + +/* Uncomment this for 50Hz CCIR */ +#define CCIR 1 + +/* Can be 1 or 2 */ +#define MAXBOARDS 1 + +#define BOARD_MAX_BUFFS 3 +#define MAXBUFFERS BOARD_MAX_BUFFS*MAXBOARDS + +#define PCI_PAGE_SIZE (1 << 12) + +#ifdef CCIR +#define DT3155_MAX_ROWS 576 +#define DT3155_MAX_COLS 768 +#define FORMAT50HZ TRUE +#else +#define DT3155_MAX_ROWS 480 +#define DT3155_MAX_COLS 640 +#define FORMAT50HZ FALSE +#endif + +/* Configuration structure */ +struct dt3155_config_s { + u_int acq_mode; + u_int cols, rows; + u_int continuous; +}; + + +/* hold data for each frame */ +typedef struct +{ + u_long addr; /* address of the buffer with the frame */ + u_long tag; /* unique number for the frame */ + struct timeval time; /* time that capture took place */ +} frame_info_t; + +/* Structure for interrupt and buffer handling. */ +/* This is the setup for 1 card */ +struct dt3155_fbuffer_s { + int nbuffers; + + frame_info_t frame_info[ BOARD_MAX_BUFFS ]; + + int empty_buffers[ BOARD_MAX_BUFFS ]; /* indexes empty frames */ + int empty_len; /* Number of empty buffers */ + /* Zero means empty */ + + int active_buf; /* Where data is currently dma'ing */ + int locked_buf; /* Buffers used by user */ + + int ready_que[ BOARD_MAX_BUFFS ]; + u_long ready_head; /* The most recent buffer located here */ + u_long ready_len; /* The number of ready buffers */ + + int even_happened; + int even_stopped; + + int stop_acquire; /* Flag to stop interrupts */ + u_long frame_count; /* Counter for frames acquired by this card */ + +}; + + + +#define DT3155_MODE_FRAME 1 +#define DT3155_MODE_FIELD 2 + +#define DT3155_SNAP 1 +#define DT3155_ACQ 2 + +/* There is one status structure for each card. */ +typedef struct dt3155_status_s +{ + int fixed_mode; /* if 1, we are in fixed frame mode */ + u_long reg_addr; /* Register address for a single card */ + u_long mem_addr; /* Buffer start addr for this card */ + u_long mem_size; /* This is the amount of mem available */ + u_int irq; /* this card's irq */ + struct dt3155_config_s config; /* configuration struct */ + struct dt3155_fbuffer_s fbuffer;/* frame buffer state struct */ + u_long state; /* this card's state */ + u_int device_installed; /* Flag if installed. 1=installed */ +} dt3155_status_t; + +/* Reference to global status structure */ +extern struct dt3155_status_s dt3155_status[MAXBOARDS]; + +#define DT3155_STATE_IDLE 0x00 +#define DT3155_STATE_FRAME 0x01 +#define DT3155_STATE_FLD 0x02 +#define DT3155_STATE_STOP 0x100 +#define DT3155_STATE_ERROR 0x200 +#define DT3155_STATE_MODE 0x0ff + +#define DT3155_IOC_MAGIC '!' + +#define DT3155_SET_CONFIG _IOW( DT3155_IOC_MAGIC, 1, struct dt3155_config_s ) +#define DT3155_GET_CONFIG _IOR( DT3155_IOC_MAGIC, 2, struct dt3155_status_s ) +#define DT3155_STOP _IO( DT3155_IOC_MAGIC, 3 ) +#define DT3155_START _IO( DT3155_IOC_MAGIC, 4 ) +#define DT3155_FLUSH _IO( DT3155_IOC_MAGIC, 5 ) +#define DT3155_IOC_MAXNR 5 + +/* Error codes */ + +#define DT_ERR_NO_BUFFERS 0x10000 /* not used but it might be one day - SS */ +#define DT_ERR_CORRUPT 0x20000 +#define DT_ERR_OVERRUN 0x30000 +#define DT_ERR_I2C_TIMEOUT 0x40000 +#define DT_ERR_MASK 0xff0000/* not used but it might be one day - SS */ + +/* User code will probably want to declare one of these for each card */ +typedef struct dt3155_read_s +{ + u_long offset; + u_long frame_seq; + u_long state; + + frame_info_t frame_info; +} dt3155_read_t; + +#endif /* _DT3155_inc */ diff --git a/drivers/staging/dt3155/dt3155.sysvinit b/drivers/staging/dt3155/dt3155.sysvinit new file mode 100644 index 0000000..92ec093 --- /dev/null +++ b/drivers/staging/dt3155/dt3155.sysvinit @@ -0,0 +1,60 @@ +#! /bin/sh +# +# Module load/unload script for use with SysV-style /etc/init.d/ systems. +# On a Debian system, copy this to /etc/init.d/dt3155 and then run +# /usr/sbin/update-rc.d dt3155 defaults 55 +# to create the appropriate /etc/rc?.d/[SK]55dt3155 start/stop links. +# (The "55" is arbitrary but is what I use to load this rather late.) +# +# Andy Dougherty Feb 22 2000 doughera@lafayette.edu +# Dept. of Physics +# Lafayette College, Easton PA 18042 +# + +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + +# Edit to point to your local copy. +FILE=/usr/local/lib/modules/dt3155/dt3155.o +NAME="dt3155" +DESC="dt3155 Frame Grabber module" +DEV="dt3155" + +if test ! -f $FILE; then + echo "Unable to locate $FILE" + exit 0 +fi + +set -e + +case "$1" in + start) + echo -n "Loading $DESC " + if /sbin/insmod -v -f $FILE; then + major=`grep $DEV /proc/devices | awk "{print \\$1}"` + rm -f /dev/dt3155? + mknod /dev/dt3155a c $major 0 + mknod /dev/dt3155b c $major 1 + chmod go+rw /dev/dt3155? + echo + else + echo "$FILE not loaded." + fi + ;; + stop) + echo -n "Unloading $DESC: " + if /sbin/rmmod $NAME ; then + echo + else + echo "$DEV not removed" + exit 0 + fi + rm -f /dev/dt3155? + ;; + *) + echo "Usage: /etc/init.d/$NAME {start|stop}" + exit 1 + ;; +esac + +exit 0 + diff --git a/drivers/staging/dt3155/dt3155_drv.c b/drivers/staging/dt3155/dt3155_drv.c new file mode 100644 index 0000000..f3ae8e8 --- /dev/null +++ b/drivers/staging/dt3155/dt3155_drv.c @@ -0,0 +1,1203 @@ +/* + +Copyright 1996,2002,2005 Gregory D. Hager, Alfred A. Rizzi, Noah J. Cowan, + Jason Lapenta, Scott Smedley, Greg Sharp + +This file is part of the DT3155 Device Driver. + +The DT3155 Device Driver is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The DT3155 Device Driver is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty +of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the DT3155 Device Driver; if not, write to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, +MA 02111-1307 USA + +-- Changes -- + + Date Programmer Description of changes made + ------------------------------------------------------------------- + 03-Jul-2000 JML n/a + 10-Oct-2001 SS port to 2.4 kernel + 02-Apr-2002 SS Mods to use allocator as a standalone module; + Merged John Roll's changes (john@cfa.harvard.edu) + to make work with multiple boards. + 02-Jul-2002 SS Merged James Rose's chages (rosejr@purdue.edu) to: + * fix successive interrupt-driven captures + * add select/poll support. + 10-Jul-2002 GCS Add error check when ndevices > MAXBOARDS. + 02-Aug-2002 GCS Fix field mode so that odd (lower) field is stored + in lower half of buffer. + 05-Aug-2005 SS port to 2.6 kernel. + 26-Oct-2009 SS port to 2.6.30 kernel. + +-- Notes -- + +** appended "mem=124" in lilo.conf to allow for 4megs free on my 128meg system. + * using allocator.c and allocator.h from o'reilly book (alessandro rubini) + ftp://ftp.systemy.it/pub/develop (see README.allocator) + + + might want to get rid of MAXboards for allocating initial buffer. + confusing and not necessary + + + in cleanup_module the MOD_IN_USE looks like it is check after it should + + * GFP_DMA should not be set with a PCI system (pg 291) + + - NJC why are only two buffers allowed? (see isr, approx line 358) + +*/ + +extern void printques(int); + +#ifdef MODULE +#include +#include +#include + + +#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 +#include +#include + +#include +#include + +#include "dt3155.h" +#include "dt3155_drv.h" +#include "dt3155_isr.h" +#include "dt3155_io.h" +#include "allocator.h" + +/* Error variable. Zero means no error. */ +int dt3155_errno = 0; + +#ifndef PCI_DEVICE_ID_INTEL_7116 +#define PCI_DEVICE_ID_INTEL_7116 0x1223 +#endif + +#define DT3155_VENDORID PCI_VENDOR_ID_INTEL +#define DT3155_DEVICEID PCI_DEVICE_ID_INTEL_7116 +#define MAXPCI 16 + +#ifdef DT_DEBUG +#define DT_3155_DEBUG_MSG(x,y) printk(x,y) +#else +#define DT_3155_DEBUG_MSG(x,y) +#endif + +/* wait queue for interrupts */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1) +wait_queue_head_t dt3155_read_wait_queue[ MAXBOARDS ]; +#else +struct wait_queue *dt3155_read_wait_queue[ MAXBOARDS ]; +#endif + +#define DT_3155_SUCCESS 0 +#define DT_3155_FAILURE -EIO + +/* set to dynamicaly allocate, but it is tunable: */ +/* insmod DT_3155 dt3155 dt3155_major=XX */ +int dt3155_major = 0; + +/* The minor numbers are 0 and 1 ... they are not tunable. + * They are used as the indices for the structure vectors, + * and register address vectors + */ + +/* Global structures and variables */ + +/* Status of each device */ +struct dt3155_status_s dt3155_status[ MAXBOARDS ]; + +/* kernel logical address of the board */ +u_char *dt3155_lbase[ MAXBOARDS ] = { NULL +#if MAXBOARDS == 2 + , NULL +#endif +}; +/* DT3155 registers */ +u_char *dt3155_bbase = NULL; /* kernel logical address of the * + * buffer region */ +u_int dt3155_dev_open[ MAXBOARDS ] = {0 +#if MAXBOARDS == 2 + , 0 +#endif +}; + +u_int ndevices = 0; +u_long unique_tag = 0;; + + +/* + * Stops interrupt generation right away and resets the status + * to idle. I don't know why this works and the other way doesn't. + * (James Rose) + */ +static void quick_stop (int minor) +{ + // TODO: scott was here +#if 1 + ReadMReg((dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg); + /* disable interrupts */ + int_csr_r.fld.FLD_END_EVE_EN = 0; + int_csr_r.fld.FLD_END_ODD_EN = 0; + WriteMReg((dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg ); + + dt3155_status[ minor ].state &= ~(DT3155_STATE_STOP|0xff); + /* mark the system stopped: */ + dt3155_status[ minor ].state |= DT3155_STATE_IDLE; + dt3155_fbuffer[ minor ]->stop_acquire = 0; + dt3155_fbuffer[ minor ]->even_stopped = 0; +#else + dt3155_status[minor].state |= DT3155_STATE_STOP; + dt3155_status[minor].fbuffer.stop_acquire = 1; +#endif + +} + + +/***************************************************** + * dt3155_isr() Interrupt service routien + * + * - looks like this isr supports IRQ sharing (or could) JML + * - Assumes irq's are disabled, via SA_INTERRUPT flag + * being set in request_irq() call from init_module() + *****************************************************/ +static inline void dt3155_isr( int irq, void *dev_id, struct pt_regs *regs ) +{ + int minor = -1; + int index; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + unsigned long flags; +#else + int flags; +#endif + u_long buffer_addr; + + /* find out who issued the interrupt */ + for ( index = 0; index < ndevices; index++ ) { + if( dev_id == (void*) &dt3155_status[ index ]) + { + minor = index; + break; + } + } + + /* hopefully we should not get here */ + if ( minor < 0 || minor >= MAXBOARDS ) { + printk(KERN_ERR "dt3155_isr called with invalid dev_id\n"); + return; + } + + /* Check for corruption and set a flag if so */ + ReadMReg( (dt3155_lbase[ minor ] + CSR1), csr1_r.reg ); + + if ( (csr1_r.fld.FLD_CRPT_EVE) || (csr1_r.fld.FLD_CRPT_ODD) ) + { + /* TODO: this should probably stop acquisition */ + /* and set some flags so that dt3155_read */ + /* returns an error next time it is called */ + dt3155_errno = DT_ERR_CORRUPT; + printk("dt3155: corrupt field\n"); + return; + } + + ReadMReg((dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg); + + /* Handle the even field ... */ + if (int_csr_r.fld.FLD_END_EVE) + { + if ( (dt3155_status[ minor ].state & DT3155_STATE_MODE) == + DT3155_STATE_FLD ) + { + dt3155_fbuffer[ minor ]->frame_count++; + } + + ReadI2C(dt3155_lbase[ minor ], EVEN_CSR, &i2c_even_csr.reg); + + /* Clear the interrupt? */ + int_csr_r.fld.FLD_END_EVE = 1; + + /* disable the interrupt if last field */ + if (dt3155_fbuffer[ minor ]->stop_acquire) + { + printk("dt3155: even stopped.\n"); + dt3155_fbuffer[ minor ]->even_stopped = 1; + if (i2c_even_csr.fld.SNGL_EVE) + { + int_csr_r.fld.FLD_END_EVE_EN = 0; + } + else + { + i2c_even_csr.fld.SNGL_EVE = 1; + } + } + + WriteMReg( (dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg ); + + /* Set up next DMA if we are doing FIELDS */ + if ( (dt3155_status[ minor ].state & DT3155_STATE_MODE ) == + DT3155_STATE_FLD) + { + /* GCS (Aug 2, 2002) -- In field mode, dma the odd field + into the lower half of the buffer */ + const u_long stride = dt3155_status[ minor ].config.cols; + buffer_addr = dt3155_fbuffer[ minor ]-> + frame_info[ dt3155_fbuffer[ minor ]->active_buf ].addr + + (DT3155_MAX_ROWS / 2) * stride; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + local_save_flags(flags); + local_irq_disable(); +#else + save_flags( flags ); + cli(); +#endif + wake_up_interruptible( &dt3155_read_wait_queue[ minor ] ); + + /* Set up the DMA address for the next field */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + local_irq_restore(flags); +#else + restore_flags( flags ); +#endif + WriteMReg((dt3155_lbase[ minor ] + ODD_DMA_START), buffer_addr); + } + + /* Check for errors. */ + i2c_even_csr.fld.DONE_EVE = 1; + if ( i2c_even_csr.fld.ERROR_EVE ) + dt3155_errno = DT_ERR_OVERRUN; + + WriteI2C( dt3155_lbase[ minor ], EVEN_CSR, i2c_even_csr.reg ); + + /* Note that we actually saw an even field meaning */ + /* that subsequent odd field complete the frame */ + dt3155_fbuffer[ minor ]->even_happened = 1; + + /* recording the time that the even field finished, this should be */ + /* about time in the middle of the frame */ + do_gettimeofday( &(dt3155_fbuffer[ minor ]-> + frame_info[ dt3155_fbuffer[ minor ]-> + active_buf ].time) ); + return; + } + + /* ... now handle the odd field */ + if ( int_csr_r.fld.FLD_END_ODD ) + { + ReadI2C( dt3155_lbase[ minor ], ODD_CSR, &i2c_odd_csr.reg ); + + /* Clear the interrupt? */ + int_csr_r.fld.FLD_END_ODD = 1; + + if (dt3155_fbuffer[ minor ]->even_happened || + (dt3155_status[ minor ].state & DT3155_STATE_MODE) == + DT3155_STATE_FLD) + { + dt3155_fbuffer[ minor ]->frame_count++; + } + + if ( dt3155_fbuffer[ minor ]->stop_acquire && + dt3155_fbuffer[ minor ]->even_stopped ) + { + printk(KERN_DEBUG "dt3155: stopping odd..\n"); + if ( i2c_odd_csr.fld.SNGL_ODD ) + { + /* disable interrupts */ + int_csr_r.fld.FLD_END_ODD_EN = 0; + dt3155_status[ minor ].state &= ~(DT3155_STATE_STOP|0xff); + + /* mark the system stopped: */ + dt3155_status[ minor ].state |= DT3155_STATE_IDLE; + dt3155_fbuffer[ minor ]->stop_acquire = 0; + dt3155_fbuffer[ minor ]->even_stopped = 0; + + printk(KERN_DEBUG "dt3155: state is now %lx\n", + dt3155_status[minor].state); + } + else + { + i2c_odd_csr.fld.SNGL_ODD = 1; + } + } + + WriteMReg( (dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg ); + + /* if the odd field has been acquired, then */ + /* change the next dma location for both fields */ + /* and wake up the process if sleeping */ + if ( dt3155_fbuffer[ minor ]->even_happened || + (dt3155_status[ minor ].state & DT3155_STATE_MODE) == + DT3155_STATE_FLD ) + { + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + local_save_flags(flags); + local_irq_disable(); +#else + save_flags( flags ); + cli(); +#endif + +#ifdef DEBUG_QUES_B + printques( minor ); +#endif + if ( dt3155_fbuffer[ minor ]->nbuffers > 2 ) + { + if ( !are_empty_buffers( minor ) ) + { + /* The number of active + locked buffers is + * at most 2, and since there are none empty, there + * must be at least nbuffers-2 ready buffers. + * This is where we 'drop frames', oldest first. */ + push_empty( pop_ready( minor ), minor ); + } + + /* The ready_que can't be full, since we know + * there is one active buffer right now, so it's safe + * to push the active buf on the ready_que. */ + push_ready( minor, dt3155_fbuffer[ minor ]->active_buf ); + /* There's at least 1 empty -- make it active */ + dt3155_fbuffer[ minor ]->active_buf = pop_empty( minor ); + dt3155_fbuffer[ minor ]-> + frame_info[ dt3155_fbuffer[ minor ]-> + active_buf ].tag = ++unique_tag; + } + else /* nbuffers == 2, special case */ + { /* There is 1 active buffer. + * If there is a locked buffer, keep the active buffer + * the same -- that means we drop a frame. + */ + if ( dt3155_fbuffer[ minor ]->locked_buf < 0 ) + { + push_ready( minor, + dt3155_fbuffer[ minor ]->active_buf ); + if (are_empty_buffers( minor ) ) + { + dt3155_fbuffer[ minor ]->active_buf = + pop_empty( minor ); + } + else + { /* no empty or locked buffers, so use a readybuf */ + dt3155_fbuffer[ minor ]->active_buf = + pop_ready( minor ); + } + } + } + +#ifdef DEBUG_QUES_B + printques( minor ); +#endif + + dt3155_fbuffer[ minor ]->even_happened = 0; + + wake_up_interruptible( &dt3155_read_wait_queue[ minor ] ); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + local_irq_restore(flags); +#else + restore_flags( flags ); +#endif + } + + + /* Set up the DMA address for the next frame/field */ + buffer_addr = dt3155_fbuffer[ minor ]-> + frame_info[ dt3155_fbuffer[ minor ]->active_buf ].addr; + if ( (dt3155_status[ minor ].state & DT3155_STATE_MODE) == + DT3155_STATE_FLD ) + { + WriteMReg((dt3155_lbase[ minor ] + EVEN_DMA_START), buffer_addr); + } + else + { + WriteMReg((dt3155_lbase[ minor ] + EVEN_DMA_START), buffer_addr); + + WriteMReg((dt3155_lbase[ minor ] + ODD_DMA_START), buffer_addr + + dt3155_status[ minor ].config.cols); + } + + /* Do error checking */ + i2c_odd_csr.fld.DONE_ODD = 1; + if ( i2c_odd_csr.fld.ERROR_ODD ) + dt3155_errno = DT_ERR_OVERRUN; + + WriteI2C(dt3155_lbase[ minor ], ODD_CSR, i2c_odd_csr.reg ); + + return; + } + /* If we get here, the Odd Field wasn't it either... */ + printk( "neither even nor odd. shared perhaps?\n"); +} + +/***************************************************** + * init_isr(int minor) + * turns on interupt generation for the card + * designated by "minor". + * It is called *only* from inside ioctl(). + *****************************************************/ +static void dt3155_init_isr(int minor) +{ + const u_long stride = dt3155_status[ minor ].config.cols; + + switch (dt3155_status[ minor ].state & DT3155_STATE_MODE) + { + case DT3155_STATE_FLD: + { + even_dma_start_r = dt3155_status[ minor ]. + fbuffer.frame_info[ dt3155_status[ minor ].fbuffer.active_buf ].addr; + even_dma_stride_r = 0; + odd_dma_stride_r = 0; + + WriteMReg((dt3155_lbase[ minor ] + EVEN_DMA_START), + even_dma_start_r); + WriteMReg((dt3155_lbase[ minor ] + EVEN_DMA_STRIDE), + even_dma_stride_r); + WriteMReg((dt3155_lbase[ minor ] + ODD_DMA_STRIDE), + odd_dma_stride_r); + break; + } + + case DT3155_STATE_FRAME: + default: + { + even_dma_start_r = dt3155_status[ minor ]. + fbuffer.frame_info[ dt3155_status[ minor ].fbuffer.active_buf ].addr; + odd_dma_start_r = even_dma_start_r + stride; + even_dma_stride_r = stride; + odd_dma_stride_r = stride; + + WriteMReg((dt3155_lbase[ minor ] + EVEN_DMA_START), + even_dma_start_r); + WriteMReg((dt3155_lbase[ minor ] + ODD_DMA_START), + odd_dma_start_r); + WriteMReg((dt3155_lbase[ minor ] + EVEN_DMA_STRIDE), + even_dma_stride_r); + WriteMReg((dt3155_lbase[ minor ] + ODD_DMA_STRIDE), + odd_dma_stride_r); + break; + } + } + + /* 50/60 Hz should be set before this point but let's make sure it is */ + /* right anyway */ + + ReadI2C(dt3155_lbase[ minor ], CONFIG, &i2c_csr2.reg); + i2c_csr2.fld.HZ50 = FORMAT50HZ; + WriteI2C(dt3155_lbase[ minor ], CONFIG, i2c_config.reg); + + /* enable busmaster chip, clear flags */ + + /* + * TODO: + * shouldn't we be concered with continuous values of + * DT3155_SNAP & DT3155_ACQ here? (SS) + */ + + csr1_r.reg = 0; + csr1_r.fld.CAP_CONT_EVE = 1; /* use continuous capture bits to */ + csr1_r.fld.CAP_CONT_ODD = 1; /* enable */ + csr1_r.fld.FLD_DN_EVE = 1; /* writing a 1 clears flags */ + csr1_r.fld.FLD_DN_ODD = 1; + csr1_r.fld.SRST = 1; /* reset - must be 1 */ + csr1_r.fld.FIFO_EN = 1; /* fifo control - must be 1 */ + csr1_r.fld.FLD_CRPT_EVE = 1; /* writing a 1 clears flags */ + csr1_r.fld.FLD_CRPT_ODD = 1; + + WriteMReg((dt3155_lbase[ minor ] + CSR1),csr1_r.reg); + + /* Enable interrupts at the end of each field */ + + int_csr_r.reg = 0; + int_csr_r.fld.FLD_END_EVE_EN = 1; + int_csr_r.fld.FLD_END_ODD_EN = 1; + int_csr_r.fld.FLD_START_EN = 0; + + WriteMReg((dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg); + + /* start internal BUSY bits */ + + ReadI2C(dt3155_lbase[ minor ], CSR2, &i2c_csr2.reg); + i2c_csr2.fld.BUSY_ODD = 1; + i2c_csr2.fld.BUSY_EVE = 1; + WriteI2C(dt3155_lbase[ minor ], CSR2, i2c_csr2.reg); + + /* Now its up to the interrupt routine!! */ + + return; +} + + +/***************************************************** + * ioctl() + * + *****************************************************/ +static int dt3155_ioctl ( + struct inode *inode, + struct file *file, + u_int cmd, + u_long arg) +{ + int minor = MINOR(inode->i_rdev); /* What device are we ioctl()'ing? */ + + if ( minor >= MAXBOARDS || minor < 0 ) + return -ENODEV; + + /* make sure it is valid command */ + if (_IOC_NR(cmd) > DT3155_IOC_MAXNR) + { + printk("DT3155: invalid IOCTL(0x%x)\n",cmd); + printk("DT3155: Valid commands (0x%x), (0x%x), (0x%x), (0x%x), (0x%x)\n", + DT3155_GET_CONFIG, DT3155_SET_CONFIG, + DT3155_START, DT3155_STOP, DT3155_FLUSH); + return -EINVAL; + } + + switch (cmd) + { + case DT3155_SET_CONFIG: + { + if (dt3155_status[minor].state != DT3155_STATE_IDLE) + return -EBUSY; + + { + struct dt3155_config_s tmp; + if (copy_from_user((void *)&tmp, (void *) arg, sizeof(tmp))) + return -EFAULT; + /* check for valid settings */ + if (tmp.rows > DT3155_MAX_ROWS || + tmp.cols > DT3155_MAX_COLS || + (tmp.acq_mode != DT3155_MODE_FRAME && + tmp.acq_mode != DT3155_MODE_FIELD) || + (tmp.continuous != DT3155_SNAP && + tmp.continuous != DT3155_ACQ)) + { + return -EINVAL; + } + dt3155_status[minor].config = tmp; + } + return 0; + } + case DT3155_GET_CONFIG: + { + if (copy_to_user((void *) arg, (void *) &dt3155_status[minor], + sizeof(dt3155_status_t) )) + return -EFAULT; + return 0; + } + case DT3155_FLUSH: /* Flushes the buffers -- ensures fresh data */ + { + if (dt3155_status[minor].state != DT3155_STATE_IDLE) + return -EBUSY; + return dt3155_flush(minor); + } + case DT3155_STOP: + { + if (dt3155_status[minor].state & DT3155_STATE_STOP || + dt3155_status[minor].fbuffer.stop_acquire) + return -EBUSY; + + if (dt3155_status[minor].state == DT3155_STATE_IDLE) + return 0; + + quick_stop(minor); + if (copy_to_user((void *) arg, (void *) &dt3155_status[minor], + sizeof(dt3155_status_t))) + return -EFAULT; + return 0; + } + case DT3155_START: + { + if (dt3155_status[minor].state != DT3155_STATE_IDLE) + return -EBUSY; + + dt3155_status[minor].fbuffer.stop_acquire = 0; + dt3155_status[minor].fbuffer.frame_count = 0; + + /* Set the MODE in the status -- we default to FRAME */ + if (dt3155_status[minor].config.acq_mode == DT3155_MODE_FIELD) + { + dt3155_status[minor].state = DT3155_STATE_FLD; + } + else + { + dt3155_status[minor].state = DT3155_STATE_FRAME; + } + + dt3155_init_isr(minor); + if (copy_to_user( (void *) arg, (void *) &dt3155_status[minor], + sizeof(dt3155_status_t))) + return -EFAULT; + return 0; + } + default: + { + printk("DT3155: invalid IOCTL(0x%x)\n",cmd); + printk("DT3155: Valid commands (0x%x), (0x%x), (0x%x), (0x%x), (0x%x)\n", + DT3155_GET_CONFIG, DT3155_SET_CONFIG, + DT3155_START, DT3155_STOP, DT3155_FLUSH); + return -ENOSYS; + } + } + return -ENOSYS; +} + +/***************************************************** + * mmap() + * + * only allow the user to mmap the registers and buffer + * It is quite possible that this is broken, since the + * addition of of the capacity for two cards!!!!!!!! + * It *looks* like it should work but since I'm not + * sure how to use it, I'm not actually sure. (NJC? ditto by SS) + *****************************************************/ +static int dt3155_mmap (struct file * file, struct vm_area_struct * vma) +{ + /* which device are we mmapping? */ + int minor = MINOR(file->f_dentry->d_inode->i_rdev); + unsigned long offset; + + /* not actually sure when vm_area_struct changed, + but it was in 2.3 sometime */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,20) + + offset = vma->vm_pgoff << PAGE_SHIFT; + + if (offset >= __pa(high_memory) || (file->f_flags & O_SYNC)) + vma->vm_flags |= VM_IO; + + /* Don't try to swap out physical pages.. */ + vma->vm_flags |= VM_RESERVED; + +#else + + if (vma->vm_offset & ~PAGE_MASK) + return -ENXIO; + + offset = vma->vm_offset; + +#endif + + /* they are mapping the registers or the buffer */ + if ((offset == dt3155_status[minor].reg_addr && + vma->vm_end - vma->vm_start == PCI_PAGE_SIZE) || + (offset == dt3155_status[minor].mem_addr && + vma->vm_end - vma->vm_start == dt3155_status[minor].mem_size)) + { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + if (remap_pfn_range(vma, + vma->vm_start, + offset >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) +#else + if (remap_page_range(vma->vm_start, + offset, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) +#endif + { + printk("DT3155: remap_page_range() failed.\n"); + return -EAGAIN; + } + } + else + { + printk("DT3155: dt3155_mmap() bad call.\n"); + return -ENXIO; + } + + return 0; +} + + +/***************************************************** + * open() + * + * Our special open code. + * MOD_INC_USE_COUNT make sure that the driver memory is not freed + * while the device is in use. + *****************************************************/ +static int dt3155_open( struct inode* inode, struct file* filep) +{ + int minor = MINOR(inode->i_rdev); /* what device are we opening? */ + if (dt3155_dev_open[ minor ]) { + printk ("DT3155: Already opened by another process.\n"); + return -EBUSY; + } + + if (dt3155_status[ minor ].device_installed==0) + { + printk("DT3155 Open Error: No such device dt3155 minor number %d\n", + minor); + return -EIO; + } + + if (dt3155_status[ minor ].state != DT3155_STATE_IDLE) { + printk ("DT3155: Not in idle state (state = %lx)\n", + dt3155_status[ minor ].state); + return -EBUSY; + } + + printk("DT3155: Device opened.\n"); + + dt3155_dev_open[ minor ] = 1 ; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + MOD_INC_USE_COUNT; +#endif + + dt3155_flush( minor ); + + /* Disable ALL interrupts */ + int_csr_r.reg = 0; + WriteMReg( (dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg ); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1) + init_waitqueue_head(&(dt3155_read_wait_queue[minor])); +#else + dt3155_read_wait_queue[minor] = NULL; +#endif + + return 0; +} + + +/***************************************************** + * close() + * + * Now decrement the use count. + * + *****************************************************/ +static int dt3155_close( struct inode *inode, struct file *filep) +{ + int minor; + + minor = MINOR(inode->i_rdev); /* which device are we closing */ + if (!dt3155_dev_open[ minor ]) + { + printk("DT3155: attempt to CLOSE a not OPEN device\n"); + } + else + { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + MOD_DEC_USE_COUNT; +#endif + dt3155_dev_open[ minor ] = 0; + + if (dt3155_status[ minor ].state != DT3155_STATE_IDLE) + { + quick_stop(minor); + } + } + return 0; +} + +/***************************************************** + * read() + * + *****************************************************/ +static int dt3155_read ( + struct file *filep, + char *buf, + size_t count, + loff_t *ppos) +{ + /* which device are we reading from? */ + int minor = MINOR(filep->f_dentry->d_inode->i_rdev); + u_long offset; + int frame_index; + frame_info_t *frame_info_p; + + /* TODO: this should check the error flag and */ + /* return an error on hardware failures */ + if (count != sizeof(dt3155_read_t)) + { + printk("DT3155 ERROR (NJC): count is not right\n"); + return -EINVAL; + } + + + /* Hack here -- I'm going to allow reading even when idle. + * this is so that the frames can be read after STOP has + * been called. Leaving it here, commented out, as a reminder + * for a short while to make sure there are no problems. + * Note that if the driver is not opened in non_blocking mode, + * and the device is idle, then it could sit here forever! */ + + /* if (dt3155_status[minor].state == DT3155_STATE_IDLE)*/ + /* return -EBUSY;*/ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1) + + /* non-blocking reads should return if no data */ + if (filep->f_flags & O_NDELAY) + { + if ((frame_index = dt3155_get_ready_buffer(minor)) < 0) { + /*printk( "dt3155: no buffers available (?)\n");*/ + /* printques(minor); */ + return -EAGAIN; + } + } + else + { + /* + * sleep till data arrives , or we get interrupted. + * Note that wait_event_interruptible() does not actually + * sleep/wait if it's condition evaluates to true upon entry. + */ + wait_event_interruptible(dt3155_read_wait_queue[minor], + (frame_index = dt3155_get_ready_buffer(minor)) + >= 0); + + if (frame_index < 0) + { + printk ("DT3155: read: interrupted\n"); + quick_stop (minor); + printques(minor); + return -EINTR; + } + } + +#else + while ((frame_index = dt3155_get_ready_buffer(minor)) < 0 ) + { + int index; + if (filep->f_flags & O_NDELAY) + return 0; + + /* sleep till data arrives , or we get interrupted */ + interruptible_sleep_on(&dt3155_read_wait_queue[minor]); + for (index = 0; index < _NSIG_WORDS; index++) + { + /* + * Changing the next line of code below to this: + * if (current->pending.signal.sig[index] & + * ~current->blocked.sig[index]) + * would also work on a 2.4 kernel, however, the above + * method is preferred & more portable. + */ + if (current->signal.sig[index] & ~current->blocked.sig[index]) + { + printk ("DT3155: read: interrupted\n"); + return -EINTR; + } + } + } + +#endif + + frame_info_p = &dt3155_status[minor].fbuffer.frame_info[frame_index]; + + /* make this an offset */ + offset = frame_info_p->addr - dt3155_status[minor].mem_addr; + + put_user(offset, (unsigned int *) buf); + buf += sizeof(u_long); + put_user( dt3155_status[minor].fbuffer.frame_count, (unsigned int *) buf); + buf += sizeof(u_long); + put_user(dt3155_status[minor].state, (unsigned int *) buf); + buf += sizeof(u_long); + if (copy_to_user(buf, frame_info_p, sizeof(frame_info_t))) + return -EFAULT; + + return sizeof(dt3155_read_t); +} + +static unsigned int dt3155_poll (struct file * filp, poll_table *wait) +{ + int minor = MINOR(filp->f_dentry->d_inode->i_rdev); + + if (!is_ready_buf_empty(minor)) + return POLLIN | POLLRDNORM; + + poll_wait (filp, &dt3155_read_wait_queue[minor], wait); + + return 0; +} + + +/***************************************************** + * file operations supported by DT3155 driver + * needed by init_module + * register_chrdev + *****************************************************/ +static struct file_operations dt3155_fops = { + read: dt3155_read, + ioctl: dt3155_ioctl, + mmap: dt3155_mmap, + poll: dt3155_poll, + open: dt3155_open, + release: dt3155_close +}; + + +/***************************************************** + * find_PCI(); + * + * PCI has been totally reworked in 2.1.. + *****************************************************/ +static int find_PCI (void) +{ + struct pci_dev *pci_dev = NULL; + int error, pci_index = 0; + unsigned short rev_device; + unsigned long base; + unsigned char irq; + + while ((pci_dev = pci_find_device + (DT3155_VENDORID, DT3155_DEVICEID, pci_dev)) != NULL) + { + pci_index ++; + + /* Is it really there? */ + if ((error = + pci_read_config_word(pci_dev, PCI_CLASS_DEVICE, &rev_device))) + continue; + + /* Found a board */ + DT_3155_DEBUG_MSG("DT3155: Device number %d \n", pci_index); + + /* Make sure the driver was compiled with enough buffers to handle + this many boards */ + if (pci_index > MAXBOARDS) { + printk("DT3155: ERROR - found %d devices, but driver only configured " + "for %d devices\n" + "DT3155: Please change MAXBOARDS in dt3155.h\n", + pci_index, MAXBOARDS); + return DT_3155_FAILURE; + } + + /* Now, just go out and make sure that this/these device(s) is/are + actually mapped into the kernel address space */ + if ((error = pci_read_config_dword( pci_dev, PCI_BASE_ADDRESS_0, + (u_int *) &base))) + { + printk("DT3155: Was not able to find device \n"); + return DT_3155_FAILURE; + } + + DT_3155_DEBUG_MSG("DT3155: Base address 0 for device is %lx \n", base); + dt3155_status[pci_index-1].reg_addr = base; + + /* Remap the base address to a logical address through which we + * can access it. */ + dt3155_lbase[ pci_index - 1 ] = ioremap(base,PCI_PAGE_SIZE); + dt3155_status[ pci_index - 1 ].reg_addr = base; + DT_3155_DEBUG_MSG("DT3155: New logical address is x%x \n", + (u_int)dt3155_lbase[pci_index-1]); + if ( !dt3155_lbase[pci_index-1] ) + { + printk("DT3155: Unable to remap control registers\n"); + return DT_3155_FAILURE; + } + + if ( (error = pci_read_config_byte( pci_dev, PCI_INTERRUPT_LINE, &irq)) ) + { + printk("DT3155: Was not able to find device \n"); + return DT_3155_FAILURE; + } + + DT_3155_DEBUG_MSG("DT3155: IRQ is %d \n",irq); + dt3155_status[ pci_index-1 ].irq = irq; + /* Set flag: kth device found! */ + dt3155_status[ pci_index-1 ].device_installed = 1; + printk("DT3155: Installing device %d w/irq %d and address 0x%x\n", + pci_index, + (u_int)dt3155_status[pci_index-1].irq, + (u_int)dt3155_lbase[pci_index-1]); + + } + ndevices = pci_index; + + return DT_3155_SUCCESS; +} + +u_long allocatorAddr = 0; + +/***************************************************** + * init_module() + *****************************************************/ +int init_module(void) +{ + int index; + int rcode = 0; + char *devname[ MAXBOARDS ]; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,1) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + SET_MODULE_OWNER(&dt3155_fops); +#endif +#endif + + devname[ 0 ] = "dt3155a"; +#if MAXBOARDS == 2 + devname[ 1 ] = "dt3155b"; +#endif + + printk("DT3155: Loading module...\n"); + + /* Register the device driver */ + rcode = register_chrdev( dt3155_major, "dt3155", &dt3155_fops ); + if( rcode < 0 ) + { + printk( KERN_INFO "DT3155: register_chrdev failed \n"); + return rcode; + } + + if( dt3155_major == 0 ) + dt3155_major = rcode; /* dynamic */ + + + /* init the status variables. */ + /* DMA memory is taken care of in setup_buffers() */ + for ( index = 0; index < MAXBOARDS; index++ ) + { + dt3155_status[ index ].config.acq_mode = DT3155_MODE_FRAME; + dt3155_status[ index ].config.continuous = DT3155_ACQ; + dt3155_status[ index ].config.cols = DT3155_MAX_COLS; + dt3155_status[ index ].config.rows = DT3155_MAX_ROWS; + dt3155_status[ index ].state = DT3155_STATE_IDLE; + + /* find_PCI() will check if devices are installed; */ + /* first assume they're not: */ + dt3155_status[ index ].mem_addr = 0; + dt3155_status[ index ].mem_size = 0; + dt3155_status[ index ].state = DT3155_STATE_IDLE; + dt3155_status[ index ].device_installed = 0; + } + + /* Now let's find the hardware. find_PCI() will set ndevices to the + * number of cards found in this machine. */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + if ( !(pcibios_present()) ) + { + printk("DT3155: Error: No PCI bios on this machine \n"); + if( unregister_chrdev( dt3155_major, "dt3155" ) != 0 ) + printk("DT3155: cleanup_module failed\n"); + + return DT_3155_FAILURE; + } + else +#endif + { + if ( (rcode = find_PCI()) != DT_3155_SUCCESS ) + { + printk("DT3155 error: find_PCI() failed to find dt3155 board(s)\n"); + unregister_chrdev( dt3155_major, "dt3155" ); + return rcode; + } + } + + /* Ok, time to setup the frame buffers */ + if( (rcode = dt3155_setup_buffers(&allocatorAddr)) < 0 ) + { + printk("DT3155: Error: setting up buffer not large enough."); + unregister_chrdev( dt3155_major, "dt3155" ); + return rcode; + } + + /* If we are this far, then there is enough RAM */ + /* for the buffers: Print the configuration. */ + for( index = 0; index < ndevices; index++ ) + { + printk("DT3155: Device = %d; acq_mode = %d; " + "continuous = %d; cols = %d; rows = %d;\n", + index , + dt3155_status[ index ].config.acq_mode, + dt3155_status[ index ].config.continuous, + dt3155_status[ index ].config.cols, + dt3155_status[ index ].config.rows); + printk("DT3155: m_addr = 0x%x; m_size = %ld; " + "state = %ld; device_installed = %d\n", + (u_int)dt3155_status[ index ].mem_addr, + dt3155_status[ index ].mem_size, + dt3155_status[ index ].state, + dt3155_status[ index ].device_installed); + } + + /* Disable ALL interrupts */ + int_csr_r.reg = 0; + for( index = 0; index < ndevices; index++ ) + { + WriteMReg( (dt3155_lbase[ index ] + INT_CSR), int_csr_r.reg ); + if( dt3155_status[ index ].device_installed ) + { + /* + * This driver *looks* like it can handle sharing interrupts, + * but I can't actually test myself. I've had reports that it + * DOES work so I'll enable it for now. This comment will remain + * as a reminder in case any problems arise. (SS) + */ + /* in older kernels flags are: SA_SHIRQ | SA_INTERRUPT */ + rcode = request_irq( dt3155_status[ index ].irq, (void *)dt3155_isr, + IRQF_SHARED | IRQF_DISABLED, devname[ index ], + (void*) &dt3155_status[index]); + if( rcode < 0 ) + { + printk("DT3155: minor %d request_irq failed for IRQ %d\n", + index, dt3155_status[index].irq); + unregister_chrdev( dt3155_major, "dt3155" ); + return rcode; + } + } + } + + printk("DT3155: finished loading\n"); + + return 0; +} + +/***************************************************** + * cleanup_module(void) + * + *****************************************************/ +void cleanup_module(void) +{ + int index; + + printk("DT3155: cleanup_module called\n"); + + /* removed DMA allocated with the allocator */ +#ifdef STANDALONE_ALLOCATOR + if (allocatorAddr != 0) + allocator_free_dma(allocatorAddr); +#else + allocator_cleanup(); +#endif + + unregister_chrdev( dt3155_major, "dt3155" ); + + for( index = 0; index < ndevices; index++ ) + { + if( dt3155_status[ index ].device_installed == 1 ) + { + printk( "DT3155: Freeing irq %d for device %d\n", + dt3155_status[ index ].irq, index ); + free_irq( dt3155_status[ index ].irq, (void*)&dt3155_status[index] ); + } + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + if (MOD_IN_USE) + printk("DT3155: device busy, remove delayed\n"); +#endif +} + diff --git a/drivers/staging/dt3155/dt3155_drv.h b/drivers/staging/dt3155/dt3155_drv.h new file mode 100644 index 0000000..92a92dc --- /dev/null +++ b/drivers/staging/dt3155/dt3155_drv.h @@ -0,0 +1,50 @@ +/* + +Copyright 1996,2002 Gregory D. Hager, Alfred A. Rizzi, Noah J. Cowan, + Scott Smedley + +This file is part of the DT3155 Device Driver. + +The DT3155 Device Driver is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The DT3155 Device Driver is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty +of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the DT3155 Device Driver; if not, write to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, +MA 02111-1307 USA +*/ + +#ifndef DT3155_DRV_INC +#define DT3155_DRV_INC + +/* kernel logical address of the frame grabbers */ +extern u_char *dt3155_lbase[ MAXBOARDS ]; + +/* kernel logical address of ram buffer */ +extern u_char *dt3155_bbase; + +#ifdef __KERNEL__ +#include + +#include /* need access to LINUX_VERSION_CODE */ +/* wait queue for reads */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1) +extern wait_queue_head_t dt3155_read_wait_queue[MAXBOARDS]; +#else +extern struct wait_queue *dt3155_read_wait_queue[MAXBOARDS]; +#endif +#endif + +/* number of devices */ +extern u_int ndevices; + +extern int dt3155_errno; + +#endif diff --git a/drivers/staging/dt3155/dt3155_io.c b/drivers/staging/dt3155/dt3155_io.c new file mode 100644 index 0000000..6d51686 --- /dev/null +++ b/drivers/staging/dt3155/dt3155_io.c @@ -0,0 +1,236 @@ +/* + +Copyright 1996,2002,2005 Gregory D. Hager, Alfred A. Rizzi, Noah J. Cowan, + Jason Lapenta, Scott Smedley + +This file is part of the DT3155 Device Driver. + +The DT3155 Device Driver is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The DT3155 Device Driver is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty +of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the DT3155 Device Driver; if not, write to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, +MA 02111-1307 USA + + +-- Changes -- + + Date Programmer Description of changes made + ------------------------------------------------------------------- + 10-Oct-2001 SS port to 2.4 kernel. + 24-Jul-2002 SS GPL licence. + 26-Jul-2002 SS Bug fix: timing logic was wrong. + 08-Aug-2005 SS port to 2.6 kernel. + +*/ + +/* This file provides some basic register io routines. It is modified + from demo code provided by Data Translations. */ + +#ifdef __KERNEL__ +#include +#endif + +#if 0 +#include +#include +#include +#endif + +#include "dt3155.h" +#include "dt3155_io.h" +#include "dt3155_drv.h" + +#ifndef __KERNEL__ +#include +#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 & . +long elapsed2 (const struct timeval *pStart, const struct timeval *pEnd) +{ + long i = (pEnd->tv_sec - pStart->tv_sec) * 1000000; + i += pEnd->tv_usec - pStart->tv_usec; + return i; +} + +/*********************************************************************** + wait_ibsyclr() + + This function handles read/write timing and r/w timeout error + + Returns TRUE if NEW_CYCLE clears + Returns FALSE if NEW_CYCLE doesn't clear in roughly 3 msecs, + otherwise returns 0 + +***********************************************************************/ +int wait_ibsyclr(u_char * lpReg) +{ + /* wait 100 microseconds */ + +#ifdef __KERNEL__ + udelay(100L); + /* __delay(loops_per_sec/10000); */ + if (iic_csr2_r.fld.NEW_CYCLE ) + { /* if NEW_CYCLE didn't clear */ + /* TIMEOUT ERROR */ + dt3155_errno = DT_ERR_I2C_TIMEOUT; + return FALSE; + } + else + return TRUE; /* no error */ +#else + struct timeval StartTime; + struct timeval EndTime; + + const int to_3ms = 3000; /* time out of 3ms = 3000us */ + + gettimeofday( &StartTime, NULL ); + do { + /* get new iic_csr2 value: */ + ReadMReg((lpReg + IIC_CSR2), iic_csr2_r.reg); + gettimeofday( &EndTime, NULL ); + } + while ((elapsed2(&StartTime, &EndTime) < to_3ms) && iic_csr2_r.fld.NEW_CYCLE); + + if (iic_csr2_r.fld.NEW_CYCLE ) + { /* if NEW_CYCLE didn't clear */ + printf("Timed out waiting for NEW_CYCLE to clear!"); + return FALSE; + } + else + return TRUE; /* no error */ +#endif +} + +/*********************************************************************** + WriteI2C() + + This function handles writing to 8-bit DT3155 registers + + 1st parameter is pointer to 32-bit register base address + 2nd parameter is reg. index; + 3rd is value to be written + + Returns TRUE - Successful completion + FALSE - Timeout error - cycle did not complete! +***********************************************************************/ +int WriteI2C (u_char * lpReg, u_short wIregIndex, u_char byVal) +{ + int writestat; /* status for return */ + + /* read 32 bit IIC_CSR2 register data into union */ + + ReadMReg((lpReg + IIC_CSR2), iic_csr2_r.reg); + + iic_csr2_r.fld.DIR_RD = 0; /* for write operation */ + iic_csr2_r.fld.DIR_ADDR = wIregIndex; /* I2C address of I2C register: */ + iic_csr2_r.fld.DIR_WR_DATA = byVal; /* 8 bit data to be written to I2C reg */ + iic_csr2_r.fld.NEW_CYCLE = 1; /* will start a direct I2C cycle: */ + + /* xfer union data into 32 bit IIC_CSR2 register */ + + WriteMReg((lpReg + IIC_CSR2), iic_csr2_r.reg); + + /* wait for IIC cycle to finish */ + + writestat = wait_ibsyclr( lpReg ); + return writestat; /* return with status */ +} + +/*********************************************************************** + ReadI2C() + + This function handles reading from 8-bit DT3155 registers + + 1st parameter is pointer to 32-bit register base address + 2nd parameter is reg. index; + 3rd is adrs of value to be read + + Returns TRUE - Successful completion + FALSE - Timeout error - cycle did not complete! +***********************************************************************/ +int ReadI2C (u_char * lpReg, u_short wIregIndex, u_char * byVal) +{ + int writestat; /* status for return */ + + /* read 32 bit IIC_CSR2 register data into union */ + ReadMReg((lpReg + IIC_CSR2), iic_csr2_r.reg); + + /* for read operation */ + iic_csr2_r.fld.DIR_RD = 1; + + /* I2C address of I2C register: */ + iic_csr2_r.fld.DIR_ADDR = wIregIndex; + + /* will start a direct I2C cycle: */ + iic_csr2_r.fld.NEW_CYCLE = 1; + + /* xfer union's data into 32 bit IIC_CSR2 register */ + WriteMReg((lpReg + IIC_CSR2), iic_csr2_r.reg); + + /* wait for IIC cycle to finish */ + writestat = wait_ibsyclr(lpReg); + + /* Next 2 commands read 32 bit IIC_CSR1 register's data into union */ + /* first read data is in IIC_CSR1 */ + ReadMReg((lpReg + IIC_CSR1), iic_csr1_r.reg); + + /* now get data u_char out of register */ + *byVal = (u_char) iic_csr1_r.fld.RD_DATA; + + return writestat; /* return with status */ +} diff --git a/drivers/staging/dt3155/dt3155_io.h b/drivers/staging/dt3155/dt3155_io.h new file mode 100644 index 0000000..c1174ab --- /dev/null +++ b/drivers/staging/dt3155/dt3155_io.h @@ -0,0 +1,400 @@ +/* + +Copyright 1996,2002 Gregory D. Hager, Alfred A. Rizzi, Noah J. Cowan, + Jason Lapenta, Scott Smedley + +This file is part of the DT3155 Device Driver. + +The DT3155 Device Driver is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The DT3155 Device Driver is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty +of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the DT3155 Device Driver; if not, write to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, +MA 02111-1307 USA + + +-- Changes -- + + Date Programmer Description of changes made + ------------------------------------------------------------------- + 24-Jul-2002 SS GPL licence. + +*/ + +/* This code is a modified version of examples provided by Data Translations.*/ + +#ifndef DT3155_IO_INC +#define DT3155_IO_INC + +/* macros to access registers */ + +#define WriteMReg(Address, Data) * ((u_long *) (Address)) = Data +#define ReadMReg(Address, Data) Data = * ((u_long *) (Address)) + +/***************** 32 bit register globals **************/ + +/* offsets for 32-bit memory mapped registers */ + +#define EVEN_DMA_START 0x000 +#define ODD_DMA_START 0x00C +#define EVEN_DMA_STRIDE 0x018 +#define ODD_DMA_STRIDE 0x024 +#define EVEN_PIXEL_FMT 0x030 +#define ODD_PIXEL_FMT 0x034 +#define FIFO_TRIGGER 0x038 +#define XFER_MODE 0x03C +#define CSR1 0x040 +#define RETRY_WAIT_CNT 0x044 +#define INT_CSR 0x048 +#define EVEN_FLD_MASK 0x04C +#define ODD_FLD_MASK 0x050 +#define MASK_LENGTH 0x054 +#define FIFO_FLAG_CNT 0x058 +#define IIC_CLK_DUR 0x05C +#define IIC_CSR1 0x060 +#define IIC_CSR2 0x064 +#define EVEN_DMA_UPPR_LMT 0x08C +#define ODD_DMA_UPPR_LMT 0x090 + +#define CLK_DUR_VAL 0x01010101 + + + +/******** Assignments and Typedefs for 32 bit Memory Mapped Registers ********/ + +/********************************** + * fifo_trigger_tag + */ +typedef union fifo_trigger_tag { + u_long reg; + struct + { + u_long PACKED : 6; + u_long : 9; + u_long PLANER : 7; + u_long : 9; + } fld; +} FIFO_TRIGGER_R; + +/********************************** + * xfer_mode_tag + */ +typedef union xfer_mode_tag { + u_long reg; + struct + { + u_long : 2; + u_long FIELD_TOGGLE : 1; + u_long : 5; + u_long : 2; + u_long : 22; + } fld; +} XFER_MODE_R; + +/********************************** + * csr1_tag + */ +typedef union csr1_tag { + u_long reg; + struct + { + u_long CAP_CONT_EVE : 1; + u_long CAP_CONT_ODD : 1; + u_long CAP_SNGL_EVE : 1; + u_long CAP_SNGL_ODD : 1; + u_long FLD_DN_EVE : 1; + u_long FLD_DN_ODD : 1; + u_long SRST : 1; + u_long FIFO_EN : 1; + u_long FLD_CRPT_EVE : 1; + u_long FLD_CRPT_ODD : 1; + u_long ADDR_ERR_EVE : 1; + u_long ADDR_ERR_ODD : 1; + u_long CRPT_DIS : 1; + u_long RANGE_EN : 1; + u_long : 16; + } fld; +} CSR1_R; + +/********************************** + * retry_wait_cnt_tag + */ +typedef union retry_wait_cnt_tag { + u_long reg; + struct + { + u_long RTRY_WAIT_CNT : 8; + u_long : 24; + } fld; +} RETRY_WAIT_CNT_R; + +/********************************** + * int_csr_tag + */ +typedef union int_csr_tag { + u_long reg; + struct + { + u_long FLD_END_EVE : 1; + u_long FLD_END_ODD : 1; + u_long FLD_START : 1; + u_long : 5; + u_long FLD_END_EVE_EN : 1; + u_long FLD_END_ODD_EN : 1; + u_long FLD_START_EN : 1; + u_long : 21; + } fld; +} INT_CSR_R; + +/********************************** + * mask_length_tag + */ +typedef union mask_length_tag { + u_long reg; + struct + { + u_long MASK_LEN_EVE : 5; + u_long : 11; + u_long MASK_LEN_ODD : 5; + u_long : 11; + } fld; +} MASK_LENGTH_R; + +/********************************** + * fifo_flag_cnt_tag + */ +typedef union fifo_flag_cnt_tag { + u_long reg; + struct + { + u_long AF_COUNT : 7; + u_long : 9; + u_long AE_COUNT : 7; + u_long : 9; + } fld; +} FIFO_FLAG_CNT_R; + +/********************************** + * iic_clk_dur + */ +typedef union iic_clk_dur { + u_long reg; + struct + { + u_long PHASE_1 : 8; + u_long PHASE_2 : 8; + u_long PHASE_3 : 8; + u_long PHASE_4 : 8; + } fld; +} IIC_CLK_DUR_R; + +/********************************** + * iic_csr1_tag + */ +typedef union iic_csr1_tag { + u_long reg; + struct + { + u_long AUTO_EN : 1; + u_long BYPASS : 1; + u_long SDA_OUT : 1; + u_long SCL_OUT : 1; + u_long : 4; + u_long AUTO_ABORT : 1; + u_long DIRECT_ABORT : 1; + u_long SDA_IN : 1; + u_long SCL_IN : 1; + u_long : 4; + u_long AUTO_ADDR : 8; + u_long RD_DATA : 8; + } fld; +} IIC_CSR1_R; + +/********************************** + * iic_csr2_tag + */ +typedef union iic_csr2_tag { + u_long reg; + struct + { + u_long DIR_WR_DATA : 8; + u_long DIR_SUB_ADDR : 8; + u_long DIR_RD : 1; + u_long DIR_ADDR : 7; + u_long NEW_CYCLE : 1; + u_long : 7; + } fld; +} IIC_CSR2_R; + +/* use for both EVEN and ODD DMA UPPER LIMITS */ + +/********************************** + * dma_upper_lmt_tag + */ +typedef union dma_upper_lmt_tag { + u_long reg; + struct + { + u_long DMA_UPPER_LMT_VAL : 24; + u_long : 8; + } fld; +} DMA_UPPER_LMT_R; + + +/*************************************** + * Global declarations of local copies + * of boards' 32 bit registers + ***************************************/ +extern u_long even_dma_start_r; /* bit 0 should always be 0 */ +extern u_long odd_dma_start_r; /* .. */ +extern u_long even_dma_stride_r; /* bits 0&1 should always be 0 */ +extern u_long odd_dma_stride_r; /* .. */ +extern u_long even_pixel_fmt_r; +extern u_long odd_pixel_fmt_r; + +extern FIFO_TRIGGER_R fifo_trigger_r; +extern XFER_MODE_R xfer_mode_r; +extern CSR1_R csr1_r; +extern RETRY_WAIT_CNT_R retry_wait_cnt_r; +extern INT_CSR_R int_csr_r; + +extern u_long even_fld_mask_r; +extern u_long odd_fld_mask_r; + +extern MASK_LENGTH_R mask_length_r; +extern FIFO_FLAG_CNT_R fifo_flag_cnt_r; +extern IIC_CLK_DUR_R iic_clk_dur_r; +extern IIC_CSR1_R iic_csr1_r; +extern IIC_CSR2_R iic_csr2_r; +extern DMA_UPPER_LMT_R even_dma_upper_lmt_r; +extern DMA_UPPER_LMT_R odd_dma_upper_lmt_r; + + + +/***************** 8 bit I2C register globals ***********/ + +#define CSR2 0x010 /* indices of 8-bit I2C mapped reg's*/ +#define EVEN_CSR 0x011 +#define ODD_CSR 0x012 +#define CONFIG 0x013 +#define DT_ID 0x01F +#define X_CLIP_START 0x020 +#define Y_CLIP_START 0x022 +#define X_CLIP_END 0x024 +#define Y_CLIP_END 0x026 +#define AD_ADDR 0x030 +#define AD_LUT 0x031 +#define AD_CMD 0x032 +#define DIG_OUT 0x040 +#define PM_LUT_ADDR 0x050 +#define PM_LUT_DATA 0x051 + + +/******** Assignments and Typedefs for 8 bit I2C Registers********************/ + +typedef union i2c_csr2_tag { + u_char reg; + struct + { + u_char CHROM_FIL : 1; + u_char SYNC_SNTL : 1; + u_char HZ50 : 1; + u_char SYNC_PRESENT : 1; + u_char BUSY_EVE : 1; + u_char BUSY_ODD : 1; + u_char DISP_PASS : 1; + } fld; +} I2C_CSR2; + +typedef union i2c_even_csr_tag { + u_char reg; + struct + { + u_char DONE_EVE : 1; + u_char SNGL_EVE : 1; + u_char ERROR_EVE : 1; + u_char : 5; + } fld; +} I2C_EVEN_CSR; + +typedef union i2c_odd_csr_tag { + u_char reg; + struct + { + u_char DONE_ODD : 1; + u_char SNGL_ODD : 1; + u_char ERROR_ODD : 1; + u_char : 5; + } fld; +} I2C_ODD_CSR; + +typedef union i2c_config_tag { + u_char reg; + struct + { + u_char ACQ_MODE : 2; + u_char EXT_TRIG_EN : 1; + u_char EXT_TRIG_POL : 1; + u_char H_SCALE : 1; + u_char CLIP : 1; + u_char PM_LUT_SEL : 1; + u_char PM_LUT_PGM : 1; + } fld; +} I2C_CONFIG; + + +typedef union i2c_ad_cmd_tag { /* bits can have 3 different meanings + depending on value of AD_ADDR */ + u_char reg; + struct + { + u_char : 2; + u_char SYNC_LVL_SEL : 2; + u_char SYNC_CNL_SEL : 2; + u_char DIGITIZE_CNL_SEL1 : 2; + } bt252_command; /* Bt252 Command Register */ + struct /* if AD_ADDR = 00h */ + { + u_char IOUT_DATA : 8; + } bt252_iout0; /* Bt252 IOUT0 register */ + struct /* if AD_ADDR = 01h */ + { + u_char IOUT_DATA : 8; + } bt252_iout1; /* BT252 IOUT1 register */ +} I2C_AD_CMD; /* if AD_ADDR = 02h */ + + +/***** Global declarations of local copies of boards' 8 bit I2C registers ***/ + +extern I2C_CSR2 i2c_csr2; +extern I2C_EVEN_CSR i2c_even_csr; +extern I2C_ODD_CSR i2c_odd_csr; +extern I2C_CONFIG i2c_config; +extern u_char i2c_dt_id; +extern u_char i2c_x_clip_start; +extern u_char i2c_y_clip_start; +extern u_char i2c_x_clip_end; +extern u_char i2c_y_clip_end; +extern u_char i2c_ad_addr; +extern u_char i2c_ad_lut; +extern I2C_AD_CMD i2c_ad_cmd; +extern u_char i2c_dig_out; +extern u_char i2c_pm_lut_addr; +extern u_char i2c_pm_lut_data; + +/* Functions for Global use */ + +/* access 8-bit IIC registers */ + +extern int ReadI2C (u_char * lpReg, u_short wIregIndex, u_char * byVal); +extern int WriteI2C (u_char * lpReg, u_short wIregIndex, u_char byVal); + +#endif diff --git a/drivers/staging/dt3155/dt3155_isr.c b/drivers/staging/dt3155/dt3155_isr.c new file mode 100644 index 0000000..339aee9 --- /dev/null +++ b/drivers/staging/dt3155/dt3155_isr.c @@ -0,0 +1,545 @@ +/* + +Copyright 1996,2002,2005 Gregory D. Hager, Alfred A. Rizzi, Noah J. Cowan, + Jason Lapenta, Scott Smedley, Greg Sharp + +This file is part of the DT3155 Device Driver. + +The DT3155 Device Driver is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The DT3155 Device Driver is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty +of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the DT3155 Device Driver; if not, write to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, +MA 02111-1307 USA + + File: dt3155_isr.c +Purpose: Buffer management routines, and other routines for the ISR + (the actual isr is in dt3155_drv.c) + +-- Changes -- + + Date Programmer Description of changes made + ------------------------------------------------------------------- + 03-Jul-2000 JML n/a + 02-Apr-2002 SS Mods to make work with separate allocator + module; Merged John Roll's mods to make work with + multiple boards. + 10-Jul-2002 GCS Complete rewrite of setup_buffers to disallow + buffers which span a 4MB boundary. + 24-Jul-2002 SS GPL licence. + 30-Jul-2002 NJC Added support for buffer loop. + 31-Jul-2002 NJC Complete rewrite of buffer management + 02-Aug-2002 NJC Including slab.h instead of malloc.h (no warning). + Also, allocator_init() now returns allocator_max + so cleaned up allocate_buffers() accordingly. + 08-Aug-2005 SS port to 2.6 kernel. + +*/ + +#include +#include +#include +#include + +#include "dt3155.h" +#include "dt3155_drv.h" +#include "dt3155_io.h" +#include "dt3155_isr.h" +#include "allocator.h" + +#define FOUR_MB (0x0400000) /* Can't DMA accross a 4MB boundary!*/ +#define UPPER_10_BITS (0x3FF<<22) /* Can't DMA accross a 4MB boundary!*/ + + +/* Pointer into global structure for handling buffers */ +struct dt3155_fbuffer_s *dt3155_fbuffer[MAXBOARDS] = {NULL +#if MAXBOARDS == 2 + , NULL +#endif +}; + +/****************************************************************************** + * Simple array based que struct + * + * Some handy functions using the buffering structure. + *****************************************************************************/ + + +/*************************** + * are_empty_buffers + * m is minor # of device + ***************************/ +inline bool are_empty_buffers( int m ) +{ + return ( dt3155_fbuffer[ m ]->empty_len ); +} + +/************************** + * push_empty + * m is minor # of device + * + * This is slightly confusing. The number empty_len is the literal # + * of empty buffers. After calling, empty_len-1 is the index into the + * empty buffer stack. So, if empty_len == 1, there is one empty buffer, + * given by dt3155_fbuffer[m]->empty_buffers[0]. + * empty_buffers should never fill up, though this is not checked. + **************************/ +inline void push_empty( int index, int m ) +{ + dt3155_fbuffer[m]->empty_buffers[ dt3155_fbuffer[m]->empty_len ] = index; + dt3155_fbuffer[m]->empty_len++; +} + +/************************** + * pop_empty( m ) + * m is minor # of device + **************************/ +inline int pop_empty( int m ) +{ + dt3155_fbuffer[m]->empty_len--; + return dt3155_fbuffer[m]->empty_buffers[ dt3155_fbuffer[m]->empty_len ]; +} + +/************************* + * is_ready_buf_empty( m ) + * m is minor # of device + *************************/ +inline bool is_ready_buf_empty( int m ) +{ + return ((dt3155_fbuffer[ m ]->ready_len) == 0); +} + +/************************* + * is_ready_buf_full( m ) + * m is minor # of device + * this should *never* be true if there are any active, locked or empty + * buffers, since it corresponds to nbuffers ready buffers!! + * 7/31/02: total rewrite. --NJC + *************************/ +inline bool is_ready_buf_full( int m ) +{ + return ( dt3155_fbuffer[ m ]->ready_len == dt3155_fbuffer[ m ]->nbuffers ); +} + +/***************************************************** + * push_ready( m, buffer ) + * m is minor # of device + * + *****************************************************/ +inline void push_ready( int m, int index ) +{ + int head = dt3155_fbuffer[m]->ready_head; + + dt3155_fbuffer[ m ]->ready_que[ head ] = index; + dt3155_fbuffer[ m ]->ready_head = ( (head + 1) % + (dt3155_fbuffer[ m ]->nbuffers) ); + dt3155_fbuffer[ m ]->ready_len++; + +} + +/***************************************************** + * get_tail() + * m is minor # of device + * + * Simply comptutes the tail given the head and the length. + *****************************************************/ +static inline int get_tail( int m ) +{ + return ((dt3155_fbuffer[ m ]->ready_head - + dt3155_fbuffer[ m ]->ready_len + + dt3155_fbuffer[ m ]->nbuffers)% + (dt3155_fbuffer[ m ]->nbuffers)); +} + + + +/***************************************************** + * pop_ready() + * m is minor # of device + * + * This assumes that there is a ready buffer ready... should + * be checked (e.g. with is_ready_buf_empty() prior to call. + *****************************************************/ +inline int pop_ready( int m ) +{ + int tail; + tail = get_tail(m); + dt3155_fbuffer[ m ]->ready_len--; + return dt3155_fbuffer[ m ]->ready_que[ tail ]; +} + + +/***************************************************** + * printques + * m is minor # of device + *****************************************************/ +inline void printques( int m ) +{ + int head = dt3155_fbuffer[ m ]->ready_head; + int tail; + int num = dt3155_fbuffer[ m ]->nbuffers; + int frame_index; + int index; + + tail = get_tail(m); + + printk("\n R:"); + for ( index = tail; index != head; index++, index = index % (num) ) + { + frame_index = dt3155_fbuffer[ m ]->ready_que[ index ]; + printk(" %d ", frame_index ); + } + + printk("\n E:"); + for ( index = 0; index < dt3155_fbuffer[ m ]->empty_len; index++ ) + { + frame_index = dt3155_fbuffer[ m ]->empty_buffers[ index ]; + printk(" %d ", frame_index ); + } + + frame_index = dt3155_fbuffer[ m ]->active_buf; + printk("\n A: %d", frame_index); + + frame_index = dt3155_fbuffer[ m ]->locked_buf; + printk("\n L: %d \n", frame_index ); + +} + +/***************************************************** + * adjust_4MB + * + * If a buffer intersects the 4MB boundary, push + * the start address up to the beginning of the + * next 4MB chunk (assuming bufsize < 4MB). + *****************************************************/ +u_long adjust_4MB (u_long buf_addr, u_long bufsize) { + if (((buf_addr+bufsize) & UPPER_10_BITS) != (buf_addr & UPPER_10_BITS)) + return (buf_addr+bufsize) & UPPER_10_BITS; + else + return buf_addr; +} + + +/***************************************************** + * allocate_buffers + * + * Try to allocate enough memory for all requested + * buffers. If there is not enough free space + * try for less memory. + *****************************************************/ +void allocate_buffers (u_long *buf_addr, u_long* total_size_kbs, + u_long bufsize) +{ + /* Compute the minimum amount of memory guaranteed to hold all + MAXBUFFERS such that no buffer crosses the 4MB boundary. + Store this value in the variable "full_size" */ + + u_long allocator_max; + u_long bufs_per_chunk = (FOUR_MB / bufsize); + u_long filled_chunks = (MAXBUFFERS-1) / bufs_per_chunk; + u_long leftover_bufs = MAXBUFFERS - filled_chunks * bufs_per_chunk; + + u_long full_size = bufsize /* possibly unusable part of 1st chunk */ + + filled_chunks * FOUR_MB /* max # of completely filled 4mb chunks */ + + leftover_bufs * bufsize; /* these buffs will be in a partly filled + chunk at beginning or end */ + + u_long full_size_kbs = 1 + (full_size-1) / 1024; + u_long min_size_kbs = 2*ndevices*bufsize / 1024; + u_long size_kbs; + + /* Now, try to allocate full_size. If this fails, keep trying for + less & less memory until it succeeds. */ +#ifndef STANDALONE_ALLOCATOR + /* initialize the allocator */ + allocator_init(&allocator_max); +#endif + size_kbs = full_size_kbs; + *buf_addr = 0; + printk ("DT3155: We would like to get: %d KB\n", (u_int)(full_size_kbs)); + printk ("DT3155: ...but need at least: %d KB\n", (u_int)(min_size_kbs)); + printk ("DT3155: ...the allocator has: %d KB\n", (u_int)(allocator_max)); + size_kbs = (full_size_kbs <= allocator_max ? full_size_kbs : allocator_max); + if (size_kbs > min_size_kbs) { + if ((*buf_addr = allocator_allocate_dma (size_kbs, GFP_KERNEL)) != 0) { + printk ("DT3155: Managed to allocate: %d KB\n", (u_int)size_kbs); + *total_size_kbs = size_kbs; + return; + } + } + /* If we got here, the allocation failed */ + printk ("DT3155: Allocator failed!\n"); + *buf_addr = 0; + *total_size_kbs = 0; + return; + +} + + +/***************************************************** + * dt3155_setup_buffers + * + * setup_buffers just puts the buffering system into + * a consistent state before the start of interrupts + * + * JML : it looks like all the buffers need to be + * continuous. So I'm going to try and allocate one + * continuous buffer. + * + * GCS : Fix DMA problems when buffer spans + * 4MB boundary. Also, add error checking. This + * function will return -ENOMEM when not enough memory. + *****************************************************/ +u_long dt3155_setup_buffers(u_long *allocatorAddr) + +{ + u_long index; + u_long rambuff_addr; /* start of allocation */ + u_long rambuff_size; /* total size allocated to driver */ + u_long rambuff_acm; /* accumlator, keep track of how much + is left after being split up*/ + u_long rambuff_end; /* end of rambuff */ + u_long numbufs; /* number of useful buffers allocated (per device) */ + u_long bufsize = DT3155_MAX_ROWS * DT3155_MAX_COLS; + int m; /* minor # of device, looped for all devs */ + + /* zero the fbuffer status and address structure */ + for ( m = 0; m < ndevices; m++) + { + dt3155_fbuffer[ m ] = &(dt3155_status[ m ].fbuffer); + + /* Make sure the buffering variables are consistent */ + { + u_char *ptr = (u_char *) dt3155_fbuffer[ m ]; + for( index = 0; index < sizeof(struct dt3155_fbuffer_s); index++) + *(ptr++)=0; + } + } + + /* allocate a large contiguous chunk of RAM */ + allocate_buffers (&rambuff_addr, &rambuff_size, bufsize); + printk( "DT3155: mem info\n" ); + printk( " - rambuf_addr = 0x%x \n", (u_int)rambuff_addr ); + printk( " - length (kb) = %u \n", (u_int)rambuff_size ); + if( rambuff_addr == 0 ) + { + printk( KERN_INFO + "DT3155: Error setup_buffers() allocator dma failed \n" ); + return -ENOMEM; + } + *allocatorAddr = rambuff_addr; + rambuff_end = rambuff_addr + 1024 * rambuff_size; + + /* after allocation, we need to count how many useful buffers there + are so we can give an equal number to each device */ + rambuff_acm = rambuff_addr; + for ( index = 0; index < MAXBUFFERS; index++) { + rambuff_acm = adjust_4MB (rambuff_acm, bufsize);/*avoid spanning 4MB bdry*/ + if (rambuff_acm + bufsize > rambuff_end) + break; + rambuff_acm += bufsize; + } + /* Following line is OK, will waste buffers if index + * not evenly divisible by ndevices -NJC*/ + numbufs = index / ndevices; + printk (" - numbufs = %u\n", (u_int) numbufs); + if (numbufs < 2) { + printk( KERN_INFO + "DT3155: Error setup_buffers() couldn't allocate 2 bufs/board\n" ); + return -ENOMEM; + } + + /* now that we have board memory we spit it up */ + /* between the boards and the buffers */ + rambuff_acm = rambuff_addr; + for ( m = 0; m < ndevices; m ++) + { + rambuff_acm = adjust_4MB (rambuff_acm, bufsize); + + /* Save the start of this boards buffer space (for mmap). */ + dt3155_status[ m ].mem_addr = rambuff_acm; + + for (index = 0; index < numbufs; index++) + { + rambuff_acm = adjust_4MB (rambuff_acm, bufsize); + if (rambuff_acm + bufsize > rambuff_end) { + /* Should never happen */ + printk ("DT3155 PROGRAM ERROR (GCS)\n" + "Error distributing allocated buffers\n"); + return -ENOMEM; + } + + dt3155_fbuffer[ m ]->frame_info[ index ].addr = rambuff_acm; + push_empty( index, m ); + /* printk(" - Buffer : %lx\n", + * dt3155_fbuffer[ m ]->frame_info[ index ].addr ); + */ + dt3155_fbuffer[ m ]->nbuffers += 1; + rambuff_acm += bufsize; + } + + /* Make sure there is an active buffer there. */ + dt3155_fbuffer[ m ]->active_buf = pop_empty( m ); + dt3155_fbuffer[ m ]->even_happened = 0; + dt3155_fbuffer[ m ]->even_stopped = 0; + + /* make sure there is no locked_buf JML 2/28/00 */ + dt3155_fbuffer[ m ]->locked_buf = -1; + + dt3155_status[ m ].mem_size = + rambuff_acm - dt3155_status[ m ].mem_addr; + + /* setup the ready queue */ + dt3155_fbuffer[ m ]->ready_head = 0; + dt3155_fbuffer[ m ]->ready_len = 0; + printk("Available buffers for device %d: %d\n", + m, dt3155_fbuffer[ m ]->nbuffers); + } + + return 1; +} + +/***************************************************** + * internal_release_locked_buffer + * + * The internal function for releasing a locked buffer. + * It assumes interrupts are turned off. + * + * m is minor number of device + *****************************************************/ +static inline void internal_release_locked_buffer( int m ) +{ + /* Pointer into global structure for handling buffers */ + if ( dt3155_fbuffer[ m ]->locked_buf >= 0 ) + { + push_empty( dt3155_fbuffer[ m ]->locked_buf, m ); + dt3155_fbuffer[ m ]->locked_buf = -1; + } +} + + +/***************************************************** + * dt3155_release_locked_buffer() + * m is minor # of device + * + * The user function of the above. + * + *****************************************************/ +inline void dt3155_release_locked_buffer( int m ) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + unsigned long int flags; + local_save_flags(flags); + local_irq_disable(); + internal_release_locked_buffer(m); + local_irq_restore(flags); +#else + int flags; + + save_flags( flags ); + cli(); + internal_release_locked_buffer( m ); + restore_flags( flags ); +#endif +} + + +/***************************************************** + * dt3155_flush() + * m is minor # of device + * + *****************************************************/ +inline int dt3155_flush( int m ) +{ + int index; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + unsigned long int flags; + local_save_flags(flags); + local_irq_disable(); +#else + int flags; + save_flags( flags ); + cli(); +#endif + + internal_release_locked_buffer( m ); + dt3155_fbuffer[ m ]->empty_len = 0; + + for ( index = 0; index < dt3155_fbuffer[ m ]->nbuffers; index++ ) + push_empty( index, m ); + + /* Make sure there is an active buffer there. */ + dt3155_fbuffer[ m ]->active_buf = pop_empty( m ); + + dt3155_fbuffer[ m ]->even_happened = 0; + dt3155_fbuffer[ m ]->even_stopped = 0; + + /* setup the ready queue */ + dt3155_fbuffer[ m ]->ready_head = 0; + dt3155_fbuffer[ m ]->ready_len = 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + local_irq_restore(flags); +#else + restore_flags( flags ); +#endif + + return 0; +} + +/***************************************************** + * dt3155_get_ready_buffer() + * m is minor # of device + * + * get_ready_buffer will grab the next chunk of data + * if it is already there, otherwise it returns 0. + * If the user has a buffer locked it will unlock + * that buffer before returning the new one. + *****************************************************/ +inline int dt3155_get_ready_buffer( int m ) +{ + int frame_index; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + unsigned long int flags; + local_save_flags(flags); + local_irq_disable(); +#else + int flags; + save_flags( flags ); + cli(); +#endif + +#ifdef DEBUG_QUES_A + printques( m ); +#endif + + internal_release_locked_buffer( m ); + + if (is_ready_buf_empty( m )) + frame_index = -1; + else + { + frame_index = pop_ready( m ); + dt3155_fbuffer[ m ]->locked_buf = frame_index; + } + +#ifdef DEBUG_QUES_B + printques( m ); +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + local_irq_restore(flags); +#else + restore_flags( flags ); +#endif + + return frame_index; +} diff --git a/drivers/staging/dt3155/dt3155_isr.h b/drivers/staging/dt3155/dt3155_isr.h new file mode 100644 index 0000000..b117b50 --- /dev/null +++ b/drivers/staging/dt3155/dt3155_isr.h @@ -0,0 +1,77 @@ +/* + +Copyright 1996,2002 Gregory D. Hager, Alfred A. Rizzi, Noah J. Cowan, + Jason Lapenta, Scott Smedley + +This file is part of the DT3155 Device Driver. + +The DT3155 Device Driver is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The DT3155 Device Driver is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty +of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the DT3155 Device Driver; if not, write to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, +MA 02111-1307 USA + + +-- Changes -- + + Date Programmer Description of changes made + ------------------------------------------------------------------- + 03-Jul-2000 JML n/a + 24-Jul-2002 SS GPL licence. + 26-Oct-2009 SS Porting to 2.6.30 kernel. + +-- notes -- + +*/ + +#ifndef DT3155_ISR_H +#define DT3155_ISR_H + +extern struct dt3155_fbuffer_s *dt3155_fbuffer[MAXBOARDS]; + +/* User functions for buffering */ +/* Initialize the buffering system. This should */ +/* be called prior to enabling interrupts */ + +u_long dt3155_setup_buffers(u_long *allocatorAddr); + +/* Get the next frame of data if it is ready. Returns */ +/* zero if no data is ready. If there is data but */ +/* the user has a locked buffer, it will unlock that */ +/* buffer and return it to the free list. */ + +int dt3155_get_ready_buffer(int minor); + +/* Return a locked buffer to the free list */ + +void dt3155_release_locked_buffer(int minor); + +/* Flush the buffer system */ +int dt3155_flush(int minor); + +/********************************** + * Simple array based que struct + **********************************/ + +bool are_empty_buffers( int minor ); +void push_empty( int index, int minor ); + +int pop_empty( int minor ); + +bool is_ready_buf_empty( int minor ); +bool is_ready_buf_full( int minor ); + +void push_ready( int minor, int index ); +int pop_ready( int minor ); + + +#endif diff --git a/drivers/staging/dt3155/pci-compat.h b/drivers/staging/dt3155/pci-compat.h new file mode 100644 index 0000000..5f47d5a --- /dev/null +++ b/drivers/staging/dt3155/pci-compat.h @@ -0,0 +1,97 @@ + +/* This header only makes send when included in a 2.0 compile */ + +#ifndef _PCI_COMPAT_H_ +#define _PCI_COMPAT_H_ + +#ifdef __KERNEL__ + +#include /* pcibios_* */ +#include /* pcibios_* */ +#include /* kmalloc */ + +/* fake the new pci interface based on the old one: encapsulate bus/devfn */ +struct pci_fake_dev { + u8 bus; + u8 devfn; + int index; +}; +#define pci_dev pci_fake_dev /* the other pci_dev is unused by 2.0 drivers */ + +extern inline struct pci_dev *pci_find_device(unsigned int vendorid, + unsigned int devid, + struct pci_dev *from) +{ + struct pci_dev *pptr = kmalloc(sizeof(*pptr), GFP_KERNEL); + int index = 0; + int ret; + + if (!pptr) return NULL; + if (from) index = pptr->index + 1; + ret = pcibios_find_device(vendorid, devid, index, + &pptr->bus, &pptr->devfn); + if (ret) { kfree(pptr); return NULL; } + return pptr; +} + +extern inline struct pci_dev *pci_find_class(unsigned int class, + struct pci_dev *from) +{ + return NULL; /* FIXME */ +} + +extern inline void pci_release_device(struct pci_dev *dev) +{ + kfree(dev); +} + +/* struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn); */ + +#define pci_present pcibios_present + +extern inline int +pci_read_config_byte(struct pci_dev *dev, u8 where, u8 *val) +{ + return pcibios_read_config_byte(dev->bus, dev->devfn, where, val); +} + +extern inline int +pci_read_config_word(struct pci_dev *dev, u8 where, u16 *val) +{ + return pcibios_read_config_word(dev->bus, dev->devfn, where, val); +} + +extern inline int +pci_read_config_dword(struct pci_dev *dev, u8 where, u32 *val) +{ + return pcibios_read_config_dword(dev->bus, dev->devfn, where, val); +} + +extern inline int +pci_write_config_byte(struct pci_dev *dev, u8 where, u8 val) +{ + return pcibios_write_config_byte(dev->bus, dev->devfn, where, val); +} + +extern inline int +pci_write_config_word(struct pci_dev *dev, u8 where, u16 val) +{ + return pcibios_write_config_word(dev->bus, dev->devfn, where, val); +} + +extern inline int +pci_write_config_dword(struct pci_dev *dev, u8 where, u32 val) +{ + return pcibios_write_config_dword(dev->bus, dev->devfn, where, val); +} + +extern inline void pci_set_master(struct pci_dev *dev) +{ + u16 cmd; + pcibios_read_config_word(dev->bus, dev->devfn, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MASTER; + pcibios_write_config_word(dev->bus, dev->devfn, PCI_COMMAND, cmd); +} + +#endif /* __KERNEL__ */ +#endif /* _PCI_COMPAT_H_ */ diff --git a/drivers/staging/dt3155/sysdep.h b/drivers/staging/dt3155/sysdep.h new file mode 100644 index 0000000..1d575d4 --- /dev/null +++ b/drivers/staging/dt3155/sysdep.h @@ -0,0 +1,820 @@ +/* + * sysdep.h -- centralizing compatibility issues between 2.0, 2.2, 2.4 + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: sysdep.h,v 1.2 2005/08/09 06:08:51 ssmedley Exp $ + */ + + +#ifndef _SYSDEP_H_ +#define _SYSDEP_H_ + +#ifndef LINUX_VERSION_CODE +# include +#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 +# endif +#endif + +#include + +/* 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 /* 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 +#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 +# 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 +# include +# 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 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 +# 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 */ + +# 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. ) */ + + +#endif /* _SYSDEP_H_ */ -- 1.8.2.3