linux I2C驱动框架(一)

目录

I2C核心。

I2C 总线驱动。

I2C 设备驱动。

linux I2C代码

i2c-core.c。

i2c-dev.c。

chips 文件夹。

busses 文件夹。

algos 文件夹。

I2C结构体

i2c_adapter 结构体

i2c_algorithm 结构体

i2c_driver 结构体

i2c_client 结构体

I2C结构体解析

i2c_adapter 与i2c_algorithm。

i2c_driver 与i2c_client。

i2c_adpater 与i2c_client。

驱动工程师工作


Linux 的I2C 体系结构分为3个组成部分,下面对这三个部分进行详细解释。

I2C核心。

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

I2C 总线驱动。

I2C 总线驱动是对 I2C 硬件体系结构中适配器端的实现,适配器可由 CPU 控制,甚至可以直接集成在 CPU 内部。

I2C 总线驱动主要包含了 I2C 适配器数据结构i2c_adapterI2C 适配器的algorithm数据结构i2c_algorithm和控制I2C 适配器产生通信信号的函数。

经由 I2C 总线驱动的代码,我们可以控制 I2C适配器以主控方式产生开始位、停止位、读写周期,以及以从设备方式被 读写、产生 ACK 等。

如上图中的紫色部分

I2C 设备驱动。

I2C 设备驱动是对 I2C 硬件体系结构中设备端的实现,设备一般挂接在受 CPU 控制的 I2C 适配器上,通过 I2C 适配器与 CPU 交换数据。如上图中的绿色部分

I2C 设备驱动主要包含了数据结构 i2c_driver和i2c_client,我们需要根据具体设备实现其中的成员函数。

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

$ tree /sys/bus/i2c/

/sys/bus/i2c/

|-- devices

| |-- 0-0048 -> ../../../devices/legacy/i2c-0/0-0048

| |-- 0-0049 -> ../../../devices/legacy/i2c-0/0-0049

| |-- 0-004a -> ../../../devices/legacy/i2c-0/0-004a

| |-- 0-004b -> ../../../devices/legacy/i2c-0/0-004b

| |-- 0-004c -> ../../../devices/legacy/i2c-0/0-004c

| |-- 0-004d -> ../../../devices/legacy/i2c-0/0-004d

| |-- 0-004e -> ../../../devices/legacy/i2c-0/0-004e

| '-- 0-004f -> ../../../devices/legacy/i2c-0/0-004f

'-- drivers

|-- i2c_adapter

'-- lm75

|-- 0-0048 -> ../../../../devices/legacy/i2c-0/0-0048

|-- 0-0049 -> ../../../../devices/legacy/i2c-0/0-0049

|-- 0-004a -> ../../../../devices/legacy/i2c-0/0-004a

|-- 0-004b -> ../../../../devices/legacy/i2c-0/0-004b

|-- 0-004c -> ../../../../devices/legacy/i2c-0/0-004c

|-- 0-004d -> ../../../../devices/legacy/i2c-0/0-004d

|-- 0-004e -> ../../../../devices/legacy/i2c-0/0-004e

'-- 0-004f -> ../../../../devices/legacy/i2c-0/0-004f

linux I2C代码

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

  • i2c-core.c

这个文件实现了 I2C 核心的功能以及/proc/bus/i2c*接口。

  • i2c-dev.c。

实现了 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 设备的工作方式。直接只用此函数尽心I2C通信,通过过程不需要I2C设备驱动的支持

  • chips 文件夹。

这个目录中包含了一些特定的I2C设备驱动,如Dallas公司的DS1337实时钟芯片、EPSON公司的RTC8564实时钟芯片和I2C接口的EEPROM驱动等。

  • busses 文件夹。

这个文件中包含了一些I2C主机控制器驱动(总线驱动),如S3C2410 的I2C 控制器驱动为i2c-s3c2410.c。

  • algos 文件夹。

实现了一些 I2C总线适配器的algorithm通信方法。

I2C结构体

内核中的 i2c.h 这个头文件i2c_driver、i2c_client、i2c_adapter 和i2c_algorithm 这4 个数据结构进行了定义。理解这4 个结构体的作用十分关键。

  • i2c_adapter 结构体

struct i2c_adapter {
    struct module *owner;/*所属模块*/    
    unsigned int class;
    struct i2c_algorithm *algo;/*总线通信方法结构体指针 */
    void *algo_data; /* algorithm 数据 */

    struct semaphore bus_lock; /*控制并发访问的自旋锁*/
    int timeout;
    int retries; /*重试次数*/
    struct device dev; /* 适配器设备 */

    int nr;    
    char name[48]; /*适配器名称*/
    struct completion dev_released; /*用于同步*/

