关于i2c_smbus系列函数

时间:2022-05-23 09:34:40
内核版本:linux-4.5

i2c_smbus系列函数有:
s32 i2c_smbus_read_byte(const struct i2c_client *client);
s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value);
s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command);
s32 i2c_smbus_write_byte_data(const struct i2c_client *client,
u8 command, u8 value);
s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command);
s32 i2c_smbus_write_word_data(const struct i2c_client *client,
u8 command, u16 value);
s32 i2c_smbus_read_block_data(const struct i2c_client *client,
u8 command, u8 *values);
s32 i2c_smbus_write_block_data(const struct i2c_client *client,
u8 command, u8 length, const u8 *values);
s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client,
u8 command, u8 length, u8 *values);
s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client,
u8 command, u8 length, const u8 *values);

smbs是基于i2c总线的,但同i2c总线还是稍微有点区别的,这里也不再关注这点。

上面的一系列函数最终都是调用的i2c_smbus_xfer()函数,i2c_smbus_xfer()函数代码如下:

/**
* i2c_smbus_xfer - execute SMBus protocol operations
* @adapter: Handle to I2C bus
* @addr: Address of SMBus slave on that bus
* @flags: I2C_CLIENT_* flags (usually zero or I2C_CLIENT_PEC)
* @read_write: I2C_SMBUS_READ or I2C_SMBUS_WRITE
* @command: Byte interpreted by slave, for protocols which use such bytes
* @protocol: SMBus protocol operation to execute, such as I2C_SMBUS_PROC_CALL
* @data: Data to be read or written
*
* This executes an SMBus protocol operation, and returns a negative
* errno code else zero on success.
*/
s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
char read_write, u8 command, int protocol,
union i2c_smbus_data *data)
{
unsigned long orig_jiffies;
int try;
s32 res;

/* If enabled, the following two tracepoints are conditional on
* read_write and protocol.
*/
trace_smbus_write(adapter, addr, flags, read_write,
command, protocol, data);
trace_smbus_read(adapter, addr, flags, read_write,
command, protocol);

flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;

if (adapter->algo->smbus_xfer) {
i2c_lock_adapter(adapter);

/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (res = 0, try = 0; try <= adapter->retries; try++) {
res = adapter->algo->smbus_xfer(adapter, addr, flags,
read_write, command,
protocol, data);
if (res != -EAGAIN)
break;
if (time_after(jiffies,
orig_jiffies + adapter->timeout))
break;
}
i2c_unlock_adapter(adapter);

if (res != -EOPNOTSUPP || !adapter->algo->master_xfer)
goto trace;
/*
* Fall back to i2c_smbus_xfer_emulated if the adapter doesn't
* implement native support for the SMBus operation.
*/
}

res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
command, protocol, data);

trace:
/* If enabled, the reply tracepoint is conditional on read_write. */
trace_smbus_reply(adapter, addr, flags, read_write,
command, protocol, data);
trace_smbus_result(adapter, addr, flags, read_write,
command, protocol, res);

