hexedit: new applet
[oweals/busybox.git] / miscutils / i2c_tools.c
index 292ff88ddb61a8e5614d9499b80609842c6b960a..fc392d9dc7c205c7e176e806d0002f381afcba22 100644 (file)
@@ -8,40 +8,40 @@
  *
  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
-
 //config:config I2CGET
-//config:      bool "i2cget"
+//config:      bool "i2cget (5.6 kb)"
 //config:      default y
 //config:      select PLATFORM_LINUX
 //config:      help
-//config:        Read from I2C/SMBus chip registers.
+//config:      Read from I2C/SMBus chip registers.
 //config:
 //config:config I2CSET
-//config:      bool "i2cset"
+//config:      bool "i2cset (6.9 kb)"
 //config:      default y
 //config:      select PLATFORM_LINUX
 //config:      help
-//config:        Set I2C registers.
+//config:      Set I2C registers.
 //config:
 //config:config I2CDUMP
-//config:      bool "i2cdump"
+//config:      bool "i2cdump (7.2 kb)"
 //config:      default y
 //config:      select PLATFORM_LINUX
 //config:      help
-//config:        Examine I2C registers.
+//config:      Examine I2C registers.
 //config:
 //config:config I2CDETECT
-//config:      bool "i2cdetect"
+//config:      bool "i2cdetect (7.2 kb)"
 //config:      default y
 //config:      select PLATFORM_LINUX
 //config:      help
-//config:        Detect I2C chips.
+//config:      Detect I2C chips.
 //config:
 
 //applet:IF_I2CGET(APPLET(i2cget, BB_DIR_USR_SBIN, BB_SUID_DROP))
 //applet:IF_I2CSET(APPLET(i2cset, BB_DIR_USR_SBIN, BB_SUID_DROP))
 //applet:IF_I2CDUMP(APPLET(i2cdump, BB_DIR_USR_SBIN, BB_SUID_DROP))
 //applet:IF_I2CDETECT(APPLET(i2cdetect, BB_DIR_USR_SBIN, BB_SUID_DROP))
+/* not NOEXEC: if hw operation stalls, use less memory in "hung" process */
 
 //kbuild:lib-$(CONFIG_I2CGET) += i2c_tools.o
 //kbuild:lib-$(CONFIG_I2CSET) += i2c_tools.o
@@ -62,7 +62,6 @@
 #include "libbb.h"
 
 #include <linux/i2c.h>
-#include <linux/i2c-dev.h>
 
 #define I2CDUMP_NUM_REGS               256
 
 #define I2CDETECT_MODE_QUICK           1
 #define I2CDETECT_MODE_READ            2
 
+/* linux/i2c-dev.h from i2c-tools overwrites the one from linux uapi
+ * and defines symbols already defined by linux/i2c.h.
+ * Also, it defines a bunch of static inlines which we would rather NOT
+ * inline. What a mess.
+ * We need only these definitions from linux/i2c-dev.h:
+ */
+#define I2C_SLAVE                      0x0703
+#define I2C_SLAVE_FORCE                        0x0706
+#define I2C_FUNCS                      0x0705
+#define I2C_PEC                                0x0708
+#define I2C_SMBUS                      0x0720
+struct i2c_smbus_ioctl_data {
+       __u8 read_write;
+       __u8 command;
+       __u32 size;
+       union i2c_smbus_data *data;
+};
+/* end linux/i2c-dev.h */
+
 /*
  * This is needed for ioctl_or_perror_and_die() since it only accepts pointers.
  */
@@ -358,7 +376,7 @@ static void check_read_funcs(int fd, int mode, int data_addr, int pec)
                break;
 #endif /* ENABLE_I2CDUMP */
        default:
-               bb_error_msg_and_die("Programmer goofed!");
+               bb_error_msg_and_die("internal error");
        }
        check_funcs_test_end(funcs, pec, err);
 }
@@ -436,31 +454,30 @@ static void confirm_action(int bus_addr, int mode, int data_addr, int pec)
 
 #if ENABLE_I2CGET
 //usage:#define i2cget_trivial_usage
-//usage:       "[-f] [-y] BUS CHIP-ADDRESS [DATA-ADDRESS [MODE]]"
+//usage:       "[-fy] BUS CHIP-ADDRESS [DATA-ADDRESS [MODE]]"
 //usage:#define i2cget_full_usage "\n\n"
