10f66f451Sopenharmony_ci/* i2ctools.c - i2c tools 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2018 The Android Open Source Project 40f66f451Sopenharmony_ci * 50f66f451Sopenharmony_ci * https://www.kernel.org/doc/Documentation/i2c/ 60f66f451Sopenharmony_ci * 70f66f451Sopenharmony_ci * Note: -y must have the same value in each toy for `confirm`. 80f66f451Sopenharmony_ci * 90f66f451Sopenharmony_ci * TODO: i2cdetect -q/-r and the "auto" mode? 100f66f451Sopenharmony_ci * TODO: i2cdump non-byte modes, -r FIRST-LAST? 110f66f451Sopenharmony_ci * TODO: i2cget non-byte modes? default to current read address? 120f66f451Sopenharmony_ci * TODO: i2cset -r? -m MASK? c/s modes, p mode modifier? 130f66f451Sopenharmony_ci 140f66f451Sopenharmony_ciUSE_I2CDETECT(NEWTOY(i2cdetect, ">3aFlqry[!qr]", TOYFLAG_USR|TOYFLAG_BIN)) 150f66f451Sopenharmony_ciUSE_I2CDUMP(NEWTOY(i2cdump, "<2>2fy", TOYFLAG_USR|TOYFLAG_BIN)) 160f66f451Sopenharmony_ciUSE_I2CGET(NEWTOY(i2cget, "<3>3fy", TOYFLAG_USR|TOYFLAG_BIN)) 170f66f451Sopenharmony_ciUSE_I2CSET(NEWTOY(i2cset, "<4fy", TOYFLAG_USR|TOYFLAG_BIN)) 180f66f451Sopenharmony_ci 190f66f451Sopenharmony_ciconfig I2CDETECT 200f66f451Sopenharmony_ci bool "i2cdetect" 210f66f451Sopenharmony_ci default y 220f66f451Sopenharmony_ci help 230f66f451Sopenharmony_ci usage: i2cdetect [-aqry] BUS [FIRST LAST] 240f66f451Sopenharmony_ci usage: i2cdetect -F BUS 250f66f451Sopenharmony_ci usage: i2cdetect -l 260f66f451Sopenharmony_ci 270f66f451Sopenharmony_ci Detect i2c devices. 280f66f451Sopenharmony_ci 290f66f451Sopenharmony_ci -a All addresses (0x00-0x7f rather than 0x03-0x77 or FIRST-LAST) 300f66f451Sopenharmony_ci -F Show functionality 310f66f451Sopenharmony_ci -l List available buses 320f66f451Sopenharmony_ci -q Probe with SMBus Quick Write (default) 330f66f451Sopenharmony_ci -r Probe with SMBus Read Byte 340f66f451Sopenharmony_ci -y Answer "yes" to confirmation prompts (for script use) 350f66f451Sopenharmony_ci 360f66f451Sopenharmony_ciconfig I2CDUMP 370f66f451Sopenharmony_ci bool "i2cdump" 380f66f451Sopenharmony_ci default y 390f66f451Sopenharmony_ci help 400f66f451Sopenharmony_ci usage: i2cdump [-fy] BUS CHIP 410f66f451Sopenharmony_ci 420f66f451Sopenharmony_ci Dump i2c registers. 430f66f451Sopenharmony_ci 440f66f451Sopenharmony_ci -f Force access to busy devices 450f66f451Sopenharmony_ci -y Answer "yes" to confirmation prompts (for script use) 460f66f451Sopenharmony_ci 470f66f451Sopenharmony_ciconfig I2CGET 480f66f451Sopenharmony_ci bool "i2cget" 490f66f451Sopenharmony_ci default y 500f66f451Sopenharmony_ci help 510f66f451Sopenharmony_ci usage: i2cget [-fy] BUS CHIP ADDR 520f66f451Sopenharmony_ci 530f66f451Sopenharmony_ci Read an i2c register. 540f66f451Sopenharmony_ci 550f66f451Sopenharmony_ci -f Force access to busy devices 560f66f451Sopenharmony_ci -y Answer "yes" to confirmation prompts (for script use) 570f66f451Sopenharmony_ci 580f66f451Sopenharmony_ciconfig I2CSET 590f66f451Sopenharmony_ci bool "i2cset" 600f66f451Sopenharmony_ci default y 610f66f451Sopenharmony_ci help 620f66f451Sopenharmony_ci usage: i2cset [-fy] BUS CHIP ADDR VALUE... MODE 630f66f451Sopenharmony_ci 640f66f451Sopenharmony_ci Write an i2c register. MODE is b for byte, w for 16-bit word, i for I2C block. 650f66f451Sopenharmony_ci 660f66f451Sopenharmony_ci -f Force access to busy devices 670f66f451Sopenharmony_ci -y Answer "yes" to confirmation prompts (for script use) 680f66f451Sopenharmony_ci*/ 690f66f451Sopenharmony_ci 700f66f451Sopenharmony_ci#define FOR_i2cdetect 710f66f451Sopenharmony_ci#define TT this.i2ctools 720f66f451Sopenharmony_ci#include "toys.h" 730f66f451Sopenharmony_ci 740f66f451Sopenharmony_ci#include <linux/i2c.h> 750f66f451Sopenharmony_ci#include <linux/i2c-dev.h> 760f66f451Sopenharmony_ci 770f66f451Sopenharmony_ciprintf_format static void confirm(const char *fmt, ...) 780f66f451Sopenharmony_ci{ 790f66f451Sopenharmony_ci va_list va; 800f66f451Sopenharmony_ci 810f66f451Sopenharmony_ci if (FLAG(y)) return; 820f66f451Sopenharmony_ci 830f66f451Sopenharmony_ci va_start(va, fmt); 840f66f451Sopenharmony_ci vfprintf(stderr, fmt, va); 850f66f451Sopenharmony_ci va_end(va); 860f66f451Sopenharmony_ci if (!yesno(1)) error_exit("Exiting"); 870f66f451Sopenharmony_ci} 880f66f451Sopenharmony_ci 890f66f451Sopenharmony_cistatic int i2c_open(int bus, int slave, int chip) 900f66f451Sopenharmony_ci{ 910f66f451Sopenharmony_ci int fd; 920f66f451Sopenharmony_ci 930f66f451Sopenharmony_ci snprintf(toybuf, sizeof(toybuf), "/dev/i2c-%d", bus); 940f66f451Sopenharmony_ci fd = xopen(toybuf, O_RDONLY); 950f66f451Sopenharmony_ci if (slave) xioctl(fd, slave, (void *)(long)chip); 960f66f451Sopenharmony_ci return fd; 970f66f451Sopenharmony_ci} 980f66f451Sopenharmony_ci 990f66f451Sopenharmony_cistatic unsigned long i2c_get_funcs(int bus) 1000f66f451Sopenharmony_ci{ 1010f66f451Sopenharmony_ci int fd = i2c_open(bus, 0, 0); 1020f66f451Sopenharmony_ci unsigned long result; 1030f66f451Sopenharmony_ci 1040f66f451Sopenharmony_ci xioctl(fd, I2C_FUNCS, &result); 1050f66f451Sopenharmony_ci close(fd); 1060f66f451Sopenharmony_ci return result; 1070f66f451Sopenharmony_ci} 1080f66f451Sopenharmony_ci 1090f66f451Sopenharmony_cistatic int i2c_read_byte(int fd, int addr, int *byte) 1100f66f451Sopenharmony_ci{ 1110f66f451Sopenharmony_ci union i2c_smbus_data data; 1120f66f451Sopenharmony_ci struct i2c_smbus_ioctl_data ioctl_data = { .read_write = I2C_SMBUS_READ, 1130f66f451Sopenharmony_ci .size = I2C_SMBUS_BYTE_DATA, .command = addr, .data = &data }; 1140f66f451Sopenharmony_ci 1150f66f451Sopenharmony_ci memset(&data, 0, sizeof(data)); 1160f66f451Sopenharmony_ci if (ioctl(fd, I2C_SMBUS, &ioctl_data)==-1) return -1; 1170f66f451Sopenharmony_ci *byte = data.byte; 1180f66f451Sopenharmony_ci return 0; 1190f66f451Sopenharmony_ci} 1200f66f451Sopenharmony_ci 1210f66f451Sopenharmony_cistatic int i2c_quick_write(int fd, int addr) 1220f66f451Sopenharmony_ci{ 1230f66f451Sopenharmony_ci struct i2c_smbus_ioctl_data ioctl_data = { .read_write = I2C_SMBUS_QUICK, 1240f66f451Sopenharmony_ci .size = 0, .command = addr }; 1250f66f451Sopenharmony_ci 1260f66f451Sopenharmony_ci return ioctl(fd, I2C_SMBUS, &ioctl_data); 1270f66f451Sopenharmony_ci} 1280f66f451Sopenharmony_ci 1290f66f451Sopenharmony_cistatic void i2cdetect_dash_F(int bus) 1300f66f451Sopenharmony_ci{ 1310f66f451Sopenharmony_ci struct { int mask; const char *name; } funcs[] = { 1320f66f451Sopenharmony_ci {I2C_FUNC_I2C, "I2C"}, 1330f66f451Sopenharmony_ci {I2C_FUNC_SMBUS_QUICK, "SMBus Quick Command"}, 1340f66f451Sopenharmony_ci {I2C_FUNC_SMBUS_WRITE_BYTE, "SMBus Send Byte"}, 1350f66f451Sopenharmony_ci {I2C_FUNC_SMBUS_READ_BYTE, "SMBus Receive Byte"}, 1360f66f451Sopenharmony_ci {I2C_FUNC_SMBUS_WRITE_BYTE_DATA, "SMBus Write Byte"}, 1370f66f451Sopenharmony_ci {I2C_FUNC_SMBUS_READ_BYTE_DATA, "SMBus Read Byte"}, 1380f66f451Sopenharmony_ci {I2C_FUNC_SMBUS_WRITE_WORD_DATA, "SMBus Write Word"}, 1390f66f451Sopenharmony_ci {I2C_FUNC_SMBUS_READ_WORD_DATA, "SMBus Read Word"}, 1400f66f451Sopenharmony_ci {I2C_FUNC_SMBUS_PROC_CALL, "SMBus Process Call"}, 1410f66f451Sopenharmony_ci {I2C_FUNC_SMBUS_WRITE_BLOCK_DATA, "SMBus Write Block"}, 1420f66f451Sopenharmony_ci {I2C_FUNC_SMBUS_READ_BLOCK_DATA, "SMBus Read Block"}, 1430f66f451Sopenharmony_ci {I2C_FUNC_SMBUS_BLOCK_PROC_CALL, "SMBus Block Process Call"}, 1440f66f451Sopenharmony_ci {I2C_FUNC_SMBUS_PEC, "SMBus PEC"}, 1450f66f451Sopenharmony_ci {I2C_FUNC_SMBUS_WRITE_I2C_BLOCK, "I2C Write Block"}, 1460f66f451Sopenharmony_ci {I2C_FUNC_SMBUS_READ_I2C_BLOCK, "I2C Read Block"}, 1470f66f451Sopenharmony_ci }; 1480f66f451Sopenharmony_ci unsigned long sup = i2c_get_funcs(bus); 1490f66f451Sopenharmony_ci int i; 1500f66f451Sopenharmony_ci 1510f66f451Sopenharmony_ci printf("Functionalities implemented by %s:\n", toybuf); 1520f66f451Sopenharmony_ci for (i = 0; i < ARRAY_LEN(funcs); ++i) 1530f66f451Sopenharmony_ci printf("%-32s %s\n", funcs[i].name, (sup & funcs[i].mask) ? "yes" : "no"); 1540f66f451Sopenharmony_ci} 1550f66f451Sopenharmony_ci 1560f66f451Sopenharmony_cistatic int i2cdetect_dash_l(struct dirtree *node) 1570f66f451Sopenharmony_ci{ 1580f66f451Sopenharmony_ci int suffix_len = strlen("/name"); 1590f66f451Sopenharmony_ci int bus; 1600f66f451Sopenharmony_ci char *fname, *p; 1610f66f451Sopenharmony_ci unsigned long funcs; 1620f66f451Sopenharmony_ci 1630f66f451Sopenharmony_ci if (!node->parent) return DIRTREE_RECURSE; // Skip the directory itself. 1640f66f451Sopenharmony_ci 1650f66f451Sopenharmony_ci if (sscanf(node->name, "i2c-%d", &bus)!=1) return 0; 1660f66f451Sopenharmony_ci funcs = i2c_get_funcs(bus); 1670f66f451Sopenharmony_ci 1680f66f451Sopenharmony_ci fname = dirtree_path(node, &suffix_len); 1690f66f451Sopenharmony_ci strcat(fname, "/name"); 1700f66f451Sopenharmony_ci xreadfile(fname, toybuf, sizeof(toybuf)); 1710f66f451Sopenharmony_ci free(fname); 1720f66f451Sopenharmony_ci if ((p = strchr(toybuf, '\n'))) *p = 0; 1730f66f451Sopenharmony_ci 1740f66f451Sopenharmony_ci // "i2c-1 i2c Synopsys DesignWare I2C adapter I2C adapter" 1750f66f451Sopenharmony_ci printf("%s\t%-10s\t%-32s\t%s\n", node->name, 1760f66f451Sopenharmony_ci (funcs & I2C_FUNC_I2C) ? "i2c" : "?", toybuf, 1770f66f451Sopenharmony_ci (funcs & I2C_FUNC_I2C) ? "I2C Adapter" : "?"); 1780f66f451Sopenharmony_ci 1790f66f451Sopenharmony_ci return 0; 1800f66f451Sopenharmony_ci} 1810f66f451Sopenharmony_ci 1820f66f451Sopenharmony_civoid i2cdetect_main(void) 1830f66f451Sopenharmony_ci{ 1840f66f451Sopenharmony_ci if (FLAG(l)) { 1850f66f451Sopenharmony_ci if (toys.optc) error_exit("-l doesn't take arguments"); 1860f66f451Sopenharmony_ci dirtree_flagread("/sys/class/i2c-dev", DIRTREE_SHUTUP, i2cdetect_dash_l); 1870f66f451Sopenharmony_ci } else if (FLAG(F)) { 1880f66f451Sopenharmony_ci if (toys.optc != 1) error_exit("-F BUS"); 1890f66f451Sopenharmony_ci i2cdetect_dash_F(atolx_range(*toys.optargs, 0, INT_MAX)); 1900f66f451Sopenharmony_ci } else { 1910f66f451Sopenharmony_ci int bus, first = 0x03, last = 0x77, fd, row, addr, byte; 1920f66f451Sopenharmony_ci 1930f66f451Sopenharmony_ci if (FLAG(a)) { 1940f66f451Sopenharmony_ci first = 0x00; 1950f66f451Sopenharmony_ci last = 0x7f; 1960f66f451Sopenharmony_ci } 1970f66f451Sopenharmony_ci 1980f66f451Sopenharmony_ci if (toys.optc!=1 && toys.optc!=3) help_exit("Needs 1 or 3 arguments"); 1990f66f451Sopenharmony_ci bus = atolx_range(*toys.optargs, 0, INT_MAX); 2000f66f451Sopenharmony_ci if (toys.optc==3) { 2010f66f451Sopenharmony_ci first = atolx_range(toys.optargs[1], 0, 0x7f); 2020f66f451Sopenharmony_ci last = atolx_range(toys.optargs[2], 0, 0x7f); 2030f66f451Sopenharmony_ci if (first > last) error_exit("first > last"); 2040f66f451Sopenharmony_ci } 2050f66f451Sopenharmony_ci 2060f66f451Sopenharmony_ci confirm("Probe chips 0x%02x-0x%02x on bus %d?", first, last, bus); 2070f66f451Sopenharmony_ci 2080f66f451Sopenharmony_ci fd = i2c_open(bus, 0, 0); 2090f66f451Sopenharmony_ci printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\n"); 2100f66f451Sopenharmony_ci for (row = 0; row <= 0x70; row += 16) { 2110f66f451Sopenharmony_ci xprintf("%02x:", row & 0xf0); 2120f66f451Sopenharmony_ci for (addr = row; addr<row+16; ++addr) { 2130f66f451Sopenharmony_ci if (addr<first || addr>last) printf(" "); 2140f66f451Sopenharmony_ci else { 2150f66f451Sopenharmony_ci if (ioctl(fd, I2C_SLAVE, addr) == -1) { 2160f66f451Sopenharmony_ci if (errno == EBUSY) { 2170f66f451Sopenharmony_ci xprintf(" UU"); 2180f66f451Sopenharmony_ci continue; 2190f66f451Sopenharmony_ci } 2200f66f451Sopenharmony_ci perror_exit("ioctl(I2C_SLAVE)"); 2210f66f451Sopenharmony_ci } 2220f66f451Sopenharmony_ci if ((FLAG(r) ? i2c_read_byte(fd, addr, &byte) 2230f66f451Sopenharmony_ci : i2c_quick_write(fd, addr)) == -1) xprintf(" --"); 2240f66f451Sopenharmony_ci else xprintf(" %02x", addr); 2250f66f451Sopenharmony_ci } 2260f66f451Sopenharmony_ci } 2270f66f451Sopenharmony_ci putchar('\n'); 2280f66f451Sopenharmony_ci } 2290f66f451Sopenharmony_ci close(fd); 2300f66f451Sopenharmony_ci } 2310f66f451Sopenharmony_ci} 2320f66f451Sopenharmony_ci 2330f66f451Sopenharmony_ci#define CLEANUP_i2cdetect 2340f66f451Sopenharmony_ci#define FOR_i2cdump 2350f66f451Sopenharmony_ci#include "generated/flags.h" 2360f66f451Sopenharmony_ci 2370f66f451Sopenharmony_civoid i2cdump_main(void) 2380f66f451Sopenharmony_ci{ 2390f66f451Sopenharmony_ci int bus = atolx_range(toys.optargs[0], 0, INT_MAX); 2400f66f451Sopenharmony_ci int chip = atolx_range(toys.optargs[1], 0, 0x7f); 2410f66f451Sopenharmony_ci int fd, row, addr, byte; 2420f66f451Sopenharmony_ci 2430f66f451Sopenharmony_ci confirm("Dump chip 0x%02x on bus %d?", chip, bus); 2440f66f451Sopenharmony_ci 2450f66f451Sopenharmony_ci fd = i2c_open(bus, FLAG(f) ? I2C_SLAVE_FORCE : I2C_SLAVE, chip); 2460f66f451Sopenharmony_ci printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef\n"); 2470f66f451Sopenharmony_ci for (row = 0; row<=0xf0; row += 16) { 2480f66f451Sopenharmony_ci xprintf("%02x:", row & 0xf0); 2490f66f451Sopenharmony_ci for (addr = row; addr<row+16; ++addr) { 2500f66f451Sopenharmony_ci if (!i2c_read_byte(fd, addr, &byte)) printf(" %02x", byte); 2510f66f451Sopenharmony_ci else { 2520f66f451Sopenharmony_ci printf(" XX"); 2530f66f451Sopenharmony_ci byte = 'X'; 2540f66f451Sopenharmony_ci } 2550f66f451Sopenharmony_ci toybuf[addr-row] = isprint(byte) ? byte : (byte ? '?' : '.'); 2560f66f451Sopenharmony_ci } 2570f66f451Sopenharmony_ci printf(" %16.16s\n", toybuf); 2580f66f451Sopenharmony_ci } 2590f66f451Sopenharmony_ci close(fd); 2600f66f451Sopenharmony_ci} 2610f66f451Sopenharmony_ci 2620f66f451Sopenharmony_ci#define CLEANUP_i2cdump 2630f66f451Sopenharmony_ci#define FOR_i2cget 2640f66f451Sopenharmony_ci#include "generated/flags.h" 2650f66f451Sopenharmony_ci 2660f66f451Sopenharmony_civoid i2cget_main(void) 2670f66f451Sopenharmony_ci{ 2680f66f451Sopenharmony_ci int bus = atolx_range(toys.optargs[0], 0, INT_MAX); 2690f66f451Sopenharmony_ci int chip = atolx_range(toys.optargs[1], 0, 0x7f); 2700f66f451Sopenharmony_ci int addr = atolx_range(toys.optargs[2], 0, 0xff); 2710f66f451Sopenharmony_ci int fd, byte; 2720f66f451Sopenharmony_ci 2730f66f451Sopenharmony_ci confirm("Read register 0x%02x from chip 0x%02x on bus %d?", addr, chip, bus); 2740f66f451Sopenharmony_ci 2750f66f451Sopenharmony_ci fd = i2c_open(bus, FLAG(f) ? I2C_SLAVE_FORCE : I2C_SLAVE, chip); 2760f66f451Sopenharmony_ci if (i2c_read_byte(fd, addr, &byte)==-1) perror_exit("i2c_read_byte"); 2770f66f451Sopenharmony_ci printf("0x%02x\n", byte); 2780f66f451Sopenharmony_ci close(fd); 2790f66f451Sopenharmony_ci} 2800f66f451Sopenharmony_ci 2810f66f451Sopenharmony_ci#define CLEANUP_i2cget 2820f66f451Sopenharmony_ci#define FOR_i2cset 2830f66f451Sopenharmony_ci#include "generated/flags.h" 2840f66f451Sopenharmony_ci 2850f66f451Sopenharmony_civoid i2cset_main(void) 2860f66f451Sopenharmony_ci{ 2870f66f451Sopenharmony_ci int bus = atolx_range(toys.optargs[0], 0, INT_MAX); 2880f66f451Sopenharmony_ci int chip = atolx_range(toys.optargs[1], 0, 0x7f); 2890f66f451Sopenharmony_ci int addr = atolx_range(toys.optargs[2], 0, 0xff); 2900f66f451Sopenharmony_ci char *mode = toys.optargs[toys.optc-1]; 2910f66f451Sopenharmony_ci int fd, i; 2920f66f451Sopenharmony_ci struct i2c_smbus_ioctl_data ioctl_data; 2930f66f451Sopenharmony_ci union i2c_smbus_data data; 2940f66f451Sopenharmony_ci 2950f66f451Sopenharmony_ci memset(&data, 0, sizeof(data)); 2960f66f451Sopenharmony_ci if (strlen(mode)!=1) help_exit("mode too long"); 2970f66f451Sopenharmony_ci if (*mode=='b' && toys.optc==5) { 2980f66f451Sopenharmony_ci ioctl_data.size = I2C_SMBUS_BYTE_DATA; 2990f66f451Sopenharmony_ci data.byte = atolx_range(toys.optargs[3], 0, 0xff); 3000f66f451Sopenharmony_ci } else if (*mode=='w' && toys.optc==5) { 3010f66f451Sopenharmony_ci ioctl_data.size = I2C_SMBUS_WORD_DATA; 3020f66f451Sopenharmony_ci data.word = atolx_range(toys.optargs[3], 0, 0xffff); 3030f66f451Sopenharmony_ci } else if (*mode=='i' && toys.optc>=5) { 3040f66f451Sopenharmony_ci if (toys.optc-4>I2C_SMBUS_BLOCK_MAX) error_exit("too much data"); 3050f66f451Sopenharmony_ci ioctl_data.size = I2C_SMBUS_I2C_BLOCK_DATA; 3060f66f451Sopenharmony_ci for (i = 0; i<toys.optc-4; ++i) 3070f66f451Sopenharmony_ci data.block[i+1] = atolx_range(toys.optargs[3+i], 0, 0xff); 3080f66f451Sopenharmony_ci data.block[0] = toys.optc-4; 3090f66f451Sopenharmony_ci } else help_exit("syntax error"); 3100f66f451Sopenharmony_ci 3110f66f451Sopenharmony_ci confirm("Write register 0x%02x from chip 0x%02x on bus %d?", addr, chip, bus); 3120f66f451Sopenharmony_ci 3130f66f451Sopenharmony_ci fd = i2c_open(bus, FLAG(f) ? I2C_SLAVE_FORCE : I2C_SLAVE, chip); 3140f66f451Sopenharmony_ci ioctl_data.read_write = I2C_SMBUS_WRITE; 3150f66f451Sopenharmony_ci ioctl_data.command = addr; 3160f66f451Sopenharmony_ci ioctl_data.data = &data; 3170f66f451Sopenharmony_ci xioctl(fd, I2C_SMBUS, &ioctl_data); 3180f66f451Sopenharmony_ci close(fd); 3190f66f451Sopenharmony_ci} 320