diff options
| author | Nikolaus Voss <nikolaus.voss@loewensteinmedical.de> | 2019-02-10 19:56:41 +0100 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2019-02-10 19:59:07 +0100 |
| commit | dac8f5ea38b0f293bc9d3b10693ebb94648a992d (patch) | |
| tree | 9e1cdca42877e3e86766aaba1ab58598903df131 | |
| parent | c89764c0633670ef28166c70d03bc593f4a1179f (diff) | |
| download | busybox-w32-dac8f5ea38b0f293bc9d3b10693ebb94648a992d.tar.gz busybox-w32-dac8f5ea38b0f293bc9d3b10693ebb94648a992d.tar.bz2 busybox-w32-dac8f5ea38b0f293bc9d3b10693ebb94648a992d.zip | |
i2ctransfer: new applet
i2ctransfer sends and receives user defined i2c messages
v2: apply Xabier's comments: add -a option, don't decrement argc,
use bb_show_usage() and xzalloc()
v3: fix possible out of bound access to msgs[nmsgs]
Reviewed-by: Xabier Oneca -- xOneca <xoneca@gmail.com>
Signed-off-by: Nikolaus Voss <nikolaus.voss@loewensteinmedical.de>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
| -rw-r--r-- | miscutils/i2c_tools.c | 178 |
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" | ||
| 83 | struct i2c_smbus_ioctl_data { | 95 | struct 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 | }; |
| 101 | struct 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 |
| 266 | static int i2c_parse_bus_addr(const char *addr_str) | 282 | static 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 | ||
| 1394 | static 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" | ||
| 1412 | int i2ctransfer_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 1413 | int 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 */ | ||
