21.2 PCI 设备驱动结构
21.2.1 PCI 设备驱动的组成
PCI 只是一种总线,具体的 PCI 设备可以是字符设备、网络设备、USB 主机控制器等,因此,一个通过 PCI 总线与系统连接的设备的驱动至少包含以下两部分内容。
1)、PCI 驱动。
2)、设备本身的驱动。
PCI 驱动只是为了辅助设备本身的驱动,它不是目的,而是手段。例如,对于通过 PCI 总线与系统连接的字符设备,则驱动中除要实现 PCI 驱动部分外,其主体仍然是设备作为字符设备本身的驱动,即实现 file_operations 成员函数并注册 cdev。
在 Linux 内核中,用 pci_driver 结构体来定义 PCI 驱动,该结构体中包含了 PCI 设备的探测/移除、挂起/恢复等函数,其定义如代码清单 21.5 所示。pci_driver起到挂接总线的作用。
代码清单 21.5 pci_driver 结构体
include/linux/pci.h
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); /* New device inserted */
void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
int (*suspend) (struct pci_dev *dev, pm_message_t state); /* Device suspended */
int (*suspend_late) (struct pci_dev *dev, pm_message_t state);
int (*resume_early) (struct pci_dev *dev);
int (*resume) (struct pci_dev *dev); /* Device woken up */
void (*shutdown) (struct pci_dev *dev);
int (*sriov_configure) (struct pci_dev *dev, int num_vfs); /* PF pdev */
const struct pci_error_handlers *err_handler;
struct device_driver driver;
struct pci_dynids dynids;
};
对 pci_driver 的注册和注销通过如下函数来实现:
int pci_register_driver(struct pci_driver *drv);
void pci_unregister_driver(struct pci_driver *drv);
pci_driver 的 probe()函数要完成 PCI 设备的初始化及其设备本身身份(字符、TTY、网络等)驱动的注册。当 Linux 内核启动并完成对所有 PCI 设备进行扫描、登录和分配资源等初始化操作的同时,会建立起系统中所有 PCI 设备的拓扑结构,probe()函数将负责硬件的探测工作并保存配置信息。
以一个 PCI 接口字符设备为例,给出 PCI 设备驱动的完整模板。其一部分代码实现 pci_driver成员函数,一部分代码实现字符设备的 file_operations 成员函数,如代码清单 21.6 所示。
代码清单 21.6 PCI 设备驱动的程序模板
/* 指明该驱动程序适用于哪一些 PCI 设备 */
static struct pci_device_id xxx_pci_tbl [] __initdata = {
{
PCI_VENDOR_ID_DEMO, PCI_DEVICE_ID_DEMO,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEMO
},
{0,}};
MODULE_DEVICE_TABLE(pci, xxx_pci_tbl);
/* 中断处理函数 */
static void xxx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
...
}
/* 字符设备 file_operations open 成员函数 */
static int xxx_open(struct inode *inode, struct file *file)
{
/* 申请中断,注册中断处理程序 */
request_irq(xxx_irq, &xxx_interrupt, ...));
...
}
/* 字符设备 file_operations ioctl 成员函数 */
static int xxx_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
...
}
/* 字符设备 file_operations read、write、mmap 等成员函数 */
/* 设备文件操作接口 */
static struct file_operations xxx_fops = {
owner: THIS_MODULE, /* xxx_fops 所属的设备模块 */
read: xxx_read, /* 读设备操作*/
write: xxx_write, /* 写设备操作*/
ioctl: xxx_ioctl, /* 控制设备操作*/
mmap: xxx_mmap, /* 内存重映射操作*/
open: xxx_open, /* 打开设备操作*/
release: xxx_release /* 释放设备操作*/
};
/* pci_driver 的 probe 成员函数 */
static int _ _init xxx_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
{
pci_enable_device(pci_dev); /* 启动 PCI 设备 */
/* 读取 PCI 配置信息 */
iobase = pci_resource_start (pci_dev,1);
...
pci_set_master(pci_dev);//设置成总线主 DMA 模式
pci_request_regions(pci_dev);/* 申请 I/O 资源 */
/* 注册字符设备 */
cdev_init(xxx_cdev,&xxx_fops);
register_chrdev_region(xxx_dev_no, 1, ...);
cdev_add(xxx_cdev);
return 0;
}
/* pci_driver 的 remove 成员函数 */
static int _ _init xxx_remove (struct pci_dev *pdev)
{
pci_release_regions(pdev);/* 释放 I/O 资源 */
pci_disable_device (pdev);/* 禁止 PCI 设备 */
unregister_chrdev_region(xxx_dev_no, 1); /* 释放占用的设备号 */
cdev_del(&xxx_dev.cdev); /* 注销字符设备 */
...
return 0;
}
/* 设备模块信息 */
static struct pci_driver xxx_pci_driver = {
name: xxx_MODULE_NAME, /* 设备模块名称 */
id_table: xxx_pci_tbl, /* 能够驱动的设备列表 */
probe: xxx_probe, /* 查找并初始化设备 */
remove: xxx_remove /* 卸载设备模块 */
};
static int _ _init xxx_init_module (void)
{
pci_register_driver(&xxx_pci_driver); // 注册