I2C驱动框架分析(2):I2C框架源码分析

I2C驱动框架分析(1):I2C重要概念与数据结构
I2C驱动框架分析(2):I2C框架源码分析
I2C驱动框架分析(3):DW_I2C驱动分析

第二章:I2C框架源码分析

2.1 I2C框架初始化

linux系统启动后调用i2c_init函数,向系统初始化i2c框架,函数主要内容为:

static int __init i2c_init(void)
{
	int retval;

	retval = bus_register(&i2c_bus_type);
	if (retval)
		return retval;

	is_registered = true;
    
	retval = i2c_add_driver(&dummy_driver);
	if (retval)
		goto class_err;

	return 0;
	...
}

上述函数主要有两个工作内容:

  1. 调用bus_register函数,向系统注册i2c bus;
  2. 调用i2c_add_driver函数,向系统添加一个空驱动。
2.2 添加I2C驱动

在向系统添加某一个i2c设备驱动时,通常使用module_i2c_driver宏来实现,其源码为:

#define module_i2c_driver(__i2c_driver) \
	module_driver(__i2c_driver, i2c_add_driver, \
			i2c_del_driver)

添加驱动调用i2c_add_driver,删除驱动i2c_del_driver

i2c_add_driver函数,向系统添加一个i2c驱动,实际调用的函数是i2c_register_driver,函数内容为:

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
	int res;

	/* Can't register until after driver model init */
	if (WARN_ON(!is_registered))
		return -EAGAIN;

	// 1. 例化成员变量
	driver->driver.owner = owner;
	driver->driver.bus = &i2c_bus_type;
	INIT_LIST_HEAD(&driver->clients);

	// 2.注册驱动
	res = driver_register(&driver->driver);
	if (res)
		return res;

	pr_debug("driver [%s] registered\n", driver->driver.name);

	// 3.遍历已经存在的适配器
	i2c_for_each_dev(driver, __process_new_driver);

	return 0;
}

上述函数主要有以下工作内容:

  1. 例化成员变量

  2. 调用driver_register,向系统注册驱动

  3. 调用i2c_for_each_dev,遍历已经存在的适配器,其实现流程为:

    ->i2c_for_each_dev(driver, __process_new_driver)
    	->__process_new_driver
        	->i2c_do_add_adapter
        		->i2c_detect  //检测确认该设备是否在该适配器上
        			->temp_client = kzalloc()   //创建一个临时client
        			->temp_client->adapter = adapter   //赋值遍历的适配器
        			->i2c_detect_address
        				->i2c_check_addr_busy
        				->i2c_default_probe
        				->driver->detect()
        				->i2c_new_device()  //创建新的client,并添加到链表中
    
2.3 添加I2C设备

向系统添加设备,调用i2c_dev_init函数实现,其内容为:

static int __init i2c_dev_init(void)
{
	int res;
	//注册设备号
	res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");
	if (res)
		goto out;
	//创建i2c的class
	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;
	}
	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);
	if (res)
		goto out_unreg_class;

	//绑定到已经存在的适配器
	i2c_for_each_dev(NULL, i2cdev_attach_adapter);

	return 0;
	...
}

在上述内容中,调用i2cdev_attach_adapter函数绑定到已经存在的适配器,其内容为:

static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
	struct i2c_adapter *adap;
	struct i2c_dev *i2c_dev;
	int res;

	if (dev->type != &i2c_adapter_type)
		return 0;
	adap = to_i2c_adapter(dev);
	//开辟i2c_dev
	i2c_dev = get_free_i2c_dev(adap);
	if (IS_ERR(i2c_dev))
		return PTR_ERR(i2c_dev);
	//初始化字符设备,提供操作符
	cdev_init(&i2c_dev->cdev, &i2cdev_fops);
	i2c_dev->cdev.owner = THIS_MODULE;
	//i2c_dev成员变量初始化
	device_initialize(&i2c_dev->dev);
	i2c_dev->dev.devt = MKDEV(I2C_MAJOR, adap->nr);
	i2c_dev->dev.class = i2c_dev_class;
	i2c_dev->dev.parent = &adap->dev;
	i2c_dev->dev.release = i2cdev_dev_release;
	dev_set_name(&i2c_dev->dev, "i2c-%d", adap->nr);
	//添加设备
	res = cdev_device_add(&i2c_dev->cdev, &i2c_dev->dev);
	if (res) {
		put_i2c_dev(i2c_dev, false);
		return res;
	}

	pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
		 adap->name, adap->nr);
	return 0;
}
2.4 I2C设备操作符函数

