Linux I2C设备分析1

Linux I2C设备分析1(基于Linux6.6)---总体框架

 

 

前言

在 Linux 中,I2C(Inter-Integrated Circuit)是一种常用的串行通信协议,用于在低速设备(如传感器、时钟芯片、EEPROM等)与主机(如微控制器、嵌入式系统)之间传输数据。I2C 总线采用主从架构,允许多个设备通过两个信号线(SDA:数据线,SCL:时钟线)进行通信。I2C 设备驱动模型是 Linux 内核中用于管理 I2C 设备的基础框架。

1. I2C 总线和 I2C 设备

I2C 协议通过两条线(SDA 和 SCL)提供双向的数据传输。I2C 总线上的设备可以有多个,其中一个是主设备,其他是从设备。每个 I2C 从设备都有唯一的地址。

在 Linux 内核中:

  • I2C 总线控制器驱动负责处理与 I2C 总线本身相关的操作(如启动、停止、时钟信号控制等)。
  • I2C 设备驱动负责与连接到 I2C 总线的特定设备进行通信。

2. I2C 设备驱动模型的主要组件

I2C 设备驱动模型的主要组件包括:

  1. I2C 总线驱动(I2C Bus Driver)
  2. I2C 设备结构(i2c_device_id)
  3. I2C 设备驱动结构(i2c_driver)

一、I2C控制器-CPU-I2C设备间的关系

在分析的LINUX设备驱动模型以及PLATFORM驱动模型,均只涉及总线-

设备-驱动,而i2c驱动模型则多了i2c适配器抽象,为什么会多个i2c适配器呢,它和i2c设备、i2c驱动、i2c总线的关系是怎样的呢,下面通过如下逻辑图进行说明。

如下图所示,CPU通过片间总线与I2C控制器通信,而各I2C设备则通过I2C总线与I2C控制器铜线。具体说明如下:

  1. CPU与I2C控制器之间主要是通过片间总线进行通信(如APB总线),但针对驱动工程师而言,对CPU与I2C控制器之间的片间总线是无感知的,驱动工程师只需要通过映射至CPU地址空间的寄存器地址,通过对寄存器进行读写即可与I2C控制器通信;
  2. I2C控制器与I2C设备之间则通过I2C总线进行通信,而I2C控制器作为主模式时,则实现用于产生时序、启动或停止传输等功能。而我们可以为该I2C控制器编写相应的驱动,实现时序产生、启动或停止传输时序,建立数据通信渠道,实现CPU与具体的I2C设备间的数据通信。
  3. 而针对具体的I2C设备,由于其实现的功能不同(rtc设备、风扇、温度传感器等),因此若要驱动具体的I2C设备,则需要针对具体的I2C设备编写相应的驱动程序。

230bdeb79f844db0a23fb292b9e1ba09.png 针对上面CPU-I2C控制器-I2C设备,我们也可以进行相应的抽象,如I2C控制器及其驱动、I2C

 

设备及其设备、I2C设备依附于具体的I2C控制器(需要借助具体I2C控制器的方法,才可以与挂载其上的I2C设备通信)、I2C控制器提供产生I2C通信相关的时序等。

以上抽象出的设备与驱动,而在LINUX的I2C驱动模型中,与上面的抽象基本一致:

  1. 在LINUX的I2C驱动模型中,其抽象了I2C adapter对应I2C controller,同时需要为每一个I2C控制器编写驱动程序
  2. LINUX定义了I2C client对应I2C设备;
  3. LINUX定义了I2C 的i2c_algorithm,该方法主要由具体的I2C adapter来实现产生I2C控制器与I2C设备间通信的机制(实现时序产生、启动或停止传输时序,建立数据通信渠道)。
  4. LINUX定义了I2C driver,作为对应I2C设备的驱动方法。

由以上的分析,我们可以看到,LINUX I2C模块通过抽象出I2C adapter、I2C client、I2C driver、i2c_algorithm,实现了I2C总线通信,而I2C模块也是对LINUX设备驱动模型的继承,自然也定义了总线类型的变量(bus_type类型的变量)。如下就是这几个结构体变量的逻辑关联图:

33a1da3a0e4745ab8bef9bcf13f1e6cd.png

 

二、I2C模型相关的结构体分析