return res;
}
EXPORT_SYMBOL(i2c_smbus_xfer);
首先在判断主控制器是否支持smbus_xfer传输,但是通常i2c主控制器都是不支持的,所以直接调用i2c_smbus_xfer_emulated()函数。
/* Simulate a SMBus command using the i2c protocol   No checking of parameters is done!  */static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,				   unsigned short flags,				   char read_write, u8 command, int size,				   union i2c_smbus_data *data){	/* So we need to generate a series of msgs. In the case of writing, we	  need to use only one message; when reading, we need two. We initialize	  most things with sane defaults, to keep the code below somewhat	  simpler. */	unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];	unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];	int num = read_write == I2C_SMBUS_READ ? 2 : 1;	int i;	u8 partial_pec = 0;	int status;	struct i2c_msg msg[2] = {		{			.addr = addr,			.flags = flags,			.len = 1,			.buf = msgbuf0,		}, {			.addr = addr,			.flags = flags | I2C_M_RD,			.len = 0,			.buf = msgbuf1,		},	};	msgbuf0[0] = command;	switch (size) {	case I2C_SMBUS_QUICK:		msg[0].len = 0;		/* Special case: The read/write field is used as data */		msg[0].flags = flags | (read_write == I2C_SMBUS_READ ?					I2C_M_RD : 0);		num = 1;		break;	case I2C_SMBUS_BYTE:		if (read_write == I2C_SMBUS_READ) {			/* Special case: only a read! */			msg[0].flags = I2C_M_RD | flags;			num = 1;		}		break;	case I2C_SMBUS_BYTE_DATA:		if (read_write == I2C_SMBUS_READ)			msg[1].len = 1;		else {			msg[0].len = 2;			msgbuf0[1] = data->byte;		}		break;	case I2C_SMBUS_WORD_DATA:		if (read_write == I2C_SMBUS_READ)			msg[1].len = 2;		else {			msg[0].len = 3;			msgbuf0[1] = data->word & 0xff;			msgbuf0[2] = data->word >> 8;		}		break;	case I2C_SMBUS_PROC_CALL:		num = 2; /* Special case */		read_write = I2C_SMBUS_READ;		msg[0].len = 3;		msg[1].len = 2;		msgbuf0[1] = data->word & 0xff;		msgbuf0[2] = data->word >> 8;		break;	case I2C_SMBUS_BLOCK_DATA:		if (read_write == I2C_SMBUS_READ) {			msg[1].flags |= I2C_M_RECV_LEN;			msg[1].len = 1; /* block length will be added by					   the underlying bus driver */		} else {			msg[0].len = data->block[0] + 2;			if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {				dev_err(&adapter->dev,					"Invalid block write size %d\n",					data->block[0]);				return -EINVAL;			}			for (i = 1; i < msg[0].len; i++)				msgbuf0[i] = data->block[i-1];		}		break;	case I2C_SMBUS_BLOCK_PROC_CALL:		num = 2; /* Another special case */		read_write = I2C_SMBUS_READ;		if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {			dev_err(&adapter->dev,				"Invalid block write size %d\n",				data->block[0]);			return -EINVAL;		}		msg[0].len = data->block[0] + 2;		for (i = 1; i < msg[0].len; i++)			msgbuf0[i] = data->block[i-1];		msg[1].flags |= I2C_M_RECV_LEN;		msg[1].len = 1; /* block length will be added by				   the underlying bus driver */		break;	case I2C_SMBUS_I2C_BLOCK_DATA:		if (read_write == I2C_SMBUS_READ) {			msg[1].len = data->block[0];		} else {			msg[0].len = data->block[0] + 1;			if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 1) {				dev_err(&adapter->dev,					"Invalid block write size %d\n",					data->block[0]);				return -EINVAL;			}			for (i = 1; i <= data->block[0]; i++)				msgbuf0[i] = data->block[i];		}		break;	default:		dev_err(&adapter->dev, "Unsupported transaction %d\n", size);		return -EOPNOTSUPP;	}	i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK				      && size != I2C_SMBUS_I2C_BLOCK_DATA);	if (i) {		/* Compute PEC if first message is a write */		if (!(msg[0].flags & I2C_M_RD)) {			if (num == 1) /* Write only */				i2c_smbus_add_pec(&msg[0]);			else /* Write followed by read */				partial_pec = i2c_smbus_msg_pec(0, &msg[0]);		}		/* Ask for PEC if last message is a read */		if (msg[num-1].flags & I2C_M_RD)			msg[num-1].len++;	}	status = i2c_transfer(adapter, msg, num);	if (status < 0)		return status;	/* Check PEC if last message is a read */	if (i && (msg[num-1].flags & I2C_M_RD)) {		status = i2c_smbus_check_pec(partial_pec, &msg[num-1]);		if (status < 0)			return status;	}	if (read_write == I2C_SMBUS_READ)		switch (size) {		case I2C_SMBUS_BYTE:			data->byte = msgbuf0[0];			break;		case I2C_SMBUS_BYTE_DATA:			data->byte = msgbuf1[0];			break;		case I2C_SMBUS_WORD_DATA:		case I2C_SMBUS_PROC_CALL:			data->word = msgbuf1[0] | (msgbuf1[1] << 8);			break;		case I2C_SMBUS_I2C_BLOCK_DATA:			for (i = 0; i < data->block[0]; i++)				data->block[i+1] = msgbuf1[i];			break;		case I2C_SMBUS_BLOCK_DATA:		case I2C_SMBUS_BLOCK_PROC_CALL:			for (i = 0; i < msgbuf1[0] + 1; i++)				data->block[i] = msgbuf1[i];			break;		}	return 0;}
首先定义了两个i2c_msg,还记得i2c_transfer()函数吗,这两个msg的buf分别为msgbuf0、msgbuf1。

然后来到switch处这里,这里主要就是针对的上面的i2c_smbus一系列函数。

1. i2c_smbus_read_byte()、i2c_smbus_write_byte()
这两个函数是没有太大意义的,如果是read,则连读的基地址都没有,如果是write,则是简单将value发送过去了,设备或许都不知道这是发送写的基地址还是写的数据,这两个函数有点类似于i2c_master_recv()、i2c_master_send()。

2. i2c_smbus_read_byte_data()、i2c_smbus_write_byte_data()
首先是read,如果是read将传送两个msg,第一个msg用来传送读的基地址,第二个msg用来读取数据。

然后是write,write只传送一个msg,只发送一个msg不好的地方是只能单方向传输,比如写就只能写,读就只能读(注意读的基地址是写方向)。但是这里为什么可以只传送一个msg呢,因为传输的方向并没有改变,包括写基地址,写数据都是写方向,但是写数据的长度为2,写基地址还是command,写的数据为value。

3. i2c_smbus_read_word_data()、i2c_smbus_write_word_data()
同上面类似,只是一次读写两个字节。

4. i2c_smbus_read_block_data()、i2c_smbus_write_block_data()
读注意,这里并没有指定读数据的长度。

5. i2c_smbus_read_i2c_block_data()、i2c_smbus_write_i2c_block_data()
同上面不一样的是,read时有长度指定。

而write同上面不同的是,i2c_smbus_write_block_data()函数会将发送数据的长度一起发送给设备,而这里则不会。


最终调用i2c_transfer函数来发送msg。

所以在我们的i2c设备驱动那边,可以简单的调用上面的i2c_smbs接口函数,也可以自己构造i2c_msg,然后调用i2c_transfer来发送数据。