本文共 21081 字,大约阅读时间需要 70 分钟。
几年前正式转到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#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 128struct 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 1struct class *mem_class;struct Pci_dev *demo_devices;struct pcie_dma_bookkeep *bkptr;static unsigned char demo_inc = 0;//Up to a Devstatic 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 uperr_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");
转载地址:https://lkyof.blog.csdn.net/article/details/117873761 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!