從零開始之驅動發開、linux驅動(四十八、linux下的IIC核心層的建立[2])

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 },
	{ }
};


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章