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设备驱动框架
以下为一个简单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");