#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <endian.h>
#include "libi2c_impl.h"
void
i2c_io_req_fini(i2c_io_req_t *req)
{
free(req);
}
bool
i2c_io_req_init(i2c_port_t *port, i2c_io_req_t **reqp)
{
i2c_hdl_t *hdl = port->port_hdl;
i2c_io_req_t *req;
if (reqp == NULL) {
return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
"invalid i2c_io_req_t output pointer: %p", reqp));
}
req = calloc(1, sizeof (i2c_io_req_t));
if (req == NULL) {
int e = errno;
return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate "
"memory for a new i2c_io_req_t"));
}
req->io_port = port;
*reqp = req;
return (i2c_success(hdl));
}
bool
i2c_io_req_set_addr(i2c_io_req_t *req, const i2c_addr_t *addr)
{
i2c_hdl_t *hdl = req->io_port->port_hdl;
if (addr == NULL) {
return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
"invalid i2c_addr_t pointer: %p", addr));
}
if (!i2c_addr_validate(hdl, addr)) {
return (false);
}
req->io_addr = *addr;
req->io_addr_valid = true;
return (i2c_success(hdl));
}
bool
i2c_io_req_set_transmit_data(i2c_io_req_t *req, const void *buf, size_t len)
{
i2c_hdl_t *hdl = req->io_port->port_hdl;
if (buf == NULL && len > 0) {
return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "transmit "
"buffer cannot be a NULL pointer when the length is "
"non-zero (0x%zu)", len));
} else if (buf != NULL && len == 0) {
return (i2c_error(hdl, I2C_ERR_IO_WRITE_LEN_RANGE, 0,
"transmit data length cannot be zero when given a "
"non-NULL pointer " "(%p)", buf));
} else if (len > I2C_REQ_MAX) {
return (i2c_error(hdl, I2C_ERR_IO_WRITE_LEN_RANGE, 0, "cannot "
"transmit more than %zu bytes in a request, valid range is "
"[0x00, 0x%x]", len, I2C_REQ_MAX));
}
req->io_tx_len = len;
req->io_tx_buf = buf;
return (i2c_success(hdl));
}
bool
i2c_io_req_set_receive_buf(i2c_io_req_t *req, void *buf, size_t len)
{
i2c_hdl_t *hdl = req->io_port->port_hdl;
if (buf == NULL && len > 0) {
return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "receive "
"buffer cannot be a NULL pointer when the length is "
"non-zero (0x%zu)", len));
} else if (buf != NULL && len == 0) {
return (i2c_error(hdl, I2C_ERR_IO_READ_LEN_RANGE, 0, "receive "
"data length cannot be zero when given a non-NULL pointer "
"(%p)", buf));
} else if (len > I2C_REQ_MAX) {
return (i2c_error(hdl, I2C_ERR_IO_READ_LEN_RANGE, 0, "cannot "
"receive more %zu bytes in a request, valid range is "
"[0x00, 0x%x]", len, I2C_REQ_MAX));
}
req->io_rx_len = len;
req->io_rx_buf = buf;
return (i2c_success(hdl));
}
bool
i2c_io_req_exec(i2c_io_req_t *req)
{
i2c_hdl_t *hdl = req->io_port->port_hdl;
i2c_req_t i2c;
if (!req->io_addr_valid) {
return (i2c_error(hdl, I2C_ERR_IO_REQ_MISSING_FIELDS, 0,
"cannot execute I/O request due to missing fields: "
"device address"));
}
if (req->io_tx_len == 0 && req->io_rx_len == 0) {
return (i2c_error(hdl, I2C_ERR_IO_REQ_IO_INVALID, 0,
"I/O request invalid: no transmit or receive specified"));
}
(void) memset(&i2c, 0, sizeof (i2c_req_t));
i2c.ir_addr = req->io_addr;
i2c.ir_wlen = req->io_tx_len;
i2c.ir_rlen = req->io_rx_len;
if (i2c.ir_wlen > 0) {
(void) memcpy(i2c.ir_wdata, req->io_tx_buf, req->io_tx_len);
}
if (ioctl(req->io_port->port_fd, UI2C_IOCTL_I2C_REQ, &i2c) != 0) {
int e = errno;
return (i2c_ioctl_syserror(hdl, e, "I2C I/O request"));
}
if (i2c.ir_error.i2c_error != I2C_CORE_E_OK) {
return (i2c_ioctl_error(hdl, &i2c.ir_error, "I2C I/O request"));
}
if (i2c.ir_rlen > 0) {
(void) memcpy(req->io_rx_buf, i2c.ir_rdata, req->io_rx_len);
}
return (i2c_success(hdl));
}
void
smbus_io_req_fini(smbus_io_req_t *req)
{
free(req);
}
static void
smbus_io_req_reset(smbus_io_req_t *req)
{
req->sir_op_valid = false;
req->sir_op = UINT32_MAX;
req->sir_flags = 0;
req->sir_cmd = 0;
req->sir_write = 0;
req->sir_writep = NULL;
req->sir_wlen = 0;
req->sir_rlen = 0;
}
bool
smbus_io_req_init(i2c_port_t *port, smbus_io_req_t **reqp)
{
i2c_hdl_t *hdl = port->port_hdl;
smbus_io_req_t *req;
if (reqp == NULL) {
return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
"invalid smbus_io_req_t output pointer: %p", reqp));
}
req = calloc(1, sizeof (smbus_io_req_t));
if (req == NULL) {
int e = errno;
return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate "
"memory for a new smbus_io_req_t"));
}
req->sir_port = port;
smbus_io_req_reset(req);
*reqp = req;
return (i2c_success(hdl));
}
bool
smbus_io_req_set_addr(smbus_io_req_t *req, const i2c_addr_t *addr)
{
i2c_hdl_t *hdl = req->sir_port->port_hdl;
if (addr == NULL) {
return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
"invalid i2c_addr_t pointer: %p", addr));
}
if (!i2c_addr_validate(hdl, addr)) {
return (false);
}
req->sir_addr = *addr;
req->sir_addr_valid = true;
return (i2c_success(hdl));
}
bool
smbus_io_req_set_quick_cmd(smbus_io_req_t *req, bool write)
{
smbus_io_req_reset(req);
req->sir_op_valid = true;
req->sir_op = SMBUS_OP_QUICK_COMMAND;
req->sir_flags = write ? I2C_IO_REQ_F_QUICK_WRITE : 0;
return (i2c_success(req->sir_port->port_hdl));
}
bool
smbus_io_req_set_send_byte(smbus_io_req_t *req, uint8_t u8)
{
smbus_io_req_reset(req);
req->sir_op_valid = true;
req->sir_op = SMBUS_OP_SEND_BYTE;
req->sir_write = u8;
return (i2c_success(req->sir_port->port_hdl));
}
bool
smbus_io_req_set_write_u8(smbus_io_req_t *req, uint8_t cmd, uint8_t u8)
{
smbus_io_req_reset(req);
req->sir_op_valid = true;
req->sir_op = SMBUS_OP_WRITE_BYTE;
req->sir_cmd = cmd;
req->sir_write = u8;
return (i2c_success(req->sir_port->port_hdl));
}
bool
smbus_io_req_set_write_u16(smbus_io_req_t *req, uint8_t cmd, uint16_t u16)
{
smbus_io_req_reset(req);
req->sir_op_valid = true;
req->sir_op = SMBUS_OP_WRITE_WORD;
req->sir_cmd = cmd;
req->sir_write = u16;
return (i2c_success(req->sir_port->port_hdl));
}
bool
smbus_io_req_set_write_u32(smbus_io_req_t *req, uint8_t cmd, uint32_t u32)
{
smbus_io_req_reset(req);
req->sir_op_valid = true;
req->sir_op = SMBUS_OP_WRITE_U32;
req->sir_cmd = cmd;
req->sir_write = u32;
return (i2c_success(req->sir_port->port_hdl));
}
bool
smbus_io_req_set_write_u64(smbus_io_req_t *req, uint8_t cmd, uint64_t u64)
{
smbus_io_req_reset(req);
req->sir_op_valid = true;
req->sir_op = SMBUS_OP_WRITE_U64;
req->sir_cmd = cmd;
req->sir_write = u64;
return (i2c_success(req->sir_port->port_hdl));
}
bool
smbus_io_req_set_write_block(smbus_io_req_t *req, uint8_t cmd,
const void *wdata, size_t wlen, bool i2c)
{
i2c_hdl_t *hdl = req->sir_port->port_hdl;
if (wdata == NULL) {
return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
"invalid input data pointer: %p", wdata));
}
if (wlen == 0) {
return (i2c_error(hdl, I2C_ERR_IO_WRITE_LEN_RANGE, 0, "write "
"block requests must tranmit a non-zero amount of data"));
} else if (wlen > I2C_REQ_MAX) {
return (i2c_error(hdl, I2C_ERR_IO_WRITE_LEN_RANGE, 0, "cannot "
"transmit %zu bytes in a request, valid range is [0x00, "
"0x%x]", wlen, I2C_REQ_MAX));
}
smbus_io_req_reset(req);
req->sir_op_valid = true;
req->sir_op = i2c ? SMBUS_OP_I2C_WRITE_BLOCK : SMBUS_OP_WRITE_BLOCK;
req->sir_cmd = cmd;
req->sir_writep = wdata;
req->sir_wlen = wlen;
return (i2c_success(hdl));
}
bool
smbus_io_req_set_recv_byte(smbus_io_req_t *req, uint8_t *u8p)
{
i2c_hdl_t *hdl = req->sir_port->port_hdl;
if (u8p == NULL) {
return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
"invalid uint8_t pointer: %p", u8p));
}
smbus_io_req_reset(req);
req->sir_op_valid = true;
req->sir_op = SMBUS_OP_RECV_BYTE;
req->sir_readp = u8p;
return (i2c_success(hdl));
}
bool
smbus_io_req_set_read_u8(smbus_io_req_t *req, uint8_t cmd, uint8_t *u8p)
{
i2c_hdl_t *hdl = req->sir_port->port_hdl;
if (u8p == NULL) {
return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
"invalid uint8_t pointer: %p", u8p));
}
smbus_io_req_reset(req);
req->sir_op_valid = true;
req->sir_op = SMBUS_OP_READ_BYTE;
req->sir_cmd = cmd;
req->sir_readp = u8p;
return (i2c_success(hdl));
}
bool
smbus_io_req_set_read_u16(smbus_io_req_t *req, uint8_t cmd, uint16_t *u16p)
{
i2c_hdl_t *hdl = req->sir_port->port_hdl;
if (u16p == NULL) {
return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
"invalid uint16_t pointer: %p", u16p));
}
smbus_io_req_reset(req);
req->sir_op_valid = true;
req->sir_op = SMBUS_OP_READ_WORD;
req->sir_cmd = cmd;
req->sir_readp = u16p;
return (i2c_success(hdl));
}
bool
smbus_io_req_set_read_u32(smbus_io_req_t *req, uint8_t cmd, uint32_t *u32p)
{
i2c_hdl_t *hdl = req->sir_port->port_hdl;
if (u32p == NULL) {
return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
"invalid uint32_t pointer: %p", u32p));
}
smbus_io_req_reset(req);
req->sir_op_valid = true;
req->sir_op = SMBUS_OP_READ_U32;
req->sir_cmd = cmd;
req->sir_readp = u32p;
return (i2c_success(hdl));
}
bool
smbus_io_req_set_read_u64(smbus_io_req_t *req, uint8_t cmd, uint64_t *u64p)
{
i2c_hdl_t *hdl = req->sir_port->port_hdl;
if (u64p == NULL) {
return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
"invalid uint64_t pointer: %p", u64p));
}
smbus_io_req_reset(req);
req->sir_op_valid = true;
req->sir_op = SMBUS_OP_READ_U64;
req->sir_cmd = cmd;
req->sir_readp = u64p;
return (i2c_success(hdl));
}
bool
smbus_io_req_set_read_block_i2c(smbus_io_req_t *req, uint8_t cmd, void *rdata,
size_t rlen)
{
i2c_hdl_t *hdl = req->sir_port->port_hdl;
if (rdata == NULL) {
return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
"invalid output data pointer: %p", rdata));
}
if (rlen == 0) {
return (i2c_error(hdl, I2C_ERR_IO_READ_LEN_RANGE, 0, "read "
"block requests must tranmit a non-zero amount of data"));
} else if (rlen > I2C_REQ_MAX) {
return (i2c_error(hdl, I2C_ERR_IO_WRITE_LEN_RANGE, 0, "cannot "
"receive %zu bytes in a request, valid range is [0x00, "
"0x%x]", rlen, I2C_REQ_MAX));
}
smbus_io_req_reset(req);
req->sir_op_valid = true;
req->sir_op = SMBUS_OP_I2C_READ_BLOCK;
req->sir_cmd = cmd;
req->sir_readp = rdata;
req->sir_rlen = rlen;
return (i2c_success(req->sir_port->port_hdl));
}
bool
smbus_io_req_set_process_call(smbus_io_req_t *req, uint8_t cmd, uint16_t wdata,
uint16_t *rdatap)
{
i2c_hdl_t *hdl = req->sir_port->port_hdl;
if (rdatap == NULL) {
return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
"invalid output data pointer: %p", rdatap));
}
smbus_io_req_reset(req);
req->sir_op_valid = true;
req->sir_op = SMBUS_OP_PROCESS_CALL;
req->sir_cmd = cmd;
req->sir_write = wdata;
req->sir_readp = rdatap;
return (i2c_success(req->sir_port->port_hdl));
}
bool
smbus_io_req_exec(smbus_io_req_t *req)
{
i2c_hdl_t *hdl = req->sir_port->port_hdl;
smbus_req_t smbus;
if (!req->sir_addr_valid || !req->sir_op_valid) {
const char *miss;
if (!req->sir_addr_valid && !req->sir_op_valid) {
miss = "device address, SMBus operation code";
} else if (!req->sir_op_valid) {
miss = "SMBus operation code";
} else {
miss = "device address";
}
return (i2c_error(hdl, I2C_ERR_IO_REQ_MISSING_FIELDS, 0,
"cannot execute I/O request due to missing fields: %s",
miss));
}
(void) memset(&smbus, 0, sizeof (smbus_req_t));
smbus.smbr_op = req->sir_op;
smbus.smbr_flags = req->sir_flags;
smbus.smbr_addr = req->sir_addr;
smbus.smbr_cmd = req->sir_cmd;
switch (req->sir_op) {
case SMBUS_OP_SEND_BYTE:
case SMBUS_OP_WRITE_BYTE:
smbus.smbr_wdata[0] = (uint8_t)req->sir_write;
break;
case SMBUS_OP_WRITE_WORD:
case SMBUS_OP_PROCESS_CALL: {
uint16_t u16 = htole16((uint16_t)req->sir_write);
(void) memcpy(smbus.smbr_wdata, &u16, sizeof (u16));
break;
}
case SMBUS_OP_WRITE_U32: {
uint32_t u32 = htole32((uint32_t)req->sir_write);
(void) memcpy(smbus.smbr_wdata, &u32, sizeof (u32));
break;
}
case SMBUS_OP_WRITE_U64: {
uint64_t u64 = htole64(req->sir_write);
(void) memcpy(smbus.smbr_wdata, &u64, sizeof (u64));
break;
}
case SMBUS_OP_I2C_WRITE_BLOCK:
case SMBUS_OP_WRITE_BLOCK:
smbus.smbr_wlen = req->sir_wlen;
(void) memcpy(smbus.smbr_wdata, req->sir_writep, req->sir_wlen);
break;
case SMBUS_OP_I2C_READ_BLOCK:
smbus.smbr_rlen = req->sir_rlen;
break;
default:
break;
}
if (ioctl(req->sir_port->port_fd, UI2C_IOCTL_SMBUS_REQ, &smbus) != 0) {
int e = errno;
return (i2c_ioctl_syserror(hdl, e, "SMBus I/O request"));
}
if (smbus.smbr_error.i2c_error != I2C_CORE_E_OK) {
return (i2c_ioctl_error(hdl, &smbus.smbr_error,
"SMBus I/O request"));
}
switch (req->sir_op) {
case SMBUS_OP_RECV_BYTE:
case SMBUS_OP_READ_BYTE:
*(uint8_t *)req->sir_readp = smbus.smbr_rdata[0];
break;
case SMBUS_OP_READ_WORD:
case SMBUS_OP_PROCESS_CALL: {
uint16_t u16;
(void) memcpy(&u16, smbus.smbr_rdata, sizeof (uint16_t));
*(uint16_t *)req->sir_readp = letoh16(u16);
break;
}
case SMBUS_OP_READ_U32: {
uint32_t u32;
(void) memcpy(&u32, smbus.smbr_rdata, sizeof (uint32_t));
*(uint32_t *)req->sir_readp = letoh32(u32);
break;
}
case SMBUS_OP_READ_U64: {
uint64_t u64;
(void) memcpy(&u64, smbus.smbr_rdata, sizeof (uint64_t));
*(uint64_t *)req->sir_readp = letoh64(u64);
break;
}
case SMBUS_OP_READ_BLOCK:
case SMBUS_OP_I2C_READ_BLOCK:
case SMBUS_OP_BLOCK_PROCESS_CALL:
(void) memcpy(req->sir_readp, smbus.smbr_rdata,
smbus.smbr_rlen);
default:
break;
}
return (i2c_success(hdl));
}