原文鏈接:https://blog.csdn.net/goodnight1994/article/details/82082089 修改個別錯誤呈現
前言: 對於linux platform device 和driver,一個driver可對應多個device,通過名字進行匹配,調用驅動裏邊實現的probe函數,本文以一個i2c設備爲例,從驅動的i2c_add_driver()開始看源碼以及用比較笨的打log的方式分析如何一步一步調用的probe()函數。
分析的代碼基於linux kernel 4.15
/****************************************/
從module_init()開始看,
定義位置:kernel4.15/include/linux/init.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 your_driver =
{
.driver =
{
.owner = THIS_MODULE,
.name = YOUR_DRIVER_NAME_UP,
.of_match_table = your_driver_of_match,
},
.probe = your_probe_func,
.remove = your_i2c_drv_remove,
.id_table = your_i2c_drv_id_table,
//.suspend = your_i2c_drv_suspend,
//.resume = your_i2c_drv_resume,
};
static int __int your_init_func(void)
{
//…
i2c_add_driver(&your_i2c_driver);
//…
}
modul_init(your_init_func);
將 your_probe_func()地址裝進你實現的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);
your_probe_func();
接下來一個一個函數分析他們到底幹了什麼:
先看i2c_register_driver(),
定義位置:kernel/4.15/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.9/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.9/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.9/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.9/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.9/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.9/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