驱动probe 被调用的流程分析

对于linux platform device 和driver,一个driver可对应多个device,通过名字进行匹配,调用驱动里边实现的probe函数
本文以一个i2c设备为例,从驱动的i2c_add_driver()开始看源码,分析如何一步一步调用的probe()函数。
分析的代码基于linux kernel msm-4.4。

/****************************************/

从module_init()开始看,

定义位置:kernel/msm-4.4/include/linux/module.h

源码里对该函数的说明:

/** 
 * module_init() - driver initialization entry point 
 * @x: function to be run at kernel boot time or module insertion 
 * 
 * module_init() will either be called during do_initcalls() (if 
 * builtin) or at module insertion time (if a module).  There can only 
 * be one per module.    
 */  

作为每个驱动的入口函数,若代码有编译进kernel的话
在开机kernel 启动(或模块被动态加载的时候)的时候被do_initcalls()调用,

从kernel如何一步一步走到do_initcalls()进而调用module_init(),调用链如下:

start_kernel();//kernel的第一个函数

rest_init();

kernel_init();

kernel_init_freeable();

do_basic_setup();

do_initcalls();//

do_initcall_level();

//......
module_init();

/*************************************/

接着通过module_init()调用你在驱动里边实现的init函数:
对于一个i2c设备来说,做法如下:

static struct i2c_driver aw9106_i2c_driver = {
    .driver = {
        .name = AW9106_I2C_NAME,
        .owner = THIS_MODULE,
        .of_match_table = of_match_ptr(aw9106_dt_match),
    },
    .probe = aw9106_i2c_probe,
    .remove = aw9106_i2c_remove,
    .id_table = aw9106_i2c_id,
};


static int __init aw9106_i2c_init(void)//入口函数 
{
    int ret = 0;

    pr_info("aw9106 driver version %s\n", AW9106_VERSION);

    ret = i2c_add_driver(&aw9106_i2c_driver);//设备注册 
    if(ret){
        pr_err("fail to add aw9106 device into i2c\n");
        return ret;
    }

    return 0;
}
module_init(aw9106_i2c_init);


static void __exit aw9106_i2c_exit(void)//出口函数 
{
    i2c_del_driver(&aw9106_i2c_driver);//注销设备 
}
module_exit(aw9106_i2c_exit);

将 aw9106_i2c_driver()地址装进你实现的struct i2c_driver的.probe 成员,

接下来,从i2c_add_driver()如何调用到你的probe函数,调用链如下:


i2c_register_driver();
driver_register();
bus_add_driver();
driver_attach();
__driver_attach (for your device);
driver_probe_device();
really_probe();
i2c_device_probe (this is what dev->bus->probe is for an i2c driver);
 aw9106_i2c_driver();

接下来一个一个函数分析他们到底干了什么:

先看i2c_register_driver(),

定义位置:kernel/msm-4.4/drivers/i2c/i2c-core.c

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)  
{  
    int res;  


/* Can't register until after driver model init */  
if (WARN_ON(!is_registered)) {  
    printk("unlikely(WARN_ON(!i2c_bus_type.p and return -EAGAIN.\n");  
    return -EAGAIN;  
}  
      

/* add the driver to the list of i2c drivers in the driver core */  
driver->driver.owner = owner;  
driver->driver.bus = &i2c_bus_type;//这边添加了bus type  
INIT_LIST_HEAD(&driver->clients);  

/* When registration returns, the driver core 
 * will have called probe() for all matching-but-unbound devices. 
 */  
res = driver_register(&driver->driver);  //这边走进去
if (res){  
    printk("driver_register return res : %d\n", res);  
    return res;  
}  

pr_debug("driver [%s] registered\n", driver->driver.name);  
printk(KERN_INFO "======>lkhlog %s\n",driver->driver.name);    

/* iWalk the adapters that are already present */  
i2c_for_each_dev(driver, __process_new_driver);  

return 0;  

}

type定义如下:

