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