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

来源:互联网 发布:北京知象科技怎么样 编辑:程序博客网 时间:2024/05/29 19:21

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; /* 厂商和设备IDPCI_ANY_ID*/

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

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

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

};

2. PCI设备驱动框架


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

#include #include #include #include #include #include //预定义主设备号#define LS_MAJOR 150static 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_MODULESlspci_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");