i2c_master_send()、i2c_master_recv()和i2c_transfer()

轉載自:https://blog.csdn.net/lz_1990/article/details/42501381

一、數據包I2C_MSG

Name
struct i2c_msg — an I2C transaction segment beginning with START

struct <span style="color:#cc0000;">i2c_msg </span>{
  __u16 addr;
  __u16 flags;
#define I2C_M_TEN        0x0010
#define I2C_M_RD        0x0001
#define I2C_M_NOSTART        0x4000
#define I2C_M_REV_DIR_ADDR    0x2000
#define I2C_M_IGNORE_NAK    0x1000
#define I2C_M_NO_RD_ACK        0x0800
#define I2C_M_RECV_LEN        0x0400
  __u16 len;
  __u8 * buf;
};  
Members
addr
Slave address, either seven or ten bits. When this is a ten bit address, I2C_M_TEN must be set in flags and the adapter must support I2C_FUNC_10BIT_ADDR.

flags
I2C_M_RD is handled by all adapters. No other flags may be provided unless the adapter exported the relevant I2C_FUNC_* flags through i2c_check_functionality.

len
Number of data bytes in buf being read from or written to the I2C slave address. For read transactions where I2C_M_RECV_LEN is set, the caller guarantees that this buffer can hold up to 32 bytes in addition to the initial length byte sent by the slave (plus, if used, the SMBus PEC); and this value will be incremented by the number of block data bytes received.

buf
The buffer into which data is read, or from which it's written.

Description
An i2c_msg is the low level representation of one segment of an I2C transaction. It is visible to drivers in the i2c_transfer() procedure, to userspace from i2c-dev, and to I2C adapter drivers through the i2c_adapter.master_xfer() method.

Except when I2C “protocol mangling” is used, all I2C adapters implement the standard rules for I2C transactions. Each transaction begins with a START. That is followed by the slave address, and a bit encoding read versus write. Then follow all the data bytes, possibly including a byte with SMBus PEC. The transfer terminates with a NAK, or when all those bytes have been transferred and ACKed. If this is the last message in a group, it is followed by a STOP. Otherwise it is followed by the next i2c_msg transaction segment, beginning with a (repeated) START.

Alternatively, when the adapter supports I2C_FUNC_PROTOCOL_MANGLING then passing certain flags may have changed those standard protocol behaviors. Those flags are only for use with broken/nonconforming slaves, and with adapters which are known to support the specific mangling options they need (one or more of IGNORE_NAK, NO_RD_ACK, NOSTART, and REV_DIR_ADDR).

即:i2c發送或者接收一次數據都以數據包 struct i2c_msg 封裝
struct i2c_msg { 
    __u16 addr;     // 從機地址 
    __u16 flags;    // 標誌 
#define I2C_M_TEN   0x0010  // 十位地址標誌 
#define I2C_M_RD    0x0001  // 接收數據標誌 
    __u16 len;      // 數據長度 
    __u8 *buf;      // 數據指針 
}; 
其中addr爲從機地址;flags則是通信的標誌,發送數據爲0,接收數據則爲 I2C_M_RD(本質是1);len是通信的數據字節數;buf 爲發送或接收數據的指針地址。

二、i2c_master_send ()和 i2c_master_recv()

在設備驅動中我們通常調用 i2c-core 定義的接口 i2c_master_send() 和 i2c_master_recv() 來發送或接收一次數據。

int i2c_master_send(struct i2c_client *client,const char *buf ,int count) 

    int ret; 
    struct i2c_adapter *adap=client->adapter;  // 獲取adapter信息 
    struct i2c_msg msg;                        // 定義一個臨時的數據包 
 
    msg.addr = client->addr;                   // 將從機地址寫入數據包 
    msg.flags = client->flags & I2C_M_TEN;     // 將從機標誌併入數據包 
    msg.len = count;                           // 將此次發送的數據字節數寫入數據包 
    msg.buf = (char *)buf;                     // 將發送數據指針寫入數據包 
 
    ret = i2c_transfer(adap, &msg, 1);         // 調用平臺接口發送數據 
 
    // If everything went ok (eg: 1 msg transmitted), return bytes number transmitted, else error code. 
    return (ret == 1) ? count : ret;           // 如果發送成功就返回字節數 

EXPORT_SYMBOL(i2c_master_send); 
參數介紹:client 爲此次與主機通信的從機,buf 爲發送的數據指針,count 爲發送數據的字節數。

int i2c_master_recv(struct i2c_client *client, char *buf ,int count) 

    struct i2c_adapter *adap=client->adapter;  // 獲取adapter信息 
    struct i2c_msg msg;                        // 定義一個臨時的數據包 
    int ret; 
 
    msg.addr = client->addr;                   // 將從機地址寫入數據包 
    msg.flags = client->flags & I2C_M_TEN;     // 將從機標誌併入數據包 
    msg.flags |= I2C_M_RD;                     // 將此次通信的標誌併入數據包 
    msg.len = count;                           // 將此次接收的數據字節數寫入數據包 
    msg.buf = buf; 
 
    ret = i2c_transfer(adap, &msg, 1);         // 調用平臺接口接收數據 
 
    /* If everything went ok , return number of bytes transmitted, else error code. */ 
    return (ret == 1) ? count : ret;           // 如果接收成功就返回字節數 