既然i2c模型也是对linux 设备-总线-驱动模型的继承,因此i2c模块相关的结构体中一定存在bus_type、driver、device-driver这三种类型的变量,否则其将无法使用linux设备-总线-驱动模型。只要是linux系统中使用设备-总线-驱动模型的子模块,基本上其结构体都要包含bus_type、driver、device_driver这三种类型的变量。在i2c中定义了bus_type类型的总线变量,命名为i2c_bus_type。而i2c_client结构体是对一个i2c硬件设备的逻辑抽象,其中包含了device类型的成员变量;i2c_adapter结构体是对一个i2c控制器的逻辑抽象,其中也包含了device类型的成员变量;i2c_driver结构体是对i2c硬件设备的驱动的逻辑抽象,其中包含了device_driver类型的成员变量。分析下i2c_client、i2c_adapter、i2c_driver这三个结构体变量。

2.1、i2c_client结构体

该结构体是i2c硬件设备的逻辑抽象,包括了该i2c设备的地址类型、i2c设备地址、其所依附的adapter指针、其所绑定的i2c驱动、device类型的成员变量、链接至i2c驱动的链接头等信息,其通过adapter、driver指针连接了i2c适配器与驱动。

include/linux/i2c.h 

 struct i2c_client {
    /*主要用于设置i2c client的地址类型(10bit还是7bit)等信息*/
	unsigned short flags;		/* div., see below		*/
#define I2C_CLIENT_PEC		0x04	/* Use Packet Error Checking */
#define I2C_CLIENT_TEN		0x10	/* we have a ten bit chip address */
					/* Must equal I2C_M_TEN below */
#define I2C_CLIENT_SLAVE	0x20	/* we are the slave */
#define I2C_CLIENT_HOST_NOTIFY	0x40	/* We want to use I2C host notify */
#define I2C_CLIENT_WAKE		0x80	/* for board_info; true iff can wake */
#define I2C_CLIENT_SCCB		0x9000	/* Use Omnivision SCCB protocol */
					/* Must match I2C_M_STOP|IGNORE_NAK */
    /*标注该i2c设备的地址,7bit或9bit()*/
	unsigned short addr;		/* chip address - NOTE: 7bit	*/
					/* addresses are stored in the	*/
					/* _LOWER_ 7 bits		*/
	char name[I2C_NAME_SIZE];
    /*该i2c client所依附的i2c适配器*/
	struct i2c_adapter *adapter;	/* the adapter we sit on	*/
    /*该i2c设备所绑定的驱动*/
	struct i2c_driver *driver;	/* and our access routines	*/
    /*对应的device类型的变量,用于使用linux设备驱动模型*/
	struct device dev;		/* the device structure		*/
    /*中断号*/
	int irq;			/* irq issued by device		*/
    /*用于将该设备链接至其绑定的驱动对应的clients链表上*/
	struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
	i2c_slave_cb_t slave_cb;	/* callback for slave mode	*/
#endif
	void *devres_group_id;		/* ID of probe devres group	*/
};

2.2、i2c_adapter结构体

该结构体的定义如下,主要包括了该adapter提供的方法,该adapter支持的类型(class变量主要用于自动探测尚未注册到i2c总线上的i2c client,需要adapter和i2c driver有共同的类型,且i2c driver支持detect方法时才会自动探测)

include/linux/i2c.h 

 struct i2c_adapter {
	struct module *owner;
    /*用于表示该adapter支持自动detect i2c设备的类型(包括spd、hwmon等),若该
    adapter不支持通过detect接口探测尚未注册至i2c bus上的i2c设备,则可不填*/
	unsigned int class;		  /* classes to allow probing for */
    /*该i2c适配器支持的方法,该变量说明了该适配器支持的方法(i2c/smbus,支持访问的方式等等)*/
	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;
    /*该adapter对应的device*/
	struct device dev;		/* the adapter device */
 
    /*adapter的id与名称等*/
	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;
	struct regulator *bus_regulator;
};

2.3、i2c_driver结构体

