xref: /illumos-gate/usr/src/cmd/i2cadm/i2cadm_device.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 device related operations.
18  */
19 
20 #include <err.h>
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <string.h>
24 #include <sys/sysmacros.h>
25 #include <ofmt.h>
26 
27 #include "i2cadm.h"
28 
29 /*
30  * Attempt to apply filters. We accept the following filters that are trying to
31  * match devices:
32  *
33  *  - Matching a specific address
34  *  - Matching a portion of a device path
35  *  - Matching a specific driver or instance
36  *  - Matching a node name
37  *
38  * These filters are shared between both device list and device addrs.
39  */
40 static bool
i2cadm_device_filt(const i2c_dev_info_t * info,int nfilts,char ** filts,bool * used)41 i2cadm_device_filt(const i2c_dev_info_t *info, int nfilts, char **filts,
42     bool *used)
43 {
44 	bool match = false;
45 	char inst[128] = { '\0' }, addr[64] = { '\0' };
46 	const char *name, *path, *driver;
47 	size_t pathlen;
48 
49 	if (nfilts == 0) {
50 		return (true);
51 	}
52 
53 	name = i2c_device_info_name(info);
54 	path = i2c_device_info_path(info);
55 	pathlen = strlen(path);
56 	driver = i2c_device_info_driver(info);
57 	if (i2c_device_info_instance(info) != -1 && driver != NULL) {
58 		(void) snprintf(inst, sizeof (inst), "%s%d", driver,
59 		    i2c_device_info_instance(info));
60 	}
61 
62 	const i2c_addr_t *ia = i2c_device_info_addr_primary(info);
63 	if (!i2c_addr_to_string(i2cadm.i2c_hdl, ia, addr, sizeof (addr))) {
64 		addr[0] = '\0';
65 	}
66 
67 	/*
68 	 * Note, we have to go through all the filters to see if they match as
69 	 * someone could have specified something more than once.
70 	 */
71 	for (int i = 0; i < nfilts; i++) {
72 		if (strcmp(filts[i], name) == 0) {
73 			used[i] = true;
74 			match = true;
75 			continue;
76 		}
77 
78 		if (addr[0] != '\0' && strcmp(filts[i], addr) == 0) {
79 			used[i] = true;
80 			match = true;
81 			continue;
82 		}
83 
84 		if (driver != NULL && strcmp(filts[i], driver) == 0) {
85 			used[i] = true;
86 			match = true;
87 			continue;
88 		}
89 
90 		if (inst[0] != '\0' && strcmp(filts[i], inst) == 0) {
91 			used[i] = true;
92 			match = true;
93 			continue;
94 		}
95 
96 		if (strcmp(path, filts[i]) == 0) {
97 			used[i] = true;
98 			match = true;
99 			continue;
100 		}
101 
102 		size_t len = strlen(filts[i]);
103 		if (len < pathlen && strncmp(path, filts[i], len) == 0) {
104 			used[i] = true;
105 			match = true;
106 			continue;
107 		}
108 	}
109 
110 	return (match);
111 }
112 
113 static int
i2cadm_device_iter(ofmt_handle_t ofmt,int argc,char * argv[],bool * filts,void (* func)(ofmt_handle_t,const i2c_dev_info_t *))114 i2cadm_device_iter(ofmt_handle_t ofmt, int argc, char *argv[], bool *filts,
115     void (*func)(ofmt_handle_t, const i2c_dev_info_t *))
116 {
117 	int ret = EXIT_SUCCESS;
118 	bool print = false;
119 	const i2c_dev_disc_t *disc;
120 	i2c_dev_iter_t *iter;
121 	i2c_iter_t iret;
122 
123 	if (!i2c_device_discover_init(i2cadm.i2c_hdl, &iter)) {
124 		i2cadm_fatal("failed to initialize device discovery");
125 	}
126 
127 	while ((iret = i2c_device_discover_step(iter, &disc)) ==
128 	    I2C_ITER_VALID) {
129 		i2c_dev_info_t *info;
130 
131 		if (!i2c_device_info_snap(i2cadm.i2c_hdl,
132 		    i2c_device_disc_devi(disc), &info)) {
133 			i2cadm_warn("failed to get device information for "
134 			    "%s", i2c_device_disc_path(disc));
135 			ret = EXIT_FAILURE;
136 			continue;
137 		}
138 
139 		if (!i2cadm_device_filt(info, argc, argv, filts)) {
140 			i2c_device_info_free(info);
141 			continue;
142 		}
143 
144 		func(ofmt, info);
145 		i2c_device_info_free(info);
146 		print = true;
147 	}
148 
149 	if (iret == I2C_ITER_ERROR) {
150 		i2cadm_warn("failed to discover devices");
151 		ret = EXIT_FAILURE;
152 	}
153 
154 	for (int i = 0; i < argc; i++) {
155 		if (!filts[i]) {
156 			warnx("filter '%s' did not match any devices",
157 			    argv[i]);
158 			ret = EXIT_FAILURE;
159 		}
160 	}
161 
162 	if (!print && argc == 0) {
163 		warnx("no I2C devices found");
164 		ret = EXIT_FAILURE;
165 	}
166 
167 	free(filts);
168 	ofmt_close(ofmt);
169 	i2c_device_discover_fini(iter);
170 
171 	return (ret);
172 }
173 
174 static void
i2cadm_device_addrs_usage(FILE * f)175 i2cadm_device_addrs_usage(FILE *f)
176 {
177 	(void) fprintf(f, "\ti2cadm device addrs [-H] [-o field,[...] [-p]] "
178 	    "[filter]\n");
179 }
180 
181 static void
i2cadm_device_addrs_help(const char * fmt,...)182 i2cadm_device_addrs_help(const char *fmt, ...)
183 {
184 	if (fmt != NULL) {
185 		va_list ap;
186 
187 		va_start(ap, fmt);
188 		vwarnx(fmt, ap);
189 		va_end(ap);
190 	}
191 
192 	(void) fprintf(stderr, "Usage:  i2cadm device addrs [-H] "
193 	    "[-o field[,...] [-p]] [filters]\n\n");
194 	(void) fprintf(stderr, "List addresses assigned to devices and their "
195 	    "source. Each <filter> selects\ndevices based upon its address, "
196 	    "the device's name, the driver's name, the\ndriver's instance, or "
197 	    "the I2C path. Multiple filters are treated as an OR. It\n is an "
198 	    "error if a filter isn't used.\n\n"
199 	    "\t-H\t\tomit the column header\n"
200 	    "\t-o field\toutput fields to print\n"
201 	    "\t-p\t\tparseable output (requires -o)\n");
202 	(void) fprintf(stderr, "\nThe following fields are supported:\n"
203 	    "\tpath\t\tthe device path\n"
204 	    "\ttype\t\tthe address's type\n"
205 	    "\taddr\t\tthe specific address\n"
206 	    "\tsource\tindicates where the address came from\n");
207 }
208 
209 typedef enum {
210 	I2CADM_DEVICE_ADDRS_PATH,
211 	I2CADM_DEVICE_ADDRS_TYPE,
212 	I2CADM_DEVICE_ADDRS_ADDR,
213 	I2CADM_DEVICE_ADDRS_SOURCE
214 } i2cadm_device_addrs_otype_t;
215 
216 typedef struct i2cadm_device_addrs_ofmt {
217 	const i2c_dev_info_t *idoa_info;
218 	const i2c_addr_t *idoa_addr;
219 	i2c_addr_source_t idoa_source;
220 } i2cadm_device_addrs_ofmt_t;
221 
222 static boolean_t
i2cadm_device_addrs_ofmt_cb(ofmt_arg_t * ofarg,char * buf,uint_t buflen)223 i2cadm_device_addrs_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
224 {
225 	const i2cadm_device_addrs_ofmt_t *arg = ofarg->ofmt_cbarg;
226 	size_t len;
227 
228 	switch (ofarg->ofmt_id) {
229 	case I2CADM_DEVICE_ADDRS_PATH:
230 		len = strlcpy(buf, i2c_device_info_path(arg->idoa_info),
231 		    buflen);
232 		break;
233 	case I2CADM_DEVICE_ADDRS_TYPE:
234 		if (arg->idoa_addr->ia_type == I2C_ADDR_7BIT) {
235 			len = strlcpy(buf, "7-bit", buflen);
236 		} else if (arg->idoa_addr->ia_type == I2C_ADDR_10BIT) {
237 			len = strlcpy(buf, "10-bit", buflen);
238 		} else {
239 			len = snprintf(buf, buflen, "unknown (0x%x)",
240 			    arg->idoa_addr->ia_type);
241 		}
242 		break;
243 	case I2CADM_DEVICE_ADDRS_ADDR:
244 		len = snprintf(buf, buflen, "0x%02x", arg->idoa_addr->ia_addr);
245 		break;
246 	case I2CADM_DEVICE_ADDRS_SOURCE:
247 		switch (arg->idoa_source) {
248 		case I2C_ADDR_SOURCE_REG:
249 			len = strlcpy(buf, "platform", buflen);
250 			break;
251 		case I2C_ADDR_SOURCE_CLAIMED:
252 			len = strlcpy(buf, "claimed", buflen);
253 			break;
254 		case I2C_ADDR_SOURCE_SHARED:
255 			len = strlcpy(buf, "shared", buflen);
256 			break;
257 		default:
258 			len = snprintf(buf, buflen, "unknown (0x%x)",
259 			    arg->idoa_source);
260 		}
261 		break;
262 	default:
263 		return (B_FALSE);
264 	}
265 
266 	return (len < buflen);
267 }
268 
269 static const char *i2cadm_device_addrs_fields = "path,type,addr,source";
270 static const ofmt_field_t i2cadm_device_addrs_ofmt[] = {
271 	{ "PATH", 40, I2CADM_DEVICE_ADDRS_PATH, i2cadm_device_addrs_ofmt_cb },
272 	{ "TYPE", 10, I2CADM_DEVICE_ADDRS_TYPE, i2cadm_device_addrs_ofmt_cb },
273 	{ "ADDR", 10, I2CADM_DEVICE_ADDRS_ADDR, i2cadm_device_addrs_ofmt_cb },
274 	{ "SOURCE", 16, I2CADM_DEVICE_ADDRS_SOURCE,
275 	    i2cadm_device_addrs_ofmt_cb },
276 	{ NULL, 0, 0, NULL }
277 };
278 
279 static void
i2cadm_device_addrs_cb(ofmt_handle_t ofmt,const i2c_dev_info_t * info)280 i2cadm_device_addrs_cb(ofmt_handle_t ofmt, const i2c_dev_info_t *info)
281 {
282 	for (uint32_t i = 0; i < i2c_device_info_naddrs(info); i++) {
283 		i2cadm_device_addrs_ofmt_t arg;
284 
285 		arg.idoa_info = info;
286 		arg.idoa_addr = i2c_device_info_addr(info, i);
287 		arg.idoa_source = i2c_device_info_addr_source(info, i);
288 		ofmt_print(ofmt, &arg);
289 	}
290 }
291 
292 static int
i2cadm_device_addrs(int argc,char * argv[])293 i2cadm_device_addrs(int argc, char *argv[])
294 {
295 	int c;
296 	uint_t flags = 0;
297 	boolean_t parse = B_FALSE;
298 	const char *fields = NULL;
299 	bool *filts = NULL;
300 	ofmt_status_t oferr;
301 	ofmt_handle_t ofmt;
302 
303 	while ((c = getopt(argc, argv, ":Ho:p")) != -1) {
304 		switch (c) {
305 		case 'H':
306 			flags |= OFMT_NOHEADER;
307 			break;
308 		case 'o':
309 			fields = optarg;
310 			break;
311 		case 'p':
312 			parse = B_TRUE;
313 			flags |= OFMT_PARSABLE;
314 			break;
315 		case ':':
316 			i2cadm_device_addrs_help("option -%c requires an "
317 			    "argument", optopt);
318 			exit(EXIT_USAGE);
319 		case '?':
320 			i2cadm_device_addrs_help("unknown option: -%c",
321 			    optopt);
322 			exit(EXIT_USAGE);
323 		}
324 	}
325 
326 	if (parse && fields == NULL) {
327 		errx(EXIT_USAGE, "-p requires fields specified with -o");
328 	}
329 
330 	if (!parse) {
331 		flags |= OFMT_WRAP;
332 	}
333 
334 	if (fields == NULL) {
335 		fields = i2cadm_device_addrs_fields;
336 	}
337 
338 	argc -= optind;
339 	argv += optind;
340 
341 	if (argc > 0) {
342 		filts = calloc(argc, sizeof (bool));
343 		if (filts == NULL) {
344 			err(EXIT_FAILURE, "failed to allocate memory for "
345 			    "filter tracking");
346 		}
347 	}
348 
349 	oferr = ofmt_open(fields, i2cadm_device_addrs_ofmt, flags, 0, &ofmt);
350 	ofmt_check(oferr, parse, ofmt, i2cadm_ofmt_errx, warnx);
351 
352 	return (i2cadm_device_iter(ofmt, argc, argv, filts,
353 	    i2cadm_device_addrs_cb));
354 }
355 
356 static void
i2cadm_device_list_usage(FILE * f)357 i2cadm_device_list_usage(FILE *f)
358 {
359 	(void) fprintf(f, "\ti2cadm device list [-H] [-o field,[...] [-p]] "
360 	    "[filter]\n");
361 }
362 
363 static void
i2cadm_device_list_help(const char * fmt,...)364 i2cadm_device_list_help(const char *fmt, ...)
365 {
366 	if (fmt != NULL) {
367 		va_list ap;
368 
369 		va_start(ap, fmt);
370 		vwarnx(fmt, ap);
371 		va_end(ap);
372 	}
373 
374 	(void) fprintf(stderr, "Usage:  i2cadm device list [-H] "
375 	    "[-o field[,...] [-p]] [filter...]\n\n");
376 	(void) fprintf(stderr, "List I2C devices in the system. Each <filter> "
377 	    "selects devices based upon its\naddress, the device's name, the "
378 	    "driver's name, the driver's instance, or the\nI2C path. Multiple "
379 	    "filters are treated as an OR. It is an error if a filter\nisn't "
380 	    "used.\n\n"
381 	    "\t-H\t\tomit the column header\n"
382 	    "\t-o field\toutput fields to print\n"
383 	    "\t-p\t\tparseable output (requires -o)\n");
384 	(void) fprintf(stderr, "\nThe following fields are supported:\n"
385 	    "\tname\t\tthe name of the device\n"
386 	    "\taddr\t\tthe primary address of the device\n"
387 	    "\tinstance\tthe driver instance of the device\n"
388 	    "\tpath\t\tthe I2C path of the device\n");
389 }
390 
391 typedef enum {
392 	I2CADM_DEVICE_LIST_NAME,
393 	I2CADM_DEVICE_LIST_ADDR,
394 	I2CADM_DEVICE_LIST_INSTANCE,
395 	I2CADM_DEVICE_LIST_PATH
396 } i2cadm_device_list_otype_t;
397 
398 static boolean_t
i2cadm_device_list_ofmt_cb(ofmt_arg_t * ofarg,char * buf,uint_t buflen)399 i2cadm_device_list_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
400 {
401 	const i2c_dev_info_t *info = ofarg->ofmt_cbarg;
402 	size_t len;
403 	const i2c_addr_t *addr;
404 
405 	switch (ofarg->ofmt_id) {
406 	case I2CADM_DEVICE_LIST_NAME:
407 		len = strlcpy(buf, i2c_device_info_name(info), buflen);
408 		break;
409 	case I2CADM_DEVICE_LIST_ADDR:
410 		addr = i2c_device_info_addr_primary(info);
411 		if (!i2c_addr_to_string(i2cadm.i2c_hdl, addr, buf, buflen)) {
412 			return (B_FALSE);
413 		}
414 
415 		return (B_TRUE);
416 	case I2CADM_DEVICE_LIST_INSTANCE:
417 		if (i2c_device_info_driver(info) != NULL &&
418 		    i2c_device_info_instance(info) != -1) {
419 			len = snprintf(buf, buflen, "%s%d",
420 			    i2c_device_info_driver(info),
421 			    i2c_device_info_instance(info));
422 		} else {
423 			len = strlcpy(buf, "--", buflen);
424 		}
425 		break;
426 	case I2CADM_DEVICE_LIST_PATH:
427 		len = strlcpy(buf, i2c_device_info_path(info), buflen);
428 		break;
429 	default:
430 		return (B_FALSE);
431 	}
432 
433 	return (len < buflen);
434 }
435 
436 static const char *i2cadm_device_list_fields = "name,addr,instance,path";
437 static const ofmt_field_t i2cadm_device_list_ofmt[] = {
438 	{ "NAME", 12, I2CADM_DEVICE_LIST_NAME, i2cadm_device_list_ofmt_cb },
439 	{ "ADDR", 12, I2CADM_DEVICE_LIST_ADDR, i2cadm_device_list_ofmt_cb },
440 	{ "INSTANCE", 16, I2CADM_DEVICE_LIST_INSTANCE,
441 	    i2cadm_device_list_ofmt_cb },
442 	{ "PATH", 40, I2CADM_DEVICE_LIST_PATH, i2cadm_device_list_ofmt_cb },
443 	{ NULL, 0, 0, NULL }
444 };
445 
446 static void
i2cadm_device_list_cb(ofmt_handle_t ofmt,const i2c_dev_info_t * info)447 i2cadm_device_list_cb(ofmt_handle_t ofmt, const i2c_dev_info_t *info)
448 {
449 	ofmt_print(ofmt, (void *)info);
450 }
451 
452 static int
i2cadm_device_list(int argc,char * argv[])453 i2cadm_device_list(int argc, char *argv[])
454 {
455 	int c;
456 	uint_t flags = 0;
457 	boolean_t parse = B_FALSE;
458 	const char *fields = NULL;
459 	bool *filts = NULL;
460 	ofmt_status_t oferr;
461 	ofmt_handle_t ofmt;
462 
463 	while ((c = getopt(argc, argv, ":Ho:p")) != -1) {
464 		switch (c) {
465 		case 'H':
466 			flags |= OFMT_NOHEADER;
467 			break;
468 		case 'o':
469 			fields = optarg;
470 			break;
471 		case 'p':
472 			parse = B_TRUE;
473 			flags |= OFMT_PARSABLE;
474 			break;
475 		case ':':
476 			i2cadm_device_list_help("option -%c requires an "
477 			    "argument", optopt);
478 			exit(EXIT_USAGE);
479 		case '?':
480 			i2cadm_device_list_help("unknown option: -%c",
481 			    optopt);
482 			exit(EXIT_USAGE);
483 		}
484 	}
485 
486 	if (parse && fields == NULL) {
487 		errx(EXIT_USAGE, "-p requires fields specified with -o");
488 	}
489 
490 	if (!parse) {
491 		flags |= OFMT_WRAP;
492 	}
493 
494 	if (fields == NULL) {
495 		fields = i2cadm_device_list_fields;
496 	}
497 
498 	argc -= optind;
499 	argv += optind;
500 
501 	if (argc > 0) {
502 		filts = calloc(argc, sizeof (bool));
503 		if (filts == NULL) {
504 			err(EXIT_FAILURE, "failed to allocate memory for "
505 			    "filter tracking");
506 		}
507 	}
508 
509 	oferr = ofmt_open(fields, i2cadm_device_list_ofmt, flags, 0, &ofmt);
510 	ofmt_check(oferr, parse, ofmt, i2cadm_ofmt_errx, warnx);
511 
512 	return (i2cadm_device_iter(ofmt, argc, argv, filts,
513 	    i2cadm_device_list_cb));
514 }
515 
516 static void
i2cadm_device_add_usage(FILE * f)517 i2cadm_device_add_usage(FILE *f)
518 {
519 	(void) fprintf(f, "\ti2cadm device add [-c compat] port name addr\n");
520 }
521 
522 static void
i2cadm_device_add_help(const char * fmt,...)523 i2cadm_device_add_help(const char *fmt, ...)
524 {
525 	if (fmt != NULL) {
526 		va_list ap;
527 
528 		va_start(ap, fmt);
529 		vwarnx(fmt, ap);
530 		va_end(ap);
531 	}
532 
533 	(void) fprintf(stderr, "Usage:  i2cadm device add [-c comapt] "
534 	    "port name addr\n\n");
535 	(void) fprintf(stderr, "Inform the system of a new I2C device with the "
536 	    "specified address. The device\nwill be inserted under the given "
537 	    "bus (or multiplexer port if specified). The\naddress must be "
538 	    "unique on its multiplexor (if applicable) and bus.\n\n"
539 	    "\t-c compat\tAdd the driver compatible entry to the device. This "
540 	    "may\n\t\t\tbe specified multiple times. They will be added to "
541 	    "the\n\t\t\tdevice in the order specified.\n");
542 }
543 
544 static int
i2cadm_device_add(int argc,char * argv[])545 i2cadm_device_add(int argc, char *argv[])
546 {
547 	int c;
548 	char **compat = NULL;
549 	size_t ncompat = 0, nalloc = 0;
550 	i2c_port_t *port;
551 	i2c_addr_t addr;
552 	i2c_dev_add_req_t *req;
553 
554 	while ((c = getopt(argc, argv, ":c")) != -1) {
555 		switch (c) {
556 		case 'c':
557 			if (ncompat == nalloc) {
558 				nalloc += 8;
559 				compat = recallocarray(compat, ncompat, nalloc,
560 				    sizeof (char *));
561 				if (compat == NULL) {
562 					err(EXIT_FAILURE, "failed to allocate "
563 					    "memory for %zu compatible array "
564 					    "entries", nalloc);
565 				}
566 			}
567 			compat[ncompat] = optarg;
568 			ncompat++;
569 			break;
570 		case ':':
571 			i2cadm_device_add_help("option -%c requires an "
572 			    "argument", optopt);
573 			exit(EXIT_USAGE);
574 		case '?':
575 			i2cadm_device_add_help("unknown option: -%c", optopt);
576 			exit(EXIT_USAGE);
577 		}
578 	}
579 
580 	argv += optind;
581 	argc -= optind;
582 
583 	if (argc > 3) {
584 		errx(EXIT_USAGE, "encountered extraneous arguments starting "
585 		    "with %s", argv[3]);
586 	} else if (argc == 0) {
587 		errx(EXIT_FAILURE, "missing required port path, device name, "
588 		    "and device address");
589 	} else if (argc == 1) {
590 		errx(EXIT_FAILURE, "missing required device name and device "
591 		    "address");
592 	} else if (argc == 2) {
593 		errx(EXIT_FAILURE, "missing required device address");
594 	}
595 
596 	if (!i2c_port_init_by_path(i2cadm.i2c_hdl, argv[0], &port)) {
597 		i2cadm_fatal("failed to parse port path %s", argv[0]);
598 	}
599 
600 	if (!i2c_addr_parse(i2cadm.i2c_hdl, argv[2], &addr)) {
601 		i2cadm_fatal("failed to parse address %s", argv[2]);
602 	}
603 
604 	if (!i2c_device_add_req_init(port, &req)) {
605 		i2cadm_fatal("failed to initialize device add request");
606 	}
607 
608 	if (!i2c_device_add_req_set_addr(req, &addr)) {
609 		i2cadm_fatal("failed to set device address");
610 	}
611 
612 	if (!i2c_device_add_req_set_name(req, argv[1])) {
613 		i2cadm_fatal("failed to set device name");
614 	}
615 
616 	if (ncompat > 0 && !i2c_device_add_req_set_compatible(req, compat,
617 	    ncompat)) {
618 		i2cadm_fatal("failed to set device compatible[]");
619 	}
620 
621 	if (!i2c_device_add_req_exec(req)) {
622 		i2cadm_fatal("failed to add device");
623 	}
624 
625 	i2c_device_add_req_fini(req);
626 	i2c_port_fini(port);
627 	free(compat);
628 	return (EXIT_SUCCESS);
629 }
630 
631 static void
i2cadm_device_remove_usage(FILE * f)632 i2cadm_device_remove_usage(FILE *f)
633 {
634 	(void) fprintf(f, "\ti2cadm device remove <path>\n");
635 }
636 
637 static void
i2cadm_device_remove_help(const char * fmt,...)638 i2cadm_device_remove_help(const char *fmt, ...)
639 {
640 	if (fmt != NULL) {
641 		va_list ap;
642 
643 		va_start(ap, fmt);
644 		vwarnx(fmt, ap);
645 		va_end(ap);
646 	}
647 
648 	(void) fprintf(stderr, "Usage:  i2cadm device remove <path>\n\n");
649 	(void) fprintf(stderr, "Remove the I2C device identified by the "
650 	    "specified path. If the device is in use,\nthis may "
651 	    "fail.\n");
652 }
653 
654 static int
i2cadm_device_remove(int argc,char * argv[])655 i2cadm_device_remove(int argc, char *argv[])
656 {
657 	int c;
658 	i2c_port_t *port;
659 	i2c_dev_info_t *info;
660 
661 	while ((c = getopt(argc, argv, "")) != -1) {
662 		switch (c) {
663 		case ':':
664 			i2cadm_device_remove_help("option -%c requires an "
665 			    "argument", optopt);
666 			exit(EXIT_USAGE);
667 		case '?':
668 			i2cadm_device_remove_help("unknown option: -%c",
669 			    optopt);
670 			exit(EXIT_USAGE);
671 		}
672 	}
673 
674 	argv += optind;
675 	argc -= optind;
676 
677 	if (argc > 1) {
678 		errx(EXIT_USAGE, "encountered extraneous arguments starting "
679 		    "with %s", argv[1]);
680 	} else if (argc == 0) {
681 		errx(EXIT_FAILURE, "missing required device path");
682 	}
683 
684 	if (!i2c_port_dev_init_by_path(i2cadm.i2c_hdl, argv[0], false, &port,
685 	    &info)) {
686 		i2cadm_fatal("failed to parse device path %s", argv[0]);
687 	}
688 
689 	if (!i2c_device_rem(port, i2c_device_info_addr_primary(info))) {
690 		i2cadm_fatal("failed to remove device %s", argv[0]);
691 	}
692 
693 	i2c_device_info_free(info);
694 	i2c_port_fini(port);
695 	return (EXIT_SUCCESS);
696 }
697 
698 static i2cadm_cmdtab_t i2cadm_device_cmds[] = {
699 	{ "list", i2cadm_device_list, i2cadm_device_list_usage },
700 	{ "addrs", i2cadm_device_addrs, i2cadm_device_addrs_usage },
701 	{ "add", i2cadm_device_add, i2cadm_device_add_usage },
702 	{ "remove", i2cadm_device_remove, i2cadm_device_remove_usage }
703 };
704 
705 int
i2cadm_device(int argc,char * argv[])706 i2cadm_device(int argc, char *argv[])
707 {
708 	return (i2cadm_walk_tab(i2cadm_device_cmds,
709 	    ARRAY_SIZE(i2cadm_device_cmds), argc, argv));
710 }
711 
712 void
i2cadm_device_usage(FILE * f)713 i2cadm_device_usage(FILE *f)
714 {
715 	i2cadm_walk_usage(i2cadm_device_cmds, ARRAY_SIZE(i2cadm_device_cmds),
716 	    f);
717 }
718