PCI Device Driver / remap_pfn_range()

From: Brian D. McGrew
Date: Mon Apr 17 2006 - 13:55:55 EST


A few weeks back I posted a cry for help with a PCI device driver which
maps hardware memory into user/kernel space using what used to be called
remap_page_range in the 2.6.9 kernel and now I'm trying to use
remap_pfn_range.

I'm still struggling with this and I'm hoping that there is an expert
out there who can point out what I'm doing wrong!!! My source code is
attached and I know I'm probably not doing this in the best way; but
it's the only way I don't know how, since I'm tasked with a job where I
have no idea what I'm doing!

When I try and access my device, which should be /dev/ibb[0-3], I get a
segmentation fault then a core dump and within a few seconds after that,
the system becomes way unstable and has to be rebooted (if it doesn't
lock up hard first).

TIA,

:b!

Brian D. McGrew { brian@xxxxxxxxxxxxx || brian@xxxxxxxxxxxxxxxxxxx }
--

static const char *ibb_c = "$Id: ibb.c,v 1.3 2006/04/17 16:08:06 brian
Exp brian $(c) MVP ";

#include <asm/uaccess.h>
#include <linux/config.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/types.h>

#include "dev/ibb.h"
#include "ibbsoft.h"

#ifndef __KERNEL__
#define __KERNEL__
#endif

#ifndef MODULE
#define MODULE
#endif

#ifndef CONFIG_PCI
#error "This driver REQUIRES PCI support!"
#endif

#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
#define MODVERSIONS
#endif

#ifdef CONFIG_DEVFS_FS
#include <linux/devfs_fs_kernel.h>
#endif

void ibb_rtc_wakeup(void);
irqreturn_t ibb_intr(int irq, void *dev_id, struct pt_regs *regs);

EXPORT_SYMBOL(ibb_rtc_wakeup);

/*
* Split minors in two parts
*/
#define TYPE(dev) (MINOR(dev) >> 4) /* high nibble */
#define NUM(dev) (MINOR(dev) & 0xf) /* low nibble */
#define dcmn_err(lvl, msg) { if ((lvl) < ibb_debug) printk msg; }

/*
* array of devices
*/
static IbbSoftDev *IbbSoft[kMaxIbbs];

static const uint32_t kIoWaiting = 0x04;
static const uint32_t MAG_FB_NUMBER = 0xCE1E57E;
static const uint16_t kIbbWakeup = 0x0fff;

/*
* local static variable
*/
static u_int ibb_debug = 100;

/*
* clamp the max frame buffer size to the lowest available memory size
*/
typedef struct ibb_ishared_attr {
u_int32_t frame_buffer_size;
int32_t dev_instance[kMaxIbbs];
} ibb_ishared_attr;

static ibb_ishared_attr ishared;

static void
lock_ibb(IbbSoftDev *ibb_sp, u_long *flags, const char *where)
{
dcmn_err(70, ("<1>" "lock_ibb(%d): %s:\n", ibb_sp->dev_num, where));
spin_lock_irqsave(&ibb_sp->mutex, *flags);
dcmn_err(70, ("<1>" "lock_ibb(%d): Done %s:\n", ibb_sp->dev_num,
where));
}

static void
unlock_ibb(IbbSoftDev *ibb_sp, u_long *flags, const char *where)
{
dcmn_err(70, ("<1>" "unlock_ibb(%d): %s:\n", ibb_sp->dev_num,
where));
spin_unlock_irqrestore(&ibb_sp->mutex, *flags);
dcmn_err(70, ("<1>" "unlock_ibb(%d): Done %s:\n", ibb_sp->dev_num,
where));
}

static void
init_ibb_soft_state(void)
{
int i;
dcmn_err(70, ("<1>" "init_ibb_soft_state:\n"));

for (i = 0; i < kMaxIbbs; i++)
IbbSoft[i] = NULL;

dcmn_err(70, ("<1>" "init_ibb_soft_state: Done\n"));
}

