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