通用i2c设备驱动分析

news/2024/7/5 21:38:51

Linux i2c驱动框架分析 (一)
Linux i2c驱动框架分析 (二)
Linux i2c驱动框架分析 (三)
通用i2c设备驱动分析

内核提供了一个通用的i2c驱动,这个驱动程序为每个适配器提供了设备文件的功能,下面就简要分析一下这个驱动程序,对应的驱动文件为drivers\i2c\i2c-dev.c。

入口函数如下:

static int __init i2c_dev_init(void)
{
	int res;

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

	//申请字符设备设备号,主设备号89
	res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");
	if (res)
		goto out;

	//创建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;

	/* 注册一个通知链,在注册/卸载适配器时会调用到
   * i2cdev_notifier.notifier_call函数,即i2cdev_notifier_call,而这个
   * 函数根据不同的事件(注册/卸载),进一步调用不同的处理函数,对于增添事件
   * 调用i2cdev_attach_adapter函数,该函数也是下面要分析的
   */
	res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
	if (res)
		goto out_unreg_class;

	//遍历i2c bus上的适配器,调用i2cdev_attach_adapter函数
	i2c_for_each_dev(NULL, i2cdev_attach_adapter);

	return 0;

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


static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,
			 void *data)
{
	struct device *dev = data;

	switch (action) {
	case BUS_NOTIFY_ADD_DEVICE:
		return i2cdev_attach_adapter(dev, NULL);
	case BUS_NOTIFY_DEL_DEVICE:
		return i2cdev_detach_adapter(dev, NULL);
	}

	return 0;
}


static struct notifier_block i2cdev_notifier = {
	.notifier_call = i2cdev_notifier_call,
};

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 = get_free_i2c_dev(adap);
	if (IS_ERR(i2c_dev))
		return PTR_ERR(i2c_dev);

	//字符设备相关设置,file_operations设置为i2cdev_fops
	cdev_init(&i2c_dev->cdev, &i2cdev_fops);
	i2c_dev->cdev.owner = THIS_MODULE;

	//注册字符设备,bus num作为次设备号
	res = cdev_add(&i2c_dev->cdev, MKDEV(I2C_MAJOR, adap->nr), 1);
	if (res)
		goto error_cdev;

	/* 创建设备文件 */
	i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
				     MKDEV(I2C_MAJOR, adap->nr), NULL,
				     "i2c-%d", adap->nr);
	if (IS_ERR(i2c_dev->dev)) {
		res = PTR_ERR(i2c_dev->dev);
		goto error;
	}

	pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
		 adap->name, adap->nr);
	return 0;
error:
	cdev_del(&i2c_dev->cdev);
error_cdev:
	put_i2c_dev(i2c_dev);
	return res;
}

i2c-dev.c实现了i2c适配器设备文件的功能,为应用层接提供了统一的编程接口。当应用层open、read等操作时,会调用到驱动层对应操作函数。


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

如,当应用层“open /dev/i2c-0”时,会调用到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;

	//通过次设备号获得对应的i2c_adapter 
	adap = i2c_get_adapter(minor);
	if (!adap)
		return -ENODEV;

	//创建一个临时的i2c_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);

	client->adapter = adap;
	file->private_data = client;

	return 0;
}

close时,会释放临时的i2c_client:

static int i2cdev_release(struct inode *inode, struct file *file)
{
	struct i2c_client *client = file->private_data;

	i2c_put_adapter(client->adapter);
	kfree(client);
	file->private_data = NULL;

	return 0;
}

这个临时i2c_client的地址是通过ioctl设置的,如“ioctl(fd, I2C_SLAVE, 0x50)”,将设备地址设置为0x50,ioctl操作最终会调用到i2cdev_ioctl函数:

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

	dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
		cmd, arg);

	switch (cmd) {
	//设置地址
	case I2C_SLAVE:
	case I2C_SLAVE_FORCE:
		if ((arg > 0x3ff) ||
		    (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
			return -EINVAL;

		/*如果这个地址对应的i2c设备已有驱动与之匹配,并且不是强制设置(I2C_SLAVE_FORCE)
     *   返回-EBUSY
     */
		if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))
			return -EBUSY;
		/* REVISIT: address could become busy later */
		client->addr = arg;
		return 0;
	......
	case I2C_RDWR:
		return i2cdev_ioctl_rdwr(client, arg);

	case I2C_SMBUS:
		return i2cdev_ioctl_smbus(client, arg);
	......
	}
	return 0;
}

下面分析最主要的读写命令I2C_RDWR:

static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client,
		unsigned long arg)
{
	struct i2c_rdwr_ioctl_data rdwr_arg;
	struct i2c_msg *rdwr_pa;
	u8 __user **data_ptrs;
	int i, res;

	......

	/* 调用i2c_transfer函数发送数据,该函数最终会调用底层的adap->algo->master_xfer
	 * 函数进行发送数据
   */ 
	res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);
	......
}

http://www.niftyadmin.cn/n/3657307.html

相关文章

Linux spi驱动框架分析(一)

本次的spi专题,主要参考:这位大佬的博客 感觉大佬们的无私奉献,而我写博客的初衷除了记录自己的学习经历之外,同时也分享给大家,分享知识。 系列文章: Linux spi驱动框架分析(一) L…

软件架构师

软件企业中有一个角色叫做软件架构师,不同公司或者不同的环境下,对该职位的定位可能不尽相同。微软首席架构师Ray Ozzie 对自己职位的一些看法,倒是给人很多启发:1. 不管是设计一座桥梁还是一幢大厦,你是在特定的情况下…

Linux spi驱动框架分析(二)

系列文章: Linux spi驱动框架分析(一) Linux spi驱动框架分析(二) Linux spi驱动框架分析(三) Linux spi驱动框架分析(四) spi core spi核心(dervers/spi/s…

Linux spi驱动框架分析(三)

系列文章: Linux spi驱动框架分析(一) Linux spi驱动框架分析(二) Linux spi驱动框架分析(三) Linux spi驱动框架分析(四) spi_master驱动 spi_master驱动负责最底层的…

Visual Studio International Pack对汉字的支持

还在重复发明轮子吗?看看微软在汉字方面的新支持:Simplified Chinese Pin-Yin Conversion Library - 支持获取简体中文字符的常用属性比如拼音,多音字,同音字,笔画数。 Traditional Chinese to Simplified Chinese Con…

Hyper-V: 一台服务器变二百台服务器

微软未来的virtualization技术会带来。。。?来自微软的邮件摘录:Q: My customer has a significant investment of 32-bit servers and wants to know why Hyper-V won’t be developed for Windows Server 2008 32-bit editions.A: Developing Hyper-V …

Linux spi驱动框架分析(四)

系列文章: Linux spi驱动框架分析(一) Linux spi驱动框架分析(二) Linux spi驱动框架分析(三) Linux spi驱动框架分析(四) spi_master的消息队列机制 SPI数据传输可以有…

Linux 输入子系统分析(一)

Linux 输入子系统分析(一) Linux 输入子系统分析(二) 分析一个内核提供的input_handler Linux内核输入子系统 输入设备(如按键、键盘、触摸屏、鼠标等)是典型的字符设备,其一般的工作机理是底…