static int mcp_smbus_xfer()

in hid-mcp2221.c [407:551]


static int mcp_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
				unsigned short flags, char read_write,
				u8 command, int size,
				union i2c_smbus_data *data)
{
	int ret;
	struct mcp2221 *mcp = i2c_get_adapdata(adapter);

	hid_hw_power(mcp->hdev, PM_HINT_FULLON);

	mutex_lock(&mcp->lock);

	ret = mcp_set_i2c_speed(mcp);
	if (ret)
		goto exit;

	switch (size) {

	case I2C_SMBUS_QUICK:
		if (read_write == I2C_SMBUS_READ)
			ret = mcp_i2c_smbus_read(mcp, NULL, MCP2221_I2C_RD_DATA,
						addr, 0, &data->byte);
		else
			ret = mcp_smbus_write(mcp, addr, command, NULL,
						0, MCP2221_I2C_WR_DATA, 1);
		break;
	case I2C_SMBUS_BYTE:
		if (read_write == I2C_SMBUS_READ)
			ret = mcp_i2c_smbus_read(mcp, NULL, MCP2221_I2C_RD_DATA,
						addr, 1, &data->byte);
		else
			ret = mcp_smbus_write(mcp, addr, command, NULL,
						0, MCP2221_I2C_WR_DATA, 1);
		break;
	case I2C_SMBUS_BYTE_DATA:
		if (read_write == I2C_SMBUS_READ) {
			ret = mcp_smbus_write(mcp, addr, command, NULL,
						0, MCP2221_I2C_WR_NO_STOP, 0);
			if (ret)
				goto exit;

			ret = mcp_i2c_smbus_read(mcp, NULL,
						MCP2221_I2C_RD_RPT_START,
						addr, 1, &data->byte);
		} else {
			ret = mcp_smbus_write(mcp, addr, command, &data->byte,
						1, MCP2221_I2C_WR_DATA, 1);
		}
		break;
	case I2C_SMBUS_WORD_DATA:
		if (read_write == I2C_SMBUS_READ) {
			ret = mcp_smbus_write(mcp, addr, command, NULL,
						0, MCP2221_I2C_WR_NO_STOP, 0);
			if (ret)
				goto exit;

			ret = mcp_i2c_smbus_read(mcp, NULL,
						MCP2221_I2C_RD_RPT_START,
						addr, 2, (u8 *)&data->word);
		} else {
			ret = mcp_smbus_write(mcp, addr, command,
						(u8 *)&data->word, 2,
						MCP2221_I2C_WR_DATA, 1);
		}
		break;
	case I2C_SMBUS_BLOCK_DATA:
		if (read_write == I2C_SMBUS_READ) {
			ret = mcp_smbus_write(mcp, addr, command, NULL,
						0, MCP2221_I2C_WR_NO_STOP, 1);
			if (ret)
				goto exit;

			mcp->rxbuf_idx = 0;
			mcp->rxbuf = data->block;
			mcp->txbuf[0] = MCP2221_I2C_GET_DATA;
			ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
			if (ret)
				goto exit;
		} else {
			if (!data->block[0]) {
				ret = -EINVAL;
				goto exit;
			}
			ret = mcp_smbus_write(mcp, addr, command, data->block,
						data->block[0] + 1,
						MCP2221_I2C_WR_DATA, 1);
		}
		break;
	case I2C_SMBUS_I2C_BLOCK_DATA:
		if (read_write == I2C_SMBUS_READ) {
			ret = mcp_smbus_write(mcp, addr, command, NULL,
						0, MCP2221_I2C_WR_NO_STOP, 1);
			if (ret)
				goto exit;

			mcp->rxbuf_idx = 0;
			mcp->rxbuf = data->block;
			mcp->txbuf[0] = MCP2221_I2C_GET_DATA;
			ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
			if (ret)
				goto exit;
		} else {
			if (!data->block[0]) {
				ret = -EINVAL;
				goto exit;
			}
			ret = mcp_smbus_write(mcp, addr, command,
						&data->block[1], data->block[0],
						MCP2221_I2C_WR_DATA, 1);
		}
		break;
	case I2C_SMBUS_PROC_CALL:
		ret = mcp_smbus_write(mcp, addr, command,
						(u8 *)&data->word,
						2, MCP2221_I2C_WR_NO_STOP, 0);
		if (ret)
			goto exit;

		ret = mcp_i2c_smbus_read(mcp, NULL,
						MCP2221_I2C_RD_RPT_START,
						addr, 2, (u8 *)&data->word);
		break;
	case I2C_SMBUS_BLOCK_PROC_CALL:
		ret = mcp_smbus_write(mcp, addr, command, data->block,
						data->block[0] + 1,
						MCP2221_I2C_WR_NO_STOP, 0);
		if (ret)
			goto exit;

		ret = mcp_i2c_smbus_read(mcp, NULL,
						MCP2221_I2C_RD_RPT_START,
						addr, I2C_SMBUS_BLOCK_MAX,
						data->block);
		break;
	default:
		dev_err(&mcp->adapter.dev,
			"unsupported smbus transaction size:%d\n", size);
		ret = -EOPNOTSUPP;
	}

exit:
	hid_hw_power(mcp->hdev, PM_HINT_NORMAL);
	mutex_unlock(&mcp->lock);
	return ret;
}