现在的位置: 首页 > 综合 > 正文

i2c-dev interface

2013年11月05日 ⁄ 综合 ⁄ 共 9137字 ⁄ 字号 评论关闭

1. i2c-dev interface

I2C dev-interface
通常,i2c设备由某个内核驱动控制。但是在用户空间,也可以访问某个I2C设备:你需要
加载i2c-dev模块。

每个被注册的i2c适配器(控制器)会获得一个数字号,从0开始。你可以检查
/sys/class/i2c-dev,来查看适配器对应哪个数字号。你也可以通过命令"i2cdetect -l"获
取你的当前系统的所有I2c适配器的列表。i2cdetct是i2c-tool包中的一个工具。

i2c设备文件是字符设备,主设备号是89,次设备号的分配如上所述。设备文件名通常被
规定为"i2c-%d"(i2c-0, i2c-1, ...,i2c-10, ...)i2c设备文件是字符设备,主设备号是
89,次设备号的分配如上所述。设备文件名通常被规定为"i2c-%d"(i2c-0, i2c-1,
...,i2c-10, ...).所有256个次设备号都保留给i2c使用。

C example
=========
假定你要在你的C应用程序中访问i2c适配器。第一件事情就是包含头文件"#include
<linux/i2c-dev.h>"。注意,存在两个"i2c-dev.h"文件: 一个属于Linux kernel,用于
内核驱动中;一个由i2c-tools发布,用于用户程序。显然,这里需要使用第二个
i2c-dev.h文件。

现在,你需要确定访问哪个适配器。你需要通过查看/sys/class/i2c-dev/或者运行
"i2cdetect -l"确定。适配器号时常是动态分配的,你无法预先假定某个值。因为它们甚
至会在系统重启后变为不同的值。

下一步,打开设备文件,如下:
    int file;
    int adapter_nr = 2; /*probably dynamically determined */
    char filename[20];

    snprintf(filename, 19, "/dev/i2c-%d", adapter_nr);
    file = open(filename, O_RDWR);
    if (file < 0) {
        /* ERROR HANDLING; you can check errno to see what went wrong */
        exit(1);
    }

当你打开设备后,你必须明确说明你相与哪个设备地址进行通信:
    int addr = 0x40;

    if (ioctl(file, I2C_SLAVE, addr) < 0) {
        /* ERROR HANDLING; you can check errno to see what went wrong */
        exit(1);
    }

Well, 你现在准备好与I2C设备通信了。你现在可以使用SMBus命令集或者无格式I2C
(plain I2C)与你的设备进行通信。如果设备支持SMB协议,则SMBus命令集优先选择。
代码如下:

    __u8 register = 0x10; /* Device register to access */
    __s32 res;
    char buf[10];

    /* Using SMBus commands */
    res = i2c_smbus_read_word_data(file, register);
    if (res < 0) {
        /* ERROR HANDLING; you can check errno to see what went wrong */
    } else {
        /* res contains the read word */
    }

    /* Using I2C Write, equivalent of
       i2c_smbus_write_word_data(file, register, 0x6543) */
    buf[0] = reister;
    buf[1] = 0x43;
    buf[2] = 0x65;
    if (write(file ,buf, 3) != 3) {
        /* ERROR HANDLING: i2c transaction failed */
    }

    /* Using I2C Read, equivalent of i2c_smbus_read_byte(file) */
    if (read(file, buf, 1) != 1) {
        /* ERROR HANDLING: i2c transaction failed */
    } else {
        /* buf[0] contains the read byte */
    }

注意,仅有I2C和SMBus协议的一部分子集功能可以通过read()和write()的调用完成。尤
其,对于组合型的传输(mixing read and write messages in the same transaction)不
被read()/write()支持。基于这个原因,read()和write()这两个接口几乎不被用户空间
程序使用。

IMPORTANT: because of the use of inline functions, you *have* to use '-O" or
some variation when you compile your program!

Full interface description
==========================

IOCTLs定义如下:

ioctl(file ,I2C_SLAVE, long addr)
Change slave address. The address is passed in the 7 lower bits of the
argument (except for 10 bit addresses, passed in the 10 lower bits in this
case)

ioctl(file, I2C_TENBIT, long select)
Selects ten bit addressed if select not equals 0, selects normal 7 bit
addresses if select equals 0. Default 0. This request is only valid if the
adapter has I2C_FUNC_10BIT_ADDR.

ioctl(file, I2C_PEC, long select)
Selects SMBus PEC (packet error checking) generation and verification if
select not equals 0, disables if select equals 0. Default 0.
Used only for SMBus transactions. This request only has an effect if the
adapter has I2C_FUNC_SMBUS_PEC; it is still safe if not, it just doesn't have
any effect.

ioctl(file, I2C_FUNCS, unsigned long *funcs)
Gets the adapter functionality and puts it in *funcs.

