什么?又来看驱动的过程了?是滴,必需滴--I2C设备的注册过程(小白篇)

为什么写?临时起意。

写什么?一个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参数的来历,以后再也不会迷路了。
 

英踹斯汀

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值