当前位置: 首页 > Linux内核研究, 嵌入式Linux > 正文

我的内核学习笔记6:PCI驱动probe的一点认知

对于PCI的学习,在文章《初识PCI》和《再识PCI:一个PCI驱动实例》中有介绍,文中使用大量代码进行演示。但总觉得有些认知不到位。于是就再写一文。

一、PCI驱动一般框架

先看一下PCI驱动一般框架的示例代码:

#include <linux/pci.h>

#include <linux/module.h>

static int misc_pci_probe(struct pci_dev *dev,
        const struct pci_device_id *ent)
{
    switch (dev->device)
    {
    case 0x0f1c:
        printk("found LPC pci device...\n");
        break;
    case 0x0f12:
        printk("found SMBus pci device...\n");
        // 此处可注册设备,如platform设备
        // platform_device_register
        
        // 设置PCI驱动数据,在本驱动退出时,可获取到paltform设备从而卸载之
        pci_set_drvdata(dev, pdev);
        break;
        
    default:
        printk("unknown pci device 0x%x...\n", dev->device);
        break;
    }

    return ret;
}

static void misc_pci_remove(struct pci_dev *dev)
{
    // 得到platform设备,卸载
    struct platform_device *pdev = pci_get_drvdata(dev);

    platform_device_unregister(pdev);
}

// 在这里指定PCI设备ID,PCI_VDEVICE会组装厂家ID和PCI设备ID,后面的是驱动私有数据,可以传递必要的信息。当然也可不写
static const struct pci_device_id misc_pci_tbl[] = {
    { PCI_VDEVICE(INTEL, 0x0f1c), 0 },
    { PCI_VDEVICE(INTEL, 0x0f12), 1 }, // SMBus 
    { },
};
MODULE_DEVICE_TABLE(pci, misc_pci_tbl);

static struct pci_driver misc_pci_driver = {
    .name           = "misc_pci",
    .id_table       = misc_pci_tbl,
    .probe          = misc_pci_probe,
    .remove         = misc_pci_remove,
};

// 注册PCI驱动
module_pci_driver(misc_pci_driver);

MODULE_DESCRIPTION("PCI driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Late Lee<[email protected]>");

二、几点说明

1、内核启动时根据深度优先方式扫描PCI总线上的设备。比如,当前的PCI设备是PCI桥的话,就会继续扫描其下的设备,直到没有为止。

2、在系统启动时,扫描PCI,找到PCI设备,如果该设备已经注册驱动,则调用驱动的probe函数。如果PCI设备已经有了对应的驱动,则不会再被调用。以上述代码为例,假如系统启动时已经有了ID为0x0f1和0x0f12的驱动,则上述的probe函数不会被调用到。可以认为当前已经有驱动占用着这2个设备。在《再识PCI:一个PCI驱动实例》一文中,还指定了E1000_DEV_ID_I211_COPPER这个ID,但没有识别出来。是因为其对应的IGB驱动被加载了。当然,如果当前不存在对应的驱动,就会调用到probe函数。

3、有多个相同设备的也会继续调用probe。直到扫描结束。比如,有2个相同的网卡设备(即网卡芯片ID相同)处于不同的PCI总线情况时,它们对应pci_device_id只有一个ID。但它们在不同PCI总线上,当扫描到设备,会调用2次对应驱动的probe函数。

三、bus(总线)、driver(驱动)、device(设备)

顺着PCI驱动,再概述一下bus、driver、device这几个概念。

一条bus上挂着许多device,而device需要driver才能工作。内核有注册device和注册driver的概念。注册device只是将deivce添加到内核中,该设备还不能工作。而注册driver,就是在对应的bus上找到device,从而调用driver的probe函数进行初始化,而后继续其它的事情。

device和driver通过某些方式匹配,就能正常工作。比如platform设备根据name来匹配的,PCI设备根据ID匹配。所以看到很多内核代码在定义platform_device时指定name,而在定义platform_driver时指定driver成员的name,因为只有这样两者才能匹配。一般地,发行版本的Linux系统都带有很多驱动。如前段时间研究WIFI,将Intel无线网卡和atheros无线网卡插到安装有发行版本的系统的设备上,就可以正常工作。

在/sys/bus/目录下有当前系统各种bus。比如i2c总线、PCI总线、SPI总线、platform总线。每种bus目录均有device和driver,在其中列出该总线上所有的设备和对应的驱动。比如platform设备、驱动分别在/sys/bus/platform/devices和/sys/bus/platform/drivers这两个目录。

 

近来经济拮据,如本文对阁下有帮助,可慷慨解囊赞助笔者以输出更多好文章。
支付宝[email protected] 或 微信fly_camel_fly 均可。感谢!
14373903313201                                  14373903313202

本文固定链接: http://www.latelee.org/embedded-linux/kernel-note-6-pci-driver-probe.html

目前暂无评论

发表评论

*

快捷键:Ctrl+Enter