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

几年前正式转到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");

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

坤昱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值