linux驱动学习记录(二)-PCI驱动框架
发布日期:2021-10-10 05:30:57 浏览次数:14 分类:技术文章

本文共 4072 字,大约阅读时间需要 13 分钟。

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");

转载地址:https://blog.csdn.net/qq_22042587/article/details/78175503 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:linux驱动学习记录(三)-PCI IO读写、中断、DMA传输
下一篇:linux驱动学习记录(一)-字符设备框架

发表评论

最新留言

感谢大佬
[***.8.128.20]2023年03月10日 05时40分20秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

最新文章