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设备驱动框架


以下为一个简单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"); 
         
       
      
      
     
     
    
    
   
   

阅读终点,创作起航,您可以撰写心得或摘录文章要点写篇博文。去创作
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
学习 Linux 底层驱动开发需要掌握以下基本知识: 1. 熟悉 Linux 操作系统的基本概念和命令行操作。 2. 掌握 C/C++ 编程语言,了解 Linux 系统调用和 API。 3. 了解硬件系统的基础知识,包括计算机体系结构、总线、设备等。 4. 了解 Linux 设备驱动的基础知识,包括字符设备驱动、块设备驱动、网络设备驱动等。 5. 了解 Linux 内核的基本结构和编译方法。 6. 掌握 Linux 内核模块的编写和调试方法。 7. 学习 Linux 设备驱动框架和接口,包括 Platform、PCI、USB、I2C、SPI 等。 8. 学习 Linux 设备驱动的调试方法,包括 printk、trace、ftrace 等。 9. 学习 Linux 设备树的基础知识和使用方法。 10. 学习 Linux 驱动的性能优化和调试方法。 建议的学习路线如下: 1. 先学习 Linux 操作系统的基本概念和命令行操作,可以参考《鸟哥的 Linux 私房菜》等书籍。 2. 掌握 C/C++ 编程语言,了解 Linux 系统调用和 API,可以参考《C++ Primer》等书籍。 3. 了解硬件系统的基础知识,可以参考《计算机组成原理》等书籍。 4. 了解 Linux 设备驱动的基础知识,可以参考《Linux 设备驱动开发详解》等书籍。 5. 了解 Linux 内核的基本结构和编译方法,可以参考《深入理解 Linux 内核》等书籍。 6. 掌握 Linux 内核模块的编写和调试方法,可以参考《Linux 内核源代码情景分析》等书籍。 7. 学习 Linux 设备驱动框架和接口,可以参考 Linux 内核源代码和相关文档。 8. 学习 Linux 设备驱动的调试方法,可以参考 Linux 内核源代码和相关文档。 9. 学习 Linux 设备树的基础知识和使用方法,可以参考 Linux 内核源代码和相关文档。 10. 学习 Linux 驱动的性能优化和调试方法,可以参考 Linux 内核源代码和相关文档。 建议通过阅读相关书籍和源代码,结合实际项目经验进行学习。同时,可以参加相关的培训或交流活动,与其他开发者共同学习和交流。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

羅昇

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

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

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

打赏作者

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

抵扣说明:

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

余额充值