14.IIC核心函数与万能驱动

IIC核心函数

i2c_add_adapter()

该函数存放在内核/drivers/i2c/i2c-core-base.c文件。注册一个i2c适配器

cpp 复制代码
/*
 * 注册一个i2c适配器
 * adapter->nr:适配器的编号
 * adapter:i2c物理控制器对应的适配器
 */

// 由kernel分配适配器的编号
int i2c_add_adapter(struct i2c_adapter *adapter)
// 自己指定适配器的编号
int i2c_add_numbered_adapter(struct i2c_adapter *adapter)

/*
 * 返回值:
 *    成功:0
 *    失败:负值
 */

i2c_add_driver()宏

该函数存放在内核/include/linux/i2c.h文件。注册一个i2c驱动

cpp 复制代码
// struct i2c_driver
#define i2c_add_driver(driver) \ i2c_register_driver(THIS_MODULE, driver)

/*
 * 存放在内核/drivers/i2c/i2c-core-base.c文件
 * 注册一个i2c驱动
 * owner:一般为THIS_MODULE
 * driver:要注册的i2c_driver,对应的驱动结构体由我们自己来实现
 */
int i2c_register_driver(struct module *owner, struct i2c_driver *driver);
/*
 * 返回值:
 *    成功:0
 *    失败:负值
 */

i2c_transfer()

该函数存放在内核/drivers/i2c/i2c-core-base.c文件。进行iic消息收发(可收可发)

该函数的核心是:adap → algo → master_xfer(adap, msgs, num),也就是适配器的具体通信方法。

cpp 复制代码
/*
 * 收发消息都要用到i2c_adapter适配器
 * adap:所使用的的I2C适配器,i2c_client会保存其对应的i2c_adapter
 * msgs:i2c要发生的一个或多个消息,每个消息对应一个消息结构体
 * num:消息数量(即msgs的数量)
 */
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
/*
 * 返回值:
 *    成功:发送的msgs的数量
 *    失败:负值
 */

/* 该结构体存放在内核/include/uapi/linux/i2c.h文件 */
struct i2c_msg
{
	__u16 addr;	    // 从机地址,发送到哪个从设备地址
	__u16 flags;    // 读写等其他特性,见下面的宏

	// 此宏表示读,0表示写
	#define I2C_M_RD		    0x0001	/* read data, from slave to master */
					                    /* I2C_M_RD is guaranteed to be 0x0001! */
	#define I2C_M_TEN		    0x0010	/* this is a ten bit chip address */
	#define I2C_M_DMA_SAFE		0x0200	/* the buffer of this message is DMA safe */
					                    /* makes only sense in kernelspace */
					                    /* userspace buffers are copied anyway */
	#define I2C_M_RECV_LEN		0x0400	/* length will be first received byte */
	#define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */
	#define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */
	#define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */
	#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_NOSTART */
	#define I2C_M_STOP		    0x8000	/* if I2C_FUNC_PROTOCOL_MANGLING */
	
	__u16 len;		// 消息数据的长度
	__u8 *buf;		// 发送或接收的消息缓冲区
};

i2c_master_send()

该函数存放在内核/include/linux/i2c.h文件。发送一个i2c消息(只能是一个)。其实最终调用的还是i2c_transfer()。

cpp 复制代码
static inline int i2c_master_send(const struct i2c_client *client,
				  const char *buf, int count)
{
	return i2c_transfer_buffer_flags(client, (char *)buf, count, 0);
};

i2c_master_recv()

该函数存放在内核/include/linux/i2c.h文件。接收一个i2c消息(只能是一个)。其实最终调用的还是i2c_transfer()。

cpp 复制代码
static inline int i2c_master_recv(const struct i2c_client *client,
				  char *buf, int count)
{
	return i2c_transfer_buffer_flags(client, buf, count, I2C_M_RD);
};

i2c_transfer_buffer_flags()

该函数存放在内核/drivers/i2c/i2c-core-base.c文件。发送一个i2c消息。其实最终调用的还是i2c_transfer()。

cpp 复制代码
int i2c_transfer_buffer_flags(const struct i2c_client *client, char *buf, int count, u16 flags)
{
	int ret;
	// 根据传入参数构建消息结构体,此结构体定义详见上
	struct i2c_msg msg =
	{
		.addr = client->addr,
		.flags = flags | (client->flags & I2C_M_TEN),
		.len = count,
		.buf = buf,
	};

	ret = i2c_transfer(client->adapter, &msg, 1);

	/*
	 * If everything went ok (i.e. 1 msg transferred), return #bytes
	 * transferred, else error code.
	 */
	return (ret == 1) ? count : ret;
}

万能的i2c驱动(i2c-dev.c文件)

文件存放于内核/drivers/i2c/。

内核集成i2c_dev驱动模块,开机自动加载

为每个i2c_adapter生成一个设备文件,通过该设备文件间接使用i2c核心函数收发数据

注册i2c总线的通知函数,解决加载顺序问题

i2c_dev_init()

cpp 复制代码
#define I2C_MAJOR	89		/* Device major number		*/
#define MINORBITS	20
#define MINORMASK	((1U << MINORBITS) - 1)
#define I2C_MINORS	MINORMASK

