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

news/2024/11/17 16:37:39/

第二十九章 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进行匹配,来自于设备树中compatiblestruct 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_xferto NULL. If an adapter algorithm can do SMBus access, setsmbus_xfer. If set to NULL, the SMBus protocol is simulatedusing common I2C messages *//* master_xfer should return the number of messages successfullyprocessed, 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_COMPATi2c_adapter_compat_class = class_compat_register("i2c-adapter");if (!i2c_adapter_compat_class) {retval = -ENOMEM;goto bus_err;}
#endifretval = 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_COMPATclass_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endifbus_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.ppmy.cn/news/4229.html

相关文章

【图像分割】粒子群优化指数熵图像分割【含Matlab源码 287期】

⛄一、图像分割简介 理论知识参考&#xff1a;【基础教程】基于matlab图像处理图像分割【含Matlab源码 191期】 ⛄二、部分源代码 %%无递推 clc; clear; Cimread(‘1.jpg’); Crgb2gray©; %Cimnoise(C,‘gaussian’,0,0.005); figure subplot(1,2,1) imshow© tic …

Python基础-画图:matplotlib

Python画图主要用到matplotlib这个库。具体来说是pylab和pyplot这两个子库。这两个库可以满足基本的画图需求。 pylab神器&#xff1a;pylab.rcParams.update(params)。这个函数几乎可以调节图的一切属性&#xff0c;包括但不限于&#xff1a;坐标范围&#xff0c;axes标签字号…

JavaScript 非字母数字代码

JavaScript是一种松散类型的语言&#xff0c;它可以&#xff1a; 类似于 truetrue 2 可以不使用数字字面量来获取数字。 符号 可以将对象类型转为字符串类型{} [object Object]可以不用字符字面量来获取字符&#xff0c;例子&#xff1a;[object Object][1] oabc[0] a 以…

采集到的数据要怎么保证准确率

大家好&#xff0c;这里是小安说网控。 完整的电商数据采集到数据库以后&#xff0c;还不能直接拿来应用于渠道管控&#xff0c;必须要对繁多冗杂的数据进行有目的的清洗&#xff0c;才能使数据符合我们的要求。 一、 清洗维度 1、 数据杂质&#xff1a;格式错误、数据异常、数…

gin学习

文章目录零、知识补充GOPROXY地址一、准备工作1、安装gin包&#xff08;mod模式&#xff09;2、文档3、测试 hello gin二、GET POST PUT DELETE请求的使用1、修改端口号2、GET 查3、POST 增4、DELETE 删5、PUT 改6、如何取出参数6.1、GET6.2、POST DELETE PUT6.3、URI三、Bind模…

音视频Media内核学习——OpenMax浅析

一、OpenMax简介&#xff08;缩写为&#xff1a;OMX&#xff09; OpenMAX是一个多媒体应用程序的标准。由NVIDIA公司和Khronos™在2006年推出。 它是无授权费的、跨平台的C语言程序接口序列&#xff0c;这些接口对音频、视频、静态图片的常用操作进行封装。 它包括三层&…

C/C++程序的断点调试 - Dev C++

本文以Dev C 5.11为例&#xff0c;简述C/C程序断点调试的基本方法和过程。其它的IDE环境&#xff0c;大同小异。 本文引用自作者编写的下述图书; 本文允许以个人学习、教学等目的引用、讲授或转载&#xff0c;但需要注明原作者"海洋饼干叔 叔"&#xff1b;本文不允许…

DSP篇--C6701功能调试系列之Bootloader程序自引导

目录 1、头文件法 2、一级boot 3、二级boot 4、flash程序3取2测试 调试的前期准备可以参考前面的博文&#xff1a;DSP篇--C6701功能调试系列之前期准备_nanke_yh的博客-CSDN博客 boot程序的大小不能超过1KB&#xff0c;它主要完成以下几个功能&#xff1a;第一是配置DSP的…