第二十九章 linux-i2c子系统二

news2024/9/30 15:18:24

第二十九章 linux-i2c子系统二


文章目录

  • 第二十九章 linux-i2c子系统二
  • linux-i2c数据结构分析
  • linux-i2c驱动框架
    • IIC核心
    • IIC总线驱动
    • IIC设备驱动
  • linux-i2c设备实现
  • 硬件拓扑


linux-i2c数据结构分析

driver
第二十八章 linux-i2c子系统二
在这里插入图片描述
设备驱动模型
在这里插入图片描述
i2c_client来描述一个挂载在I2C总线上的I2C设备。

struct i2c_client {//描述一个从设备的信息,不需要在代码中创建,i2c adapter帮我们创建
	unsigned short flags;		/* div., see below		*/
	unsigned short addr;//从设备地址,来自于设备树中<reg>
					/* chip address - NOTE: 7bit	*/
					/* addresses are stored in the	*/
					/* _LOWER_ 7 bits		*/
	char name[I2C_NAME_SIZE];//用于i2c driver进行匹配,来自于设备树中compatible
	struct i2c_adapter *adapter;//指向当前从设备所存在的i2c_adapter
	/* the adapter we sit on	*/
	struct device dev;		    //继承了父类
	/* the device structure		*/
	int irq;			//设备申请的中断号
	/* irq issued by device		*/
	struct list_head detected;//设备申请的中断号
#if IS_ENABLED(CONFIG_I2C_SLAVE)
	i2c_slave_cb_t slave_cb;	/* callback for slave mode	*/
#endif
};

i2c_driver来描述一个IIC设备的驱动程序。每个i2c_client对应一个i2c_driver。

struct i2c_driver {//表示一个从设备的驱动对象
	unsigned int class; //驱动的类型

	/* Notifies the driver that a new bus has appeared. You should avoid
	 * using this, it will be removed in a near future.
	 */
	int (*attach_adapter)(struct i2c_adapter *) __deprecated;//当检测到适配器时调用的函数

	/* Standard driver model interfaces */
	int (*probe)(struct i2c_client *, const struct i2c_device_id *);//新类型设备探测函数
	int (*remove)(struct i2c_client *);//新类型设备的移除函数

	/* driver model interfaces that don't relate to enumeration  */
	void (*shutdown)(struct i2c_client *);//新类型设备的移除函数

	/* Alert callback, for example for the SMBus alert protocol.
	 * The format and meaning of the data value depends on the protocol.
	 * For the SMBus alert protocol, there is a single bit of data passed
	 * as the alert response's low bit ("event flag").
	 */
	void (*alert)(struct i2c_client *, unsigned int data);

	/* a ioctl like command that can be used to perform specific functions
	 * with the device.
	 */
	int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);//使用命令使设备完成特殊的功能。类似ioctl()函数

	struct device_driver driver;//继承了父类,设备驱动结构体
	const struct i2c_device_id *id_table;//用于做比对,非设备树的情况,//设备ID表

	/* Device detection callback for automatic device creation */
	int (*detect)(struct i2c_client *, struct i2c_board_info *);//设备所在的地址范围
	const unsigned short *address_list;//设备所在的地址范围
	struct list_head clients;//指向驱动支持的设备
};

i2c_adapter来描述一个IIC总线适配器。IIC总线适配器就是SoC内部的IIC总线控制器,在物理上连接若干个IIC设备。IIC总线适配器本质上是一个物理设备,其主要功能是完成IIC总线控制器相关的数据通信。

struct i2c_adapter {//描述一个i2c控制器

	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	*/
	struct rt_mutex bus_lock;//对总线进行操作时,将获得总线锁

	int timeout;			/* in jiffies */
	int retries;
	struct device dev;	//继承父类,也会加入到i2c bus
	/* the adapter device */

	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;
};

i2c_algorithm来描述IIC适配器与IIC设备的通信方法