struct bus_type i2c_bus_type = {  
    .name       = "i2c",  
    .match      = i2c_device_match,  
    .probe      = i2c_device_probe,//后面会回调这个函数,在上面的调用链里有提到  
    .remove     = i2c_device_remove,  
    .shutdown   = i2c_device_shutdown,  
};  

添加了bus_type,然后调用driver_register(),

接着看driver_register(),

定义位置:kernel/msm-4.4/drivers/base/driver.c

/** 
 * driver_register - register driver with bus 注册总线驱动,和总线类型联系起来 
 * @drv: driver to register 
 * 
 * We pass off most of the work to the bus_add_driver() call, 
 * since most of the things we have to do deal with the bus 
 * structures. 
 */  
int driver_register(struct device_driver *drv)  
	{  
	    int ret;  
	    struct device_driver *other;  
  
    BUG_ON(!drv->bus->p);  
    //……  
    other = driver_find(drv->name, drv->bus);//确认该驱动是否已经注册过  
    printk(KERN_WARNING "======>lkh driver_find, other : %d", other);  
     //……  
    ret = bus_add_driver(drv);//主要从这儿走进去  
    printk(KERN_WARNING "======>lkh bus_add_driver, ret : %d", ret);  
    if (ret)  
        return ret;  
    ret = driver_add_groups(drv, drv->groups);  
    if (ret) {  
        printk(KERN_WARNING "======>lkh bus_remove_driver");  
        bus_remove_driver(drv);  
        return ret;  
    }  
    kobject_uevent(&drv->p->kobj, KOBJ_ADD);  
  
    return ret;  
} 

接着看 bus_add_driver(),

定义位置:kernel/msm-4.4/drivers/base/bus.c

/** 
 * bus_add_driver - Add a driver to the bus. 
 * @drv: driver. 
 */  
int bus_add_driver(struct device_driver *drv)  
{  
  
  
    bus = bus_get(drv->bus);//重新获取到前边装进去的bus  
    if (!bus) {   
        printk(KERN_ERR "======>lkh return -EINVAL\n");  
        return -EINVAL;  
    }  
  
    printk(KERN_ERR "======>lkh bus_add_driver\n");  
  
    pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);  
  
    priv = kzalloc(sizeof(*priv), GFP_KERNEL);  
    if (!priv) {  
        error = -ENOMEM;  
        goto out_put_bus;  
    }  
    klist_init(&priv->klist_devices, NULL, NULL);  
    priv->driver = drv;//driver_private 里边指针指向device_driver  
    drv->p = priv;  
    //device_driver也有指针指向driver_private,这样就可通过其中一个获取到另外一个  
    priv->kobj.kset = bus->p->drivers_kset;  
    error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,  
                     "%s", drv->name);  
    if (error)  
        goto out_unregister;  
  
    klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);  
    if (drv->bus->p->drivers_autoprobe) {  
        printk(KERN_ERR "======>lkh drv->bus->p->drivers_autoprobe == true, name : %s\n", drv->name);  
        if (driver_allows_async_probing(drv)) {  
            pr_debug("bus: '%s': probing driver %s asynchronously\n",  
                drv->bus->name, drv->name);  
            printk(KERN_ERR "======>lkh bus: '%s': probing driver %s asynchronously\n",  
                drv->bus->name, drv->name);  
            async_schedule(driver_attach_async, drv);  
        } else {  
            printk(KERN_ERR "======>lkh enter driver_attach, name : %s\n", drv->name);  
            error = driver_attach(drv);//这边走进去  
            printk(KERN_ERR "======>lkh driver_attach, error : %d\n", error);  
            if (error)  
                goto out_unregister;  
        }  
    }  
    printk(KERN_ERR "======>lkh bus_add_driver 2, name : %s \n", drv->name);  
    //若前边driver_attach()返回没有错误的话,  
    //这边会进去创建相关节点,链接  
    module_add_driver(drv->owner, drv);  
    //……
} 

接着看driver_attach(),直接看函数说明就能明白了,
定义位置:kernel/msm-4.4/drivers/base/dd.c

