1*32002227SRobert Mustacchi /*
2*32002227SRobert Mustacchi * This file and its contents are supplied under the terms of the
3*32002227SRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4*32002227SRobert Mustacchi * You may only use this file in accordance with the terms of version
5*32002227SRobert Mustacchi * 1.0 of the CDDL.
6*32002227SRobert Mustacchi *
7*32002227SRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8*32002227SRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
9*32002227SRobert Mustacchi * http://www.illumos.org/license/CDDL.
10*32002227SRobert Mustacchi */
11*32002227SRobert Mustacchi
12*32002227SRobert Mustacchi /*
13*32002227SRobert Mustacchi * Copyright 2025 Oxide Computer Company
14*32002227SRobert Mustacchi */
15*32002227SRobert Mustacchi
16*32002227SRobert Mustacchi /*
17*32002227SRobert Mustacchi * Perform I/O on a given I2C bus, allowing a given mux segment to be activated
18*32002227SRobert Mustacchi * or a specific device.
19*32002227SRobert Mustacchi */
20*32002227SRobert Mustacchi
21*32002227SRobert Mustacchi #include <err.h>
22*32002227SRobert Mustacchi #include <stdio.h>
23*32002227SRobert Mustacchi #include <stdlib.h>
24*32002227SRobert Mustacchi #include <string.h>
25*32002227SRobert Mustacchi #include <strings.h>
26*32002227SRobert Mustacchi #include <sys/sysmacros.h>
27*32002227SRobert Mustacchi #include <sys/debug.h>
28*32002227SRobert Mustacchi #include <sys/hexdump.h>
29*32002227SRobert Mustacchi #include <fcntl.h>
30*32002227SRobert Mustacchi #include <unistd.h>
31*32002227SRobert Mustacchi
32*32002227SRobert Mustacchi #include "i2cadm.h"
33*32002227SRobert Mustacchi
34*32002227SRobert Mustacchi /*
35*32002227SRobert Mustacchi * Currently we don't have an SMBus block read present here. The main reason is
36*32002227SRobert Mustacchi * that we haven't been able to test this end-to-end and therefore don't have a
37*32002227SRobert Mustacchi * great API for extracting the target read length. If we have something we can
38*32002227SRobert Mustacchi * test against, then we can go ahead and add this.
39*32002227SRobert Mustacchi */
40*32002227SRobert Mustacchi typedef enum {
41*32002227SRobert Mustacchi I2CADM_IO_M_I2C,
42*32002227SRobert Mustacchi I2CADM_IO_M_QUICK_READ,
43*32002227SRobert Mustacchi I2CADM_IO_M_QUICK_WRITE,
44*32002227SRobert Mustacchi I2CADM_IO_M_RECV_U8,
45*32002227SRobert Mustacchi I2CADM_IO_M_READ_U8,
46*32002227SRobert Mustacchi I2CADM_IO_M_READ_U16,
47*32002227SRobert Mustacchi I2CADM_IO_M_READ_U32,
48*32002227SRobert Mustacchi I2CADM_IO_M_READ_U64,
49*32002227SRobert Mustacchi I2CADM_IO_M_READ_BLOCK_I2C,
50*32002227SRobert Mustacchi I2CADM_IO_M_SEND_U8,
51*32002227SRobert Mustacchi I2CADM_IO_M_WRITE_U8,
52*32002227SRobert Mustacchi I2CADM_IO_M_WRITE_U16,
53*32002227SRobert Mustacchi I2CADM_IO_M_WRITE_U32,
54*32002227SRobert Mustacchi I2CADM_IO_M_WRITE_U64,
55*32002227SRobert Mustacchi I2CADM_IO_M_WRITE_BLOCK,
56*32002227SRobert Mustacchi I2CADM_IO_M_WRITE_BLOCK_I2C,
57*32002227SRobert Mustacchi I2CADM_IO_M_CALL
58*32002227SRobert Mustacchi } i2cadm_io_mode_t;
59*32002227SRobert Mustacchi
60*32002227SRobert Mustacchi typedef enum {
61*32002227SRobert Mustacchi /*
62*32002227SRobert Mustacchi * Indicates that the size of this is fixed and the target size is
63*32002227SRobert Mustacchi * specified in the mode_rlen and mode_wlen fields.
64*32002227SRobert Mustacchi */
65*32002227SRobert Mustacchi I2CADM_IO_T_FIXED,
66*32002227SRobert Mustacchi /*
67*32002227SRobert Mustacchi * Indicates that a variable read or write length is required, but not
68*32002227SRobert Mustacchi * both.
69*32002227SRobert Mustacchi */
70*32002227SRobert Mustacchi I2CADM_IO_T_VAR_READ,
71*32002227SRobert Mustacchi I2CADM_IO_T_VAR_WRITE,
72*32002227SRobert Mustacchi /*
73*32002227SRobert Mustacchi * Indicates that both a variable read and write length is required. The
74*32002227SRobert Mustacchi * next one is that only one of them is required, but both are allowed.
75*32002227SRobert Mustacchi */
76*32002227SRobert Mustacchi I2CADM_IO_T_VAR_RW,
77*32002227SRobert Mustacchi I2CADM_IO_T_VAR_R_OR_W
78*32002227SRobert Mustacchi } i2cadm_io_type_t;
79*32002227SRobert Mustacchi
80*32002227SRobert Mustacchi typedef struct {
81*32002227SRobert Mustacchi const char *mode_str;
82*32002227SRobert Mustacchi const char *mode_help;
83*32002227SRobert Mustacchi i2cadm_io_mode_t mode_val;
84*32002227SRobert Mustacchi i2cadm_io_type_t mode_io;
85*32002227SRobert Mustacchi bool mode_need_cmd;
86*32002227SRobert Mustacchi uint32_t mode_rlen;
87*32002227SRobert Mustacchi uint32_t mode_wlen;
88*32002227SRobert Mustacchi size_t mode_dlen;
89*32002227SRobert Mustacchi } i2cadm_mode_info_t;
90*32002227SRobert Mustacchi
91*32002227SRobert Mustacchi typedef struct i2cadm_io_req {
92*32002227SRobert Mustacchi const i2cadm_mode_info_t *io_mode;
93*32002227SRobert Mustacchi i2c_io_req_t *io_i2c;
94*32002227SRobert Mustacchi smbus_io_req_t *io_smbus;
95*32002227SRobert Mustacchi uint8_t io_cmd;
96*32002227SRobert Mustacchi uint16_t io_rlen;
97*32002227SRobert Mustacchi uint16_t io_wlen;
98*32002227SRobert Mustacchi void *io_wdata;
99*32002227SRobert Mustacchi void *io_rdata;
100*32002227SRobert Mustacchi } i2cadm_io_req_t;
101*32002227SRobert Mustacchi
102*32002227SRobert Mustacchi static const i2cadm_mode_info_t i2cadm_io_modes[] = {
103*32002227SRobert Mustacchi [I2CADM_IO_M_I2C] = {
104*32002227SRobert Mustacchi .mode_str = "i2c",
105*32002227SRobert Mustacchi .mode_help = "\t\t\tgeneral-purpose I2C I/O",
106*32002227SRobert Mustacchi .mode_val = I2CADM_IO_M_I2C,
107*32002227SRobert Mustacchi .mode_io = I2CADM_IO_T_VAR_R_OR_W,
108*32002227SRobert Mustacchi .mode_dlen = sizeof (uint8_t)
109*32002227SRobert Mustacchi },
110*32002227SRobert Mustacchi [I2CADM_IO_M_QUICK_READ] = {
111*32002227SRobert Mustacchi .mode_str = "quick-read",
112*32002227SRobert Mustacchi .mode_help = "\t\tSMBus quick read",
113*32002227SRobert Mustacchi .mode_val = I2CADM_IO_M_QUICK_READ,
114*32002227SRobert Mustacchi .mode_io = I2CADM_IO_T_FIXED
115*32002227SRobert Mustacchi },
116*32002227SRobert Mustacchi [I2CADM_IO_M_QUICK_WRITE] = {
117*32002227SRobert Mustacchi .mode_str = "quick-write",
118*32002227SRobert Mustacchi .mode_help = "\t\tSMBus write read",
119*32002227SRobert Mustacchi .mode_val = I2CADM_IO_M_QUICK_WRITE,
120*32002227SRobert Mustacchi .mode_io = I2CADM_IO_T_FIXED
121*32002227SRobert Mustacchi },
122*32002227SRobert Mustacchi [I2CADM_IO_M_RECV_U8] = {
123*32002227SRobert Mustacchi .mode_str = "recv-u8",
124*32002227SRobert Mustacchi .mode_help = "\t\t\tSMBus receive byte",
125*32002227SRobert Mustacchi .mode_val = I2CADM_IO_M_RECV_U8,
126*32002227SRobert Mustacchi .mode_io = I2CADM_IO_T_FIXED,
127*32002227SRobert Mustacchi .mode_rlen = sizeof (uint8_t),
128*32002227SRobert Mustacchi .mode_dlen = sizeof (uint8_t)
129*32002227SRobert Mustacchi },
130*32002227SRobert Mustacchi [I2CADM_IO_M_READ_U8] = {
131*32002227SRobert Mustacchi .mode_str = "read-u8",
132*32002227SRobert Mustacchi .mode_help = "\t\t\tSMBus read byte with command",
133*32002227SRobert Mustacchi .mode_val = I2CADM_IO_M_READ_U8,
134*32002227SRobert Mustacchi .mode_io = I2CADM_IO_T_FIXED,
135*32002227SRobert Mustacchi .mode_need_cmd = true,
136*32002227SRobert Mustacchi .mode_rlen = sizeof (uint8_t),
137*32002227SRobert Mustacchi .mode_dlen = sizeof (uint8_t)
138*32002227SRobert Mustacchi },
139*32002227SRobert Mustacchi [I2CADM_IO_M_READ_U16] = {
140*32002227SRobert Mustacchi .mode_str = "read-u16",
141*32002227SRobert Mustacchi .mode_help = "\t\tSMBus read word with command",
142*32002227SRobert Mustacchi .mode_val = I2CADM_IO_M_READ_U16,
143*32002227SRobert Mustacchi .mode_io = I2CADM_IO_T_FIXED,
144*32002227SRobert Mustacchi .mode_need_cmd = true,
145*32002227SRobert Mustacchi .mode_rlen = sizeof (uint16_t),
146*32002227SRobert Mustacchi .mode_dlen = sizeof (uint16_t)
147*32002227SRobert Mustacchi },
148*32002227SRobert Mustacchi [I2CADM_IO_M_READ_U32] = {
149*32002227SRobert Mustacchi .mode_str = "read-u32",
150*32002227SRobert Mustacchi .mode_help = "\t\tSMBus read u32 with command",
151*32002227SRobert Mustacchi .mode_val = I2CADM_IO_M_READ_U32,
152*32002227SRobert Mustacchi .mode_io = I2CADM_IO_T_FIXED,
153*32002227SRobert Mustacchi .mode_need_cmd = true,
154*32002227SRobert Mustacchi .mode_rlen = sizeof (uint32_t),
155*32002227SRobert Mustacchi .mode_dlen = sizeof (uint32_t)
156*32002227SRobert Mustacchi },
157*32002227SRobert Mustacchi [I2CADM_IO_M_READ_U64] = {
158*32002227SRobert Mustacchi .mode_str = "read-u64",
159*32002227SRobert Mustacchi .mode_help = "\t\tSMBus read u64 with command",
160*32002227SRobert Mustacchi .mode_val = I2CADM_IO_M_READ_U64,
161*32002227SRobert Mustacchi .mode_io = I2CADM_IO_T_FIXED,
162*32002227SRobert Mustacchi .mode_need_cmd = true,
163*32002227SRobert Mustacchi .mode_rlen = sizeof (uint64_t),
164*32002227SRobert Mustacchi .mode_dlen = sizeof (uint64_t)
165*32002227SRobert Mustacchi },
166*32002227SRobert Mustacchi [I2CADM_IO_M_READ_BLOCK_I2C] = {
167*32002227SRobert Mustacchi .mode_str = "read-block-i2c",
168*32002227SRobert Mustacchi .mode_help = "\t\tSMBus I2C block read with command (length "
169*32002227SRobert Mustacchi "not sent)",
170*32002227SRobert Mustacchi .mode_val = I2CADM_IO_M_READ_BLOCK_I2C,
171*32002227SRobert Mustacchi .mode_io = I2CADM_IO_T_VAR_READ,
172*32002227SRobert Mustacchi .mode_need_cmd = true,
173*32002227SRobert Mustacchi .mode_dlen = sizeof (uint8_t)
174*32002227SRobert Mustacchi },
175*32002227SRobert Mustacchi [I2CADM_IO_M_SEND_U8] = {
176*32002227SRobert Mustacchi .mode_str = "send-u8",
177*32002227SRobert Mustacchi .mode_help = "\t\t\tSMBus send byte",
178*32002227SRobert Mustacchi .mode_val = I2CADM_IO_M_SEND_U8,
179*32002227SRobert Mustacchi .mode_io = I2CADM_IO_T_FIXED,
180*32002227SRobert Mustacchi .mode_wlen = sizeof (uint8_t),
181*32002227SRobert Mustacchi .mode_dlen = sizeof (uint8_t)
182*32002227SRobert Mustacchi },
183*32002227SRobert Mustacchi [I2CADM_IO_M_WRITE_U8] = {
184*32002227SRobert Mustacchi .mode_str = "write-u8",
185*32002227SRobert Mustacchi .mode_help = "\t\tSMBus write byte with command",
186*32002227SRobert Mustacchi .mode_val = I2CADM_IO_M_WRITE_U8,
187*32002227SRobert Mustacchi .mode_io = I2CADM_IO_T_FIXED,
188*32002227SRobert Mustacchi .mode_need_cmd = true,
189*32002227SRobert Mustacchi .mode_wlen = sizeof (uint8_t),
190*32002227SRobert Mustacchi .mode_dlen = sizeof (uint8_t)
191*32002227SRobert Mustacchi },
192*32002227SRobert Mustacchi [I2CADM_IO_M_WRITE_U16] = {
193*32002227SRobert Mustacchi .mode_str = "write-u16",
194*32002227SRobert Mustacchi .mode_help = "\t\tSMBus write word with command",
195*32002227SRobert Mustacchi .mode_val = I2CADM_IO_M_WRITE_U16,
196*32002227SRobert Mustacchi .mode_io = I2CADM_IO_T_FIXED,
197*32002227SRobert Mustacchi .mode_need_cmd = true,
198*32002227SRobert Mustacchi .mode_wlen = sizeof (uint16_t),
199*32002227SRobert Mustacchi .mode_dlen = sizeof (uint16_t)
200*32002227SRobert Mustacchi },
201*32002227SRobert Mustacchi [I2CADM_IO_M_WRITE_U32] = {
202*32002227SRobert Mustacchi .mode_str = "write-u32",
203*32002227SRobert Mustacchi .mode_help = "\t\tSMBus write u32 with command",
204*32002227SRobert Mustacchi .mode_val = I2CADM_IO_M_WRITE_U32,
205*32002227SRobert Mustacchi .mode_io = I2CADM_IO_T_FIXED,
206*32002227SRobert Mustacchi .mode_need_cmd = true,
207*32002227SRobert Mustacchi .mode_wlen = sizeof (uint32_t),
208*32002227SRobert Mustacchi .mode_dlen = sizeof (uint32_t)
209*32002227SRobert Mustacchi },
210*32002227SRobert Mustacchi [I2CADM_IO_M_WRITE_U64] = {
211*32002227SRobert Mustacchi .mode_str = "write-u64",
212*32002227SRobert Mustacchi .mode_help = "\t\tSMBus write u64 with command",
213*32002227SRobert Mustacchi .mode_val = I2CADM_IO_M_WRITE_U64,
214*32002227SRobert Mustacchi .mode_io = I2CADM_IO_T_FIXED,
215*32002227SRobert Mustacchi .mode_need_cmd = true,
216*32002227SRobert Mustacchi .mode_wlen = sizeof (uint64_t),
217*32002227SRobert Mustacchi .mode_dlen = sizeof (uint64_t)
218*32002227SRobert Mustacchi },
219*32002227SRobert Mustacchi [I2CADM_IO_M_WRITE_BLOCK] = {
220*32002227SRobert Mustacchi .mode_str = "write-block",
221*32002227SRobert Mustacchi .mode_help = "\t\tSMBus block write with command and length",
222*32002227SRobert Mustacchi .mode_val = I2CADM_IO_M_WRITE_BLOCK,
223*32002227SRobert Mustacchi .mode_io = I2CADM_IO_T_VAR_WRITE,
224*32002227SRobert Mustacchi .mode_need_cmd = true,
225*32002227SRobert Mustacchi .mode_dlen = sizeof (uint8_t)
226*32002227SRobert Mustacchi },
227*32002227SRobert Mustacchi [I2CADM_IO_M_WRITE_BLOCK_I2C] = {
228*32002227SRobert Mustacchi .mode_str = "write-block-i2c",
229*32002227SRobert Mustacchi .mode_help = "\t\tSMBus I2C block write with command (length "
230*32002227SRobert Mustacchi "not sent)",
231*32002227SRobert Mustacchi .mode_val = I2CADM_IO_M_WRITE_BLOCK_I2C,
232*32002227SRobert Mustacchi .mode_io = I2CADM_IO_T_VAR_WRITE,
233*32002227SRobert Mustacchi .mode_need_cmd = true,
234*32002227SRobert Mustacchi .mode_dlen = sizeof (uint8_t)
235*32002227SRobert Mustacchi },
236*32002227SRobert Mustacchi [I2CADM_IO_M_CALL] = {
237*32002227SRobert Mustacchi .mode_str = "call",
238*32002227SRobert Mustacchi .mode_help = "\t\t\tSMBus process call with command (tx and "
239*32002227SRobert Mustacchi "rx a u16)",
240*32002227SRobert Mustacchi .mode_val = I2CADM_IO_M_CALL,
241*32002227SRobert Mustacchi .mode_io = I2CADM_IO_T_FIXED,
242*32002227SRobert Mustacchi .mode_need_cmd = true,
243*32002227SRobert Mustacchi .mode_rlen = sizeof (uint16_t),
244*32002227SRobert Mustacchi .mode_wlen = sizeof (uint16_t),
245*32002227SRobert Mustacchi .mode_dlen = sizeof (uint16_t)
246*32002227SRobert Mustacchi }
247*32002227SRobert Mustacchi };
248*32002227SRobert Mustacchi
249*32002227SRobert Mustacchi void
i2cadm_io_usage(FILE * f)250*32002227SRobert Mustacchi i2cadm_io_usage(FILE *f)
251*32002227SRobert Mustacchi {
252*32002227SRobert Mustacchi (void) fprintf(f, "\ti2cadm io [-m mode] -d dest [-a addr] [-c cmd] "
253*32002227SRobert Mustacchi "[-w wlen] [-r rlen] [-o output] <data>\n");
254*32002227SRobert Mustacchi }
255*32002227SRobert Mustacchi
256*32002227SRobert Mustacchi static void
i2cadm_io_help(const char * fmt,...)257*32002227SRobert Mustacchi i2cadm_io_help(const char *fmt, ...)
258*32002227SRobert Mustacchi {
259*32002227SRobert Mustacchi if (fmt != NULL) {
260*32002227SRobert Mustacchi va_list ap;
261*32002227SRobert Mustacchi
262*32002227SRobert Mustacchi va_start(ap, fmt);
263*32002227SRobert Mustacchi vwarnx(fmt, ap);
264*32002227SRobert Mustacchi va_end(ap);
265*32002227SRobert Mustacchi }
266*32002227SRobert Mustacchi
267*32002227SRobert Mustacchi (void) fprintf(stderr, "Usage: i2cadm io [-m mode] -d dest [-a addr] "
268*32002227SRobert Mustacchi "[-r rlen] [-w wlen] [-o output]\n\t<data>\n");
269*32002227SRobert Mustacchi (void) fprintf(stderr, "\nPerform I/O to any arbitrary I2C address on "
270*32002227SRobert Mustacchi "the specified controller and\nport. If a mux is part of the "
271*32002227SRobert Mustacchi "destination path, then it will be activated\nprior to issuing "
272*32002227SRobert Mustacchi "the I/O. Transmitted data will be taken from positional\n"
273*32002227SRobert Mustacchi "arguments.\n\nThe following options are supported:\n\n"
274*32002227SRobert Mustacchi "\t-a addr\t\tthe 7-bit address to send the I/O to\n"
275*32002227SRobert Mustacchi "\t-d dest\t\tspecifies the controller and port to target\n"
276*32002227SRobert Mustacchi "\t-m mode\t\tsets the type of I/O issued, defaults to I2C\n"
277*32002227SRobert Mustacchi "\t-o output\twrite raw data read to file output\n"
278*32002227SRobert Mustacchi "\t-r rlen\t\tsets the number of bytes to read\n"
279*32002227SRobert Mustacchi "\t-w wlen\t\tsets the number of bytes to write\n"
280*32002227SRobert Mustacchi "\nThe following I/O modes are supported:\n");
281*32002227SRobert Mustacchi
282*32002227SRobert Mustacchi for (size_t i = 0; i < ARRAY_SIZE(i2cadm_io_modes); i++) {
283*32002227SRobert Mustacchi (void) fprintf(stderr, "\t%s%s\n", i2cadm_io_modes[i].mode_str,
284*32002227SRobert Mustacchi i2cadm_io_modes[i].mode_help);
285*32002227SRobert Mustacchi }
286*32002227SRobert Mustacchi exit(EXIT_FAILURE);
287*32002227SRobert Mustacchi }
288*32002227SRobert Mustacchi
289*32002227SRobert Mustacchi static const i2cadm_mode_info_t *
i2cadm_io_parse_mode(const char * str)290*32002227SRobert Mustacchi i2cadm_io_parse_mode(const char *str)
291*32002227SRobert Mustacchi {
292*32002227SRobert Mustacchi for (size_t i = 0; i < ARRAY_SIZE(i2cadm_io_modes); i++) {
293*32002227SRobert Mustacchi if (strcasecmp(str, i2cadm_io_modes[i].mode_str) == 0) {
294*32002227SRobert Mustacchi return (&i2cadm_io_modes[i]);
295*32002227SRobert Mustacchi }
296*32002227SRobert Mustacchi }
297*32002227SRobert Mustacchi
298*32002227SRobert Mustacchi warnx("unknown I/O mode: %s", str);
299*32002227SRobert Mustacchi (void) printf("Valid I/O Modes:\n");
300*32002227SRobert Mustacchi for (size_t i = 0; i < ARRAY_SIZE(i2cadm_io_modes); i++) {
301*32002227SRobert Mustacchi (void) printf("\t%s%s\n", i2cadm_io_modes[i].mode_str,
302*32002227SRobert Mustacchi i2cadm_io_modes[i].mode_help);
303*32002227SRobert Mustacchi }
304*32002227SRobert Mustacchi exit(EXIT_FAILURE);
305*32002227SRobert Mustacchi }
306*32002227SRobert Mustacchi
307*32002227SRobert Mustacchi static bool
i2cadm_io_read_ok(const i2cadm_mode_info_t * mode)308*32002227SRobert Mustacchi i2cadm_io_read_ok(const i2cadm_mode_info_t *mode)
309*32002227SRobert Mustacchi {
310*32002227SRobert Mustacchi switch (mode->mode_io) {
311*32002227SRobert Mustacchi case I2CADM_IO_T_FIXED:
312*32002227SRobert Mustacchi return (mode->mode_rlen != 0);
313*32002227SRobert Mustacchi case I2CADM_IO_T_VAR_READ:
314*32002227SRobert Mustacchi case I2CADM_IO_T_VAR_RW:
315*32002227SRobert Mustacchi case I2CADM_IO_T_VAR_R_OR_W:
316*32002227SRobert Mustacchi return (true);
317*32002227SRobert Mustacchi case I2CADM_IO_T_VAR_WRITE:
318*32002227SRobert Mustacchi default:
319*32002227SRobert Mustacchi return (false);
320*32002227SRobert Mustacchi }
321*32002227SRobert Mustacchi }
322*32002227SRobert Mustacchi
323*32002227SRobert Mustacchi static bool
i2cadm_io_read_req(const i2cadm_mode_info_t * mode)324*32002227SRobert Mustacchi i2cadm_io_read_req(const i2cadm_mode_info_t *mode)
325*32002227SRobert Mustacchi {
326*32002227SRobert Mustacchi switch (mode->mode_io) {
327*32002227SRobert Mustacchi case I2CADM_IO_T_VAR_READ:
328*32002227SRobert Mustacchi case I2CADM_IO_T_VAR_RW:
329*32002227SRobert Mustacchi return (true);
330*32002227SRobert Mustacchi case I2CADM_IO_T_FIXED:
331*32002227SRobert Mustacchi case I2CADM_IO_T_VAR_WRITE:
332*32002227SRobert Mustacchi case I2CADM_IO_T_VAR_R_OR_W:
333*32002227SRobert Mustacchi default:
334*32002227SRobert Mustacchi return (false);
335*32002227SRobert Mustacchi }
336*32002227SRobert Mustacchi }
337*32002227SRobert Mustacchi
338*32002227SRobert Mustacchi static bool
i2cadm_io_write_req(const i2cadm_mode_info_t * mode)339*32002227SRobert Mustacchi i2cadm_io_write_req(const i2cadm_mode_info_t *mode)
340*32002227SRobert Mustacchi {
341*32002227SRobert Mustacchi switch (mode->mode_io) {
342*32002227SRobert Mustacchi case I2CADM_IO_T_VAR_WRITE:
343*32002227SRobert Mustacchi case I2CADM_IO_T_VAR_RW:
344*32002227SRobert Mustacchi return (true);
345*32002227SRobert Mustacchi case I2CADM_IO_T_FIXED:
346*32002227SRobert Mustacchi case I2CADM_IO_T_VAR_READ:
347*32002227SRobert Mustacchi case I2CADM_IO_T_VAR_R_OR_W:
348*32002227SRobert Mustacchi default:
349*32002227SRobert Mustacchi return (false);
350*32002227SRobert Mustacchi }
351*32002227SRobert Mustacchi }
352*32002227SRobert Mustacchi
353*32002227SRobert Mustacchi static bool
i2cadm_io_write_ok(const i2cadm_mode_info_t * mode)354*32002227SRobert Mustacchi i2cadm_io_write_ok(const i2cadm_mode_info_t *mode)
355*32002227SRobert Mustacchi {
356*32002227SRobert Mustacchi switch (mode->mode_io) {
357*32002227SRobert Mustacchi case I2CADM_IO_T_FIXED:
358*32002227SRobert Mustacchi return (mode->mode_wlen != 0);
359*32002227SRobert Mustacchi case I2CADM_IO_T_VAR_WRITE:
360*32002227SRobert Mustacchi case I2CADM_IO_T_VAR_RW:
361*32002227SRobert Mustacchi case I2CADM_IO_T_VAR_R_OR_W:
362*32002227SRobert Mustacchi return (true);
363*32002227SRobert Mustacchi case I2CADM_IO_T_VAR_READ:
364*32002227SRobert Mustacchi default:
365*32002227SRobert Mustacchi return (false);
366*32002227SRobert Mustacchi }
367*32002227SRobert Mustacchi }
368*32002227SRobert Mustacchi
369*32002227SRobert Mustacchi /*
370*32002227SRobert Mustacchi * Look at the specific requested mode and parse the corresponding read and
371*32002227SRobert Mustacchi * write lengths. There are a few different cases for how a command performs
372*32002227SRobert Mustacchi * I/O:
373*32002227SRobert Mustacchi *
374*32002227SRobert Mustacchi * - Commands that have a built-in length. For example, SMBus read/write
375*32002227SRobert Mustacchi * u8/16 commands. Here if someone specifies the exact required length,
376*32002227SRobert Mustacchi * that's fine, otherwise it's an error.
377*32002227SRobert Mustacchi * - Commands that require both a read and write length: block call.
378*32002227SRobert Mustacchi * - Commands that require either a read or a write length: other block
379*32002227SRobert Mustacchi * operations.
380*32002227SRobert Mustacchi * - Commands that require at least one I/O direction, but can use both, aka
381*32002227SRobert Mustacchi * I2C.
382*32002227SRobert Mustacchi */
383*32002227SRobert Mustacchi static void
i2cadm_io_parse_rw_len(i2cadm_io_req_t * req,const char * rstr,const char * wstr)384*32002227SRobert Mustacchi i2cadm_io_parse_rw_len(i2cadm_io_req_t *req, const char *rstr, const char *wstr)
385*32002227SRobert Mustacchi {
386*32002227SRobert Mustacchi const i2cadm_mode_info_t *mode = req->io_mode;
387*32002227SRobert Mustacchi const char *modestr = mode->mode_str;
388*32002227SRobert Mustacchi i2cadm_io_type_t type = mode->mode_io;
389*32002227SRobert Mustacchi
390*32002227SRobert Mustacchi /*
391*32002227SRobert Mustacchi * First check if we have the required strings for the command mode.
392*32002227SRobert Mustacchi */
393*32002227SRobert Mustacchi if (rstr == NULL && i2cadm_io_read_req(mode)) {
394*32002227SRobert Mustacchi errx(EXIT_FAILURE, "missing required I/O read length "
395*32002227SRobert Mustacchi "(-r) which is required for I/O mode %s", modestr);
396*32002227SRobert Mustacchi }
397*32002227SRobert Mustacchi
398*32002227SRobert Mustacchi if (wstr == NULL && i2cadm_io_write_req(mode)) {
399*32002227SRobert Mustacchi errx(EXIT_FAILURE, "missing required I/O write length "
400*32002227SRobert Mustacchi "(-w) which is required for I/O mode %s", modestr);
401*32002227SRobert Mustacchi }
402*32002227SRobert Mustacchi
403*32002227SRobert Mustacchi if (type == I2CADM_IO_T_VAR_R_OR_W && rstr == NULL && wstr == NULL) {
404*32002227SRobert Mustacchi errx(EXIT_FAILURE, "I/O mode %s requires at least one or both "
405*32002227SRobert Mustacchi "of a read length (-r) and write length (-w) to be "
406*32002227SRobert Mustacchi "specified", modestr);
407*32002227SRobert Mustacchi }
408*32002227SRobert Mustacchi
409*32002227SRobert Mustacchi /*
410*32002227SRobert Mustacchi * Now if we have a string, check if it is allowed. If so, then parse
411*32002227SRobert Mustacchi * it. If this was a fixed length we need to verify that things match.
412*32002227SRobert Mustacchi */
413*32002227SRobert Mustacchi if (rstr != NULL) {
414*32002227SRobert Mustacchi const char *errstr;
415*32002227SRobert Mustacchi
416*32002227SRobert Mustacchi if (!i2cadm_io_read_ok(mode)) {
417*32002227SRobert Mustacchi errx(EXIT_FAILURE, "I/O mode %s does not allow "
418*32002227SRobert Mustacchi "specifying a read length (-r)", modestr);
419*32002227SRobert Mustacchi }
420*32002227SRobert Mustacchi
421*32002227SRobert Mustacchi req->io_rlen = (uint16_t)strtonumx(rstr, 1, I2C_REQ_MAX,
422*32002227SRobert Mustacchi &errstr, 0);
423*32002227SRobert Mustacchi if (errstr != NULL) {
424*32002227SRobert Mustacchi errx(EXIT_FAILURE, "invalid read length: %s is %s, "
425*32002227SRobert Mustacchi "valid values are between 1 and %u", rstr, errstr,
426*32002227SRobert Mustacchi I2C_REQ_MAX);
427*32002227SRobert Mustacchi }
428*32002227SRobert Mustacchi
429*32002227SRobert Mustacchi if (type == I2CADM_IO_T_FIXED && req->io_rlen !=
430*32002227SRobert Mustacchi mode->mode_rlen) {
431*32002227SRobert Mustacchi errx(EXIT_FAILURE, "I/O mode %s has a fixed read "
432*32002227SRobert Mustacchi "length of %u bytes, either do not specify -r or "
433*32002227SRobert Mustacchi "set it to %u, not %s", modestr, mode->mode_rlen,
434*32002227SRobert Mustacchi mode->mode_rlen, rstr);
435*32002227SRobert Mustacchi }
436*32002227SRobert Mustacchi } else if (type == I2CADM_IO_T_FIXED) {
437*32002227SRobert Mustacchi req->io_rlen = mode->mode_rlen;
438*32002227SRobert Mustacchi }
439*32002227SRobert Mustacchi
440*32002227SRobert Mustacchi if (wstr != NULL) {
441*32002227SRobert Mustacchi const char *errstr;
442*32002227SRobert Mustacchi
443*32002227SRobert Mustacchi if (!i2cadm_io_write_ok(mode)) {
444*32002227SRobert Mustacchi errx(EXIT_FAILURE, "I/O mode %s does not allow "
445*32002227SRobert Mustacchi "specifying a write length (-w)", modestr);
446*32002227SRobert Mustacchi }
447*32002227SRobert Mustacchi
448*32002227SRobert Mustacchi req->io_wlen = (uint16_t)strtonumx(wstr, 1, I2C_REQ_MAX,
449*32002227SRobert Mustacchi &errstr, 0);
450*32002227SRobert Mustacchi if (errstr != NULL) {
451*32002227SRobert Mustacchi errx(EXIT_FAILURE, "invalid write length: %s is %s, "
452*32002227SRobert Mustacchi "valid values are between 1 and %u", wstr, errstr,
453*32002227SRobert Mustacchi I2C_REQ_MAX);
454*32002227SRobert Mustacchi }
455*32002227SRobert Mustacchi
456*32002227SRobert Mustacchi if (type == I2CADM_IO_T_FIXED && req->io_wlen !=
457*32002227SRobert Mustacchi mode->mode_wlen) {
458*32002227SRobert Mustacchi errx(EXIT_FAILURE, "I/O mode %s has a fixed write "
459*32002227SRobert Mustacchi "length of %u bytes, either do not specify -w or "
460*32002227SRobert Mustacchi "set it to %u, not %s", modestr, mode->mode_wlen,
461*32002227SRobert Mustacchi mode->mode_wlen, wstr);
462*32002227SRobert Mustacchi }
463*32002227SRobert Mustacchi } else if (type == I2CADM_IO_T_FIXED) {
464*32002227SRobert Mustacchi req->io_wlen = mode->mode_wlen;
465*32002227SRobert Mustacchi }
466*32002227SRobert Mustacchi }
467*32002227SRobert Mustacchi
468*32002227SRobert Mustacchi /*
469*32002227SRobert Mustacchi * Go through and parse data into the requisite format. Different commands have
470*32002227SRobert Mustacchi * a different data size element. While most are a uint8_t, some are larger. We
471*32002227SRobert Mustacchi * adjust what we are parsing at this phase.
472*32002227SRobert Mustacchi *
473*32002227SRobert Mustacchi * We require one argument per data point. We should probably in the future
474*32002227SRobert Mustacchi * allow for something like looking for comma characters, but this works for
475*32002227SRobert Mustacchi * now.
476*32002227SRobert Mustacchi */
477*32002227SRobert Mustacchi static void
i2cadm_io_parse_data(i2cadm_io_req_t * req,int argc,char * argv[])478*32002227SRobert Mustacchi i2cadm_io_parse_data(i2cadm_io_req_t *req, int argc, char *argv[])
479*32002227SRobert Mustacchi {
480*32002227SRobert Mustacchi uint32_t nents;
481*32002227SRobert Mustacchi
482*32002227SRobert Mustacchi VERIFY3U(req->io_wlen, !=, 0);
483*32002227SRobert Mustacchi VERIFY3U(req->io_mode->mode_dlen, !=, 0);
484*32002227SRobert Mustacchi VERIFY0(req->io_wlen % req->io_mode->mode_dlen);
485*32002227SRobert Mustacchi
486*32002227SRobert Mustacchi nents = req->io_wlen / req->io_mode->mode_dlen;
487*32002227SRobert Mustacchi if (nents > 1 && req->io_mode->mode_dlen != 1) {
488*32002227SRobert Mustacchi errx(EXIT_FAILURE, "fatal internal error, cannot handle "
489*32002227SRobert Mustacchi "I/O request with multiple non-byte sized data points");
490*32002227SRobert Mustacchi }
491*32002227SRobert Mustacchi
492*32002227SRobert Mustacchi req->io_wdata = calloc(nents, req->io_mode->mode_dlen);
493*32002227SRobert Mustacchi if (req->io_wdata == NULL) {
494*32002227SRobert Mustacchi err(EXIT_FAILURE, "failed to allocate write data buffer (%u "
495*32002227SRobert Mustacchi "elements, %zu bytes)", nents, req->io_mode->mode_dlen);
496*32002227SRobert Mustacchi }
497*32002227SRobert Mustacchi
498*32002227SRobert Mustacchi if (argc != nents) {
499*32002227SRobert Mustacchi errx(EXIT_FAILURE, "write data requires %u elements, but only "
500*32002227SRobert Mustacchi "found %d remaining arguments", nents, argc);
501*32002227SRobert Mustacchi }
502*32002227SRobert Mustacchi
503*32002227SRobert Mustacchi for (int i = 0; i < argc; i++) {
504*32002227SRobert Mustacchi unsigned long long ull, max;
505*32002227SRobert Mustacchi char *eptr;
506*32002227SRobert Mustacchi uint8_t *u8;
507*32002227SRobert Mustacchi uint16_t *u16;
508*32002227SRobert Mustacchi uint32_t *u32;
509*32002227SRobert Mustacchi uint64_t *u64;
510*32002227SRobert Mustacchi
511*32002227SRobert Mustacchi /*
512*32002227SRobert Mustacchi * Note, we can't use strtonumx here because we want to be able
513*32002227SRobert Mustacchi * to parse a uint64_t but strtonumx maxes out at a long long.
514*32002227SRobert Mustacchi */
515*32002227SRobert Mustacchi errno = 0;
516*32002227SRobert Mustacchi ull = strtoull(argv[i], &eptr, 0);
517*32002227SRobert Mustacchi if (errno != 0 || *eptr != '\0') {
518*32002227SRobert Mustacchi errx(EXIT_FAILURE, "failed to parse data element %s",
519*32002227SRobert Mustacchi argv[i]);
520*32002227SRobert Mustacchi }
521*32002227SRobert Mustacchi
522*32002227SRobert Mustacchi switch (req->io_mode->mode_dlen) {
523*32002227SRobert Mustacchi case 1:
524*32002227SRobert Mustacchi max = UINT8_MAX;
525*32002227SRobert Mustacchi break;
526*32002227SRobert Mustacchi case 2:
527*32002227SRobert Mustacchi max = UINT16_MAX;
528*32002227SRobert Mustacchi break;
529*32002227SRobert Mustacchi case 4:
530*32002227SRobert Mustacchi max = UINT32_MAX;
531*32002227SRobert Mustacchi break;
532*32002227SRobert Mustacchi case 8:
533*32002227SRobert Mustacchi max = UINT64_MAX;
534*32002227SRobert Mustacchi break;
535*32002227SRobert Mustacchi default:
536*32002227SRobert Mustacchi abort();
537*32002227SRobert Mustacchi }
538*32002227SRobert Mustacchi
539*32002227SRobert Mustacchi if (ull > max) {
540*32002227SRobert Mustacchi errx(EXIT_FAILURE, "data element %s is outside the "
541*32002227SRobert Mustacchi "bounds for a %zu byte datum ([0, 0x%llx])",
542*32002227SRobert Mustacchi argv[i], req->io_mode->mode_dlen, max);
543*32002227SRobert Mustacchi }
544*32002227SRobert Mustacchi
545*32002227SRobert Mustacchi switch (req->io_mode->mode_dlen) {
546*32002227SRobert Mustacchi case 1:
547*32002227SRobert Mustacchi u8 = req->io_wdata;
548*32002227SRobert Mustacchi u8[i] = (uint8_t)ull;
549*32002227SRobert Mustacchi break;
550*32002227SRobert Mustacchi case 2:
551*32002227SRobert Mustacchi u16 = req->io_wdata;
552*32002227SRobert Mustacchi u16[i] = (uint16_t)ull;
553*32002227SRobert Mustacchi break;
554*32002227SRobert Mustacchi case 4:
555*32002227SRobert Mustacchi u32 = req->io_wdata;
556*32002227SRobert Mustacchi u32[i] = (uint32_t)ull;
557*32002227SRobert Mustacchi break;
558*32002227SRobert Mustacchi case 8:
559*32002227SRobert Mustacchi u64 = req->io_wdata;
560*32002227SRobert Mustacchi u64[i] = (uint64_t)ull;
561*32002227SRobert Mustacchi break;
562*32002227SRobert Mustacchi default:
563*32002227SRobert Mustacchi abort();
564*32002227SRobert Mustacchi }
565*32002227SRobert Mustacchi }
566*32002227SRobert Mustacchi }
567*32002227SRobert Mustacchi
568*32002227SRobert Mustacchi static void
i2cadm_io_write(const i2cadm_io_req_t * req,const i2cadm_mode_info_t * mode,int ofd)569*32002227SRobert Mustacchi i2cadm_io_write(const i2cadm_io_req_t *req, const i2cadm_mode_info_t *mode,
570*32002227SRobert Mustacchi int ofd)
571*32002227SRobert Mustacchi {
572*32002227SRobert Mustacchi size_t to_write = 0, off = 0;
573*32002227SRobert Mustacchi
574*32002227SRobert Mustacchi switch (mode->mode_val) {
575*32002227SRobert Mustacchi case I2CADM_IO_M_I2C:
576*32002227SRobert Mustacchi case I2CADM_IO_M_READ_BLOCK_I2C:
577*32002227SRobert Mustacchi to_write = req->io_rlen;
578*32002227SRobert Mustacchi break;
579*32002227SRobert Mustacchi case I2CADM_IO_M_RECV_U8:
580*32002227SRobert Mustacchi case I2CADM_IO_M_READ_U8:
581*32002227SRobert Mustacchi to_write = sizeof (uint8_t);
582*32002227SRobert Mustacchi break;
583*32002227SRobert Mustacchi case I2CADM_IO_M_READ_U16:
584*32002227SRobert Mustacchi to_write = sizeof (uint16_t);
585*32002227SRobert Mustacchi break;
586*32002227SRobert Mustacchi case I2CADM_IO_M_READ_U32:
587*32002227SRobert Mustacchi to_write = sizeof (uint32_t);
588*32002227SRobert Mustacchi break;
589*32002227SRobert Mustacchi case I2CADM_IO_M_READ_U64:
590*32002227SRobert Mustacchi to_write = sizeof (uint64_t);
591*32002227SRobert Mustacchi break;
592*32002227SRobert Mustacchi default:
593*32002227SRobert Mustacchi break;
594*32002227SRobert Mustacchi }
595*32002227SRobert Mustacchi
596*32002227SRobert Mustacchi while (to_write > 0) {
597*32002227SRobert Mustacchi ssize_t ret = write(ofd, req->io_rdata + off, to_write);
598*32002227SRobert Mustacchi if (ret < 0) {
599*32002227SRobert Mustacchi err(EXIT_FAILURE, "failed to write %zu bytes to "
600*32002227SRobert Mustacchi "output file at offset %zu", to_write, off);
601*32002227SRobert Mustacchi }
602*32002227SRobert Mustacchi
603*32002227SRobert Mustacchi to_write -= ret;
604*32002227SRobert Mustacchi off += ret;
605*32002227SRobert Mustacchi }
606*32002227SRobert Mustacchi }
607*32002227SRobert Mustacchi
608*32002227SRobert Mustacchi static void
i2cadm_io_init(const i2cadm_io_req_t * req,const i2cadm_mode_info_t * mode)609*32002227SRobert Mustacchi i2cadm_io_init(const i2cadm_io_req_t *req, const i2cadm_mode_info_t *mode)
610*32002227SRobert Mustacchi {
611*32002227SRobert Mustacchi switch (mode->mode_val) {
612*32002227SRobert Mustacchi case I2CADM_IO_M_I2C:
613*32002227SRobert Mustacchi if (req->io_rlen != 0 &&
614*32002227SRobert Mustacchi !i2c_io_req_set_receive_buf(req->io_i2c, req->io_rdata,
615*32002227SRobert Mustacchi req->io_rlen)) {
616*32002227SRobert Mustacchi i2cadm_fatal("failed to set I2C read buffer");
617*32002227SRobert Mustacchi }
618*32002227SRobert Mustacchi
619*32002227SRobert Mustacchi if (req->io_wlen != 0 &&
620*32002227SRobert Mustacchi !i2c_io_req_set_transmit_data(req->io_i2c, req->io_wdata,
621*32002227SRobert Mustacchi req->io_wlen)) {
622*32002227SRobert Mustacchi i2cadm_fatal("Failed to set I2C write buffer");
623*32002227SRobert Mustacchi }
624*32002227SRobert Mustacchi break;
625*32002227SRobert Mustacchi case I2CADM_IO_M_QUICK_READ:
626*32002227SRobert Mustacchi if (!smbus_io_req_set_quick_cmd(req->io_smbus, false)) {
627*32002227SRobert Mustacchi i2cadm_fatal("failed to set quick command request");
628*32002227SRobert Mustacchi }
629*32002227SRobert Mustacchi break;
630*32002227SRobert Mustacchi case I2CADM_IO_M_QUICK_WRITE:
631*32002227SRobert Mustacchi if (!smbus_io_req_set_quick_cmd(req->io_smbus, true)) {
632*32002227SRobert Mustacchi i2cadm_fatal("failed to set quick command request");
633*32002227SRobert Mustacchi }
634*32002227SRobert Mustacchi break;
635*32002227SRobert Mustacchi case I2CADM_IO_M_RECV_U8:
636*32002227SRobert Mustacchi if (!smbus_io_req_set_recv_byte(req->io_smbus, req->io_rdata)) {
637*32002227SRobert Mustacchi i2cadm_fatal("failed to set receive byte request");
638*32002227SRobert Mustacchi }
639*32002227SRobert Mustacchi break;
640*32002227SRobert Mustacchi case I2CADM_IO_M_READ_U8:
641*32002227SRobert Mustacchi if (!smbus_io_req_set_read_u8(req->io_smbus, req->io_cmd,
642*32002227SRobert Mustacchi req->io_rdata)) {
643*32002227SRobert Mustacchi i2cadm_fatal("failed to set read byte request");
644*32002227SRobert Mustacchi }
645*32002227SRobert Mustacchi break;
646*32002227SRobert Mustacchi case I2CADM_IO_M_READ_U16:
647*32002227SRobert Mustacchi if (!smbus_io_req_set_read_u16(req->io_smbus, req->io_cmd,
648*32002227SRobert Mustacchi req->io_rdata)) {
649*32002227SRobert Mustacchi i2cadm_fatal("failed to set read word request");
650*32002227SRobert Mustacchi }
651*32002227SRobert Mustacchi break;
652*32002227SRobert Mustacchi case I2CADM_IO_M_READ_U32:
653*32002227SRobert Mustacchi if (!smbus_io_req_set_read_u32(req->io_smbus, req->io_cmd,
654*32002227SRobert Mustacchi req->io_rdata)) {
655*32002227SRobert Mustacchi i2cadm_fatal("failed to set read u32 request");
656*32002227SRobert Mustacchi }
657*32002227SRobert Mustacchi break;
658*32002227SRobert Mustacchi case I2CADM_IO_M_READ_U64:
659*32002227SRobert Mustacchi if (!smbus_io_req_set_read_u64(req->io_smbus, req->io_cmd,
660*32002227SRobert Mustacchi req->io_rdata)) {
661*32002227SRobert Mustacchi i2cadm_fatal("failed to set read u64 request");
662*32002227SRobert Mustacchi }
663*32002227SRobert Mustacchi break;
664*32002227SRobert Mustacchi case I2CADM_IO_M_READ_BLOCK_I2C:
665*32002227SRobert Mustacchi if (!smbus_io_req_set_read_block_i2c(req->io_smbus, req->io_cmd,
666*32002227SRobert Mustacchi req->io_rdata, req->io_rlen)) {
667*32002227SRobert Mustacchi i2cadm_fatal("failed to set read block request");
668*32002227SRobert Mustacchi }
669*32002227SRobert Mustacchi break;
670*32002227SRobert Mustacchi case I2CADM_IO_M_SEND_U8:
671*32002227SRobert Mustacchi if (!smbus_io_req_set_send_byte(req->io_smbus,
672*32002227SRobert Mustacchi *(uint8_t *)req->io_wdata)) {
673*32002227SRobert Mustacchi i2cadm_fatal("failed to set send byte request");
674*32002227SRobert Mustacchi }
675*32002227SRobert Mustacchi break;
676*32002227SRobert Mustacchi case I2CADM_IO_M_WRITE_U8:
677*32002227SRobert Mustacchi if (!smbus_io_req_set_write_u8(req->io_smbus, req->io_cmd,
678*32002227SRobert Mustacchi *(uint8_t *)req->io_wdata)) {
679*32002227SRobert Mustacchi i2cadm_fatal("failed to set write byte request");
680*32002227SRobert Mustacchi }
681*32002227SRobert Mustacchi break;
682*32002227SRobert Mustacchi case I2CADM_IO_M_WRITE_U16:
683*32002227SRobert Mustacchi if (!smbus_io_req_set_write_u16(req->io_smbus, req->io_cmd,
684*32002227SRobert Mustacchi *(uint16_t *)req->io_wdata)) {
685*32002227SRobert Mustacchi i2cadm_fatal("failed to set write word request");
686*32002227SRobert Mustacchi }
687*32002227SRobert Mustacchi break;
688*32002227SRobert Mustacchi case I2CADM_IO_M_WRITE_U32:
689*32002227SRobert Mustacchi if (!smbus_io_req_set_write_u32(req->io_smbus, req->io_cmd,
690*32002227SRobert Mustacchi *(uint32_t *)req->io_wdata)) {
691*32002227SRobert Mustacchi i2cadm_fatal("failed to set write u32 request");
692*32002227SRobert Mustacchi }
693*32002227SRobert Mustacchi break;
694*32002227SRobert Mustacchi case I2CADM_IO_M_WRITE_U64:
695*32002227SRobert Mustacchi if (!smbus_io_req_set_write_u64(req->io_smbus, req->io_cmd,
696*32002227SRobert Mustacchi *(uint64_t *)req->io_wdata)) {
697*32002227SRobert Mustacchi i2cadm_fatal("failed to set write u64 request");
698*32002227SRobert Mustacchi }
699*32002227SRobert Mustacchi break;
700*32002227SRobert Mustacchi case I2CADM_IO_M_WRITE_BLOCK:
701*32002227SRobert Mustacchi case I2CADM_IO_M_WRITE_BLOCK_I2C:
702*32002227SRobert Mustacchi if (!smbus_io_req_set_write_block(req->io_smbus, req->io_cmd,
703*32002227SRobert Mustacchi req->io_wdata, req->io_wlen,
704*32002227SRobert Mustacchi mode->mode_val == I2CADM_IO_M_WRITE_BLOCK_I2C)) {
705*32002227SRobert Mustacchi i2cadm_fatal("failed to set write block request");
706*32002227SRobert Mustacchi }
707*32002227SRobert Mustacchi break;
708*32002227SRobert Mustacchi case I2CADM_IO_M_CALL:
709*32002227SRobert Mustacchi if (!smbus_io_req_set_process_call(req->io_smbus, req->io_cmd,
710*32002227SRobert Mustacchi *(uint16_t *)req->io_wdata, req->io_rdata)) {
711*32002227SRobert Mustacchi i2cadm_fatal("failed to set process call request");
712*32002227SRobert Mustacchi }
713*32002227SRobert Mustacchi break;
714*32002227SRobert Mustacchi }
715*32002227SRobert Mustacchi }
716*32002227SRobert Mustacchi
717*32002227SRobert Mustacchi static void
i2cadm_io_print(const i2cadm_io_req_t * req,const i2cadm_mode_info_t * mode)718*32002227SRobert Mustacchi i2cadm_io_print(const i2cadm_io_req_t *req, const i2cadm_mode_info_t *mode)
719*32002227SRobert Mustacchi {
720*32002227SRobert Mustacchi switch (mode->mode_val) {
721*32002227SRobert Mustacchi case I2CADM_IO_M_I2C:
722*32002227SRobert Mustacchi case I2CADM_IO_M_READ_BLOCK_I2C:
723*32002227SRobert Mustacchi /*
724*32002227SRobert Mustacchi * If we didn't actually get any bytes (READ BLOCK) or this
725*32002227SRobert Mustacchi * request didn't include a read (I2C), don't do anything.
726*32002227SRobert Mustacchi */
727*32002227SRobert Mustacchi if (req->io_rlen == 0)
728*32002227SRobert Mustacchi break;
729*32002227SRobert Mustacchi
730*32002227SRobert Mustacchi /*
731*32002227SRobert Mustacchi * While convention wants to include HDF_ADDR here, we do not
732*32002227SRobert Mustacchi * since we may be reading at some arbitrary offset via
733*32002227SRobert Mustacchi * registers. We're not going to try to interpret that.
734*32002227SRobert Mustacchi */
735*32002227SRobert Mustacchi (void) hexdump_file(req->io_rdata, req->io_rlen, HDF_HEADER |
736*32002227SRobert Mustacchi HDF_ASCII, stdout);
737*32002227SRobert Mustacchi break;
738*32002227SRobert Mustacchi case I2CADM_IO_M_RECV_U8:
739*32002227SRobert Mustacchi case I2CADM_IO_M_READ_U8:
740*32002227SRobert Mustacchi (void) printf("0x%x\n", *(uint8_t *)req->io_rdata);
741*32002227SRobert Mustacchi break;
742*32002227SRobert Mustacchi case I2CADM_IO_M_READ_U16:
743*32002227SRobert Mustacchi (void) printf("0x%x\n", *(uint16_t *)req->io_rdata);
744*32002227SRobert Mustacchi break;
745*32002227SRobert Mustacchi case I2CADM_IO_M_READ_U32:
746*32002227SRobert Mustacchi (void) printf("0x%x\n", *(uint32_t *)req->io_rdata);
747*32002227SRobert Mustacchi break;
748*32002227SRobert Mustacchi case I2CADM_IO_M_READ_U64:
749*32002227SRobert Mustacchi (void) printf("0x%" PRIx64 "\n", *(uint64_t *)req->io_rdata);
750*32002227SRobert Mustacchi break;
751*32002227SRobert Mustacchi default:
752*32002227SRobert Mustacchi VERIFY3U(req->io_rlen, ==, 0);
753*32002227SRobert Mustacchi break;
754*32002227SRobert Mustacchi }
755*32002227SRobert Mustacchi }
756*32002227SRobert Mustacchi
757*32002227SRobert Mustacchi int
i2cadm_io(int argc,char * argv[])758*32002227SRobert Mustacchi i2cadm_io(int argc, char *argv[])
759*32002227SRobert Mustacchi {
760*32002227SRobert Mustacchi int c, ofd = -1;
761*32002227SRobert Mustacchi const i2cadm_mode_info_t *mode = &i2cadm_io_modes[I2CADM_IO_M_I2C];
762*32002227SRobert Mustacchi const char *dpath = NULL, *addrstr = NULL, *cmdstr = NULL;
763*32002227SRobert Mustacchi const char *wstr = NULL, *rstr = NULL, *output = NULL;
764*32002227SRobert Mustacchi i2c_port_t *port;
765*32002227SRobert Mustacchi i2c_dev_info_t *info;
766*32002227SRobert Mustacchi i2c_addr_t addr;
767*32002227SRobert Mustacchi i2cadm_io_req_t req;
768*32002227SRobert Mustacchi
769*32002227SRobert Mustacchi while ((c = getopt(argc, argv, ":a:c:d:m:o:r:w:")) != -1) {
770*32002227SRobert Mustacchi switch (c) {
771*32002227SRobert Mustacchi case 'a':
772*32002227SRobert Mustacchi addrstr = optarg;
773*32002227SRobert Mustacchi break;
774*32002227SRobert Mustacchi case 'c':
775*32002227SRobert Mustacchi cmdstr = optarg;
776*32002227SRobert Mustacchi break;
777*32002227SRobert Mustacchi case 'd':
778*32002227SRobert Mustacchi dpath = optarg;
779*32002227SRobert Mustacchi break;
780*32002227SRobert Mustacchi case 'm':
781*32002227SRobert Mustacchi mode = i2cadm_io_parse_mode(optarg);
782*32002227SRobert Mustacchi break;
783*32002227SRobert Mustacchi case 'o':
784*32002227SRobert Mustacchi output = optarg;
785*32002227SRobert Mustacchi break;
786*32002227SRobert Mustacchi case 'r':
787*32002227SRobert Mustacchi rstr = optarg;
788*32002227SRobert Mustacchi break;
789*32002227SRobert Mustacchi case 'w':
790*32002227SRobert Mustacchi wstr = optarg;
791*32002227SRobert Mustacchi break;
792*32002227SRobert Mustacchi case ':':
793*32002227SRobert Mustacchi i2cadm_io_help("option -%c requires an argument",
794*32002227SRobert Mustacchi optopt);
795*32002227SRobert Mustacchi exit(EXIT_USAGE);
796*32002227SRobert Mustacchi case '?':
797*32002227SRobert Mustacchi i2cadm_io_help("unknown option: -%c", optopt);
798*32002227SRobert Mustacchi exit(EXIT_USAGE);
799*32002227SRobert Mustacchi }
800*32002227SRobert Mustacchi }
801*32002227SRobert Mustacchi
802*32002227SRobert Mustacchi /*
803*32002227SRobert Mustacchi * First establish that we have a valid destination and address that
804*32002227SRobert Mustacchi * we're targetting. If the user gives us a full path to a device, then
805*32002227SRobert Mustacchi * we don't want -a to be specified. if not, then we need -a
806*32002227SRobert Mustacchi * ("addrstr").
807*32002227SRobert Mustacchi */
808*32002227SRobert Mustacchi if (dpath == NULL) {
809*32002227SRobert Mustacchi errx(EXIT_FAILURE, "missing required destination path");
810*32002227SRobert Mustacchi }
811*32002227SRobert Mustacchi
812*32002227SRobert Mustacchi if (!i2c_port_dev_init_by_path(i2cadm.i2c_hdl, dpath, true, &port,
813*32002227SRobert Mustacchi &info)) {
814*32002227SRobert Mustacchi i2cadm_fatal("failed to parse path %s", dpath);
815*32002227SRobert Mustacchi }
816*32002227SRobert Mustacchi
817*32002227SRobert Mustacchi if (info != NULL) {
818*32002227SRobert Mustacchi if (addrstr != NULL) {
819*32002227SRobert Mustacchi errx(EXIT_FAILURE, "target address specified twice: "
820*32002227SRobert Mustacchi "either use an I2C path that specified a device or "
821*32002227SRobert Mustacchi "-a, not both");
822*32002227SRobert Mustacchi }
823*32002227SRobert Mustacchi
824*32002227SRobert Mustacchi addr = *i2c_device_info_addr_primary(info);
825*32002227SRobert Mustacchi i2c_device_info_free(info);
826*32002227SRobert Mustacchi info = NULL;
827*32002227SRobert Mustacchi } else {
828*32002227SRobert Mustacchi if (addrstr == NULL) {
829*32002227SRobert Mustacchi errx(EXIT_FAILURE, "missing target address: specify an "
830*32002227SRobert Mustacchi "I2C path that refers to a device or use -a");
831*32002227SRobert Mustacchi }
832*32002227SRobert Mustacchi
833*32002227SRobert Mustacchi if (!i2c_addr_parse(i2cadm.i2c_hdl, addrstr, &addr)) {
834*32002227SRobert Mustacchi i2cadm_fatal("failed to parse address %s", addrstr);
835*32002227SRobert Mustacchi }
836*32002227SRobert Mustacchi }
837*32002227SRobert Mustacchi
838*32002227SRobert Mustacchi bzero(&req, sizeof (req));
839*32002227SRobert Mustacchi req.io_mode = mode;
840*32002227SRobert Mustacchi if (mode->mode_val == I2CADM_IO_M_I2C) {
841*32002227SRobert Mustacchi if (!i2c_io_req_init(port, &req.io_i2c)) {
842*32002227SRobert Mustacchi i2cadm_fatal("failed to initialize I2C I/O request");
843*32002227SRobert Mustacchi }
844*32002227SRobert Mustacchi
845*32002227SRobert Mustacchi if (!i2c_io_req_set_addr(req.io_i2c, &addr)) {
846*32002227SRobert Mustacchi i2cadm_fatal("failed to set I2C request address");
847*32002227SRobert Mustacchi }
848*32002227SRobert Mustacchi } else {
849*32002227SRobert Mustacchi if (!smbus_io_req_init(port, &req.io_smbus)) {
850*32002227SRobert Mustacchi i2cadm_fatal("failed to initialize SMBus I/O request");
851*32002227SRobert Mustacchi }
852*32002227SRobert Mustacchi
853*32002227SRobert Mustacchi if (!smbus_io_req_set_addr(req.io_smbus, &addr)) {
854*32002227SRobert Mustacchi i2cadm_fatal("failed to set I2C request address");
855*32002227SRobert Mustacchi }
856*32002227SRobert Mustacchi }
857*32002227SRobert Mustacchi
858*32002227SRobert Mustacchi if (mode->mode_need_cmd) {
859*32002227SRobert Mustacchi const char *errstr = NULL;
860*32002227SRobert Mustacchi if (cmdstr == NULL) {
861*32002227SRobert Mustacchi errx(EXIT_FAILURE, "missing required SMBus command "
862*32002227SRobert Mustacchi "value (-c) for I/O mode %s", mode->mode_str);
863*32002227SRobert Mustacchi }
864*32002227SRobert Mustacchi req.io_cmd = (uint8_t)strtonumx(cmdstr, 0, UINT8_MAX, &errstr,
865*32002227SRobert Mustacchi 0);
866*32002227SRobert Mustacchi if (errstr != NULL) {
867*32002227SRobert Mustacchi errx(EXIT_FAILURE, "invalid command value (-c): %s "
868*32002227SRobert Mustacchi "is %s, valid values are between 0x00 and 0x%x",
869*32002227SRobert Mustacchi cmdstr, errstr, UINT8_MAX);
870*32002227SRobert Mustacchi }
871*32002227SRobert Mustacchi } else {
872*32002227SRobert Mustacchi if (cmdstr != NULL) {
873*32002227SRobert Mustacchi errx(EXIT_FAILURE, "I/O mode %s does not allow "
874*32002227SRobert Mustacchi "specifying an SMBus cmd (-c)", mode->mode_str);
875*32002227SRobert Mustacchi }
876*32002227SRobert Mustacchi }
877*32002227SRobert Mustacchi
878*32002227SRobert Mustacchi i2cadm_io_parse_rw_len(&req, rstr, wstr);
879*32002227SRobert Mustacchi argc -= optind;
880*32002227SRobert Mustacchi argv += optind;
881*32002227SRobert Mustacchi
882*32002227SRobert Mustacchi if (req.io_wlen == 0) {
883*32002227SRobert Mustacchi if (argc != 0) {
884*32002227SRobert Mustacchi errx(EXIT_USAGE, "encountered extraneous arguments "
885*32002227SRobert Mustacchi "starting with %s", argv[0]);
886*32002227SRobert Mustacchi }
887*32002227SRobert Mustacchi } else {
888*32002227SRobert Mustacchi i2cadm_io_parse_data(&req, argc, argv);
889*32002227SRobert Mustacchi }
890*32002227SRobert Mustacchi
891*32002227SRobert Mustacchi if (req.io_rlen != 0) {
892*32002227SRobert Mustacchi req.io_rdata = calloc(req.io_rlen, sizeof (uint8_t));
893*32002227SRobert Mustacchi if (req.io_rdata == NULL) {
894*32002227SRobert Mustacchi err(EXIT_FAILURE, "failed to allocate %u bytes for "
895*32002227SRobert Mustacchi "request read buffer", req.io_rlen);
896*32002227SRobert Mustacchi }
897*32002227SRobert Mustacchi
898*32002227SRobert Mustacchi if (output != NULL) {
899*32002227SRobert Mustacchi ofd = open(output, O_RDWR | O_TRUNC | O_CREAT);
900*32002227SRobert Mustacchi if (ofd < 0) {
901*32002227SRobert Mustacchi err(EXIT_FAILURE, "failed to open ouput "
902*32002227SRobert Mustacchi "file (-o) %s", output);
903*32002227SRobert Mustacchi }
904*32002227SRobert Mustacchi }
905*32002227SRobert Mustacchi } else if (output != NULL) {
906*32002227SRobert Mustacchi errx(EXIT_FAILURE, "cannot specify output file -o when no "
907*32002227SRobert Mustacchi "data is being read");
908*32002227SRobert Mustacchi }
909*32002227SRobert Mustacchi
910*32002227SRobert Mustacchi i2cadm_io_init(&req, mode);
911*32002227SRobert Mustacchi
912*32002227SRobert Mustacchi if (req.io_i2c != NULL) {
913*32002227SRobert Mustacchi if (!i2c_io_req_exec(req.io_i2c)) {
914*32002227SRobert Mustacchi i2cadm_fatal("failed to execute I2C request");
915*32002227SRobert Mustacchi }
916*32002227SRobert Mustacchi } else {
917*32002227SRobert Mustacchi if (!smbus_io_req_exec(req.io_smbus)) {
918*32002227SRobert Mustacchi i2cadm_fatal("failed to execute SMBus request");
919*32002227SRobert Mustacchi }
920*32002227SRobert Mustacchi }
921*32002227SRobert Mustacchi
922*32002227SRobert Mustacchi if (ofd != -1) {
923*32002227SRobert Mustacchi i2cadm_io_write(&req, mode, ofd);
924*32002227SRobert Mustacchi (void) close(ofd);
925*32002227SRobert Mustacchi } else {
926*32002227SRobert Mustacchi i2cadm_io_print(&req, mode);
927*32002227SRobert Mustacchi }
928*32002227SRobert Mustacchi
929*32002227SRobert Mustacchi if (req.io_i2c != NULL) {
930*32002227SRobert Mustacchi i2c_io_req_fini(req.io_i2c);
931*32002227SRobert Mustacchi }
932*32002227SRobert Mustacchi if (req.io_smbus != NULL) {
933*32002227SRobert Mustacchi smbus_io_req_fini(req.io_smbus);
934*32002227SRobert Mustacchi }
935*32002227SRobert Mustacchi
936*32002227SRobert Mustacchi free(req.io_wdata);
937*32002227SRobert Mustacchi free(req.io_rdata);
938*32002227SRobert Mustacchi i2c_port_fini(port);
939*32002227SRobert Mustacchi return (EXIT_SUCCESS);
940*32002227SRobert Mustacchi }
941