速通I2C通信原理以及驱动开发(下)

目录

  • i2c驱动整体框架
  • dev结构体初始化
  • read、write寄存器函数
    • i2c_client
      • i2c_adapter
        • i2c_algorithm
        • i2c_transfer
          • __i2c_transfer
  • ops编写
  • i2c驱动结构体
    • probe
    • remove
    • 两种匹配方式
      • ID列表
      • 设备树列表
  • 驱动入口出口

i2c驱动整体框架

速通I2C通信原理以及驱动开发(下)_第1张图片

dev结构体初始化

struct ap3216c_dev {
	dev_t devid;			/* 设备号 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;	/* 类 		*/
	struct device *device;	/* 设备 	 */
	struct device_node	*nd; /* 设备节点 */
	int major;			/* 主设备号 */
	void *private_data;	/* 私有数据 */
	unsigned short ir, als, ps;		/* 三个光传感器数据 */
};
static struct ap3216c_dev ap3216cdev;

read、write寄存器函数

先看一下整体代码

static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len)
{
	int ret;
	struct i2c_msg msg[2];
	struct i2c_client *client = (struct i2c_client *)dev->private_data;
	/* msg[0]为发送要读取的首地址 */
	msg[0].addr = client->addr;			/* ap3216c地址 */
	msg[0].flags = 0;					/* 标记为发送数据 */
	msg[0].buf = ®					/* 读取的首地址 */
	msg[0].len = 1;						/* reg长度*/
	/* msg[1]读取数据 */
	msg[1].addr = client->addr;			/* ap3216c地址 */
	msg[1].flags = I2C_M_RD;			/* 标记为读取数据*/
	msg[1].buf = val;					/* 读取数据缓冲区 */
	msg[1].len = len;					/* 要读取的数据长度*/
	ret = i2c_transfer(client->adapter, msg, 2);
···
	return ret;
}

static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, u8 len)
{
	u8 b[256];
	struct i2c_msg msg;
	struct i2c_client *client = (struct i2c_client *)dev->private_data;
	b[0] = reg;					/* 寄存器首地址 */
	memcpy(&b[1],buf,len);		/* 将要写入的数据拷贝到数组b里面 */
	msg.addr = client->addr;	/* ap3216c地址 */
	msg.flags = 0;				/* 标记为写数据 */
	msg.buf = b;				/* 要写入的数据缓冲区 */
	msg.len = len + 1;			/* 要写入的数据长度 */

	return i2c_transfer(client->adapter, &msg, 1);
}

通过阅读代码会有两个问题:i2c_client、i2c_transfer是什么?驱动时如何实现数据收发的?

i2c_client

struct i2c_client {
	unsigned short flags;		/* div., see below		*/
	unsigned short addr;		/* chip address - NOTE: 7bit	*/
	char name[I2C_NAME_SIZE];
	struct i2c_adapter *adapter;	/* the adapter we sit on	*/
	struct device dev;		/* the device structure		*/
	int init_irq;			/* irq set at initialization	*/
	int irq;			/* irq issued by device		*/
	struct list_head detected;
};

描述了一个i2c设备,包括标志位,地址,名称,i2c适配器等等,我们发现在使用i2c_transfer时,里面的参数也有i2c_adapter。所以继续跟进i2c_adapter内

i2c_adapter

struct i2c_adapter {
	struct module *owner;
	unsigned int class;		  /* classes to allow probing for */
	const struct i2c_algorithm *algo; /* the algorithm to access the bus */
	void *algo_data;
	/* data fields that are valid for all devices	*/
	const struct i2c_lock_operations *lock_ops;
	struct rt_mutex bus_lock;
	struct rt_mutex mux_lock;
	int timeout;			/* in jiffies */
	int retries;
	struct device dev;		/* the adapter device */
	unsigned long locked_flags;	/* owned by the I2C core */
	int nr;
	char name[48];
	struct completion dev_released;
	struct mutex userspace_clients_lock;
	struct list_head userspace_clients;
	struct i2c_bus_recovery_info *bus_recovery_info;
	const struct i2c_adapter_quirks *quirks;
	struct irq_domain *host_notify_domain;
};

i2c_adapter是用于标识物理i2c总线的结构体 具有访问它所需的访问算法i2c_algorithm。

i2c_algorithm
struct i2c_algorithm {
	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
	int (*master_xfer_atomic)(struct i2c_adapter *adap,struct i2c_msg *msgs, int num);
	int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data);
	int (*smbus_xfer_atomic)(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write,u8 command, int size, union i2c_smbus_data *data);
	/* To determine what the adapter supports */
	u32 (*functionality)(struct i2c_adapter *adap);
};

这里看第一个函数指针 (*master_xfer):向 msgs 数组定义的给定 I2C 适配器发出一组 i2c 事务,其中有 num 条消息可通过 adap 指定的适配器传输。

i2c_transfer
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
	int ret;
···
	ret = __i2c_lock_bus_helper(adap);
	if (ret)
		return ret;
	ret = __i2c_transfer(adap, msgs, num);
	i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
	return ret;
}

