diff options
-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 */ | ||