首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Linux下PCI设备驱动程序开发(3)PCI驱动程序实现-1

Linux下PCI设备驱动程序开发(3)PCI驱动程序实现-1

1. 关键数据结构PCI设备上有三种地址空间:PCI的I/O空间、PCI的存储空间和PCI的配置空间。CPU可以访问PCI设备上的所有地址空间,其中I/O空间和存储空间提供给设备驱动程序使用,而配置空间则由Linux内核中的PCI初始化代码使用。内核在启动时负责对所有PCI设备进行初始化,配置好所有的PCI设备,包括中断号以及I/O基址,并在文件/proc/pci中列出所有找到的PCI设备,以及这些设备的参数和属性。
Linux驱动程序通常使用结构(struct)来表示一种设备,而结构体中的变量则代表某一具体设备,该变量存放了与该设备相关的所有信息。好的驱动程序都应该能驱动多个同种设备,每个设备之间用次设备号进行区分,如果采用结构数据来代表所有能由该驱动程序驱动的设备,那么就可以简单地使用数组下标来表示次设备号。
在PCI驱动程序中,下面几个关键数据结构起着非常核心的作用:
  • pci_driver这个数据结构在文件include/linux/pci.h里,这是Linux内核版本2.4之后为新型的PCI设备驱动程序所添加的,其中最主要的是用于识别设备的id_table结构,以及用于检测设备的函数probe( )和卸载设备的函数remove( ):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    struct pci_driver {
        struct list_head node;
        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  (*save_state) (struct pci_dev *dev, u32 state);
        int  (*suspend)(struct pci_dev *dev, u32 state);
        int  (*resume) (struct pci_dev *dev);
        int  (*enable_wake) (struct pci_dev *dev, u32 state, int enable);
    };




  • pci_dev这个数据结构也在文件include/linux/pci.h里,它详细描述了一个PCI设备几乎所有的硬件信息,包括厂商ID、设备ID、各种资源等:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    struct pci_dev {
        struct list_head global_list;
        struct list_head bus_list;
        struct pci_bus  *bus;
        struct pci_bus  *subordinate;
        void        *sysdata;
        struct proc_dir_entry *procent;
        unsigned int    devfn;
        unsigned short  vendor;
        unsigned short  device;
        unsigned short  subsystem_vendor;
        unsigned short  subsystem_device;
        unsigned int    class;
        u8      hdr_type;
        u8      rom_base_reg;
        struct pci_driver *driver;
        void        *driver_data;
        u64     dma_mask;
        u32             current_state;
        unsigned short vendor_compatible[DEVICE_COUNT_COMPATIBLE];
        unsigned short device_compatible[DEVICE_COUNT_COMPATIBLE];
        unsigned int    irq;
        struct resource resource[DEVICE_COUNT_RESOURCE];
        struct resource dma_resource[DEVICE_COUNT_DMA];
        struct resource irq_resource[DEVICE_COUNT_IRQ];
        char        name[80];
        char        slot_name[8];
        int     active;
        int     ro;
        unsigned short  regs;
        int (*prepare)(struct pci_dev *dev);
        int (*activate)(struct pci_dev *dev);
        int (*deactivate)(struct pci_dev *dev);
    };




2. 基本框架在用模块方式实现PCI设备驱动程序时,通常至少要实现以下几个部分:初始化设备模块、设备打开模块、数据读写和控制模块、中断处理模块、设备释放模块、设备卸载模块。下面给出一个典型的PCI设备驱动程序的基本框架,从中不难体会到这几个关键模块是如何组织起来的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/* 指明该驱动程序适用于哪一些PCI设备 */
static struct pci_device_id demo_pci_tbl [] __initdata = {
    {PCI_VENDOR_ID_DEMO, PCI_DEVICE_ID_DEMO,
     PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEMO},
    {0,}
};
/* 对特定PCI设备进行描述的数据结构 */
struct demo_card {
    unsigned int magic;
    /* 使用链表保存所有同类的PCI设备 */
    struct demo_card *next;
     
    /* ... */
}
/* 中断处理模块 */
static void demo_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
    /* ... */
}
/* 设备文件操作接口 */
static struct file_operations demo_fops = {
    owner:      THIS_MODULE,   /* demo_fops所属的设备模块 */
    read:       demo_read,    /* 读设备操作*/
    write:      demo_write,    /* 写设备操作*/
    ioctl:      demo_ioctl,    /* 控制设备操作*/
    mmap:       demo_mmap,    /* 内存重映射操作*/
    open:       demo_open,    /* 打开设备操作*/
    release:    demo_release    /* 释放设备操作*/
    /* ... */
};
/* 设备模块信息 */
static struct pci_driver demo_pci_driver = {
    name:       demo_MODULE_NAME,    /* 设备模块名称 */
    id_table:   demo_pci_tbl,    /* 能够驱动的设备列表 */
    probe:      demo_probe,    /* 查找并初始化设备 */
    remove:     demo_remove    /* 卸载设备模块 */
    /* ... */
};
static int __init demo_init_module (void)
{
    /* ... */
}
static void __exit demo_cleanup_module (void)
{
    pci_unregister_driver(&demo_pci_driver);
}
/* 加载驱动程序模块入口 */
module_init(demo_init_module);
/* 卸载驱动程序模块入口 */
module_exit(demo_cleanup_module);




上面这段代码给出了一个典型的PCI设备驱动程序的框架,是一种相对固定的模式。需要注意的是,同加载和卸载模块相关的函数或数据结构都要在前面加上__init、__exit等标志符,以使同普通函数区分开来。构造出这样一个框架之后,接下去的工作就是如何完成框架内的各个功能模块了。



返回列表