/** 
 * driver_attach - try to bind driver to devices. 
 * @drv: driver. 
 * 
 * Walk the list of devices that the bus has on it and try to 
 * match the driver with each one.  If driver_probe_device() 
 * returns 0 and the @dev->driver is set, we've found a 
 * compatible pair. 
 */  
int driver_attach(struct device_driver *drv)  
{  
    return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);  
}  
在bus_for_each_dev()里边,将会遍历总线上的每个设备,并调用__driver_attach() 函数,如下:
定义位置:kernel/msm-4.9/drivers/base/bus.c
/** 
 * bus_for_each_dev - device iterator. 
 * @bus: bus type. 
 * @start: device to start iterating from. 
 * @data: data for the callback. 
 * @fn: function to be called for each device. 
 * 
 * Iterate over @bus's list of devices, and call @fn for each, 
 * passing it @data. If @start is not NULL, we use that device to 
 * begin iterating from. 
 * 
 */  
int bus_for_each_dev(struct bus_type *bus, struct device *start,  
             void *data, int (*fn)(struct device *, void *))  
{  
   //……          
    klist_iter_init_node(&bus->p->klist_devices, &i,  
                 (start ? &start->p->knode_bus : NULL));  
  
    printk(KERN_WARNING "======>lkh while\n");  
    //获取每一个device,并调用__driver_attach  
    while ((dev = next_device(&i)) && !error){  
        error = fn(dev, data);  //__driver_attach(dev,data)  
        //printk(KERN_WARNING "======>lkh while enter\n");  
          
    //}  
  
    klist_iter_exit(&i);  
    printk(KERN_WARNING "======>lkh bus_for_each_dev end \n");  
    return error;  
}  

接着走进__driver_attach()继续看,
定义位置:kernel/msm-4.4/drivers/base/dd.c

static int __driver_attach(struct device *dev, void *data)  
{  
    struct device_driver *drv = data;  
    int ret;  
  
//调用 i2c_device_match(),匹配设备和驱动  
ret = driver_match_device(drv, dev);  
//……  
if (!dev->driver){  
    printk(KERN_DEBUG "======>lkh enter driver_probe_device \n");  
    driver_probe_device(drv, dev);//这边走进去  
}     
device_unlock(dev);  
if (dev->parent)  
    device_unlock(dev->parent);  

return 0;  

}
先看一下driver_match_device(),
定义位置:kernel/msm-4.4/drivers/base/base.h\

 kernel/msm-4.9/drivers/i2c/i2c-core.c
static inline int driver_match_device(struct device_driver *drv,  
                      struct device *dev)  
{  
    return drv->bus->match ? drv->bus->match(dev, drv) : 1;  
}  
  
  
static int i2c_device_match(struct device *dev, struct device_driver *drv)  
{  
	    struct i2c_client   *client = i2c_verify_client(dev);  
	    struct i2c_driver   *driver;  
	  
  
    /* Attempt an OF style match */  
    //在这儿匹配,三种匹配方式:  
    //Compatible match has highest priority       
    //Matching type is better than matching name  
    //Matching name is a bit better than not  
    if (i2c_of_match_device(drv->of_match_table, client))  
        return 1;  
  
    /* Then ACPI style match */  
    if (acpi_driver_match_device(dev, drv))  
        return 1;  
  
    driver = to_i2c_driver(drv);  
  
    /* Finally an I2C match */  
    if (i2c_match_id(driver->id_table, client))  
        return 1;  
  
    return 0;  
}  

这个i2c_device_match()就是前边在函数i2c_register_driver()里边装进driver->driver.bus的:

driver->driver.bus = &i2c_bus_type;  
struct bus_type i2c_bus_type = {  
    .name       = "i2c",  
    .match      = i2c_device_match,  
    .probe      = i2c_device_probe,  
    .remove     = i2c_device_remove,  
    .shutdown   = i2c_device_shutdown,  
};  

若匹配成功,那么就走进driver_probe_device()里边了,
定义位置:kernel/msm-4.4/drivers/base/dd.c

/** 
 * driver_probe_device - attempt to bind device & driver together 
 */  
int driver_probe_device(struct device_driver *drv, struct device *dev)  
{  
    int ret = 0;  
  
    printk(KERN_DEBUG "======>lkh driver_probe_device enter\n");  
  
    //检测设备是否已经注册  
    if (!device_is_registered(dev))  
        return -ENODEV;  
    //……
    pr_debug("bus: '%s': %s: matched device %s with driver %s\n",  
         drv->bus->name, __func__, dev_name(dev), drv->name);  
    pm_runtime_barrier(dev);  
    ret = really_probe(dev, drv);//这边走进去  
    //……
    return ret;  
} 
若设备已经注册,那么调用really_probe():
定义位置:kernel/msm-4.9/drivers/base/dd.c
static int really_probe(struct device *dev, struct device_driver *drv)  
{  
     // ……
re_probe:  
    //设备与驱动匹配,device里边的driver装上了其对应的driver  
    dev->driver = drv;  
  
  
    /* 
	     * Ensure devices are listed in devices_kset in correct order 
	     * It's important to move Dev to the end of devices_kset before 
	     * calling .probe, because it could be recursive and parent Dev 
	     * should always go first 
	     */  
	    devices_kset_move_last(dev);  
	  
	    if (dev->bus->probe) {  
	        ret = dev->bus->probe(dev);//调用i2c_device_probe()  
	        if (ret)  
	            goto probe_failed;  
	    } else if (drv->probe) {  
	        ret = drv->probe(dev);
	        if (ret)  
	            goto probe_failed;  
	    }  
	    //……
	}
在i2c_device_probe()里边,将调用your_probe_func(),
定义位置: kernel/msm-4.9/drivers/i2c/i2c-core.c
static int i2c_device_probe(struct device *dev)  
{  
    struct i2c_client   *client = i2c_verify_client(dev);  
    struct i2c_driver   *driver;  
    int status;  
  
    if (!client)  
        return 0;  
  
	    driver = to_i2c_driver(dev->driver);  
	    // ……
	    /* 
	     * When there are no more users of probe(), 
	     * rename probe_new to probe. 
	     */  
	    if (driver->probe_new)  
	        status = driver->probe_new(client);  
	    else if (driver->probe)//调用your_probe_func()  
	        status = driver->probe(client,  
	                       i2c_match_id(driver->id_table, client));  
	      
	    else  
	        status = -EINVAL;  
	  
	    if (status)  
	        goto err_detach_pm_domain;  
	  
	    return 0;  
	  
err_detach_pm_domain:  
    dev_pm_domain_detach(&client->dev, true);  
err_clear_wakeup_irq:  
    dev_pm_clear_wake_irq(&client->dev);  
    device_init_wakeup(&client->dev, false);  
    return status;  
} 

至此,就调用到了你在自己驱动里边实现的your_probe_func()里边了,
关于probe函数,在里边主要做的主要工作如下:

a,给设备上电;
主要用到的函数为:

	regulator_get()     
    regulator_set_voltage()    

 
   具体上多少电,在哪一个电路上电,和你在dtsi文件的配置有关,而dtsi如何来配置,
   也取决于设备在硬件上接哪路电路供电以及芯片平台对配置选项的定义。

b,初始化设备;

c,创建相关节点接口;

        主要用到的函数为: sysfs_create_group();
        需要实现static struct attribute_group 的一个结构体以及你需要的接口,如:


static DEVICE_ATTR(enable,  S_IRUGO|S_IWUSR|S_IWGRP, your_deriver_enable_sho
w, your_deriver_enable_store);  
static DEVICE_ATTR(delay,   S_IRUGO|S_IWUSR|S_IWGRP, your_deriver_delay_show
,  your_deriver_delay_store);  
static DEVICE_ATTR(debug,   S_IRUGO|S_IWUSR|S_IWGRP, your_deriver_debug_show
,  your_deriver_debug_store);  
static DEVICE_ATTR(wake,    S_IWUSR|S_IWGRP,         NULL,            your_d
eriver_wake_store);  
static DEVICE_ATTR(rawdata, S_IRUGO|S_IWUSR|S_IWGRP, your_deriver_data_show, 
  NULL);  
static DEVICE_ATTR(dump,    S_IRUGO|S_IWUSR|S_IWGRP, your_deriver_dump_show, 
  NULL);  
  
static struct attribute *your_deriver_attributes[] = {  
    &dev_attr_enable.attr,  
    &dev_attr_delay.attr,  
    &dev_attr_debug.attr,  
    &dev_attr_wake.attr,  
    &dev_attr_rawdata.attr,  
    &dev_attr_dump.attr,  
    NULL  
};  
  
  
static struct attribute_group your_driver_attribute_group = {  
    .attrs = your_driver_attributes  
};  



sysfs_create_group(&p_data->input_dev->dev.kobj, &your_driver_attribute_group);  

创建完节点后,就可以在linux应用层空间通过操作节点来调用驱动里边的对应函数了。
如,对节点enable,delay的read(或cat),write(或echo),就对应到其驱动实现的

your_deriver_enable_show, your_deriver_enable_store
your_deriver_delay_show,  your_deriver_delay_store
<think>好的,我现在要回答用户关于Linux驱动开发中probe函数的作用与实现的问题。首先,我需要回忆自己之前学过的驱动开发知识,并结合用户提供的引用内容来组织答案。 用户提到了几个引用,其中引用[1]和[3]解释了probe函数何时被调用,即当设备与驱动匹配时,总线会调用驱动probe函数。引用[5]详细说明了probe函数的结构和功能,比如初始化设备、分配资源等。引用[2]和[4]提到了在probe函数中注册设备节点以及使用cdev_init等函数。这些信息需要整合到回答中。 首先,probe函数的作用是什么?根据引用[1]和[5],probe函数是在设备被检测到时由内核调用的,用于初始化设备、分配资源、设置寄存器等。这应该是作用部分的核心内容。 然后是probe函数的实现流程。根据引用[5]中的示例,probe函数通常会进行设备初始化,检查硬件是否存在,分配资源,注册设备节点,设置操作函数等步骤。同时,引用[2]和[4]提到注册设备节点和使用cdev_init,这些步骤需要在实现流程中详细说明。 接下来,需要讨论probe调用时机,这里引用[1]、[3]指出当设备和驱动匹配时,总线(如platform总线)会调用probe函数。可能还需要提到设备树匹配或ACPI匹配的情况,但用户提供的引用中没有这些,可能需要避免推测,只根据现有引用说明。 另外,用户提供的代码片段如cdev_init和文件操作函数的结构,应该在实现流程中涉及,比如初始化字符设备和注册文件操作函数。引用[4]中的cdev_init是用来初始化cdev结构体的,并关联file_operations,这应该在资源初始化部分提到。 需要注意的是,用户可能对probe函数参数(如client或id)的来源有疑问,但根据引用[1],用户提到参数不是驱动定义的,而是由系统传递的。这部分可能需要解释参数的意义,比如struct platform_device *pdev代表对应的设备信息,驱动可以通过它获取资源或设备树数据。 最后,引用[5]中的示例代码结构可以作为一个例子,说明probe函数通常包含错误处理步骤,比如初始化失败时返回错误码。 现在需要将以上信息组织成结构清晰的回答,分点说明作用、实现流程调用时机,并附上示例代码。同时,生成相关问题需要考虑用户可能进一步想了解的内容,比如probe函数和init函数的区别,如何调试probe函数,或者匹配机制的具体细节。 检查是否有遗漏的引用内容,比如引用[3]提到probe调用前的流程,这可能需要在调用时机部分简要提及,但用户问题主要关注probe本身,所以可能不需要深入流程细节。 确保使用中文回答,行内公式正确使用$...$,独立公式用$$...$$,但这个问题可能不涉及数学公式,所以可以忽略。代码块需要用正确的语法标注,比如C代码和可能的Kernel函数。 现在开始组织内容: 作用部分: - 设备初始化(硬件检查、寄存器配置) - 资源分配(内存、中断) - 注册设备节点(引用[2]中的内容) - 关联操作函数(file_operations,引用[4]的cdev_init) 实现流程: 1. 参数解析(从platform_device获取资源) 2. 硬件检查 3. 资源分配 4. 初始化设备结构体(如cdev_init) 5. 注册设备(如cdev_add) 6. 文件操作函数设置 调用时机: - 设备与驱动匹配时(总线触发,引用[1]和[3]) 示例代码结构: 如引用[5]中的代码,展示probe函数的基本结构,包括初始化步骤和错误处理。 相关问题可能涉及: 1. probe函数何时被调用? 2. 如何获取设备资源(如设备树中的数据)? 3. probe函数出错如何处理? 4. 与init函数的区别? 5. 如何调试probe函数中的问题? 需要确保每个引用在回答中被正确引用,例如引用[1]用于调用时机,引用[5]用于作用,引用[2][4]用于实现步骤。 最后检查是否所有用户提供的引用都被合理利用,没有遗漏关键点,同时回答结构清晰,分点明确。</think>### Linux驱动开发:probe函数的作用与实现 #### 一、probe函数的作用 1. **设备初始化** probe函数负责完成设备的硬件初始化,例如配置寄存器、检查设备是否存在等。这是驱动与设备建立联系的第一步[^5]。 2. **资源分配** 包括申请内存、中断、DMA等硬件资源。例如,通过`request_mem_region`或`devm_kmalloc`分配内存。 3. **注册设备节点** 在probe函数中调用`cdev_init`初始化字符设备,并通过`cdev_add`将其注册到内核,最终在`/dev`目录生成设备文件[^2][^4]。 4. **关联操作函数** 绑定设备节点的文件操作函数(如`open`、`read`、`write`),实现用户空间与硬件的交互。 #### 二、probe函数的实现流程 1. **参数解析** 通过`struct platform_device *pdev`获取设备信息(如设备树中的配置或ACPI参数),例如使用`platform_get_resource`获取资源。 2. **硬件检查与初始化** 验证设备是否响应,并配置寄存器。例如,通过I2C总线发送探测指令确认设备存在。 3. **资源分配** ```c // 示例:申请中断 int irq = platform_get_irq(pdev, 0); request_irq(irq, interrupt_handler, IRQF_SHARED, "my_device", dev); ``` 4. **设备节点注册** ```c // 初始化字符设备 cdev_init(&my_cdev, &my_fops); my_cdev.owner = THIS_MODULE; cdev_add(&my_cdev, dev_num, 1); ``` 5. **文件操作函数绑定** ```c static struct file_operations my_fops = { .owner = THIS_MODULE, .open = my_device_open, .read = my_device_read, .release = my_device_release, }; ``` #### 三、probe函数的调用时机 当内核检测到**设备与驱动匹配**时,总线(如platform总线、I2C总线)会调用probe函数。匹配条件包括: - 设备树中的`compatible`属性与驱动定义的`of_match_table`匹配。 - ACPI ID或设备名称匹配[^1][^3]。 #### 四、示例代码 ```c static int my_driver_probe(struct platform_device *pdev) { struct my_device *dev; // 1. 分配设备结构体 dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); // 2. 获取资源(如寄存器地址) dev->regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); // 3. 初始化硬件 if (my_device_hw_init(dev) < 0) { dev_err(&pdev->dev, "硬件初始化失败"); return -ENODEV; } // 4. 注册字符设备 cdev_init(&dev->cdev, &my_fops); cdev_add(&dev->cdev, dev_num, 1); // 5. 保存设备上下文 platform_set_drvdata(pdev, dev); return 0; } ``` #### 五、常见问题 1. **参数来源** probe函数的参数(如`struct platform_device *pdev`)由内核自动传递,包含设备在总线上的信息[^5]。 2. **错误处理** 若probe函数返回非零值,内核会标记驱动加载失败,并可能卸载模块。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值