static int
alloc_ibb_soft_state(void)
{
int i;
dcmn_err(70, ("<1>" "alloc_ibb_soft_state:\n"));

for (i = 0; i < kMaxIbbs; i++) {
if (IbbSoft[i] == NULL) {
IbbSoft[i] = kmalloc(sizeof(IbbSoftDev), GFP_KERNEL);

if (IbbSoft[i] == NULL) {
dcmn_err(1, ("<1>" "alloc_ibb_soft_state: out of
memory.\n"));
return(-1);
}

dcmn_err(70, ("<1>" "alloc_ibb_soft_state: return %d\n",
i));
return(i);
}
}

/* Shouldn't get here! */
return(-1);
}

/*
* There may be a better way to do this!
*
* I need to make sure the data I initialized in ibb_probe()
* for device "X" is the same data I use in ibb_open().
*
* I could use the minor numbers, but I have to trust that
* the 'load' script for the ibb device is written correctly,
* since that's where minor numbers are assigned.
*
* So I'm using the major numbers. It's a little slower, since
* major numbers tend to be >200 I'm not just going to use
* them as indexes into my array. Instead I'm going though
* all the devices looking for the matching major.
*
* My concern, frankly, is that no other drivers seem to be
* using this method. But it seems the only foolproof way
* to keep the data with the device
*/

static int
find_ibb_soft_state(int ibb_major)
{
int i;
dcmn_err(70, ("<1>" "find_ibb_soft_state: major %d\n", ibb_major));

for (i = 0; i < kMaxIbbs; i++) {
if (IbbSoft[i] != NULL) {
if (ibb_major == IbbSoft[i]->ibb_major) {
dcmn_err(70, ("<1>" "find_ibb_soft_state: return %d\n",
i));
return(i);
}
}
}

dcmn_err(1,("<1>"
"find_ibb_soft_state: Can't find matching soft_state %d!\n",
ibb_major));

return(-1);
}

static void
free_ibb_soft_state(int i)
{
if(IbbSoft[i] == NULL)
return;

dcmn_err(70, ("<1>" "free_ibb_soft_state(%d):\n",
IbbSoft[i]->dev_num));

kfree(IbbSoft[i]);
IbbSoft[i] = NULL;

return;
}

ssize_t
ibb_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
dcmn_err(1, ("<1>" "ibb_read:\n"));
return(-EINVAL);
}

ssize_t
ibb_write(struct file *filp, const char *buf, size_t count, loff_t
*f_pos)
{
dcmn_err(1, ("<1>" "ibb_write:\n"));
return(-EINVAL);
}

/*
* IBBWAIT ioctl routine
* wait for ushared->image_table_index to *exceed* value
* passed in - (IBBWAIT,0) waits for snap 0 to be processed and
* index to advance to 1
*/

static inline int
ibb_wait(IbbSoftDev *ibb_sp, u_long arg)
{
uint32_t table_index = (uint32_t)arg;
int dev_num = ibb_sp->dev_num;

u_long flags;
int wq_ret = 0;

if (ibb_sp->ushared == NULL) {
return(EAGAIN);
}

if (ibb_sp->ushared->image_table_index > table_index) {
dcmn_err(27, ("<1>" "ibb_ioctl(%d): IBBWAIT already got index
%d\n",
dev_num, ibb_sp->ushared->image_table_index));

return(0);
}

lock_ibb(ibb_sp, &flags, "IBBWAIT 0");

ibb_sp->flags |= kIoWaiting;
ibb_sp->wait_for = table_index;

unlock_ibb(ibb_sp, &flags, "IBBWAIT 0");

dcmn_err(27, ("<1>" "ibb_ioctl(%d): IBBWAIT wait for %u, %u\n",
dev_num, ibb_sp->wait_for, ibb_sp->ushared->image_table_index)
);

wq_ret = wait_event_interruptible(ibb_sp->wq,
(ibb_sp->ushared->image_table_index > table_index));

dcmn_err(27, ("<1>" "ibb_ioctl(%d): IBBWAIT got index %d\n",
dev_num,ibb_sp->wait_for) );

lock_ibb(ibb_sp, &flags, "IBBWAIT 1");

ibb_sp->flags &= ~kIoWaiting;

unlock_ibb(ibb_sp, &flags, "IBBWAIT 1");

if (ibb_sp->wait_for == kIbbWakeup) {
dcmn_err(27, ("<1>" "ibb_ioctl(%d): IBBWAIT got Wakeup\n",
dev_num));
}
else if (ibb_sp->ushared->image_table_index > ibb_sp->wait_for) {
dcmn_err(27, ("<1>" "ibb_ioctl(%d): IBBWAIT got index %d\n",
dev_num,ibb_sp->wait_for));
}
else {
if(wq_ret != 0) {
dcmn_err(0, ("<1>" "ibb_ioctl(%d): IBBWAIT signal intr\n",
dev_num));
}
else {
dcmn_err(0, ("<1>" "ibb_ioctl(%d): IBBWAIT Weird cond.\n",
dev_num));
}

return(-1); /* !! there's been a problem */
}

return(0);
}

int
ibb_ioctl(struct inode *inode, struct file *filp, u_int cmd, u_long arg)
{
IbbSoftDev *ibb_sp = filp->private_data;

int retval = 0;
u_long flags;

if (ibb_sp == NULL) {
dcmn_err(1, ("<1>" "ibb_ioctl(%d): Soft Info Lost\n",
ibb_sp->dev_num));

return(-EFAULT);
}

switch (cmd) {
case IBBWAIT:
dcmn_err(27, ("<1>" "ibb_ioctl(%d): cmd IBBWAIT arg %lu\n",
ibb_sp->dev_num,arg));

retval = ibb_wait(ibb_sp, arg);
break;

case IBBTST:
if (ibb_sp->image_table == NULL)
return 0;

dcmn_err(1, ("<1>" "ibb_ioctl(%d): table 0 %x \n",
ibb_sp->dev_num,ibb_sp->image_table[0]) );
dcmn_err(1, ("<1>" "ibb_ioctl(%d): table 1 %x \n",
ibb_sp->dev_num,ibb_sp->image_table[1]) );

break;

case IBBWAKEUP:
dcmn_err(30, ("<1>" "ibb_ioctl(%d): cmd IBBWAKEUP\n",
ibb_sp->dev_num));

retval = 0;

if (ibb_sp->flags & kIoWaiting) {
lock_ibb(ibb_sp, &flags, "IBBWAKEUP");
ibb_sp->wait_for = kIbbWakeup;
unlock_ibb(ibb_sp, &flags, "IBBWAKEUP");
wake_up_interruptible(&ibb_sp->wq);
}

break;

case IBBFBACOUNT:
dcmn_err(30, ("<1>" "ibb_ioctl(%d): cmd IBBFBACOUNT arg
%lu\n",
ibb_sp->dev_num, arg) );

lock_ibb(ibb_sp, &flags, "IBBFBACOUNT");
*(ibb_sp->fbac_virt) = (uint32_t)arg;
unlock_ibb(ibb_sp, &flags, "IBBFBACOUNT");

dcmn_err(37, ("<1>" "ibb_ioctl(%d): set fb count to %u\n",
ibb_sp->dev_num,*(ibb_sp->fbac_virt)) );

retval = 0;
break;

case IBBFSIZE:
dcmn_err(30, ("<1>" "ibb_ioctl(%d): cmd IBBFSIZE arg %lu\n",

ibb_sp->dev_num,arg) );

lock_ibb(ibb_sp, &flags, "IBBFSIZE");
*(ibb_sp->fsize_virt) = (uint32_t)arg;
unlock_ibb(ibb_sp, &flags, "IBBFSIZE");

dcmn_err(37, ("<1>" "ibb_ioctl(%d): set frame size to %u\n",
ibb_sp->dev_num,*(ibb_sp->fsize_virt)));

break;

case IBBSIZE:
dcmn_err(30, ("<1>" "ibb_ioctl(%d): cmd is IBBSIZE: %d\n",
ibb_sp->dev_num, ishared.frame_buffer_size));

retval = put_user(ishared.frame_buffer_size, (u_long *)arg);
break;

case IBBVERSION:
if (put_user(kIbbVersion, (u_long *)arg)) {
dcmn_err(1, ("<1>" "ibb_ioctl(%d): can't copy ver info
%d\n",
ibb_sp->dev_num,(int)kIbbVersion)) ;

retval = -EFAULT;
}

dcmn_err(1, ("<1>" "ibb_ioctl(%d): Copied ver info %d\n",
ibb_sp->dev_num, (int)kIbbVersion));

break;

case IBBCLIBB_ID:
if (put_user(ibb_sp->clibb_id, (short *)arg)) {
dcmn_err(1, ("<1>" "ibb_ioctl(%d): can't copy CLIBB ID
%d\n",
ibb_sp->dev_num,ibb_sp->clibb_id)) ;

retval = -EFAULT;
}

dcmn_err(1, ("<1>" "ibb_ioctl(%d): Copied CLIBB IDD %d\n",
ibb_sp->dev_num,ibb_sp->clibb_id));

break;

case IBBHEIGHTGO:
if (ibb_sp->clibb_id != kClibb_HSC) {
dcmn_err(1, ("<1>" "ibb_ioctl(%d): ERROR: No Height
Sensor\n",
ibb_sp->dev_num) );

return(-EFAULT);
}

ibb_sp->height_inspection = 1;

dcmn_err(1, ("<1>" "ibb_ioctl(%d): Start Height Inspection
%d\n",
ibb_sp->dev_num,ibb_sp->height_inspection));

break;

case IBBHEIGHTSTOP:
ibb_sp->height_inspection = 0;

dcmn_err(1, ("<1>" "ibb_ioctl(%d): Stop Height Inspection
%d\n",
ibb_sp->dev_num,ibb_sp->height_inspection));

break;

default:
dcmn_err(1,("<1>" "ibb_ioctl(%d): unknown case: %d\n",
ibb_sp->dev_num, cmd) );

return(-ENOTTY);
break;
}

return(retval);
}

int
ibb_open(struct inode *inode, struct file *filep)
{
int dev_num;
IbbSoftDev *ibb_sp;

int major = MAJOR(inode->i_rdev);
int minor = MINOR(inode->i_rdev);

dcmn_err(40, ("<1>" "ibb_open: major %d minor %d\n",
major, minor));

dev_num = find_ibb_soft_state(major);

if (dev_num < 0) {
dcmn_err(0, ("<1>" "ibb_open: dev_num %d failed\n",
dev_num));

return(-1);
}

ibb_sp = IbbSoft[dev_num];
filep->private_data = ibb_sp;

dcmn_err(40, ("<1>" "ibb_open(%d): opened by %s pid %d PAGE_SIZE
%#010lx\n",
dev_num,current->comm, current->pid, PAGE_SIZE));

return(0);
}

int
ibb_close(struct inode *inode, struct file *filep)
{
IbbSoftDev *ibb_sp = filep->private_data;

dcmn_err(40, ("<1>" "ibb_close(%d): called\n",
ibb_sp->dev_num));

return(0);
}

static void
free_ibb_usr_shared(IbbSoftDev *ibb_sp)
{
struct page *page;

u_long virt_addr;
u_long ushared_addr = (u_long)ibb_sp->ushared;

for (virt_addr = ushared_addr;
virt_addr < ushared_addr + PAGE_ALIGN(IBB_SHARED_SIZE);
virt_addr += PAGE_SIZE)
{
page = virt_to_page(virt_addr);
ClearPageReserved(page);

atomic_set(&page->_count, 1);
vfree((void *)virt_addr);
}

if (ibb_sp->ushared)
ibb_sp->ushared = NULL;
}

static int
alloc_ibb_usr_shared(IbbSoftDev *ibb_sp)
{
u_long virt_addr;
u_long ushared_addr;
int order = 0;

/*
* From Linux Device Drivers - memory that is going to
* be mmaped must be PAGE_SIZE grained. Since we do mmap
* ushared to user space, we need to allocated it in
* PAGE_SIZE chunks.
*/

int size = PAGE_ALIGN(IBB_SHARED_SIZE);

dcmn_err(1, ("<1>" "ibb::size is %d\n", size));
dcmn_err(1, ("<1>" "ibb::order is %d\n", order));

do {
dcmn_err(1, ("<1>" "ibb::size is %d\n", size));
dcmn_err(1, ("<1>" "ibb::order is %d\n", order));

order++;
} while (size > (PAGE_SIZE * (1 << order)));

// ibb_sp->ushared = (IbbUserShared *) __get_free_pages(GFP_KERNEL,
order);
ibb_sp->ushared = (IbbUserShared *) vmalloc(4096 * 1024);

if (ibb_sp->ushared == NULL) {
dcmn_err(1, ("<1>" "alloc_ibb_usr_shared(%d): out of memory.\n",
ibb_sp->dev_num));

return(-1);
}

ushared_addr = (u_long) ibb_sp->ushared;

/* reserve all pages to make them remapable */
for (virt_addr = ushared_addr;
virt_addr < ushared_addr + size;
virt_addr += PAGE_SIZE)
{
SetPageReserved(virt_to_page(virt_addr));
}

ibb_sp->ushared->badsnap = 0;
ibb_sp->ushared->image_table_index = 0;

return(0);
}

