[PATCH] libmisc/shell: Add an i2c command

chrisj at rtems.org chrisj at rtems.org
Thu Aug 25 05:45:18 UTC 2022


From: Chris Johns <chrisj at rtems.org>

Closes #4708
---
 cpukit/include/rtems/shellconfig.h |   7 +
 cpukit/libmisc/shell/main_i2c.c    | 625 +++++++++++++++++++++++++++++
 spec/build/cpukit/objshell.yml     |   1 +
 3 files changed, 633 insertions(+)
 create mode 100644 cpukit/libmisc/shell/main_i2c.c

diff --git a/cpukit/include/rtems/shellconfig.h b/cpukit/include/rtems/shellconfig.h
index a013840ee7..bd44d9d310 100644
--- a/cpukit/include/rtems/shellconfig.h
+++ b/cpukit/include/rtems/shellconfig.h
@@ -98,6 +98,7 @@ extern rtems_shell_cmd_t rtems_shell_MD5_Command;
 
 extern rtems_shell_cmd_t rtems_shell_RTC_Command;
 extern rtems_shell_cmd_t rtems_shell_SPI_Command;
+extern rtems_shell_cmd_t rtems_shell_I2C_Command;
 extern rtems_shell_cmd_t rtems_shell_I2CDETECT_Command;
 extern rtems_shell_cmd_t rtems_shell_I2CGET_Command;
 extern rtems_shell_cmd_t rtems_shell_I2CSET_Command;
@@ -556,6 +557,12 @@ extern rtems_shell_alias_t * const rtems_shell_Initial_aliases[];
       &rtems_shell_SPI_Command,
     #endif
 
+    #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) \
+          && !defined(CONFIGURE_SHELL_NO_COMMAND_I2C)) \
+        || defined(CONFIGURE_SHELL_COMMAND_I2C)
+      &rtems_shell_I2C_Command,
+    #endif
+
     #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) \
           && !defined(CONFIGURE_SHELL_NO_COMMAND_I2CDETECT)) \
         || defined(CONFIGURE_SHELL_COMMAND_I2CDETECT)
