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 controller related operations. 18 */ 19 20 #include <stdio.h> 21 #include <stdarg.h> 22 #include <string.h> 23 #include <err.h> 24 #include <sys/sysmacros.h> 25 #include <ofmt.h> 26 #include <sys/ilstr.h> 27 #include <sys/debug.h> 28 29 #include "i2cadm.h" 30 31 /* 32 * Various property conversion routines. These could also potentially be in 33 * libi2c if we find it useful for other consumers. 34 */ 35 typedef struct op_map { 36 uint32_t om_op; 37 const char *om_name; 38 } op_map_t; 39 40 static const op_map_t speed_op_map[] = { 41 { I2C_SPEED_STD, "standard" }, 42 { I2C_SPEED_FAST, "fast" }, 43 { I2C_SPEED_FPLUS, "fast-plus" }, 44 { I2C_SPEED_HIGH, "high" }, 45 { I2C_SPEED_ULTRA, "ultra" } 46 }; 47 48 static const op_map_t type_op_map[] = { 49 { I2C_CTRL_TYPE_I2C, "i2c" }, 50 { I2C_CTRL_TYPE_I3C, "i3c" }, 51 { I2C_CTRL_TYPE_SMBUS, "smbus" } 52 }; 53 54 static const op_map_t smbus_op_map[] = { 55 { SMBUS_PROP_OP_QUICK_COMMAND, "quick" }, 56 { SMBUS_PROP_OP_SEND_BYTE, "send-byte", }, 57 { SMBUS_PROP_OP_RECV_BYTE, "recv-byte" }, 58 { SMBUS_PROP_OP_WRITE_BYTE, "write-byte" }, 59 { SMBUS_PROP_OP_READ_BYTE, "read-byte" }, 60 { SMBUS_PROP_OP_WRITE_WORD, "write-word" }, 61 { SMBUS_PROP_OP_READ_WORD, "read-word" }, 62 { SMBUS_PROP_OP_PROCESS_CALL, "process-call" }, 63 { SMBUS_PROP_OP_WRITE_BLOCK, "write-block" }, 64 { SMBUS_PROP_OP_READ_BLOCK, "read-block" }, 65 { SMBUS_PROP_OP_HOST_NOTIFY, "host-notify" }, 66 { SMBUS_PROP_OP_BLOCK_PROCESS_CALL, "block-call" }, 67 { SMBUS_PROP_OP_WRITE_U32, "write-u32" }, 68 { SMBUS_PROP_OP_READ_U32, "read-u32" }, 69 { SMBUS_PROP_OP_WRITE_U64, "write-u64" }, 70 { SMBUS_PROP_OP_READ_U64, "read-u64" }, 71 { SMBUS_PROP_OP_I2C_WRITE_BLOCK, "write-i2c-block" }, 72 { SMBUS_PROP_OP_I2C_READ_BLOCK, "read-i2c-block" } 73 }; 74 75 static boolean_t 76 i2cadm_map_to_str_one(uint32_t val, char *buf, uint_t buflen, 77 const op_map_t *map, size_t nents) 78 { 79 if (val == 0) { 80 return (strlcpy(buf, "--", buflen) < buflen); 81 } 82 83 for (size_t i = 0; i < nents; i++) { 84 if (map[i].om_op == val) { 85 return (strlcpy(buf, map[i].om_name, buflen) < buflen); 86 } 87 } 88 89 return (B_FALSE); 90 } 91 92 static boolean_t 93 i2cadm_map_to_str(uint32_t val, char *buf, uint_t buflen, const op_map_t *map, 94 size_t nents) 95 { 96 ilstr_t ilstr; 97 98 if (val == 0) { 99 return (strlcpy(buf, "--", buflen) < buflen); 100 } 101 102 ilstr_init_prealloc(&ilstr, buf, buflen); 103 104 for (size_t i = 0; i < nents; i++) { 105 if ((val & map[i].om_op) == 0) 106 continue; 107 108 val &= ~map[i].om_op; 109 if (i > 0) { 110 ilstr_append_char(&ilstr, ','); 111 } 112 ilstr_append_str(&ilstr, map[i].om_name); 113 } 114 115 if (val != 0) { 116 char str[32]; 117 (void) snprintf(str, sizeof (str), ",0x%x", val); 118 119 if (ilstr_len(&ilstr) > 0) { 120 ilstr_append_char(&ilstr, ','); 121 } 122 ilstr_append_str(&ilstr, str); 123 } 124 125 ilstr_errno_t err = ilstr_errno(&ilstr); 126 ilstr_fini(&ilstr); 127 return (err == ILSTR_ERROR_OK); 128 } 129 130 static boolean_t 131 i2cadm_value_print(i2c_prop_info_t *info, uint32_t val, char *buf, 132 uint_t buflen) 133 { 134 uint_t len; 135 136 switch (i2c_prop_info_id(info)) { 137 case I2C_PROP_BUS_SPEED: 138 return (i2cadm_map_to_str_one(val, buf, buflen, speed_op_map, 139 ARRAY_SIZE(speed_op_map))); 140 case I2C_PROP_TYPE: 141 return (i2cadm_map_to_str_one(val, buf, buflen, type_op_map, 142 ARRAY_SIZE(type_op_map))); 143 case SMBUS_PROP_SUP_OPS: 144 return (i2cadm_map_to_str(val, buf, buflen, smbus_op_map, 145 ARRAY_SIZE(smbus_op_map))); 146 default: 147 len = snprintf(buf, buflen, "%u", val); 148 } 149 150 return (len < buflen); 151 } 152 153 static boolean_t 154 i2cadm_value_print_pos_u32(const i2c_prop_range_t *range, char *buf, 155 uint_t buflen) 156 { 157 ilstr_t ilstr; 158 159 ilstr_init_prealloc(&ilstr, buf, buflen); 160 for (uint32_t i = 0; i < range->ipr_count; i++) { 161 const i2c_prop_u32_range_t *r; 162 char str[64]; 163 164 r = &range->ipr_range[i].ipvr_u32; 165 if (r->ipur_min == r->ipur_max) { 166 (void) snprintf(str, sizeof (str), "u", r->ipur_min); 167 } else { 168 (void) snprintf(str, sizeof (str), "%u-%u", r->ipur_min, 169 r->ipur_max); 170 } 171 if (i > 0) { 172 ilstr_append_char(&ilstr, ','); 173 } 174 ilstr_append_str(&ilstr, str); 175 } 176 177 ilstr_errno_t err = ilstr_errno(&ilstr); 178 ilstr_fini(&ilstr); 179 return (err == ILSTR_ERROR_OK); 180 } 181 182 static boolean_t 183 i2cadm_value_print_pos_bit32(i2c_prop_info_t *info, 184 const i2c_prop_range_t *range, char *buf, uint_t buflen) 185 { 186 if (range->ipr_count != 1) { 187 return (B_FALSE); 188 } 189 190 uint32_t val = range->ipr_range[0].ipvr_bit32; 191 switch (i2c_prop_info_id(info)) { 192 case I2C_PROP_BUS_SPEED: 193 return (i2cadm_map_to_str(val, buf, buflen, speed_op_map, 194 ARRAY_SIZE(speed_op_map))); 195 case SMBUS_PROP_SUP_OPS: 196 return (i2cadm_map_to_str(val, buf, buflen, smbus_op_map, 197 ARRAY_SIZE(smbus_op_map))); 198 default: 199 return (snprintf(buf, buflen, "0x%x", val) < buflen); 200 } 201 } 202 203 static boolean_t 204 i2cadm_value_print_pos(i2c_prop_info_t *info, char *buf, uint_t buflen) 205 { 206 uint_t len; 207 const i2c_prop_range_t *range = i2c_prop_info_pos(info); 208 209 if (range == NULL) { 210 if (i2c_err(i2cadm.i2c_hdl) == I2C_ERR_PROP_UNSUP) { 211 return (strlcpy(buf, "--", buflen) < buflen); 212 } 213 return (B_FALSE); 214 } 215 216 if (range->ipr_count == 0) { 217 return (strlcpy(buf, "--", buflen) < buflen); 218 } 219 220 switch (range->ipr_type) { 221 case I2C_PROP_TYPE_U32: 222 return (i2cadm_value_print_pos_u32(range, buf, buflen)); 223 case I2C_PROP_TYPE_BIT32: 224 return (i2cadm_value_print_pos_bit32(info, range, buf, buflen)); 225 default: 226 return (B_FALSE); 227 } 228 229 return (len <= buflen); 230 } 231 232 static void 233 i2cadm_controller_prop_get_usage(FILE *f) 234 { 235 (void) fprintf(stderr, "\ti2cadm controller prop get [-Hp] " 236 "[-o field[,...] <controller> [filter]\n"); 237 } 238 239 static void 240 i2cadm_controller_prop_get_help(const char *fmt, ...) 241 { 242 if (fmt != NULL) { 243 va_list ap; 244 245 va_start(ap, fmt); 246 vwarnx(fmt, ap); 247 va_end(ap); 248 } 249 250 (void) fprintf(stderr, "Usage: i2cadm controller prop get [-H] " 251 "[-o field[,...] [-p]] <controller> [filter...]\n\n"); 252 (void) fprintf(stderr, "List properties on the specified controller. " 253 "Each <filter> selects a property\nbased on its name. When " 254 "multiple filters are specified, they are treated like\nan OR. It " 255 "is an error if a filter isn't used.\n\n" 256 "\t-H\t\tomit the column header\n" 257 "\t-o field\toutput fields to print\n" 258 "\t-p\t\tparseable output (requires -o)\n"); 259 (void) fprintf(stderr, "\nThe following fields are supported:\n" 260 "\tproperty\tthe name of the property\n" 261 "\tperm\t\tthe property's permissions\n" 262 "\tvalue\t\tthe property's value\n" 263 "\tdefault\t\tthe property's default value\n" 264 "\tpossible\tthe property's possible values\n" 265 "\ttype\t\tthe property's type\n" 266 "\tctrl\t\tthe name of the controller\n" 267 "\tid\t\tthe system id for the property\n"); 268 } 269 270 typedef enum { 271 I2CADM_CTRL_PROP_GET_PROP, 272 I2CADM_CTRL_PROP_GET_PERM, 273 I2CADM_CTRL_PROP_GET_VALUE, 274 I2CADM_CTRL_PROP_GET_DEF, 275 I2CADM_CTRL_PROP_GET_POS, 276 I2CADM_CTRL_PROP_GET_TYPE, 277 I2CADM_CTRL_PROP_GET_CTRL, 278 I2CADM_CTRL_PROP_GET_ID 279 } i2cadm_ctrl_prop_get_otype_t; 280 281 typedef struct i2cadm_ctrl_prop_get_ofmt { 282 const char *icpg_ctrl; 283 i2c_prop_info_t *icpg_info; 284 bool icpg_valid; 285 uint32_t icpg_u32; 286 } i2cadm_ctrl_prop_get_ofmt_t; 287 288 static boolean_t 289 i2cadm_ctrl_prop_get_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen) 290 { 291 i2cadm_ctrl_prop_get_ofmt_t *arg = ofarg->ofmt_cbarg; 292 size_t len; 293 uint32_t def; 294 295 switch (ofarg->ofmt_id) { 296 case I2CADM_CTRL_PROP_GET_PROP: 297 len = strlcpy(buf, i2c_prop_info_name(arg->icpg_info), buflen); 298 break; 299 case I2CADM_CTRL_PROP_GET_PERM: 300 switch (i2c_prop_info_perm(arg->icpg_info)) { 301 case I2C_PROP_PERM_RO: 302 len = strlcpy(buf, "r-", buflen); 303 break; 304 case I2C_PROP_PERM_RW: 305 len = strlcpy(buf, "rw", buflen); 306 break; 307 default: 308 return (B_FALSE); 309 } 310 break; 311 case I2CADM_CTRL_PROP_GET_VALUE: 312 if (!arg->icpg_valid) { 313 len = strlcpy(buf, "--", buflen); 314 break; 315 } 316 317 return (i2cadm_value_print(arg->icpg_info, arg->icpg_u32, buf, 318 buflen)); 319 case I2CADM_CTRL_PROP_GET_DEF: 320 if (!i2c_prop_info_def_u32(arg->icpg_info, &def)) { 321 len = strlcpy(buf, "--", buflen); 322 break; 323 } 324 325 return (i2cadm_value_print(arg->icpg_info, def, buf, buflen)); 326 case I2CADM_CTRL_PROP_GET_POS: 327 return (i2cadm_value_print_pos(arg->icpg_info, buf, buflen)); 328 case I2CADM_CTRL_PROP_GET_TYPE: 329 switch (i2c_prop_info_type(arg->icpg_info)) { 330 case I2C_PROP_TYPE_U32: 331 len = strlcpy(buf, "u32", buflen); 332 break; 333 case I2C_PROP_TYPE_BIT32: 334 len = strlcpy(buf, "bit32", buflen); 335 break; 336 default: 337 return (B_FALSE); 338 } 339 break; 340 case I2CADM_CTRL_PROP_GET_CTRL: 341 len = strlcpy(buf, arg->icpg_ctrl, buflen); 342 break; 343 case I2CADM_CTRL_PROP_GET_ID: 344 len = snprintf(buf, buflen, "%u", 345 i2c_prop_info_id(arg->icpg_info)); 346 break; 347 default: 348 return (B_FALSE); 349 } 350 351 return (len < buflen); 352 } 353 354 static const char *i2cadm_ctrl_prop_get_fields = 355 "property,perm,value,default,possible"; 356 static const ofmt_field_t i2cadm_ctrl_prop_get_ofmt[] = { 357 { "PROPERTY", 20, I2CADM_CTRL_PROP_GET_PROP, 358 i2cadm_ctrl_prop_get_ofmt_cb }, 359 { "PERM", 6, I2CADM_CTRL_PROP_GET_PERM, i2cadm_ctrl_prop_get_ofmt_cb }, 360 { "VALUE", 16, I2CADM_CTRL_PROP_GET_VALUE, 361 i2cadm_ctrl_prop_get_ofmt_cb }, 362 { "DEFAULT", 16, I2CADM_CTRL_PROP_GET_DEF, 363 i2cadm_ctrl_prop_get_ofmt_cb }, 364 { "POSSIBLE", 16, I2CADM_CTRL_PROP_GET_POS, 365 i2cadm_ctrl_prop_get_ofmt_cb }, 366 { "TYPE", 6, I2CADM_CTRL_PROP_GET_TYPE, i2cadm_ctrl_prop_get_ofmt_cb }, 367 { "CONTROLLER", 12, I2CADM_CTRL_PROP_GET_CTRL, 368 i2cadm_ctrl_prop_get_ofmt_cb }, 369 { "ID", 8, I2CADM_CTRL_PROP_GET_ID, i2cadm_ctrl_prop_get_ofmt_cb }, 370 { NULL, 0, 0, NULL } 371 }; 372 373 static int 374 i2cadm_controller_prop_get(int argc, char *argv[]) 375 { 376 int c, ret = EXIT_SUCCESS; 377 uint_t flags = 0; 378 boolean_t parse = B_FALSE; 379 const char *fields = NULL, *cname; 380 i2c_ctrl_t *ctrl; 381 bool *filts = NULL; 382 ofmt_status_t oferr; 383 ofmt_handle_t ofmt; 384 385 while ((c = getopt(argc, argv, ":Ho:p")) != -1) { 386 switch (c) { 387 case 'H': 388 flags |= OFMT_NOHEADER; 389 break; 390 case 'o': 391 fields = optarg; 392 break; 393 case 'p': 394 parse = B_TRUE; 395 flags |= OFMT_PARSABLE; 396 break; 397 case ':': 398 i2cadm_controller_prop_get_help("option -%c requires " 399 "an argument", optopt); 400 exit(EXIT_USAGE); 401 case '?': 402 i2cadm_controller_prop_get_help("unknown option: -%c", 403 optopt); 404 exit(EXIT_USAGE); 405 } 406 } 407 408 if (parse && fields == NULL) { 409 errx(EXIT_USAGE, "-p requires fields specified with -o"); 410 } 411 412 if (!parse) { 413 flags |= OFMT_WRAP; 414 } 415 416 if (fields == NULL) { 417 fields = i2cadm_ctrl_prop_get_fields; 418 } 419 420 argc -= optind; 421 argv += optind; 422 if (argc == 0) { 423 errx(EXIT_FAILURE, "missing required controller"); 424 } 425 426 cname = argv[0]; 427 argc--; 428 argv++; 429 if (!i2c_ctrl_init_by_path(i2cadm.i2c_hdl, cname, &ctrl)) { 430 i2cadm_fatal("failed to initialize controller %s", cname); 431 } 432 433 if (argc > 0) { 434 filts = calloc(argc, sizeof (bool)); 435 if (filts == NULL) { 436 err(EXIT_FAILURE, "failed to allocate memory for " 437 "filter tracking"); 438 } 439 } 440 441 oferr = ofmt_open(fields, i2cadm_ctrl_prop_get_ofmt, flags, 0, &ofmt); 442 ofmt_check(oferr, parse, ofmt, i2cadm_ofmt_errx, warnx); 443 444 uint32_t nprops = i2c_ctrl_nprops(ctrl); 445 for (uint32_t i = 0; i < nprops; i++) { 446 i2c_prop_info_t *info; 447 i2cadm_ctrl_prop_get_ofmt_t arg; 448 449 if (!i2c_prop_info(ctrl, i, &info)) { 450 i2cadm_warn("failed to get property %u information", i); 451 ret = EXIT_FAILURE; 452 continue; 453 } 454 455 if (argc > 0) { 456 const char *name = i2c_prop_info_name(info); 457 bool match = false; 458 459 for (int i = 0; i < argc; i++) { 460 if (strcmp(argv[i], name) == 0) { 461 match = true; 462 filts[i] = true; 463 } 464 } 465 466 if (!match) { 467 i2c_prop_info_free(info); 468 continue; 469 } 470 } 471 472 (void) memset(&arg, 0, sizeof (arg)); 473 arg.icpg_ctrl = cname; 474 arg.icpg_info = info; 475 476 if (i2c_prop_info_sup(info)) { 477 i2c_prop_type_t type = i2c_prop_info_type(info); 478 if (type == I2C_PROP_TYPE_U32 || 479 type == I2C_PROP_TYPE_BIT32) { 480 size_t len = sizeof (uint32_t); 481 if (!i2c_prop_get(ctrl, i, &arg.icpg_u32, 482 &len)) { 483 i2cadm_warn("failed to get property " 484 "%s (%u)", i2c_prop_info_name(info), 485 i); 486 ret = EXIT_FAILURE; 487 } else if (len != sizeof (uint32_t)) { 488 warnx("property %s (%u) returned " 489 "unexpected property size of %zu, " 490 "but %zu was expected, unable to " 491 "print value", 492 i2c_prop_info_name(info), i, len, 493 sizeof (uint32_t)); 494 ret = EXIT_FAILURE; 495 } else { 496 arg.icpg_valid = true; 497 } 498 } else { 499 warnx("property %s (%u) has unknown type 0x%x, " 500 "cannot get or display value", 501 i2c_prop_info_name(info), i, type); 502 ret = EXIT_FAILURE; 503 } 504 } 505 506 ofmt_print(ofmt, &arg); 507 free(info); 508 } 509 510 for (int i = 0; i < argc; i++) { 511 if (!filts[i]) { 512 warnx("filter '%s' did not match any properties", 513 argv[i]); 514 ret = EXIT_FAILURE; 515 } 516 } 517 518 free(filts); 519 ofmt_close(ofmt); 520 i2c_ctrl_fini(ctrl); 521 return (ret); 522 } 523 524 static void 525 i2cadm_controller_prop_set_usage(FILE *f) 526 { 527 (void) fprintf(stderr, "\ti2cadm controller prop set <controller> " 528 "<property>=<value>\n"); 529 } 530 531 static int 532 i2cadm_controller_prop_set(int argc, char *argv[]) 533 { 534 i2c_ctrl_t *ctrl; 535 i2c_prop_info_t *info; 536 char *prop, *val; 537 size_t buflen = 0; 538 void *buf = NULL; 539 540 if (argc == 0) { 541 errx(EXIT_FAILURE, "missing required controller and property"); 542 } else if (argc == 1) { 543 errx(EXIT_FAILURE, "missing required property"); 544 } else if (argc > 2) { 545 errx(EXIT_FAILURE, "only one property can be set at a time, " 546 "extraneous arguments start with %s", argv[2]); 547 } 548 549 if (!i2c_ctrl_init_by_path(i2cadm.i2c_hdl, argv[0], &ctrl)) { 550 i2cadm_fatal("failed to initialize controller %s", argv[0]); 551 } 552 553 prop = argv[1]; 554 val = strchr(prop, '='); 555 if (val == NULL) { 556 errx(EXIT_FAILURE, "could not parse property name and value " 557 "from %s: missing = separator", argv[1]); 558 } 559 *val = '\0'; 560 val++; 561 562 if (!i2c_prop_info_by_name(ctrl, prop, &info)) { 563 i2cadm_fatal("failed to get information for property %s", prop); 564 } 565 566 if (!i2c_prop_info_sup(info)) { 567 errx(EXIT_FAILURE, "controller %s does not support property %s", 568 argv[0], prop); 569 } 570 571 if (i2c_prop_info_perm(info) != I2C_PROP_PERM_RW) { 572 errx(EXIT_FAILURE, "property %s is read-only on controller %s", 573 prop, argv[0]); 574 } 575 576 /* 577 * See if this property is one that we parse via string transformations. 578 */ 579 switch (i2c_prop_info_id(info)) { 580 case I2C_PROP_BUS_SPEED: 581 for (size_t i = 0; i < ARRAY_SIZE(speed_op_map); i++) { 582 if (strcmp(val, speed_op_map[i].om_name) == 0) { 583 buflen = sizeof (uint32_t); 584 buf = calloc(1, buflen); 585 if (buf == NULL) { 586 errx(EXIT_FAILURE, "failed to allocate " 587 "%zu bytes of memory to hold %s " 588 "property value", buflen, prop); 589 } 590 591 (void) memcpy(buf, &speed_op_map[i].om_op, 592 sizeof (uint32_t)); 593 } 594 } 595 break; 596 default: 597 break; 598 } 599 600 if (buf == NULL) { 601 uint32_t u32; 602 const char *errstr; 603 604 switch (i2c_prop_info_type(info)) { 605 case I2C_PROP_TYPE_U32: 606 case I2C_PROP_TYPE_BIT32: 607 u32 = (uint32_t)strtonumx(val, 0, UINT32_MAX, &errstr, 608 0); 609 if (errstr != NULL) { 610 errx(EXIT_FAILURE, "invalid 32-bit %s property " 611 "values: %s is %s", prop, val, errstr); 612 } 613 614 buflen = sizeof (uint32_t); 615 buf = calloc(1, buflen); 616 if (buf == NULL) { 617 errx(EXIT_FAILURE, "failed to allocate %zu " 618 "bytes of memory to hold %s property value", 619 buflen, prop); 620 } 621 622 (void) memcpy(buf, &u32, sizeof (uint32_t)); 623 break; 624 default: 625 errx(EXIT_FAILURE, "unable to parse property %s type " 626 "0x%x", prop, i2c_prop_info_type(info)); 627 628 } 629 } 630 631 if (!i2c_prop_set(ctrl, i2c_prop_info_id(info), buf, buflen)) { 632 i2cadm_fatal("failed to set property %s to %s", prop, val); 633 } 634 635 i2c_prop_info_free(info); 636 i2c_ctrl_fini(ctrl); 637 return (EXIT_SUCCESS); 638 } 639 640 static i2cadm_cmdtab_t i2cadm_ctrl_prop_cmds[] = { 641 { "get", i2cadm_controller_prop_get, i2cadm_controller_prop_get_usage }, 642 { "set", i2cadm_controller_prop_set, i2cadm_controller_prop_set_usage } 643 }; 644 645 static int 646 i2cadm_controller_prop(int argc, char *argv[]) 647 { 648 return (i2cadm_walk_tab(i2cadm_ctrl_prop_cmds, 649 ARRAY_SIZE(i2cadm_ctrl_prop_cmds), argc, argv)); 650 } 651 652 static void 653 i2cadm_controller_prop_usage(FILE *f) 654 { 655 i2cadm_walk_usage(i2cadm_ctrl_prop_cmds, 656 ARRAY_SIZE(i2cadm_ctrl_prop_cmds), f); 657 } 658 659 static void 660 i2cadm_controller_list_usage(FILE *f) 661 { 662 (void) fprintf(f, "\ti2cadm controller list [-H] [-o field,[...] [-p]] " 663 "[filter]\n"); 664 } 665 666 static void 667 i2cadm_controller_list_help(const char *fmt, ...) 668 { 669 if (fmt != NULL) { 670 va_list ap; 671 672 va_start(ap, fmt); 673 vwarnx(fmt, ap); 674 va_end(ap); 675 } 676 677 (void) fprintf(stderr, "Usage: i2cadm controller list [-H] " 678 "[-o field[,...] [-p]] [filter...]\n\n"); 679 (void) fprintf(stderr, "List I2C Controllers. Each <filter> selects a " 680 "set of controllers to show and\ncan be a controller or driver " 681 "name. When multiple filters are specified, they\nare treated like " 682 "an OR. It is an error if a filter isn't used.\n\n" 683 "\t-H\t\tomit the column header\n" 684 "\t-o field\toutput fields to print\n" 685 "\t-p\t\tparseable output (requires -o)\n"); 686 (void) fprintf(stderr, "\nThe following fields are supported:\n" 687 "\tname\t\tthe controller's name\n" 688 "\ttype\t\tthe controller's type (e.g. i2c, smbus)\n" 689 "\tspeed\t\tthe controller's current speed\n" 690 "\tdriver\t\tthe name of the driver for the controller\n" 691 "\tinstance\tthe driver instance for the controller\n" 692 "\tprovider\tthe /devices path of the provider\n"); 693 } 694 695 typedef enum { 696 I2CADM_CTRL_LIST_NAME, 697 I2CADM_CTRL_LIST_TYPE, 698 I2CADM_CTRL_LIST_SPEED, 699 I2CADM_CTRL_LIST_NPORTS, 700 I2CADM_CTRL_LIST_DRIVER, 701 I2CADM_CTRL_LIST_INSTANCE, 702 I2CADM_CTRL_LIST_PROVIDER 703 } i2cadm_ctrl_list_otype_t; 704 705 typedef struct i2cadm_ctrl_list_ofmt { 706 di_node_t icl_nexus; 707 di_node_t icl_drv; 708 i2c_ctrl_t *icl_ctrl; 709 uint32_t icl_speed; 710 uint32_t icl_type; 711 uint32_t icl_nports; 712 } i2cadm_ctrl_list_ofmt_t; 713 714 static boolean_t 715 i2cadm_ctrl_list_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen) 716 { 717 i2cadm_ctrl_list_ofmt_t *arg = ofarg->ofmt_cbarg; 718 size_t len; 719 720 switch (ofarg->ofmt_id) { 721 case I2CADM_CTRL_LIST_NAME: 722 len = strlcat(buf, di_bus_addr(arg->icl_nexus), buflen); 723 break; 724 case I2CADM_CTRL_LIST_TYPE: 725 return (i2cadm_map_to_str_one(arg->icl_type, buf, buflen, 726 type_op_map, ARRAY_SIZE(type_op_map))); 727 case I2CADM_CTRL_LIST_SPEED: 728 return (i2cadm_map_to_str_one(arg->icl_speed, buf, buflen, 729 speed_op_map, ARRAY_SIZE(speed_op_map))); 730 case I2CADM_CTRL_LIST_NPORTS: 731 len = snprintf(buf, buflen, "%u", arg->icl_nports); 732 break; 733 case I2CADM_CTRL_LIST_DRIVER: 734 len = strlcat(buf, di_driver_name(arg->icl_drv), buflen); 735 break; 736 case I2CADM_CTRL_LIST_INSTANCE: 737 len = snprintf(buf, buflen, "%s%d", 738 di_driver_name(arg->icl_drv), di_instance(arg->icl_drv)); 739 break; 740 case I2CADM_CTRL_LIST_PROVIDER: 741 len = strlcat(buf, i2c_ctrl_path(arg->icl_ctrl), buflen); 742 break; 743 default: 744 return (B_FALSE); 745 } 746 747 return (len < buflen); 748 } 749 750 static const char *i2cadm_ctrl_list_fields = "name,type,speed,nports,provider"; 751 static const ofmt_field_t i2cadm_ctrl_list_ofmt[] = { 752 { "NAME", 12, I2CADM_CTRL_LIST_NAME, i2cadm_ctrl_list_ofmt_cb }, 753 { "TYPE", 10, I2CADM_CTRL_LIST_TYPE, i2cadm_ctrl_list_ofmt_cb }, 754 { "SPEED", 12, I2CADM_CTRL_LIST_SPEED, i2cadm_ctrl_list_ofmt_cb }, 755 { "NPORTS", 8, I2CADM_CTRL_LIST_NPORTS, i2cadm_ctrl_list_ofmt_cb }, 756 { "DRIVER", 10, I2CADM_CTRL_LIST_DRIVER, i2cadm_ctrl_list_ofmt_cb }, 757 { "INSTANCE", 16, I2CADM_CTRL_LIST_INSTANCE, i2cadm_ctrl_list_ofmt_cb }, 758 { "PROVIDER", 40, I2CADM_CTRL_LIST_PROVIDER, i2cadm_ctrl_list_ofmt_cb }, 759 { NULL, 0, 0, NULL } 760 }; 761 762 static int 763 i2cadm_controller_list(int argc, char *argv[]) 764 { 765 int c, ret = EXIT_SUCCESS; 766 uint_t flags = 0; 767 boolean_t parse = B_FALSE; 768 const char *fields = NULL; 769 bool *filts = NULL, print = false; 770 ofmt_status_t oferr; 771 ofmt_handle_t ofmt; 772 i2c_ctrl_iter_t *iter; 773 i2c_iter_t iret; 774 const i2c_ctrl_disc_t *disc; 775 776 while ((c = getopt(argc, argv, ":Ho:p")) != -1) { 777 switch (c) { 778 case 'H': 779 flags |= OFMT_NOHEADER; 780 break; 781 case 'o': 782 fields = optarg; 783 break; 784 case 'p': 785 parse = B_TRUE; 786 flags |= OFMT_PARSABLE; 787 break; 788 case ':': 789 i2cadm_controller_list_help("option -%c requires an " 790 "argument", optopt); 791 exit(EXIT_USAGE); 792 case '?': 793 i2cadm_controller_list_help("unknown option: -%c", 794 optopt); 795 exit(EXIT_USAGE); 796 } 797 } 798 799 if (parse && fields == NULL) { 800 errx(EXIT_USAGE, "-p requires fields specified with -o"); 801 } 802 803 if (!parse) { 804 flags |= OFMT_WRAP; 805 } 806 807 if (fields == NULL) { 808 fields = i2cadm_ctrl_list_fields; 809 } 810 811 argc -= optind; 812 argv += optind; 813 814 if (argc > 0) { 815 filts = calloc(argc, sizeof (bool)); 816 if (filts == NULL) { 817 err(EXIT_FAILURE, "failed to allocate memory for " 818 "filter tracking"); 819 } 820 } 821 822 oferr = ofmt_open(fields, i2cadm_ctrl_list_ofmt, flags, 0, &ofmt); 823 ofmt_check(oferr, parse, ofmt, i2cadm_ofmt_errx, warnx); 824 825 if (!i2c_ctrl_discover_init(i2cadm.i2c_hdl, &iter)) { 826 i2cadm_fatal("failed to initialize controller walk"); 827 } 828 829 while ((iret = i2c_ctrl_discover_step(iter, &disc)) == I2C_ITER_VALID) { 830 i2cadm_ctrl_list_ofmt_t arg; 831 i2c_ctrl_t *ctrl; 832 833 (void) memset(&arg, 0, sizeof (arg)); 834 arg.icl_nexus = i2c_ctrl_disc_devi(disc); 835 arg.icl_drv = di_parent_node(arg.icl_nexus); 836 837 if (argc > 0) { 838 const char *name = di_bus_addr(arg.icl_nexus); 839 const char *drv = di_driver_name(arg.icl_drv); 840 bool match = false; 841 842 for (int i = 0; i < argc; i++) { 843 if (strcmp(argv[i], name) == 0 || 844 strcmp(argv[i], drv) == 0) { 845 match = true; 846 filts[i] = true; 847 } 848 } 849 850 if (!match) { 851 continue; 852 } 853 } 854 855 if (!i2c_ctrl_init(i2cadm.i2c_hdl, arg.icl_nexus, &ctrl)) { 856 i2cadm_warn("failed to initialize controller %s", 857 di_bus_addr(arg.icl_nexus)); 858 continue; 859 } 860 861 size_t len = sizeof (uint32_t); 862 if (!i2c_prop_get(ctrl, I2C_PROP_BUS_SPEED, &arg.icl_speed, 863 &len)) { 864 i2cadm_warn("failed to get controller %s speed", 865 di_bus_addr(arg.icl_nexus)); 866 i2c_ctrl_fini(ctrl); 867 continue; 868 } 869 VERIFY3U(len, ==, sizeof (uint32_t)); 870 871 if (!i2c_prop_get(ctrl, I2C_PROP_TYPE, &arg.icl_type, &len)) { 872 i2cadm_warn("failed to get controller %s type", 873 di_bus_addr(arg.icl_nexus)); 874 i2c_ctrl_fini(ctrl); 875 continue; 876 } 877 VERIFY3U(len, ==, sizeof (uint32_t)); 878 879 if (!i2c_prop_get(ctrl, I2C_PROP_NPORTS, &arg.icl_nports, 880 &len)) { 881 i2cadm_warn("failed to get controller %s ports", 882 di_bus_addr(arg.icl_nexus)); 883 i2c_ctrl_fini(ctrl); 884 continue; 885 } 886 VERIFY3U(len, ==, sizeof (uint32_t)); 887 888 arg.icl_ctrl = ctrl; 889 ofmt_print(ofmt, &arg); 890 i2c_ctrl_fini(ctrl); 891 print = true; 892 } 893 894 if (iret == I2C_ITER_ERROR) { 895 i2cadm_warn("failed to iterate controllers"); 896 ret = EXIT_FAILURE; 897 } 898 899 for (int i = 0; i < argc; i++) { 900 if (!filts[i]) { 901 warnx("filter '%s' did not match any controllers", 902 argv[i]); 903 ret = EXIT_FAILURE; 904 } 905 } 906 907 if (!print && argc == 0) { 908 warnx("no controllers found"); 909 ret = EXIT_FAILURE; 910 } 911 912 free(filts); 913 ofmt_close(ofmt); 914 i2c_ctrl_discover_fini(iter); 915 return (ret); 916 } 917 918 static i2cadm_cmdtab_t i2cadm_ctrl_cmds[] = { 919 { "list", i2cadm_controller_list, i2cadm_controller_list_usage }, 920 { "prop", i2cadm_controller_prop, i2cadm_controller_prop_usage } 921 }; 922 923 int 924 i2cadm_controller(int argc, char *argv[]) 925 { 926 return (i2cadm_walk_tab(i2cadm_ctrl_cmds, ARRAY_SIZE(i2cadm_ctrl_cmds), 927 argc, argv)); 928 } 929 930 void 931 i2cadm_controller_usage(FILE *f) 932 { 933 i2cadm_walk_usage(i2cadm_ctrl_cmds, ARRAY_SIZE(i2cadm_ctrl_cmds), f); 934 } 935