上一小节中在初始化字符设备,提供操作符时,i2c_dev设备操作符定义为:

static const struct file_operations i2cdev_fops = {
	.owner		= THIS_MODULE,
	.llseek		= no_llseek,
	.read		= i2cdev_read,
	.write		= i2cdev_write,
	.unlocked_ioctl	= i2cdev_ioctl,
	.compat_ioctl	= compat_i2cdev_ioctl,
	.open		= i2cdev_open,
	.release	= i2cdev_release,
};

上层应用程序通过操作符参数来对设备进行读写,下面对常用操作符进行介绍。

2.4.1 i2cdev_read
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;

	if (count > 8192)
		count = 8192;

	tmp = kmalloc(count, GFP_KERNEL);
	if (tmp == NULL)
		return -ENOMEM;

	pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
		iminor(file_inode(file)), count);
	//从i2c设备读取数据
	ret = i2c_master_recv(client, tmp, count);
	if (ret >= 0)
        //将读取的数据从内核拷贝到应用层
		ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;
	kfree(tmp);
	return ret;
}

读取数据函数i2c_master_recv调用流程为:

-> i2c_master_recv
	-> i2c_transfer_buffer_flags(client, buf, count, I2C_M_RD)
		-> i2c_transfer  //传输函数
2.4.2 i2cdev_write
static ssize_t i2cdev_write(struct file *file, const char __user *buf,
		size_t count, loff_t *offset)
{
	int ret;
	char *tmp;
	struct i2c_client *client = file->private_data;

	if (count > 8192)
		count = 8192;
	//将用户层数据拷贝到内核	
	tmp = memdup_user(buf, count);
	if (IS_ERR(tmp))
		return PTR_ERR(tmp);

	pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n",
		iminor(file_inode(file)), count);
	//写数据	
	ret = i2c_master_send(client, tmp, count);
	kfree(tmp);
	return ret;
}

写数据根据函数i2c_master_send调用流程为:

-> i2c_master_send
	-> i2c_transfer_buffer_flags(client, (char *)buf, count, 0)
		-> i2c_transfer  //传输函数
2.4.3 i2cdev_open
static int i2cdev_open(struct inode *inode, struct file *file)
{
	unsigned int minor = iminor(inode);
	struct i2c_client *client;
	struct i2c_adapter *adap;
	//获取adapter
	adap = i2c_get_adapter(minor);
	if (!adap)
		return -ENODEV;
	//开辟client
	client = kzalloc(sizeof(*client), GFP_KERNEL);
	if (!client) {
		i2c_put_adapter(adap);
		return -ENOMEM;
	}
	snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);

    //将i2c_client对象作为file的私有数据供后续其他操作使用
	client->adapter = adap;
	file->private_data = client;

	return 0;
}
2.4.4 i2cdev_ioctl

相较于read与write操作符接口,此函数在信息传输时调用更频繁,根据ioctl传入的参数进行读写操作,函数主要内容为:

static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	struct i2c_client *client = file->private_data;
	unsigned long funcs;

	switch (cmd) {
	...
	case I2C_RDWR: {
		struct i2c_rdwr_ioctl_data rdwr_arg;
		struct i2c_msg *rdwr_pa;

		if (copy_from_user(&rdwr_arg,
				   (struct i2c_rdwr_ioctl_data __user *)arg,
				   sizeof(rdwr_arg)))
			return -EFAULT;

		if (!rdwr_arg.msgs || rdwr_arg.nmsgs == 0)
			return -EINVAL;

		/*
		 * Put an arbitrary limit on the number of messages that can
		 * be sent at once
		 */
		if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS)
			return -EINVAL;

		rdwr_pa = memdup_user(rdwr_arg.msgs,
				      rdwr_arg.nmsgs * sizeof(struct i2c_msg));
		if (IS_ERR(rdwr_pa))
			return PTR_ERR(rdwr_pa);

		return i2cdev_ioctl_rdwr(client, rdwr_arg.nmsgs, rdwr_pa);
	}

	case I2C_SMBUS: {
		struct i2c_smbus_ioctl_data data_arg;
		if (copy_from_user(&data_arg,
				   (struct i2c_smbus_ioctl_data __user *) arg,
				   sizeof(struct i2c_smbus_ioctl_data)))
			return -EFAULT;
		return i2cdev_ioctl_smbus(client, data_arg.read_write,
					  data_arg.command,
					  data_arg.size,
					  data_arg.data);
	}
	...
	}
	return 0;
}
  • I2C_RDWR:先将用户层数据拷贝到内核,最后调用i2cdev_ioctl_rdwr函数实现传输,其最终调用i2c_transfer实现传输。

  • I2C_SMBUS:先将用户层数据拷贝到内核,最后调用i2cdev_ioctl_smbus函数实现传输,其调用流程为:

    -> i2cdev_ioctl_smbus
    	-> i2c_smbus_xfer
    		-> __i2c_smbus_xfer
    			-> i2c_smbus_xfer_emulated
    				-> __i2c_transfer
    
