为什么写?临时起意。
写什么?一个Battery Fuel Guage,I2C设备驱动的注册过程。
遇到一个电池Guage的使用,写一下对I2C设备注册的整个过程。该电池Gauage型号是Maxim
max1720x/max1721x。
驱动入口,通常就是probe函数,例如:
static int max1720x_probe(struct i2c_client *client,const struct i2c_device_id *id),在这个函数中,进行Guage的初始化/信息获取等操作。
那问题来了,这个接口的参数怎么来的呢?是啥作用?带着这两个问题,简单跟一内核代码(kernle4.19)。
接口隶属于下面的结构:
- static struct i2c_driver max1720x_i2c_driver = {
- .driver = {
- .name = "max1720x", //这个名字在驱动和设备的匹配中没有起到作用,仅仅是在设备表示上有意义.
-
- #ifdef CONFIG_OF //OF open firmware.
- .of_match_table = of_match_ptr(max1720x_dt_match), // of: open firmware
- #endif
- .pm = &max1720x_pm_ops,
- },
- .probe = max1720x_probe,
- .remove = max1720x_remove,
- .id_table = max1720x_id,
- };
- /* module_i2c_driver(max1720x_i2c_driver); */
很清楚能够看到这是一个struct i2c_driver 类型的变量的一个成员函数。那么,这个变量如何使用?可以看到,内核设备驱动的统一入口使用了它。
- static int __init max1720x_battery_init(void)
- {
- int ret;
-
- ret = i2c_add_driver(&max1720x_i2c_driver);
- if (ret)
- printk(KERN_ERR "Unable to register MAX1720x driver\n");
-
- return ret;
- }
- module_init(max1720x_battery_init); //这个module_init就是设备驱动的统一入口,后面再细说。反正内核启动之后,会逐一去运行module_init中的函数。
module_init( xxx ),这个暂且不提,就知道系统内核启动过程中会去调用其参数(接口)就行。
max1720x_battery_init( xxx )函数提只进行了一项操作--> i2c_add_driver ( xxx );
好,现在注意力集中了,我们要正式进入linux 内核的世界 了,英踹斯汀。
i2c_add_driver( xxx )定义在linux/include/i2c.h
- /* use a define to avoid include chaining to get THIS_MODULE */
- #define i2c_add_driver(driver) \
- i2c_register_driver(THIS_MODULE, driver)
- //第一句的注释是没看懂没关系,老规矩,先别急,随着眼界的不断提高,慢慢就懂了。
-
- //这是一个宏,对应的定义位于 driver/i2c/i2c-core.c , 我们正式来到了i2c的驱动核心了。
- /*
- * An i2c_driver is used with one or more i2c_client (device) nodes to access
- * i2c slave chips, on a bus instance associated with some i2c_adapter.
- * 这个注释能想到,刚开始学习驱动时候,一个驱动对应多个同类型设备的概念。
- */
-
- int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
- {
- int res;
-
- /* Can't register until after driver model init */
- if (unlikely(WARN_ON(!i2c_bus_type.p)))
- 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; //先给要注册到内核的驱动对应的总线。显然,这个是i2c总线。物理上,总线是通常直接到CPU,总线上安插着控制器和多个设备。
- 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); //这里注册的不是i2c_driver 的实例(max1720x_i2c_driver),而是device_driver 的一个变量。这里借用C++面向对象的说法,i2c_driver 继承了struct device_driver。 可以说注册的是父类的一个实例。
- if (res)
- return res;
-
- pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
-
- /* Walk the adapters that are already present */ //这个操作很神奇,为什么要去遍历控制器adapter?老规矩,先别急,随着眼界的不断提高,慢慢就懂了。目前知道一个事情就行了,bus对附着着的设备一视同仁,都是设备。
- i2c_for_each_dev(driver, __process_new_driver);
-
- return 0;
- }
- EXPORT_SYMBOL(i2c_register_driver); //简单的说,就是这里定义,他出使用。
重点看下 res = driver_register(&driver->driver); 定义位于:driver/base/driver.c。将要进行一个最基本struct device_driver变量的一个注册操作。
- /**
- * 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. //可见这个bus多重要。要想富,先铺路啊。
- */
- int driver_register(struct device_driver *drv) //驱动模型的最小粒度数据结构
- {
- int ret;
- struct device_driver *other;
-
- BUG_ON(!drv->bus->p); //很好玩的一个调试手段,英踹斯汀
-
- if ((drv->bus->probe && drv->probe) || //drv->probe肯定得是空,为什么?因为父类的方法一定是需要重写的方法。父类只负责内核的设备模型,而具体实现/行为是每个设备自己实现。
- (drv->bus->remove && drv->remove) ||
- (drv->bus->shutdown && drv->shutdown))
- printk(KERN_WARNING "Driver '%s' needs updating - please use "
- "bus_type methods\n", drv->name);
-
- other = driver_find(drv->name, drv->bus); //这里要干什么?内核要确认目前的总线上并没有哦注册同名的驱动,如果有重名的,说明有冲突或者说已经注册过驱动了。
- if (other) {
- printk(KERN_ERR "Error: Driver '%s' is already registered, "
- "aborting...\n", drv->name);
- return -EBUSY;
- }
-
- ret = bus_add_driver(drv); //父类driver负责设备模型,将驱动加入和内核世界。这里会走驱动和设备的匹配过程。暂时按下不表。
- if (ret)
- return ret;
- ret = driver_add_groups(drv, drv->groups); //将device_driver放入到驱动组用于设备模型构建。
- if (ret) {
- bus_remove_driver(drv);
- return ret;
- }
- kobject_uevent(&drv->p->kobj, KOBJ_ADD); //uevent又是一个英踹斯汀话题,暂时按下不表。
-
- return ret;
- }
- EXPORT_SYMBOL_GPL(driver_register);
我们来看看这个功能最复杂的也是最重要的bus_add_driver函数.
- /**
- * bus_add_driver - Add a driver to the bus.
- * @drv: driver.
- */
- int bus_add_driver(struct device_driver *drv)
- {
- struct bus_type *bus;
- struct driver_private *priv;
- int error = 0;
-
- bus = bus_get(drv->bus);
- if (!bus)
- return -EINVAL;
-
- pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
-
- priv = kzalloc(sizeof(*priv), GFP_KERNEL); //为driver申请私有数据空间
- if (!priv) {
- error = -ENOMEM;
- goto out_put_bus;
- }
- klist_init(&priv->klist_devices, NULL, NULL);
- priv->driver = drv;
- drv->p = priv;
- 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) { //i2c_bus 中没有初始化p。那如果初始化了,会有什么好处?设计上的优势?
- if (driver_allows_async_probing(drv)) {
- pr_debug("bus: '%s': probing driver %s asynchronously\n",
- drv->bus->name, drv->name);
- async_schedule(driver_attach_async, drv);
- } else {
- error = driver_attach(drv); //驱动和对应的设备进行链接/和对应的bus进行链接
- if (error)
- goto out_unregister;
- }
- }
- module_add_driver(drv->owner, drv); //
-
- error = driver_create_file(drv, &driver_attr_uevent);
- if (error) {
- printk(KERN_ERR "%s: uevent attr (%s) failed\n",
- __func__, drv->name);
- }
- error = driver_add_groups(drv, bus->drv_groups);
- if (error) {
- /* How the hell do we get out of this pickle? Give up */
- printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
- __func__, drv->name);
- }
-
- if (!drv->suppress_bind_attrs) {
- error = add_bind_files(drv); //这是在做什么?
- if (error) {
- /* Ditto */
- printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
- __func__, drv->name);
- }
- }
-
- return 0;
-
- out_unregister:
- kobject_put(&priv->kobj);
- /* drv->p is freed in driver_release() */
- drv->p = NULL;
- out_put_bus:
- bus_put(bus);
- return error;
- }
来到,driver_attach(...); driver/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.
- * 这段注释很优美,能够知道很多内容。1. 内核中已有设备list。2. 用当前的driver去遍历 3. 遍历bus上已经有的所有设备。
- */
- int driver_attach(struct device_driver *drv)
- {
- return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); //__driver_attach,作为参数传入的函数调用。优美!
- }
- EXPORT_SYMBOL_GPL(driver_attach);
接着是,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, //这个fn厉害了!
- * passing it @data. If @start is not NULL, we use that device to
- * begin iterating from.
- *
- * We check the return of @fn each time. If it returns anything
- * other than 0, we break out and return that value.
- *
- * NOTE: The device that returns a non-zero value is not retained
- * in any way, nor is its refcount incremented. If the caller needs
- * to retain this data, it should do so, and increment the reference
- * count in the supplied callback.
- */
- int bus_for_each_dev(struct bus_type *bus, struct device *start,
- void *data, int (*fn)(struct device *, void *))
- {
- struct klist_iter i; //是不是有点迭代器内味。
- struct device *dev;
- int error = 0;
-
- if (!bus || !bus->p)
- return -EINVAL;
-
- klist_iter_init_node(&bus->p->klist_devices, &i,
- (start ? &start->p->knode_bus : NULL));
- while ((dev = next_device(&i)) && !error) //原来设备时这么来的,某bus上的设备列表。
- error = fn(dev, data); //从上面的调用,可以看到这个data就是struct driver *device_driver, 这里即i2c_driver中的device_driver. fn即__driver_attach()
- klist_iter_exit(&i);
- return error;
- }
- EXPORT_SYMBOL_GPL(bus_for_each_dev);
来了来了,fb( dev, data) -> __driver_attach(dev,drv):
- static int __driver_attach(struct device *dev, void *data)
- {
- struct device_driver *drv = (struct device_driver*) data; //进行强行类型转换。这里的void*是无类型指针,可以指向任何类型,不要尝试进行解引用。英踹斯汀。
-
- /*
- * Lock device and try to bind to it. We drop the error
- * here and always return 0, because we need to keep trying
- * to bind to devices and some drivers will return an error
- * simply if it didn't support the device.
- * 优美的注释,可以说“清水出芙蓉,天然去雕饰”的注释,为什么返回0,因为我允许你可以范错误。
- * driver_probe_device() will spit a warning if there
- * is an error.
- */
-
- if (!driver_match_device(drv, dev)) //通过idtable中的名称和设备树中的名称进行匹配;确定系统启动过程中,dt正确,对应的设备创建合理,最后bus上挂有对应的设备.
- return 0;
- if (lock_parent(dev))
- device_lock(dev->parent);
- device_lock(dev);
- if (!dev->driver) //这个必然为空,大家想想为什么?(想想多态性,用父类的指针指向子类时候,对应的接口,我们会根据上下文context,决定要调用子类中的的接口,这里父类的接口一定格式一个虚,需要子类复写。所以,这里的作为父类的device,移动不用实现)
- driver_probe_device(drv, dev); //准备调用驱动中的probe接口,进行设备和驱动的绑定,这里对应的就是max1270x电池设备驱动的max1720x_probe。
- device_unlock(dev);
- if (lock_parent(dev))
- device_unlock(dev->parent);
-
- return 0;
- }
最核心的时候到了,driver_probe_device(...);
- /**
- * driver_probe_device - attempt to bind device & driver together
- * @drv: driver to bind a device to
- * @dev: device to try to bind to the driver
- *
- * This function returns -ENODEV if the device is not registered,
- * 1 if the device is bound successfully and 0 otherwise.
- *
- * This function must be called with @dev lock held. When called for a
- * USB interface, @dev->parent lock must be held as well.
- *
- * If the device has a parent, runtime-resume the parent before driver probing.
- */
- int driver_probe_device(struct device_driver *drv, struct device *dev)
- {
- int ret = 0;
-
- 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);
-
- if (dev->parent)
- pm_runtime_get_sync(dev->parent);
-
- pm_runtime_barrier(dev); //英踹斯汀,暂时按下不表。
- ret = really_probe(dev, drv); //这个名称起的真是好,真正的探寻设备。
- pm_request_idle(dev);
-
- if (dev->parent)
- pm_runtime_put(dev->parent);
-
- return ret;
- }
really_probe(...)//简单的说就是真正的探测设备来了。要用子类/具体类型的设备驱动来绑定对应的dts中定义的设备。
- static int really_probe(struct device *dev, struct device_driver *drv)
- {
- int ret = 0;
- int local_trigger_count = atomic_read(&deferred_trigger_count);
-
- atomic_inc(&probe_count);
- pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
- drv->bus->name, __func__, drv->name, dev_name(dev));
- WARN_ON(!list_empty(&dev->devres_head));
-
- dev->driver = drv;
-
- /* If using pinctrl, bind pins now before probing */
- ret = pinctrl_bind_pins(dev); //将设备和用pin(GPIO)的情况绑定,英踹斯汀。
- if (ret)
- goto probe_failed;
-
- if (driver_sysfs_add(dev)) {
- printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
- __func__, dev_name(dev));
- goto probe_failed;
- }
-
- if (dev->pm_domain && dev->pm_domain->activate) {
- ret = dev->pm_domain->activate(dev); //激活设备,优美。
- if (ret)
- goto probe_failed;
- }
-
- if (dev->bus->probe) { //如果对应的总线上有probe接口,那么调用总线的probe. 最终还是call到了所注册驱动的probe接口.
- ret = dev->bus->probe(dev); //这个probe就是i2c_bus 中的probe,我们回到i2c-core.c
- if (ret)
- goto probe_failed;
- } else if (drv->probe) {
- ret = drv->probe(dev); //driver都没实现,怎么会有接口实现呢?
- if (ret)
- goto probe_failed;
- }
-
- pinctrl_init_done(dev);
-
- if (dev->pm_domain && dev->pm_domain->sync)
- dev->pm_domain->sync(dev);
-
- driver_bound(dev); //新面孔
- ret = 1;
- pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
- drv->bus->name, __func__, dev_name(dev), drv->name);
- goto done;
-
- probe_failed:
- devres_release_all(dev);
- driver_sysfs_remove(dev);
- dev->driver = NULL;
- dev_set_drvdata(dev, NULL);
- if (dev->pm_domain && dev->pm_domain->dismiss)
- dev->pm_domain->dismiss(dev);
-
- switch (ret) {
- case -EPROBE_DEFER:
- /* Driver requested deferred probing */
- dev_dbg(dev, "Driver %s requests probe deferral\n", drv->name);
- driver_deferred_probe_add(dev);
- /* Did a trigger occur while probing? Need to re-trigger if yes */
- if (local_trigger_count != atomic_read(&deferred_trigger_count))
- driver_deferred_probe_trigger();
- break;
- case -ENODEV:
- case -ENXIO:
- pr_debug("%s: probe of %s rejects match %d\n",
- drv->name, dev_name(dev), ret);
- break;
- default:
- /* driver matched but the probe failed */
- printk(KERN_WARNING
- "%s: probe of %s failed with error %d\n",
- drv->name, dev_name(dev), ret);
- }
- /*
- * Ignore errors returned by ->probe so that the next driver can try
- * its luck.
- */
- ret = 0;
- done:
- atomic_dec(&probe_count);
- wake_up(&probe_waitqueue);
- return ret;
- }
i2c-core.c中的i2c_bus_type -> probe
- static int i2c_device_probe(struct device *dev)
- {
- struct i2c_client *client = i2c_verify_client(dev); //这个verify是具体如何check的呢?
- struct i2c_driver *driver;
- int status;
-
- if (!client)
- return 0;
-
- if (!client->irq) {
- int irq = -ENOENT;
-
- if (dev->of_node) {
- irq = of_irq_get_byname(dev->of_node, "irq");
- if (irq == -EINVAL || irq == -ENODATA)
- irq = of_irq_get(dev->of_node, 0);
- } else if (ACPI_COMPANION(dev)) {
- irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0);
- }
- if (irq == -EPROBE_DEFER)
- return irq;
- if (irq < 0)
- irq = 0;
-
- client->irq = irq;
- }
-
- driver = to_i2c_driver(dev->driver); //来了来了, 跑到了最后的i2c_drvier了;具体类型的bus,具体设备类型的驱动。是i2c就是i2c_driver, 完美!
- if (!driver->probe || !driver->id_table)
- return -ENODEV;
-
- if (client->flags & I2C_CLIENT_WAKE) {
- int wakeirq = -ENOENT;
-
- if (dev->of_node) {
- wakeirq = of_irq_get_byname(dev->of_node, "wakeup"); //唤醒系统功能?
- if (wakeirq == -EPROBE_DEFER)
- return wakeirq;
- }
-
- device_init_wakeup(&client->dev, true); //注册的设备是否具有唤醒系统功能,英踹斯汀,暂时按下不表。
-
- if (wakeirq > 0 && wakeirq != client->irq)
- status = dev_pm_set_dedicated_wake_irq(dev, wakeirq);
- else if (client->irq > 0)
- status = dev_pm_set_wake_irq(dev, client->irq);
- else
- status = 0;
-
- if (status)
- dev_warn(&client->dev, "failed to set up wakeup irq");
- }
-
- dev_dbg(dev, "probe\n");
-
- status = of_clk_set_defaults(dev->of_node, false);
- if (status < 0)
- goto err_clear_wakeup_irq;
-
- status = dev_pm_domain_attach(&client->dev, true);
- if (status == -EPROBE_DEFER)
- goto err_clear_wakeup_irq;
-
- status = driver->probe(client, i2c_match_id(driver->id_table, client)); //最终还是call的注册驱动的probe接口进行初始化!来了来了。这里的client,id_table 和具体的电池驱动中的接口对上了:static int max1720x_probe(struct i2c_client *client,const struct i2c_device_id *id)。
- 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;
- }
static int max1720x_probe(struct i2c_client *client,const struct i2c_device_id *id) 开始运行。到此,从具体i2c设备驱动的注册到内核中的基础代码的执行过程。最终,我们知道了,具体i2c驱动中的probe参数的来历,以后再也不会迷路了。
英踹斯汀