ioctl(file, I2C_RDWR, struct i2c_rdwr_ioctl_data *msgset)
Do combined read/write transaction without stop in between. Only valid if the
adatpter has I2C_FUNC_I2C. The argument is a pointer to a

struct i2c_rdwr_ioctl_data {
    struct i2c_msg *msgs;    /* ptr to array of simple messages */
    int nmsgs;        /* number of messages to exchanges */
}

msgs[]包含指向data buffer的指针。此函数调用会根据每个message中的I2C_M_RD flag
的设置向buffer写或者读数据。在每个message里,slave address和是否使用
ten-bit-address必须设置。

ioctl(file, I2C_SMBUS, struct i2c_smbus_ioctl_data *args)
Not meant to be called directly; instead, use the access functions below.

你可以通过使用read(2)和write(2)调用来进行无格式i2c传输。在访问设备前通过ioctl
I2C_SLAVE来设置地址。

你可以使用SMBus级传输(see documentation file smbus-protocol for details),通过
如下函数调用:
    __s32 i2c_smbus_write_quick(int file, __u8 value);
    __s32 i2c_smbus_read_byte(int file);
    __s32 i2c_smbus_write_byte(int file, __u8 value);
    __s32 i2c_smbus_read_byte_data(int file, __u8 command);
    __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value);
    __s32 i2c_smbus_read_word_data(int file, __u8 command);
    __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value);
    __s32 i2c_smbus_process_call(int file, __u8 comand, __u16 value);
    __s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values);
    __s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length,
                                    __u8 *values);
 
所有上述传输失败时返回-1;你可以读取errno来检查具体错误信息。'write'传输成功后
返回0;'read'传输成功后返回读取的值。但是read_block例外,他返回读取的字节数,
block buffer长度不超过32字节。

The above functions are all inline functions, that resolve to calls to the
i2c_smbus_access function, that on its turn calls a specific ioctl with the
data in a specific format. Read the source code if you want to know what
happenss behind the screens.

Implementation details
======================

下面是当你使用i2c的/dev接口时内核的内部处理:

1* 你的程序打开/dev/i2c-N并调用了ioctl()

2* open()和ioctl()调用会被i2c-dev内核驱动处理:参考i2c-dev.c的相关函数
(i2cdev_ioctl(), i2cdev_open() ...)。你可以认为i2c-dev是由用户程序访问的通用
i2c chip driver。

3* 一些ioctl的命令是针对管理任务的,可以被i2c-dev直接处理。例如I2C_SLAVE(设置你
想访问的设备地址)和I2C_PEC(使能或禁止SMBus错误检查)。

4* 其他ioctl的命令会被i2c-dev转化为内核驱动里的函数调用。例如I2C_FUNCS,通过调
用i2c.h:2c_get_functionality()来请求I2C适配器所支持的功能(functionality);
I2C_SMBUS,通过调用i2c-core.c:i2c_smbus_xfer()来执行SMBus传输。

i2c-dev driver负责检查来自用户空间的参数的有效性。除了这一点,用户程序访问
i2c和内核访问i2c的函数调用没什么区别。这意味着在i2c bus driver里不必实现任何支
持用户访问的代码。

5* i2c-core.c/i2c-.h中的函数是你的实际i2c bus driver所需要实现的封装调用例程。每个
吃配器都必须声明一些回调函数来实现这些标准调用。
i2c.h:i2c_get_functionality()调用i2c_adapter.algo->functionality(),
i2c-core.c:i2c_smbus_xfer()调用i2c_adapter.algo->smbus_xfer() (如果实现了
smbus_xfer()回调函数),否则
i2c-core.c:i2c_smbus_xfer_emulated()会调用i2c_adapter.algo->master_xfer()。
(注:smbus_xfer()和master_xfer()回调函数必须至少实现一个)

After your I2C bus driver has processed these request, execution runs up the
call chain, with almost no processing done, except by i2c-dev to package the
returned data, if any, in suitable format for the ioctl.

--------------------------------------------------------------------------------

 

2. i2c functionality

INTRODUCTION
------------

不是所有的I2C或者SMBus适配器实现了I2C规范上的所有功能,因此当访问I2C适配器时,
并不能完全假定适配器提供了你所需的功能。the client需要有一种检测适配器是否提供
了所需功能的方法。

FUNCTIONALILTY CONSTANTS
-----------------------

对于不断更新的I2C适配器功能常量列表,参考<linux/i2c.h>

I2C_FUNC_I2C            无格式i2c-level命令(Pure SMBus适配器不能用这些
                命令)
I2C_FUNC_10BIT_ADDR        处理10-bit地址的扩展
I2C_FUNC_PROTOCOL_MANGLING    熟知的有I2C_M_IGNORE_NAK, I2C_M_REV_DIR_ADDR,
                I2C_M_NOSTART, I2C_MNO_RD_ACK等flags (which modify the I2C protocol)
