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