struct i2c_algorithm {
	/* If an adapter algorithm can't do I2C-level access, set master_xfer
	   to NULL. If an adapter algorithm can do SMBus access, set
	   smbus_xfer. If set to NULL, the SMBus protocol is simulated
	   using common I2C messages */
	/* master_xfer should return the number of messages successfully
	   processed, or a negative value on error */
	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
			   int num);
	/*传输函数指针,指向实现IIC总线通信协议的函数,用来确定适配器支持那些传输类型    */
	int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
			   unsigned short flags, char read_write,
			   u8 command, int size, union i2c_smbus_data *data);
	/*smbus方式传输函数指针,指向实现SMBus总线通信协议的函数。SMBus和IIC之间可以通过软件方式兼容,所以这里提供了一个函数,但是一般都赋值为NULL*/ 
	/* To determine what the adapter supports */
	u32 (*functionality) (struct i2c_adapter *);/*返回适配器支持的功能*/

#if IS_ENABLED(CONFIG_I2C_SLAVE)
	int (*reg_slave)(struct i2c_client *client);
	int (*unreg_slave)(struct i2c_client *client);
#endif
};

1. i2c_driver和i2c_client
i2c_client对应真实的IIC物理设备,每个IIC设备都需要一个i2c_client来描述;而i2c_driver对应一套驱动方法。i2c_driver与i2c_client的关系是一对多,即一个i2c_driver上可以支持多个同等类型的i2c_client。
2. i2c_adapter与i2c_algorithm
i2c_adapter对应一个IIC总线适配器(SoC内部的IIC总线控制器),而i2c_algorithm对应一套通信方法。一个IIC适配器需要i2c_algorithm中提供的通信函数来控制适配器上产生特定的访问周期。缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用i2c_algorithm的指针。
3. i2c_adapter和i2c_client
i2c_adapter和i2c_client的关系与IIC硬件体系中适配器和设备的关系一致,即i2c_client依附于i2c_adapter,由于一个适配器上可以连接多个i2c设备,所以i2c_adapter中包含依附于它的i2c_client的链表。

linux-i2c驱动框架

IIC核心

IIC 核心提供了IIC总线驱动和设备驱动的注册、注销方法、IIC通信方法(algorithm)上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。

IIC总线驱动

IIC总线驱动是对IIC硬件体系结构中适配器端(SoC内部的IIC总线控制器)的实现。IIC总线驱动主要包含了IIC适配器数据结构i2c_adapter,IIC适配器的通信方法数据结构i2c_algorithm和控制I2C适配器产生通信信号的函数。经由IIC总线驱动的代码,我们可以控制IIC适配器以主控方式产生开始位,停止位,读写周期,以及以从设备方式被读写,产生ACK等。不同的CPU平台对应着不同的I2C总线驱动。

Linux内核初始化阶段,调用i2c_init() 函数来初始化IIC总线

static int __init i2c_init(void)
{
	int retval;

	retval = of_alias_get_highest_id("i2c");

	down_write(&__i2c_board_lock);
	if (retval >= __i2c_first_dynamic_bus_num)
		__i2c_first_dynamic_bus_num = retval + 1;
	up_write(&__i2c_board_lock);

	retval = bus_register(&i2c_bus_type);//注册IIC总线
	if (retval)
		return retval;
#ifdef CONFIG_I2C_COMPAT
	i2c_adapter_compat_class = class_compat_register("i2c-adapter");
	if (!i2c_adapter_compat_class) {
		retval = -ENOMEM;
		goto bus_err;
	}
#endif
	retval = i2c_add_driver(&dummy_driver);//添加一个空驱动,不知为何要添加这个空驱动
	if (retval)
		goto class_err;

	if (IS_ENABLED(CONFIG_OF_DYNAMIC))
		WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier));

	return 0;

class_err:
#ifdef CONFIG_I2C_COMPAT
	class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
	bus_unregister(&i2c_bus_type);
	return retval;
}

i2c_init() 函数中调用bus_register()函数注册IIC总线

struct bus_type i2c_bus_type = {
	.name		= "i2c",
	.match		= i2c_device_match,//match方法用来进行 device 和driver 的匹配,在向总线注册设备或是驱动的的时候会调用此方法
	.probe		= i2c_device_probe,//probe方法在完成设备和驱动的配对之后调用执行
	.remove		= i2c_device_remove,
	.shutdown	= i2c_device_shutdown,
};

