原创 Linux PCI驱动的编写

2010-4-13 14:36 2209 3 3 分类: MCU/ 嵌入式

 


作者:曹忠明,华清远见嵌入式学院讲师。


PCI是外围设备互联的简称(Peripheral Component Interconnect)的简称,作为一种通用的总线接口标准,他在计算机系统中得到了广泛的使用。PCI的速度能够达到132M/s。在这里简单的介绍一下 linux 下PCI驱动的实现。


在编写一个PCI驱动的时候我们先得确定系统中是否有我们的设备。我们可以通过lspci查看PCI设备。


[root@localhost ~]# lspci
        00:00.0 Host bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX Host bridge (rev 01)
        00:01.0 PCI bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX AGP bridge (rev 01)
        00:07.0 ISA bridge: Intel Corporation 82371AB/EB/MB PIIX4 ISA (rev 08)
        00:07.1 IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)
        00:07.2 USB Controller: Intel Corporation 82371AB/EB/MB PIIX4 USB
        00:07.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 08)
        00:0f.0 VGA compatible controller: VMware Inc Abstract SVGA II Adapter
        00:10.0 SCSI storage controller: LSI Logic / Symbios Logic 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 01)
        00:11.0 PCI bridge: VMware Inc PCI bridge (rev 02)
        02:00.0 Ethernet controller: Advanced Micro Devices [AMD] 79c970 [PCnet32 LANCE] (rev 10)
        02:01.0 Multimedia audio controller: Ensoniq ES1371 [AudioPCI-97] (rev 02)
        02:02.0 USB Controller: VMware Inc Abstract USB2 EHCI Controller


确定有设备以后,我们就可以开始我们的PCI设备驱动的编写了。


1、 首先我们介绍几个必须了解的结构体


pci_driver:这个结构体定义在include/linux/pci.h,这里我们最关注的是id_table、probe和remove。id_table是一个结构体数组,用来存放驱动程序适用的设备信息,probe用于检测设备,remove为设备卸载时调用。


struct pci_driver {
                struct list_head node;
                char *name;
                const struct pci_device_id *id_table;        /* must be non-NULL for probe to be called */
                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 (*resume) (struct pci_dev *dev);                /* Device woken up */
                int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable);        /* Enable wake event */
                void (*shutdown) (struct pci_dev *dev);


        struct pci_error_handlers *err_handler;
                struct device_driver driver;
                struct pci_dynids dynids;
        };


pci_dev:这个结构体同样也是定义在include/linux/pci.h,它详细的定义了PCI的设备的信息。这些信息我们可以通过查看proc及sys目录先相应文件得到。


struct pci_dev {
                struct list_head global_list;        /* node in list of all PCI devices */
                struct list_head bus_list;        /* node in per-bus list */
                struct pci_bus *bus;        /* bus this device is on */
                struct pci_bus *subordinate;        /* bus this device bridges to */


        void        *sysdata;        /* hook for sys-specific extension */
                struct proc_dir_entry *procent;        /* device entry in /proc/bus/pci */


        unsigned int        devfn;        /* encoded device & function index */
                unsigned short vendor;
                unsigned short device;
                unsigned short subsystem_vendor;
                unsigned short subsystem_device;
                unsigned int        class;        /* 3 bytes: (base,sub,prog-if) */
                u8         hdr_type; /* PCI header type (`multi' flag masked out) */
                u8         rom_base_reg; /* which config register controls the ROM */
                u8         pin;        /* which interrupt pin this device uses */


        struct pci_driver *driver; /* which driver has allocated this device */
                u64         dma_mask;         /* Mask of the bits of bus address this
                                                            device implements. Normally this is
                                                            0xffffffff. You only need to change
                                                            this if your device has broken DMA
                                                            or supports 64-bit transfers. */


        pci_power_t        current_state;        /* Current operating state. In ACPI-speak,
                                                            this is D0-D3, D0 being fully functional,
                                                            and D3 being off. */