static int __init i2c_dev_init(void)
{
	int res;

	printk(KERN_INFO "i2c /dev entries driver\n");
	// 申请设备号,I2C_MAJOR为89,次设备号为0,I2C_MINORS为1<<20-1,表示次设备号的数量。
	// 就是把这个主设备号对应的次设备号都申请了。
	res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");
	if (res)
		goto out;
	// 创建一个同名类,在 /sys/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总线的通知函数
	// 参数2详见下
	res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
	if (res)
		goto out_unreg_class;

	/* Bind to already existing adapters right away */
	// 遍历i2c总线上的所有设备,每次都执行第二个参数对应的函数
	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 struct notifier_block i2cdev_notifier =
{
	// 详见下
	.notifier_call = i2cdev_notifier_call,
};

static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,
			 void *data)
{
	struct device *dev = data;
	// 发生的事件类型
	switch (action) {
	// 此i2c总线下发生添加设备事件
	case BUS_NOTIFY_ADD_DEVICE:
		// 创建设备文件之类的操作
		return i2cdev_attach_adapter(dev, NULL);
	// 此i2c总线下发生删除设备事件
	case BUS_NOTIFY_DEL_DEVICE:
		return i2cdev_detach_adapter(dev, NULL);
	}

	return 0;
}

int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *))
{
	int res;

	mutex_lock(&core_lock);
	res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);
	mutex_unlock(&core_lock);

	return res;
}

int bus_for_each_dev(struct bus_type *bus, struct device *start,
		     void *data, int (*fn)(struct device *, void *))
{
	struct klist_iter i;
	struct device *dev;
	int error = 0;

	if (!bus || !bus->p)
		return -EINVAL;

	klist_iter_init_node(&bus->p->klist_devices, &i,
			     (start ? &start->p->knode_bus : NULL));
	while (!error && (dev = next_device(&i)))
		error = fn(dev, data);
	klist_iter_exit(&i);
	return error;
}

i2cdev_attach_adapter()

i2c总线添加设备后回调执行的函数

cpp 复制代码
static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
	struct i2c_adapter *adap;
	struct i2c_dev *i2c_dev;
	int res;
	// 若设备类型不是i2c适配器,直接返回
	// 也有可能是 i2c_client 设备
	if (dev->type != &i2c_adapter_type)
		return 0;
	// 从i2c设备结构体中获取i2c适配器结构体
	adap = to_i2c_adapter(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;
	// 注意次设备号为适配器对应的编号,可以自己指定,也可以有系统分配
	// 参数3为次设备号的数量,此fops只对应次设备号的文件
	res = cdev_add(&i2c_dev->cdev, MKDEV(I2C_MAJOR, adap->nr), 1);
	if (res)
		goto error_cdev;

	/* register this i2c device with the driver core */
	// 在/sys下,在i2c_dev_class类(参数1)下创建一个新的目录, "i2c-适配器编号"就是目录名
	// 然后会通知用户空间的uevent守护进程,此守护进程会创建一个同参数4名的设备文件
	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;
}

i2cdev_fops结构体

cpp 复制代码
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,
};

i2cdev_open()

cpp 复制代码
static int i2cdev_open(struct inode *inode, struct file *file)
{
	// 从inode获取次设备号,就是适配器的编号
	unsigned int minor = iminor(inode);
	struct i2c_client *client;
	struct i2c_adapter *adap;

	// 根据次设备号从i2c总线获取对应的适配器
	adap = i2c_get_adapter(minor);
	if (!adap)
		return -ENODEV;

	// client表示某个具体的i2c设备,为其分配内存
	client = kzalloc(sizeof(*client), GFP_KERNEL);
	if (!client) {
		i2c_put_adapter(adap);
		return -ENOMEM;
	}

	// 设置具体的i2c设备的名字
	snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
	// 设置具体的i2c设备归属的适配器
	client->adapter = adap;
	// 以后可以通过file的此成员获取client指针
	file->private_data = client;

	return 0;
}

i2cdev_read()

cpp 复制代码
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;
}

i2cdev_write()

cpp 复制代码
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;

	把用户空间的buf拷贝内核空间的tmp
	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);
	
    // 核心函数,发送一个i2c消息
	ret = i2c_master_send(client, tmp, count);

	kfree(tmp);
	return ret;
}
相关推荐
许白掰1 小时前
Linux入门篇学习——Linux 工具之 make 工具和 makefile 文件
linux·运维·服务器·前端·学习·编辑器
longze_75 小时前
Ubuntu连接不上网络问题(Network is unreachable)
linux·服务器·ubuntu
Dirschs5 小时前
【Ubuntu22.04安装ROS Noetic】
linux·ubuntu·ros
qianshanxue115 小时前
ubuntu 操作记录
linux
AmosTian8 小时前
【系统与工具】Linux——Linux简介、安装、简单使用
linux·运维·服务器
这我可不懂11 小时前
Python 项目快速部署到 Linux 服务器基础教程
linux·服务器·python
车车不吃香菇11 小时前
java idea 本地debug linux服务
java·linux·intellij-idea
tan77º11 小时前
【Linux网络编程】Socket - TCP
linux·网络·c++·tcp/ip
kfepiza12 小时前
Linux的`if test`和`if [ ]中括号`的取反语法比较 笔记250709
linux·服务器·笔记·bash
CodeWithMe12 小时前
【Note】《深入理解Linux内核》 第十九章:深入理解 Linux 进程通信机制
linux·运维·php