xref: /illumos-gate/usr/src/cmd/i2cadm/i2cadm.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  * i2c, SMBus, and i3c administration
18  */
19 
20 #include <stdlib.h>
21 #include <strings.h>
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <err.h>
25 #include <libgen.h>
26 #include <sys/sysmacros.h>
27 
28 #include "i2cadm.h"
29 
30 i2cadm_t i2cadm;
31 
32 /*
33  * The number of devices per line for the pretty printed address table.
34  */
35 #define	DEVS_PER_LINE	16
36 
37 void
i2cadm_print_table(const i2cadm_table_t * table,void * arg)38 i2cadm_print_table(const i2cadm_table_t *table, void *arg)
39 {
40 	bool post = false;
41 
42 	(void) printf("%s %s:\n\n%s\n", table->table_msg, table->table_port,
43 	    table->table_key);
44 
45 	(void) printf("ADDR");
46 	for (uint32_t i = 0; i < DEVS_PER_LINE; i++) {
47 		(void) printf("%s0x%x", i != 0 ? " " : "\t", i);
48 	}
49 	(void) printf("\n");
50 	for (uint16_t i = 0; i < table->table_max; i++) {
51 		bool line_start = (i % DEVS_PER_LINE) == 0;
52 
53 		if (line_start) {
54 			if (table->table_max > UINT8_MAX) {
55 				(void) printf("0x%03x\t", i);
56 			} else {
57 				(void) printf("0x%02x\t", i);
58 			}
59 		}
60 
61 		if (!line_start) {
62 			(void) printf(" ");
63 		}
64 
65 		if (table->table_cb(arg, i))
66 			post = true;
67 
68 		if ((i % DEVS_PER_LINE) == DEVS_PER_LINE - 1) {
69 			(void) printf("\n");
70 		}
71 	}
72 
73 	if (post) {
74 		table->table_post(arg, table->table_max);
75 	}
76 }
77 
78 static void
i2cadm_vwarn(const char * fmt,va_list ap)79 i2cadm_vwarn(const char *fmt, va_list ap)
80 {
81 	i2c_hdl_t *hdl = i2cadm.i2c_hdl;
82 
83 	(void) fprintf(stderr, "i2cadm: ");
84 	(void) vfprintf(stderr, fmt, ap);
85 	(void) fprintf(stderr, ": %s: %s (libi2c: 0x%x, sys: %d)\n",
86 	    i2c_errmsg(hdl), i2c_errtostr(hdl, i2c_err(hdl)),
87 	    i2c_err(hdl), i2c_syserr(hdl));
88 }
89 
90 void
i2cadm_warn(const char * fmt,...)91 i2cadm_warn(const char *fmt, ...)
92 {
93 	va_list ap;
94 
95 	va_start(ap, fmt);
96 	i2cadm_vwarn(fmt, ap);
97 	va_end(ap);
98 }
99 
100 void __NORETURN
i2cadm_fatal(const char * fmt,...)101 i2cadm_fatal(const char *fmt, ...)
102 {
103 	va_list ap;
104 
105 	va_start(ap, fmt);
106 	i2cadm_vwarn(fmt, ap);
107 	va_end(ap);
108 
109 	exit(EXIT_FAILURE);
110 }
111 
112 void
i2cadm_ofmt_errx(const char * fmt,...)113 i2cadm_ofmt_errx(const char *fmt, ...)
114 {
115 	va_list ap;
116 
117 	va_start(ap, fmt);
118 	verrx(EXIT_FAILURE, fmt, ap);
119 }
120 
121 void
i2cadm_walk_usage(const i2cadm_cmdtab_t * tab,size_t len,FILE * f)122 i2cadm_walk_usage(const i2cadm_cmdtab_t *tab, size_t len, FILE *f)
123 {
124 	for (size_t i = 0; i < len; i++) {
125 		tab[i].icmd_use(f);
126 	}
127 }
128 
129 static void
i2cadm_usage(const i2cadm_cmdtab_t * tab,size_t len,const char * format,...)130 i2cadm_usage(const i2cadm_cmdtab_t *tab, size_t len, const char *format, ...)
131 {
132 	if (format != NULL) {
133 		va_list ap;
134 
135 		va_start(ap, format);
136 		vwarnx(format, ap);
137 		va_end(ap);
138 	}
139 
140 	if (tab == NULL)
141 		return;
142 
143 	fprintf(stderr, "Usage: i2cadm <subcommand> <args> ... \n\n");
144 	i2cadm_walk_usage(tab, len, stderr);
145 }
146 
147 int
i2cadm_walk_tab(const i2cadm_cmdtab_t * tab,size_t len,int argc,char * argv[])148 i2cadm_walk_tab(const i2cadm_cmdtab_t *tab, size_t len, int argc, char *argv[])
149 {
150 	if (argc == 0) {
151 		i2cadm_usage(tab, len, "missing required sub-command");
152 		return (EXIT_FAILURE);
153 	}
154 
155 	for (size_t i = 0; i < len; i++) {
156 		if (strcmp(argv[0], tab[i].icmd_name) != 0)
157 			continue;
158 
159 		argc--;
160 		argv++;
161 		optind = 0;
162 		return (tab[i].icmd_func(argc, argv));
163 	}
164 
165 	i2cadm_usage(tab, len, "unknown subcommand %s", argv[0]);
166 	return (EXIT_USAGE);
167 }
168 
169 /*
170  * The order commands are listed below impacts the order in usage and help
171  * statements. We order these roughly based upon the general operations that one
172  * needs to take starting with all operations that list different entities,
173  * roughly ordered by importance, followed by operations which operate on the
174  * device.
175  */
176 static const i2cadm_cmdtab_t i2cadm_cmds[] = {
177 	{ "controller", i2cadm_controller, i2cadm_controller_usage },
178 	{ "device", i2cadm_device, i2cadm_device_usage },
179 	{ "mux", i2cadm_mux, i2cadm_mux_usage },
180 	{ "port", i2cadm_port, i2cadm_port_usage },
181 	{ "io", i2cadm_io, i2cadm_io_usage },
182 	{ "scan", i2cadm_scan, i2cadm_scan_usage }
183 };
184 
185 int
main(int argc,char * argv[])186 main(int argc, char *argv[])
187 {
188 	i2cadm.i2c_progname = basename(argv[0]);
189 
190 	if (argc < 2) {
191 		i2cadm_usage(i2cadm_cmds, ARRAY_SIZE(i2cadm_cmds),
192 		    "missing required sub-command");
193 		exit(EXIT_USAGE);
194 	}
195 
196 	i2cadm.i2c_hdl = i2c_init();
197 	if (i2cadm.i2c_hdl == NULL) {
198 		err(EXIT_FAILURE, "failed to initialize libi2c");
199 	}
200 
201 	argc--;
202 	argv++;
203 
204 	return (i2cadm_walk_tab(i2cadm_cmds, ARRAY_SIZE(i2cadm_cmds), argc,
205 	    argv));
206 }
207