xref: /aosp_15_r20/external/toybox/toys/other/gpiod.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
1 /* gpiod.c - gpio tools
2  *
3  * Copyright 2021 The Android Open Source Project
4  *
5  * TODO: gpiomon
6 
7 USE_GPIODETECT(NEWTOY(gpiodetect, ">0", TOYFLAG_USR|TOYFLAG_BIN))
8 USE_GPIOINFO(NEWTOY(gpioinfo, 0, TOYFLAG_USR|TOYFLAG_BIN))
9 USE_GPIOGET(NEWTOY(gpioget, "<2l", TOYFLAG_USR|TOYFLAG_BIN))
10 USE_GPIOFIND(NEWTOY(gpiofind, "<1>1", TOYFLAG_USR|TOYFLAG_BIN))
11 USE_GPIOSET(NEWTOY(gpioset, "<2l", TOYFLAG_USR|TOYFLAG_BIN))
12 
13 config 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 
21 config 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 
29 config GPIOINFO
30   bool "gpioinfo"
31   default y
32   help
33     usage: gpioinfo [CHIP...]
34 
35     Show gpio chips' lines.
36 
37 config 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 
48 config 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 #include "toys.h"
62 
GLOBALS(struct double_list * chips;int chip_count;)63 GLOBALS(
64   struct double_list *chips;
65   int chip_count;
66 )
67 
68 #include <linux/gpio.h>
69 
70 static int open_chip(char *chip)
71 {
72   sprintf(toybuf, isdigit(*chip) ? "/dev/gpiochip%s" : "/dev/%s", chip);
73   return xopen(toybuf, O_RDWR);
74 }
75 
collect_chips(struct dirtree * node)76 static int collect_chips(struct dirtree *node)
77 {
78   int n;
79 
80   if (!node->parent) return DIRTREE_RECURSE; // Skip the directory itself.
81 
82   if (sscanf(node->name, "gpiochip%d", &n)!=1) return 0;
83 
84   dlist_add(&TT.chips, xstrdup(node->name));
85   TT.chip_count++;
86 
87   return 0;
88 }
89 
comparator(const void * a,const void * b)90 static int comparator(const void *a, const void *b)
91 {
92   struct double_list *lhs = *(struct double_list **)a,
93     *rhs = *(struct double_list **)b;
94 
95   return strcmp(lhs->data, rhs->data);
96 }
97 
98 // call cb() in sorted order
foreach_chip(void (* cb)(char * name))99 static void foreach_chip(void (*cb)(char *name))
100 {
101   struct double_list **sorted, *chip;
102   int i = 0;
103 
104   dirtree_flagread("/dev", DIRTREE_SHUTUP, collect_chips);
105   if (!TT.chips) return;
106 
107   sorted = xmalloc(TT.chip_count*sizeof(void *));
108   for (chip = TT.chips; i<TT.chip_count; chip = chip->next) sorted[i++] = chip;
109   qsort(sorted, TT.chip_count, sizeof(void *), comparator);
110 
111   for (i = 0; i<TT.chip_count; i++) {
112     sprintf(toybuf, "/dev/%s", sorted[i]->data);
113     cb(toybuf);
114   }
115 
116   free(sorted);
117   llist_traverse(TT.chips, llist_free_double);
118 }
119 
gpiodetect(char * path)120 static void gpiodetect(char *path)
121 {
122   struct gpiochip_info chip;
123   int fd = xopen(path, O_RDWR);
124 
125   xioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &chip);
126   close(fd);
127 
128   // gpiochip0 [pinctrl-bcm2711] (58 line)
129   printf("%s [%s] (%u line%s)\n", chip.name, chip.label, chip.lines,
130          chip.lines==1?"":"s");
131 }
132 
gpiodetect_main(void)133 void gpiodetect_main(void)
134 {
135   foreach_chip(gpiodetect);
136 }
137 
138 #define FOR_gpiofind
139 #include "generated/flags.h"
140 
gpiofind(char * path)141 static void gpiofind(char *path)
142 {
143   struct gpiochip_info chip;
144   struct gpioline_info line;
145   int fd = xopen(path, O_RDWR);
146 
147   xioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &chip);
148 
149   for (line.line_offset=0; line.line_offset<chip.lines; line.line_offset++) {
150     xioctl(fd, GPIO_GET_LINEINFO_IOCTL, &line);
151     if (!strcmp(line.name, *toys.optargs)) {
152       printf("%s %d\n", chip.name, line.line_offset);
153       break;
154     }
155   }
156   close(fd);
157 }
158 
gpiofind_main(void)159 void gpiofind_main(void)
160 {
161   foreach_chip(gpiofind);
162 }
163 
164 #define FOR_gpioinfo
165 #include "generated/flags.h"
166 
gpioinfo_fd(int fd)167 static void gpioinfo_fd(int fd)
168 {
169   struct gpiochip_info chip;
170   struct gpioline_info line;
171 
172   xioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &chip);
173 
174   // gpiochip1 - 8 lines:
175   printf("%s - %d line%s:\n", chip.name, chip.lines, chip.lines==1?"":"s");
176 
177   //     line   4: "VDD_SD_IO_SEL" "vdd-sd-io" output active-high [used]
178   // We use slightly wider columns for the name and consumer; just wide enough
179   // to show all Raspberry Pi 400 pins without wrapping an 80-column terminal.
180   for (line.line_offset=0; line.line_offset<chip.lines; line.line_offset++) {
181     xioctl(fd, GPIO_GET_LINEINFO_IOCTL, &line);
182     if (*line.name) sprintf(toybuf, "\"%s\"", line.name);
183     else strcpy(toybuf, "unnamed");
184     if (*line.consumer) sprintf(toybuf+64, "\"%s\"", line.consumer);
185     else strcpy(toybuf+64, "unused");
186     printf("\tline %3d:%18s %18s", line.line_offset, toybuf, toybuf+64);
187     printf(" %sput", line.flags&GPIOLINE_FLAG_IS_OUT?"out":" in");
188     printf(" active-%s", line.flags&GPIOLINE_FLAG_ACTIVE_LOW?"low ":"high");
189     if (line.flags&GPIOLINE_FLAG_KERNEL) printf(" [used]");
190     printf("\n");
191   }
192 
193   close(fd);
194 }
195 
gpioinfo(char * path)196 static void gpioinfo(char *path)
197 {
198   gpioinfo_fd(xopen(path, O_RDWR));
199 }
200 
gpioinfo_main(void)201 void gpioinfo_main(void)
202 {
203   int i;
204 
205   if (!toys.optc) foreach_chip(gpioinfo);
206   else for (i = 0; toys.optargs[i];i++) gpioinfo_fd(open_chip(toys.optargs[i]));
207 }
208 
209 #define FOR_gpioget
210 #include "generated/flags.h"
211 
212 // TODO: half the get/set plumbing same here, maybe collate?
gpioget_main(void)213 void 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 
gpioset_main(void)239 void 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