获取MAC地址的PCI驱动程序



我试图编写一个可以显示以太网卡MAC地址的pci驱动程序。在VM上运行Ubuntu,我的以太网卡是Intel的,如下所示00:08.0以太网控制器:Intel Corporation 82540EM千兆以太网控制器(修订版02)

我能够从英特尔网站上获得相同的数据表,根据数据表,IO地址映射到Bar 2(参考第87页),MAC可以使用RAL/RAH寄存器读取,该寄存器的偏移量为RAL(05400h+8*n;R/W)和RAH(05404h+8nR/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 :%dn", 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以太网控制器:Intel Corporation 82540EM千兆以太网控制器(修订版02)00:86 80 0e 10 07 00 30 02 02 00 02 00 40 0010:00 00 82 f0 00 00 00 41 d2 00 00 00 0020:00 00 00 0030:00 00 00 dc 00 00 00 00 09 01 ff 00

有人能告诉我我做错了什么吗?

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

/* 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 :%dn", 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()的相应调用。请查看英特尔E100E驱动程序源代码,以获取一些好的示例。

最新更新