-//usage:       "Read from I2C/SMBus chip registers\n"
-//usage:     "\n       I2CBUS  i2c bus number"
-//usage:     "\n       ADDRESS 0x03 - 0x77"
+//usage:       "Read from I2C/SMBus chip registers"
+//usage:     "\n"
+//usage:     "\n       I2CBUS  I2C bus number"
+//usage:     "\n       ADDRESS 0x03-0x77"
 //usage:     "\nMODE is:"
-//usage:     "\n       b       read byte data (default)"
-//usage:     "\n       w       read word data"
-//usage:     "\n       c       write byte/read byte"
+//usage:     "\n       b       Read byte data (default)"
+//usage:     "\n       w       Read word data"
+//usage:     "\n       c       Write byte/read byte"
 //usage:     "\n       Append p for SMBus PEC"
 //usage:     "\n"
-//usage:     "\n       -f      force access"
-//usage:     "\n       -y      disable interactive mode"
+//usage:     "\n       -f      Force access"
+//usage:     "\n       -y      Disable interactive mode"
 int i2cget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int i2cget_main(int argc UNUSED_PARAM, char **argv)
 {
        const unsigned opt_f = (1 << 0), opt_y = (1 << 1);
-       const char *const optstr = "fy";
 
        int bus_num, bus_addr, data_addr = -1, status;
        int mode = I2C_SMBUS_BYTE, pec = 0, fd;
        unsigned opts;
 
-        opt_complementary = "-2:?4"; /* from 2 to 4 args */
-       opts = getopt32(argv, optstr);
+       opts = getopt32(argv, "^" "fy" "\0" "-2:?4"/*from 2 to 4 args*/);
        argv += optind;
 
        bus_num = i2c_bus_lookup(argv[0]);
@@ -520,29 +537,29 @@ int i2cget_main(int argc UNUSED_PARAM, char **argv)
 
 #if ENABLE_I2CSET
 //usage:#define i2cset_trivial_usage
-//usage:       "[-f] [-y] [-m MASK] BUS CHIP-ADDR DATA-ADDR [VALUE] ... [MODE]"
+//usage:       "[-fy] [-m MASK] BUS CHIP-ADDRESS DATA-ADDRESS [VALUE] ... [MODE]"
 //usage:#define i2cset_full_usage "\n\n"
-//usage:       "Set I2C registers\n"
-//usage:     "\n       I2CBUS  i2c bus number"
-//usage:     "\n       ADDRESS 0x03 - 0x77"
+//usage:       "Set I2C registers"
+//usage:     "\n"
+//usage:     "\n       I2CBUS  I2C bus number"
+//usage:     "\n       ADDRESS 0x03-0x77"
 //usage:     "\nMODE is:"
-//usage:     "\n       c       byte, no value"
-//usage:     "\n       b       byte data (default)"
-//usage:     "\n       w       word data"
+//usage:     "\n       c       Byte, no value"
+//usage:     "\n       b       Byte data (default)"
+//usage:     "\n       w       Word data"
 //usage:     "\n       i       I2C block data"
 //usage:     "\n       s       SMBus block data"
 //usage:     "\n       Append p for SMBus PEC"
 //usage:     "\n"
-//usage:     "\n       -f      force access"
-//usage:     "\n       -y      disable interactive mode"
-//usage:     "\n       -r      read back and compare the result"
-//usage:     "\n       -m MASK mask specifying which bits to write"
+//usage:     "\n       -f      Force access"
+//usage:     "\n       -y      Disable interactive mode"
+//usage:     "\n       -r      Read back and compare the result"
+//usage:     "\n       -m MASK Mask specifying which bits to write"
 int i2cset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int i2cset_main(int argc, char **argv)
 {
        const unsigned opt_f = (1 << 0), opt_y = (1 << 1),
                              opt_m = (1 << 2), opt_r = (1 << 3);
-       const char *const optstr = "fym:r";
 
        int bus_num, bus_addr, data_addr, mode = I2C_SMBUS_BYTE, pec = 0;
        int val, blen = 0, mask = 0, fd, status;
@@ -550,8 +567,7 @@ int i2cset_main(int argc, char **argv)
        char *opt_m_arg = NULL;
        unsigned opts;
 
-        opt_complementary = "-3"; /* from 3 to ? args */
-       opts = getopt32(argv, optstr, &opt_m_arg);
+       opts = getopt32(argv, "^" "fym:r" "\0" "-3"/*from 3 to ? args*/, &opt_m_arg);
        argv += optind;
        argc -= optind;
 
@@ -723,16 +739,18 @@ static int read_block_data(int buf_fd, int mode, int *block)
        uint8_t cblock[I2C_SMBUS_BLOCK_MAX + I2CDUMP_NUM_REGS];
        int res, blen = 0, tmp, i;
 
-       if (mode == I2C_SMBUS_BLOCK_DATA || mode == I2C_SMBUS_I2C_BLOCK_DATA) {
-               res = i2c_smbus_read_block_data(buf_fd, 0, cblock);
-               blen = res;
+       if (mode == I2C_SMBUS_BLOCK_DATA) {
+               blen = i2c_smbus_read_block_data(buf_fd, 0, cblock);
+               if (blen <= 0)
+                       goto fail;
        } else {
                for (res = 0; res < I2CDUMP_NUM_REGS; res += tmp) {
                        tmp = i2c_smbus_read_i2c_block_data(
                                        buf_fd, res, I2C_SMBUS_BLOCK_MAX,
                                        cblock + res);
-                       if (tmp < 0) {
-                               bb_error_msg_and_die("block read failed");
+                       if (tmp <= 0) {
+                               blen = tmp;
+                               goto fail;
                        }
                }
 
@@ -748,6 +766,9 @@ static int read_block_data(int buf_fd, int mode, int *block)
        }
 
        return blen;
+
+ fail:
+       bb_error_msg_and_die("block read failed: %d", blen);
 }
 
 /* Dump all but word data. */
@@ -876,38 +897,41 @@ static void dump_word_data(int bus_fd, unsigned first, unsigned last)
 }
 
 //usage:#define i2cdump_trivial_usage
-//usage:       "[-f] [-r FIRST-LAST] [-y] BUS ADDR [MODE]"
+//usage:       "[-fy] [-r FIRST-LAST] BUS ADDR [MODE]"
 //usage:#define i2cdump_full_usage "\n\n"
-//usage:       "Examine I2C registers\n"
-//usage:     "\n       I2CBUS  i2c bus number"
-//usage:     "\n       ADDRESS 0x03 - 0x77"
+//usage:       "Examine I2C registers"
+//usage:     "\n"
+//usage:     "\n       I2CBUS  I2C bus number"
+//usage:     "\n       ADDRESS 0x03-0x77"
 //usage:     "\nMODE is:"
-//usage:     "\n       b       byte (default)"
-//usage:     "\n       w       word"
-//usage:     "\n       W       word on even register addresses"
+//usage:     "\n       b       Byte (default)"
+//usage:     "\n       w       Word"
+//usage:     "\n       W       Word on even register addresses"
 //usage:     "\n       i       I2C block"
 //usage:     "\n       s       SMBus block"
-//usage:     "\n       c       consecutive byte"
+//usage:     "\n       c       Consecutive byte"
 //usage:     "\n       Append p for SMBus PEC"
 //usage:     "\n"
-//usage:     "\n       -f      force access"
-//usage:     "\n       -y      disable interactive mode"
-//usage:     "\n       -r      limit the number of registers being accessed"
+//usage:     "\n       -f      Force access"
+//usage:     "\n       -y      Disable interactive mode"
+//usage:     "\n       -r      Limit the number of registers being accessed"
 int i2cdump_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int i2cdump_main(int argc UNUSED_PARAM, char **argv)
 {
        const unsigned opt_f = (1 << 0), opt_y = (1 << 1),
                              opt_r = (1 << 2);
-       const char *const optstr = "fyr:";
 
        int bus_num, bus_addr, mode = I2C_SMBUS_BYTE_DATA, even = 0, pec = 0;
        unsigned first = 0x00, last = 0xff, opts;
-       int *block = (int *)bb_common_bufsiz1;
+       int block[I2CDUMP_NUM_REGS];
        char *opt_r_str, *dash;
-       int fd, res, blen;
+       int fd, res;
 
-        opt_complementary = "-2:?3"; /* from 2 to 3 args */
-       opts = getopt32(argv, optstr, &opt_r_str);
+       opts = getopt32(argv, "^"
+               "fyr:"
+               "\0" "-2:?3" /* from 2 to 3 args */,
+               &opt_r_str
+       );
        argv += optind;
 
        bus_num = i2c_bus_lookup(argv[0]);
@@ -971,7 +995,10 @@ int i2cdump_main(int argc UNUSED_PARAM, char **argv)
 
        /* All but word data. */
        if (mode != I2C_SMBUS_WORD_DATA || even) {
-               blen = read_block_data(fd, mode, block);
+               int blen = 0;
+
+               if (mode == I2C_SMBUS_BLOCK_DATA || mode == I2C_SMBUS_I2C_BLOCK_DATA)
+                       blen = read_block_data(fd, mode, block);
 
                if (mode == I2C_SMBUS_BYTE) {
                        res = i2c_smbus_write_byte(fd, first);
@@ -1022,33 +1049,33 @@ static const struct i2c_func i2c_funcs_tab[] = {
        { .value = I2C_FUNC_I2C,
          .name = "I2C" },
        { .value = I2C_FUNC_SMBUS_QUICK,
-         .name = "SMBus Quick Command" },
+         .name = "SMBus quick command" },
        { .value = I2C_FUNC_SMBUS_WRITE_BYTE,
-         .name = "SMBus Send Byte" },
+         .name = "SMBus send byte" },
        { .value = I2C_FUNC_SMBUS_READ_BYTE,
-         .name = "SMBus Receive Byte" },
+         .name = "SMBus receive byte" },
        { .value = I2C_FUNC_SMBUS_WRITE_BYTE_DATA,
-         .name = "SMBus Write Byte" },
+         .name = "SMBus write byte" },
        { .value = I2C_FUNC_SMBUS_READ_BYTE_DATA,
-         .name = "SMBus Read Byte" },
+         .name = "SMBus read byte" },
        { .value = I2C_FUNC_SMBUS_WRITE_WORD_DATA,
-         .name = "SMBus Write Word" },
+         .name = "SMBus write word" },
        { .value = I2C_FUNC_SMBUS_READ_WORD_DATA,
-         .name = "SMBus Read Word" },
+         .name = "SMBus read word" },
        { .value = I2C_FUNC_SMBUS_PROC_CALL,
-         .name = "SMBus Process Call" },
+         .name = "SMBus process call" },
        { .value = I2C_FUNC_SMBUS_WRITE_BLOCK_DATA,
-         .name = "SMBus Block Write" },
+         .name = "SMBus block write" },
        { .value = I2C_FUNC_SMBUS_READ_BLOCK_DATA,
-         .name = "SMBus Block Read" },
+         .name = "SMBus block read" },
        { .value = I2C_FUNC_SMBUS_BLOCK_PROC_CALL,
-         .name = "SMBus Block Process Call" },
+         .name = "SMBus block process call" },
        { .value = I2C_FUNC_SMBUS_PEC,
          .name = "SMBus PEC" },
        { .value = I2C_FUNC_SMBUS_WRITE_I2C_BLOCK,
-         .name = "I2C Block Write" },
+         .name = "I2C block write" },
        { .value = I2C_FUNC_SMBUS_READ_I2C_BLOCK,
-         .name = "I2C Block Read" },
+         .name = "I2C block read" },
        { .value = 0, .name = NULL }
 };
 
