一、pci_driver结构体
《Linux设备驱动开发》中讲到,PCI只是一种总线,具体的PCI设备可以是字符设备、网络设备、USB主机控制器等,因此一个通过PCI总线与系统连接的设备的驱动至少包含两部分:PCI设备驱动和设备本身的驱动。 对于这句话我的理解是,PCI驱动指实现对PCI设备的探测、移除、挂起\恢复等功能的代码部分。而设备本身的驱动指的是file_operations结构体实现的open、release、read、write等功能的代码部分。
其中pci_driver的结构如下所示。其中的probe函数要完成对PCI设备的初始化及其设备本身身份(字符、TTY、网络等)驱动的注册。当Linux内核启动并完成对所有PCI设备进行扫描、登录和分配资源等初始化操作的同时,会建立起系统中所有PCI设备的拓扑结构,probe函数负责硬件的探测工作并保存配置信息。
struct pci_driver {
struct list_head node;
const char *name;
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 (*resume)(struct pci_dev *dev); /* 设备唤醒 */
void (*shutdown)(struct pci_dev *dev);
int (*sriov_configure)(struct pci_dev *dev, int num_vfs); /* On PF */
int (*sriov_set_msix_vec_count)(struct pci_dev *vf, int msix_vec_count); /* On PF */
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;
bool driver_managed_dma;
};
其中pci_device_id结构体如下所示。PCI_ANY_ID定义为~0,即对任意ID都适用。
struct pci_device_id {
__u32 vendor;/* 设备供应商ID,如果为:PCI_ANY_ID则表示不检查此值*/
__u32 device; /* 指定设备的设备ID,如果为:PCI_ANY_ID则表示不检查此值*/
__u32 subvendor; /* 设备子供应商ID,如果为:PCI_ANY_ID则表示不检查此值*/
__u32 subdevice; /* 指定设备的子设备ID,如果为:PCI_ANY_ID则表示不检查此值 */
__u32 class; /* 指定设备的类代码,由基类、子类和接口组成 */
__u32 class_mask; /* 类代码的掩码,由基类、子类和接口组成 */
kernel_ulong_t driver_data; /* 指向驱动程序私有数据的指针 */
__u32 override_only;
};
二、 PCI设备驱动的组成
以下pci驱动示例代码以驱动字符设备为例,其一部分代码实现pci_driver成员函数,一部分代码实现字符设备的file_operations成员函数。
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/bits.h>
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/pmbus.h>
#include <linux/util_macros.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
int tmc_pci_open(struct inode *node, struct file *filp){
/*......*/
}
int tmc_pci_release(struct inode *, struct file *)
{
/*......*/
}
ssize_t tmc_pci_read(struct file *, char __user *, size_t, loff_t *)
{
/*......*/
}
ssize_t tmc_pci_write(struct file *, const char __user *, size_t, loff_t *)
{
/*......*/
}
struct file_operations tmc_pci_fops = {
.open = tmc_pci_open,
.release = tmc_pci_release,
.read = tmc_pci_read,
.write = tmc_pci_write,
};
int tmc_spi_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
/*......*/
}
void tmc_spi_remove(struct pci_dev *dev)
{
/*......*/
}
const struct pci_device_id tmc_spi_idtable[] = {
{/*非空*/},
{}
};
struct pci_driver tmc_pci_drv = {
.probe = tmc_spi_probe,
.remove = tmc_spi_remove,
.id_table = tmc_spi_idtable,
};
int tmc_pci_init()
{
pci_register_driver(tmc_pci_drv);
return 0;
}
void tmc_pci_exit
{
pci_unregister_driver(tmc_pci_drv);
}
module_init(tmc_pci_init);
mosule_exit(tmc_pci_init);
MODULE_LICENSE("GPL");
三、PCI驱动网卡案例