第15章 Linux I2C核心、总线与设备驱动之Linux I2C核心

15.2 Linux I2C核心

I2C核心(drivers/i2c/i2c-core.c)中提供一组不依赖于硬件平台的接口函数,这个文件一般不需要被修改,但是理解其中的主要函数非常关键,因为I2C总线驱动和设备驱动之间以I2C核心作为纽带

I2C核心中的主要函数如下:

include/linux/i2c.h

(1)增加/删除i2c_adapter

int i2c_add_adapter(struct i2c_adapter *adap);

void i2c_del_adapter(struct i2c_adapter *adap);

(2)增加/删除i2c_driver

int i2c_register_driver(struct module *owner, struct i2c_driver *driver);

void i2c_del_driver(struct i2c_driver *driver);

/* use a define to avoid include chaining to get THIS_MODULE */
#define i2c_add_driver(driver) \

i2c_register_driver(THIS_MODULE, driver)

(3)I2C传输、发送和接收

int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num);
int i2c_master_send(struct i2c_client *client,const char *buf ,int count); // 写

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

备注:

i2c_transfer()函数用于进行I2C适配器和I2C设备之间的一组消息交互,其中第2个参数是一个指向i2c_msg数组的指针,所以i2c_transfer()一次可以传输多个i2c_msg(考虑到很多外设的读写波形比较复杂,比如读寄存器可能要先写,所以需要两个以上的消息)。对于时序比较简单的外设,i2c_master_send()函数和i2c_master_recv()函数内部会调用i2c_transfer()函数分别完成一条写消息和一条读消息,如代码清单15.6、15.7所示。

代码清单15.6 I2C核心的i2c_master_send()函数

/**
 * i2c_master_send - issue a single I2C message in master transmit mode
 * @client: Handle to slave device
 * @buf: Data that will be written to the slave
 * @count: How many bytes to write, must be less than 64k since msg.len is u16
 *
 * Returns negative errno, or else the number of bytes written.
 */
int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
{
int ret;
struct i2c_adapter *adap = client->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 (i.e. 1 msg transmitted), return #bytes
* transmitted, else error code.
*/
return (ret == 1) ? count : ret;
}
EXPORT_SYMBOL(i2c_master_send);

代码清单15.7 I2C核心的i2c_master_recv()函数

/**
 * i2c_master_recv - issue a single I2C message in master receive mode
 * @client: Handle to slave device
 * @buf: Where to store data read from slave
 * @count: How many bytes to read, must be less than 64k since msg.len is u16
 *
 * Returns negative errno, or else the number of bytes read.
 */
int i2c_master_recv(const struct i2c_client *client, char *buf, int count)
{
struct i2c_adapter *adap = client->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 (i.e. 1 msg received), return #bytes received,
* else error code.
*/
return (ret == 1) ? count : ret;
}

EXPORT_SYMBOL(i2c_master_recv);

备注:

i2c_transfer()函数本身不具备驱动适配器物理硬件以完成消息交互的能力,i2c_transfer()只是寻找到与
i2c_adapter对应的i2c_algorithm,并使用i2c_algorithm的master_xfer()函数真正驱动硬件流程,如代码清
单15.8所示。

代码清单15.8 I2C核心的i2c_transfer()函数

/**
 * i2c_transfer - execute a single or combined(组合的) I2C message
 * @adap: Handle to I2C bus
 * @msgs: One or more messages to execute before STOP is issued to
 * terminate the operation; each message begins with a START.
 * @num: Number of messages to be executed.
 *
 * Returns negative errno, else the number of messages executed.
 *
 * Note that there is no requirement that each message be sent to
 * the same slave address, although that is the most common model.
 */
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
int ret;

if (adap->algo->master_xfer) {
#ifdef DEBUG
for (ret = 0; ret < num; ret++) {
dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
"len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
}
#endif

if (in_atomic() || irqs_disabled()) {
ret = i2c_trylock_adapter(adap);
if (!ret)
/* I2C activity is ongoing. */
return -EAGAIN;
} else {
i2c_lock_adapter(adap);
}

ret = __i2c_transfer(adap, msgs, num);
i2c_unlock_adapter(adap);

return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -EOPNOTSUPP;
}
}
EXPORT_SYMBOL(i2c_transfer);

/**
 * __i2c_transfer - unlocked flavor of i2c_transfer
 * @adap: Handle to I2C bus
 * @msgs: One or more messages to execute before STOP is issued to
 * terminate the operation; each message begins with a START.
 * @num: Number of messages to be executed.
 *
 * Returns negative errno, else the number of messages executed.
 *
 * Adapter lock must be held when calling this function. No debug logging
 * takes place. adap->algo->master_xfer existence isn't checked.
 */
int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;

/* i2c_trace_msg gets enabled when tracepoint i2c_transfer gets
* enabled.  This is an efficient way of keeping the for-loop from
* being executed when not needed.
*/
if (static_key_false(&i2c_trace_msg)) {
int i;
for (i = 0; i < num; i++)
if (msgs[i].flags & I2C_M_RD)
trace_i2c_read(adap, &msgs[i], i);
else
trace_i2c_write(adap, &msgs[i], i);
}

/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (ret = 0, try = 0; try <= adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num);
if (ret != -EAGAIN)
break;
if (time_after(jiffies, orig_jiffies + adap->timeout))
break;
}

if (static_key_false(&i2c_trace_msg)) {
int i;
for (i = 0; i < ret; i++)
if (msgs[i].flags & I2C_M_RD)
trace_i2c_reply(adap, &msgs[i], i);
trace_i2c_result(adap, i, ret);
}

return ret;
}
EXPORT_SYMBOL(__i2c_transfer);


猜你喜欢

转载自blog.csdn.net/xiezhi123456/article/details/80540074