I2C_FUNC_SMBUS_QUICK        处理SMBus write_quick命令
I2C_FUNC_SMBUS_READ_BYTE    处理SMBus read_byte命令
I2C_FUNC_SMBUS_WRITE_BYTE    处理SMBus write_byte命令
I2C_FUNC_SMBUS_READ_BYTE_DATA    处理SMBus read_byte_data命令
I2C_FUNC_SMBUS_WRITE_BYTE_DATA    处理SMBus write_byte_data命令
I2C_FUNC_SMBUS_READ_WORD_DATA    处理SMBus read_word_data命令
I2C_FUNC_SMBUS_WRITE_WORD_DATA    处理SMBus write_word_data命令
I2C_FUNC_SMBUS_PROC_CALL    处理SMBus process_call命令
I2C_FUNC_SMBUS_READ_BLOCK_DATA    处理SMBus read_block_data命令
I2C_FUNC_SMBUS_WIRTE_BLOCK_DATA    处理SMBus wrtie_block_data命令
I2C_FUNC_SMBUS_READ_I2C_BLOCK    处理SMBus read_i2c_block_data命令
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK    处理SMBus write_i2c_block_data命令

还有一些flags的组合定义如下(为了你的方便):

I2C_FUNC_SMBUS_BYTE        处理SMBus read_byte & write_byte命令
I2C_FUNC_SMBUS_BYTE_DATA    处理SMBus read_byte_data & write_byte_data命令
I2C_FUNC_SMBUS_WORD_DATA    处理SMBus read_word_data & write_word_data命令
I2C_FUNC_SMBUS_BLOCK_DATA    处理SMBus read_block_data & write_block_data命令
I2C_FU7NC_SMBUS_I2C_BLOCK    处理SMBus read_i2c_block_data &
                write_i2c_block_data命令
I2C_FUNC_SMBUS_EMUL        处理所有的能够被I2C adapter仿真的SMBus命令
                (using transparent emulation layer)

ADAPTER IMPLEMENTATION
----------------------

当你写一个新的adapter driver时,你必须实现回调函数'functionality'。典型的实现
如下所示。

一个典型的SMBus-only adapter需要列出它能够支持的所有SMBus transactions。下面的
例子来自i2c-piix4 driver:

static u32 piix4_func(struct i2c_adapter *adapter)
{
    return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
               I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
               I2C_FUNC_SMBUS_BLOCK_DATA;
}

一个典型的full-I2C adapter需要使用如下的functionality函数(来自i2c-pxa driver):

srtatic u32 i2c_pxa_functionality(struct i2c_adapter *adap)
{
    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}

I2C_FUNC_SMBUS_EMUL包含了所有的SMBus transactions (包括I2C block
transactions), i2c-core可以使用I2C_FUNC_I2C来仿真SMBus所有命令。这个构思是希望
client drivers仅检查i2c子系统是否支持SMBus命令集,而不用关心是硬件支持还是软件
仿真。

CLIENT CHECKING
---------------

在一个client尝试访问I2C适配器之前,或者在测试适配器是否支持某一设备之前,应当
首先检测所需的functionality是否支持。典型的做法如下(from Im75 driver):

static int Im75_detect(...)
{
    (...)
    if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
                    I2C_FUNC_SMBUS_WORD_DATA))
        goto exit;
    (...)
}

Im75 driver检测了adapter是否支持SMBus byte data & SMBus word data
transactions。如果不支持,driver将不再在这个adapter上工作,不再继续执行。如果
检测成功,则driver知道它可以调用下面的函数:
i2c_smbus_read_byte_data(), i2c_smbus_write_byte_data(),
i2c_smbus_read_word_data(), i2c_smbus_write_word_data().
因此,你使用i2c_check_functionality()检测的functionality常量,应当与你的
驱动希望调用的函数接口相匹配。

注意上述的检测并不关心functionalities是否由硬件实现或者软件仿真。client
drivers不需要关心这个,i2c-core会在i2c adapter driver之上透明地实现SMBus
transactions.

CHECKING THROUGH /DEV
---------------------

如果你需要在用户空间访问i2c适配器的话,你需要使用/dev interface。你仍然需要检
测你需要的functionality是否被支持。这通过I2C_FUNCS ioctl来完成。示例如下:

int file;
if (file = open("/dev/i2c-0", O_RDWR) < 0) {
    /* Some kind of error handling */
    exit(1);
}
if (ioctl(file, I2C_FUNCS, &funcs) < 0) {
    /* Some kind of error handling */
    exit(1);
}
if (!(funcs & I2C_FUNC_SMBUS_QUICK)) {
    /* Oops, the needed functionality (SMBus write_quick function) is not
       available! */
    exit(1);
}
/*Now it is safe to use the SMBus write_quick command */

 

抱歉!评论已关闭.