Linux 的 I2C 体系结构

来源:互联网 发布:2017程序员薪资报告 编辑:程序博客网 时间:2024/05/29 13:41
Linux 的 I2C 体系结构分为 3 个组成部分。
(1)I2C 核心 drivers\i2c\I2c-core.c

I2C 核心提供了 I2C 总线驱动和设备驱动的注册、注销方法,I2C 通信方法(即“algorithm”,笔者认为直译为“运算方法”并不合适,为免引起误解,下文将直接使用“algorithm”)上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等.

(2)I2C 总线驱动。
I2C 总线驱动是对 I2C 硬件体系结构中适配器端的实现,适配器可由 CPU 控制,甚至可以直接集成在 CPU 内部。I2C 总线驱动主要包含了 I2C 适配器数据结构 i2c_adapter、I2C 适配器的 algorithm数据结构 i2c_algorithm 和控制 I2C 适配器产生通信信号的函数。经由 I2C 总线驱动的代码,我们可以控制 I2C 适配器以主控方式产生开始位、停止位、读写周期,以及以从设备方式被读写、产生 ACK 等。

(3)I2C 设备驱动

I2C 设备驱动是对 I2C 硬件体系结构中设备端的实现,设备一般挂接在受 CPU 控制的 I2C 适配器上,通过 I2C 适配器与CPU 交换数据。
I2C 设备驱动主要包含了数据结构 i2c_driver 和 i2c_client,我们需要根据具体设备实现其中的成员函数。

这三者之间的关系如下图所示:


在 Linux 2.6 内核中,所有的 I2C 设备都在 sysfs 文件系统中显示,存于/sys/bus/i2c/目录,以适配器地址和芯片地址的形式列出。

在 Linux 内核源代码中的 drivers 目录下包含一个 i2c 目录,而在 i2c 目录下又包含如下文件和文件夹。


1)i2c-core.c。
这个文件实现了 I2C 核心的功能以及/proc/bus/i2c*接口。主要是一些供别的文件调用的函数
(2)i2c-dev.c(主要是适配器设备的driver实现)。
实现了 I2C 适配器设备文件的功能,每一个 I2C 适配器都被分配一个设备。通过适配器访问设备时的主设备号都为 89,次设备号为 0~255。应用程序通过“i2c-%d”(i2c-0, i2c-1,..., i2c-10,...)文件名并使用文件操作接口 open()、write()、read()、ioctl()和 close()等来访问这个设备。
i2c-dev.c 并没有针对特定的设备而设计,只是提供了通用的 read()、write()和 ioctl()
等接口,应用层可以借用这些接口访问挂接在适配器上的 I2C 设备的存储空间或寄存器,并控制 I2C设备的工作方式。

(3)chips 文件夹。
这个目录中包含了一些特定的 I2C 设备驱动,如 Dallas 公司的 DS1337 实时钟芯片、EPSON 公司的 RTC8564 实时钟芯片和 I2C 接口的 EEPROM 驱动等。
(4)busses 文件夹。
这个文件中包含了一些 I2C总线的驱动,如 S3C2410 的 I2C 控制器驱动为i2c-s3c2410.c

5)algos 文件夹。
实现了一些 I2C 总线适配器的 algorithm。此外 , 内 核 中 的 i2c.h 这 个 头 文 件 对 i2c_driver 、 i2c_client 、 i2c_adapter 和
i2c_algorithm
这 4 个数据结构进行了定义。理解这 4 个结构体的作用十分关键

下面列出这几个结构体的定义:

i2c_adapter结构体:

struct i2c_adapter {struct module *owner;//所属的模块unsigned int id;//algorithm的类型,定义于i2c-id.h,以I2C_ALGO_开始unsigned int class;const struct i2c_algorithm *algo; /*总线通信方法结构体指针,非常重要*/void *algo_data;//通信方法的数据/* --- administration stuff. */int (*client_register)(struct i2c_client *);//client注册时被调用int (*client_unregister)(struct i2c_client *);//client注销时被调用/* data fields that are valid for all devices*/u8 level; /* nesting level for lockdep */struct mutex bus_lock;          //控制并发访问的互斥锁struct mutex clist_lock;int timeout;int retries;                     //重试的次数struct device dev;/* the adapter device 设备器的设备结构*/int nr;struct list_head clients;       //client的链表头,因为一个adapter上可能会有多个clientstruct list_head list;char name[48];                  //适配器名称struct completion dev_released;  //用于同步};
i2c_algorithm

struct i2c_algorithm {int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,                   int num);//i2c的传输函数指针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传输函数指针/* --- ioctl like call to set div. parameters. */int (*algo_control)(struct i2c_adapter *, unsigned int, unsigned long);        //类似于ioctl的函数指针/* To determine what the adapter supports */u32 (*functionality) (struct i2c_adapter *);//返回适配器支持的功能};
i2c_driver
struct i2c_driver {int id;unsigned int class;        //老版本的i2c驱动操作函数int (*attach_adapter)(struct i2c_adapter *);int (*detach_adapter)(struct i2c_adapter *);int (*detach_client)(struct i2c_client *);        //新版本的i2c驱动操作函数int (*probe)(struct i2c_client *);int (*remove)(struct i2c_client *);void (*shutdown)(struct i2c_client *);int (*suspend)(struct i2c_client *, pm_message_t mesg);int (*resume)(struct i2c_client *);/* 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);//类似ioctlstruct device_driver driver;//设备驱动结构体struct list_head list;};
i2c_client
struct i2c_client {unsigned short flags;//标志unsigned short addr;//芯片的地址,位于低7位char name[I2C_NAME_SIZE];struct i2c_adapter *adapter;//依附的i2c_adapterstruct i2c_driver *driver;//依附的i2c_driverint usage_count;//访问计数struct device dev;//设备结构体int irq;//中断?char driver_name[KOBJ_NAME_LEN];struct list_head list;//链表头struct completion released;//用于同步};
下面分析 i2c_driver、i2c_client、i2c_adapter 和 i2c_algorithm这 4 个数据结构的作用及其盘根错节的关系。
(1)i2c_adapter 与 i2c_algorithm。
i2c_adapter 对应于物理上的一个适配器,而 i2c_algorithm 对应一套通信方法。一个 I2C 适配器需要 i2c_algorithm 中提供的通信函数来控制适配器上产生特定的访问周期。缺少 i2c_algorithm 的 i2c_adapter 什么也做不了,因此 i2c_adapter 中包含其使用的 i2c_algorithm 的指针。
i2c_algorithm 中的关键函数 master_xfer()用于产生 I2C 访问周期需要的信号,以i2c_msg(即 I2C 消息)为单位。i2c_msg 结构体也非常关键,下面代码给出它的定义:

struct i2c_msg {__u16 addr;//子设备的地址__u16 flags;    //标志#define I2C_M_TEN0x10/* we have a ten bit chip address*/#define I2C_M_RD0x01#define I2C_M_NOSTART0x4000#define I2C_M_REV_DIR_ADDR0x2000#define I2C_M_IGNORE_NAK0x1000#define I2C_M_NO_RD_ACK0x0800#define I2C_M_RECV_LEN0x0400 /* length will be first received byte */__u16 len;//消息的长度__u8 *buf;//数据所存放的buf};
2)i2c_driver 与 i2c_client。
i2c_driver 对应一套驱动方法,是纯粹的用于辅助作用的数据结构,它不对应于任何的物理实体。i2c_client 对应于真实的物理设备(子设备芯片??),每个 I2C 设备都需要一个 i2c_client来描述。i2c_client 一般被包含在 I2C 字符设备的私有信息结构体中。
i2c_driver 与 i2c_client 发生关联的时刻在 i2c_driver 的 attach_adapter()函数被运行时。attach_adapter()会探测物理设备,当确定一个 client 存在时,把该 client 使用的i2c_client 数据结构的 adapter 指针指向对应的 i2c_adapter,driver 指针指向该 i2c_driver,并 会 调 用 i2c_adapter 的 client_register() 函 数 。 相反 的 过 程 发 生在 i2c_driver 的detach_client()函数被调用的时候。
(3)i2c_adpater 与 i2c_client。
i2c_adpater 与 i2c_client 的关系与 I2C 硬件体系中适配器和设备的关系一致,即i2c_client 依附于 i2c_adpater。由于一个适配器上可以连接多个 I2C 设备,所以一个i2c_adpater 也可以被多个 i2c_client 依附,i2c_adpater 中包括依附于它的 i2c_client 的链表。

假设 I2C 总线适配器 xxx 上有两个使用相同驱动程序的 yyy I2C 设备,在打开该I2C 总线的设备结点后相关数据结构之间的逻辑组织关系将如下图所示:

从上面的分析可知,虽然 I2C 硬件体系结构比较简单,但是 I2C 体系结构在 Linux中的实现却相当复杂。当工程师拿到实际的电路板,面对复杂的 Linux I2C 子系统,应该如何下手写驱动呢?究竟有哪些是需要亲自做的,哪些是内核已经提供的呢?理清这个问题非常有意义,可以使我们面对具体问题时迅速地抓住重点。
一方面,适配器驱动可能是 Linux 内核本身还不包含的;另一方面,挂接在适配器上的具体设备驱动可能也是 Linux 内核还不包含的。即便上述设备驱动都存在于Linux 内核中,其基于的平台也可能与我们的电路板不一样。因此,工程师要实现的主要工作如下。

1.提供 I2C 适配器的硬件驱动,探测、初始化 I2C 适配器(如申请 I2C 的 I/O 地址和中断号),驱动 CPU 控制的 I2C 适配器从硬件上产生各种信号以及处理I2C 中断等。
2.提 供 I2C 适 配 器 的 algorithm , 用 具 体 适 配 器 的 xxx_xfer() 函 数 填 充i2c_algorithm 的 master_xfer 指针,并把 i2c_algorithm 指针赋值给 i2c_adapter的 algo 指针。

3.实现 I2C 设备驱动与 i2c_driver 接口,用具体设备 yyy 的 yyy_attach_adapter()函数指针、yyy_detach_client()函数指针和 yyy_command()函数指针的赋值给i2c_driver 的 attach_ adapter、detach_adapter 和 detach_client 指针。

4.实现 I2C 设备驱动的文件操作接口,即实现具体设备 yyy 的 yyy_read()、yyy_write()和 yyy_ioctl()函数等。

注意:上述工作中前两个属于 I2C 总线驱动,后两个属于 I2C 设备驱动。