2.5 添加adapter

i2c_add_adapter函数向总线添加adapter,函数内容为:

int i2c_add_adapter(struct i2c_adapter *adapter)
{
	struct device *dev = &adapter->dev;
	int id;

    //从设备树中获取id
	if (dev->of_node) {
		id = of_alias_get_id(dev->of_node, "i2c");
		if (id >= 0) {
			adapter->nr = id;
			return __i2c_add_numbered_adapter(adapter);
		}
	}
	//动态分配id
	mutex_lock(&core_lock);
	id = idr_alloc(&i2c_adapter_idr, adapter,
		       __i2c_first_dynamic_bus_num, 0, GFP_KERNEL);
	mutex_unlock(&core_lock);
	if (WARN(id < 0, "couldn't get idr"))
		return id;

	adapter->nr = id;

	return i2c_register_adapter(adapter);
}

在添加adapter时,首先获取其id。获取的方式有两种:第一种,从设备树获取;第二种,系统动态分配。一般会使用第二种方式。

分配完id后,调用i2c_register_adapter函数注册daapter,其内容主要为:

static int i2c_register_adapter(struct i2c_adapter *adap)
{
	int res = -EINVAL;
	...
	dev_set_name(&adap->dev, "i2c-%d", adap->nr);  	//根据id设置name
	adap->dev.bus = &i2c_bus_type;				   	//设置bus类型
	adap->dev.type = &i2c_adapter_type;				//设置device类型
	res = device_register(&adap->dev);				//注册设备
	if (res) {
		pr_err("adapter '%s': can't register device (%d)\n", adap->name, res);
		goto out_list;
	}

	res = of_i2c_setup_smbus_alert(adap);
	if (res)
		goto out_reg;

	dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);

	pm_runtime_no_callbacks(&adap->dev);
	pm_suspend_ignore_children(&adap->dev, true);
	pm_runtime_enable(&adap->dev);

#ifdef CONFIG_I2C_COMPAT
	res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
				       adap->dev.parent);
	if (res)
		dev_warn(&adap->dev,
			 "Failed to create compatibility class link\n");
#endif

	i2c_init_recovery(adap);

	/* create pre-declared device nodes */
	of_i2c_register_devices(adap);
	i2c_acpi_install_space_handler(adap);
	i2c_acpi_register_devices(adap);

	if (adap->nr < __i2c_first_dynamic_bus_num)
		i2c_scan_static_board_info(adap);

	/* Notify drivers */
	mutex_lock(&core_lock);
    //遍历drivers添加adapter
	bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
	mutex_unlock(&core_lock);

	return 0;
2.6 i2c_transfer传输函数

i2c数据传输时,由i2c_transfer函数实现,函数原型为:

//struct i2c_adapter *adap:	此函数调用的i2c适配器
//struct i2c_msg *msgs:		要传输的数据
//int num:					数据长度
i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

此函数实现实际调用的函数是__i2c_transfer,函数内容为:

int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
	unsigned long orig_jiffies;
	int ret, try;
	...
        
	orig_jiffies = jiffies;
	for (ret = 0, try = 0; try <= adap->retries; try++) {
		if (i2c_in_atomic_xfer_mode() && adap->algo->master_xfer_atomic)
			ret = adap->algo->master_xfer_atomic(adap, msgs, num);
		else
			ret = adap->algo->master_xfer(adap, msgs, num);

		if (ret != -EAGAIN)
			break;
		if (time_after(jiffies, orig_jiffies + adap->timeout))
			break;
	}
	...
     
	return ret;
}

在上述函数中,通过调用adap->algo->master_xfer(adap, msgs, num)钩子函数实现数据的传输。
前两章简单介绍linux下i2c框架相关内容,下一章将正式分析i2c驱动。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值