1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2022 Oxide Computer Company
14 */
15
16 #include <string.h>
17 #include <err.h>
18 #include <stdlib.h>
19 #include <stdarg.h>
20 #include <ofmt.h>
21 #include <libdevinfo.h>
22 #include <strings.h>
23 #include <sys/debug.h>
24
25 #include "gpioadm.h"
26
27 static void
gpioadm_controller_list_usage(FILE * f)28 gpioadm_controller_list_usage(FILE *f)
29 {
30 (void) fprintf(f, "\tgpioadm controller list [-H] [-o field[,...] "
31 "[-p]] [filter...]\n");
32 }
33
34 static void __PRINTFLIKE(1)
gpioadm_controller_list_help(const char * fmt,...)35 gpioadm_controller_list_help(const char *fmt, ...)
36 {
37 if (fmt != NULL) {
38 va_list ap;
39
40 va_start(ap, fmt);
41 vwarnx(fmt, ap);
42 va_end(ap);
43 }
44
45 (void) fprintf(stderr, "Usage: gpioadm controller list [-H] [-o "
46 "field[,...] [-p]] [filter...]\n");
47 (void) fprintf(stderr, "\nList GPIO controllers in the system and "
48 "associated information.\n\n"
49 "\t-H\t\tomit the column header\n"
50 "\t-o field\toutput fields to print\n"
51 "\t-p\t\tparsable output (requires -o)\n\n"
52 "The following fields are supported:\n"
53 "\tcontroller\tthe name of the controller\n"
54 "\tngpios\t\tthe number of GPIOs the controller has\n"
55 "\tndpios\t\tthe number of DPIOs the controller has\n"
56 "\tpath\t\tthe path to the minor node of the controller\n"
57 "\tprovider\tthe /devices path of the provider\n\n"
58 "Filters restrict output to the named controllers. Each filter is "
59 "treated\nlike an OR allowing one to limit output to specific "
60 "controllers. It is\nan error if a controller isn't found.\n");
61 }
62
63 typedef enum gpioadm_controller_list_otype {
64 GPIOADM_CTRL_LIST_CTRLR,
65 GPIOADM_CTRL_LIST_NGPIO,
66 GPIOADM_CTRL_LIST_NDPIO,
67 GPIOADM_CTRL_LIST_PATH,
68 GPIOADM_CTRL_LIST_PROVIDER
69 } gpioadm_controllier_list_otype_t;
70
71 typedef struct gpioadm_controller_list_ofmt {
72 const char *gclo_minor;
73 const char *gclo_devpath;
74 char *gclo_path;
75 uint32_t gclo_ngpio;
76 uint32_t gclo_ndpio;
77 } gpioadm_controller_list_ofmt_t;
78
79 static boolean_t
gpioadm_controller_list_ofmt_cb(ofmt_arg_t * ofarg,char * buf,uint_t buflen)80 gpioadm_controller_list_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
81 {
82 gpioadm_controller_list_ofmt_t *gclo = ofarg->ofmt_cbarg;
83
84 switch (ofarg->ofmt_id) {
85 case GPIOADM_CTRL_LIST_CTRLR:
86 if (strlcpy(buf, gclo->gclo_minor, buflen) >= buflen) {
87 return (B_FALSE);
88 }
89 break;
90 case GPIOADM_CTRL_LIST_NGPIO:
91 if (snprintf(buf, buflen, "%u", gclo->gclo_ngpio) >= buflen) {
92 return (B_FALSE);
93 }
94 break;
95 case GPIOADM_CTRL_LIST_NDPIO:
96 if (snprintf(buf, buflen, "%u", gclo->gclo_ndpio) >= buflen) {
97 return (B_FALSE);
98 }
99 break;
100 case GPIOADM_CTRL_LIST_PROVIDER:
101 if (strlcpy(buf, gclo->gclo_devpath, buflen) >= buflen) {
102 return (B_FALSE);
103 }
104 break;
105 case GPIOADM_CTRL_LIST_PATH:
106 /*
107 * Asking for the minor path can fail. So if we have nothing,
108 * just ignore this.
109 */
110 if (gclo->gclo_path == NULL)
111 break;
112 if (strlcpy(buf, gclo->gclo_path, buflen) >= buflen) {
113 return (B_FALSE);
114 }
115 break;
116 default:
117 abort();
118 }
119 return (B_TRUE);
120 }
121
122 static const char *gpioadm_controller_list_fields = "controller,ngpios,ndpios,"
123 "provider";
124 static const ofmt_field_t gpioadm_controller_list_ofmt[] = {
125 { "CONTROLLER", 16, GPIOADM_CTRL_LIST_CTRLR,
126 gpioadm_controller_list_ofmt_cb },
127 { "NGPIOS", 8, GPIOADM_CTRL_LIST_NGPIO,
128 gpioadm_controller_list_ofmt_cb },
129 { "NDPIOS", 8, GPIOADM_CTRL_LIST_NDPIO,
130 gpioadm_controller_list_ofmt_cb },
131 { "PROVIDER", 42, GPIOADM_CTRL_LIST_PROVIDER,
132 gpioadm_controller_list_ofmt_cb },
133 { "PATH", 42, GPIOADM_CTRL_LIST_PATH,
134 gpioadm_controller_list_ofmt_cb },
135 { NULL, 0, 0, NULL }
136 };
137
138 typedef struct {
139 ofmt_handle_t gcl_ofmt;
140 uint32_t gcl_nprint;
141 bool gcl_err;
142 int gcl_nfilts;
143 char **gcl_filts;
144 bool *gcl_used;
145 } gpioadm_controller_list_t;
146
147 static bool
gpioadm_controller_list_cb(xpio_t * xpio,xpio_ctrl_disc_t * disc,void * arg)148 gpioadm_controller_list_cb(xpio_t *xpio, xpio_ctrl_disc_t *disc, void *arg)
149 {
150 xpio_ctrl_t *ctrl;
151 xpio_ctrl_info_t *info;
152 gpioadm_controller_list_ofmt_t list;
153 gpioadm_controller_list_t *gcl = arg;
154 const char *mname = di_minor_name(disc->xcd_minor);
155
156 if (gcl->gcl_nfilts > 0) {
157 bool match = false;
158
159 for (int i = 0; i < gcl->gcl_nfilts; i++) {
160 if (strcmp(mname, gcl->gcl_filts[i]) == 0) {
161 gcl->gcl_used[i] = true;
162 match = true;
163 break;
164 }
165 }
166
167 if (!match) {
168 return (true);
169 }
170 }
171
172 if (!xpio_ctrl_init(xpio, disc->xcd_minor, &ctrl)) {
173 gpioadm_warn("failed to initialize controller %s", mname);
174 gcl->gcl_err = B_TRUE;
175 return (true);
176 }
177
178 if (!xpio_ctrl_info(ctrl, &info)) {
179 gpioadm_warn("failed to get controller info for %s", mname);
180 xpio_ctrl_fini(ctrl);
181 gcl->gcl_err = B_TRUE;
182 return (true);
183 }
184
185 bzero(&list, sizeof (list));
186 list.gclo_minor = mname;
187 list.gclo_ngpio = xpio_ctrl_info_ngpios(info);
188 list.gclo_ndpio = xpio_ctrl_info_ndpios(info);
189 list.gclo_devpath = xpio_ctrl_info_devpath(info);
190 list.gclo_path = di_devfs_minor_path(disc->xcd_minor);
191
192 ofmt_print(gcl->gcl_ofmt, &list);
193 gcl->gcl_nprint++;
194
195 di_devfs_path_free(list.gclo_path);
196 xpio_ctrl_info_free(info);
197 xpio_ctrl_fini(ctrl);
198 return (true);
199 }
200
201 static int
gpioadm_controller_list(int argc,char * argv[])202 gpioadm_controller_list(int argc, char *argv[])
203 {
204 int c;
205 uint_t flags = 0;
206 boolean_t parse = B_FALSE;
207 const char *fields = NULL;
208 ofmt_status_t oferr;
209 ofmt_handle_t ofmt;
210 gpioadm_controller_list_t gcl;
211
212 (void) memset(&gcl, 0, sizeof (gcl));
213
214 while ((c = getopt(argc, argv, ":Ho:p")) != -1) {
215 switch (c) {
216 case 'H':
217 flags |= OFMT_NOHEADER;
218 break;
219 case 'o':
220 fields = optarg;
221 break;
222 case 'p':
223 parse = B_TRUE;
224 flags |= OFMT_PARSABLE;
225 break;
226 case ':':
227 gpioadm_controller_list_help("option -%c requires an "
228 "argument", optopt);
229 exit(EXIT_USAGE);
230 case '?':
231 gpioadm_controller_list_help("unknown option: -%c",
232 optopt);
233 exit(EXIT_USAGE);
234 }
235 }
236
237 if (parse && fields == NULL) {
238 errx(EXIT_USAGE, "-p requires fields specified with -o");
239 }
240
241 if (fields == NULL) {
242 fields = gpioadm_controller_list_fields;
243 }
244
245 argc -= optind;
246 argv += optind;
247 if (argc > 0) {
248 gcl.gcl_nfilts = argc;
249 gcl.gcl_filts = argv;
250 gcl.gcl_used = calloc(argc, sizeof (bool));
251 if (gcl.gcl_used == NULL) {
252 err(EXIT_FAILURE, "failed to allocate filter tracking "
253 "memory");
254 }
255 }
256 oferr = ofmt_open(fields, gpioadm_controller_list_ofmt, flags, 0,
257 &ofmt);
258 ofmt_check(oferr, parse, ofmt, gpioadm_ofmt_errx, warnx);
259
260 gcl.gcl_nprint = 0;
261 gcl.gcl_err = B_FALSE;
262 gcl.gcl_ofmt = ofmt;
263 xpio_ctrl_discover(gpioadm.gpio_xpio, gpioadm_controller_list_cb,
264 &gcl);
265
266 for (int i = 0; i < gcl.gcl_nfilts; i++) {
267 if (!gcl.gcl_used[i]) {
268 warnx("filter '%s' did not match any controllers",
269 gcl.gcl_filts[i]);
270 gcl.gcl_err = true;
271 }
272 }
273
274 if (gcl.gcl_nprint == 0) {
275 /*
276 * We only bother to warn about no controllers being found when
277 * there are no filters as otherwise the user would have gotten
278 * a message about unmatched filters just above.
279 */
280 if (gcl.gcl_nfilts == 0) {
281 warnx("no gpio controllers found");
282 }
283 gcl.gcl_err = true;
284 }
285
286 return (gcl.gcl_err ? EXIT_FAILURE : EXIT_SUCCESS);
287 }
288
289 static const gpioadm_cmdtab_t gpioadm_cmds_ctrl[] = {
290 { "list", gpioadm_controller_list, gpioadm_controller_list_usage },
291 { NULL, NULL, NULL }
292 };
293
294 int
gpioadm_controller(int argc,char * argv[])295 gpioadm_controller(int argc, char *argv[])
296 {
297 return (gpioadm_walk_tab(gpioadm_cmds_ctrl, argc, argv));
298 }
299
300 void
gpioadm_controller_usage(FILE * f)301 gpioadm_controller_usage(FILE *f)
302 {
303 gpioadm_walk_usage(gpioadm_cmds_ctrl, f);
304 }
305