EXPORT_SYMBOL(i2c_master_recv); 
參數介紹:client 爲此次與主機通信的從機,buf 爲接收的數據指針,count 爲接收數據的字節數。

三、 i2c_transfer 接口的參數說明:

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); 
其中 adap 爲主機與從機通信的適配器;msgs 爲通信的數據包,可以是單個或多個數據包;num 用於指定數據包的個數,如果大於1則表明將進行不止一次的通信。通信一次就需要尋址一次,如果需要多次通信就需要多次尋址,前面2個接口都是進行一次通信,所以 num 爲1;有的情況下我們要讀一個寄存器的值,就需要先向從機發送一個寄存器地址然後再接收數據,這樣如果想自己封裝一個接口就需要將 num 設置爲2。接口的返回值如果失敗則爲負數,如果成功則返回傳輸的數據包個數。比如讀一個寄存器的值的接口可以如下封裝:
static int readI2cReg(struct i2c_client *client, unsigned char reg, unsigned char *data) 

    int ret;
    struct i2c_msg msgs[] = { 
        { 
            .addr   = client->addr, 
            .flags  = 0, 
            .len    = 1, 
            .buf    = &reg,  // 寄存器地址 
        }, 
        { 
            .addr   = client->addr, 
            .flags  = I2C_M_RD, 
            .len    = 1, 
            .buf    = data,  // 該地址用來存儲reg寄存器的值 
        }, 
    }; 
 
    ret = i2c_transfer(client->adapter, msgs, 2);  // 這裏 num = 2,通信成功 ret = 2 
    if (ret < 0) 
        print_err("%s error: %d\n", __func__, ret);
    return ret; 

還可調用前面所述的接口封裝:
static unsigned char readI2cReg(struct i2c_client *client, unsigned char reg) 

    unsigned char buf;
    i2c_master_send(client, &reg, 1);  // 發送寄存器地址 
    i2c_master_recv(client, &buf, 1);  // 接收寄存器的值
    return  buf; 

四、reset 接口
最近因爲平臺的i2c總線經常發生死鎖,用邏輯分析儀檢測發現通常爲SDA和SCL都被拉低,於是在i2c-core中加入了reset機制,總體思路如下:
1)在i2c.driver和i2c.adapter的結構中加入reset接口,即每一個i2c設備都可以註冊reset函數,每條i2c總線都有相應的reset接口
2)當發生死鎖時,首先根據i2c-timeout的信息獲取當前通信的設備地址和總線編號,然後依次執行當前總線下所有i2c設備的reset函數,再嘗試發送是否成功;如果總線仍然處於死鎖狀態則執行i2c.adapter的reset函數;如果總線還是處於死鎖狀態就重啓機器;總共3層reset機制
3)i2c.driver的reset函數一般操作設備的reset pin或者電源(需要根據硬件設計進行相應操作)
4)i2c.adapter的reset函數首選進行SCL的模擬解鎖方案,然後再是操作整個總線上設備的電源(需要根據硬件設計進行相應操作)
5)重啓是最後的一層機制,此時無法恢復設備的正常使用就只能重啓了
因爲i2c.adapter層的需要,在i2c-core中加入了遍歷當前總線所有設備並執行設備reset函數的接口i2c_reset_device:
/**
 * i2c_reset_device - reset I2C device when bus dead
 * @adapter: the adapter being reset
 * @addr: the device address
 */ 
static int __i2c_reset_device(struct device *dev, void *addrp) 

    struct i2c_client *client = to_i2c_client(dev); 
    int addr = *(int *)addrp; 
 
    if (client && client->driver && client->driver->reset) 
        return client->driver->reset();

    return 0; 

 
int i2c_reset_device(struct i2c_adapter *adapter, int addr) 

    return device_for_each_child(&adapter->dev, &addr, __i2c_reset_device); 

EXPORT_SYMBOL(i2c_reset_device); 
需要注意的是i2c.driver的reset函數返回值需要爲0,不然device_for_each_child不會繼續後面的遍歷。

用GPIO模擬SCL解鎖的參考代碼如下:

static int i2c_reset_adapter(void) 

    int counter = 0; 
 
    gpio_request(I2C_BUS_DATA, "gpioxx"); 
    gpio_request(I2C_BUS_CLK, "gpioxx"); 
    /* try to recover I2C bus */ 
    gpio_direction_input(I2C_BUS_DATA); 
 
    if (!__gpio_get_value(I2C_BUS_DATA)) { 
        while((!__gpio_get_value(I2C_BUS_DATA)) && ++counter < 10) 
        { 
            udelay(5); 
            gpio_direction_output(I2C_BUS_CLK, 1); 
            udelay(5); 
            gpio_direction_output(I2C_BUS_CLK, 0); 
        } 
        i2c_err("try to recover i2c bus, retry times are %d\n",counter); 
        if (counter < 10) { 
            udelay(5); 
            gpio_direction_output(I2C_BUS_DATA, 0); 
            udelay(5); 
            gpio_direction_output(I2C_BUS_CLK, 1); 
            udelay(5); 
            gpio_direction_output(I2C_BUS_DATA, 1); 
            msleep(10); 
        } else { 
            i2c_err("try to recover i2c bus failed!\n"); 
        } 
    }
    gpio_free(I2C_BUS_DATA); 
    gpio_free(I2C_BUS_CLK);
    return 0; 
}
 

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