PCI驱动程序获取MAC地址

时间:2013-11-19 19:12:10

标签: linux driver linux-device-driver ethernet pci

我正在尝试编写一个可以显示以太网卡MAC地址的pci驱动程序。 在VM和我的以太网卡上运行Ubuntu是Intel,如下所示 00:08.0以太网控制器:英特尔公司82540EM千兆以太网控制器(rev 02)

我能够从英特尔网站获得相同的数据表,并且根据数据表,它说IO地址映射到Bar 2(参见第87页),并且可以使用RAL / RAH寄存器读取MAC偏移RAL(05400h + 8 * n; R / W)和RAH(05404h +8n; R / W) 2 18h IO寄存器基址(位31:2)0b mem

根据这些信息,我写了一个小型PCI驱动程序,但我总是得到MAC作为fff,当我进一步调试时,我看到io_base地址始终为零。

以下是代码

 1 /*
  2  Program to find a device on the PCI sub-system 
  3 */
  4 #define VENDOR_ID       0x8086
  5 #define DEVICE_ID       0x100e
  6 
  7 #include <linux/kernel.h>
  8 #include <linux/module.h>
  9 #include <linux/stddef.h>
 10 #include <linux/pci.h>
 11 #include <linux/init.h>
 12 #include <linux/cdev.h>
 13 #include <linux/device.h>
 14 #include <asm/io.h>
 15 
 16 #define LOG(string...) printk(KERN_INFO string)
 17 
 18 #define CDEV_MAJOR      227
 19 #define CDEV_MINOR      0
 20 
 21 
 22 MODULE_LICENSE("GPL");
 23 
 24 struct pci_dev *pci_dev;
 25 unsigned long mmio_addr;
 26 unsigned long reg_len;
 27 unsigned long *base_addr;
 28 
 29 int device_probe(struct pci_dev *dev, const struct pci_device_id *id);
 30 void device_remove(struct pci_dev *dev);
 31 
 32 struct pci_device_id  pci_device_id_DevicePCI[] =
 33 {
 34         {VENDOR_ID, DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
 35 };
 36 
 37 struct pci_driver  pci_driver_DevicePCI =
 38 {
 39   name: "MyPCIDevice",
 40   id_table: pci_device_id_DevicePCI,
 41   probe: device_probe,
 42   remove: device_remove
 43 };
 44 
 45 
 46 int init_module(void)
 47 {
 48         //struct pci_dev *pdev = NULL;
 49         int ret = 0;
 50 
 51         pci_register_driver(&pci_driver_DevicePCI);
 52 
 53         return ret;
 54 }
 55 
 56 void cleanup_module(void)
 57 {
 58         pci_unregister_driver(&pci_driver_DevicePCI);
 59 
 60 }
 61 
 62 #define REGISTER_OFFSET 0x05400
 64 int device_probe(struct pci_dev *dev, const struct pci_device_id *id)
 65 {
 66         int ret;
 67         int bar = 2; // Bar to be reserved
 68         unsigned long io_base = 0;
 69         unsigned long mem_len = 0;
 70         unsigned int register_data = 0;
 71 
 72         LOG("Device probed");
 73 
 74         /* Reserve the access to PCI device */
 75         ret = pci_request_region(dev, bar, "my_pci");
 76         if (ret) {
 77                 printk(KERN_ERR "request region failed :%d\n", ret);
 78                 return ret;
 79         }
 80 
 81         ret  = pci_enable_device(dev);
 82         if (ret < 0 ) LOG("Failed while enabling ... ");
 83 
 84         io_base = pci_resource_start(dev, bar);
 85         mem_len = pci_resource_len(dev, bar);
 86 
 87         request_region(io_base, mem_len, "my_pci");
 88         register_data = inw(io_base + REGISTER_OFFSET);
 89         printk(KERN_INFO "IO base = %lx", io_base);
 90         printk(KERN_INFO "MAC = %x", register_data);
 91 
 92         return ret;
 93 }
 94 
95 void device_remove(struct pci_dev *dev)
 96 {
 97   pci_release_regions(dev);
 98   pci_disable_device(dev);
 99 }
100 

我卡的lspci -x输出 00:08.0以太网控制器:英特尔公司82540EM千兆以太网控制器(rev 02) 00:86 80 0e 10 07 00 30 02 02 00 00 02 00 40 00 00 10:00 00 82 f0 00 00 00 00 41 d2 00 00 00 00 00 00 20:00 00 00 00 00 00 00 00 00 00 00 00 86 80 1e 00 30:00 00 00 00 dc 00 00 00 00 00 00 00 09 01 ff 00

任何人都可以让我知道我做错了什么吗?

1 个答案:

答案 0 :(得分:0)

我修改了您的代码并对更改进行了评论。我已删除所有现有注释以避免混淆,并且只修改了您的探测功能。

/* We need a place to store a logical address for unmapping later */
static void* logical_address;

int device_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
    int ret;
    int bar_mask;          /* BAR mask (this variable) and the integer BAR */
    int requested_bar = 2; /* (this variable) are not the same thing, so give them */
                           /* separate variables */
    resource_size_t io_base = 0; /* use kernel macros instead of built-in datatypes */
    resource_size_t mem_len = 0;
    unsigned int register_data = 0;

    LOG("Device probed");

    /* add this call to get the correct BAR mask */
    bar_mask = pci_select_bars(dev, 0);

    /* switched order - enable device before requesting memory */
    ret  = pci_enable_device(dev);
    if (ret < 0 ) LOG("Failed while enabling ... ");

    /* for this call, we want to pass the BAR mask, NOT the integer bar we want */
    ret = pci_request_region(dev, bar_mask, "my_pci");

    if (ret) {
        printk(KERN_ERR "request region failed :%d\n", ret);
        return ret;
    }

    /* it is in THESE calls that we request a specific BAR */
    io_base = pci_resource_start(dev, requested_bar);
    mem_len = pci_resource_len(dev, requested_bar);

    /* you don't need to request anything again, so get rid of this line: */
    /* request_region(io_base, mem_len, "my_pci"); */

    /* you're missing an important step: we need to translate the IO address
     * to a kernel logical address that we can actually use. Add a call to
     * ioremap()
     */

    logical_address = ioremap(io_base, mem_len);

    /* we need to use the logical address returned by ioremap(), not the physical
     * address returned by resource_start
     */
    register_data = inw(logical_address + REGISTER_OFFSET);
    printk(KERN_INFO "IO base = %lx", io_base);
    printk(KERN_INFO "MAC = %x", register_data);

    return ret;
}

您需要在device_remove()例程中添加对iounmap()的相应调用。看看Intel E100E driver source code的一些好例子。