该结构体的定义如下,其中class的含义与i2c_adapter中的含义相同,而detect函数指针就是上面所说对未注册进i2c总线的I2c设备进行自动探测,并注册进i2c总线中;而probe、remove等接口则主要是对设备驱动模型中device_driver中对应函数指针的实例;而attach_adapter接口则是在adapter注册进i2c总线时,对adapter下所有未注册的设备进行探测与匹配注册的操作,目前该接口已不推荐使用;而id_table主要用于说明该驱动支持哪些i2c client;address_list主要是配置detect函数指针使用,当探测尚未注册的i2c 设备时,仅对该变量中定义的i2c设备地址进行探测操作。

 include/linux/i2c.h

 struct i2c_driver {
    /*该变量主要指示该driver支持的类型,包括hwmon、内存相关的spd等,主要用于detect某一个adapter下的
    已挂载的硬件设备(且该硬件设备对应的逻辑设备未注册到i2c总线上)。*/
	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.
	 */
	/*该接口主要用于新注册adapter时,通过遍历i2c总线上所有已注册的驱动程序,针对每一个驱动均调用
    attach_adapter接口,识别该adapter上挂载的i2c设备,并未识别到的设备创建其对应的i2c_client*/
	int (*attach_adapter)(struct i2c_adapter *) __deprecated;
 
	/* Standard driver model interfaces */
    /*设备驱动模型的probe接口,在driver_register/device_register时,对于已注册的device和driver,
    若匹配则调用driver_probe_device进行探测操作,最终即调用该驱动的probe接口*/
	int (*probe)(struct i2c_client *, const struct i2c_device_id *);
    /*设备驱动模型的probe接口,在driver_unregister/device_unregister时,对于已绑定的device-driver,
    调用__device_release_driver进行移除操作,最终会调用该去的的remove接口*/
	int (*remove)(struct i2c_client *);
 
	/* driver model interfaces that don't relate to enumeration  */
    /*电源管理相关的接口*/
	void (*shutdown)(struct i2c_client *);
	int (*suspend)(struct i2c_client *, pm_message_t mesg);
	int (*resume)(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);
 
    /*设备驱动模型中的device-driver类型变量,为了继承设备驱动模型,包含该变量,即可
    使用设备驱动模型中的相应接口,实现驱动注册、驱动与设备的探测与移除等等内容*/
	struct device_driver driver;
    /*该驱动支持的设备列表,该变量主要用于设备与驱动的匹配*/
	const struct i2c_device_id *id_table;
 
	/* Device detection callback for automatic device creation */
    /*该接口的作用是若已注册到i2c总线上的adapter上挂载的物理设备,并没有创建对应的逻辑对象,则调用
    该接口,并根据该驱动的address_list上支持的地址类型,进行搜索该adapter上挂载的物理设
    备(且没有创建对应的逻辑对象)*/
	int (*detect)(struct i2c_client *, struct i2c_board_info *);
    /*表明该驱动所支持的i2c设备的地址列表*/
	const unsigned short *address_list;
	struct list_head clients;
	u32 flags;
};

三、i2c模块各结构体间的关联

在分析具体的实现前,先分析i2c模块的结构体间的关联,当对结构体间的关联熟悉后,再分析实现就简单很多。

主要从以下几个方面进行说明:

I2c各结构体之间设备驱动模型的关联、i2c adapter与i2c client的关联、i2c adapter与i2c client、i2c driver的关联。

3.1、I2c各结构体之间设备驱动模型的关联

本模块主要用于说明i2c adapter、 i2c driver、i2c client、i2c_bus是如何通过设备-总线-驱动模型间的结构体进行关联的。

  1. I2c driver通过device_driver类型的成员变量,完成与i2c_bus的关联,主要通过i2c bus的driver_kset、klist_drivers这两个变量实现不同的关联;
  2. I2c client/adapter通过device类型变量,完成与i2c bus的关联,主要通过klist_devices变量实现关联。特别的是i2c client通过device类型变量完成与i2c_driver的device_driver类型的成员变量的关联,通过knode_driver以及device_driver的klist_drivers的关联。

而device_driver、device、bus_type的关联以及驱动与设备绑定及探测,是通过设备驱动模型的接口device_register、driver_register实现的。

acd7f8f687c84f04a2224a5010c9e93e.png 

 

3.2、I2c client与i2c adapter之间的关联

上面已经说明了i2c client、i2c adapter、i2c driver之间通过设备-驱动模型的结构体进行关联,此处仅说明i2c client、i2c adapter这两个结构体本身成员的关联,如下图所示:

I2c adapter对应的device变量成员,是所有依附在该adapter上的i2c client的父设备,它们之间通过klist完成了关联(图中黑色箭头流向为此种关联);

I2c client通过其i2c_adapter类型的指针,完成对其所依附i2c adapter的指定。

053172a32b214feb9172a0e07d1e5ca8.png

 

3.3、I2c adapter、i2c client、i2c driver之间的关联

此处主要说明这三个结构体体间的关联,它们三个通过i2c client为桥梁实现了链接,其中i2c client、 i2c adapter之间的关联上面已经说明。