IIC总线提供的match方法:match方法用来进行 i2c_driver 和 i2c_client 的匹配,在向总线注册i2c_driver或i2c_client的的时候会调用此方法。匹配的方法是拿id_table 中的每一项与 i2c_client 的name 进行匹配,如果名字相同则匹配成功。

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
	struct i2c_client	*client = i2c_verify_client(dev);
	struct i2c_driver	*driver;

	if (!client)
		return 0;

	/* Attempt an OF style match */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	driver = to_i2c_driver(drv);
	/* match on an id table if there is one */
	if (driver->id_table)//如果IIC驱动的id_table 存在的话,使用i2c_match_id 进行函数进行匹配。
		return i2c_match_id(driver->id_table, client) != NULL;

	return 0;
}

i2c_driver 和 i2c_client匹配成功后,IIC总线提供的probe方法将被调用执行,即执行i2c_device_probe()函数。实质上,最终调用执行的是IIC设备驱动中的probe函数,即i2c_driver->probe。

static int i2c_device_probe(struct device *dev)
{
	struct i2c_client	*client = i2c_verify_client(dev);
	struct i2c_driver	*driver;
	int status;

	if (!client)
		return 0;

	if (!client->irq && dev->of_node) {
		int irq = of_irq_get(dev->of_node, 0);

		if (irq == -EPROBE_DEFER)
			return irq;
		if (irq < 0)
			irq = 0;

		client->irq = irq;
	}

	driver = to_i2c_driver(dev->driver);
	if (!driver->probe || !driver->id_table)
		return -ENODEV;

	if (!device_can_wakeup(&client->dev))
		device_init_wakeup(&client->dev,
					client->flags & I2C_CLIENT_WAKE);
	dev_dbg(dev, "probe\n");

	status = of_clk_set_defaults(dev->of_node, false);
	if (status < 0)
		return status;

	status = dev_pm_domain_attach(&client->dev, true);
	if (status != -EPROBE_DEFER) {
		status = driver->probe(client, i2c_match_id(driver->id_table,
					client));//调用IIC设备驱动中的probe函数
		if (status)
			dev_pm_domain_detach(&client->dev, true);
	}

	return status;
}

增加/删除IIC总线适配器

/*增加一个IIC总线适配器*/
int i2c_add_adapter(struct i2c_adapter *adapter);

/*删除一个IIC总线适配器*/
int i2c_del_adapter(struct i2c_adapter *adap);

增加/删除IIC从设备驱动

/*增加一个IIC从设备驱动*/
int i2c_add_driver(struct i2c_driver *driver);

/*删除一个IIC从设备驱动*/
void i2c_del_driver(struct i2c_driver *driver);

IIC数据传输

/*
*参数:    struct i2c_adapter *adap:IIC总线适配器
*            struct i2c_msg*msgs:
*            int num:
*/
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg*msgs, int num) ;

/*从以下代码可知,IIC的数据传输是调用i2c_adapter->i2c_algorithm->master_xfer完成*/
int i2c_transfer(structi2c_adapter *adap, struct i2c_msg *msgs, int num)  
{  
    ... ...
    if (adap->algo->master_xfer)
    {  
         for (ret = 0, try = 0; try <=adap->retries; try++) 
        {  
             ret = adap->algo->master_xfer(adap, msgs,num);  
        }  
    }
    ... ...
}

IIC总线上的数据传输是以字节为单位的,有读和写两种通信模式。IIC子系统为了实现这种通信方法,提供了i2c_msg结构,对于每一个START信号,都对应一个i2c_msg对象,实际操作中我们会将所有的请求封装成一个struct i2c_msg[],一次性将所有的请求通过i2c_transfer()发送给匹配到的client的从属的adapter,由adapter根据相应的algo域以及master_xfer域通过主机驱动来将这些请求发送给硬件上的设备

struct i2c_msg {//描述一个从设备要发送的数据的数据包
	__u16 addr;	//从设备地址要发送给哪个从设备
	/* slave address			*/
	__u16 flags;//读1  写0
#define I2C_M_TEN		0x0010	/* this is a ten bit chip address */
#define I2C_M_RD		0x0001	/* read data, from slave to master */
#define I2C_M_STOP		0x8000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_NOSTART */
#define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN		0x0400	/* length will be first received byte */
	__u16 len;	//发送数据的长度
	/* msg length				*/
	__u8 *buf;	//指向数据的指针
	/* pointer to msg data			*/
};

IIC设备驱动

