Radia的专栏

专注嵌入式系统开发。

linux中i2c子系统代码结构详解

本文介绍一下linux驱动代码中i2c子系统的驱动代码结构和使用方法,示例平台文件为高通msmXXXX平台
一,i2c子系统代码结构

i2c-core.c

1,使用bus_register进行总线注册

2,提供与具体硬件无关的操作逻辑供i2c-dev.c中使用
3,将操作逻辑通过EXPORT_SYMBOL导出到整个内核,供其他基于i2c的设备驱动调用
i2c-dev.c
实现i2c基本read、write功能,创建/dev/i2c-x节点
bus文件夹中的i2c-msm-v2.c
对i2c_core中物理层相关的操作进行实现,如clk配置、收发函数等等
二,i2c-dev中对i2c-croe的使用
i2c-dev.c中没有probe函数,在初始化函数中注册了各个设备对应的/dev/i2c-X节点。
static int __init i2c_dev_init(void)
{
	int res;

	res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
	i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
	i2c_dev_class->dev_groups = i2c_groups;

	/* Keep track of adapters which will be added or removed later */
	res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
	i2c_for_each_dev(NULL, i2cdev_attach_adapter);

	return 0;
}
i2cdev_attach_adapter函数中的get_free_i2c_dev会向i2c设备list添加一个新项。device_create会在/dev下创建节点。
static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
	struct i2c_adapter *adap;
	struct i2c_dev *i2c_dev;
	int res;


	adap = to_i2c_adapter(dev);


	i2c_dev = get_free_i2c_dev(adap);
	/* register this i2c device with the driver core */
	i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
				     MKDEV(I2C_MAJOR, adap->nr), NULL,
				     "i2c-%d", adap->nr);
	return 0;
}

之后可以在open函数中由次设备号获取相应i2c adapter
static int i2cdev_open(struct inode *inode, struct file *file)
{
	unsigned int minor = iminor(inode);
	struct i2c_client *client;
	struct i2c_adapter *adap;
	struct i2c_dev *i2c_dev;

	i2c_dev = i2c_dev_get_by_minor(minor);
	adap = i2c_get_adapter(i2c_dev->adap->nr);
	client = kzalloc(sizeof(*client), GFP_KERNEL);
	snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
	client->adapter = adap;
	file->private_data = client;
	return 0;
}
读写函数可以有open时获取的i2c adapter去调用i2c-core中的传输函数完成通信。
static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
		loff_t *offset)
{
	char *tmp;
	int ret;
	struct i2c_client *client = file->private_data;
	tmp = kmalloc(count, GFP_KERNEL);
	ret = i2c_master_recv(client, tmp, count);
	kfree(tmp);
	return ret;
}
三,基于i2c子系统的的驱动对i2c-core 的使用
对于其它基于i2c协议的设备驱动,可以使用i2c_register_driver()函数即可将本驱动注册为i2c子系统中。
static int __init aw2013_init(void)
{
	return i2c_add_driver(&aw2013_driver);
}
module_init(aw2013_init);
i2c_add_driver函数最终会调用i2c_register_driver,其中会在i2c_client设备的list中增加一个新项,并完成设备注册。
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
	int res;
	driver->driver.owner = owner;
	driver->driver.bus = &i2c_bus_type;
	res = driver_register(&driver->driver);
	INIT_LIST_HEAD(&driver->clients);
	i2c_for_each_dev(driver, __process_new_driver);
	return 0;
}
可以使用probe返回的的i2c_client结构体调用i2c-core.c中EXPORT_SYMBOL得到的函数进行读写操作。
static int aw2013_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	psx93XX_t this = 0;
	int ret;
	if (this) {
		/* setup i2c communication */
		this->bus = client;
		i2c_set_clientdata(client, this);

		/* record device struct */
		this->pdev = &client->dev;
	....
	
	return ret;
}
static int write_register(psx93XX_t this, u8 address, u8 value)
{
	struct i2c_client *i2c = 0;
	char buffer[2];
	int returnValue = 0;

	buffer[0] = address;
	buffer[1] = value;
	returnValue = -ENOMEM;
	if (this && this->bus) {
		i2c = this->bus;

		returnValue = i2c_master_send(i2c, buffer, 2);
	}
	return returnValue;
}



阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。如本文对您有帮助,欢迎点赞评论。 https://blog.csdn.net/RadianceBlau/article/details/77966449
个人分类: Linux Driver
上一篇linux模块(module_init)、子系统(subsys_initcall)入口函数详解
下一篇linux双向链表List结构分析
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