#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef __SIM_INT_MODE
#define __SIM_INT_MODE
#endif
#ifndef MODULE
#define MODULE
#endif
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <linux/fcntl.h>
#include <linux/pci.h>
#include <linux/timer.h>
#include <linux/poll.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/time.h>
#include "cpci50401.h"
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("ustb.");
MODULE_DESCRIPTION("CPCI50401B Dev Driver");
#ifndef CONFIG_PCI
# error "This driver needs PCI support to be available"
#endif
EXPORT_NO_SYMBOLS;
int cpci50401b_major = 247;
static int drv_file_opened = 0;
static unsigned long cfg_base_address, mem_base0_address,mem_base1_address;
int cpci50401b_irq;
struct CPCI50401_IO_MEM cpci50401b_io;
static struct pci_dev *cpci50401b_dev;
static struct fasync_struct *async_queue = NULL;
static int intr_enb = 0;
#define TIME_FILTER 0
static unsigned long newtime = 0;
static unsigned long oldtime = 0;
static unsigned long delta = 0;
static void enable_pci_int(void)
{
int val;
val = readw((unsigned)cpci50401b_io.int_enb);
rmb();
val = (val | Lint1_ENB | Lint1_HIGH | Lint1_EDGE | Lint2_ENB | Lint2_HIGH | Lint2_EDGE | Lint1_CLRINT | Lint2_CLRINT | PCI_ENB);
writew(val, (unsigned)cpci50401b_io.int_enb);
wmb();
}
static void disable_pci_int(void)
{
int val;
val = readw((unsigned)cpci50401b_io.int_enb);
rmb();
val = val & ~PCI_ENB;
writew(val, (unsigned)cpci50401b_io.int_enb);
wmb();
}
#ifdef __SIM_INT_MODE
#define VECQLEN 1024
static struct vectorqueue {
int vec[VECQLEN];
int front;
int rear;
} vecq;
static int add_vecq(int vec){
int tmprear = (vecq.rear + 1) % VECQLEN;
if(tmprear == vecq.front)
return -1;
vecq.vec[vecq.rear] = vec;
vecq.rear = (vecq.rear + 1) % VECQLEN;
/*if(vecq.rear - vecq.front > 10)
printk("<1>cpci50401b: geu interrupt overspeed!\r\n");*/
return 0;
}
static int del_vecq(unsigned long *vec){
*vec = vecq.vec[vecq.front];
vecq.front = (vecq.front + 1) % VECQLEN;
return 0;
}
static int is_consuming = 1;
static int co = 0;
void cpci50401b_interrupt_handler(int irq, void *dev_id, struct pt_regs *regs)
{
int get;
int ret=0;
get = readw((unsigned)cpci50401b_io.int_enb);
rmb();
if(get & Lint1_ACT) // local 1
{
// printk("<1>Get Local1 Int : 0x%x\n",get);
// int data,chnl;
int i,j;
for(i = 0;i<500;i++)
for(j=0;j<100;j++);
// data = readw((unsigned)cpci50401b_io.baseaddr0);
// rmb();
// chnl = readw((unsigned)cpci50401b_io.baseaddr1);
// rmb();
// if(co++>1000)
// {
// printk("channel %d data : 0x%x\n",chnl,data);
// co = 0;
// }
ret = add_vecq(0130);
// printk("<1>cpci50401b: vector = 130\n");
if(ret < 0){ /* intr queue full */
is_consuming = 0;
vecq.front = vecq.rear = 0;
/* interrupt disable */
// disable_pci_int();
// printk("<1>cpci50401b: vector queue length is too short!\n");
}
/* clear local1 intr */
get |= 0x400;
writew(get, (unsigned)cpci50401b_io.int_enb);
wmb();
}
if(get & Lint2_ACT) // local 2
{
//printk("<1>Get Local2 Int : 0x%x\n",get);
#if TIME_FILTER
struct timeval tv;
static int sum = 0;
do_gettimeofday(&tv);
newtime = tv.tv_sec * 1000 * 1000 + tv.tv_usec;
if(oldtime != 0){
delta = newtime - oldtime;
if((delta > 15000) && (delta < 18000)){
if (async_queue)
kill_fasync(&async_queue, SIGIO, POLL_IN);
}else{
sum += delta;
if((sum > 15000) && (sum < 18000)){
if (async_queue)
kill_fasync(&async_queue, SIGIO, POLL_IN);
sum = 0;
}
}
}
oldtime = newtime;
#else
// if (async_queue)
// kill_fasync(&async_queue, SIGIO, POLL_IN);
#endif
get |= 0x800;
writew(get, (unsigned)cpci50401b_io.int_enb);
wmb();
}
}
#endif
struct pci_dev *
cpci50401_find_second_dev(unsigned int vendor,unsigned int device,unsigned int devfn)
{
struct pci_dev *dev;
pci_for_each_dev(dev) {
printk("<1>info:%x,%x,%X\n",dev->vendor,dev->device,dev->devfn);
if((dev->vendor == vendor)
&&(dev->device == device)
&&(dev->devfn == DRIVER_NO_B))
return dev;
}
return NULL;
}
int find_cpci50401b_device(unsigned int vendor, unsigned int device)
{
cpci50401b_dev = NULL;
if (!pci_present())
return -ENODEV;
cpci50401b_dev = cpci50401_find_second_dev(vendor,device,DRIVER_NO_B);
if (!cpci50401b_dev)
return -ENODEV;
printk("devfn:0x%x\n",cpci50401b_dev->devfn);
pci_enable_device(cpci50401b_dev);
return 0;
}
int allocate_cpci50401b_resource(void)
{
int result;
unsigned char irq_no;
cfg_base_address = pci_resource_start(cpci50401b_dev, CONFIG_BASE);
mem_base0_address = pci_resource_start(cpci50401b_dev, MEM_BASE0);
mem_base1_address = pci_resource_start(cpci50401b_dev, MEM_BASE1);
// printk("<1> cpci50401b: config base address: %x\n", cfg_base_address);
// printk("<1> cpci50401b: io base0 address: %x\n", mem_base0_address);
// printk("<1> cpci50401b: io base1 address: %x\n", mem_base1_address);
/* find the IRQ number assigned by the kernel */
result = pci_read_config_byte(cpci50401b_dev, PCI_INTERRUPT_LINE, &irq_no);
// printk("<1> cpci50401b: IRQ no: %d\n", irq_no);
if (result)
{
printk("<1>cpci50401b: read pci interrupt number error!\n");
return -EBUSY;
}
cpci50401b_irq = irq_no;
// Map I/O Address Space
if (check_mem_region(mem_base0_address, MEM_LEN))
{
printk("<1>cpci50401b: io0 space already in use!\n");
return -EBUSY;
}
request_mem_region(mem_base0_address, MEM_LEN, CPCI50401_B);
cpci50401b_io.baseaddr0 = (void *)ioremap(mem_base0_address,MEM_LEN);
if (check_mem_region(mem_base1_address, MEM_LEN))
{
printk("<1>cpci50401b: io1 space already in use!\n");
release_mem_region(mem_base0_address, MEM_LEN);
return -EBUSY;
}
request_mem_region(mem_base1_address, MEM_LEN, CPCI50401_B);
cpci50401b_io.baseaddr1 = (void *)ioremap(mem_base1_address,MEM_LEN);
// Map Configuration Address Space
if (check_mem_region(cfg_base_address, CONFIG_LEN))
{
printk("<1>cpci50401b: configuration space already in use!\n");
release_mem_region(mem_base0_address, MEM_LEN);
release_mem_region(mem_base1_address, MEM_LEN);
return -EBUSY;
}
request_mem_region(cfg_base_address, CONFIG_LEN, CPCI50401_B);
cpci50401b_io.int_enb = (void *)(ioremap(cfg_base_address,CONFIG_LEN) + 0x4c);
#ifdef __SIM_INT_MODE
/* register interrupt handler */
/* interrupt enable: enable PCI intr & local intr1 & local intr2 */
enable_pci_int();
vecq.front = vecq.rear = 0;
result = request_irq(cpci50401b_irq, cpci50401b_interrupt_handler, \
SA_SHIRQ, CPCI50401_B, cpci50401b_dev);
if(result)
{
printk("<1> cpci50401b: cannot install interrupt handler!\n");
release_mem_region(mem_base0_address, MEM_LEN);
release_mem_region(mem_base1_address, MEM_LEN);
release_mem_region(cfg_base_address, CONFIG_LEN);
return -EBUSY;
}
/* interrupt enable: enable PCI intr & local intr1 & local intr2 */
// enable_pci_int();
#else
/* interrupt enable: enable local intr1 & local intr2 */
// writel(0xf1b, cpci50401b_io.int_enb);
// wmb();
#endif
return 0;
}
int release_cpci50401b_resource(void)
{
release_mem_region(mem_base0_address, MEM_LEN);
release_mem_region(mem_base1_address, MEM_LEN);
/* interrupt disable */
disable_pci_int();
release_mem_region(cfg_base_address, CONFIG_LEN);
#ifdef __SIM_INT_MODE
free_irq(cpci50401b_irq, NULL);
#endif
return 0;
}
int cpci50401b_open(struct inode *inode, struct file *fp)
{
int ret;
if (drv_file_opened != 0)
{
printk("<1>cpci50401b: Driver file can only be opened for 1 program!\n");
return -ENODEV;
}
drv_file_opened = 1;
// printk("<1>cpci50401b: Driver file opened!\n");
ret = find_cpci50401b_device(VENDOR_ID, DEVICE_ID);
if (ret < 0)
{
printk("<1>cpci50401b: find device failure!\n");
return -ENODEV;
}
ret = allocate_cpci50401b_resource();
if (ret < 0)
{
printk("<1>cpci50401b: allocate resou