这段代码主要执行了内核的__i2c_transfer,并在执行前后加了i2c的bus线程锁。

__i2c_transfer
int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
	unsigned long orig_jiffies;
	int ret, try;
···
	if (static_branch_unlikely(&i2c_trace_msg_key)) {
		int i;
		for (i = 0; i < num; i++)
			if (msgs[i].flags & I2C_M_RD)
				trace_i2c_read(adap, &msgs[i], i);
			else
				trace_i2c_write(adap, &msgs[i], i);
	}//检查是否启用了跟踪功能,如果启用了,它会追踪每个消息的读写操作。

	orig_jiffies = jiffies;
	for (ret = 0, try = 0; try <= adap->retries; try++) {//代码进行了一个循环,用于在发生仲裁丢失(arbitration loss)时自动重试传输。这个循环包含一个计数器 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;
	}//在循环中,根据是否处于原子传输模式(atomic transfer mode),选择调用 adap->algo->master_xfer_atomic 或adap->algo->master_xfer 函数来执行传输。如果返回值不是 -EAGAIN,则表示传输成功,退出循环。如果传输超时(时间差大于适配器的超时时间),也退出循环。
	if (static_branch_unlikely(&i2c_trace_msg_key)) {
		int i;
		for (i = 0; i < ret; i++)
			if (msgs[i].flags & I2C_M_RD)
				trace_i2c_reply(adap, &msgs[i], i);
		trace_i2c_result(adap, num, ret);
	}//最后,如果启用了跟踪功能,代码将追踪传输的结果。
	return ret;
}

最终我们找到了i2c_transfer 的真正作用:用来调用adapter->algorithm->master_xfer,以此来发送接收数据

ops编写

static const struct file_operations ap3216c_ops = {
	.owner = THIS_MODULE,
	.open = ap3216c_open,
	.read = ap3216c_read,
	.release = ap3216c_release,
};

用于应用程序调用驱动生成的设备文件,基本上都是调用了上面read、write寄存器函数,不再过多赘述。想了解的可以看博主之前的文章https://blog.csdn.net/qq_52837539/article/details/127399039

i2c驱动结构体

static struct i2c_driver ap3216c_driver = {
	.probe = ap3216c_probe,
	.remove = ap3216c_remove,
	.driver = {
			.owner = THIS_MODULE,
		   	.name = "ap3216c",
		   	.of_match_table = ap3216c_of_match, 
		   },
	.id_table = ap3216c_id,
};

这里有多了一组操作函数,实现了i2c设备匹配、创建和移除。

probe

static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	/* 1、构建设备号 */
	if (ap3216cdev.major) {
		ap3216cdev.devid = MKDEV(ap3216cdev.major, 0);
		register_chrdev_region(ap3216cdev.devid, AP3216C_CNT, AP3216C_NAME);
	} else {
		alloc_chrdev_region(&ap3216cdev.devid, 0, AP3216C_CNT, AP3216C_NAME);
		ap3216cdev.major = MAJOR(ap3216cdev.devid);
	}
	/* 2、注册设备 */
	cdev_init(&ap3216cdev.cdev, &ap3216c_ops);
	cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT);
	/* 3、创建类 */
	ap3216cdev.class = class_create(THIS_MODULE, AP3216C_NAME);
	if (IS_ERR(ap3216cdev.class)) {
		return PTR_ERR(ap3216cdev.class);
	}
	/* 4、创建设备 */
	ap3216cdev.device = device_create(ap3216cdev.class, NULL, ap3216cdev.devid, NULL, AP3216C_NAME);
	if (IS_ERR(ap3216cdev.device)) {
		return PTR_ERR(ap3216cdev.device);
	}
	ap3216cdev.private_data = client;
	return 0;
}

这里和字符设备大体差不多,主要是设备匹配成后执行的操作

remove

static int ap3216c_remove(struct i2c_client *client)
{
	/* 删除设备 */
	cdev_del(&ap3216cdev.cdev);
	unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);

	/* 注销掉类和设备 */
	device_destroy(ap3216cdev.class, ap3216cdev.devid);
	class_destroy(ap3216cdev.class);
	return 0;
}

移除设备操作

两种匹配方式

ID列表

/* 传统匹配方式ID列表 */
static const struct i2c_device_id ap3216c_id[] = {
	{"alientek,ap3216c", 0},  
	{}
};

设备树列表

/* 设备树匹配列表 */
static const struct of_device_id ap3216c_of_match[] = {
	{ .compatible = "alientek,ap3216c" },
	{ /* Sentinel */ }
};

驱动入口出口

static int __init ap3216c_init(void)
{
	int ret = 0;
	ret = i2c_add_driver(&ap3216c_driver);
	return ret;
}
static void __exit ap3216c_exit(void)
{
	i2c_del_driver(&ap3216c_driver);
}

最后就是系统调用的添加驱动和删除驱动了

你可能感兴趣的:(驱动开发,b树,数据结构)