xref: /illumos-gate/usr/src/cmd/i2cadm/i2cadm_controller.c (revision 32002227574cf0a435dc03de622191ca53724f0a)
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