unplugged-system/external/toybox/toys/other/i2ctools.c

316 lines
8.7 KiB
C

/* i2ctools.c - i2c tools
*
* Copyright 2018 The Android Open Source Project
*
* https://www.kernel.org/doc/Documentation/i2c/
*
* Note: -y must have the same value in each toy for `confirm`.
*
* TODO: i2cdetect -q/-r and the "auto" mode?
* TODO: i2cdump non-byte modes, -r FIRST-LAST?
* TODO: i2cget non-byte modes? default to current read address?
* TODO: i2cset -r? -m MASK? c/s modes, p mode modifier?
* TODO: I2C_M_TEN bit addressing (-t, larger range in probe...)
// note: confirm() needs "y" to be in same place for all commands
USE_I2CDETECT(NEWTOY(i2cdetect, ">3aFlqry[!qr]", TOYFLAG_USR|TOYFLAG_SBIN))
USE_I2CDUMP(NEWTOY(i2cdump, "<2>2fy", TOYFLAG_USR|TOYFLAG_SBIN))
USE_I2CGET(NEWTOY(i2cget, "<2>3fy", TOYFLAG_USR|TOYFLAG_SBIN))
USE_I2CSET(NEWTOY(i2cset, "<4fy", TOYFLAG_USR|TOYFLAG_SBIN))
config I2CDETECT
bool "i2cdetect"
default y
help
usage: i2cdetect [-aqry] BUS [FIRST LAST]
usage: i2cdetect -F BUS
usage: i2cdetect -l
Detect i2c devices.
-a All addresses (0x00-0x7f rather than 0x03-0x77 or FIRST-LAST)
-F Show functionality
-l List available buses
-q Probe with SMBus Quick Write (default)
-r Probe with SMBus Read Byte
-y Skip confirmation prompts (yes to all)
config I2CDUMP
bool "i2cdump"
default y
help
usage: i2cdump [-fy] BUS CHIP
Dump i2c registers.
-f Force access to busy devices
-y Skip confirmation prompts (yes to all)
config I2CGET
bool "i2cget"
default y
help
usage: i2cget [-fy] BUS CHIP [ADDR]
Read an i2c register.
-f Force access to busy devices
-y Skip confirmation prompts (yes to all)
config I2CSET
bool "i2cset"
default y
help
usage: i2cset [-fy] BUS CHIP ADDR VALUE... MODE
Write an i2c register. MODE is b for byte, w for 16-bit word, i for I2C block.
-f Force access to busy devices
-y Skip confirmation prompts (yes to all)
*/
#define FOR_i2cdetect
#define FORCE_FLAGS
#define TT this.i2ctools
#include "toys.h"
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
printf_format static void confirm(const char *fmt, ...)
{
va_list va;
if (FLAG(y)) return;
va_start(va, fmt);
vfprintf(stderr, fmt, va);
va_end(va);
if (!yesno(1)) error_exit("Exiting");
}
static int i2c_open(int bus, int slave, long chip)
{
int fd;
sprintf(toybuf, "/dev/i2c-%d", bus);
fd = xopen(toybuf, O_RDONLY);
if (slave) xioctl(fd, slave, (void *)chip);
return fd;
}
static unsigned long i2c_get_funcs(int bus)
{
int fd = i2c_open(bus, 0, 0);
unsigned long result;
xioctl(fd, I2C_FUNCS, &result);
close(fd);
return result;
}
static int i2c_read_byte(int fd, int addr, char *byte)
{
union i2c_smbus_data data;
struct i2c_smbus_ioctl_data ioctl_data = { .read_write = I2C_SMBUS_READ,
.size = I2C_SMBUS_BYTE_DATA, .command = addr, .data = &data };
memset(&data, 0, sizeof(data));
if (ioctl(fd, I2C_SMBUS, &ioctl_data)==-1) return -1;
*byte = data.byte;
return 0;
}
static int i2c_quick_write(int fd, int addr)
{
struct i2c_smbus_ioctl_data ioctl_data = { .read_write = I2C_SMBUS_QUICK,
.command = addr };
return ioctl(fd, I2C_SMBUS, &ioctl_data);
}
static void i2cdetect_dash_F(int bus)
{
char *funcs[] = {
"I2C", "10 bit", 0, "SMBus PEC", 0, 0, "SMBus Block Process Call",
"SMBus Quick Command", "SMBus Receive Byte", "SMBus Send Byte",
"SMBus Read Byte", "SMBus Write Byte", "SMBus Read Word",
"SMBus Write Word", "SMBus Process Call", "SMBus Read Block",
"SMBus Write Block", "I2C Read Block", "I2C Write Block" };
unsigned sup = i2c_get_funcs(bus), i;
printf("Functionalities implemented by %s:\n", toybuf);
for (i = 0; i<ARRAY_LEN(funcs); i++)
if (funcs[i]) printf("%-32s %s\n", funcs[i], (sup&(1<<i)) ? "yes" : "no");
}
static int i2cdetect_dash_l(struct dirtree *node)
{
int suffix_len = strlen("/name");
int bus;
char *fname, *p;
unsigned long funcs;
if (!node->parent) return DIRTREE_RECURSE; // Skip the directory itself.
if (sscanf(node->name, "i2c-%d", &bus)!=1) return 0;
funcs = i2c_get_funcs(bus);
fname = dirtree_path(node, &suffix_len);
strcat(fname, "/name");
xreadfile(fname, toybuf, sizeof(toybuf));
free(fname);
if ((p = strchr(toybuf, '\n'))) *p = 0;
// "i2c-1 i2c Synopsys DesignWare I2C adapter I2C adapter"
printf("%s\t%-10s\t%-32s\t%s\n", node->name,
(funcs & I2C_FUNC_I2C) ? "i2c" : "?", toybuf,
(funcs & I2C_FUNC_I2C) ? "I2C Adapter" : "?");
return 0;
}
void i2cdetect_main(void)
{
if (FLAG(l)) {
if (toys.optc) error_exit("-l doesn't take arguments");
dirtree_flagread("/sys/class/i2c-dev", DIRTREE_SHUTUP, i2cdetect_dash_l);
} else if (FLAG(F)) {
if (toys.optc != 1) error_exit("-F BUS");
i2cdetect_dash_F(atolx_range(*toys.optargs, 0, 0x3f));
} else {
int bus, first = 0x03, last = 0x77, fd, row, addr;
char byte;
if (FLAG(a)) {
first = 0x00;
last = 0x7f;
}
if (toys.optc!=1 && toys.optc!=3) help_exit("Needs 1 or 3 arguments");
bus = atolx_range(*toys.optargs, 0, 0x3f);
if (toys.optc==3) {
first = atolx_range(toys.optargs[1], 0, 0x7f);
last = atolx_range(toys.optargs[2], 0, 0x7f);
if (first > last) error_exit("first > last");
}
confirm("Probe chips 0x%02x-0x%02x on bus %d?", first, last, bus);
fd = i2c_open(bus, 0, 0);
printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\n");
for (row = 0; row < 0x80; row += 16) {
xprintf("%02x:", row & 0xf0);
for (addr = row; addr<row+16; ++addr) {
if (addr<first || addr>last) {
printf(" ");
continue;
}
if (ioctl(fd, I2C_SLAVE, addr) == -1) {
if (errno == EBUSY) {
xprintf(" UU");
continue;
}
perror_exit("ioctl(I2C_SLAVE)");
}
if ((FLAG(r) ? i2c_read_byte(fd, addr, &byte)
: i2c_quick_write(fd, addr)) == -1) xprintf(" --");
else xprintf(" %02x", addr);
}
putchar('\n');
}
close(fd);
}
}
#define FOR_i2cdump
#include "generated/flags.h"
void i2cdump_main(void)
{
int fd, row, addr, bus = atolx_range(toys.optargs[0], 0, 0x3f),
chip = atolx_range(toys.optargs[1], 0, 0x7f);
char byte;
confirm("Dump chip 0x%02x on bus %d?", chip, bus);
fd = i2c_open(bus, FLAG(f) ? I2C_SLAVE_FORCE : I2C_SLAVE, chip);
printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef\n");
for (row = 0; row<0x100; row += 16) {
xprintf("%02x:", row & 0xf0);
for (addr = row; addr<row+16; ++addr) {
if (!i2c_read_byte(fd, addr, &byte)) printf(" %02x", byte);
else {
printf(" XX");
byte = 'X';
}
toybuf[addr-row] = isprint(byte) ? byte : byte ? '?' : '.';
}
printf(" %16.16s\n", toybuf);
}
close(fd);
}
#define FOR_i2cget
#include "generated/flags.h"
void i2cget_main(void)
{
int fd, bus = atolx_range(toys.optargs[0], 0, 0x3f),
chip = atolx_range(toys.optargs[1], 0, 0x7f),
addr = (toys.optc == 3) ? atolx_range(toys.optargs[2], 0, 0xff) : -1;
char byte;
confirm("Read register 0x%02x from chip 0x%02x on bus %d?", addr, chip, bus);
fd = i2c_open(bus, FLAG(f) ? I2C_SLAVE_FORCE : I2C_SLAVE, chip);
if (toys.optc == 3) {
if (i2c_read_byte(fd, addr, &byte)==-1) perror_exit("i2c_read_byte");
} else if (read(fd, &byte, 1) != 1) perror_exit("i2c_read");
printf("0x%02x\n", byte);
close(fd);
}
#define FOR_i2cset
#include "generated/flags.h"
void i2cset_main(void)
{
int fd, i, bus = atolx_range(toys.optargs[0], 0, 0x3f),
chip = atolx_range(toys.optargs[1], 0, 0x7f),
addr = atolx_range(toys.optargs[2], 0, 0xff);
char *mode = toys.optargs[toys.optc-1];
struct i2c_smbus_ioctl_data ioctl_data;
union i2c_smbus_data data;
memset(&data, 0, sizeof(data));
if (strlen(mode)!=1) help_exit("mode too long");
if (*mode=='b' && toys.optc==5) {
ioctl_data.size = I2C_SMBUS_BYTE_DATA;
data.byte = atolx_range(toys.optargs[3], 0, 0xff);
} else if (*mode=='w' && toys.optc==5) {
ioctl_data.size = I2C_SMBUS_WORD_DATA;
data.word = atolx_range(toys.optargs[3], 0, 0xffff);
} else if (*mode=='i' && toys.optc>=5) {
if (toys.optc-4>I2C_SMBUS_BLOCK_MAX) error_exit("too much data");
ioctl_data.size = I2C_SMBUS_I2C_BLOCK_DATA;
data.block[0] = toys.optc-4;
for (i = 0; i<toys.optc-4; i++)
data.block[i+1] = atolx_range(toys.optargs[3+i], 0, 0xff);
} else help_exit("syntax error");
confirm("Write register 0x%02x from chip 0x%02x on bus %d?", addr, chip, bus);
// We open the device read-only and the write command works?
fd = i2c_open(bus, FLAG(f) ? I2C_SLAVE_FORCE : I2C_SLAVE, chip);
ioctl_data.read_write = I2C_SMBUS_WRITE;
ioctl_data.command = addr;
ioctl_data.data = &data;
xioctl(fd, I2C_SMBUS, &ioctl_data);
close(fd);
}