我是靠谱客的博主 犹豫百褶裙,最近开发中收集的这篇文章主要介绍驱动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 %sn", AW9106_VERSION);

    ret = i2c_add_driver(&aw9106_i2c_driver);//设备注册 
    if(ret){
        pr_err("fail to add aw9106 device into i2cn");
        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 : %dn", res);  
    return res;  
}  

pr_debug("driver [%s] registeredn", driver->driver.name);  
printk(KERN_INFO "======>lkhlog %sn",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 -EINVALn");  
        return -EINVAL;  
    }  
  
    printk(KERN_ERR "======>lkh bus_add_drivern");  
  
    pr_debug("bus: '%s': add driver %sn", 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 : %sn", drv->name);  
        if (driver_allows_async_probing(drv)) {  
            pr_debug("bus: '%s': probing driver %s asynchronouslyn",  
                drv->bus->name, drv->name);  
            printk(KERN_ERR "======>lkh bus: '%s': probing driver %s asynchronouslyn",  
                drv->bus->name, drv->name);  
            async_schedule(driver_attach_async, drv);  
        } else {  
            printk(KERN_ERR "======>lkh enter driver_attach, name : %sn", drv->name);  
            error = driver_attach(drv);//这边走进去  
            printk(KERN_ERR "======>lkh driver_attach, error : %dn", 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 whilen");  
    //获取每一个device,并调用__driver_attach  
    while ((dev = next_device(&i)) && !error){  
        error = fn(dev, data);  //__driver_attach(dev,data)  
        //printk(KERN_WARNING "======>lkh while entern");  
          
    //}  
  
    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 entern");  
  
    //检测设备是否已经注册  
    if (!device_is_registered(dev))  
        return -ENODEV;  
    //……
    pr_debug("bus: '%s': %s: matched device %s with driver %sn",  
         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

最后

以上就是犹豫百褶裙为你收集整理的驱动probe 被调用的流程分析的全部内容,希望文章能够帮你解决驱动probe 被调用的流程分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(41)

评论列表共有 0 条评论

立即
投稿
返回
顶部