IIC设备驱动是对IIC硬件体系结构中设备端的实现,与挂在I2C总线上的具体的设备通讯的驱动。通过I2C总线驱动提供的函数,设备驱动可以忽略不同IIC总线适配器的差异,不考虑其实现细节地与硬件设备通讯。这部分代码一般由驱动工程师完成。

linux-i2c设备实现

在这里插入图片描述
在这里插入图片描述

硬件拓扑

i2C协议是主从式的,包括master(主设备)和slave(从设备)。

一般情况下:运行Linux kernel的设备,在i2C总线里面,都是i2C master,Linux kernel-3.19以后,增加slave的支持。其他模
块可以调用i2c core提供的i2c_slave_register/i2c_slave_unregister来注册一个或者注销一个i2c的slave设备。一旦氵主册了slave
设备,底层的adapter要切换到slave mode,该i2cslave设备可以响应来自对端i2c master设备的各种命令和数据了。

Note.一般不使用i2c slave功能,主控都作为i2c master。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/96150.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【图像处理】数字图像处理笔记

文章目录直方图处理滤波器图像复原形态学图像处理灰度形态学——多使用平坦结构元&#xff08;SE&#xff09;【数字图像处理P428图像分割1、canny边缘检测【数字图像处理P463】图像的表征特征描述子目标检测分类器AdaBoost算法——集成学习、迭代算法直方图处理 1、直方图均衡…

ARM处理器概论

目录 一、ARM处理器概述 1.ARM的含义 2.ARM公司 3.主流的两种处理器 RISC处理器&#xff08;精简指令集&#xff09; CISC处理器&#xff08;复杂指令集&#xff09; 4.SOC 二、ARM指令集概述 1.指令与指令集 指令 指令集 2.ARM指令集 ARM指令集 Thumb指令集 3.编…

vuex----的辅助函数mapState, mapActions, mapMutations用法和混入

Vuex的辅助函数mapState, mapActions, mapMutations用法和混入 爱学习的渣渣关注IP属地: 江苏 2022.03.28 00:14:13字数 287阅读 469 一.使用mapState来获取state里的值 第一步先解构 1.最简单的使用数组的形式来获取模块中state中的值 2.用对象的形式来获取模块中的state的…

面试官:Spring refresh过程是怎样的?

小熊学Java网站&#xff1a;https://javaxiaobear.gitee.io/&#xff0c;每周持续更新干货&#xff0c;建议收藏&#xff01; 1. Spring refresh 流程 refresh 是 AbstractApplicationContext 中的核心方法&#xff0c;负责初始化 ApplicationContext 容器&#xff0c;容器必须…

【算法】七月算法打卡

# 2022-07-11 深拷贝 const copyObject (obj {}) > {let newObj nullif (typeof (obj) object && obj ! null) {newObj obj instanceof Array ? [] : {}// 进入下一层进行递归for (let i in obj) newObj[i] copyObject(obj[i])} else {newObj obj}return …

手机(Android)刷NetHunter安装指南,无需ssh执行kali命令, NetHunter支持的无线网卡列表!

一、安装NetHunter 前提&#xff1a;确保手机已经root&#xff0c;已装上magisk。如果没有root&#xff0c;可用尝试magisk root 后执行此文 1、下载Nethunter&#xff1a;Get Kali | Kali Linux 然后push 到sdcard 里&#xff0c; 2、打开magisk&#xff0c;选择刚刚下好的…

krpt.dll丢失怎么安装?怎么修复快一点

krpt.dll丢失怎么安装&#xff1f;其实你只要记住一点&#xff0c;只要是dll文件丢失&#xff0c;那么就不外乎那几种修复方法&#xff0c;自己下载丢失的文件安装&#xff0c;或者使用dll修复工具&#xff0c;这些方法都是可以修复的。下面我们详细的说一说。 目录 一.krpt.…

性能优化-内存泄漏、内存溢出、cpu占用高、死锁、栈溢出、FullGC频繁检测手段-总结与分享

介绍 什么是内存泄漏 含义&#xff1a;内层泄露是程序中己动态分配的堆内存由于某种原因程序未释放或无法释放&#xff0c;造成系统内存的浪费。&#xff08;换言之&#xff0c;GC回收不了这些不再被使用的对象&#xff0c;这些对象的生命周期太长&#xff09; 危害&#xff…

Linux学习03-Linux基础命令1