static void
free_ibb_image_table_mem(IbbSoftDev *ibb_sp)
{
uint32_t *virt_addr;
struct page *page;

dcmn_err(1, ("<1>" "free_ibb_image_table_mem(%d): Free size %d.\n",
ibb_sp->dev_num,ibb_sp->image_table_size));

/* unreserve all pages */
for( virt_addr = ibb_sp->image_table;
virt_addr < ibb_sp->image_table + ibb_sp->image_table_size;
virt_addr += PAGE_SIZE)
{
page = virt_to_page(virt_addr);
ClearPageReserved(page);

atomic_set(&page->_count, 1);
free_page((void *)virt_addr);
}

if (ibb_sp->image_table) {
ibb_sp->image_table = NULL;
}

ibb_sp->image_table_size = 0;
return;
}

static int
alloc_ibb_image_table_mem(IbbSoftDev *ibb_sp, int size)
{
int order = 0;
uint32_t *virt_addr;

u_int table_size = PAGE_ALIGN(size);

dcmn_err(1, ("<1>" "alloc_ibb_image_table_mem(%d): Alloc size
%d.\n",
ibb_sp->dev_num,table_size) );

/*
* get a memory area with kmalloc and aligned it to a page. This
area
* will be physically contigous
*/

while (size > (PAGE_SIZE * (1 << order))) {
order++;
}

// ibb_sp->image_table = (uint32_t *) __get_free_pages(GFP_KERNEL,
order);
ibb_sp->ushared = (IbbUserShared *) vmalloc(4096 * 1024);

if (ibb_sp->image_table == NULL) {
dcmn_err(1, ("<1>" "alloc_ibb_image_table_mem(%d): out of
memory.\n",
ibb_sp->dev_num));

ibb_sp->image_table_size = 0;
return(-1);
}

/* reserve all pages to make them remapable */
for (virt_addr = ibb_sp->image_table;
virt_addr < ibb_sp->image_table + table_size;
virt_addr += PAGE_SIZE)
{
SetPageReserved(virt_to_page(virt_addr));
}

ibb_sp->image_table_size = table_size;
return(0);
}

static int
ibb_map_one(IbbSoftDev *ibb_sp, u_long phys, u_long size, uint32_t
**virt, const char *what)
{
struct resource *res;

if ((res = request_mem_region(phys, size, ibb_sp->devname)) == NULL)
{
dcmn_err(0, ("<1>"
"ibb_map_one(%d): can't allocate %s at %010lx %#010lx %s\n",
ibb_sp->dev_num,what,phys,size,ibb_sp->devname));

return(-1);
}
else {
*virt = ioremap_nocache(phys, size);

if (!virt) {
dcmn_err(0, ( "<1>" "ibb_map_one(%d): Error in ioremap\n",
ibb_sp->dev_num));

release_mem_region(phys, size);
return(-1);
}
}

dcmn_err(40, ( "<1>" "ibb_map_one(%d): Successful map %s\n",
ibb_sp->dev_num,what));

return(0);
}

