linux驱动学习记录(二)-PCI驱动框架

1. PCI设备驱动的组成

 

PCI 驱动只是为了辅助设备本身的驱动,它不是目的,而是手段。例如,对于通过PCI 总线与系统连接的字符设备,则驱动中除了要实现PCI 驱动部分外,其主体仍然是设备作为字符设备本身的驱动,即实现file_operations成员函数并注册cdev。

 

在Linux 内核中,用pci_driver 结构体来定义PCI 驱动,该结构体中包含了PCI 设备的探测/移除、挂起/恢复等函数,其定义如下

struct pci_driver {

struct list_head node;

char *name;

struct module *owner;

const struct pci_device_id *id_table; /*不能为NULL,以便probe 函数调用*/

/* 新设备添加 */

int(*probe)(struct pci_dev *dev, const struct pci_device_id *id);

void(*remove)(struct pci_dev *dev); /* 设备移出 */

int(*suspend)(struct pci_dev *dev, pm_message_t state); /* 设备挂起 */

int (*suspend_late) (struct pci_dev *dev, pm_message_t state);

int (*resume_early) (struct pci_dev *dev);

int(*resume)(struct pci_dev *dev); /* 设备唤醒 */

void(*shutdown)(struct pci_dev *dev);

struct pm_ext_ops *pm;

struct pci_error_handlers *err_handler;

struct device_driver driver;

struct pci_dynids dynids;

};

 

pci_device_id 用于标识一个PCI 设备。它的成员包括:厂商ID、设备ID、子厂商ID、子设备ID、类别、类别掩码(类可分为基类、子类)和私有数据。每一个PCI 设备的驱动程序都有一个pci_device_id 的数组,用于告诉PCI 核心自己能够驱动哪些设备,pci_device_id 结构体的定义如下

struct pci_device_id {

_ _u32 vendor, device; /* 厂商和设备ID PCI_ANY_ID*/

_ _u32 subvendor, subdevice; /* 子厂商ID PCI_ANY_ID */

 _ _u32 class, class_mask; /* (类、子类、prog-if) 三元组 */

kernel_ulong_t driver_data; /* 驱动私有数据 */

};

2. PCI设备驱动框架

linux驱动学习记录(二)-PCI驱动框架_第1张图片

以下为一个简单PCI驱动框架示例代码,没有任何功能。

#include 
#include 
#include 
#include 
#include 
#include 


//预定义主设备号
#define LS_MAJOR 150

static int lspci_major = LS_MAJOR;
module_param(lspci_major, int, S_IRUGO);

//自定义设备结构体
struct lspci_dev {
	struct cdev cdev;			//在Linux内核中,使用cdev结构体描述一个字符设备
};
struct lspci_dev *lspci_devp;

/* 字符设备file_operations open 成员函数 */
static int xxx_open(struct inode *inode, struct file *filp)
{
	return 0;
}

/* 字符设备file_operations ioctl 成员函数 */
static long xxx_ioctl(struct file *filp, unsigned int cmd, 
unsigned long arg)
{
	return 0;
}

/* 字符设备file_operations read 成员函数 */
static ssize_t xxx_read(struct file *filp, char __user * buf, size_t size, loff_t * ppos)
{
	return 0;
}

/* 字符设备file_operations write成员函数 */
static ssize_t xxx_write(struct file *filp, const char __user *buf,size_t size, loff_t *ppos)
{
	return 0;
}

/* 字符设备file_operations release成员函数 */
static int xxx_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/* 设备文件操作接口 */
static const struct file_operations xxx_fops = {
	.owner = THIS_MODULE, /* xxx_fops 所属的设备模块 */
	.read = xxx_read, /* 读设备操作*/
	.write = xxx_write, /* 写设备操作*/
	.unlocked_ioctl = xxx_ioctl, /* 控制设备操作*/
	.open = xxx_open, /* 打开设备操作*/
	.release = xxx_release, /* 释放设备操作*/
};

/*pci_device_id 用于标识一个PCI 设备。
它的成员包括:厂商ID、设备ID、子厂商ID、子设备ID、
类别、类别掩码(类可分为基类、子类)和私有数据。*/
static struct pci_device_id xxx_pci_tbl [] __initdata = {
	{0x10ee, 0x0050,PCI_ANY_ID, PCI_ANY_ID, },
	{0,}
};
MODULE_DEVICE_TABLE(pci, xxx_pci_tbl);

/* pci_driver 的probe 成员函数 */
static int xxx_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
{
        //申请字符设备号
	dev_t xxx_dev_no = MKDEV(lspci_major, 0);
	register_chrdev_region(xxx_dev_no, 1, "driver_test");
        //为自定义设备结构体申请内存
	lspci_devp = kzalloc(sizeof(struct lspci_dev), GFP_KERNEL);
	//将自定义设备结构体内的cdev成员与file_operations设备文件操作接口绑定
	cdev_init(&lspci_devp->cdev,&xxx_fops);
	//拥有该结构的模块的指针,一般为THIS_MODULES
	lspci_devp->cdev.owner = THIS_MODULE;
	//注册设备
	cdev_add(&lspci_devp->cdev, xxx_dev_no, 1);

	pci_set_master(pci_dev);//设置成总线主DMA 模式
	pci_request_regions(pci_dev, PCI_NAME);// 申请I/O 资源 

	return 0;
}

/* pci_driver 的remove 成员函数 */
static void xxx_remove(struct pci_dev *pdev)
{
        /* 注销字符设备 */
	cdev_del(&lspci_dev->cdev);	
	/* 释放占用的设备号 */
	unregister_chrdev_region(MKDEV(ls_major, 0), 1);
	kfree(lspci_devp);	

}

/* PCI设备模块信息 */
static struct pci_driver xxx_pci_driver = {
	.name = PCI_NAME, /* 设备模块名称 */
	.id_table = xxx_pci_tbl, /* 能够驱动的设备列表 */
	.probe = xxx_probe, /* 查找并初始化设备 */
	.remove = xxx_remove, /* 卸载设备模块 */
};

static int __init xxx_init_module (void)
{
	//注册pci驱动,进入probe函数
	pci_register_driver(&xxx_pci_driver);
	return 0;
}

static void __exit xxx_cleanup_module (void)
{
	//注销pci驱动
	pci_unregister_driver(&xxx_pci_driver);
}

/* 驱动模块加载函数 */
module_init(xxx_init_module);
/* 驱动模块卸载函数 */
module_exit(xxx_cleanup_module);

MODULE_AUTHOR("LuoSheng");
MODULE_LICENSE("GPL v2");

你可能感兴趣的:(Linux)