toybox/toys/other/gpiod.c
<<
>>
Prefs
   1/* gpiod.c - gpio tools
   2 *
   3 * Copyright 2021 The Android Open Source Project
   4 *
   5 * TODO: gpiomon
   6
   7USE_GPIODETECT(NEWTOY(gpiodetect, ">0", TOYFLAG_USR|TOYFLAG_BIN))
   8USE_GPIOFIND(NEWTOY(gpioinfo, 0, TOYFLAG_USR|TOYFLAG_BIN))
   9USE_GPIOGET(NEWTOY(gpioget, "<2l", TOYFLAG_USR|TOYFLAG_BIN))
  10USE_GPIOINFO(NEWTOY(gpiofind, "<1>1", TOYFLAG_USR|TOYFLAG_BIN))
  11USE_GPIOSET(NEWTOY(gpioset, "<2l", TOYFLAG_USR|TOYFLAG_BIN))
  12
  13config GPIODETECT
  14  bool "gpiodetect"
  15  default y
  16  help
  17    usage: gpiodetect
  18
  19    Show all gpio chips' names, labels, and number of lines.
  20
  21config GPIOFIND
  22  bool "gpiofind"
  23  default y
  24  help
  25    usage: gpiofind NAME
  26
  27    Show the chip and line number for the given line name.
  28
  29config GPIOINFO
  30  bool "gpioinfo"
  31  default y
  32  help
  33    usage: gpioinfo [CHIP...]
  34
  35    Show gpio chips' lines.
  36
  37config GPIOGET
  38  bool "gpioget"
  39  default y
  40  help
  41    usage: gpioget [-l] CHIP LINE...
  42
  43    Gets the values of the given lines on CHIP. Use gpiofind to convert line
  44    names to numbers.
  45
  46    -l  Active low
  47
  48config GPIOSET
  49  bool "gpioset"
  50  default y
  51  help
  52    usage: gpioset [-l] CHIP LINE=VALUE...
  53
  54    Set the lines on CHIP to the given values. Use gpiofind to convert line
  55    names to numbers.
  56
  57    -l  Active low
  58*/
  59
  60#define FOR_gpiodetect
  61#define TT this.gpiod
  62#include "toys.h"
  63
  64GLOBALS(
  65  struct double_list *chips;
  66  int chip_count;
  67)
  68
  69#include <linux/gpio.h>
  70
  71static int open_chip(char *chip)
  72{
  73  sprintf(toybuf, isdigit(*chip) ? "/dev/gpiochip%s" : "/dev/%s", chip);
  74  return xopen(toybuf, O_RDWR);
  75}
  76
  77static int collect_chips(struct dirtree *node)
  78{
  79  int n;
  80
  81  if (!node->parent) return DIRTREE_RECURSE; // Skip the directory itself.
  82
  83  if (sscanf(node->name, "gpiochip%d", &n)!=1) return 0;
  84
  85  dlist_add(&TT.chips, strdup(node->name));
  86  TT.chip_count++;
  87
  88  return 0;
  89}
  90
  91static int comparator(const void *a, const void *b)
  92{
  93  struct double_list *lhs = *(struct double_list **)a,
  94    *rhs = *(struct double_list **)b;
  95
  96  return strcmp(lhs->data, rhs->data);
  97}
  98
  99// call cb() in sorted order
 100static void foreach_chip(void (*cb)(char *name))
 101{
 102  struct double_list **sorted;
 103  int i = 0;
 104
 105  dirtree_flagread("/dev", DIRTREE_SHUTUP, collect_chips);
 106  if (!TT.chips) return;
 107
 108  sorted = xmalloc(TT.chip_count*sizeof(void *));
 109  for (i = 0; i<TT.chip_count; i++) sorted[i] = TT.chips = TT.chips->next;
 110  qsort(sorted, TT.chip_count, sizeof(void *), comparator);
 111
 112  for (i = 0; i<TT.chip_count; i++) {
 113    sprintf(toybuf, "/dev/%s", sorted[i]->data);
 114    cb(toybuf);
 115  }
 116
 117  free(sorted);
 118  llist_traverse(&TT.chips, llist_free_arg);
 119}
 120
 121static void gpiodetect(char *path)
 122{
 123  struct gpiochip_info chip;
 124  int fd = xopen(path, O_RDWR);
 125
 126  xioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &chip);
 127  close(fd);
 128
 129  // gpiochip0 [pinctrl-bcm2711] (58 line)
 130  printf("%s [%s] (%u line%s)\n", chip.name, chip.label, chip.lines,
 131         chip.lines==1?"":"s");
 132}
 133
 134void gpiodetect_main(void)
 135{
 136  foreach_chip(gpiodetect);
 137}
 138
 139#define FOR_gpiofind
 140#include "generated/flags.h"
 141
 142static void gpiofind(char *path)
 143{
 144  struct gpiochip_info chip;
 145  struct gpioline_info line;
 146  int fd = xopen(path, O_RDWR);
 147
 148  xioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &chip);
 149
 150  for (line.line_offset=0; line.line_offset<chip.lines; line.line_offset++) {
 151    xioctl(fd, GPIO_GET_LINEINFO_IOCTL, &line);
 152    if (!strcmp(line.name, *toys.optargs)) {
 153      printf("%s %d\n", chip.name, line.line_offset);
 154      break;
 155    }
 156  }
 157  close(fd);
 158}
 159
 160void gpiofind_main(void)
 161{
 162  foreach_chip(gpiofind);
 163}
 164
 165#define FOR_gpioinfo
 166#include "generated/flags.h"
 167
 168static void gpioinfo_fd(int fd)
 169{
 170  struct gpiochip_info chip;
 171  struct gpioline_info line;
 172
 173  xioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &chip);
 174
 175  // gpiochip1 - 8 lines:
 176  printf("%s - %d line%s:\n", chip.name, chip.lines, chip.lines==1?"":"s");
 177
 178  //     line   4: "VDD_SD_IO_SEL" "vdd-sd-io" output active-high [used]
 179  // We use slightly wider columns for the name and consumer; just wide enough
 180  // to show all Raspberry Pi 400 pins without wrapping an 80-column terminal.
 181  for (line.line_offset=0; line.line_offset<chip.lines; line.line_offset++) {
 182    xioctl(fd, GPIO_GET_LINEINFO_IOCTL, &line);
 183    if (*line.name) sprintf(toybuf, "\"%s\"", line.name);
 184    else strcpy(toybuf, "unnamed");
 185    if (*line.consumer) sprintf(toybuf+64, "\"%s\"", line.consumer);
 186    else strcpy(toybuf+64, "unused");
 187    printf("\tline %3d:%18s %18s", line.line_offset, toybuf, toybuf+64);
 188    printf(" %sput", line.flags&GPIOLINE_FLAG_IS_OUT?"out":" in");
 189    printf(" active-%s", line.flags&GPIOLINE_FLAG_ACTIVE_LOW?"low ":"high");
 190    if (line.flags&GPIOLINE_FLAG_KERNEL) printf(" [used]");
 191    printf("\n");
 192  }
 193
 194  close(fd);
 195}
 196
 197static void gpioinfo(char *path)
 198{
 199  gpioinfo_fd(xopen(path, O_RDWR));
 200}
 201
 202void gpioinfo_main(void)
 203{
 204  int i;
 205
 206  if (!toys.optc) foreach_chip(gpioinfo);
 207  else for (i = 0; toys.optargs[i];i++) gpioinfo_fd(open_chip(toys.optargs[i]));
 208}
 209
 210#define FOR_gpioget
 211#include "generated/flags.h"
 212
 213void gpioget_main(void)
 214{
 215  struct gpiohandle_request req = { .flags = GPIOHANDLE_REQUEST_INPUT };
 216  struct gpiohandle_data data;
 217  struct gpiochip_info chip;
 218  char **args = toys.optargs;
 219  int fd, line;
 220
 221  fd = open_chip(*args);
 222  xioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &chip);
 223  if (FLAG(l)) req.flags |= GPIOHANDLE_REQUEST_ACTIVE_LOW;
 224  for (args++; *args; args++, req.lines++) {
 225    if (req.lines >= GPIOHANDLES_MAX) error_exit("too many requests!");
 226    line = atolx_range(*args, 0, chip.lines);
 227    req.lineoffsets[req.lines] = line;
 228  }
 229  xioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
 230  xioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
 231  for (line = 0; line<req.lines; line++)
 232    printf("%s%d", " "+(line<1), data.values[line]);
 233  xputc('\n');
 234}
 235
 236#define FOR_gpioset
 237#include "generated/flags.h"
 238
 239void gpioset_main(void)
 240{
 241  struct gpiohandle_request req = { .flags = GPIOHANDLE_REQUEST_OUTPUT };
 242  char **args = toys.optargs;
 243  int fd, value;
 244
 245  fd = open_chip(*args);
 246  if (FLAG(l)) req.flags |= GPIOHANDLE_REQUEST_ACTIVE_LOW;
 247  for (args++; *args; args++, req.lines++) {
 248    if (req.lines == GPIOHANDLES_MAX) error_exit("too many requests!");
 249    if (sscanf(*args, "%d=%d", req.lineoffsets+req.lines, &value) != 2)
 250      perror_exit("not LINE=VALUE: %s", *args);
 251    req.default_values[req.lines] = value;
 252  }
 253  xioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
 254}
 255