日期:2014-05-16  浏览次数:20721 次

Linux IIC框架(下)

 水平有限,描述不当之处还请之处,转载请注明出处http://blog.csdn.net/vanbreaker/article/details/7743184        

       本节结合i2cdev,来阐述Linux下的IIC是如何进行数据传输的。和spidev类似,i2cdev也是一个通用的设备驱动,但是又有些不同。在spidev中,spidev驱动注册会和相应的从设备绑定,也就是说spidev对应的是一个实际的从设备,而i2cdev只是一个虚拟的从设备,因为它并不对应一个实际的从设备,而是和IIC控制器adapter绑定,只有当用户调用open()打开一个设备文件时,才会创建一个虚拟的client。那么怎么知道该i2cdev对应哪个从设备呢?其实很简单,只需要用户在用户空间多做一个工作,就是通过Iocntl()函数来设定从设备的地址,当然这个从设备必须是挂接在对应的adapter下的。所以i2cdev作为一个虚拟设备,本身并不对应任何设备,但又可以对应任意一个挂接在其依附的adapter下的设备。

先来看下i2cdev的定义

struct i2c_dev {
	struct list_head list;//用于链入i2c_dev_list
	struct i2c_adapter *adap;//依附的IIC主控制器
	struct device *dev;
};


 

第一步,当然是初始化i2cdev模块,将i2cdev作为字符设备注册,创建一个i2cdev类,并且注册i2cdev的驱动

static int __init i2c_dev_init(void)
{
	int res;

	printk(KERN_INFO "i2c /dev entries driver\n");

	/*注册i2cdev字符设备*/
	res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
	if (res)
		goto out;

         /*创建i2cdev类*/
	i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
	if (IS_ERR(i2c_dev_class)) {
		res = PTR_ERR(i2c_dev_class);
		goto out_unreg_chrdev;
	}

	/*添加i2cdev驱动*/
	res = i2c_add_driver(&i2cdev_driver);
	if (res)
		goto out_unreg_class;

	return 0;

out_unreg_class:
	class_destroy(i2c_dev_class);
out_unreg_chrdev:
	unregister_chrdev(I2C_MAJOR, "i2c");
out:
	printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
	return res;
}


i2c_add_driver()只是i2c_register_driver()的一个封装,进入i2c_register_driver()

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
	...
         ...
	...
	driver->driver.bus = &i2c_bus_type;
         ...
 	res = driver_register(&driver->driver);
         ...
	/*遍历i2c_adapter_class下的i2c_adapter,并调用__attach_adapter*/
	class_for_each_device(&i2c_adapter_class, NULL, driver,
			      __attach_adapter);
         ...
	return 0;
}

我们可以看到主要操作有两个,一个是driver_register(),还有一个就是遍历adapter,并调用__attach_adapter()

实际上这里的driver_register()并没有太多实际的作用,因为它无法完成i2cdev_driver和从设备的匹配,为什么呢?我们来看一下i2c_bus_type中定义的match函数

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

	/* make legacy i2c drivers bypass driver model probing entirely;
	 * such drivers scan each i2c adapter/bus themselves.
	 */
	if (!is_newstyle_driver(driver))
		return 0;

	/* 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;
}


 

可以看到,只有当driver->id_table不为空时,才会调用实际的匹配函数i2c_match_id(),而i2cdev_driver中并没有定义id_table,因此是无法和任何从设备匹配的。

static struct i2c_driver i2cdev_driver = {
	.driver = {
		.name	= "dev_driver",
	},
	.attach_adapter	= i2cdev_attach_adapter,
	.detach_adapter	= i2cdev_detach_adapter,
};