aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--miscutils/i2c_tools.c178
1 files changed, 177 insertions, 1 deletions
diff --git a/miscutils/i2c_tools.c b/miscutils/i2c_tools.c
index 610fed5d6..455ff028d 100644
--- a/miscutils/i2c_tools.c
+++ b/miscutils/i2c_tools.c
@@ -36,17 +36,26 @@
36//config: help 36//config: help
37//config: Detect I2C chips. 37//config: Detect I2C chips.
38//config: 38//config:
39//config:config I2CTRANSFER
40//config: bool "i2ctransfer (4.0 kb)"
41//config: default y
42//config: select PLATFORM_LINUX
43//config: help
44//config: Send user-defined I2C messages in one transfer.
45//config:
39 46
40//applet:IF_I2CGET(APPLET(i2cget, BB_DIR_USR_SBIN, BB_SUID_DROP)) 47//applet:IF_I2CGET(APPLET(i2cget, BB_DIR_USR_SBIN, BB_SUID_DROP))
41//applet:IF_I2CSET(APPLET(i2cset, BB_DIR_USR_SBIN, BB_SUID_DROP)) 48//applet:IF_I2CSET(APPLET(i2cset, BB_DIR_USR_SBIN, BB_SUID_DROP))
42//applet:IF_I2CDUMP(APPLET(i2cdump, BB_DIR_USR_SBIN, BB_SUID_DROP)) 49//applet:IF_I2CDUMP(APPLET(i2cdump, BB_DIR_USR_SBIN, BB_SUID_DROP))
43//applet:IF_I2CDETECT(APPLET(i2cdetect, BB_DIR_USR_SBIN, BB_SUID_DROP)) 50//applet:IF_I2CDETECT(APPLET(i2cdetect, BB_DIR_USR_SBIN, BB_SUID_DROP))
51//applet:IF_I2CTRANSFER(APPLET(i2ctransfer, BB_DIR_USR_SBIN, BB_SUID_DROP))
44/* not NOEXEC: if hw operation stalls, use less memory in "hung" process */ 52/* not NOEXEC: if hw operation stalls, use less memory in "hung" process */
45 53
46//kbuild:lib-$(CONFIG_I2CGET) += i2c_tools.o 54//kbuild:lib-$(CONFIG_I2CGET) += i2c_tools.o
47//kbuild:lib-$(CONFIG_I2CSET) += i2c_tools.o 55//kbuild:lib-$(CONFIG_I2CSET) += i2c_tools.o
48//kbuild:lib-$(CONFIG_I2CDUMP) += i2c_tools.o 56//kbuild:lib-$(CONFIG_I2CDUMP) += i2c_tools.o
49//kbuild:lib-$(CONFIG_I2CDETECT) += i2c_tools.o 57//kbuild:lib-$(CONFIG_I2CDETECT) += i2c_tools.o
58//kbuild:lib-$(CONFIG_I2CTRANSFER) += i2c_tools.o
50 59
51/* 60/*
52 * Unsupported stuff: 61 * Unsupported stuff:
@@ -80,12 +89,19 @@
80#define I2C_FUNCS 0x0705 89#define I2C_FUNCS 0x0705
81#define I2C_PEC 0x0708 90#define I2C_PEC 0x0708
82#define I2C_SMBUS 0x0720 91#define I2C_SMBUS 0x0720
92#define I2C_RDWR 0x0707
93#define I2C_RDWR_IOCTL_MAX_MSGS 42
94#define I2C_RDWR_IOCTL_MAX_MSGS_STR "42"
83struct i2c_smbus_ioctl_data { 95struct i2c_smbus_ioctl_data {
84 __u8 read_write; 96 __u8 read_write;
85 __u8 command; 97 __u8 command;
86 __u32 size; 98 __u32 size;
87 union i2c_smbus_data *data; 99 union i2c_smbus_data *data;
88}; 100};
101struct i2c_rdwr_ioctl_data {
102 struct i2c_msg *msgs; /* pointers to i2c_msgs */
103 __u32 nmsgs; /* number of i2c_msgs */
104};
89/* end linux/i2c-dev.h */ 105/* end linux/i2c-dev.h */
90 106
91/* 107/*
@@ -262,7 +278,7 @@ static int i2c_bus_lookup(const char *bus_str)
262 return xstrtou_range(bus_str, 10, 0, 0xfffff); 278 return xstrtou_range(bus_str, 10, 0, 0xfffff);
263} 279}
264 280
265#if ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP 281#if ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP || ENABLE_I2CTRANSFER
266static int i2c_parse_bus_addr(const char *addr_str) 282static int i2c_parse_bus_addr(const char *addr_str)
267{ 283{
268 /* Slave address must be in range 0x03 - 0x77. */ 284 /* Slave address must be in range 0x03 - 0x77. */
@@ -1373,3 +1389,163 @@ int i2cdetect_main(int argc UNUSED_PARAM, char **argv)
1373 return 0; 1389 return 0;
1374} 1390}
1375#endif /* ENABLE_I2CDETECT */ 1391#endif /* ENABLE_I2CDETECT */
1392
1393#if ENABLE_I2CTRANSFER
1394static void check_i2c_func(int fd)
1395{
1396 unsigned long funcs;
1397
1398 get_funcs_matrix(fd, &funcs);
1399
1400 if (!(funcs & I2C_FUNC_I2C))
1401 bb_error_msg_and_die("adapter does not support I2C transfers");
1402}
1403
1404//usage:#define i2ctransfer_trivial_usage
1405//usage: "[-fay] I2CBUS {rLENGTH[@ADDR] | wLENGTH[@ADDR] DATA...}..."
1406//usage:#define i2ctransfer_full_usage "\n\n"
1407//usage: "Read/write I2C data in one transfer"
1408//usage: "\n"
1409//usage: "\n -f Force access"
1410//usage: "\n -a Force scanning of non-regular addresses"
1411//usage: "\n -y Disable interactive mode"
1412int i2ctransfer_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1413int i2ctransfer_main(int argc UNUSED_PARAM, char **argv)
1414{
1415 enum {
1416 opt_f = (1 << 0),
1417 opt_y = (1 << 1),
1418 opt_a = (1 << 2),
1419 };
1420 int bus_num, bus_addr;
1421 int fd;
1422 unsigned opts, first, last;
1423 int nmsgs, nmsgs_sent, i;
1424 struct i2c_msg msgs[I2C_RDWR_IOCTL_MAX_MSGS];
1425 struct i2c_rdwr_ioctl_data rdwr;
1426
1427 memset(msgs, 0, sizeof(msgs));
1428
1429 opts = getopt32(argv, "^"
1430 "fya"
1431 "\0" "-2" /* minimum 2 args */
1432 );
1433 first = 0x03;
1434 last = 0x77;
1435 if (opts & opt_a) {
1436 first = 0x00;
1437 last = 0x7f;
1438 }
1439
1440 argv += optind;
1441 bus_num = i2c_bus_lookup(argv[0]);
1442 fd = i2c_dev_open(bus_num);
1443 check_i2c_func(fd);
1444
1445 bus_addr = -1;
1446 nmsgs = 0;
1447 while (*++argv) {
1448 char *arg_ptr;
1449 unsigned len;
1450 uint16_t flags;
1451 char *end;
1452
1453 if (nmsgs >= I2C_RDWR_IOCTL_MAX_MSGS)
1454 bb_error_msg_and_die("too many messages, max: "I2C_RDWR_IOCTL_MAX_MSGS_STR);
1455
1456 flags = 0;
1457 arg_ptr = *argv;
1458 switch (*arg_ptr++) {
1459 case 'r': flags |= I2C_M_RD; break;
1460 case 'w': break;
1461 default:
1462 bb_show_usage();
1463 }
1464
1465 end = strchr(arg_ptr, '@');
1466 if (end) *end = '\0';
1467 len = xstrtou_range(arg_ptr, 0, 0, 0xffff);
1468 if (end) {
1469 bus_addr = xstrtou_range(end + 1, 0, first, last);
1470//TODO: will this work correctly if -f is specified?
1471 if (!(opts & opt_f))
1472 i2c_set_slave_addr(fd, bus_addr, (opts & opt_f));
1473 } else {
1474 /* Reuse last address if possible */
1475 if (bus_addr < 0)
1476 bb_error_msg_and_die("no address given in '%s'", *argv);
1477 }
1478
1479 msgs[nmsgs].addr = bus_addr;
1480 msgs[nmsgs].flags = flags;
1481 msgs[nmsgs].len = len;
1482 if (len)
1483 msgs[nmsgs].buf = xzalloc(len);
1484
1485 if (!(flags & I2C_M_RD)) {
1486 /* Consume DATA arg(s) */
1487 unsigned buf_idx = 0;
1488
1489 while (buf_idx < len) {
1490 uint8_t data8;
1491 unsigned long data;
1492
1493 arg_ptr = *++argv;
1494 if (!arg_ptr)
1495 bb_show_usage();
1496 data = strtoul(arg_ptr, &end, 0);
1497 if (data > 0xff || arg_ptr == end)
1498 bb_error_msg_and_die("invalid data byte '%s'", *argv);
1499
1500 data8 = data;
1501 while (buf_idx < len) {
1502 msgs[nmsgs].buf[buf_idx++] = data8;
1503 if (!*end)
1504 break;
1505 switch (*end) {
1506 /* Pseudo randomness (8 bit AXR with a=13 and b=27) */
1507 case 'p':
1508 data8 = (data8 ^ 27) + 13;
1509 data8 = (data8 << 1) | (data8 >> 7);
1510 break;
1511 case '+': data8++; break;
1512 case '-': data8--; break;
1513 case '=': break;
1514 default:
1515 bb_error_msg_and_die("invalid data byte suffix: '%s'",
1516 *argv);
1517 }
1518 }
1519 }
1520 }
1521 nmsgs++;
1522 }
1523
1524 if (!(opts & opt_y))
1525 confirm_action(bus_addr, 0, 0, 0);
1526
1527 rdwr.msgs = msgs;
1528 rdwr.nmsgs = nmsgs;
1529 nmsgs_sent = ioctl_or_perror_and_die(fd, I2C_RDWR, &rdwr, "I2C_RDWR");
1530 if (nmsgs_sent < nmsgs)
1531 bb_error_msg("warning: only %u/%u messages sent", nmsgs_sent, nmsgs);
1532
1533 for (i = 0; i < nmsgs_sent; i++) {
1534 if (msgs[i].len != 0 && (msgs[i].flags & I2C_M_RD)) {
1535 int j;
1536 for (j = 0; j < msgs[i].len - 1; j++)
1537 printf("0x%02x ", msgs[i].buf[j]);
1538 /* Print final byte with newline */
1539 printf("0x%02x\n", msgs[i].buf[j]);
1540 }
1541 }
1542
1543# if ENABLE_FEATURE_CLEAN_UP
1544 close(fd);
1545 for (i = 0; i < nmsgs; i++)
1546 free(msgs[i].buf);
1547# endif
1548
1549 return 0;
1550}
1551#endif /* ENABLE_I2CTRANSFER */