        pci_channel_state_t error_state;        /* current connectivity state */
                struct        device        dev;        /* Generic device interface */


        /* device is compatible with these IDs */
                unsigned short vendor_compatible[DEVICE_COUNT_COMPATIBLE];
                unsigned short device_compatible[DEVICE_COUNT_COMPATIBLE];


        int         cfg_size; /* Size of configuration space */


        /*
                  * Instead of touching interrupt line and base address registers
                  * directly, use the values stored here. They might be different!
                */
                unsigned int irq;
                struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */


        /* These fields are used by common fixups */
                unsigned int        transparent:1;                /* Transparent PCI bridge */
                unsigned int        multifunction:1;        /* Part of multi-function device */
                /* keep track of device state */
                unsigned int is_enabled:1;        /* pci_enable_device has been called */
                unsigned int        is_busmaster:1;         /* device is busmaster */
                unsigned int        no_msi:1;         /* device may not use msi */
                unsigned int        no_d1d2:1;        /* only allow d0 or d3 */
                unsigned int        block_ucfg_access:1;        /* userspace config space access is blocked */
                unsigned int        broken_parity_status:1;        /* Device generates false positive parity */
                unsigned int        msi_enabled:1;
                unsigned int        msix_enabled:1;
        #ifndef __GENKSYMS__
                unsigned int is_managed:1;
        #endif


        u32        saved_config_space[16]; /* config space saved at suspend time */
                struct hlist_head saved_cap_space;
                struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
                int rom_attr_enabled;        /* has display of the rom attribute been enabled? */
                struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */
        #ifndef __GENKSYMS__
                u8        revision;        /* PCI revision, low byte of class word */
        #endif
        };


2、 这里我们开始编写一个简单的PCI驱动


●    LICENSE的声明必不可少:


MODULE_LICENSE ("GPL");


●    pci_driver的定义:


#define PCI_MODULE_NAME "dsp_pci_module"


static struct pci_driver new_pci_driver = {
        name:        PCI_MODULE_NAME,
        id_table:new_pci_tbl,
        probe:        pci_probe,
        remove: pci_remove,
        };


pci_driver中对应的pci_tbl定义:


#define NEW_PCI_VENDOR_ID 0x15ad
        #define NEW_PCI_DEVICE_ID 0x0405


static struct pci_device_id new_pci_tbl[] __initdata = {
                {NEW_PCI_VENDOR_ID, NEW_PCI_DEVICE_ID,
                        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
                {0,}
        };
        MODULE_DEVICE_TABLE (pci, new_pci_tbl);


●        probe函数的声明:


static int __devinit pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
        {
                /*在这里我们可以对PCI设备进行初始化及IO的注册等操作*/
                return 0;
        }


●        remove函数的声明:


static void __devexit pci_remove(struct pci_dev *pci_dev)
        {
                /*对资源释放*/
        }


●        module_init和module_exit这两个函数在驱动中必不可少,分别在驱动被加载和卸载时调用:


static int __init pci_init_module (void)
        {
                return pci_register_driver(&new_pci_driver);
        }


static void __exit pci_cleanup_module (void)
        {
                pci_unregister_driver(&new_pci_driver);
        }


module_init (pci_init_module);
        module_exit (pci_cleanup_module);


下面我们说一下这个驱动的执行过程:


系统加载模块是调用pci_init_module函数,在这个函数中我们通过pci_register_driver把new_pci_driver注册到系统中,这个函数首先检测id_table中定义的PCI信息是否和系统中的PCI信息有匹配,如果有则返回0,匹配成功后调用probe函数对PCI设备进行进一步的操作。同样在卸载模块时调用pci_cleanup_module,这个函数中通过pci_unregister_driver对new_pci_driver进行注销,这个会调用到remove函数。

文章评论0条评论)

登录后参与讨论
我要评论
0
3
关闭 站长推荐上一条 /2 下一条