几年前正式转到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 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 1
struct 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 :offsetif (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)
{//regvolatile unsigned char p8[1];volatile unsigned short p16[1];volatile unsigned int p32[1];unsigned int val;//write reg to valunsigned 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 8addr = buf[0];//addrval = buf[1];//valstatic 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-2if(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 addressbreak;}case KER_RW_R16:{//val = ioread16(bkptr->bar[nbar] + addr);val = *(unsigned short*)(bkptr->bar[nbar] + addr);//2017-11-2if(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-2if(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 addressiounmap(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;} elsedev_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 Connectedif(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);//devbkptr->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");

Linux内核驱动如何编写?我们先从字符驱动入门开始相关推荐

  1. Linux内核网络数据发送(六)——网络设备驱动

    Linux内核网络数据发送(六)--网络设备驱动 1. 前言 2. 驱动回调函数注册 3. `ndo_start_xmit` 发送数据 4. `igb_tx_map` 1. 前言 本文主要介绍设备通过 ...

  2. linux内核源码实战_3.2理解设备驱动和文件系统

    linux内核源码实战_3.2理解设备驱动和文件系统 linux内核源码实战_理解设备驱动和文件系统 理解设备驱动和文件系统 理解设备驱动和文件系统详解 7-文件系统-proc文件系统实现 总结 li ...

  3. linux内核源码只有makefile文件没有c文件,linux内核代码的编写初步以及makefile的配置...

    在linux内核代码开发中,头文件不能包含标准C头文件,只能采用GNC标准 而且内核开发中没有main函数,只有init 和 exit ,这是每个内核模块中必须要包含的函数模块. 在GNU C标准中, ...

  4. ubuntu linux内核版本>5.15安装usb无线网卡驱动

    真没想到ubuntu 22.04上安装无线网卡驱动这么曲折! 系统:ubuntu 22.04 我的网卡芯片为:RTL8812AU 遇到的问题: 内核版本与大多现有驱动不兼容 比如我买的绿联网卡,芯片为 ...

  5. linux内核源代码情景分析(第八章 设备驱动)

    第八章 设备驱动 文章目录

  6. Android驱动(1)---Ubuntu中为Android系统上编写Linux内核驱动程序实现方法

    Ubuntu中为Android系统上编写Linux内核驱动程序实现方法 本文主要介绍在Ubuntu 上为Android系统编写Linux内核驱动程序, 这里对编写驱动程序做了详细的说明,对研究Andr ...

  7. android驱动之旅-在Ubuntu上为Android系统编写Linux内核驱动程序(3)

    Android的硬件抽象层,简单来说,就是对Linux内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节.也就是说,把对硬件的支持分成了两层,一层放在用户空间(User Space),一层放在内核空 ...

  8. NanoPi NEO Air使用九:使用Linux内核自带的LED驱动

    NanoPi NEO Air使用一:介绍 NanoPi NEO Air使用二:固件烧录 NanoPi NEO Air使用三:OverlayFS.CPU温度和频率.wifi.蓝牙.npi-config ...

  9. 在Ubuntu上为Android系统编写Linux内核驱动程序

    在智能手机时代,每个品牌的手机都有自己的个性特点.正是依靠这种与众不同的个性来吸引用户,营造品牌凝聚力和用户忠城度,典型的代表非iphone莫属了. 据统计,截止2011年5月,AppStore的应用 ...

  10. 在Ubuntu上为Android系统编写Linux内核驱动程序(学习老罗的)

    首先提出2个问题 1. 驱动程序的作用是什么? 答:驱动程序的作用主要是向上层提供访问设备寄存器的一个接口,包括读和写. 2. 访问设备驱动程序的方法? 答:a. 通过proc文件系统来访问:b. 通 ...

最新文章

  1. 爱了!Intellij IDEA 自带的 Vim 插件真心不错
  2. 目前服务器操作系统版本号,目前服务器操作系统版本号
  3. NETCONF 环境搭建
  4. jQuery事件绑定on()、bind()与delegate() 方法详解
  5. 8.8 正睿暑期集训营 Day5
  6. oracle ora 00283,【案例】Oracle报错ORA-16433非归档丢失redo无法启动的恢复过程
  7. 八种常见的 SQL 错误用法
  8. Web开发工具大集合
  9. 随想录(git操作)
  10. Hibernate实体类注解中如何忽略某些字段的映射
  11. 全网最全ADB命令,没有之一
  12. 计算机电脑哪个是复位键,电脑一键还原按哪个键
  13. python运行环境配置Django
  14. Unity制作出《超级马里奥》的2D和3D混合效果
  15. wav2letter++ 第一次training 日志
  16. 一个字符等于多少个字节?
  17. 2021年中国民航及其重点企业对比分析(中航集团VS东航集团VS南航集团VS海航集团)[图]
  18. 利用cpolar为群晖NAS建立稳定外网地址(1)
  19. 分享16个Python接单平台,做私活爽歪歪!(附100个爬虫源码)
  20. docker暴露端口、端口映射

热门文章

  1. 教你彻底学会Java序列化和反序列化
  2. 40张图揭秘,「键入网址发生了什么」
  3. 阿里云上千规模节点项目提炼精华,全在这里了!
  4. 如何高效地进行敏捷开发管理
  5. 百度编辑器 Ueditor 如何增加模板 ?
  6. MySQL学习笔记_5_SQL语言的设计与编写(上)
  7. 没网?没问题。用Air Gap使用Artifactory
  8. Tyvj P1463 智商问题 分块
  9. UILabel常用属性
  10. Java I/O学习——File