static void
free_all_mappings(IbbSoftDev *ibb_sp)
{
if (ibb_sp->creg_virt != NULL) {
iounmap(ibb_sp->creg_virt);
ibb_sp->creg_virt = NULL;
}

/* we can call release_mem_region() without checking anything -
* if the region hasn't been requested, the function will
* just exit with an error message.
* (unlike iounmap())
*/

release_mem_region(ibb_sp->creg_phys, ibb_sp->creg_size);
if (ibb_sp->height_virt != NULL) {
iounmap(ibb_sp->height_virt);
ibb_sp->height_virt = NULL;
}

release_mem_region(ibb_sp->height_phys, ibb_sp->height_size);
if (ibb_sp->csram_virt != NULL) {
iounmap(ibb_sp->csram_virt);
ibb_sp->csram_virt = NULL;
}

release_mem_region(ibb_sp->csram_phys, ibb_sp->csram_size);
if (ibb_sp->rsram_virt != NULL) {
iounmap(ibb_sp->rsram_virt);
ibb_sp->rsram_virt = NULL;
}

release_mem_region(ibb_sp->rsram_phys, ibb_sp->rsram_size);
if (ibb_sp->fbac_virt != NULL) {
iounmap(ibb_sp->fbac_virt);
ibb_sp->fbac_virt = NULL;
}

release_mem_region(ibb_sp->fbac_phys, ibb_sp->fbac_size);
if (ibb_sp->camdelay_virt != NULL) {
iounmap(ibb_sp->camdelay_virt);
ibb_sp->camdelay_virt = NULL;
}

release_mem_region(ibb_sp->camdelay_phys, ibb_sp->camdelay_size);
if (ibb_sp->fsize_virt != NULL) {
iounmap(ibb_sp->fsize_virt);
ibb_sp->fsize_virt = NULL;
}

release_mem_region(ibb_sp->fsize_phys, ibb_sp->fsize_size);
if (ibb_sp->extime_virt != NULL) {
iounmap(ibb_sp->extime_virt);
ibb_sp->extime_virt = NULL;
}

release_mem_region(ibb_sp->extime_phys, ibb_sp->extime_size);
if (ibb_sp->plsram_virt != NULL) {
iounmap(ibb_sp->plsram_virt);
ibb_sp->plsram_virt = NULL;
}

release_mem_region(ibb_sp->plsram_phys, ibb_sp->plsram_size);
if (ibb_sp->fb0_virt != NULL) {
iounmap(ibb_sp->fb0_virt);
ibb_sp->fb0_virt = NULL;
}

release_mem_region(ibb_sp->fb0_phys, ibb_sp->fb0_size);
if (ibb_sp->fb1_virt != NULL) {
iounmap(ibb_sp->fb1_virt);
ibb_sp->fb1_virt = NULL;
}

release_mem_region(ibb_sp->fb1_phys, ibb_sp->fb1_size);
if (ibb_sp->image_table != NULL) {
free_ibb_image_table_mem(ibb_sp);
}

if (ibb_sp->ushared != NULL) {
free_ibb_usr_shared(ibb_sp);
}

return;
}

static int
ibb_map_frame_buffer(IbbSoftDev *ibb_sp, u_long phys, ulong
*size,uint32_t **virt, const char *what)
{
char whatsize[100];
uint32_t *check_addr;

long check;

sprintf(whatsize,"%s 64", what);
*size = IBB_FB_64_MG;

if (ibb_map_one(ibb_sp, phys, *size, virt, whatsize) < 0) {
return(-1);
}

check = MAG_FB_NUMBER;
check_addr = *virt;
writel(check, check_addr);

dcmn_err(45, ("<1>" "ibb_map_frame_buffer(%d): %s put %lX at addr
%p\n",
ibb_sp->dev_num, whatsize, check, check_addr));

check = readl(check_addr);

dcmn_err(45, ("<1>" "ibb_map_frame_buffer(%d): %s read %lX at addr
%p\n",
ibb_sp->dev_num, whatsize, check, check_addr));

check_addr = (*virt) + (IBB_FB_32_MG / 4);
check = readl(check_addr);

dcmn_err(45, ("<1>" "ibb_map_frame_buffer(%d): %s read %lX at addr
%p\n",
ibb_sp->dev_num, whatsize, check, check_addr));

if (check == MAG_FB_NUMBER) {
/* we only have 32 MB, but we've mapped 64 - try again */
iounmap(*virt);
(*virt) = NULL;
release_mem_region(phys, *size);

sprintf(whatsize, "%s 32", what);
*size = IBB_FB_32_MG;

if (ibb_map_one(ibb_sp, phys, *size, virt, whatsize) < 0) {
return(-1);
}

ishared.frame_buffer_size = IBB_FB_32_MG;
}

return(0);
}

static int
ibb_do_all_mappings(IbbSoftDev *ibb_sp)
{
/*
* Initialize here so we can call free_all_mappings without
* freeing unallocated mem
*/

ibb_sp->creg_virt = NULL;
ibb_sp->height_virt = NULL;
ibb_sp->csram_virt = NULL;
ibb_sp->rsram_virt = NULL;
ibb_sp->fbac_virt = NULL;
ibb_sp->camdelay_virt = NULL;
ibb_sp->fsize_virt = NULL;
ibb_sp->extime_virt = NULL;
ibb_sp->plsram_virt = NULL;
ibb_sp->fb0_virt = NULL;
ibb_sp->fb1_virt = NULL;
ibb_sp->image_table = NULL;
ibb_sp->image_table_size = 0;

/* Map Control Register */

ibb_sp->creg_phys = ibb_sp->iobase + IBB_CONTROL_OFF;
ibb_sp->creg_size = IBB_CONTROL_SIZE;

if (ibb_map_one(ibb_sp,
ibb_sp->creg_phys,
ibb_sp->creg_size,
&ibb_sp->creg_virt,
"Control Register") < 0)
{
return(-1);
}

/* Map Height Sensor Register */

ibb_sp->height_phys = ibb_sp->iobase + IBB_HEIGHT_OFF;
ibb_sp->height_size = IBB_HEIGHT_SIZE;

if (ibb_map_one(ibb_sp,
ibb_sp->height_phys,
ibb_sp->height_size,
&ibb_sp->height_virt,
"Height Sensor") < 0)
{
return(-1);
}

/* Map Col Sram */

ibb_sp->csram_phys = ibb_sp->iobase + IBB_CSRAM_OFF;
ibb_sp->csram_size = IBB_CSRAM_SIZE;

if (ibb_map_one(ibb_sp,
ibb_sp->csram_phys,
ibb_sp->csram_size,
&ibb_sp->csram_virt,
"Col Sram") < 0)
{
free_all_mappings(ibb_sp);
return(-1);
}

/* Map Row Sram */

ibb_sp->rsram_phys = ibb_sp->iobase + IBB_RSRAM_OFF;
ibb_sp->rsram_size = IBB_RSRAM_SIZE;

if (ibb_map_one(ibb_sp,
ibb_sp->rsram_phys,
ibb_sp->rsram_size,
&ibb_sp->rsram_virt,
"Row Sram") < 0)
{
free_all_mappings(ibb_sp);
return(-1);
}

/* map FB Address Counter */

ibb_sp->fbac_phys = ibb_sp->iobase + IBB_FBACOUNT_OFF;
ibb_sp->fbac_size = IBB_FBACOUNT_SIZE;

if (ibb_map_one(ibb_sp,
ibb_sp->fbac_phys,
ibb_sp->fbac_size,
&ibb_sp->fbac_virt,
"FB Addr Count") < 0)
{
free_all_mappings(ibb_sp);
return(-1);
}

/* map Camera Delay Register */

ibb_sp->camdelay_phys = ibb_sp->iobase + IBB_CAMDELAY_OFF;
ibb_sp->camdelay_size = IBB_CAMDELAY_SIZE;

if (ibb_map_one(ibb_sp,
ibb_sp->camdelay_phys,
ibb_sp->camdelay_size,
&ibb_sp->camdelay_virt,
"Cameral Delay Reg") < 0)
{
free_all_mappings(ibb_sp);
return(-1);
}

/* map Frame Size Register */

ibb_sp->fsize_phys = ibb_sp->iobase + IBB_FSIZE_OFF;
ibb_sp->fsize_size = IBB_FSIZE_SIZE;

if (ibb_map_one(ibb_sp,
ibb_sp->fsize_phys,
ibb_sp->fsize_size,
&ibb_sp->fsize_virt,
"Frame Size Reg") < 0)
{
free_all_mappings(ibb_sp);
return(-1);
}

/* map Exposure Time Register */

ibb_sp->extime_phys = ibb_sp->iobase + IBB_EXTIME_OFF;
ibb_sp->extime_size = IBB_EXTIME_SIZE;

if (ibb_map_one(ibb_sp,
ibb_sp->extime_phys,
ibb_sp->extime_size,
&ibb_sp->extime_virt,
"Exposure Time Reg") < 0)
{
free_all_mappings(ibb_sp);
return(-1);
}

/* map Pixel LUT Sram */

ibb_sp->plsram_phys = ibb_sp->iobase + IBB_PLSRAM_OFF;
ibb_sp->plsram_size = IBB_PLSRAM_SIZE;

if (ibb_map_one(ibb_sp,
ibb_sp->plsram_phys,
ibb_sp->plsram_size,
&ibb_sp->plsram_virt,
"Pixel LUT Sram") < 0)
{
free_all_mappings(ibb_sp);
return(-1);
}

/* map Frame Buffers - now it gets a little trickier */

ibb_sp->fb0_phys = ibb_sp->iobase + IBB_FB0_OFF;
ibb_sp->fb0_size = 0;

if (ibb_map_frame_buffer(ibb_sp,
ibb_sp->fb0_phys,
&ibb_sp->fb0_size,
&ibb_sp->fb0_virt,
"Frame Buffer 0") < 0)
{
free_all_mappings(ibb_sp);
return(-1);
}

dcmn_err(40, ("<1>" "ibb_do_all_mappings(%d): fb0 Mapped Size
%lx\n",
ibb_sp->dev_num,ibb_sp->fb0_size));

ibb_sp->fb1_phys = ibb_sp->iobase + IBB_FB1_OFF;
ibb_sp->fb1_size = 0;

if (ibb_map_frame_buffer(ibb_sp,
ibb_sp->fb1_phys,
&ibb_sp->fb1_size,
&ibb_sp->fb1_virt,
"Frame Buffer 1") < 0)
{
free_all_mappings(ibb_sp);
return(-1);
}

dcmn_err(40, ("<1>" "ibb_do_all_mappings(%d): fb1 Mapped Size
%lx\n",
ibb_sp->dev_num,ibb_sp->fb1_size));

#ifdef IMAGETABLEALLOC
/* two pages of memory should always be enough ? */

ibb_sp->image_table_mem = NULL;

if (alloc_ibb_image_table_mem(ibb_sp, PAGE_SIZE * 2) < 0) {
free_all_mappings(ibb_sp);
return(-1);
}

ibb_sp->image_table_size = PAGE_SIZE * 2;

dcmn_err(40,
("<1>" "ibb_do_all_mappings(%d): Image Table Mapped Size %lx\n",
ibb_sp->dev_num, PAGE_SIZE * 2));
#endif

if (alloc_ibb_usr_shared(ibb_sp) < 0) {
free_all_mappings(ibb_sp);
return(-1);
}

dcmn_err(40, ("<1>" "ibb_do_all_mappings(%d): IbbUserShared %lx\n",
ibb_sp->dev_num, (long)IBB_SHARED_SIZE));

return(0);
}

