Commit aa337ef1 authored by Scott Smedley's avatar Scott Smedley Committed by Greg Kroah-Hartman

Staging: add dt3155 driver

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