【问题标题】:PCI driver to fetch MAC address用于获取 MAC 地址的 PCI 驱动程序
【发布时间】:2013-12-03 12:11:44
【问题描述】:

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

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

基于此信息,我编写了一个小型 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 直流 00 00 00 00 00 00 00 09 01 ff 00

谁能告诉我我做错了什么?

【问题讨论】:

  • @pkumarn..必须尝试使用​​pci_read_config_dword()函数读取,该函数用于访问配置空间寄存器?

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


【解决方案1】:

我已经修改了您的代码并评论了更改。为了避免混淆,我已经删除了你所有现有的 cmets,并且只修改了你的探测函数。

/* 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 中的一些很好的例子。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-01
    • 2012-10-06
    • 2016-04-26
    • 2018-09-04
    • 1970-01-01
    • 1970-01-01
    • 2014-07-24
    相关资源
    最近更新 更多