    struct mutex userspace_clients_lock;
    struct list_head userspace_clients; /* client 链表头*/
    Struct i2c_bus_recovery_info *bus_recovery_info;
};
  • i2c_algorithm 结构体

struct i2c_algorithm {
    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
    /*i2c 传输函数指针,对应于普通的 i2c 传输协议*/

    int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
    /*smbus 传输函数指针,对应于i2c协议子集 smbus ,有些设备只支持这个协议*/
            unsigned short flags, char read_write,
            u8 command, int size, union i2c_smbus_data * data);

    u32 (*functionality) (struct i2c_adapter *);
    /*返回适配器支持的功能,用来描述,adapter 所具有的功能,比如是否支持 smbus*/

};

上述代码的master_xfer对应为I2C传输函数指针,I2C的主机驱动部分的大部分工作也聚焦在这里,smbus_xfer对应SMBus 传输函数指针,SMBus大部分基于I2C总线规范,SMBus不需增加额外引脚。与I2C总线相比,SMBus增加了一些新的功能性,在访问时序也有一定的差异。

  • i2c_driver 结构体

struct i2c_driver {
    unsigned int class;
    /* Notifies the driver that a new bus has appeared or is about to be
     * removed. You should avoid using this, it will be removed in a
     * near future.
     */
    int (*attach_adapter)(struct i2c_adapter *) __deprecated;
    int (*detach_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 *);
    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);

    struct device_driver driver;
    const struct i2c_device_id *id_table;

    /* 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_client 结构体

struct i2c_client {
    unsigned short flags; /* div., see below */
    unsigned short addr; /* chip address - NOTE: 7bit */
    /* addresses are stored in the */
    /* _LOWER_ 7 bits */
    char name[I2C_NAME_SIZE];
    struct i2c_adapter *adapter; /* the adapter we sit on */
    struct i2c_driver *driver; /* and our access routines */
    struct device dev; /* the device structure */
    int irq; /* irq issued by device */
    struct list_head detected;
};

I2C结构体解析

下面分析i2c_driver、i2c_client、i2c_adapter 和i2c_algorithm 这4 个数据结构的作用及其盘根错节的关系。

  • 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; /* slave address */
    __u16 flags;
    __u16 len; /* msg length */
    __u8 *buf; /* pointer to msg data */
};
  • i2c_driver 与i2c_client。

i2c_driver 对应一套驱动方法,是纯粹的用于辅助作用的数据结构,它不对应于任何的物理实体。其主要成员函数是probe、remove、suspend、resume等,另外。Struct i2c_device_id形式的id_table是该驱动所支持的I2C设备的ID表。i2c_client对应于真正的物理设备,每个i2c设备都需要一个i2c_client来描述,i2c_driver于i2c_client的关系是一对多,一个i2c_driver可以支持多个类型的i2c_client。

i2c_client的信息通常在bsp的版文件中通过i2c_board_info填充,如下面的代码就定义了一个I2C设备的id为“ad7142_joystick”、地址为0X2C,中断号为39的i2c_client。

static struct i2c_board_info __initdata bfin_i2c_board_info = {

{
    I2C_BOARD_INFO("ad7142_joystick", 0x2C),
    .irq = 39,
}
  • i2c_adpater 与i2c_client。

i2c_adpater 与i2c_client 的关系与I2C 硬件体系中适配器和设备的关系一致,即i2c_client 依附于i2c_adpater。由于一个适配器上可以连接多个I2C 设备,所以一个i2c_adpater 也可以被多个i2c_client 依附,i2c_adpater 中包括依附于它的i2c_client 的链表。

驱动工程师工作

虽然 I2C 硬件体系结构比较简单,但是I2C 体系结构在Linux 中的实现却相当复杂。当工程师拿到实际的电路板,面对复杂的Linux I2C 子系统,应该如何下手写驱动呢?究竟有哪些是需要亲自做的,哪些是内核已经提供的呢?理清这个问题非常有意义,可以使我们面对具体问题时迅速地抓住重点。

一方面,适配器驱动可能是 Linux 内核本身还不包含的;另一方面,挂接在适配器上的具体设备驱动可能也是Linux 内核还不包含的。即便上述设备驱动都存在于Linux 内核中,其基于的平台也可能与我们的电路板不一样。因此,工程师要实现的主要工作如下。

  • 提供 I2C 适配器的硬件驱动,探测、初始化I2C 适配器(如申请I2C 的I/O 地址和中断号)、驱动CPU 控制的I2C 适配器从硬件上产生各种信号以及处理I2C 中断等。

  • 提供 I2C 适配器的algorithm,用具体适配器的xxx_xfer()函数填充i2c_algorithm 的master_xfer 指针,并把i2c_algorithm 指针赋值给i2c_adapter 的algo 指针。

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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章