int
ibb_mmap(struct file *filep, struct vm_area_struct *vma)
{
IbbSoftDev *ibb_sp = filep->private_data;

u_long offset = vma->vm_pgoff << PAGE_SHIFT;
u_long vsize = vma->vm_end - vma->vm_start;

if (offset > __pa(high_memory) || (filep->f_flags & O_SYNC)) {
vma->vm_flags |= VM_IO;
}

vma->vm_flags |= VM_RESERVED;

dcmn_err(90, ("<1>"
"mmap(%d): st %#010lx off %#010lx(%#010lx) sz %#010lx (end
%#010lx)\n",
ibb_sp->dev_num, vma->vm_start, ibb_sp->iobase + offset,
offset, vsize, vma->vm_end));

dcmn_err(30, ("<1>" "ibb_mmap(%d): opened by %s pid %d\n",
ibb_sp->dev_num, current->comm, current->pid));

if (offset == IBB_IMAGE_ADDR) {
if (ibb_sp->image_table != NULL && vsize >
ibb_sp->image_table_size) {
/* release the current memory and allocate new memory *
below */
dcmn_err(1, ("<1>" "ibb_mmap(%d): Free Image Table\n",
ibb_sp->dev_num));

free_ibb_image_table_mem(ibb_sp);
}

if (ibb_sp->image_table == NULL) {
dcmn_err(1, ("<1>" "ibb_mmap(%d): Alloc New Image Table\n",
ibb_sp->dev_num) );

alloc_ibb_image_table_mem(ibb_sp, vsize);
}

if (ibb_sp->image_table != NULL && vsize <=
ibb_sp->image_table_size) {
const u_long start = vma->vm_start;
const u_long size = (vma->vm_end - vma->vm_start);
const u_long page = (void *)ibb_sp->image_table;

if (io_remap_pfn_range(vma, start, page, size,
vma->vm_page_prot)) {
dcmn_err(1, ("<1>" "ibb_mmap(%d): image table remap
failed\n",
ibb_sp->dev_num));

return(-EAGAIN);
}
}

else {
dcmn_err(1, ("<1>"
"ibb_mmap(%d): image table params failed vs %d is %d\n",
ibb_sp->dev_num, (int)vsize,
(int)ibb_sp->image_table_size));

return(-EAGAIN);
}
}
else if (offset == IBB_SHARED_ADDR) {
if (ibb_sp->ushared != NULL && vsize <=
PAGE_ALIGN(IBB_SHARED_SIZE)) {
const u_long start = vma->vm_start;
const u_long size = (vma->vm_end - vma->vm_start);
const u_long page = (void *)ibb_sp->ushared;

if (io_remap_pfn_range(vma, start, page, size,
vma->vm_page_prot)) {
dcmn_err(1, ("<1>" "ibb_mmap(%d): ushared remap
failed\n",
ibb_sp->dev_num));

return(-EAGAIN);
}

dcmn_err(1, ("<1>" "ibb_mmap(%d): \
ushared remap: ibb_sp->ushared: %lx vma->vm_start:
%lx\n",
ibb_sp->dev_num,
(u_long)ibb_sp->ushared,
(u_long)vma->vm_start) );
}
else {
dcmn_err(1, ("<1>" "ibb_mmap(%d): ushared mmap failed\n",
ibb_sp->dev_num));

return(-EAGAIN);
}
}

/*
* vsize is PAGE_SIZE at minimum. For registers and other
* small memory areas we need to check for VSIZE. For large
* chunks, vsize should equal the chunk size
*/

else if (offset == IBB_CONTROL_OFF && vsize == PAGE_SIZE) {
const u_long start = vma->vm_start;
const u_long size = (vma->vm_end - vma->vm_start);
const u_long page = ibb_sp->creg_phys;

if (remap_pfn_range(vma, start, page, size, vma->vm_page_prot))
{
return -EAGAIN;
}
}

else if (offset == IBB_CSRAM_OFF && vsize == IBB_CSRAM_SIZE) {
const u_long start = vma->vm_start;
const u_long size = (vma->vm_end - vma->vm_start);
const u_long page = ibb_sp->csram_phys;

if (remap_pfn_range(vma, start, page, size, vma->vm_page_prot))
{
return(-EAGAIN);
}
}

else if (offset == IBB_RSRAM_OFF && vsize == IBB_RSRAM_SIZE) {
const u_long start = vma->vm_start;
const u_long size = (vma->vm_end - vma->vm_start);
const u_long page = ibb_sp->rsram_phys;

if (remap_pfn_range(vma, start, page, size, vma->vm_page_prot))
{
return(-EAGAIN);
}
}

else if (offset == IBB_FBACOUNT_OFF && vsize == PAGE_SIZE) {
const u_long start = vma->vm_start;
const u_long size = (vma->vm_end - vma->vm_start);
const u_long page = ibb_sp->fbac_phys;

if (remap_pfn_range(vma, start, page, size, vma->vm_page_prot))
{
return(-EAGAIN);
}
}

else if (offset == IBB_CAMDELAY_OFF && vsize == PAGE_SIZE) {
const u_long start = vma->vm_start;
const u_long size = (vma->vm_end - vma->vm_start);
const u_long page = ibb_sp->camdelay_phys;

if (remap_pfn_range(vma, start, page, size, vma->vm_page_prot))
{
return(-EAGAIN);
}
}

else if (offset == IBB_FSIZE_OFF && vsize == PAGE_SIZE) {
const u_long start = vma->vm_start;
const u_long size = (vma->vm_end - vma->vm_start);
const u_long page = ibb_sp->fsize_phys;

if (remap_pfn_range(vma, start, page, size, vma->vm_page_prot))
{
return(-EAGAIN);
}
}

else if (offset == IBB_EXTIME_OFF && vsize == PAGE_SIZE) {
const u_long start = vma->vm_start;
const u_long size = (vma->vm_end - vma->vm_start);
const u_long page = ibb_sp->extime_phys;

if (remap_pfn_range(vma, start, page, size, vma->vm_page_prot))
{
return(-EAGAIN);
}
}

else if (offset == IBB_PLSRAM_OFF && vsize == IBB_PLSRAM_SIZE) {
dcmn_err(0, ("<1>" "ibb_mmap(%d): PLUT Sram %lx %lx\n",
ibb_sp->dev_num,
(long)offset,
(long)vsize));

const u_long start = vma->vm_start;
const u_long size = (vma->vm_end - vma->vm_start);
const u_long page = ibb_sp->plsram_phys;

if (remap_pfn_range(vma, start, page, size, vma->vm_page_prot))
{
return(-EAGAIN);
}
}

else if (offset == IBB_FB0_OFF
&& (vsize == IBB_FB_32_MG
|| vsize == IBB_FB_64_MG))
{
const u_long start = vma->vm_start;
const u_long size = (vma->vm_end - vma->vm_start);
const u_long page = ibb_sp->fb0_phys;

if (remap_pfn_range(vma, start, page, size, vma->vm_page_prot))
{
return(-EAGAIN);
}
}

else if (offset == IBB_FB1_OFF
&& (vsize == IBB_FB_32_MG
|| vsize == IBB_FB_64_MG))
{
const u_long start = vma->vm_start;
const u_long size = (vma->vm_end - vma->vm_start);
const u_long page = ibb_sp->fb1_phys;

if (remap_pfn_range(vma, start, page, size, vma->vm_page_prot))
{
return(-EAGAIN);
}
}

else {
return(-EAGAIN);
}

return(0);
}