@@ -1180,33 +1207,34 @@ static void will_skip(const char *cmd)
 }
 
 //usage:#define i2cdetect_trivial_usage
-//usage:       "[-F I2CBUS] [-l] [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]"
+//usage:       "-l | -F I2CBUS | [-ya] [-q|-r] I2CBUS [FIRST LAST]"
 //usage:#define i2cdetect_full_usage "\n\n"
-//usage:       "Detect I2C chips.\n"
-//usage:     "\n       I2CBUS  i2c bus number"
-//usage:     "\n       FIRST and LAST limit the probing range"
+//usage:       "Detect I2C chips"
 //usage:     "\n"
-//usage:     "\n       -l      output list of installed busses"
-//usage:     "\n       -y      disable interactive mode"
-//usage:     "\n       -a      force scanning of non-regular addresses"
-//usage:     "\n       -q      use smbus quick write commands for probing (default)"
-//usage:     "\n       -r      use smbus read byte commands for probing"
-//usage:     "\n       -F      display list of functionalities"
+//usage:     "\n       -l      List installed buses"
+//usage:     "\n       -F BUS# List functionalities on this bus"
+//usage:     "\n       -y      Disable interactive mode"
+//usage:     "\n       -a      Force scanning of non-regular addresses"
+//usage:     "\n       -q      Use smbus quick write commands for probing (default)"
+//usage:     "\n       -r      Use smbus read byte commands for probing"
+//usage:     "\n       FIRST and LAST limit probing range"
 int i2cdetect_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int i2cdetect_main(int argc UNUSED_PARAM, char **argv)
 {
        const unsigned opt_y = (1 << 0), opt_a = (1 << 1),
                              opt_q = (1 << 2), opt_r = (1 << 3),
                              opt_F = (1 << 4), opt_l = (1 << 5);
-       const char *const optstr = "yaqrFl";
 
-       int fd, bus_num, i, j, mode = I2CDETECT_MODE_AUTO, status;
+       int fd, bus_num, i, j, mode = I2CDETECT_MODE_AUTO, status, cmd;
        unsigned first = 0x03, last = 0x77, opts;
        unsigned long funcs;
 
-       opt_complementary = "q--r:r--q:" /* mutually exclusive */
-                       "?3"; /* up to 3 args */
-       opts = getopt32(argv, optstr);
+       opts = getopt32(argv, "^"
+                       "yaqrFl"
+                       "\0"
+                       "q--r:r--q:"/*mutually exclusive*/
+                       "?3"/*up to 3 args*/
+       );
        argv += optind;
 
        if (opts & opt_l)
@@ -1251,17 +1279,17 @@ int i2cdetect_main(int argc UNUSED_PARAM, char **argv)
                no_support("detection commands");
        } else
        if (mode == I2CDETECT_MODE_QUICK && !(funcs & I2C_FUNC_SMBUS_QUICK)) {
-               no_support("SMBus Quick Write command");
+               no_support("SMBus quick write");
        } else
        if (mode == I2CDETECT_MODE_READ && !(funcs & I2C_FUNC_SMBUS_READ_BYTE)) {
-               no_support("SMBus Receive Byte command");
+               no_support("SMBus receive byte");
        }
 
        if (mode == I2CDETECT_MODE_AUTO) {
                if (!(funcs & I2C_FUNC_SMBUS_QUICK))
-                       will_skip("SMBus Quick Write");
+                       will_skip("SMBus quick write");
                if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE))
