• 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

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

    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)
    
    • 1
    • 2
    • 3

    添加驱动调用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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

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

    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,并添加到链表中
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
    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;
    	...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在上述内容中,调用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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    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,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

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

    -> i2c_master_recv
    	-> i2c_transfer_buffer_flags(client, buf, count, I2C_M_RD)
    		-> i2c_transfer  //传输函数
    
    • 1
    • 2
    • 3
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

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

    -> i2c_master_send
    	-> i2c_transfer_buffer_flags(client, (char *)buf, count, 0)
    		-> i2c_transfer  //传输函数
    
    • 1
    • 2
    • 3
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 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
      
      • 1
      • 2
      • 3
      • 4
      • 5
    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在添加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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    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)
    
    • 1
    • 2
    • 3
    • 4

    此函数实现实际调用的函数是__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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

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

  • 相关阅读:
    实用技巧,用lsof命令监控tar文件解压进度,简单有效!
    IDENTIFY HIGH RISK HEALTH RECORDS
    银行金融科技岗笔试题资料大总结
    4-6 最小生成树Prim,Kruskal(贪心)
    HTML5详解
    【系统分析师之路】第五章 复盘软件工程(逆向净室与模型驱动开发)
    pem文件类解析
    【经验】解决重置 Windows 10 时报错:“无法找到介质” 的错误
    【网络编程】网络层——IP协议
    SVN+Gitee配置版本控制库
  • 原文地址:https://blog.csdn.net/weixin_43952192/article/details/127522974