一、如何编写Linux PCI驱动程序

PCI的世界是广阔的,充满了(大部分令人不快的)惊喜。由于每个CPU体系结构实现不同的芯片集,并且PCI设备有不同的需求(“特性”),因此Linux内核中的PCI支持并不像人们希望的那么简单。这篇简短的文章介绍用于PCI设备驱动程序的Linux APIs。

一个更完整的资源是由Jonathan Corbet、Alessandro Rubini和Greg Kroah-Hartman编写的“Linux设备驱动程序”的第三版。LDD3可以从https://lwn.net/Kernel/LDD3/免费获得(根据知识共享许可协议)。

1.1 PCI驱动程序结构

PCI驱动程序通过pci_register_driver()在系统中"发现"PCI设备。事实上,恰恰相反。当PCI通用代码发现一个新设备时,具有匹配“描述”的驱动程序将被通知。详情如下。

pci_register_driver()将设备的大部分探测留给PCI层,并支持在线插入/删除设备[因此在单个驱动程序中支持热插拔PCI、CardBus和Express-Card]。pci_register_driver()调用需要传入一个函数指针表,从而指示驱动程序的更高一级结构体。

一旦驱动程序知道了一个PCI设备并获得了所有权,驱动程序通常需要执行以下初始化:

启用设备

请求MMIO / IOP资源

设置DMA掩码大小(用于一致性DMA和流式DMA)

分配和初始化共享控制数据(pci_allocate_coherent())

访问设备配置空间(如果需要)

注册IRQ处理程序(request_irq())

初始化non-PCI(即LAN/SCSI/等芯片部分)

启用DMA /处理引擎

当使用设备完成时,可能需要卸载模块,驱动程序需要采取以下步骤:

禁止设备产生irq

释放IRQ (free_irq())

停止所有DMA活动

释放DMA缓冲区(包括流式DMA和一致性DMA)

从其他子系统注销(例如scsi或netdev)

释放MMIO / IOP资源

禁用该设备

下面几节将介绍这些主题中的大部分。其余部分请查看LDD3或<linux/pci.h>。

如果PCI子系统没有配置(没有设置CONFIG_PCI),下面描述的大多数PCI函数都被定义为内联函数,要么完全空,要么只是返回一个适当的错误代码,以避免在驱动程序中出现大量ifdefs。

 1.2 pci_register_driver()调用

PCI设备驱动程序在初始化过程中调用pci_register_driver(),使用一个指向描述该驱动程序结构(struct pci_driver)的指针:

struct pci_driver  定义

struct pci_driver { struct list_head node; const char *name; const struct pci_device_id *id_table; 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 (*resume)(struct pci_dev *dev); void (*shutdown)(struct pci_dev *dev); int (*sriov_configure)(struct pci_dev *dev, int num_vfs); int (*sriov_set_msix_vec_count)(struct pci_dev *vf, int msix_vec_count); u32 (*sriov_get_vf_total_msix)(struct pci_dev *pf); const struct pci_error_handlers *err_handler; const struct attribute_group **groups; const struct attribute_group **dev_groups; struct device_driver driver; struct pci_dynids dynids; };

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgjywy.html