toybox/toys/other/i2ctools.c
<<
>>
Prefs
   1/* i2ctools.c - i2c tools
   2 *
   3 * Copyright 2018 The Android Open Source Project
   4 *
   5 * https://www.kernel.org/doc/Documentation/i2c/
   6 *
   7 * Note: -y must have the same value in each toy for `confirm`.
   8 *
   9 * TODO: i2cdetect -q/-r and the "auto" mode?
  10 * TODO: i2cdump non-byte modes, -r FIRST-LAST?
  11 * TODO: i2cget non-byte modes? default to current read address?
  12 * TODO: i2cset -r? -m MASK? c/s modes, p mode modifier?
  13
  14USE_I2CDETECT(NEWTOY(i2cdetect, ">3aFly", TOYFLAG_USR|TOYFLAG_BIN))
  15USE_I2CDUMP(NEWTOY(i2cdump, "<2>2fy", TOYFLAG_USR|TOYFLAG_BIN))
  16USE_I2CGET(NEWTOY(i2cget, "<3>3fy", TOYFLAG_USR|TOYFLAG_BIN))
  17USE_I2CSET(NEWTOY(i2cset, "<4fy", TOYFLAG_USR|TOYFLAG_BIN))
  18
  19config I2CDETECT
  20  bool "i2cdetect"
  21  default y
  22  help
  23    usage: i2cdetect [-ary] BUS [FIRST LAST]
  24    usage: i2cdetect -F BUS
  25    usage: i2cdetect -l
  26
  27    Detect i2c devices.
  28
  29    -a  All addresses (0x00-0x7f rather than 0x03-0x77)
  30    -F  Show functionality
  31    -l  List all buses
  32    -r  Probe with SMBus Read Byte
  33    -y  Answer "yes" to confirmation prompts (for script use)
  34
  35config I2CDUMP
  36  bool "i2cdump"
  37  default y
  38  help
  39    usage: i2cdump [-fy] BUS CHIP
  40
  41    Dump i2c registers.
  42
  43    -f  Force access to busy devices
  44    -y  Answer "yes" to confirmation prompts (for script use)
  45
  46config I2CGET
  47  bool "i2cget"
  48  default y
  49  help
  50    usage: i2cget [-fy] BUS CHIP ADDR
  51
  52    Read an i2c register.
  53
  54    -f  Force access to busy devices
  55    -y  Answer "yes" to confirmation prompts (for script use)
  56
  57config I2CSET
  58  bool "i2cset"
  59  default y
  60  help
  61    usage: i2cset [-fy] BUS CHIP ADDR VALUE... MODE
  62
  63    Write an i2c register. MODE is b for byte, w for 16-bit word, i for I2C block.
  64
  65    -f  Force access to busy devices
  66    -y  Answer "yes" to confirmation prompts (for script use)
  67*/
  68
  69#define FOR_i2cdetect
  70#include "toys.h"
  71
  72#include <linux/i2c.h>
  73#include <linux/i2c-dev.h>
  74
  75printf_format static void confirm(const char *fmt, ...)
  76{
  77  va_list va;
  78
  79  if (toys.optflags & FLAG_y) return;
  80
  81  va_start(va, fmt);
  82  vfprintf(stderr, fmt, va);
  83  va_end(va);
  84  if (!yesno(1)) error_exit("Exiting");
  85}
  86
  87static int i2c_open(int bus, int slave, int chip)
  88{
  89  int fd;
  90
  91  snprintf(toybuf, sizeof(toybuf), "/dev/i2c-%d", bus);
  92  fd = xopen(toybuf, O_RDONLY);
  93  if (slave) xioctl(fd, slave, (void *)(long)chip);
  94  return fd;
  95}
  96
  97static unsigned long i2c_get_funcs(int bus)
  98{
  99  int fd = i2c_open(bus, 0, 0);
 100  unsigned long result;
 101
 102  xioctl(fd, I2C_FUNCS, &result);
 103  close(fd);
 104  return result;
 105}
 106
 107static int i2c_read_byte(int fd, int addr, int *byte)
 108{
 109  struct i2c_smbus_ioctl_data ioctl_data;
 110  union i2c_smbus_data data;
 111
 112  memset(&data, 0, sizeof(data));
 113  ioctl_data.read_write = I2C_SMBUS_READ;
 114  ioctl_data.size = I2C_SMBUS_BYTE_DATA;
 115  ioctl_data.command = addr;
 116  ioctl_data.data = &data;
 117  if (ioctl(fd, I2C_SMBUS, &ioctl_data) == -1) return -1;
 118  *byte = data.byte;
 119  return 0;
 120}
 121
 122static void i2cdetect_dash_F(int bus)
 123{
 124  size_t i;
 125
 126  struct { int mask; const char *name; } funcs[] = {
 127    {I2C_FUNC_I2C, "I2C"},
 128    {I2C_FUNC_SMBUS_QUICK, "SMBus Quick Command"},
 129    {I2C_FUNC_SMBUS_WRITE_BYTE, "SMBus Send Byte"},
 130    {I2C_FUNC_SMBUS_READ_BYTE, "SMBus Receive Byte"},
 131    {I2C_FUNC_SMBUS_WRITE_BYTE_DATA, "SMBus Write Byte"},
 132    {I2C_FUNC_SMBUS_READ_BYTE_DATA, "SMBus Read Byte"},
 133    {I2C_FUNC_SMBUS_WRITE_WORD_DATA, "SMBus Write Word"},
 134    {I2C_FUNC_SMBUS_READ_WORD_DATA, "SMBus Read Word"},
 135    {I2C_FUNC_SMBUS_PROC_CALL, "SMBus Process Call"},
 136    {I2C_FUNC_SMBUS_WRITE_BLOCK_DATA, "SMBus Write Block"},
 137    {I2C_FUNC_SMBUS_READ_BLOCK_DATA, "SMBus Read Block"},
 138    {I2C_FUNC_SMBUS_BLOCK_PROC_CALL, "SMBus Block Process Call"},
 139    {I2C_FUNC_SMBUS_PEC, "SMBus PEC"},
 140    {I2C_FUNC_SMBUS_WRITE_I2C_BLOCK, "I2C Write Block"},
 141    {I2C_FUNC_SMBUS_READ_I2C_BLOCK, "I2C Read Block"},
 142  };
 143  unsigned long supported = i2c_get_funcs(bus);
 144
 145  printf("Functionalities implemented by %s:\n", toybuf);
 146  for (i = 0; i < ARRAY_LEN(funcs); ++i) {
 147    printf("%-32s %s\n", funcs[i].name,
 148           (supported & funcs[i].mask) ? "yes" : "no");
 149  }
 150}
 151
 152static int i2cdetect_dash_l(struct dirtree *node)
 153{
 154  int suffix_len = strlen("/name");
 155  int bus;
 156  char *fname, *p;
 157  unsigned long funcs;
 158
 159  if (!node->parent) return DIRTREE_RECURSE; // Skip the directory itself.
 160
 161  if (sscanf(node->name, "i2c-%d", &bus) != 1) return 0;
 162  funcs = i2c_get_funcs(bus);
 163
 164  fname = dirtree_path(node, &suffix_len);
 165  strcat(fname, "/name");
 166  xreadfile(fname, toybuf, sizeof(toybuf));
 167  free(fname);
 168  if ((p = strchr(toybuf, '\n'))) *p = 0;
 169
 170  // "i2c-1     i2c             Synopsys DesignWare I2C adapter         I2C adapter"
 171  printf("%s\t%-10s\t%-32s\t%s\n", node->name,
 172         (funcs & I2C_FUNC_I2C) ? "i2c" : "?",
 173         toybuf,
 174         (funcs & I2C_FUNC_I2C) ? "I2C Adapter" : "?");
 175
 176  return 0;
 177}
 178
 179void i2cdetect_main(void)
 180{
 181  if (toys.optflags & FLAG_l) {
 182    if (toys.optc) error_exit("-l doesn't take arguments");
 183    dirtree_read("/sys/class/i2c-dev", i2cdetect_dash_l);
 184  } else if (toys.optflags & FLAG_F) {
 185    if (toys.optc != 1) error_exit("-F BUS");
 186    i2cdetect_dash_F(atolx_range(*toys.optargs, 0, INT_MAX));
 187  } else {
 188    int bus, first = 0x03, last = 0x77, fd, row, addr, byte;
 189
 190    if (toys.optflags & FLAG_a) {
 191      first = 0x00;
 192      last = 0x7f;
 193    }
 194
 195    if (toys.optc != 1 && toys.optc != 3) error_exit("bad args");
 196    bus = atolx_range(*toys.optargs, 0, INT_MAX);
 197    if (toys.optc == 3) {
 198      first = atolx_range(toys.optargs[1], 0, 0x7f);
 199      last = atolx_range(toys.optargs[1], 0, 0x7f);
 200      if (first > last) error_exit("first > last");
 201    }
 202
 203    confirm("Probe chips 0x%02x-0x%02x on bus %d?", first, last, bus);
 204
 205    fd = i2c_open(bus, 0, 0);
 206    printf("     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f\n");
 207    for (row = 0; row <= 0x70; row += 16) {
 208      xprintf("%02x:", row & 0xf0);
 209      for (addr = row; addr < row + 16; ++addr) {
 210        if (addr < first || addr > last) printf("   ");
 211        else {
 212          if (ioctl(fd, I2C_SLAVE, addr) == -1) {
 213            if (errno == EBUSY) {
 214              xprintf(" UU");
 215              continue;
 216            }
 217            perror_exit("ioctl(I2C_SLAVE)");
 218          }
 219          if (i2c_read_byte(fd, addr, &byte) == -1) xprintf(" --");
 220          else xprintf(" %02x", addr);
 221        }
 222      }
 223      putchar('\n');
 224    }
 225    close(fd);
 226  }
 227}
 228
 229#define CLEANUP_i2cdetect
 230#define FOR_i2cdump
 231#include "generated/flags.h"
 232
 233void i2cdump_main(void)
 234{
 235  int bus = atolx_range(toys.optargs[0], 0, INT_MAX);
 236  int chip = atolx_range(toys.optargs[1], 0, 0x7f);
 237  int fd, row, addr, byte;
 238
 239  confirm("Dump chip 0x%02x on bus %d?", chip, bus);
 240
 241  fd = i2c_open(bus, (toys.optflags&FLAG_f)?I2C_SLAVE_FORCE:I2C_SLAVE, chip);
 242  printf("     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef\n");
 243  for (row = 0; row <= 0xf0; row += 16) {
 244    xprintf("%02x:", row & 0xf0);
 245    for (addr = row; addr < row + 16; ++addr) {
 246      if (i2c_read_byte(fd, addr, &byte) == -1) perror_exit("i2c_read_byte");
 247      printf(" %02x", byte);
 248      toybuf[addr-row] = isprint(byte) ? byte : (byte ? '?' : '.');
 249    }
 250    printf("    %16.16s\n", toybuf);
 251  }
 252  close(fd);
 253}
 254
 255#define CLEANUP_i2cdump
 256#define FOR_i2cget
 257#include "generated/flags.h"
 258
 259void i2cget_main(void)
 260{
 261  int bus = atolx_range(toys.optargs[0], 0, INT_MAX);
 262  int chip = atolx_range(toys.optargs[1], 0, 0x7f);
 263  int addr = atolx_range(toys.optargs[2], 0, 0xff);
 264  int fd, byte;
 265
 266  confirm("Read register 0x%02x from chip 0x%02x on bus %d?", addr, chip, bus);
 267
 268  fd = i2c_open(bus, (toys.optflags&FLAG_f)?I2C_SLAVE_FORCE:I2C_SLAVE, chip);
 269  if (i2c_read_byte(fd, addr, &byte) == -1) perror_exit("i2c_read_byte");
 270  printf("0x%02x\n", byte);
 271  close(fd);
 272}
 273
 274#define CLEANUP_i2cget
 275#define FOR_i2cset
 276#include "generated/flags.h"
 277
 278void i2cset_main(void)
 279{
 280  int bus = atolx_range(toys.optargs[0], 0, INT_MAX);
 281  int chip = atolx_range(toys.optargs[1], 0, 0x7f);
 282  int addr = atolx_range(toys.optargs[2], 0, 0xff);
 283  char *mode = toys.optargs[toys.optc-1];
 284  int fd, i;
 285  struct i2c_smbus_ioctl_data ioctl_data;
 286  union i2c_smbus_data data;
 287
 288  memset(&data, 0, sizeof(data));
 289  if (strlen(mode) != 1) help_exit("mode too long");
 290  if (*mode == 'b' && toys.optc == 5) {
 291    ioctl_data.size = I2C_SMBUS_BYTE_DATA;
 292    data.byte = atolx_range(toys.optargs[3], 0, 0xff);
 293  } else if (*mode == 'w' && toys.optc == 5) {
 294    ioctl_data.size = I2C_SMBUS_WORD_DATA;
 295    data.word = atolx_range(toys.optargs[3], 0, 0xffff);
 296  } else if (*mode == 'i' && toys.optc >= 5) {
 297    if (toys.optc - 4 > I2C_SMBUS_BLOCK_MAX) error_exit("too much data");
 298    ioctl_data.size = I2C_SMBUS_I2C_BLOCK_DATA;
 299    for (i = 0; i < toys.optc - 4; ++i)
 300      data.block[i+1] = atolx_range(toys.optargs[3+i], 0, 0xff);
 301    data.block[0] = toys.optc - 4;
 302  } else {
 303    help_exit("syntax error");
 304  }
 305
 306  confirm("Write register 0x%02x from chip 0x%02x on bus %d?", addr, chip, bus);
 307
 308  fd = i2c_open(bus, (toys.optflags&FLAG_f)?I2C_SLAVE_FORCE:I2C_SLAVE, chip);
 309  ioctl_data.read_write = I2C_SMBUS_WRITE;
 310  ioctl_data.command = addr;
 311  ioctl_data.data = &data;
 312  xioctl(fd, I2C_SMBUS, &ioctl_data);
 313  close(fd);
 314}
 315