而i2c client与i2c driver之间,若i2c client是由i2c driver的detect接口探测到并添加到i2c总线上的,则它们之间通过链表链接在一起;若不是通过i2c driver的detect接口探测到的,则它们之间无须通过i2c driver的clients链表链接(这也是下图中使用虚线链接的原因,而通过detect探测的i2c设备一般是spd eeprom、hwmon类型的设备)。

37e4b038e8bb4c9d9a609118876fc257.png

 

3.3.1、Linux I2C 模块总体框架

Linux I2C 模块提供了一种标准的方式来在 I2C 总线上与多个设备进行通信。它是一个层次化的框架,主要包括以下几个关键组件:

  1. I2C 总线(I2C Adapter)

    • 每个 I2C 总线都由一个 I2C 总线适配器(i2c_adapter 驱动来管理。适配器负责执行 I2C 事务,并与物理硬件(例如 I2C 控制器)进行通信。
  2. I2C 设备(I2C Client)

    • 每个连接到 I2C 总线的设备都有一个 i2c_client 结构体,表示该设备在总线上。设备通过该结构体与驱动进行交互,并执行数据读写操作。
  3. I2C 驱动(I2C Driver)

    • i2c_driver 结构体定义了设备驱动的核心功能,负责匹配设备、处理设备的探测(probe)和移除(remove)操作。
    • 驱动与 I2C 设备通过设备 ID(i2c_device_id)进行匹配,确定哪个设备将由该驱动管理。
  4. 设备与驱动的匹配

    • i2c_device_id 结构体列出了设备的名称及其他标识信息,帮助内核识别哪个设备匹配哪一个驱动。

3.3.2、主要结构体及其关联关系

1. i2c_adapter(I2C 总线适配器)

  • 功能:管理和操作 I2C 总线,提供数据传输的接口。

  • 关键字段

    • master_xfer:定义如何执行 I2C 传输操作的函数指针。
    • dev:继承自 device 结构体,表示该总线适配器作为设备存在。
  • i2c_client 关系

    • 每个 i2c_client(I2C 设备)都属于一个 i2c_adapter(I2C 总线适配器)。通过 i2c_client->adapter 可以访问到相应的总线适配器。

2. i2c_client(I2C 设备)

  • 功能:表示一个 I2C 设备实例,包含设备在 I2C 总线上的地址和与设备驱动交互所需的基本信息。

  • 关键字段

    • adapter:指向 i2c_adapter,即设备所连接的总线适配器。
    • dev:继承自 device,表示该设备的基本信息。
    • addr:设备的 I2C 地址。
  • i2c_driver 关系

    • i2c_driver 通过匹配表 id_table 找到对应的 i2c_client。匹配成功后,probe 函数会被调用来初始化设备。

3. i2c_driver(I2C 设备驱动)

  • 功能:描述一个 I2C 设备驱动,处理设备的探测、初始化、数据传输以及设备的移除等操作。

  • 关键字段

    • probe:设备探测函数,内核会在匹配成功时调用此函数进行设备初始化。
    • remove:设备移除函数,在设备被移除时调用,释放资源。
    • id_table:设备与驱动之间的匹配表,存储 i2c_device_id 数组,帮助内核确定设备和驱动的对应关系。
  • i2c_device_id 关系

    • i2c_driver 通过 id_table 数组中的设备 ID 匹配表,确定哪个设备属于当前驱动。

4. i2c_device_id(I2C 设备标识符)

  • 功能:标识一个特定的 I2C 设备。通常包含设备的名称和一些特定标识数据。

  • 关键字段

    • name:设备的名称,通常与设备硬件的名称一致。
    • driver_data:与设备驱动相关的数据,通常用于特定驱动程序的标识符。
  • i2c_driver 关系

    • i2c_driver 使用 id_table 来匹配设备名(name)与设备驱动。

3.3.3、I2C 设备驱动的工作流程

  1. 设备驱动注册

    • 使用 module_i2c_driver() 宏注册 I2C 驱动,内核通过 i2c_driver 结构体找到匹配的设备并执行 probe
  2. 设备探测(probe

    • 当一个 I2C 设备被检测到并与驱动匹配时,内核调用 probe 函数进行初始化。
    • 驱动会在 probe 函数中初始化设备,配置设备硬件,申请资源等。
  3. 数据传输

    • 在设备驱动的运行过程中,可以通过 I2C 接口函数(如 i2c_master_send()i2c_master_recv())来执行数据传输。
  4. 设备移除(remove

    • 当设备被移除时,内核调用 remove 函数释放资源,关闭设备。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值