diff --git a/cpukit/libmisc/shell/main_i2c.c b/cpukit/libmisc/shell/main_i2c.c
new file mode 100644
index 0000000000..96f53d3b8f
--- /dev/null
+++ b/cpukit/libmisc/shell/main_i2c.c
@@ -0,0 +1,625 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup
+ *
+ * @brief This source file contains the I2C command.
+ */
+
+/*
+ * Copyright (c) 2022 Chris Johns.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <errno.h>
+
+#include <rtems.h>
+#include <rtems/shell.h>
+
+#include <dev/i2c/i2c.h>
+#include <linux/i2c-dev.h>
+
+#define BUF_SIZE  2048
+#define MAX_BUSES 8
+#define MIN_ADDR  1
+#define MAX_ADDR  127 /* needs updating for 10bit addresses */
+
+static char bus_template[128] = "/dev/i2c-%d";
+
+typedef struct {
+  int argc;
+  char** argv;
+  int arg;
+  int bus[MAX_BUSES];
+  uint8_t buffer[BUF_SIZE];
+} i2c_data;
+
+static void dump_memory(const uint8_t* mem, size_t len) {
+  size_t i;
+  for (i = 0; i < len; ++i) {
+    if ((i % 16) == 0) {
+      if (i > 0) {
+        printf("\n");
+      }
+      printf("%04zu", i);
+    }
+    if ((i % 8) == 0) {
+      printf(" ");
+    }
+    printf(" %02x", (unsigned int) mem[i]);
+  }
+  printf("\n");
+}
+
+static int hex_to_bin(const char hex) {
+  int bin = -1;
+  if (hex >= '0' && hex <= '9') {
+    bin = hex - '0';
+  } else if (hex >= 'a' && hex <= 'z') {
+    bin = hex - 'a' + 10;
+  } else if (hex >= 'A' && hex <= 'A') {
+    bin = hex - 'A' + 10;
+  }
+  return bin;
+}
+
+static bool check_args(const char* cmd, int count, i2c_data* i2c) {
+  bool ok = count + i2c->arg <= i2c->argc;
+  if (!ok) {
+    printf("error: %s: not enough arguments\n", cmd);
+  }
+  return ok;
+}
+
+static bool check_flag(i2c_data* i2c) {
+  if (i2c->arg >= i2c->argc) {
+    return false;
+  }
+    return i2c->argv[i2c->arg][0] == '-';
+}
+
+static const char* get_arg_inc(i2c_data* i2c) {
+  if (i2c->arg < i2c->argc) {
+    return i2c->argv[i2c->arg++];
+  }
+  return "";
+}
+
+static int get_value(
+  const char* cmd, const char* type, int min, int max, int* val, i2c_data* i2c) {
+  const char* arg;
+  if (!check_args(cmd, 1, i2c)) {
+    return -1;
+  }
+  arg = get_arg_inc(i2c);
+  *val = (int) strtol(arg, NULL, 0);
+  if (*val < min || *val > max) {
+    printf("error: %s: invalid %s: %s\n", cmd, type, arg);
+    return false;
+  }
+  return true;
+}
+
+/*
+ * Address format is `bus:device`
+ */
+static bool get_device_address(const char* cmd, int* bus, int* addr, i2c_data* i2c) {
+  const char* arg;
+  const char* colon;
+  char* end = NULL;
+  *bus = -1;
+  *addr = -1;
+  if (!check_args(cmd, 1, i2c)) {
+    return -1;
+  }
+  arg = get_arg_inc(i2c);
+  colon = strchr(arg, ':');
+  if (colon != NULL) {
+    *bus = (int) strtol(arg, &end, 0);
+    if (end != NULL) {
+      if (colon != end) {
+        printf("error: invalid address: %s\n", arg);
+        return false;
+      }
+    }
+    arg = colon + 1;
+  }
+  *addr = (int) strtol(arg, &end, 16);
+  if (end != NULL && *end != '\0') {
+    printf("error: invalid address: %s\n", arg);
+    return false;
+  }
+  if (*addr < MIN_ADDR || *addr > MAX_ADDR) {
+    printf("error: invalid address: %s\n", arg);
+    return false;
+  }
+  return true;
+}
+
+static bool get_data(const char* cmd, int* len, i2c_data* i2c) {
+  const char* hex;
+  int l;
+  *len = -1;
+  if (!check_args(cmd, 1, i2c)) {
+    return false;
+  }
+  hex = get_arg_inc(i2c);
+  l = 0;
+  while (hex[l] != '\0') {
+    int bin;
+    bin = hex_to_bin(hex[l]);
+    if (bin < 0) {
+      printf("error: wr: invalid data: at %d in %s\n", l, hex);
+      return false;
+    }
+    i2c->buffer[l / 2] = bin << 4;
+    ++l;
+    if (hex[l] == '\0') {
+      printf("error: wr: data length not even: %s\n", hex);
+      return false;
+    }
+    bin = hex_to_bin(hex[l]);
+    if (bin < 0) {
+      printf("error: wr: invalid data: at %d in %s\n", l, hex);
+      return false;
+    }
+    i2c->buffer[l / 2] |= bin;
+    ++l;
+    if (l / 2 >= BUF_SIZE) {
+      printf("error: wr: data string to long\n");
+      return false;
+    }
+  }
+  *len = l;
+  return true;
+}
+
+static bool get_address_8(const char* cmd, int* addr, i2c_data* i2c) {
+  return get_value(cmd, "address", 0, 255, addr, i2c);
+}
+
+static bool get_address_16(const char* cmd, int* addr, i2c_data* i2c) {
+  return get_value(cmd, "address", 0, 65535, addr, i2c);
+}
+
+static bool get_length(const char* cmd, int* len, i2c_data* i2c) {
+  return get_value(cmd, "length", 1, sizeof(i2c->buffer), len, i2c);
+}
+
+static int i2c_bus_open(int bus, bool error) {
+  char path[32];
+  snprintf(path, sizeof(path), bus_template, bus);
+  int fd = open(path, O_RDWR);
+  if (fd < 0 && error) {
+    printf("error: open: %s: %s\n", path, strerror(errno));
+  }
+  return fd;
+}
+
+static void i2c_bus_close(int bus) {
+  if (bus >= 0) {
+    close(bus);
+  }
+}
+
+static i2c_data* i2c_data_alloc(int argc, char *argv[]) {
+  i2c_data* i2c;
+  int i;
+  i2c = calloc(1, sizeof(i2c_data));
+  if (i2c == NULL) {
+    printf("error: no memory\n");
+    return NULL;
+  }
+  i2c->argc = argc;
+  i2c->argv = argv;
+  i2c->arg = 1;
+  for (i = 0; i < MAX_BUSES; ++i) {
+    i2c->bus[i] = -1;
+  }
+  return i2c;
+}
+
+static void i2c_data_free(i2c_data* i2c) {
+  int i;
+  for (i = 0; i < MAX_BUSES; ++i) {
+    i2c_bus_close(i2c->bus[i]);
+  }
+  free(i2c);
+}
+
+static bool i2c_connect(i2c_data* i2c, int bus, bool error) {
+  if (bus >= MAX_BUSES) {
+    printf("error: bus: invalid bus %d\n", bus);
+    return false;
+  }
+  if (i2c->bus[bus] < 0) {
+    i2c->bus[bus] = i2c_bus_open(bus, error);
+  }
+  return i2c->bus[bus] >= 0;
+}
+
+static bool i2c_set_slave(i2c_data* i2c, int bus, int addr, bool error) {
+  int r;
+  if (addr < MIN_ADDR || addr > MAX_ADDR) {
+    if (error) {
+      printf("error: set slave: invalid address: %d\n", addr);
+    }
+    return false;
+  }
+  if (!i2c_connect(i2c, bus, error)) {
+    return false;
+  }
+  r = ioctl(i2c->bus[bus], I2C_SLAVE, (intptr_t) addr);
+  if (r < 0) {
+    if (error) {
+      printf("error: set slave: ioctl: %s\n", strerror(errno));
+    }
+    return false;
+  }
+  return true;
+}
+
+static bool i2c_set_bus_template(i2c_data* i2c) {
+  const char* bus_templ;
+  if (!check_args("bus", 1, i2c)) {
+    return false;
+  }
+  bus_templ = get_arg_inc(i2c);
+  if (strnlen(bus_templ, sizeof(bus_template)) > sizeof(bus_template)) {
+    printf("error: bus template: too big\n");
+    return false;
+  }
+  strcpy(bus_template, bus_templ);
+  return true;
+}
+
+static bool i2c_detect(i2c_data* i2c) {
+  int bus = 0;
+  int bus_max = MAX_BUSES;
+  if (check_flag(i2c)) {
+    const char* opt = get_arg_inc(i2c);
+    if (strcmp(opt, "-b") == 0) {
+      char* end;
+      if (!check_args("detect", 1, i2c)) {
+        return false;
+      }
+      opt = get_arg_inc(i2c);
+      bus = (int) strtol(opt, &end, 0);
+      if (end == opt || *end != '\0') {
+        printf("error: detect: invalid bus: %s\n", opt);
+        return false;
+      }
+      if (bus >= MAX_BUSES) {
+        printf("error: detect: invalid bus: %s\n", opt);
+        return false;
+      }
+      bus_max = bus + 1;
+    } else {
+      printf("error: detect: invalid option\n");
+      return false;
+    }
+  }
+  while (bus < bus_max) {
+    if (i2c_connect(i2c, bus, false)) {
+      char state[MAX_ADDR + 1] = { '\0' };
+      int addr;
+      int p;
+      for (addr = MIN_ADDR; addr <= MAX_ADDR; ++addr) {
+        if (!i2c_set_slave(i2c, bus, addr, false)) {
+          state[addr] = 'E';
+        } else {
+          uint8_t buf;
+          ssize_t r = read(i2c->bus[bus], &buf, 1);
+          if (r == 1) {
+            state[addr] = 'X';
+          } else {
+            buf = I2C_SMBUS_WRITE;
+            ssize_t r = write(i2c->bus[bus], &buf, 1);
+            if (r == 1) {
+              state[addr] = 'X';
+            }
+          }
+        }
+      }
+      p = 0;
+      for (addr = MIN_ADDR; addr <= MAX_ADDR; ++addr) {
+        if (state[addr] != '\0') {
+          if ((p % 16) == 0) {
+            printf("%2d ", bus);
+          }
+          printf(" %02x", addr);
+          if (state[addr] == 'E') {
+            printf("e");
+          }
+          ++p;
+          if ((p % 16) == 0) {
+            printf("\n");
+          }
+        }
+      }
+      if (p > 0 && (p % 16) != 0) {
+        printf("\n");
+      }
+    }
+    ++bus;
+  }
+  return true;
+}
+
+static bool i2c_reader(i2c_data* i2c) {
+  struct i2c_rdwr_ioctl_data data;
+  i2c_msg msg[1];
+  int bus = 0;
+  int addr = 0;
+  int len = -1;
+  int rc;
+  if (!get_device_address("rd", &bus, &addr, i2c)) {
+    return false;
+  }
+  if (!i2c_connect(i2c, bus, true)) {
+    return false;
+  }
+  if (!get_length("rd", &len, i2c)) {
+    return false;
+  }
+  msg[0].addr = addr;
+  msg[0].flags = I2C_M_RD;
+  msg[0].len = len;
+  msg[0].buf = i2c->buffer;
+  data.msgs = &msg[0];
+  data.nmsgs = 1;
+  rc = ioctl(i2c->bus[bus], I2C_RDWR, &data);
+  if (rc < 0) {
+    printf("error: rd: ioctl: %s\n", strerror(errno));
+    return false;
+  }
+  dump_memory(i2c->buffer, len);
+  return true;
+}
+
+static bool i2c_writer(i2c_data* i2c) {
+  struct i2c_rdwr_ioctl_data data;
+  i2c_msg msg[1];
+  int bus = 0;
+  int addr = 0;
+  int len = 0;
+  int rc;
+  if (!get_device_address("wr", &bus, &addr, i2c)) {
+    return false;
+  }
+  if (!i2c_connect(i2c, bus, true)) {
+    return false;
+  }
+  if (!get_data("wr", &len, i2c)) {
+    return false;
+  }
+  msg[0].addr = addr;
+  msg[0].flags = 0;
+  msg[0].len = len / 2;
+  msg[0].buf = i2c->buffer;
+  data.msgs = &msg[0];
+  data.nmsgs = 1;
+  rc = ioctl(i2c->bus[bus], I2C_RDWR, &data);
+  if (rc < 0) {
+    printf("error: wr: ioctl: %s\n", strerror(errno));
+    return false;
+  }
+  return true;
+}
+
+static bool i2c_addr_reader(i2c_data* i2c) {
+  struct i2c_rdwr_ioctl_data data;
+  i2c_msg msg[2];
+  int bus = 0;
+  int addr = 0;
+  uint8_t dev_addr[2];
+  int dev_addr_len = 1;
+  int len = -1;
+  int rc;
+  if (check_flag(i2c)) {
+    const char* opt = get_arg_inc(i2c);
+    if (strcmp(opt, "-8") == 0) {
+      dev_addr_len = 1;
+    } else if (strcmp(opt, "-16") == 0) {
+      dev_addr_len = 2;
+    } else {
+      printf("error: rda: invalid option\n");
+      return false;
+    }
+  }
+  if (!get_device_address("rda", &bus, &addr, i2c)) {
+    return false;
+  }
+  if (!i2c_connect(i2c, bus, true)) {
+    return false;
+  }
+  if (dev_addr_len == 1) {
+    int daddr = 0;
+    if (!get_address_8("rda", &daddr, i2c)) {
+      return false;
+    }
+    dev_addr[0] = daddr;
+  } else {
+    int daddr = 0;
+    if (!get_address_16("rda", &daddr, i2c)) {
+      return false;
+    }
+    dev_addr[0] = (uint8_t) (daddr >> 8);
+    dev_addr[1] = (uint8_t) daddr;
+  }
+  if (!get_length("rda", &len, i2c)) {
+    return false;
+  }
+  msg[0].addr = addr;
+  msg[0].flags = 0;
+  msg[0].len = dev_addr_len;
+  msg[0].buf = &dev_addr[0];
+  msg[1].addr = addr;
+  msg[1].flags = I2C_M_RD;
+  msg[1].len = len;
+  msg[1].buf = i2c->buffer;
+  data.msgs = &msg[0];
+  data.nmsgs = 2;
+  rc = ioctl(i2c->bus[bus], I2C_RDWR, &data);
+  if (rc < 0) {
+    printf("error: rda: ioctl: %s\n", strerror(errno));
+    return false;
+  }
+  dump_memory(i2c->buffer, len);
+  return true;
+}
+
+static bool i2c_addr_writer(i2c_data* i2c) {
+  struct i2c_rdwr_ioctl_data data;
+  i2c_msg msg[2];
+  int bus = 0;
+  int addr = 0;
+  uint8_t dev_addr[2];
+  int dev_addr_len = 1;
+  int len = 0;
+  int rc;
+  if (check_flag(i2c)) {
+    const char* opt = get_arg_inc(i2c);
+    if (strcmp(opt, "-8") == 0) {
+      dev_addr_len = 1;
+    } else if (strcmp(opt, "-16") == 0) {
+      dev_addr_len = 2;
+    } else {
+      printf("error: wra: invalid option\n");
+      return false;
+    }
+  }
+  if (!get_device_address("wra", &bus, &addr, i2c)) {
+    return false;
+  }
+  if (!i2c_connect(i2c, bus, true)) {
+    return false;
+  }
+  if (dev_addr_len == 1) {
+    int daddr = 0;
+    if (!get_address_8("wra", &daddr, i2c)) {
+      return false;
+    }
+    dev_addr[0] = daddr;
+  } else {
+    int daddr = 0;
+    if (!get_address_16("wra", &daddr, i2c)) {
+      return false;
+    }
+    dev_addr[0] = (uint8_t) (daddr >> 8);
+    dev_addr[1] = (uint8_t) daddr;
+  }
+  if (!get_data("wra", &len, i2c)) {
+    return false;
+  }
+  msg[0].addr = addr;
+  msg[0].flags = 0;
+  msg[0].len = dev_addr_len;
+  msg[0].buf = &dev_addr[0];
+  msg[1].addr = addr;
+  msg[1].flags = I2C_M_NOSTART;
+  msg[1].len = len / 2;
+  msg[1].buf = i2c->buffer;
+  data.msgs = &msg[0];
+  data.nmsgs = 2;
+  rc = ioctl(i2c->bus[bus], I2C_RDWR, &data);
+  if (rc < 0) {
+    printf("error: ioctl: %s\n", strerror(errno));
+    return false;
+  }
+  return true;
+}
+
+static void help(void) {
+  printf( "Usage: i2c <command> ...\n");
+  printf( " where <command> is:\n");
+  printf( "  help                                : this help\n");
+  printf( "  bus path                            : set the bus path template, include '%%d' for bus\n");
+  printf( "  det <-b bus>                        : detect devices on bus(es)\n");
+  printf( "  rd bus:addr len                     : read of data from slave\n");
+  printf( "  wr bus:addr data..                  : write of data to slave\n");
+  printf( "  rda <-8> <-16> bus:addr addr len    : write address then read data from slave\n");
+  printf( "  wra <-8> <-16> bus:addr addr data.. : write address then write data to slave\n");
+  printf( " bus:addr - decimal bus number and hex address, eg 0:74 \n");
+  printf( " data - even length hex data string, eg 001122334455 \n");
+}
+
+static int rtems_shell_main_i2c(
+  int argc, char *argv[]) {
+  bool error = true;;
+  if (argc == 1) {
+    help();
+  } else {
+    i2c_data* i2c = i2c_data_alloc(argc, argv);
+    if (i2c != NULL) {
+      error = false;
+      while (!error && i2c->arg < i2c->argc) {
+        int cmd = i2c->arg++;
+        if (strcmp(i2c->argv[cmd], "help") == 0) {
+          help();
+          error = true;
+        } else if (strcmp(i2c->argv[cmd], "bus") == 0) {
+          error = i2c_set_bus_template(i2c);
+        } else if (strcmp(i2c->argv[cmd], "det") == 0) {
+          error = i2c_detect(i2c);
+        } else if (strcmp(i2c->argv[cmd], "rd") == 0) {
+          error = i2c_reader(i2c);
+        } else if (strcmp(i2c->argv[cmd], "wr") == 0) {
+          error = i2c_writer(i2c);
+        } else if (strcmp(i2c->argv[cmd], "rda") == 0) {
+          error = i2c_addr_reader(i2c);
+        } else if (strcmp(i2c->argv[cmd], "wra") == 0) {
+          error = i2c_addr_writer(i2c);
+        } else {
+          printf("error: invalid command: %s\n", i2c->argv[cmd]);
+          error = true;
+        }
+      }
+      i2c_data_free(i2c);
+    }
+  }
+  return error ? 1 : 0;
+}
+
+#define HELP_LINE \
+  "i2c <command> ... (try: i2c help))"
+
+rtems_shell_cmd_t rtems_shell_I2C_Command = {
+  "i2c",                 /* name */
+  HELP_LINE,             /* usage */
+  "rtems",               /* topic */
+  rtems_shell_main_i2c,  /* command */
+  NULL,                  /* alias */
+  NULL,                  /* next */
+  0500,                  /* mode */
+  0,                     /* uid */
+  0                      /* gid */
+};
diff --git a/spec/build/cpukit/objshell.yml b/spec/build/cpukit/objshell.yml
index 5fd259bee3..190223b37b 100644
--- a/spec/build/cpukit/objshell.yml
+++ b/spec/build/cpukit/objshell.yml
@@ -56,6 +56,7 @@ source:
 - cpukit/libmisc/shell/main_halt.c
 - cpukit/libmisc/shell/main_help.c
 - cpukit/libmisc/shell/main_hexdump.c
+- cpukit/libmisc/shell/main_i2c.c
 - cpukit/libmisc/shell/main_i2cdetect.c
 - cpukit/libmisc/shell/main_i2cget.c
 - cpukit/libmisc/shell/main_i2cset.c
-- 
2.19.1



More information about the devel mailing list