1 开始执行命令 command [-options] parameter1 parameter2 一行命令中第一个输入的部分绝对是命令或者可执行文件。 例如 ls命令列出目录的文件&#xff0c;需要列出隐藏文件则加 -al。 ls -al ls ls -a -l 2 基础命令操作 显示日期与时间的命令&#xff1a;date 显示日…

Ubuntu22.04系统中二进制包安装公有云k8s

目录公有云版k8s的架构是怎样的公有云中创建k8s实例的过程如下二进制法创建k8s的一般过程Kubernetes的重要性check nodes每台服务器执行基线配置CA rootetcd HA cluster根据CA根证书创建etcd的专有CA证书将etcd注册成为systemd服务配置各master节点的etcd.confansible配置各个m…

东北大学2023分布式操作系统考试重点

Note&#xff1a;以下10个点为老师画的重点 1.分布式系统目标 第一章 分布式系统概论&#xff0c;第二节 2.RPC过程 第四章 分布式通信管理&#xff0c; 第二节 客户过程以普通方式调用相应的客户存根客户存根建立消息并激活内核陷阱内核将消息发送到远程内核远程内核将消息…

计算机毕设Python+Vue写字楼物业管理系统(程序+LW+部署)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

C++ Reference: Standard C++ Library reference: Containers: map: map: operator[]

C官网参考链接&#xff1a;https://cplusplus.com/reference/map/map/operator[]/ 公有成员函数 <map> std::map::operator[] C98 mapped_type& operator[] (const key_type& k); C11 mapped_type& operator[] (const key_type& k); mapped_type& …

SQL基础——数据更新

数据更新前言思维导图数据的插入&#xff08;INSERT语句的使用方法&#xff09;什么是INSERTINSERT语句的基本语法语法4.1 INSERT语句代码示例4.2 创建 ProductIns表的CREATE TABLE语句代码示例4.3 向表中插入一行数据代码示例4.4 INSERT插入多行数据列清单的省略代码示例4.5 省…

chatGPT的49种应用场景,及各开发语言对接chatGPT参考指南

前沿 可能有人在问我&#xff0c;勇哥为什么chatGPT都被微信下架了&#xff0c;你还要写相关的chatGPT的文章呢&#xff1f;其实我们先不论微信下架的原因&#xff0c;单说chatGPT的达芬奇模型给勇哥带来的科技感早就超越了一切&#xff0c;所以勇哥依旧决定连续熬两个夜为大家…

【与达梦同行】达梦驱动图谱

达梦驱动图谱 摘要 达梦提供了大部分主流开发语言的驱动接口&#xff0c;在我用使用过的国产数据库中对客户端驱动的支持应该算是非常不错的。本文主要介绍达梦的驱动开发&#xff0c;通过实际操作&#xff0c;从环境搭建到实践验证&#xff0c;介绍了达梦各种语言驱动的详细使…

博途S7-1500T 使用工艺对象驱动液压轴(含SimaHydTO库)

利用工艺对象控制液压轴位置含PLC控制和仿真程序文档资料下载地址如下: https://support.industry.siemens.com/cs/document/109756217/simatic-s7-1500(t)-lsimahydto-for-hydraulic-applications-and-hydraulic-presses?dti=0&dl=en&lc=zh-CNhttps://support.indu…

圣诞树-python绘制雪夜圣诞树并封装为小程序

绘制雪夜圣诞树并封装为小程序 使用turtle绘制一颗雪夜圣诞树&#xff0c;然后封装成exe小程序送给你的朋友吧&#xff01; PS&#xff1a;只能在windows运行。 转载注明本文链接和作者 先看效果图&#xff1a; 绘制雪夜圣诞树 由于代码有三百多行&#xff0c;我放在下面的两…

一文彻底搞懂cookie、session、token

1.Cookie Cookie是客户端保存用户信息的一种机制&#xff0c;用来记录用户的一些信息&#xff0c;实际上Cookie是服务器在本地机器上存储的一小段文本&#xff0c;并随着每次请求发送到服务器。 Cookie技术通过请求和响应报文中写入Cookie信息来控制客户端的状态。 Cookie会…

28.项目搭建网关

项目搭建网关 一、项目架构 二、创建模块&#xff0c;引入依赖&#xff0c;创建启动类&#xff0c;添加application.yml配置文件 2.1依赖 <dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-…