-                       will_skip("SMBus Receive Byte");
+                       will_skip("SMBus receive byte");
        }
 
        if (!(opts & opt_y))
@@ -1270,22 +1298,23 @@ int i2cdetect_main(int argc UNUSED_PARAM, char **argv)
        puts("     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f");
        for (i = 0; i < 128; i += 16) {
                printf("%02x: ", i);
-               for(j = 0; j < 16; j++) {
+               for (j = 0; j < 16; j++) {
                        fflush_all();
 
+                       cmd = mode;
                        if (mode == I2CDETECT_MODE_AUTO) {
                                if ((i+j >= 0x30 && i+j <= 0x37) ||
                                    (i+j >= 0x50 && i+j <= 0x5F))
-                                       mode = I2CDETECT_MODE_READ;
+                                       cmd = I2CDETECT_MODE_READ;
                                else
-                                       mode = I2CDETECT_MODE_QUICK;
+                                       cmd = I2CDETECT_MODE_QUICK;
                        }
 
                        /* Skip unwanted addresses. */
                        if (i+j < first
                         || i+j > last
-                        || (mode == I2CDETECT_MODE_READ && !(funcs & I2C_FUNC_SMBUS_READ_BYTE))
-                        || (mode == I2CDETECT_MODE_QUICK && !(funcs & I2C_FUNC_SMBUS_QUICK)))
+                        || (cmd == I2CDETECT_MODE_READ && !(funcs & I2C_FUNC_SMBUS_READ_BYTE))
+                        || (cmd == I2CDETECT_MODE_QUICK && !(funcs & I2C_FUNC_SMBUS_QUICK)))
                        {
                                printf("   ");
                                continue;
@@ -1302,7 +1331,7 @@ int i2cdetect_main(int argc UNUSED_PARAM, char **argv)
                                        "can't set address to 0x%02x", i + j);
                        }
 
-                       switch (mode) {
+                       switch (cmd) {
                        case I2CDETECT_MODE_READ:
                                /*
                                 * This is known to lock SMBus on various