几年前正式转到linux开发岗位的时候,由于项目急需编写linux驱动来控制项目采集设备(板卡),我便被安排做这一部分工作。那时候挺慌的…,在之前的一年多时间里基本都是window应用开发,对于linux理解也相对较少。还好那时候认识一些别的公司的大佬,给指点了一二,便开始“模仿造车之路”…
记得那时候领导给了一个月调研时间,希望在一个月内能搞明白板卡的驱动采用哪种方式实现,需要购买什么书籍。
那时候心里虽没有底…,但我也想完成这次挑战,就随便购买了一本《Linux设备驱动程序(中文版第三版)》,事后只能说这本书真香…
好了,现在我们进入今天的主题:
通常情况下,“应用程序”通过“内核驱动”来操作“硬件设备”,比如我们操作一块2A板卡,我们需要在应用程序中编写2A板卡的功能模块,然后通过驱动程序中映射出的虚拟地址写入这些信息,最终实现操作硬件设备(基于操作系统层面)。下面我们分析一下这个例子:
假如我们的2A板卡功能有开启工作模式、关闭工作模式、开始采集数据、停止采集数据、获取采集状态, 地址方面为0x20(采集状态地址)、0x64(开启工作模式、关闭工作模式)、0x128(开始采集数据、停止采集数据),采集的数据量为一次2048字节(每次采集完成后即可开启下次采集)。
通过这个设备信息可以分析出,功能较为简单,数据交互量也比较少,这种情况可以直接选择“字符驱动”。字符驱动较与别的驱动类型来说更简单、直接一些,适合用在各种“非触摸式”并且数据交互量较少的场景。
接下来,我们在思路上模拟实现这个设备的驱动与应用:
驱动思路:
1. 首先我们得到这个设备的厂商ID和设备ID,注册为pci驱动,并实现探测函数(.probe)和卸载函数(.remove)。
2. 执行探测函数并创建字符驱动和描述符(应用层通过它来访问驱动),然后映射bar(最多可以映射6个bar(6个不同的地址空间),相当于把设备物理地址映射到内核虚拟地址,通常情况下只有2至3个bar,bar0一般属于只读空间)。
字符驱动主要通过ioctl访问映射的bar空间完成具体操作,如开启工作模式,8位的情况下可通过pci_write_config_byte(bkptr->pcie_dev, addr, (u8 )&val);或者*(unsigned char*)(bkptr->bar[1] + addr) = val;
应用思路:
1. 首先通过驱动描述符打开设备。
2. 通过ioctl函数访问设备地址,根据业务需要执行相关的操作,如开启工作模式,ioctl(fd_A2, 0x64, data)),data1表示开启,0表示关闭;
嗯…,字符驱动就是这么简单、直接,基本通过ioctl就可解决大部分问题,如果你的需求稍微复杂一些(带DMA芯片或echo等操作),则会用到write和read函数。
驱动示例:
.h
/*
* Pci_A.h
*
* Created on: Sep 24, 2017
* Author: linux
*/
#ifndef PCI__H_
#define PCI__H_
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/cdev.h>
#include <linux/version.h>
#include <linux/vmalloc.h>
#include <linux/ctype.h>
#include <linux/pagemap.h>
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/pci.h>
#include <linux/device.h>
#undef PDEBUG
#ifdef PCI_DEBUG
#ifdef __KERNEL__
#define PCI_DEBUG(fmt,args...) printk(KERN_DEBUG "PCI:" fmt,##args)
#else
#define PCI_DEBUG(fmt,args...) printf(stderr, "PCI:" fmt,##args)
#endif
#else
#define PCI_DEBUG(fmt,args...)
#endif
#define DMA_BAR_NUM (6)
#define DMA_DESCRIPTOR_NUM 128
struct Pci_dev
{
struct cdev cdev;
};
struct pcie_dma_bookkeep {
struct pci_dev *pci_dev;
u8 revision;
u8 irq_pin;
char msi_enabled;
u8 irq_line;
unsigned long bus;
unsigned long deviceNo;
unsigned long devfn;
char dma_capable;
void * __iomem bar[DMA_BAR_NUM];
size_t bar_length[DMA_BAR_NUM];
struct pci_dev *pcie_dev;
struct dma_desc_table *table_rd_cpu_virt_addr;
struct dma_desc_table *table_wr_cpu_virt_addr;
struct lite_dma_desc_table *lite_table_rd_cpu_virt_addr;
struct lite_dma_desc_table *lite_table_wr_cpu_virt_addr;
dma_addr_t lite_table_rd_bus_addr;
dma_addr_t table_rd_bus_addr;
dma_addr_t lite_table_wr_bus_addr;
dma_addr_t table_wr_bus_addr;
int numpages;
u8 *rp_rd_buffer_virt_addr;
dma_addr_t rp_rd_buffer_bus_addr;
u8 *rp_wr_buffer_virt_addr;
dma_addr_t rp_wr_buffer_bus_addr;
dev_t cdevno;
struct cdev cdev;
int user_pid;
struct task_struct *user_task;
wait_queue_head_t wait_q;
atomic_t status;
// struct dma_status dma_status;
u32 prevalue;
};
static int Pci_open(struct inode *inode,struct file *filp);
static int Pci_release(struct inode *inode,struct file *filp);
static ssize_t Pci_read(struct file *filp,char __user *buf,size_t count,loff_t *f_pos);
static ssize_t Pci_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_pos);
static loff_t Pci_llseek(struct file *filp,loff_t off,int whence);
static int Pci_ioctl(/*struct inode *inode,*/struct file *filp,unsigned int cmd,unsigned long arg);
static int __init pci_probe(struct pci_dev *dev, const struct pci_device_id *id);
static init_chrdev(struct pcie_dma_bookkeep *bk_ptr);
static void unmap_bars(struct pcie_dma_bookkeep *bk_ptr, struct pci_dev *dev);
static int __init map_bars(struct pcie_dma_bookkeep *bk_ptr, struct pci_dev *dev);
static int scan_bars(struct pcie_dma_bookkeep *bk_ptr, struct pci_dev *dev);
static int map_Pci(struct pcie_dma_bookkeep *bk_ptr, struct pci_dev *dev);
#endif /* PCI__H_ */
.c
/*
* Pci_A.c
*
* Created on: Sep 24, 2017
* Author: linux
*/
#include "Pci_A.h"
#define A_MANF_ID 0xAAAA /* Instrument manufacturer ID FOR AMC*/
#define A_MODEL_CODE 0xaaaa /* Instrument model code for switch */
#define BUFFER_SIZE 512L /* File I/O buffer size */
#define ADEVNAME "Pci_A"
#define ClassName "class_A"
#define DEMO_MAJOR 0
#define DEMO_MINOR 0
#define KER_RW_R8 0
#define KER_RW_R16 1
#define KER_RW_R32 6
#define KER_RW_W8 3
#define KER_RW_W16 4
#define KER_RW_W32 5
#define KER_RW_revision 7
#define KER_RW_irq_pin 8
#define KER_RW_irq_line 9
#define KER_RW_bar 20
#define KER_RW_Config_R8 30
#define KER_RW_Config_R16 31
#define KER_RW_Config_R32 32
#define KER_RW_Config_W8 33
#define KER_RW_Config_W16 34
#define KER_RW_Config_W32 35
#define KER_RW_VX_R8 40
#define KER_RW_VX_R16 41
#define KER_RW_VX_R32 42
#define KER_RW_VX_W8 43
#define KER_RW_VX_W16 44
#define KER_RW_VX_W32 45
#define KER_R_bus 50
#define KER_R_deviceNo 51
#define KER_R_devfn 52
#define DBUG_PRINT 1
struct class *mem_class;
struct Pci_dev *demo_devices;
struct pcie_dma_bookkeep *bkptr;
static unsigned char demo_inc = 0;//Up to a Dev
static unsigned char demo_buffer[512L];
static unsigned int bufferLen = 512L;
//dev_t dev;
int npci_register;
static int Pci_open(struct inode *inode,struct file *filp)
{
struct pcie_dma_bookkeep *dev_;
dev_ = container_of(inode->i_cdev,struct pcie_dma_bookkeep,cdev);
filp->private_data = dev_;
//dev->user_pid = current->pid;
return 0;
}
static int Pci_release(struct inode *inode,struct file *filp)
{
return 0;
}
static ssize_t Pci_read(struct file *filp,char __user *buf,size_t count,loff_t *f_pos)
{
int result;
loff_t pos = *f_pos; // pos :offset
if (pos >= bufferLen)
{
result = 0;
goto out;
}
if (count > (bufferLen - pos))
{
count = bufferLen - pos;
}
pos += count;
if (copy_to_user(buf,demo_buffer + *f_pos,count))
{
count = -2;
goto out;
}
*f_pos = pos;
out:
return count;
}
static ssize_t Pci_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_pos)
{
size_t retval = -3;
loff_t pos = *f_pos;
if (pos > bufferLen)
{
goto out;
}
if (count > (bufferLen - pos))
{
count = bufferLen - pos;
}
pos += count;
if (copy_from_user(demo_buffer + *f_pos,buf,count))
{
retval = -2;
goto out;
}
*f_pos = pos;
retval = count;
out:
return retval;
}
static int Pci_ioctl(/*struct inode *inode,*/struct file *filp,unsigned int cmd,unsigned long arg)
{//reg
volatile unsigned char p8[1];
volatile unsigned short p16[1];
volatile unsigned int p32[1];
unsigned int val;//write reg to val
unsigned int addr;
unsigned int buf[2];
//unsigned short nbar = cmd >> 16;
//unsigned short ncmd = (unsigned short)cmd;
unsigned short nbar = ((cmd >> 16) & 0xFFFF);
unsigned short ncmd = (unsigned short)(cmd & 0x0000FFFF);
/*user to copy into the kernel*/
copy_from_user(buf,(const void __user *)arg,8);
//length 8
addr = buf[0];//addr
val = buf[1];//val
static unsigned long nNum = 0;
if(DBUG_PRINT)printk(KERN_ALERT "Pci_ioctl:addr:%u , val:%u ,bar:%u ,type:%u ,Num:%u ,cmd:%u ,arg:%x \n",addr,val,nbar,ncmd,++nNum,cmd,arg);
switch (ncmd)
{
case KER_RW_R8:
{
//val = ioread8(bkptr->bar[nbar] + addr);
val = *(unsigned char*)(bkptr->bar[nbar] + addr);//2017-11-2
if(DBUG_PRINT)printk(KERN_ALERT "ioread8 :addr: %u , bar from addr:%u ,bar:%u ,val:%u \n",addr,(unsigned long)bkptr->bar[nbar],nbar,val);
copy_to_user((void __user*)(arg+4),&val,4);
//copy 4 bytes to user space ,The back four is value,In front of the 4 bytes in address
break;
}
case KER_RW_R16:
{
//val = ioread16(bkptr->bar[nbar] + addr);
val = *(unsigned short*)(bkptr->bar[nbar] + addr);//2017-11-2
if(DBUG_PRINT)printk(KERN_ALERT "ioread16 :addr: %u , bar from addr:%u ,bar:%u ,val:%u \n",addr,(unsigned long)bkptr->bar[nbar],nbar,val);
copy_to_user((void __user*)(arg+4),&val,4);
break;
}
case KER_RW_R32:
{
//val = ioread32(bkptr->bar[nbar] + addr);
val = *(unsigned long*)(bkptr->bar[nbar] + addr);//2017-11-2
if(DBUG_PRINT)printk(KERN_ALERT "ioread32 :addr: %u , bar from addr:%u ,bar:%u ,val:%u \n",addr,(unsigned long)bkptr->bar[nbar],nbar,val);
copy_to_user((void __user*)(arg+4),&val,4);
break;
}
case KER_RW_W8:
{
//*p8 = val;
if(DBUG_PRINT)printk(KERN_ALERT "iowrite8 :addr: %u , bar from addr:%u ,bar:%u ,val:%u \n",addr,(unsigned long)bkptr->bar[nbar],nbar,val);
//iowrite8(bkptr->bar[nbar] + addr,(unsigned char)val);
*(unsigned char*)(bkptr->bar[nbar] + addr) = val;
break;
}
case KER_RW_W16:
{
//*p16 = val;
if(DBUG_PRINT)printk(KERN_ALERT "iowrite8 :addr: %u , bar from addr:%u ,bar:%u ,val:%u \n",addr,(unsigned long)bkptr->bar[nbar],nbar,val);
//iowrite16(bkptr->bar[nbar] + addr,(unsigned short)val);
*(unsigned short*)(bkptr->bar[nbar] + addr) = val;
break;
}
case KER_RW_W32:
{
//*p32 = val;
if(DBUG_PRINT)printk(KERN_ALERT "iowrite8 :addr: %u , bar from addr:%u ,bar:%u ,val:%u \n",addr,(unsigned long)bkptr->bar[nbar],nbar,val);
//iowrite32(bkptr->bar[nbar] + addr,(unsigned long)val);
*(unsigned long*)(bkptr->bar[nbar] + addr) = val;
break;
}
case KER_RW_revision:
{ val = bkptr->revision;
copy_to_user((void __user*)(arg+4),&val,4);
break;
}
case KER_RW_irq_pin:
{ val = bkptr->irq_pin;
copy_to_user((void __user*)(arg+4),&val,4);
break;
}
case KER_RW_irq_line:
{ val = bkptr->irq_line;
copy_to_user((void __user*)(arg+4),&val,4);
break;
}
case KER_RW_bar:
{ val = (unsigned long)bkptr->bar[nbar];
copy_to_user((void __user*)(arg+4),&val,4);
break;
}
case KER_RW_Config_R8:
{
pci_read_config_byte(bkptr->pcie_dev,addr, (u8 *)&val);
if(DBUG_PRINT)printk(KERN_ALERT "KER_RW_Config_R8 , pcie_dev:%u ,addr:%u ,val:%u \n",(unsigned long)bkptr->pcie_dev,addr,val);
copy_to_user((void __user*)(arg+4),&val,4);
break;
}
case KER_RW_Config_R16:
{
pci_read_config_word(bkptr->pcie_dev,addr, (u16 *)&val);
if(DBUG_PRINT)printk(KERN_ALERT "KER_RW_Config_R16 , pcie_dev:%u ,addr:%u ,val:%u \n",(unsigned long)bkptr->pcie_dev,addr,val);
copy_to_user((void __user*)(arg+4),&val,4);
break;
}
case KER_RW_Config_R32:
{
pci_read_config_dword(bkptr->pcie_dev,addr, (u32 *)&val);
if(DBUG_PRINT)printk(KERN_ALERT "KER_RW_Config_R32 , pcie_dev:%u ,addr:%u ,val:%u \n",(unsigned long)bkptr->pcie_dev,addr,val);
copy_to_user((void __user*)(arg+4),&val,4);
break;
}
case KER_RW_Config_W8:
{
pci_write_config_byte(bkptr->pcie_dev,addr, (u8 )&val);
if(DBUG_PRINT)printk(KERN_ALERT "KER_RW_Config_W8 , pcie_dev:%u ,addr:%u ,val:%u \n",(unsigned long)bkptr->pcie_dev,addr,val);
break;
}
case KER_RW_Config_W16:
{
pci_write_config_word(bkptr->pcie_dev,addr, (u16 )&val);
if(DBUG_PRINT)printk(KERN_ALERT "KER_RW_Config_W16 , pcie_dev:%u ,addr:%u ,val:%u \n",(unsigned long)bkptr->pcie_dev,addr,val);
break;
}
case KER_RW_Config_W32:
{
pci_write_config_dword(bkptr->pcie_dev,addr, (u32)&val);
if(DBUG_PRINT)printk(KERN_ALERT "KER_RW_Config_W32 , pcie_dev:%u ,addr:%u ,val:%u \n",(unsigned long)bkptr->pcie_dev,addr,val);
break;
}
case KER_RW_VX_R8:
{
unsigned char * p8 = (volatile unsigned char *) ioremap(addr,4);
//val = ioread8(p8);
val = *p8;
if(DBUG_PRINT)printk(KERN_ALERT "KER_RW_VX_R8 :addr: %u,val:%u \n",addr,val);
copy_to_user((void __user*)(arg+4),&val,4);
//copy 4 bytes to user space ,The back four is value,In front of the 4 bytes in address
iounmap(p8);
break;
}
case KER_RW_VX_R16:
{
unsigned short * p16 = (volatile unsigned short *) ioremap(addr,4);//test
//val = ioread16(p16);
val = *p16;
if(DBUG_PRINT)printk(KERN_ALERT "KER_RW_VX_R16 :addr: %u,val:%u \n",addr,val);
copy_to_user((void __user*)(arg+4),&val,4);
iounmap(p16);//exit map*/
break;
}
case KER_RW_VX_R32:
{
unsigned long * p32 = (volatile unsigned long *) ioremap(addr,4);
//val = ioread32(p32);
val = *p32;
if(DBUG_PRINT)printk(KERN_ALERT "KER_RW_VX_R32 :addr: %u,val:%u \n",addr,val);
copy_to_user((void __user*)(arg+4),&val,4);
iounmap(p32);
break;
}
case KER_RW_VX_W8:
{
unsigned char * p8 = (volatile unsigned char *) ioremap(addr,4);
if(DBUG_PRINT)printk(KERN_ALERT "KER_RW_VX_W8 :addr: %u,val:%u \n",p8,(unsigned char)val);
//iowrite8(p8,(unsigned char)val);
*p8 = val;
iounmap(p8);
break;
}
case KER_RW_VX_W16:
{
unsigned short * p16 = (volatile unsigned short *) ioremap(addr,4);
if(DBUG_PRINT)printk(KERN_ALERT "KER_RW_VX_W16 :addr: %u,val:%u \n",p16,(unsigned short)val);
*p16 = val;
iounmap(p16);//exit map*/
break;
}
case KER_RW_VX_W32:
{
unsigned long * p32 = (volatile unsigned long *) ioremap(addr,4);
if(DBUG_PRINT)printk(KERN_ALERT "KER_RW_VX_W32 :addr: %u,val:%u \n",p32,(unsigned int)val);
*p32 = val;
iounmap(p32);
break;
}
case KER_R_bus:
{
val = bkptr->bus;
copy_to_user((void __user*)(arg+4),&val,4);
break;
}
case KER_R_deviceNo:
{
val = bkptr->deviceNo;
copy_to_user((void __user*)(arg+4),&val,4);
break;
}
case KER_R_devfn:
{
val = bkptr->devfn;
copy_to_user((void __user*)(arg+4),&val,4);
break;
}
}
return 0;
}
static loff_t Pci_llseek(struct file *filp,loff_t off,int whence)
{
loff_t pos;
pos = filp->f_pos;
switch (whence)
{
case 0:
pos = off;
break;
case 1:
pos += off;
break;
case 2:
default:
return -2;
}
if ((pos > 512) || (pos < 0))
return -2;
return filp->f_pos = pos;
}
static struct file_operations demo_fops = {
.owner = THIS_MODULE,
.llseek = Pci_llseek,
.read = Pci_read,
.write = Pci_write,
.unlocked_ioctl = Pci_ioctl,
.open = Pci_open,
.release = Pci_release,
};
static int scan_bars(struct pcie_dma_bookkeep *bk_ptr, struct pci_dev *dev)
{
int i;
for (i = 0; i < DMA_BAR_NUM; i++) {
unsigned long bar_start = pci_resource_start(dev, i);
unsigned long bar_end = pci_resource_end(dev, i);
unsigned long bar_flags = pci_resource_flags(dev, i);
bk_ptr->bar_length[i] = pci_resource_len(dev, i);
dev_info(&dev->dev, "BAR[%d] 0x%08lx-0x%08lx flags 0x%08lx, length %d", i, bar_start, bar_end, bar_flags, (int)bk_ptr->bar_length[i]);
}
return 0;
}
static int __init map_bars(struct pcie_dma_bookkeep *bk_ptr, struct pci_dev *dev)
{
int i;
for (i = 0; i < DMA_BAR_NUM; i++) {
unsigned long bar_start = pci_resource_start(dev, i);
bk_ptr->bar_length[i] = pci_resource_len(dev, i);
if (!bk_ptr->bar_length[i]) {
bk_ptr->bar[i] = NULL;
continue;
}
bk_ptr->bar[i] = ioremap(bar_start, bk_ptr->bar_length[i]);
if (!bk_ptr->bar[i]) {
dev_err(&dev->dev, "could not map BAR[%d]", i);
return -1;
} else
dev_info(&dev->dev, "BAR[%d] mapped to 0x%p, length %lu", i, bk_ptr->bar[i], (long unsigned int)bk_ptr->bar_length[i]);
}
return 0;
}
static void unmap_bars(struct pcie_dma_bookkeep *bk_ptr, struct pci_dev *dev)
{
int i;
for (i = 0; i < DMA_BAR_NUM; i++) {
if (bk_ptr->bar[i]) {
pci_iounmap(dev, bk_ptr->bar[i]);
bk_ptr->bar[i] = NULL;
}
}
}
static init_chrdev(struct pcie_dma_bookkeep *bk_ptr)
{
static nRun = 0;
int result;
if(DBUG_PRINT)printk(KERN_ALERT "init_chrdev nRun:%d \n",nRun);
if (0 == nRun)
{
bk_ptr->cdevno = 0;
result = alloc_chrdev_region(&bk_ptr->cdevno, 0, 2, ADEVNAME);
if(DBUG_PRINT)printk(KERN_ALERT "mem_class = class_create(THIS_MODULE,\"class_\"); \n");
mem_class = class_create(THIS_MODULE,ClassName);// /dev/ create devfile
if (IS_ERR(mem_class))
{
printk(KERN_ALERT "Err:failed in creating class!\n");
goto fail;
}
if(DBUG_PRINT)printk(KERN_ALERT "device_create(mem_class,NULL,dev,NULL,ADEVNAME); \n");
device_create(mem_class,NULL,bk_ptr->cdevno,NULL,ADEVNAME);
if (result < 0)
{
printk(KERN_WARNING "can't get major %d\n",DEMO_MAJOR);
return result;
}
cdev_init(&bk_ptr->cdev,&demo_fops);
bk_ptr->cdev.owner = THIS_MODULE;
bk_ptr->cdev.ops = &demo_fops;//Create Dev and file_operations Connected
if(DBUG_PRINT)printk(KERN_ALERT "result = cdev_add(&bk_ptr->cdev,dev,1);\n");
result = cdev_add(&bk_ptr->cdev,bk_ptr->cdevno,1);
if (result){
printk(KERN_ALERT "error%d adding demo\n",result);
goto fail;
}
++nRun;
return result;
fail:{
if(DBUG_PRINT)printk(KERN_ALERT "init_chrdev :fail result:%d ~!\n",result);
if (bk_ptr)
{
cdev_del(&bk_ptr->cdev);
kfree(demo_devices);
}
unregister_chrdev_region(bk_ptr->cdevno,1);
}
return result;
}
return 0;
}
static int __init pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
if(DBUG_PRINT)printk(KERN_ALERT "pci_probe :begin !\n");
int rc = 0;
unsigned int last_id=0;
pci_set_drvdata(dev, bkptr);
if(DBUG_PRINT)printk(KERN_ALERT "init_chrdev :begin !\n");
rc = init_chrdev(bkptr);
if(DBUG_PRINT)printk(KERN_ALERT "init_chrdev :end !\n");
if (rc) {
dev_err(&dev->dev, "init_chrdev() failed\n");
goto err_initchrdev;
}
rc = pci_enable_device(dev);
if (rc) {
dev_err(&dev->dev, "pci_enable_device() failed\n");
goto err_enable;
} else {
dev_info(&dev->dev, "pci_enable_device() successful");
}
rc = pci_request_regions(dev, ADEVNAME);
if (rc) {
dev_err(&dev->dev, "pci_request_regions() failed\n");
goto err_regions;
}
pci_set_master(dev);
rc = pci_enable_msi(dev);
if (rc) {
dev_info(&dev->dev, "pci_enable_msi() failed\n");
bkptr->msi_enabled = 0;
} else {
dev_info(&dev->dev, "pci_enable_msi() successful\n");
bkptr->msi_enabled = 1;
}
pci_read_config_byte(dev, PCI_REVISION_ID, (u8 *)&bkptr->revision);
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, (u8 *)&bkptr->irq_pin);
pci_read_config_byte(dev, PCI_INTERRUPT_LINE, (u8 *)&bkptr->irq_line);
if(DBUG_PRINT)printk(KERN_ALERT "PCI_REVISION_ID: %d , PCI_INTERRUPT_PIN:%d ,PCI_INTERRUPT_LINE:%d \n",bkptr->revision,bkptr->irq_pin,bkptr->irq_line);
dev_info(&dev->dev, "irq pin: %d\n", bkptr->irq_pin);
dev_info(&dev->dev, "irq line: %d\n", bkptr->irq_line);
dev_info(&dev->dev, "irq: %d\n", dev->irq);
bkptr->bus = dev->bus->number;
bkptr->deviceNo = (((dev->devfn) >> 3) & 0x1f);
bkptr->devfn = ((dev->devfn) & 0x07);
dev_info(&dev->dev, "pci devfn: %u\n", bkptr->devfn);
dev_info(&dev->dev, "pci bus: %u\n", bkptr->bus);
dev_info(&dev->dev, "pci device: %u\n", bkptr->deviceNo);
rc = 0;
if (rc) {
dev_info(&dev->dev, "Could not request IRQ #%d", bkptr->irq_line);
bkptr->irq_line = -1;
goto err_irq;
} else {
dev_info(&dev->dev, "request irq: %d", bkptr->irq_line);
}
scan_bars(bkptr, dev);
map_bars(bkptr, dev);
//dev
bkptr->pcie_dev = dev;
return 0;
// error clean up
err_wr_buffer:
dev_err(&dev->dev, "goto err_wr_buffer");
//pci_free_consistent(dev, PAGE_SIZE*bk_ptr->numpages, bk_ptr->rp_rd_buffer_virt_addr, bk_ptr->rp_rd_buffer_bus_addr);
err_rd_buffer:
dev_err(&dev->dev, "goto err_rd_buffer");
//pci_free_consistent(dev, sizeof(struct dma_desc_table), bk_ptr->table_wr_cpu_virt_addr, bk_ptr->table_wr_bus_addr);
err_wr_table:
dev_err(&dev->dev, "goto err_wr_table");
//pci_free_consistent(dev, sizeof(struct dma_desc_table), bk_ptr->table_rd_cpu_virt_addr, bk_ptr->table_rd_bus_addr);
err_rd_table:
dev_err(&dev->dev, "goto err_rd_table");
err_irq:
dev_err(&dev->dev, "goto err_regions");
err_dma_mask:
dev_err(&dev->dev, "goto err_dma_mask");
pci_release_regions(dev);
err_regions:
dev_err(&dev->dev, "goto err_irq");
pci_disable_device(dev);
err_enable:
dev_err(&dev->dev, "goto err_enable");
unregister_chrdev_region (bkptr->cdevno, 1);
err_initchrdev:
dev_err(&dev->dev, "goto err_initchrdev");
kfree(bkptr);
err_bk_alloc:
dev_err(&dev->dev, "goto err_bk_alloc");
if(DBUG_PRINT)printk(KERN_ALERT "pci_probe :end !\n");
return rc;
}
static void __exit pci_remove(struct pci_dev *dev)
{
unregister_chrdev_region(bkptr->cdevno, 1);
pci_disable_device(dev);
if(bkptr) {
if(bkptr->msi_enabled) {
pci_disable_msi(dev);
bkptr->msi_enabled = 0;
}
}
unmap_bars(bkptr, dev);
pci_release_regions(dev);
if (!IS_ERR(mem_class))
{
device_destroy(mem_class,bkptr->cdevno);
class_destroy(mem_class);
mem_class = 0;
}
if (&bkptr->cdev)
{
cdev_del(&bkptr->cdev);
}
kfree(dev);
}
static struct pci_device_id pci_ids[] = {
{ PCI_DEVICE(A_MANF_ID,A_MODEL_CODE) },
{ 0 }
};
static struct pci_driver driver_ops = {
.name = ADEVNAME,
.id_table = pci_ids,
.probe = pci_probe,
.remove = pci_remove,
};
static void Pci_cleanupmodule(void)
{
if (0 == npci_register)
pci_unregister_driver(&driver_ops);
kfree(bkptr);
}
static int Pci_init_module(void)
{
npci_register = -1;
bkptr = kzalloc(sizeof(struct pcie_dma_bookkeep), GFP_KERNEL);
memset(bkptr,0,sizeof(struct pcie_dma_bookkeep));
if(printk_ratelimit())
if(DBUG_PRINT)printk(KERN_ALERT "pci_register_driver :Begin !\n");
npci_register = pci_register_driver(&driver_ops);
if (npci_register) {
printk(KERN_ERR ADEVNAME ": PCI driver registration failed\n");
goto fail;
}
if(DBUG_PRINT)printk(KERN_ALERT "pci_register_driver :end !\n");
return npci_register;
fail:
if(DBUG_PRINT)printk(KERN_ALERT "Pci_init_module :fail !\n");
return npci_register;
}
module_init(Pci_init_module);
module_exit(Pci_cleanupmodule);
MODULE_AUTHOR(ADEVNAME);
MODULE_LICENSE("GPL");