#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/sysmacros.h>
#include <sys/debug.h>
#include <sys/hexdump.h>
#include <fcntl.h>
#include <unistd.h>
#include "i2cadm.h"
typedef enum {
I2CADM_IO_M_I2C,
I2CADM_IO_M_QUICK_READ,
I2CADM_IO_M_QUICK_WRITE,
I2CADM_IO_M_RECV_U8,
I2CADM_IO_M_READ_U8,
I2CADM_IO_M_READ_U16,
I2CADM_IO_M_READ_U32,
I2CADM_IO_M_READ_U64,
I2CADM_IO_M_READ_BLOCK_I2C,
I2CADM_IO_M_SEND_U8,
I2CADM_IO_M_WRITE_U8,
I2CADM_IO_M_WRITE_U16,
I2CADM_IO_M_WRITE_U32,
I2CADM_IO_M_WRITE_U64,
I2CADM_IO_M_WRITE_BLOCK,
I2CADM_IO_M_WRITE_BLOCK_I2C,
I2CADM_IO_M_CALL
} i2cadm_io_mode_t;
typedef enum {
I2CADM_IO_T_FIXED,
I2CADM_IO_T_VAR_READ,
I2CADM_IO_T_VAR_WRITE,
I2CADM_IO_T_VAR_RW,
I2CADM_IO_T_VAR_R_OR_W
} i2cadm_io_type_t;
typedef struct {
const char *mode_str;
const char *mode_help;
i2cadm_io_mode_t mode_val;
i2cadm_io_type_t mode_io;
bool mode_need_cmd;
uint32_t mode_rlen;
uint32_t mode_wlen;
size_t mode_dlen;
} i2cadm_mode_info_t;
typedef struct i2cadm_io_req {
const i2cadm_mode_info_t *io_mode;
i2c_io_req_t *io_i2c;
smbus_io_req_t *io_smbus;
uint8_t io_cmd;
uint16_t io_rlen;
uint16_t io_wlen;
void *io_wdata;
void *io_rdata;
} i2cadm_io_req_t;
static const i2cadm_mode_info_t i2cadm_io_modes[] = {
[I2CADM_IO_M_I2C] = {
.mode_str = "i2c",
.mode_help = "\t\t\tgeneral-purpose I2C I/O",
.mode_val = I2CADM_IO_M_I2C,
.mode_io = I2CADM_IO_T_VAR_R_OR_W,
.mode_dlen = sizeof (uint8_t)
},
[I2CADM_IO_M_QUICK_READ] = {
.mode_str = "quick-read",
.mode_help = "\t\tSMBus quick read",
.mode_val = I2CADM_IO_M_QUICK_READ,
.mode_io = I2CADM_IO_T_FIXED
},
[I2CADM_IO_M_QUICK_WRITE] = {
.mode_str = "quick-write",
.mode_help = "\t\tSMBus write read",
.mode_val = I2CADM_IO_M_QUICK_WRITE,
.mode_io = I2CADM_IO_T_FIXED
},
[I2CADM_IO_M_RECV_U8] = {
.mode_str = "recv-u8",
.mode_help = "\t\t\tSMBus receive byte",
.mode_val = I2CADM_IO_M_RECV_U8,
.mode_io = I2CADM_IO_T_FIXED,
.mode_rlen = sizeof (uint8_t),
.mode_dlen = sizeof (uint8_t)
},
[I2CADM_IO_M_READ_U8] = {
.mode_str = "read-u8",
.mode_help = "\t\t\tSMBus read byte with command",
.mode_val = I2CADM_IO_M_READ_U8,
.mode_io = I2CADM_IO_T_FIXED,
.mode_need_cmd = true,
.mode_rlen = sizeof (uint8_t),
.mode_dlen = sizeof (uint8_t)
},
[I2CADM_IO_M_READ_U16] = {
.mode_str = "read-u16",
.mode_help = "\t\tSMBus read word with command",
.mode_val = I2CADM_IO_M_READ_U16,
.mode_io = I2CADM_IO_T_FIXED,
.mode_need_cmd = true,
.mode_rlen = sizeof (uint16_t),
.mode_dlen = sizeof (uint16_t)
},
[I2CADM_IO_M_READ_U32] = {
.mode_str = "read-u32",
.mode_help = "\t\tSMBus read u32 with command",
.mode_val = I2CADM_IO_M_READ_U32,
.mode_io = I2CADM_IO_T_FIXED,
.mode_need_cmd = true,
.mode_rlen = sizeof (uint32_t),
.mode_dlen = sizeof (uint32_t)
},
[I2CADM_IO_M_READ_U64] = {
.mode_str = "read-u64",
.mode_help = "\t\tSMBus read u64 with command",
.mode_val = I2CADM_IO_M_READ_U64,
.mode_io = I2CADM_IO_T_FIXED,
.mode_need_cmd = true,
.mode_rlen = sizeof (uint64_t),
.mode_dlen = sizeof (uint64_t)
},
[I2CADM_IO_M_READ_BLOCK_I2C] = {
.mode_str = "read-block-i2c",
.mode_help = "\t\tSMBus I2C block read with command (length "
"not sent)",
.mode_val = I2CADM_IO_M_READ_BLOCK_I2C,
.mode_io = I2CADM_IO_T_VAR_READ,
.mode_need_cmd = true,
.mode_dlen = sizeof (uint8_t)
},
[I2CADM_IO_M_SEND_U8] = {
.mode_str = "send-u8",
.mode_help = "\t\t\tSMBus send byte",
.mode_val = I2CADM_IO_M_SEND_U8,
.mode_io = I2CADM_IO_T_FIXED,
.mode_wlen = sizeof (uint8_t),
.mode_dlen = sizeof (uint8_t)
},
[I2CADM_IO_M_WRITE_U8] = {
.mode_str = "write-u8",
.mode_help = "\t\tSMBus write byte with command",
.mode_val = I2CADM_IO_M_WRITE_U8,
.mode_io = I2CADM_IO_T_FIXED,
.mode_need_cmd = true,
.mode_wlen = sizeof (uint8_t),
.mode_dlen = sizeof (uint8_t)
},
[I2CADM_IO_M_WRITE_U16] = {
.mode_str = "write-u16",
.mode_help = "\t\tSMBus write word with command",
.mode_val = I2CADM_IO_M_WRITE_U16,
.mode_io = I2CADM_IO_T_FIXED,
.mode_need_cmd = true,
.mode_wlen = sizeof (uint16_t),
.mode_dlen = sizeof (uint16_t)
},
[I2CADM_IO_M_WRITE_U32] = {
.mode_str = "write-u32",
.mode_help = "\t\tSMBus write u32 with command",
.mode_val = I2CADM_IO_M_WRITE_U32,
.mode_io = I2CADM_IO_T_FIXED,
.mode_need_cmd = true,
.mode_wlen = sizeof (uint32_t),
.mode_dlen = sizeof (uint32_t)
},
[I2CADM_IO_M_WRITE_U64] = {
.mode_str = "write-u64",
.mode_help = "\t\tSMBus write u64 with command",
.mode_val = I2CADM_IO_M_WRITE_U64,
.mode_io = I2CADM_IO_T_FIXED,
.mode_need_cmd = true,
.mode_wlen = sizeof (uint64_t),
.mode_dlen = sizeof (uint64_t)
},
[I2CADM_IO_M_WRITE_BLOCK] = {
.mode_str = "write-block",
.mode_help = "\t\tSMBus block write with command and length",
.mode_val = I2CADM_IO_M_WRITE_BLOCK,
.mode_io = I2CADM_IO_T_VAR_WRITE,
.mode_need_cmd = true,
.mode_dlen = sizeof (uint8_t)
},
[I2CADM_IO_M_WRITE_BLOCK_I2C] = {
.mode_str = "write-block-i2c",
.mode_help = "\t\tSMBus I2C block write with command (length "
"not sent)",
.mode_val = I2CADM_IO_M_WRITE_BLOCK_I2C,
.mode_io = I2CADM_IO_T_VAR_WRITE,
.mode_need_cmd = true,
.mode_dlen = sizeof (uint8_t)
},
[I2CADM_IO_M_CALL] = {
.mode_str = "call",
.mode_help = "\t\t\tSMBus process call with command (tx and "
"rx a u16)",
.mode_val = I2CADM_IO_M_CALL,
.mode_io = I2CADM_IO_T_FIXED,
.mode_need_cmd = true,
.mode_rlen = sizeof (uint16_t),
.mode_wlen = sizeof (uint16_t),
.mode_dlen = sizeof (uint16_t)
}
};
void
i2cadm_io_usage(FILE *f)
{
(void) fprintf(f, "\ti2cadm io [-m mode] -d dest [-a addr] [-c cmd] "
"[-w wlen] [-r rlen] [-o output] <data>\n");
}
static void
i2cadm_io_help(const char *fmt, ...)
{
if (fmt != NULL) {
va_list ap;
va_start(ap, fmt);
vwarnx(fmt, ap);
va_end(ap);
}
(void) fprintf(stderr, "Usage: i2cadm io [-m mode] -d dest [-a addr] "
"[-r rlen] [-w wlen] [-o output]\n\t<data>\n");
(void) fprintf(stderr, "\nPerform I/O to any arbitrary I2C address on "
"the specified controller and\nport. If a mux is part of the "
"destination path, then it will be activated\nprior to issuing "
"the I/O. Transmitted data will be taken from positional\n"
"arguments.\n\nThe following options are supported:\n\n"
"\t-a addr\t\tthe 7-bit address to send the I/O to\n"
"\t-d dest\t\tspecifies the controller and port to target\n"
"\t-m mode\t\tsets the type of I/O issued, defaults to I2C\n"
"\t-o output\twrite raw data read to file output\n"
"\t-r rlen\t\tsets the number of bytes to read\n"
"\t-w wlen\t\tsets the number of bytes to write\n"
"\nThe following I/O modes are supported:\n");
for (size_t i = 0; i < ARRAY_SIZE(i2cadm_io_modes); i++) {
(void) fprintf(stderr, "\t%s%s\n", i2cadm_io_modes[i].mode_str,
i2cadm_io_modes[i].mode_help);
}
exit(EXIT_FAILURE);
}
static const i2cadm_mode_info_t *
i2cadm_io_parse_mode(const char *str)
{
for (size_t i = 0; i < ARRAY_SIZE(i2cadm_io_modes); i++) {
if (strcasecmp(str, i2cadm_io_modes[i].mode_str) == 0) {
return (&i2cadm_io_modes[i]);
}
}
warnx("unknown I/O mode: %s", str);
(void) printf("Valid I/O Modes:\n");
for (size_t i = 0; i < ARRAY_SIZE(i2cadm_io_modes); i++) {
(void) printf("\t%s%s\n", i2cadm_io_modes[i].mode_str,
i2cadm_io_modes[i].mode_help);
}
exit(EXIT_FAILURE);
}
static bool
i2cadm_io_read_ok(const i2cadm_mode_info_t *mode)
{
switch (mode->mode_io) {
case I2CADM_IO_T_FIXED:
return (mode->mode_rlen != 0);
case I2CADM_IO_T_VAR_READ:
case I2CADM_IO_T_VAR_RW:
case I2CADM_IO_T_VAR_R_OR_W:
return (true);
case I2CADM_IO_T_VAR_WRITE:
default:
return (false);
}
}
static bool
i2cadm_io_read_req(const i2cadm_mode_info_t *mode)
{
switch (mode->mode_io) {
case I2CADM_IO_T_VAR_READ:
case I2CADM_IO_T_VAR_RW:
return (true);
case I2CADM_IO_T_FIXED:
case I2CADM_IO_T_VAR_WRITE:
case I2CADM_IO_T_VAR_R_OR_W:
default:
return (false);
}
}
static bool
i2cadm_io_write_req(const i2cadm_mode_info_t *mode)
{
switch (mode->mode_io) {
case I2CADM_IO_T_VAR_WRITE:
case I2CADM_IO_T_VAR_RW:
return (true);
case I2CADM_IO_T_FIXED:
case I2CADM_IO_T_VAR_READ:
case I2CADM_IO_T_VAR_R_OR_W:
default:
return (false);
}
}
static bool
i2cadm_io_write_ok(const i2cadm_mode_info_t *mode)
{
switch (mode->mode_io) {
case I2CADM_IO_T_FIXED:
return (mode->mode_wlen != 0);
case I2CADM_IO_T_VAR_WRITE:
case I2CADM_IO_T_VAR_RW:
case I2CADM_IO_T_VAR_R_OR_W:
return (true);
case I2CADM_IO_T_VAR_READ:
default:
return (false);
}
}
static void
i2cadm_io_parse_rw_len(i2cadm_io_req_t *req, const char *rstr, const char *wstr)
{
const i2cadm_mode_info_t *mode = req->io_mode;
const char *modestr = mode->mode_str;
i2cadm_io_type_t type = mode->mode_io;
if (rstr == NULL && i2cadm_io_read_req(mode)) {
errx(EXIT_FAILURE, "missing required I/O read length "
"(-r) which is required for I/O mode %s", modestr);
}
if (wstr == NULL && i2cadm_io_write_req(mode)) {
errx(EXIT_FAILURE, "missing required I/O write length "
"(-w) which is required for I/O mode %s", modestr);
}
if (type == I2CADM_IO_T_VAR_R_OR_W && rstr == NULL && wstr == NULL) {
errx(EXIT_FAILURE, "I/O mode %s requires at least one or both "
"of a read length (-r) and write length (-w) to be "
"specified", modestr);
}
if (rstr != NULL) {
const char *errstr;
if (!i2cadm_io_read_ok(mode)) {
errx(EXIT_FAILURE, "I/O mode %s does not allow "
"specifying a read length (-r)", modestr);
}
req->io_rlen = (uint16_t)strtonumx(rstr, 1, I2C_REQ_MAX,
&errstr, 0);
if (errstr != NULL) {
errx(EXIT_FAILURE, "invalid read length: %s is %s, "
"valid values are between 1 and %u", rstr, errstr,
I2C_REQ_MAX);
}
if (type == I2CADM_IO_T_FIXED && req->io_rlen !=
mode->mode_rlen) {
errx(EXIT_FAILURE, "I/O mode %s has a fixed read "
"length of %u bytes, either do not specify -r or "
"set it to %u, not %s", modestr, mode->mode_rlen,
mode->mode_rlen, rstr);
}
} else if (type == I2CADM_IO_T_FIXED) {
req->io_rlen = mode->mode_rlen;
}
if (wstr != NULL) {
const char *errstr;
if (!i2cadm_io_write_ok(mode)) {
errx(EXIT_FAILURE, "I/O mode %s does not allow "
"specifying a write length (-w)", modestr);
}
req->io_wlen = (uint16_t)strtonumx(wstr, 1, I2C_REQ_MAX,
&errstr, 0);
if (errstr != NULL) {
errx(EXIT_FAILURE, "invalid write length: %s is %s, "
"valid values are between 1 and %u", wstr, errstr,
I2C_REQ_MAX);
}
if (type == I2CADM_IO_T_FIXED && req->io_wlen !=
mode->mode_wlen) {
errx(EXIT_FAILURE, "I/O mode %s has a fixed write "
"length of %u bytes, either do not specify -w or "
"set it to %u, not %s", modestr, mode->mode_wlen,
mode->mode_wlen, wstr);
}
} else if (type == I2CADM_IO_T_FIXED) {
req->io_wlen = mode->mode_wlen;
}
}
static void
i2cadm_io_parse_data(i2cadm_io_req_t *req, int argc, char *argv[])
{
uint32_t nents;
VERIFY3U(req->io_wlen, !=, 0);
VERIFY3U(req->io_mode->mode_dlen, !=, 0);
VERIFY0(req->io_wlen % req->io_mode->mode_dlen);
nents = req->io_wlen / req->io_mode->mode_dlen;
if (nents > 1 && req->io_mode->mode_dlen != 1) {
errx(EXIT_FAILURE, "fatal internal error, cannot handle "
"I/O request with multiple non-byte sized data points");
}
req->io_wdata = calloc(nents, req->io_mode->mode_dlen);
if (req->io_wdata == NULL) {
err(EXIT_FAILURE, "failed to allocate write data buffer (%u "
"elements, %zu bytes)", nents, req->io_mode->mode_dlen);
}
if (argc != nents) {
errx(EXIT_FAILURE, "write data requires %u elements, but only "
"found %d remaining arguments", nents, argc);
}
for (int i = 0; i < argc; i++) {
unsigned long long ull, max;
char *eptr;
uint8_t *u8;
uint16_t *u16;
uint32_t *u32;
uint64_t *u64;
errno = 0;
ull = strtoull(argv[i], &eptr, 0);
if (errno != 0 || *eptr != '\0') {
errx(EXIT_FAILURE, "failed to parse data element %s",
argv[i]);
}
switch (req->io_mode->mode_dlen) {
case 1:
max = UINT8_MAX;
break;
case 2:
max = UINT16_MAX;
break;
case 4:
max = UINT32_MAX;
break;
case 8:
max = UINT64_MAX;
break;
default:
abort();
}
if (ull > max) {
errx(EXIT_FAILURE, "data element %s is outside the "
"bounds for a %zu byte datum ([0, 0x%llx])",
argv[i], req->io_mode->mode_dlen, max);
}
switch (req->io_mode->mode_dlen) {
case 1:
u8 = req->io_wdata;
u8[i] = (uint8_t)ull;
break;
case 2:
u16 = req->io_wdata;
u16[i] = (uint16_t)ull;
break;
case 4:
u32 = req->io_wdata;
u32[i] = (uint32_t)ull;
break;
case 8:
u64 = req->io_wdata;
u64[i] = (uint64_t)ull;
break;
default:
abort();
}
}
}
static void
i2cadm_io_write(const i2cadm_io_req_t *req, const i2cadm_mode_info_t *mode,
int ofd)
{
size_t to_write = 0, off = 0;
switch (mode->mode_val) {
case I2CADM_IO_M_I2C:
case I2CADM_IO_M_READ_BLOCK_I2C:
to_write = req->io_rlen;
break;
case I2CADM_IO_M_RECV_U8:
case I2CADM_IO_M_READ_U8:
to_write = sizeof (uint8_t);
break;
case I2CADM_IO_M_READ_U16:
to_write = sizeof (uint16_t);
break;
case I2CADM_IO_M_READ_U32:
to_write = sizeof (uint32_t);
break;
case I2CADM_IO_M_READ_U64:
to_write = sizeof (uint64_t);
break;
default:
break;
}
while (to_write > 0) {
ssize_t ret = write(ofd, req->io_rdata + off, to_write);
if (ret < 0) {
err(EXIT_FAILURE, "failed to write %zu bytes to "
"output file at offset %zu", to_write, off);
}
to_write -= ret;
off += ret;
}
}
static void
i2cadm_io_init(const i2cadm_io_req_t *req, const i2cadm_mode_info_t *mode)
{
switch (mode->mode_val) {
case I2CADM_IO_M_I2C:
if (req->io_rlen != 0 &&
!i2c_io_req_set_receive_buf(req->io_i2c, req->io_rdata,
req->io_rlen)) {
i2cadm_fatal("failed to set I2C read buffer");
}
if (req->io_wlen != 0 &&
!i2c_io_req_set_transmit_data(req->io_i2c, req->io_wdata,
req->io_wlen)) {
i2cadm_fatal("Failed to set I2C write buffer");
}
break;
case I2CADM_IO_M_QUICK_READ:
if (!smbus_io_req_set_quick_cmd(req->io_smbus, false)) {
i2cadm_fatal("failed to set quick command request");
}
break;
case I2CADM_IO_M_QUICK_WRITE:
if (!smbus_io_req_set_quick_cmd(req->io_smbus, true)) {
i2cadm_fatal("failed to set quick command request");
}
break;
case I2CADM_IO_M_RECV_U8:
if (!smbus_io_req_set_recv_byte(req->io_smbus, req->io_rdata)) {
i2cadm_fatal("failed to set receive byte request");
}
break;
case I2CADM_IO_M_READ_U8:
if (!smbus_io_req_set_read_u8(req->io_smbus, req->io_cmd,
req->io_rdata)) {
i2cadm_fatal("failed to set read byte request");
}
break;
case I2CADM_IO_M_READ_U16:
if (!smbus_io_req_set_read_u16(req->io_smbus, req->io_cmd,
req->io_rdata)) {
i2cadm_fatal("failed to set read word request");
}
break;
case I2CADM_IO_M_READ_U32:
if (!smbus_io_req_set_read_u32(req->io_smbus, req->io_cmd,
req->io_rdata)) {
i2cadm_fatal("failed to set read u32 request");
}
break;
case I2CADM_IO_M_READ_U64:
if (!smbus_io_req_set_read_u64(req->io_smbus, req->io_cmd,
req->io_rdata)) {
i2cadm_fatal("failed to set read u64 request");
}
break;
case I2CADM_IO_M_READ_BLOCK_I2C:
if (!smbus_io_req_set_read_block_i2c(req->io_smbus, req->io_cmd,
req->io_rdata, req->io_rlen)) {
i2cadm_fatal("failed to set read block request");
}
break;
case I2CADM_IO_M_SEND_U8:
if (!smbus_io_req_set_send_byte(req->io_smbus,
*(uint8_t *)req->io_wdata)) {
i2cadm_fatal("failed to set send byte request");
}
break;
case I2CADM_IO_M_WRITE_U8:
if (!smbus_io_req_set_write_u8(req->io_smbus, req->io_cmd,
*(uint8_t *)req->io_wdata)) {
i2cadm_fatal("failed to set write byte request");
}
break;
case I2CADM_IO_M_WRITE_U16:
if (!smbus_io_req_set_write_u16(req->io_smbus, req->io_cmd,
*(uint16_t *)req->io_wdata)) {
i2cadm_fatal("failed to set write word request");
}
break;
case I2CADM_IO_M_WRITE_U32:
if (!smbus_io_req_set_write_u32(req->io_smbus, req->io_cmd,
*(uint32_t *)req->io_wdata)) {
i2cadm_fatal("failed to set write u32 request");
}
break;
case I2CADM_IO_M_WRITE_U64:
if (!smbus_io_req_set_write_u64(req->io_smbus, req->io_cmd,
*(uint64_t *)req->io_wdata)) {
i2cadm_fatal("failed to set write u64 request");
}
break;
case I2CADM_IO_M_WRITE_BLOCK:
case I2CADM_IO_M_WRITE_BLOCK_I2C:
if (!smbus_io_req_set_write_block(req->io_smbus, req->io_cmd,
req->io_wdata, req->io_wlen,
mode->mode_val == I2CADM_IO_M_WRITE_BLOCK_I2C)) {
i2cadm_fatal("failed to set write block request");
}
break;
case I2CADM_IO_M_CALL:
if (!smbus_io_req_set_process_call(req->io_smbus, req->io_cmd,
*(uint16_t *)req->io_wdata, req->io_rdata)) {
i2cadm_fatal("failed to set process call request");
}
break;
}
}
static void
i2cadm_io_print(const i2cadm_io_req_t *req, const i2cadm_mode_info_t *mode)
{
switch (mode->mode_val) {
case I2CADM_IO_M_I2C:
case I2CADM_IO_M_READ_BLOCK_I2C:
if (req->io_rlen == 0)
break;
(void) hexdump_file(req->io_rdata, req->io_rlen, HDF_HEADER |
HDF_ASCII, stdout);
break;
case I2CADM_IO_M_RECV_U8:
case I2CADM_IO_M_READ_U8:
(void) printf("0x%x\n", *(uint8_t *)req->io_rdata);
break;
case I2CADM_IO_M_READ_U16:
(void) printf("0x%x\n", *(uint16_t *)req->io_rdata);
break;
case I2CADM_IO_M_READ_U32:
(void) printf("0x%x\n", *(uint32_t *)req->io_rdata);
break;
case I2CADM_IO_M_READ_U64:
(void) printf("0x%" PRIx64 "\n", *(uint64_t *)req->io_rdata);
break;
default:
VERIFY3U(req->io_rlen, ==, 0);
break;
}
}
int
i2cadm_io(int argc, char *argv[])
{
int c, ofd = -1;
const i2cadm_mode_info_t *mode = &i2cadm_io_modes[I2CADM_IO_M_I2C];
const char *dpath = NULL, *addrstr = NULL, *cmdstr = NULL;
const char *wstr = NULL, *rstr = NULL, *output = NULL;
i2c_port_t *port;
i2c_dev_info_t *info;
i2c_addr_t addr;
i2cadm_io_req_t req;
while ((c = getopt(argc, argv, ":a:c:d:m:o:r:w:")) != -1) {
switch (c) {
case 'a':
addrstr = optarg;
break;
case 'c':
cmdstr = optarg;
break;
case 'd':
dpath = optarg;
break;
case 'm':
mode = i2cadm_io_parse_mode(optarg);
break;
case 'o':
output = optarg;
break;
case 'r':
rstr = optarg;
break;
case 'w':
wstr = optarg;
break;
case ':':
i2cadm_io_help("option -%c requires an argument",
optopt);
exit(EXIT_USAGE);
case '?':
i2cadm_io_help("unknown option: -%c", optopt);
exit(EXIT_USAGE);
}
}
if (dpath == NULL) {
errx(EXIT_FAILURE, "missing required destination path");
}
if (!i2c_port_dev_init_by_path(i2cadm.i2c_hdl, dpath, true, &port,
&info)) {
i2cadm_fatal("failed to parse path %s", dpath);
}
if (info != NULL) {
if (addrstr != NULL) {
errx(EXIT_FAILURE, "target address specified twice: "
"either use an I2C path that specified a device or "
"-a, not both");
}
addr = *i2c_device_info_addr_primary(info);
i2c_device_info_free(info);
info = NULL;
} else {
if (addrstr == NULL) {
errx(EXIT_FAILURE, "missing target address: specify an "
"I2C path that refers to a device or use -a");
}
if (!i2c_addr_parse(i2cadm.i2c_hdl, addrstr, &addr)) {
i2cadm_fatal("failed to parse address %s", addrstr);
}
}
bzero(&req, sizeof (req));
req.io_mode = mode;
if (mode->mode_val == I2CADM_IO_M_I2C) {
if (!i2c_io_req_init(port, &req.io_i2c)) {
i2cadm_fatal("failed to initialize I2C I/O request");
}
if (!i2c_io_req_set_addr(req.io_i2c, &addr)) {
i2cadm_fatal("failed to set I2C request address");
}
} else {
if (!smbus_io_req_init(port, &req.io_smbus)) {
i2cadm_fatal("failed to initialize SMBus I/O request");
}
if (!smbus_io_req_set_addr(req.io_smbus, &addr)) {
i2cadm_fatal("failed to set I2C request address");
}
}
if (mode->mode_need_cmd) {
const char *errstr = NULL;
if (cmdstr == NULL) {
errx(EXIT_FAILURE, "missing required SMBus command "
"value (-c) for I/O mode %s", mode->mode_str);
}
req.io_cmd = (uint8_t)strtonumx(cmdstr, 0, UINT8_MAX, &errstr,
0);
if (errstr != NULL) {
errx(EXIT_FAILURE, "invalid command value (-c): %s "
"is %s, valid values are between 0x00 and 0x%x",
cmdstr, errstr, UINT8_MAX);
}
} else {
if (cmdstr != NULL) {
errx(EXIT_FAILURE, "I/O mode %s does not allow "
"specifying an SMBus cmd (-c)", mode->mode_str);
}
}
i2cadm_io_parse_rw_len(&req, rstr, wstr);
argc -= optind;
argv += optind;
if (req.io_wlen == 0) {
if (argc != 0) {
errx(EXIT_USAGE, "encountered extraneous arguments "
"starting with %s", argv[0]);
}
} else {
i2cadm_io_parse_data(&req, argc, argv);
}
if (req.io_rlen != 0) {
req.io_rdata = calloc(req.io_rlen, sizeof (uint8_t));
if (req.io_rdata == NULL) {
err(EXIT_FAILURE, "failed to allocate %u bytes for "
"request read buffer", req.io_rlen);
}
if (output != NULL) {
ofd = open(output, O_RDWR | O_TRUNC | O_CREAT);
if (ofd < 0) {
err(EXIT_FAILURE, "failed to open ouput "
"file (-o) %s", output);
}
}
} else if (output != NULL) {
errx(EXIT_FAILURE, "cannot specify output file -o when no "
"data is being read");
}
i2cadm_io_init(&req, mode);
if (req.io_i2c != NULL) {
if (!i2c_io_req_exec(req.io_i2c)) {
i2cadm_fatal("failed to execute I2C request");
}
} else {
if (!smbus_io_req_exec(req.io_smbus)) {
i2cadm_fatal("failed to execute SMBus request");
}
}
if (ofd != -1) {
i2cadm_io_write(&req, mode, ofd);
(void) close(ofd);
} else {
i2cadm_io_print(&req, mode);
}
if (req.io_i2c != NULL) {
i2c_io_req_fini(req.io_i2c);
}
if (req.io_smbus != NULL) {
smbus_io_req_fini(req.io_smbus);
}
free(req.io_wdata);
free(req.io_rdata);
i2c_port_fini(port);
return (EXIT_SUCCESS);
}