PCI驱动程序

配置寄存器和初始化
struct pci_device_id {  
  __u32 vendor, device;        /* Vendor and device ID or PCI_ANY_ID*/  
  __u32 subvendor, subdevice;      /* Subsystem ID's or PCI_ANY_ID */  
  __u32 class, class_mask;      /* (class,subclass,prog-if) triplet */  
  kernel_ulong_t driver_data;     /* Data private to the driver */
};
1、 vendor  (厂商号)这个寄存器标识一个硬件制造商,例如,每个Intel的设备都标有相贩供应商号, 0x8086, 这样的号有一个全球注册,由PCI特别利益体所维护, 并且供应商必须申请有一个唯一的分配给它们的编号
2、device  (设备号) 这个寄存器,由供应商选择。
3、subvendor  (子厂商号)  如果驱动可处理任何类型的子系统ID, 值PCI_ANY_ID应该用于设置此域
4、subdevice  (子设备号)  如果驱动可处理任何类型的子系统ID, 值PCI_ANY_ID应该用于设置此域 
5、class      (类别)      如果驱动可处理任何类型的子系统ID, 值PCI_ANY_ID应该用于设置此域
6、class_mask  (类别掩码)  如果驱动可处理任何类型的子系统ID, 值PCI_ANY_ID应该用于设置此域
7、driver_data  (私有数据)
 
再介绍两个宏定义用来初始化struct pci_device_id
 
PCI_DEVICE(vendor, device)   :   这个创建一个 struct pci_device_id, 它匹配特定的供应商和设备ID,这个宏设置这个结构的子供应商和子设备号为PCI_ANY_ID
 
PCI_DEVICE_CLASS(device_class, device_class_mask)   : 这个创建一个struct pci_device_id它匹配一个特定的PCI类。
 
MODULE_DEVICE_TABLE宏
MODULE_DEVICE_TABLE   : 用于将pci_device_id结构输出到用户空间,来允许执插拔各模块加载系统知道什么模块使用什么硬件设备。示例: MODULE_DEVICE_TABLE(pci, i810_ids); 这个语句创建一个名为__mod_pci_device_table的局部变量,指向struct pci_device_id的列表,稍后在内核构建过程中,depmod程序在所有的模块中录找__mod_pci_device_table。如果找个这个符号,它将数据拉出模块并添加到文件/lib/modules/KERNEL_VERSION/modules.pcimap。在depmod完成后,所有的被内核中的模块支持的PCI设备被列出,带有它们模块名子,在那个文件中,当内核告知热插拔系统有新的PCI设备已找到,热插拔系统使用modules.pcimap文件来找到正确的驱动来加载。
 
注册PCI驱动程序
为了被正确注册到内核, 所有的 PCI 驱动必须创建的主结构是 struct pci_driver 结构。这个结构包含许多函数回调和变量, 来描述 PCI 驱动给 PCI核心。这里是这个结构的一个 PCI 驱动需要知道的成员:
const char *name;

驱动的名字。在内核中所有 PCI 驱动里中它必须是唯一的。通常被设置为和驱动模块名字相同的名子。当驱动程序运行在内核中时,它显示在 sysfs 中在/sys/bus/pci/drivers/ 下面。
 
const struct pci_device_id *id_table;

指向 struct pci_device_id 表的指针。
 
int (*probe) (struct pci_dev *dev, const struct pci_device_id *id);

指向 PCI 驱动中 probe 函数的指针。当PCI 核心有一个它认为驱动程序需要控制的 struct pci_dev 时,就会调用这个函数。 PCI 核心用来做判断的 struct pci_device_id 指针也被的, 也被传递给这个函数。如果PCI驱动程序确认传递给它的 struct pci_dev,则应该恰当地初始化这个设备并且返回 0。如果这个驱动确认该设备,或者发生了错误,它应当返回一个负的错误值。

void (*remove) (struct pci_dev *dev);

指向一个移除函数的指针,当 struct pci_dev被从系统中移除,或者PCI驱动程序正在从内核中卸载时,PCI核心调用该函数。
 
int (*suspend) (struct pci_dev *dev, u32 state);

当 struct pci_dev 被挂起时 PCI 核心调用的函数的指针. 挂起状态在 state 变量里传递. 这个函数是可选的; 一个驱动不必提供它.

int (*resume) (struct pci_dev *dev);

当 pci_dev 被恢复时 PCI 核心调用的函数的指针. 它总是在挂起函数已经被调用之后被调用。这个函数时可选的; 一个驱动不必提供它.
 
总之, 为创建一个正确的 struct pci_driver 结构, 只需要四个字段被初始化:
static struct pci_driver pci_driver = {
  .name = "pci_skel",
  .id_table = ids,
  .probe = probe,
  .remove = remove,
};
 
为了把struct pci_driver注册到PCI核心中,需要调用以 struct pci_driver 指针为参数的pci_register_driver函数。通常在PCI驱动程序的模块初始化代码中完成该工作:
static int __init pci_skel_init(void)
{
  return pci_register_driver(&pci_driver);
}
如果注册成功,pci_register_driver函数返回 0;否则,返回一个负的错误编号。
 
当PCI驱动程序将要被卸载的时候,需要把struct pci_driver从内核注销。这是通过调用pci_unregister_driver来完成的。当该函数被调用时,当前绑定到该驱动程序的任何PCI设备都被移除,该PCI驱动程序的移除函数在pci_ungister_driver函数返回之前被调用。
static void __init pci_skel_exit(void)
{
  pci_unregister_driver(&pci_driver);
}
 
激活PCI设备
在PCI驱动程序的探测函数中,在驱动程序可以访问PCI设备的任何设备资源之前(I/O区域或者中断), 驱动程序必须调用pci_enable_device函数:
int pci_enable_device(struct pci_dev *dev);
 
访问I/O和内存空间
unsigned long pci_resource_start(struct pci_dev *dev, int bar);

该函数返回六个PCI I/O区域之一的首地址(内存地址或I/O端口号)。该区域由整数的bar(base address register,基地址寄存器)指定,bar的取值为 0 到 5。
 
unsigned long pci_resource_end(struct pci_dev *dev, int bar);

这个函数返回第bar个I/O区域的尾地址。注意这是最后一个可用地址,而不是该区域之后的第一个地址。
 
unsigned long pci_resource_flags(struct pci_dev *dev, int bar);

这个函数返回与这个资源相关联的标志。

资源标识用来定义单个资源的一些特性。对于和 PCI I/O 区相关联的 PCI 资源,这个信息从基地址寄存器中抽取出来,但是对于和 PCI 设备无关的资源,它可能来自其他地方。
 
所有资源标志定义在<linux/ioport.h>中,其中最重要的几个:
IORESOURCE_IO
IORESOURCE_MEM
       如果相关的I/O区域存在,将设置这些标志之一。
IORESOURCE_PREFETCH
IORESOURCE_READONLY
       这些标志表明内存区域是否为可预取的和/或是写保护的。对于PCI资源来说,从来不会设置后面的那个标志。
 
原文地址:https://www.cnblogs.com/gjfhopeful/p/3670306.html