struct file_operations ibb_fops = {
read: ibb_read,
write: ibb_write,
ioctl: ibb_ioctl,
mmap: ibb_mmap,
open: ibb_open,
release: ibb_close,
};

#ifdef MISCLOAD
struct miscdevice ibb_misc_device = {
-1,
"ibb",
&ibb_fops
};
#endif

static int __initdata
ibb_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
{
IbbSoftDev *ibb_sp;

int i = 0;
int dev_num;
int ibb_major;
int result;
int res;

u8 rev_id = 0;
u16 device_id;
uint16_t subsystem_id = 0;

char revid = ' ';

dcmn_err(40, ("<1>" "ibb_probe: enter\n"))

if (pci_enable_device(pdev)) {
return(-ENODEV);
}

result = 0;

/* stealing code from cciss.c - thanks compaq! */

dcmn_err(0, ("<1>"
"ibb_probe: IBB Device 0x%08x has been found @bus %d dev %d func
%d\n",
pdev->device,
pdev->bus->number,
PCI_SLOT(pdev->devfn),
PCI_FUNC(pdev->devfn)));

/* alloc our per-device data */

dev_num = alloc_ibb_soft_state();

if (dev_num < 0) {
/* I've already printk'd error message */
return(-ENOMEM);
}

/* initialize data */

memset(IbbSoft[dev_num], 0, sizeof(IbbSoft[dev_num]));
ibb_sp = IbbSoft[dev_num];

sprintf(ibb_sp->devname, "ibb%d", dev_num);

spin_lock_init(&ibb_sp->mutex);

ibb_sp->dev_num = dev_num;
ibb_sp->pdev = pdev;
ibb_sp->height_inspection = 0;

sema_init(&ibb_sp->sem, 1);
pci_set_drvdata(pdev, ibb_sp); /* we'll need this in remove: */

dcmn_err(40, ( "<1>" "ibb_probe: device %d\n", dev_num));

#define REQUESTMEM
#ifdef REQUESTMEM

for (i = 0; i < kMaxIbbs; i++) {
ishared.dev_instance[i] = -1;
}

/* start with the max size */
ishared.frame_buffer_size = IBB_FB_64_MG;

/*
* map the card memory areas into kernel space
* and store the address in our IbbDev info
*/

ibb_sp->iobase = pci_resource_start(pdev, 0);
ibb_sp->iosize = pci_resource_len(pdev, 0);

if (ibb_do_all_mappings(ibb_sp) < 0) {
free_ibb_soft_state(ibb_sp->dev_num);
return(-ENODEV);
}

#endif

#ifdef CONFIG_DEVFS_FS
ibb_sp->ibb_devfs_dir = devfs_mk_dir(NULL, ibb_sp->devname, NULL);
if (!ibb_sp->ibb_devfs_dir) {
dcmn_err(0, ("<1>" "ibb_probe(%d): can't register devfs\n",
dev_num));

free_all_mappings(ibb_sp);
free_ibb_soft_state(ibb_sp->dev_num);

return(-EBUSY);
}

devfs_register(ibb_sp->ibb_devfs_dir,
ibb_sp->devname,
DEVFS_FL_AUTO_DEVNUM, /* autoallocate
major/minor */
0, /* ignored because of
flag */
0, /* ignored because of
flag */
S_IFCHR | S_IRUGO | S_IWUGO, /* ??? */
&ibb_fops,
ibb_sp);

#else // ifdef CONFIG_DEVFS_FS

ibb_major = 0;
result = register_chrdev(ibb_major, ibb_sp->devname, &ibb_fops);

if (result < 0) {
dcmn_err(0, ( "<1>" "ibb_probe(%d): can't get register driver\n"
,dev_num));

free_all_mappings(ibb_sp);
free_ibb_soft_state(ibb_sp->dev_num);

return(-EBUSY);
}

if (ibb_major == 0) {
ibb_major = result; /* dynamic */
}

dcmn_err(30, ( "<1>" "ibb_probe(%d): got major
%d\n",dev_num,ibb_major));

#endif // ifdef CONFIG_DEVFS_FS

ibb_sp->ibb_major = ibb_major;

pci_read_config_word(pdev, 2, &device_id);
pci_read_config_byte(pdev, 8, &rev_id);
pci_read_config_word(pdev, 46, &subsystem_id);

ibb_sp->clibb_id = subsystem_id;

if (rev_id > 0 && rev_id < 27) {
rev_id += 0x40; /* translate to ascii (A = 1, B = 2,
etc) */
revid = rev_id;
}

if (!pdev->irq) {
dcmn_err(0, ("<1>" "ibb_probe_module(%d): can't get interrupt
number\n",
dev_num));

free_all_mappings(ibb_sp);
free_ibb_soft_state(ibb_sp->dev_num);

return(-ENODEV);
}

ibb_sp->myint = pdev->irq;
res = request_irq(ibb_sp->myint,
ibb_intr,
SA_SHIRQ,ibb_sp->devname,
ibb_sp);

if (res != 0) {
dcmn_err(0, ("<1>" "ibb_open(%d): irq request %d failed\n",
dev_num,res) );

return(-1);
}

init_waitqueue_head(&ibb_sp->wq);

if (ibb_sp->clibb_id == kIbbId) {
dcmn_err(0, ("<1>" "MVP Image Buffer Board: ibb%d,%X.%c, Int:
%d\n",
dev_num,
device_id,
revid,
ibb_sp->myint));
}

else if (ibb_sp->clibb_id == kClibbSingle) {
dcmn_err(0, ("<1>" "MVP Camera Link Single IBB: ibb%d,%X.%c,
Int: %d\n",
dev_num,
device_id,
revid,
ibb_sp->myint));
}

else if (ibb_sp->clibb_id == kClibbDualRow) {
dcmn_err(0, ("<1>" "MVP Camera Link DualRow IBB: ibb%d,%X.%c,
Int: %d\n"
,dev_num,
device_id,
revid,
ibb_sp->myint));
}

else if (ibb_sp->clibb_id == kClibbDualCol) {
dcmn_err(0, ("<1>" "MVP CL DualCol IBB ibb%d, %X.%c, Int: %d\n",
dev_num,
device_id,
revid,
ibb_sp->myint));
}

else if (ibb_sp->clibb_id == kClibb_HSC) {
dcmn_err(0, ("<1>" "MVP HSC Camera Link IBB: ibb%d, %X.%c, Int:
%d\n",
dev_num,
device_id,
revid,
ibb_sp->myint));
}

else {
dcmn_err(0, ("<1>" "MVP Unknown IBB: ibb%d, %X.%c, Int: %d\n",
dev_num,
device_id,
revid,
ibb_sp->myint));
}

/* store device id */
dcmn_err(0, ("<1>" "MVP driver(%d) %s\n",
dev_num,
ibb_c));

return(result);
}

static void __exitdata
ibb_remove(struct pci_dev *pdev)
{
IbbSoftDev *ibb_sp = (IbbSoftDev *)pci_get_drvdata(pdev);

if (ibb_sp == NULL) {
dcmn_err(0, ("<1>" "ibb_remove: Error: Null IBB Data in
remove\n"));
return;
}

dcmn_err(40, ( "<1>" "ibb_remove(%d):\n", ibb_sp->dev_num));

#ifdef CONFIG_DEVFS_FS
dcmn_err(40, ( "<1>" "ibb_remove(%d): unregister dev %s\n",
ibb_sp->dev_num,
ibb_sp->devname));

devfs_unregister(ibb_sp->ibb_devfs_dir);
#else
dcmn_err(40, ( "<1>" "ibb_remove(%d): unregister drv %s\n",
ibb_sp->dev_num,
ibb_sp->devname));

if (ibb_sp) {
free_irq(ibb_sp->myint, (void *)ibb_sp);
dcmn_err(0, ("<1>" "ibb_remove(%d): free_irq myint%d\n",
ibb_sp->dev_num,
ibb_sp->myint));

ibb_sp->myint = 0;
}

unregister_chrdev(ibb_sp->ibb_major, ibb_sp->devname);
#endif

dcmn_err(40, ( "<1>" "ibb_remove(%d): release memory\n",
ibb_sp->dev_num));

free_all_mappings(ibb_sp);
free_ibb_soft_state(ibb_sp->dev_num);
}


static struct pci_device_id ibb_id_table[] __devinitdata = {
{ 0x8f73, 0xb1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ 0x8f73, 0xb2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ 0x8f73, 0xb3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ 0x8f73, 0xcb1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ 0x8f73, 0xcb2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }
};

/*
* have _no idea_ why I'm calling this - find out!
*
* from /usr/inluclude/linux/module.h
* MODULE_DEVICE_TABLE exports information about devices
* currently supported by this module. A device type, such as PCI,
* is a C-like identifier passed as the first arg to this macro.
* The second macro arg is the variable containing the device
* information being made public.
*
* The following is a list of known device types (arg 1),
* and the C types which are to be passed as arg 2.
* pci - struct pci_device_id - List of PCI ids supported by this module
* isapnp - struct isapnp_device_id -
* List of ISA PnP ids supported by this module
* usb - struct usb_device_id - List of USB ids supported by this module
*
* still - ok so it exports it, who imports it and what do they
* do with it?
*/

MODULE_DEVICE_TABLE(pci, ibb_id_table);

struct pci_driver ibb_driver = {
name: "ibb",
id_table: ibb_id_table,
probe: ibb_probe,
remove: ibb_remove,
};

int __init
ibb_init_module(void)
{
dcmn_err(0, ("<1>" "ibb_init_module: Start\n"))

init_ibb_soft_state();

return(pci_module_init(&ibb_driver));
}

void __exit
ibb_cleanup_module(void)
{
dcmn_err(0, ("<1>" "ibb_cleanup_module: End\n"))
pci_unregister_driver(&ibb_driver);
}

static inline void
set_next_fb(uint32_t *control_regp, uint32_t next_snap, int instance,
int temp_debug)
{
uint32_t next_fb = (next_snap & IAT_FB_MASK) >> IAT_FB_SHIFT;

if (next_fb == 0) {
*(control_regp) = *(control_regp) | ICR_FB0_ENABL;
*(control_regp) = *(control_regp) & ~ICR_FB1_ENABL;
}
else if (next_fb == 1) {
*(control_regp) = *(control_regp) | ICR_FB1_ENABL;
*(control_regp) = *(control_regp) & ~ICR_FB0_ENABL;
}

dcmn_err(temp_debug, ("<1>" "ibb_intr(%d): set fb %d %x\n",
instance,
next_fb,
*(control_regp)));
}

static inline void
set_next_camera(uint32_t *control_regp, uint32_t next_snap, int
instance, int temp_debug)
{
uint32_t next_camera;

/* first, clear the previous camera settings */
*(control_regp) &= ICR_CLEARCAM;

next_camera = (next_snap & IAT_CAMERA_MASK) >> IAT_CAMERA_SHIFT;

if (next_camera < 0 || next_camera > 7) {
return;
}

*(control_regp) |= (next_camera << ICR_CAMERA_SHIFT);

dcmn_err(temp_debug, ("<1>" "ibb_intr(%d): set camera %d reg %x\n",
instance,
next_camera,
*(control_regp)) );
}

static inline void
set_next_address(IbbSoftDev *ibb_p, uint32_t ibb_control_reg, uint32_t
next_snap, int instance, int temp_debug)
{
uint32_t next_address = (next_snap & IAT_ADDR_MASK);

dcmn_err(temp_debug, ("<1>"
"ibb_intr(%d): got address 0x%x set 0x%x\n",
instance,
*(ibb_p->fbac_virt),
next_address));

*(ibb_p->fbac_virt) = next_address;
}

static inline void
check_intr_delay(IbbSoftDev *ibb_p)
{
#ifdef TESTCAMDELAY
hrtime_t snap_ndelay;
hrtime_t ibb_ntimestamp;

/* this may be useful but only in fly mode */
ibb_ntimestamp = gethrtime();

dcmn_err(10, ("<1>" "ibb_intr: ibbtime %llu\n",ibb_ntimestamp /
1000000));
dcmn_err(10, ("<1>" "ibb_intr: rtctime %llu\n",
ishared.rtc_ntimestamp));

if (ibb_ntimestamp < ishared.rtc_ntimestamp) {
dcmn_err(1, ("<1>" "ibb_intr: Bad value %llu for rtc intr\n",
ishared.rtc_ntimestamp));
}

snap_ndelay = ibb_ntimestamp - ishared.rtc_ntimestamp;

if (snap_ndelay < kMinSnapDelay) {
dcmn_err(1, ("<1>" "ibb_intr: grab snap delay %lld too short\n",
snap_ndelay / 1000000));

ibb_p->ushared->badsnap = kIbbShortSnap;
}

else if (snap_ndelay > kMaxSnapDelay) {
dcmn_err(1, ("<1>" "ibb_intr: grab snap delay %lld too long\n",
snap_ndelay / 1000000));

ibb_p->ushared->badsnap = kIbbLongSnap;
}
#endif

return;
}


irqreturn_t
ibb_intr(int irq, void *dev_id, struct pt_regs *regs)
{
IbbSoftDev *ibb_sp = dev_id;

uint32_t creg_val;
uint32_t intr_set;
uint32_t last_snap;
uint32_t next_snap;

int instance;
int temp_debug = 100;

u_long flags;

if (ibb_sp == NULL) {
/* WTF??? */
dcmn_err(1, ("<1>" "ERROR: ibb_intr: ibb_sp = NULL\n"));
return(IRQ_NONE);
}

lock_ibb(ibb_sp, &flags, "ibb_intr");
instance = ibb_sp->dev_num;
creg_val = *ibb_sp->creg_virt;

if (!(creg_val & ICR_INTR_PENDING)) {
dcmn_err(1, ("<1>" "ibb_intr(%d): Not mine\n",
ibb_sp->dev_num));

unlock_ibb(ibb_sp, &flags, "ibb_intr Error");
return(IRQ_NONE);
}

if (creg_val & ICR_CAM_FAIL) {
dcmn_err(1, ("<1>" "ibb_intr(%d): Camera
Failure\n",ibb_sp->dev_num));
}

/* RESET INTR set ICR_INTR_CLEAR hi and remove software intrs*/

intr_set =
(ICR_INTR_CLEAR | creg_val | ICR_CLR_CAM_FAIL)
& ~(ICR_SOFT_IMAGE | ICR_SIM_FIRE);

*ibb_sp->creg_virt = intr_set;

if (ibb_sp->ushared != NULL && ibb_sp->image_table != NULL) {
ibb_sp->ushared->badsnap = 0;
last_snap =
ibb_sp->image_table[ibb_sp->ushared->image_table_index];

if (ibb_sp->height_inspection) {
uint32_t *address_start;
uint32_t *last_frame;

uint32_t height_data;
uint32_t last_address;

uint32_t last_fb = (last_snap & IAT_FB_MASK) >>
IAT_FB_SHIFT;

if (last_fb == 0) {
address_start = ibb_sp->fb0_virt;
}
else {
address_start = ibb_sp->fb1_virt;
}

last_address = last_snap & IAT_ADDR_MASK;
last_frame = address_start + last_address;

height_data = *(ibb_sp->height_virt);
height_data <<= 16;

*(last_frame) = height_data;
}

last_snap = last_snap | IAT_SNAP_DONE;
ibb_sp->image_table[ibb_sp->ushared->image_table_index] =
last_snap;

ibb_sp->ushared->image_table_index++;

dcmn_err(55, ("<1>" "ibb_intr(%d): set index %d\n",
ibb_sp->dev_num,
ibb_sp->ushared->image_table_index));

next_snap =
ibb_sp->image_table[ibb_sp->ushared->image_table_index];

dcmn_err(55, ("<1>" "ibb_intr(%d): got next entry %d
%x\n",instance,
ibb_sp->ushared->image_table_index,
next_snap));

creg_val = *ibb_sp->creg_virt;
dcmn_err(55, ("<1>" "ibb_intr(%d): set %x intr %x\n",
ibb_sp->dev_num,
intr_set,
creg_val));

set_next_fb(ibb_sp->creg_virt, next_snap, instance, temp_debug);
set_next_camera(ibb_sp->creg_virt, next_snap, instance,
temp_debug);
set_next_address(ibb_sp, creg_val, next_snap, instance,
temp_debug);

dcmn_err(55, ("<1>" "ibb_intr(%d): fbacount 0x%x\n",
instance,
*(ibb_sp->fbac_virt)));

if (ibb_sp->flags & kIoWaiting) {
dcmn_err(55, ("<1>" "ibb_intr(%d): waiting for ind %d got
%d\n",
instance,
ibb_sp->wait_for,
ibb_sp->ushared->image_table_index));

if (ibb_sp->ushared->image_table_index > ibb_sp->wait_for) {
dcmn_err(55, ("<1>" "ibb_intr(%d): wake up wait
queue\n",
ibb_sp->dev_num));

wake_up_interruptible(&ibb_sp->wq);
}
}

check_intr_delay(ibb_sp);
}

barrier();

*ibb_sp->creg_virt =
(~(ICR_INTR_CLEAR|ICR_CLR_CAM_FAIL) & *ibb_sp->creg_virt);

unlock_ibb(ibb_sp, &flags, "ibb_intr");

return(IRQ_HANDLED);
}

void
ibb_rtc_wakeup(void)
{
int i;
u_long flags;

for (i = 0; i < kMaxIbbs; i++) {
if (IbbSoft[i] != NULL) {
lock_ibb(IbbSoft[i], &flags, "ibb_rtc_wakeup");

IbbSoft[i]->wait_for = kIbbWakeup;
IbbSoft[i]->ushared->image_table_index =
IbbSoft[i]->wait_for + 1;

unlock_ibb(IbbSoft[i], &flags, "ibb_rtc_wakeup");
wake_up_interruptible(&IbbSoft[i]->wq);
}
}
}

module_init(ibb_init_module);
module_exit(ibb_cleanup_module);

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/