zoukankan      html  css  js  c++  java
  • PCI设备驱动 三

    http://hi.baidu.com/linux_kernel/blog/item/5c8510dfbfdb9b1363279884.html   

    为了能看到实际的运行效果,我们选择8139too网卡作为示例,从该网卡的linux驱动程序中裁剪相关代码。
        一个PCI设备的驱动程序必须要向内核中的PCI核心描述自己。同时,它也必须告诉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 */
        };
        struct pci_driver {
            struct list_head node;
            char *name;
            struct module *owner;
            const struct pci_device_id *id_table; //驱动所能操纵的设备id列表。
            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);
            int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable);
            void (*shutdown) (struct pci_dev *dev);
            struct device_driver    driver;
            struct pci_dynids dynids;
        };
        pci_device_id唯一标识一个PCI设备。它的几个成员依次分别表示:厂商号,设备号,子厂商号,子设备号,类别,类别掩码(类可分为基类,子类),私有数据。每一个PCI设备的驱动程序都有一个pci_device_id的数组,用于告诉PCI核心自己能够驱动哪些设备。8139too的驱动程序定义它的pci_device_id数组如下:
            static struct pci_device_id rtl8139_pci_tbl[];
        该数组被初始化为8139系列的一组网卡,当PCI核心得到这个数组后,会拿数组中的每一项跟从PCI配置空间中读取到的数据进行比对,从而为该驱动程序找到正确的设备。而pci_driver代表一个pci驱动程序。成员id_talbe即是指向pci_device_id数组的指针。name是驱动程序的名字,probe完成探测工作,即拿pci_device_id数组与内核中的数据进行比对。remove完成驱动程序的移除工作。关键的成员就这几个。
        驱动程序通过pci_module_init向内核注册自己(我们有时会看到pci_register_driver函数,其实它们是同一个,在内核代码中会看到,只是一个简单的#define):
                pci_module_init(&pci_driver);
        调用函数后,如果pci_device_id数组中标识的设备存在于系统中,并且该设备恰好还没有驱动程序,则该驱动程序会被安装。下面我们来看从8139too驱动代码中裁剪的pci设备初始化代码:
    pci_driver.h:
    /* pci_driver.h
    * helinqiang@hotmail.com
    * 2006-3-5
    */
    #ifndef PCI_DRIVER_H
    #define PCI_DRIVER_H
    #include <linux/mod_devicetable.h>  //for struct pci_device_id
    #include <linux/module.h>           //for MODULE_DEVICE_TABLE
    #include <linux/pci.h>              //for struct pci_driver
    #define DRV_NAME    "8139too"
    #define DRV_VERSION "0.9.27"
    #define RTL8139_DRIVER_NAME   DRV_NAME " Fast Ethernet driver " DRV_VERSION
    typedef enum{
        RTL8139 = 0,
        RTL8129,
    }board_t;
    static struct pci_device_id rtl8139_pci_tbl[] = {
        {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
        {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
        {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
        {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
        {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
        {0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
        {0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
        {0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
        {0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
        {0x1259, 0xa11e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
        {0x14ea, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
        {0x14ea, 0xab07, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
        {0x11db, 0x1234, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
        {0x1432, 0x9130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
        {0x02ac, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
        {0x018a, 0x0106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
        {0x126c, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
        {0x1743, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
        {0x021b, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    #ifdef CONFIG_SH_SECUREEDGE5410
        /* Bogus 8139 silicon reports 8129 without external PROM :-( */
        {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    #endif
    #ifdef CONFIG_8139TOO_8129
        {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 },
    #endif
    /* some crazy cards report invalid vendor ids like
         * 0x0001 here.  The other ids are valid and constant,
         * so we simply don't match on the main vendor id.
         */
        {PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0, RTL8139 },
        {PCI_ANY_ID, 0x8139, 0x1186, 0x1300, 0, 0, RTL8139 },
        {PCI_ANY_ID, 0x8139, 0x13d1, 0xab06, 0, 0, RTL8139 },
        {0,}
    };
    MODULE_DEVICE_TABLE(pci, rtl8139_pci_tbl);
    static int __devinit rtl8139_init_one(struct pci_dev *pdev, const struct pci_device_id *id);
    static void __devexit rtl8139_remove_one(struct pci_dev *pdev);
    static struct pci_driver rtl8139_pci_driver = {
        .name       = DRV_NAME,
        .id_table   = rtl8139_pci_tbl,
        .probe      = rtl8139_init_one,
        .remove     = __devexit_p(rtl8139_remove_one),
    };
    #endif //PCI_DRIVER_H
    pci_driver.c:
    /* pci_driver.c
    * helinqiang@hotmail.com
    * 2006-3-5
    */
    #include "pci_driver.h"
    #include <linux/init.h>
    MODULE_AUTHOR("Linqiang He, Hangzhou China");
    MODULE_LICENSE("Dual BSD/GPL");
    static int __init rtl8139_init_module(void)
    {
        /* when we're a module, we always print a version message,
         * even if no 8139 board is found.
         */
    #ifdef MODULE
        printk (KERN_INFO RTL8139_DRIVER_NAME "\n");
    #endif
        return pci_module_init(&rtl8139_pci_driver);
    }
    static void __exit rtl8139_cleanup_module (void)
    {
        pci_unregister_driver(&rtl8139_pci_driver);
    }
    module_init(rtl8139_init_module);
    module_exit(rtl8139_cleanup_module);
    int __devinit rtl8139_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
    {
        //这里可插入各种调试代码,下文会有专门描述。
        return 0;
    }
    void __devexit rtl8139_remove_one (struct pci_dev *pdev)
    {
    }
        注册驱动程序成功后,rtl8139_init_one会被调用,在这个函数中,我们可以通过插入一些打印输出语句看到PCI的配置地址空间和I/O地址区域的一些情况。
        首先,插入以下语句:
                u16 vendor, device;
                pci_read_config_word(pdev, 0, &vendor);
                pci_read_config_word(pdev, 2, &device);
                printk(KERN_INFO "%x, %x\n", vendor, device);
        这段代码读取了网卡设备的配置地址空间的前四位,它正好是设备的厂商号和设备号。下面是输出:
                Mar  9 21:44:39 localhost kernel: 10ec, 8139
        10ec和8139就是我的网卡的厂商号和设备号了。
        再插入下列代码:
                u32 addr1,addr2,addr3, addr4,addr5,addr6;
                pci_read_config_dword(pdev, 16, &addr1);
                pci_read_config_dword(pdev, 20, &addr2);
                pci_read_config_dword(pdev, 24, &addr3);
                pci_read_config_dword(pdev, 28, &addr4);
                pci_read_config_dword(pdev, 32, &addr5);
                pci_read_config_dword(pdev, 36, &addr6);
                printk(KERN_INFO "%x,%x,%x,%x,%x,%x\n",addr1, addr2, addr3, addr4,addr5,addr6);
        这段代码读取网卡设备的6个I/O地址区域的址始位置。下面是输出:
        Mar  9 21:55:06 localhost kernel: 3401,e0000800,0,0,0,0
        可见,该设备只使用了前两个I/O地址区域,分别标识它的I/O端口区域和内存地址空间。
        另外,在这里,还可直接打印出网卡的MAC地址。不再详述。

  • 相关阅读:
    《构建之法》第四章学习笔记
    《深入理解计算机系统》第四章学习笔记
    《构建之法》第三章学习笔记
    《深入理解计算机系统》第三章学习笔记
    《文献管理与信息分析》第二讲学习笔记
    《深入理解计算机系统》第二章学习笔记
    20179223《Linux内核原理与分析》第十二周学习笔记
    《从问题到程序》第三章学习笔记
    51nod1256乘法逆元
    51nod1212无向图最小生成树
  • 原文地址:https://www.cnblogs.com/cute/p/2033061.html
Copyright © 2011-2022 走看看