从零开始之驱动发开、linux驱动(四十八、linux下的IIC核心层的建立[2])

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_16777851/article/details/88909555

I2C核心(drivers/i2c/i2c-core.c) 中提供了一组不依赖于硬件平台的接口函数, 这个文件一般不需要被工程师修改, 但是理解其中的主要函数非常关键, 因为I2C总线驱动和设备驱动之间以I2C核心作为纽带。

首先看一下i2c核心层的创建函数。

static int __init i2c_init(void);
static void __exit i2c_exit(void);

static int __init i2c_init(void)
{
	int retval;

	retval = bus_register(&i2c_bus_type);
	if (retval)
		return retval;
#ifdef CONFIG_I2C_COMPAT
	i2c_adapter_compat_class = class_compat_register("i2c-adapter");
	if (!i2c_adapter_compat_class) {
		retval = -ENOMEM;
		goto bus_err;
	}
#endif
	retval = i2c_add_driver(&dummy_driver);
	if (retval)
		goto class_err;
	return 0;

class_err:
#ifdef CONFIG_I2C_COMPAT
	class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
	bus_unregister(&i2c_bus_type);
	return retval;
}

static void __exit i2c_exit(void)
{
	i2c_del_driver(&dummy_driver);
#ifdef CONFIG_I2C_COMPAT
	class_compat_unregister(i2c_adapter_compat_class);
#endif
	bus_unregister(&i2c_bus_type);
}

postcore_initcall(i2c_init);
module_exit(i2c_exit);

这里要注意的是总线的注册

struct bus_type i2c_bus_type = {
	.name		= "i2c",
	.match		= i2c_device_match,
	.probe		= i2c_device_probe,
	.remove		= i2c_device_remove,
	.shutdown	= i2c_device_shutdown,
	.pm		= &i2c_device_pm_ops,
};

总线提供的match方法:match方法用来进行 device 和driver 的匹配,在向总线注册设备或是驱动的的时候会调用此方法。

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
	struct i2c_client	*client = i2c_verify_client(dev);
	struct i2c_driver	*driver;

	if (!client)
		return 0;

	driver = to_i2c_driver(drv);
	/* match on an id table if there is one */
	if (driver->id_table)
		return i2c_match_id(driver->id_table, client) != NULL;

	return 0;
}

static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
						const struct i2c_client *client)
{
	while (id->name[0]) {
		if (strcmp(client->name, id->name) == 0)
			return id;
		id++;
	}
	return NULL;
}

用struct i2c_client 来描述一个具体的IIC设备,这里指的是client device 。

如果IIC驱动的id_table 存在的话,使用i2c_match_id 进行函数进行匹配。匹配的方法是拿id_table 中的每一项与client 的name 进行匹配,如果名字相同则匹配成功。从这里我们可以看出IIC总线的匹配方式与platform 总线的匹配方式是不同的:

  • IIC总线根据设备名字和驱动中的id_table进行匹配(这里adapoer类似设备驱动)
  • platform总线根据设备名字和设备驱动名字进行匹配
     

总线提供的probe方法:probe方法在完成设备和驱动的配对之后调用执行。


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);
	if (!driver->probe || !driver->id_table)
		return -ENODEV;
	client->driver = driver;                //设备匹配到驱动后,把驱动绑定在设备上
	if (!device_can_wakeup(&client->dev))   //提供了电源管理相关的则,设置相关标志
		device_init_wakeup(&client->dev,
					client->flags & I2C_CLIENT_WAKE);
	dev_dbg(dev, "probe\n");

    /* 设备和驱动匹配上后,执行驱动程序的probe函数 */
	status = driver->probe(client, i2c_match_id(driver->id_table, client));
	if (status) {
		client->driver = NULL;
		i2c_set_clientdata(client, NULL);
	}
	return status;
}


/* 当然这里这里的probe函数,有个参数是匹配表的某一项
 * 因为一个驱动通常可以支持多个设备,每个设备可能有自己一些特殊的设置,
 * 这里用匹配表刚好也可进行参数传递
 */
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
						const struct i2c_client *client)
{
	while (id->name[0]) {
		if (strcmp(client->name, id->name) == 0)
			return id;
		id++;
	}
	return NULL;
}

/* 这个就是我在内核中搜到的匹配表的的其中之一,
 * 第一个参数名字用来和设备匹配 
 * 第二个参数刚好用来给驱动程序传递参数
 */
static const struct i2c_device_id adm1021_id[] = {
	{ "adm1021", adm1021 },
	{ "adm1023", adm1023 },
	{ "max1617", max1617 },
	{ "max1617a", max1617a },
	{ "thmc10", thmc10 },
	{ "lm84", lm84 },
	{ "gl523sm", gl523sm },
	{ "mc1066", mc1066 },
	{ }
};


 

猜你喜欢

转载自blog.csdn.net/qq_16777851/article/details/88909555