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 2025 Oxide Computer Company
14 */
15
16 /*
17 * i2cadm controller related operations.
18 */
19
20 #include <stdio.h>
21 #include <stdarg.h>
22 #include <string.h>
23 #include <err.h>
24 #include <sys/sysmacros.h>
25 #include <ofmt.h>
26 #include <sys/ilstr.h>
27 #include <sys/debug.h>
28
29 #include "i2cadm.h"
30
31 /*
32 * Various property conversion routines. These could also potentially be in
33 * libi2c if we find it useful for other consumers.
34 */
35 typedef struct op_map {
36 uint32_t om_op;
37 const char *om_name;
38 } op_map_t;
39
40 static const op_map_t speed_op_map[] = {
41 { I2C_SPEED_STD, "standard" },
42 { I2C_SPEED_FAST, "fast" },
43 { I2C_SPEED_FPLUS, "fast-plus" },
44 { I2C_SPEED_HIGH, "high" },
45 { I2C_SPEED_ULTRA, "ultra" }
46 };
47
48 static const op_map_t type_op_map[] = {
49 { I2C_CTRL_TYPE_I2C, "i2c" },
50 { I2C_CTRL_TYPE_I3C, "i3c" },
51 { I2C_CTRL_TYPE_SMBUS, "smbus" }
52 };
53
54 static const op_map_t smbus_op_map[] = {
55 { SMBUS_PROP_OP_QUICK_COMMAND, "quick" },
56 { SMBUS_PROP_OP_SEND_BYTE, "send-byte", },
57 { SMBUS_PROP_OP_RECV_BYTE, "recv-byte" },
58 { SMBUS_PROP_OP_WRITE_BYTE, "write-byte" },
59 { SMBUS_PROP_OP_READ_BYTE, "read-byte" },
60 { SMBUS_PROP_OP_WRITE_WORD, "write-word" },
61 { SMBUS_PROP_OP_READ_WORD, "read-word" },
62 { SMBUS_PROP_OP_PROCESS_CALL, "process-call" },
63 { SMBUS_PROP_OP_WRITE_BLOCK, "write-block" },
64 { SMBUS_PROP_OP_READ_BLOCK, "read-block" },
65 { SMBUS_PROP_OP_HOST_NOTIFY, "host-notify" },
66 { SMBUS_PROP_OP_BLOCK_PROCESS_CALL, "block-call" },
67 { SMBUS_PROP_OP_WRITE_U32, "write-u32" },
68 { SMBUS_PROP_OP_READ_U32, "read-u32" },
69 { SMBUS_PROP_OP_WRITE_U64, "write-u64" },
70 { SMBUS_PROP_OP_READ_U64, "read-u64" },
71 { SMBUS_PROP_OP_I2C_WRITE_BLOCK, "write-i2c-block" },
72 { SMBUS_PROP_OP_I2C_READ_BLOCK, "read-i2c-block" }
73 };
74
75 static boolean_t
i2cadm_map_to_str_one(uint32_t val,char * buf,uint_t buflen,const op_map_t * map,size_t nents)76 i2cadm_map_to_str_one(uint32_t val, char *buf, uint_t buflen,
77 const op_map_t *map, size_t nents)
78 {
79 if (val == 0) {
80 return (strlcpy(buf, "--", buflen) < buflen);
81 }
82
83 for (size_t i = 0; i < nents; i++) {
84 if (map[i].om_op == val) {
85 return (strlcpy(buf, map[i].om_name, buflen) < buflen);
86 }
87 }
88
89 return (B_FALSE);
90 }
91
92 static boolean_t
i2cadm_map_to_str(uint32_t val,char * buf,uint_t buflen,const op_map_t * map,size_t nents)93 i2cadm_map_to_str(uint32_t val, char *buf, uint_t buflen, const op_map_t *map,
94 size_t nents)
95 {
96 ilstr_t ilstr;
97
98 if (val == 0) {
99 return (strlcpy(buf, "--", buflen) < buflen);
100 }
101
102 ilstr_init_prealloc(&ilstr, buf, buflen);
103
104 for (size_t i = 0; i < nents; i++) {
105 if ((val & map[i].om_op) == 0)
106 continue;
107
108 val &= ~map[i].om_op;
109 if (i > 0) {
110 ilstr_append_char(&ilstr, ',');
111 }
112 ilstr_append_str(&ilstr, map[i].om_name);
113 }
114
115 if (val != 0) {
116 char str[32];
117 (void) snprintf(str, sizeof (str), ",0x%x", val);
118
119 if (ilstr_len(&ilstr) > 0) {
120 ilstr_append_char(&ilstr, ',');
121 }
122 ilstr_append_str(&ilstr, str);
123 }
124
125 ilstr_errno_t err = ilstr_errno(&ilstr);
126 ilstr_fini(&ilstr);
127 return (err == ILSTR_ERROR_OK);
128 }
129
130 static boolean_t
i2cadm_value_print(i2c_prop_info_t * info,uint32_t val,char * buf,uint_t buflen)131 i2cadm_value_print(i2c_prop_info_t *info, uint32_t val, char *buf,
132 uint_t buflen)
133 {
134 uint_t len;
135
136 switch (i2c_prop_info_id(info)) {
137 case I2C_PROP_BUS_SPEED:
138 return (i2cadm_map_to_str_one(val, buf, buflen, speed_op_map,
139 ARRAY_SIZE(speed_op_map)));
140 case I2C_PROP_TYPE:
141 return (i2cadm_map_to_str_one(val, buf, buflen, type_op_map,
142 ARRAY_SIZE(type_op_map)));
143 case SMBUS_PROP_SUP_OPS:
144 return (i2cadm_map_to_str(val, buf, buflen, smbus_op_map,
145 ARRAY_SIZE(smbus_op_map)));
146 default:
147 len = snprintf(buf, buflen, "%u", val);
148 }
149
150 return (len < buflen);
151 }
152
153 static boolean_t
i2cadm_value_print_pos_u32(const i2c_prop_range_t * range,char * buf,uint_t buflen)154 i2cadm_value_print_pos_u32(const i2c_prop_range_t *range, char *buf,
155 uint_t buflen)
156 {
157 ilstr_t ilstr;
158
159 ilstr_init_prealloc(&ilstr, buf, buflen);
160 for (uint32_t i = 0; i < range->ipr_count; i++) {
161 const i2c_prop_u32_range_t *r;
162 char str[64];
163
164 r = &range->ipr_range[i].ipvr_u32;
165 if (r->ipur_min == r->ipur_max) {
166 (void) snprintf(str, sizeof (str), "u", r->ipur_min);
167 } else {
168 (void) snprintf(str, sizeof (str), "%u-%u", r->ipur_min,
169 r->ipur_max);
170 }
171 if (i > 0) {
172 ilstr_append_char(&ilstr, ',');
173 }
174 ilstr_append_str(&ilstr, str);
175 }
176
177 ilstr_errno_t err = ilstr_errno(&ilstr);
178 ilstr_fini(&ilstr);
179 return (err == ILSTR_ERROR_OK);
180 }
181
182 static boolean_t
i2cadm_value_print_pos_bit32(i2c_prop_info_t * info,const i2c_prop_range_t * range,char * buf,uint_t buflen)183 i2cadm_value_print_pos_bit32(i2c_prop_info_t *info,
184 const i2c_prop_range_t *range, char *buf, uint_t buflen)
185 {
186 if (range->ipr_count != 1) {
187 return (B_FALSE);
188 }
189
190 uint32_t val = range->ipr_range[0].ipvr_bit32;
191 switch (i2c_prop_info_id(info)) {
192 case I2C_PROP_BUS_SPEED:
193 return (i2cadm_map_to_str(val, buf, buflen, speed_op_map,
194 ARRAY_SIZE(speed_op_map)));
195 case SMBUS_PROP_SUP_OPS:
196 return (i2cadm_map_to_str(val, buf, buflen, smbus_op_map,
197 ARRAY_SIZE(smbus_op_map)));
198 default:
199 return (snprintf(buf, buflen, "0x%x", val) < buflen);
200 }
201 }
202
203 static boolean_t
i2cadm_value_print_pos(i2c_prop_info_t * info,char * buf,uint_t buflen)204 i2cadm_value_print_pos(i2c_prop_info_t *info, char *buf, uint_t buflen)
205 {
206 uint_t len;
207 const i2c_prop_range_t *range = i2c_prop_info_pos(info);
208
209 if (range == NULL) {
210 if (i2c_err(i2cadm.i2c_hdl) == I2C_ERR_PROP_UNSUP) {
211 return (strlcpy(buf, "--", buflen) < buflen);
212 }
213 return (B_FALSE);
214 }
215
216 if (range->ipr_count == 0) {
217 return (strlcpy(buf, "--", buflen) < buflen);
218 }
219
220 switch (range->ipr_type) {
221 case I2C_PROP_TYPE_U32:
222 return (i2cadm_value_print_pos_u32(range, buf, buflen));
223 case I2C_PROP_TYPE_BIT32:
224 return (i2cadm_value_print_pos_bit32(info, range, buf, buflen));
225 default:
226 return (B_FALSE);
227 }
228
229 return (len <= buflen);
230 }
231
232 static void
i2cadm_controller_prop_get_usage(FILE * f)233 i2cadm_controller_prop_get_usage(FILE *f)
234 {
235 (void) fprintf(stderr, "\ti2cadm controller prop get [-Hp] "
236 "[-o field[,...] <controller> [filter]\n");
237 }
238
239 static void
i2cadm_controller_prop_get_help(const char * fmt,...)240 i2cadm_controller_prop_get_help(const char *fmt, ...)
241 {
242 if (fmt != NULL) {
243 va_list ap;
244
245 va_start(ap, fmt);
246 vwarnx(fmt, ap);
247 va_end(ap);
248 }
249
250 (void) fprintf(stderr, "Usage: i2cadm controller prop get [-H] "
251 "[-o field[,...] [-p]] <controller> [filter...]\n\n");
252 (void) fprintf(stderr, "List properties on the specified controller. "
253 "Each <filter> selects a property\nbased on its name. When "
254 "multiple filters are specified, they are treated like\nan OR. It "
255 "is an error if a filter isn't used.\n\n"
256 "\t-H\t\tomit the column header\n"
257 "\t-o field\toutput fields to print\n"
258 "\t-p\t\tparseable output (requires -o)\n");
259 (void) fprintf(stderr, "\nThe following fields are supported:\n"
260 "\tproperty\tthe name of the property\n"
261 "\tperm\t\tthe property's permissions\n"
262 "\tvalue\t\tthe property's value\n"
263 "\tdefault\t\tthe property's default value\n"
264 "\tpossible\tthe property's possible values\n"
265 "\ttype\t\tthe property's type\n"
266 "\tctrl\t\tthe name of the controller\n"
267 "\tid\t\tthe system id for the property\n");
268 }
269
270 typedef enum {
271 I2CADM_CTRL_PROP_GET_PROP,
272 I2CADM_CTRL_PROP_GET_PERM,
273 I2CADM_CTRL_PROP_GET_VALUE,
274 I2CADM_CTRL_PROP_GET_DEF,
275 I2CADM_CTRL_PROP_GET_POS,
276 I2CADM_CTRL_PROP_GET_TYPE,
277 I2CADM_CTRL_PROP_GET_CTRL,
278 I2CADM_CTRL_PROP_GET_ID
279 } i2cadm_ctrl_prop_get_otype_t;
280
281 typedef struct i2cadm_ctrl_prop_get_ofmt {
282 const char *icpg_ctrl;
283 i2c_prop_info_t *icpg_info;
284 bool icpg_valid;
285 uint32_t icpg_u32;
286 } i2cadm_ctrl_prop_get_ofmt_t;
287
288 static boolean_t
i2cadm_ctrl_prop_get_ofmt_cb(ofmt_arg_t * ofarg,char * buf,uint_t buflen)289 i2cadm_ctrl_prop_get_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
290 {
291 i2cadm_ctrl_prop_get_ofmt_t *arg = ofarg->ofmt_cbarg;
292 size_t len;
293 uint32_t def;
294
295 switch (ofarg->ofmt_id) {
296 case I2CADM_CTRL_PROP_GET_PROP:
297 len = strlcpy(buf, i2c_prop_info_name(arg->icpg_info), buflen);
298 break;
299 case I2CADM_CTRL_PROP_GET_PERM:
300 switch (i2c_prop_info_perm(arg->icpg_info)) {
301 case I2C_PROP_PERM_RO:
302 len = strlcpy(buf, "r-", buflen);
303 break;
304 case I2C_PROP_PERM_RW:
305 len = strlcpy(buf, "rw", buflen);
306 break;
307 default:
308 return (B_FALSE);
309 }
310 break;
311 case I2CADM_CTRL_PROP_GET_VALUE:
312 if (!arg->icpg_valid) {
313 len = strlcpy(buf, "--", buflen);
314 break;
315 }
316
317 return (i2cadm_value_print(arg->icpg_info, arg->icpg_u32, buf,
318 buflen));
319 case I2CADM_CTRL_PROP_GET_DEF:
320 if (!i2c_prop_info_def_u32(arg->icpg_info, &def)) {
321 len = strlcpy(buf, "--", buflen);
322 break;
323 }
324
325 return (i2cadm_value_print(arg->icpg_info, def, buf, buflen));
326 case I2CADM_CTRL_PROP_GET_POS:
327 return (i2cadm_value_print_pos(arg->icpg_info, buf, buflen));
328 case I2CADM_CTRL_PROP_GET_TYPE:
329 switch (i2c_prop_info_type(arg->icpg_info)) {
330 case I2C_PROP_TYPE_U32:
331 len = strlcpy(buf, "u32", buflen);
332 break;
333 case I2C_PROP_TYPE_BIT32:
334 len = strlcpy(buf, "bit32", buflen);
335 break;
336 default:
337 return (B_FALSE);
338 }
339 break;
340 case I2CADM_CTRL_PROP_GET_CTRL:
341 len = strlcpy(buf, arg->icpg_ctrl, buflen);
342 break;
343 case I2CADM_CTRL_PROP_GET_ID:
344 len = snprintf(buf, buflen, "%u",
345 i2c_prop_info_id(arg->icpg_info));
346 break;
347 default:
348 return (B_FALSE);
349 }
350
351 return (len < buflen);
352 }
353
354 static const char *i2cadm_ctrl_prop_get_fields =
355 "property,perm,value,default,possible";
356 static const ofmt_field_t i2cadm_ctrl_prop_get_ofmt[] = {
357 { "PROPERTY", 20, I2CADM_CTRL_PROP_GET_PROP,
358 i2cadm_ctrl_prop_get_ofmt_cb },
359 { "PERM", 6, I2CADM_CTRL_PROP_GET_PERM, i2cadm_ctrl_prop_get_ofmt_cb },
360 { "VALUE", 16, I2CADM_CTRL_PROP_GET_VALUE,
361 i2cadm_ctrl_prop_get_ofmt_cb },
362 { "DEFAULT", 16, I2CADM_CTRL_PROP_GET_DEF,
363 i2cadm_ctrl_prop_get_ofmt_cb },
364 { "POSSIBLE", 16, I2CADM_CTRL_PROP_GET_POS,
365 i2cadm_ctrl_prop_get_ofmt_cb },
366 { "TYPE", 6, I2CADM_CTRL_PROP_GET_TYPE, i2cadm_ctrl_prop_get_ofmt_cb },
367 { "CONTROLLER", 12, I2CADM_CTRL_PROP_GET_CTRL,
368 i2cadm_ctrl_prop_get_ofmt_cb },
369 { "ID", 8, I2CADM_CTRL_PROP_GET_ID, i2cadm_ctrl_prop_get_ofmt_cb },
370 { NULL, 0, 0, NULL }
371 };
372
373 static int
i2cadm_controller_prop_get(int argc,char * argv[])374 i2cadm_controller_prop_get(int argc, char *argv[])
375 {
376 int c, ret = EXIT_SUCCESS;
377 uint_t flags = 0;
378 boolean_t parse = B_FALSE;
379 const char *fields = NULL, *cname;
380 i2c_ctrl_t *ctrl;
381 bool *filts = NULL;
382 ofmt_status_t oferr;
383 ofmt_handle_t ofmt;
384
385 while ((c = getopt(argc, argv, ":Ho:p")) != -1) {
386 switch (c) {
387 case 'H':
388 flags |= OFMT_NOHEADER;
389 break;
390 case 'o':
391 fields = optarg;
392 break;
393 case 'p':
394 parse = B_TRUE;
395 flags |= OFMT_PARSABLE;
396 break;
397 case ':':
398 i2cadm_controller_prop_get_help("option -%c requires "
399 "an argument", optopt);
400 exit(EXIT_USAGE);
401 case '?':
402 i2cadm_controller_prop_get_help("unknown option: -%c",
403 optopt);
404 exit(EXIT_USAGE);
405 }
406 }
407
408 if (parse && fields == NULL) {
409 errx(EXIT_USAGE, "-p requires fields specified with -o");
410 }
411
412 if (!parse) {
413 flags |= OFMT_WRAP;
414 }
415
416 if (fields == NULL) {
417 fields = i2cadm_ctrl_prop_get_fields;
418 }
419
420 argc -= optind;
421 argv += optind;
422 if (argc == 0) {
423 errx(EXIT_FAILURE, "missing required controller");
424 }
425
426 cname = argv[0];
427 argc--;
428 argv++;
429 if (!i2c_ctrl_init_by_path(i2cadm.i2c_hdl, cname, &ctrl)) {
430 i2cadm_fatal("failed to initialize controller %s", cname);
431 }
432
433 if (argc > 0) {
434 filts = calloc(argc, sizeof (bool));
435 if (filts == NULL) {
436 err(EXIT_FAILURE, "failed to allocate memory for "
437 "filter tracking");
438 }
439 }
440
441 oferr = ofmt_open(fields, i2cadm_ctrl_prop_get_ofmt, flags, 0, &ofmt);
442 ofmt_check(oferr, parse, ofmt, i2cadm_ofmt_errx, warnx);
443
444 uint32_t nprops = i2c_ctrl_nprops(ctrl);
445 for (uint32_t i = 0; i < nprops; i++) {
446 i2c_prop_info_t *info;
447 i2cadm_ctrl_prop_get_ofmt_t arg;
448
449 if (!i2c_prop_info(ctrl, i, &info)) {
450 i2cadm_warn("failed to get property %u information", i);
451 ret = EXIT_FAILURE;
452 continue;
453 }
454
455 if (argc > 0) {
456 const char *name = i2c_prop_info_name(info);
457 bool match = false;
458
459 for (int i = 0; i < argc; i++) {
460 if (strcmp(argv[i], name) == 0) {
461 match = true;
462 filts[i] = true;
463 }
464 }
465
466 if (!match) {
467 i2c_prop_info_free(info);
468 continue;
469 }
470 }
471
472 (void) memset(&arg, 0, sizeof (arg));
473 arg.icpg_ctrl = cname;
474 arg.icpg_info = info;
475
476 if (i2c_prop_info_sup(info)) {
477 i2c_prop_type_t type = i2c_prop_info_type(info);
478 if (type == I2C_PROP_TYPE_U32 ||
479 type == I2C_PROP_TYPE_BIT32) {
480 size_t len = sizeof (uint32_t);
481 if (!i2c_prop_get(ctrl, i, &arg.icpg_u32,
482 &len)) {
483 i2cadm_warn("failed to get property "
484 "%s (%u)", i2c_prop_info_name(info),
485 i);
486 ret = EXIT_FAILURE;
487 } else if (len != sizeof (uint32_t)) {
488 warnx("property %s (%u) returned "
489 "unexpected property size of %zu, "
490 "but %zu was expected, unable to "
491 "print value",
492 i2c_prop_info_name(info), i, len,
493 sizeof (uint32_t));
494 ret = EXIT_FAILURE;
495 } else {
496 arg.icpg_valid = true;
497 }
498 } else {
499 warnx("property %s (%u) has unknown type 0x%x, "
500 "cannot get or display value",
501 i2c_prop_info_name(info), i, type);
502 ret = EXIT_FAILURE;
503 }
504 }
505
506 ofmt_print(ofmt, &arg);
507 free(info);
508 }
509
510 for (int i = 0; i < argc; i++) {
511 if (!filts[i]) {
512 warnx("filter '%s' did not match any properties",
513 argv[i]);
514 ret = EXIT_FAILURE;
515 }
516 }
517
518 free(filts);
519 ofmt_close(ofmt);
520 i2c_ctrl_fini(ctrl);
521 return (ret);
522 }
523
524 static void
i2cadm_controller_prop_set_usage(FILE * f)525 i2cadm_controller_prop_set_usage(FILE *f)
526 {
527 (void) fprintf(stderr, "\ti2cadm controller prop set <controller> "
528 "<property>=<value>\n");
529 }
530
531 static int
i2cadm_controller_prop_set(int argc,char * argv[])532 i2cadm_controller_prop_set(int argc, char *argv[])
533 {
534 i2c_ctrl_t *ctrl;
535 i2c_prop_info_t *info;
536 char *prop, *val;
537 size_t buflen = 0;
538 void *buf = NULL;
539
540 if (argc == 0) {
541 errx(EXIT_FAILURE, "missing required controller and property");
542 } else if (argc == 1) {
543 errx(EXIT_FAILURE, "missing required property");
544 } else if (argc > 2) {
545 errx(EXIT_FAILURE, "only one property can be set at a time, "
546 "extraneous arguments start with %s", argv[2]);
547 }
548
549 if (!i2c_ctrl_init_by_path(i2cadm.i2c_hdl, argv[0], &ctrl)) {
550 i2cadm_fatal("failed to initialize controller %s", argv[0]);
551 }
552
553 prop = argv[1];
554 val = strchr(prop, '=');
555 if (val == NULL) {
556 errx(EXIT_FAILURE, "could not parse property name and value "
557 "from %s: missing = separator", argv[1]);
558 }
559 *val = '\0';
560 val++;
561
562 if (!i2c_prop_info_by_name(ctrl, prop, &info)) {
563 i2cadm_fatal("failed to get information for property %s", prop);
564 }
565
566 if (!i2c_prop_info_sup(info)) {
567 errx(EXIT_FAILURE, "controller %s does not support property %s",
568 argv[0], prop);
569 }
570
571 if (i2c_prop_info_perm(info) != I2C_PROP_PERM_RW) {
572 errx(EXIT_FAILURE, "property %s is read-only on controller %s",
573 prop, argv[0]);
574 }
575
576 /*
577 * See if this property is one that we parse via string transformations.
578 */
579 switch (i2c_prop_info_id(info)) {
580 case I2C_PROP_BUS_SPEED:
581 for (size_t i = 0; i < ARRAY_SIZE(speed_op_map); i++) {
582 if (strcmp(val, speed_op_map[i].om_name) == 0) {
583 buflen = sizeof (uint32_t);
584 buf = calloc(1, buflen);
585 if (buf == NULL) {
586 errx(EXIT_FAILURE, "failed to allocate "
587 "%zu bytes of memory to hold %s "
588 "property value", buflen, prop);
589 }
590
591 (void) memcpy(buf, &speed_op_map[i].om_op,
592 sizeof (uint32_t));
593 }
594 }
595 break;
596 default:
597 break;
598 }
599
600 if (buf == NULL) {
601 uint32_t u32;
602 const char *errstr;
603
604 switch (i2c_prop_info_type(info)) {
605 case I2C_PROP_TYPE_U32:
606 case I2C_PROP_TYPE_BIT32:
607 u32 = (uint32_t)strtonumx(val, 0, UINT32_MAX, &errstr,
608 0);
609 if (errstr != NULL) {
610 errx(EXIT_FAILURE, "invalid 32-bit %s property "
611 "values: %s is %s", prop, val, errstr);
612 }
613
614 buflen = sizeof (uint32_t);
615 buf = calloc(1, buflen);
616 if (buf == NULL) {
617 errx(EXIT_FAILURE, "failed to allocate %zu "
618 "bytes of memory to hold %s property value",
619 buflen, prop);
620 }
621
622 (void) memcpy(buf, &u32, sizeof (uint32_t));
623 break;
624 default:
625 errx(EXIT_FAILURE, "unable to parse property %s type "
626 "0x%x", prop, i2c_prop_info_type(info));
627
628 }
629 }
630
631 if (!i2c_prop_set(ctrl, i2c_prop_info_id(info), buf, buflen)) {
632 i2cadm_fatal("failed to set property %s to %s", prop, val);
633 }
634
635 i2c_prop_info_free(info);
636 i2c_ctrl_fini(ctrl);
637 return (EXIT_SUCCESS);
638 }
639
640 static i2cadm_cmdtab_t i2cadm_ctrl_prop_cmds[] = {
641 { "get", i2cadm_controller_prop_get, i2cadm_controller_prop_get_usage },
642 { "set", i2cadm_controller_prop_set, i2cadm_controller_prop_set_usage }
643 };
644
645 static int
i2cadm_controller_prop(int argc,char * argv[])646 i2cadm_controller_prop(int argc, char *argv[])
647 {
648 return (i2cadm_walk_tab(i2cadm_ctrl_prop_cmds,
649 ARRAY_SIZE(i2cadm_ctrl_prop_cmds), argc, argv));
650 }
651
652 static void
i2cadm_controller_prop_usage(FILE * f)653 i2cadm_controller_prop_usage(FILE *f)
654 {
655 i2cadm_walk_usage(i2cadm_ctrl_prop_cmds,
656 ARRAY_SIZE(i2cadm_ctrl_prop_cmds), f);
657 }
658
659 static void
i2cadm_controller_list_usage(FILE * f)660 i2cadm_controller_list_usage(FILE *f)
661 {
662 (void) fprintf(f, "\ti2cadm controller list [-H] [-o field,[...] [-p]] "
663 "[filter]\n");
664 }
665
666 static void
i2cadm_controller_list_help(const char * fmt,...)667 i2cadm_controller_list_help(const char *fmt, ...)
668 {
669 if (fmt != NULL) {
670 va_list ap;
671
672 va_start(ap, fmt);
673 vwarnx(fmt, ap);
674 va_end(ap);
675 }
676
677 (void) fprintf(stderr, "Usage: i2cadm controller list [-H] "
678 "[-o field[,...] [-p]] [filter...]\n\n");
679 (void) fprintf(stderr, "List I2C Controllers. Each <filter> selects a "
680 "set of controllers to show and\ncan be a controller or driver "
681 "name. When multiple filters are specified, they\nare treated like "
682 "an OR. It is an error if a filter isn't used.\n\n"
683 "\t-H\t\tomit the column header\n"
684 "\t-o field\toutput fields to print\n"
685 "\t-p\t\tparseable output (requires -o)\n");
686 (void) fprintf(stderr, "\nThe following fields are supported:\n"
687 "\tname\t\tthe controller's name\n"
688 "\ttype\t\tthe controller's type (e.g. i2c, smbus)\n"
689 "\tspeed\t\tthe controller's current speed\n"
690 "\tdriver\t\tthe name of the driver for the controller\n"
691 "\tinstance\tthe driver instance for the controller\n"
692 "\tprovider\tthe /devices path of the provider\n");
693 }
694
695 typedef enum {
696 I2CADM_CTRL_LIST_NAME,
697 I2CADM_CTRL_LIST_TYPE,
698 I2CADM_CTRL_LIST_SPEED,
699 I2CADM_CTRL_LIST_NPORTS,
700 I2CADM_CTRL_LIST_DRIVER,
701 I2CADM_CTRL_LIST_INSTANCE,
702 I2CADM_CTRL_LIST_PROVIDER
703 } i2cadm_ctrl_list_otype_t;
704
705 typedef struct i2cadm_ctrl_list_ofmt {
706 di_node_t icl_nexus;
707 di_node_t icl_drv;
708 i2c_ctrl_t *icl_ctrl;
709 uint32_t icl_speed;
710 uint32_t icl_type;
711 uint32_t icl_nports;
712 } i2cadm_ctrl_list_ofmt_t;
713
714 static boolean_t
i2cadm_ctrl_list_ofmt_cb(ofmt_arg_t * ofarg,char * buf,uint_t buflen)715 i2cadm_ctrl_list_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
716 {
717 i2cadm_ctrl_list_ofmt_t *arg = ofarg->ofmt_cbarg;
718 size_t len;
719
720 switch (ofarg->ofmt_id) {
721 case I2CADM_CTRL_LIST_NAME:
722 len = strlcat(buf, di_bus_addr(arg->icl_nexus), buflen);
723 break;
724 case I2CADM_CTRL_LIST_TYPE:
725 return (i2cadm_map_to_str_one(arg->icl_type, buf, buflen,
726 type_op_map, ARRAY_SIZE(type_op_map)));
727 case I2CADM_CTRL_LIST_SPEED:
728 return (i2cadm_map_to_str_one(arg->icl_speed, buf, buflen,
729 speed_op_map, ARRAY_SIZE(speed_op_map)));
730 case I2CADM_CTRL_LIST_NPORTS:
731 len = snprintf(buf, buflen, "%u", arg->icl_nports);
732 break;
733 case I2CADM_CTRL_LIST_DRIVER:
734 len = strlcat(buf, di_driver_name(arg->icl_drv), buflen);
735 break;
736 case I2CADM_CTRL_LIST_INSTANCE:
737 len = snprintf(buf, buflen, "%s%d",
738 di_driver_name(arg->icl_drv), di_instance(arg->icl_drv));
739 break;
740 case I2CADM_CTRL_LIST_PROVIDER:
741 len = strlcat(buf, i2c_ctrl_path(arg->icl_ctrl), buflen);
742 break;
743 default:
744 return (B_FALSE);
745 }
746
747 return (len < buflen);
748 }
749
750 static const char *i2cadm_ctrl_list_fields = "name,type,speed,nports,provider";
751 static const ofmt_field_t i2cadm_ctrl_list_ofmt[] = {
752 { "NAME", 12, I2CADM_CTRL_LIST_NAME, i2cadm_ctrl_list_ofmt_cb },
753 { "TYPE", 10, I2CADM_CTRL_LIST_TYPE, i2cadm_ctrl_list_ofmt_cb },
754 { "SPEED", 12, I2CADM_CTRL_LIST_SPEED, i2cadm_ctrl_list_ofmt_cb },
755 { "NPORTS", 8, I2CADM_CTRL_LIST_NPORTS, i2cadm_ctrl_list_ofmt_cb },
756 { "DRIVER", 10, I2CADM_CTRL_LIST_DRIVER, i2cadm_ctrl_list_ofmt_cb },
757 { "INSTANCE", 16, I2CADM_CTRL_LIST_INSTANCE, i2cadm_ctrl_list_ofmt_cb },
758 { "PROVIDER", 40, I2CADM_CTRL_LIST_PROVIDER, i2cadm_ctrl_list_ofmt_cb },
759 { NULL, 0, 0, NULL }
760 };
761
762 static int
i2cadm_controller_list(int argc,char * argv[])763 i2cadm_controller_list(int argc, char *argv[])
764 {
765 int c, ret = EXIT_SUCCESS;
766 uint_t flags = 0;
767 boolean_t parse = B_FALSE;
768 const char *fields = NULL;
769 bool *filts = NULL, print = false;
770 ofmt_status_t oferr;
771 ofmt_handle_t ofmt;
772 i2c_ctrl_iter_t *iter;
773 i2c_iter_t iret;
774 const i2c_ctrl_disc_t *disc;
775
776 while ((c = getopt(argc, argv, ":Ho:p")) != -1) {
777 switch (c) {
778 case 'H':
779 flags |= OFMT_NOHEADER;
780 break;
781 case 'o':
782 fields = optarg;
783 break;
784 case 'p':
785 parse = B_TRUE;
786 flags |= OFMT_PARSABLE;
787 break;
788 case ':':
789 i2cadm_controller_list_help("option -%c requires an "
790 "argument", optopt);
791 exit(EXIT_USAGE);
792 case '?':
793 i2cadm_controller_list_help("unknown option: -%c",
794 optopt);
795 exit(EXIT_USAGE);
796 }
797 }
798
799 if (parse && fields == NULL) {
800 errx(EXIT_USAGE, "-p requires fields specified with -o");
801 }
802
803 if (!parse) {
804 flags |= OFMT_WRAP;
805 }
806
807 if (fields == NULL) {
808 fields = i2cadm_ctrl_list_fields;
809 }
810
811 argc -= optind;
812 argv += optind;
813
814 if (argc > 0) {
815 filts = calloc(argc, sizeof (bool));
816 if (filts == NULL) {
817 err(EXIT_FAILURE, "failed to allocate memory for "
818 "filter tracking");
819 }
820 }
821
822 oferr = ofmt_open(fields, i2cadm_ctrl_list_ofmt, flags, 0, &ofmt);
823 ofmt_check(oferr, parse, ofmt, i2cadm_ofmt_errx, warnx);
824
825 if (!i2c_ctrl_discover_init(i2cadm.i2c_hdl, &iter)) {
826 i2cadm_fatal("failed to initialize controller walk");
827 }
828
829 while ((iret = i2c_ctrl_discover_step(iter, &disc)) == I2C_ITER_VALID) {
830 i2cadm_ctrl_list_ofmt_t arg;
831 i2c_ctrl_t *ctrl;
832
833 (void) memset(&arg, 0, sizeof (arg));
834 arg.icl_nexus = i2c_ctrl_disc_devi(disc);
835 arg.icl_drv = di_parent_node(arg.icl_nexus);
836
837 if (argc > 0) {
838 const char *name = di_bus_addr(arg.icl_nexus);
839 const char *drv = di_driver_name(arg.icl_drv);
840 bool match = false;
841
842 for (int i = 0; i < argc; i++) {
843 if (strcmp(argv[i], name) == 0 ||
844 strcmp(argv[i], drv) == 0) {
845 match = true;
846 filts[i] = true;
847 }
848 }
849
850 if (!match) {
851 continue;
852 }
853 }
854
855 if (!i2c_ctrl_init(i2cadm.i2c_hdl, arg.icl_nexus, &ctrl)) {
856 i2cadm_warn("failed to initialize controller %s",
857 di_bus_addr(arg.icl_nexus));
858 continue;
859 }
860
861 size_t len = sizeof (uint32_t);
862 if (!i2c_prop_get(ctrl, I2C_PROP_BUS_SPEED, &arg.icl_speed,
863 &len)) {
864 i2cadm_warn("failed to get controller %s speed",
865 di_bus_addr(arg.icl_nexus));
866 i2c_ctrl_fini(ctrl);
867 continue;
868 }
869 VERIFY3U(len, ==, sizeof (uint32_t));
870
871 if (!i2c_prop_get(ctrl, I2C_PROP_TYPE, &arg.icl_type, &len)) {
872 i2cadm_warn("failed to get controller %s type",
873 di_bus_addr(arg.icl_nexus));
874 i2c_ctrl_fini(ctrl);
875 continue;
876 }
877 VERIFY3U(len, ==, sizeof (uint32_t));
878
879 if (!i2c_prop_get(ctrl, I2C_PROP_NPORTS, &arg.icl_nports,
880 &len)) {
881 i2cadm_warn("failed to get controller %s ports",
882 di_bus_addr(arg.icl_nexus));
883 i2c_ctrl_fini(ctrl);
884 continue;
885 }
886 VERIFY3U(len, ==, sizeof (uint32_t));
887
888 arg.icl_ctrl = ctrl;
889 ofmt_print(ofmt, &arg);
890 i2c_ctrl_fini(ctrl);
891 print = true;
892 }
893
894 if (iret == I2C_ITER_ERROR) {
895 i2cadm_warn("failed to iterate controllers");
896 ret = EXIT_FAILURE;
897 }
898
899 for (int i = 0; i < argc; i++) {
900 if (!filts[i]) {
901 warnx("filter '%s' did not match any controllers",
902 argv[i]);
903 ret = EXIT_FAILURE;
904 }
905 }
906
907 if (!print && argc == 0) {
908 warnx("no controllers found");
909 ret = EXIT_FAILURE;
910 }
911
912 free(filts);
913 ofmt_close(ofmt);
914 i2c_ctrl_discover_fini(iter);
915 return (ret);
916 }
917
918 static i2cadm_cmdtab_t i2cadm_ctrl_cmds[] = {
919 { "list", i2cadm_controller_list, i2cadm_controller_list_usage },
920 { "prop", i2cadm_controller_prop, i2cadm_controller_prop_usage }
921 };
922
923 int
i2cadm_controller(int argc,char * argv[])924 i2cadm_controller(int argc, char *argv[])
925 {
926 return (i2cadm_walk_tab(i2cadm_ctrl_cmds, ARRAY_SIZE(i2cadm_ctrl_cmds),
927 argc, argv));
928 }
929
930 void
i2cadm_controller_usage(FILE * f)931 i2cadm_controller_usage(FILE *f)
932 {
933 i2cadm_walk_usage(i2cadm_ctrl_cmds, ARRAY_SIZE(i2cadm_ctrl_cmds), f);
934 }
935