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 * Copyright 2022 Tintri by DDN, Inc. All rights reserved. 15 */ 16 17 /* 18 * nvmeadm output formatting for ofmt based rendering 19 */ 20 21 #include <strings.h> 22 #include <sys/sysmacros.h> 23 #include <err.h> 24 25 #include "nvmeadm.h" 26 27 typedef struct { 28 uint32_t nb_flag; 29 const char *nb_str; 30 } nvmeadm_bitstr_t; 31 32 static boolean_t 33 nvmeadm_bits_to_str(uint32_t val, const nvmeadm_bitstr_t *strs, size_t nstrs, 34 char *buf, size_t buflen) 35 { 36 boolean_t comma = B_FALSE; 37 38 buf[0] = '\0'; 39 for (size_t i = 0; i < nstrs; i++) { 40 if ((val & strs[i].nb_flag) != strs[i].nb_flag) 41 continue; 42 if (comma && strlcat(buf, ",", buflen) >= buflen) 43 return (B_FALSE); 44 if (strlcat(buf, strs[i].nb_str, buflen) >= buflen) 45 return (B_FALSE); 46 comma = true; 47 } 48 49 if (buf[0] == '\0') { 50 if (strlcat(buf, "--", buflen) >= buflen) 51 return (B_FALSE); 52 } 53 54 return (B_TRUE); 55 } 56 57 typedef enum nvme_list_ofmt_field { 58 NVME_LIST_MODEL, 59 NVME_LIST_SERIAL, 60 NVME_LIST_FWREV, 61 NVME_LIST_VERSION, 62 NVME_LIST_SIZE, 63 NVME_LIST_CAPACITY, 64 NVME_LIST_USED, 65 NVME_LIST_INSTANCE, 66 NVME_LIST_NAMESPACE, 67 NVME_LIST_DISK, 68 NVME_LIST_UNALLOC, 69 NVME_LIST_NS_STATE 70 } nvme_list_ofmt_field_t; 71 72 static boolean_t 73 nvmeadm_list_common_ofmt_cb(ofmt_arg_t *ofmt_arg, char *buf, uint_t buflen) 74 { 75 nvmeadm_list_ofmt_arg_t *list = ofmt_arg->ofmt_cbarg; 76 nvme_ctrl_info_t *ctrl = list->nloa_ctrl; 77 const nvme_version_t *vers; 78 size_t ret; 79 80 switch (ofmt_arg->ofmt_id) { 81 case NVME_LIST_MODEL: 82 ret = strlcpy(buf, nvme_ctrl_info_model(ctrl), buflen); 83 break; 84 case NVME_LIST_SERIAL: 85 ret = strlcpy(buf, nvme_ctrl_info_serial(ctrl), buflen); 86 break; 87 case NVME_LIST_FWREV: 88 ret = strlcpy(buf, nvme_ctrl_info_fwrev(ctrl), buflen); 89 break; 90 case NVME_LIST_VERSION: 91 vers = nvme_ctrl_info_version(ctrl); 92 ret = snprintf(buf, buflen, "%u.%u", vers->v_major, 93 vers->v_minor); 94 break; 95 case NVME_LIST_INSTANCE: 96 ret = strlcpy(buf, list->nloa_name, buflen); 97 break; 98 default: 99 warnx("internal programmer error: encountered unknown ofmt " 100 "argument id 0x%x", ofmt_arg->ofmt_id); 101 abort(); 102 } 103 if (ret >= buflen) { 104 return (B_FALSE); 105 } 106 return (B_TRUE); 107 } 108 109 static boolean_t 110 nvmeadm_list_ctrl_ofmt_cb(ofmt_arg_t *ofmt_arg, char *buf, uint_t buflen) 111 { 112 nvmeadm_list_ofmt_arg_t *list = ofmt_arg->ofmt_cbarg; 113 nvme_ctrl_info_t *ctrl = list->nloa_ctrl; 114 nvme_uint128_t u128; 115 size_t ret; 116 117 switch (ofmt_arg->ofmt_id) { 118 case NVME_LIST_CAPACITY: 119 if (nvme_ctrl_info_cap(ctrl, &u128)) { 120 ret = nvme_snprint_uint128(buf, buflen, u128, 0, 0); 121 } else { 122 return (B_FALSE); 123 } 124 break; 125 case NVME_LIST_UNALLOC: 126 if (nvme_ctrl_info_unalloc_cap(ctrl, &u128)) { 127 ret = nvme_snprint_uint128(buf, buflen, u128, 0, 0); 128 } else { 129 return (B_FALSE); 130 } 131 break; 132 default: 133 warnx("internal programmer error: encountered unknown ofmt " 134 "argument id 0x%x", ofmt_arg->ofmt_id); 135 abort(); 136 } 137 138 if (ret >= buflen) { 139 return (B_FALSE); 140 } 141 return (B_TRUE); 142 } 143 144 static boolean_t 145 nvmeadm_list_nsid_ofmt_cb(ofmt_arg_t *ofmt_arg, char *buf, uint_t buflen) 146 { 147 nvmeadm_list_ofmt_arg_t *list = ofmt_arg->ofmt_cbarg; 148 nvme_ns_info_t *ns = list->nloa_ns; 149 const nvme_nvm_lba_fmt_t *fmt = NULL; 150 uint64_t val; 151 size_t ret; 152 153 154 (void) nvme_ns_info_curformat(ns, &fmt); 155 156 switch (ofmt_arg->ofmt_id) { 157 case NVME_LIST_NAMESPACE: 158 ret = snprintf(buf, buflen, "%u", nvme_ns_info_nsid(ns)); 159 break; 160 case NVME_LIST_DISK: 161 if (list->nloa_disk != NULL) { 162 ret = strlcpy(buf, list->nloa_disk, buflen); 163 } else { 164 return (B_FALSE); 165 } 166 break; 167 case NVME_LIST_SIZE: 168 if (nvme_ns_info_size(ns, &val) && fmt != NULL) { 169 val *= nvme_nvm_lba_fmt_data_size(fmt); 170 ret = snprintf(buf, buflen, "%" PRIu64, val); 171 } else { 172 return (B_FALSE); 173 } 174 break; 175 case NVME_LIST_CAPACITY: 176 if (nvme_ns_info_size(ns, &val) && fmt != NULL) { 177 val *= nvme_nvm_lba_fmt_data_size(fmt); 178 ret = snprintf(buf, buflen, "%" PRIu64, val); 179 } else { 180 return (B_FALSE); 181 } 182 break; 183 case NVME_LIST_USED: 184 if (nvme_ns_info_size(ns, &val) && fmt != NULL) { 185 val *= nvme_nvm_lba_fmt_data_size(fmt); 186 ret = snprintf(buf, buflen, "%" PRIu64, val); 187 } else { 188 return (B_FALSE); 189 } 190 break; 191 case NVME_LIST_NS_STATE: 192 ret = strlcpy(buf, list->nloa_state, buflen); 193 break; 194 default: 195 warnx("internal programmer error: encountered unknown ofmt " 196 "argument id 0x%x", ofmt_arg->ofmt_id); 197 abort(); 198 } 199 200 if (ret >= buflen) { 201 return (B_FALSE); 202 } 203 return (B_TRUE); 204 } 205 206 const ofmt_field_t nvmeadm_list_ctrl_ofmt[] = { 207 { "MODEL", 30, NVME_LIST_MODEL, nvmeadm_list_common_ofmt_cb }, 208 { "SERIAL", 30, NVME_LIST_SERIAL, nvmeadm_list_common_ofmt_cb }, 209 { "FWREV", 10, NVME_LIST_FWREV, nvmeadm_list_common_ofmt_cb }, 210 { "VERSION", 10, NVME_LIST_VERSION, nvmeadm_list_common_ofmt_cb }, 211 { "CAPACITY", 15, NVME_LIST_CAPACITY, nvmeadm_list_ctrl_ofmt_cb }, 212 { "INSTANCE", 10, NVME_LIST_INSTANCE, nvmeadm_list_common_ofmt_cb }, 213 { "UNALLOCATED", 15, NVME_LIST_UNALLOC, nvmeadm_list_ctrl_ofmt_cb }, 214 { NULL, 0, 0, NULL } 215 }; 216 217 const ofmt_field_t nvmeadm_list_nsid_ofmt[] = { 218 { "MODEL", 30, NVME_LIST_MODEL, nvmeadm_list_common_ofmt_cb }, 219 { "SERIAL", 30, NVME_LIST_SERIAL, nvmeadm_list_common_ofmt_cb }, 220 { "FWREV", 10, NVME_LIST_FWREV, nvmeadm_list_common_ofmt_cb }, 221 { "VERSION", 10, NVME_LIST_VERSION, nvmeadm_list_common_ofmt_cb }, 222 { "SIZE", 15, NVME_LIST_SIZE, nvmeadm_list_nsid_ofmt_cb }, 223 { "CAPACITY", 15, NVME_LIST_CAPACITY, nvmeadm_list_nsid_ofmt_cb }, 224 { "USED", 15, NVME_LIST_USED, nvmeadm_list_nsid_ofmt_cb }, 225 { "INSTANCE", 10, NVME_LIST_INSTANCE, nvmeadm_list_common_ofmt_cb }, 226 { "NAMESPACE", 10, NVME_LIST_NAMESPACE, nvmeadm_list_nsid_ofmt_cb }, 227 { "DISK", 15, NVME_LIST_DISK, nvmeadm_list_nsid_ofmt_cb }, 228 { "NS-STATE", 10, NVME_LIST_NS_STATE, nvmeadm_list_nsid_ofmt_cb }, 229 { NULL, 0, 0, NULL } 230 }; 231 232 typedef enum { 233 NVME_LIST_LOGS_DEVICE, 234 NVME_LIST_LOGS_NAME, 235 NVME_LIST_LOGS_DESC, 236 NVME_LIST_LOGS_SCOPE, 237 NVME_LIST_LOGS_FIELDS, 238 NVME_LIST_LOGS_CSI, 239 NVME_LIST_LOGS_LID, 240 NVME_LIST_LOGS_SIZE, 241 NVME_LIST_LOGS_MINSIZE, 242 NVME_LIST_LOGS_IMPL, 243 NVME_LIST_LOGS_SOURCES, 244 NVME_LIST_LOGS_KIND 245 } nvme_list_logs_ofmt_field_t; 246 247 static const nvmeadm_bitstr_t nvmeadm_log_scopes[] = { 248 { NVME_LOG_SCOPE_CTRL, "controller" }, 249 { NVME_LOG_SCOPE_NVM, "nvm" }, 250 { NVME_LOG_SCOPE_NS, "namespace" } 251 }; 252 253 static const nvmeadm_bitstr_t nvmeadm_log_fields[] = { 254 { NVME_LOG_DISC_F_NEED_LSP, "lsp" }, 255 { NVME_LOG_DISC_F_NEED_LSI, "lsi" }, 256 { NVME_LOG_DISC_F_NEED_RAE, "rae" } 257 }; 258 259 static const nvmeadm_bitstr_t nvmeadm_log_sources[] = { 260 { NVME_LOG_DISC_S_SPEC, "spec" }, 261 { NVME_LOG_DISC_S_ID_CTRL, "identify-controller" }, 262 { NVME_LOG_DISC_S_DB, "internal-db" }, 263 { NVME_LOG_DISC_S_CMD, "command" } 264 }; 265 266 static boolean_t 267 nvmeadm_list_logs_ofmt_cb(ofmt_arg_t *ofmt_arg, char *buf, uint_t buflen) 268 { 269 const nvmeadm_list_logs_ofmt_arg_t *list = ofmt_arg->ofmt_cbarg; 270 const nvme_log_disc_t *disc = list->nlloa_disc; 271 uint64_t alloc; 272 size_t ret; 273 nvme_log_size_kind_t kind; 274 275 switch (ofmt_arg->ofmt_id) { 276 case NVME_LIST_LOGS_DEVICE: 277 ret = strlcpy(buf, list->nlloa_name, buflen); 278 break; 279 case NVME_LIST_LOGS_NAME: 280 ret = strlcpy(buf, nvme_log_disc_name(disc), buflen); 281 break; 282 case NVME_LIST_LOGS_DESC: 283 ret = strlcpy(buf, nvme_log_disc_desc(disc), buflen); 284 break; 285 case NVME_LIST_LOGS_SCOPE: 286 return (nvmeadm_bits_to_str(nvme_log_disc_scopes(disc), 287 nvmeadm_log_scopes, ARRAY_SIZE(nvmeadm_log_scopes), buf, 288 buflen)); 289 case NVME_LIST_LOGS_FIELDS: 290 return (nvmeadm_bits_to_str(nvme_log_disc_fields(disc), 291 nvmeadm_log_fields, ARRAY_SIZE(nvmeadm_log_fields), buf, 292 buflen)); 293 break; 294 case NVME_LIST_LOGS_CSI: 295 switch (nvme_log_disc_csi(disc)) { 296 case NVME_CSI_NVM: 297 ret = strlcpy(buf, "nvm", buflen); 298 break; 299 case NVME_CSI_KV: 300 ret = strlcpy(buf, "kv", buflen); 301 break; 302 case NVME_CSI_ZNS: 303 ret = strlcpy(buf, "zns", buflen); 304 break; 305 default: 306 ret = snprintf(buf, buflen, "unknown (0x%x)", 307 nvme_log_disc_csi(disc)); 308 break; 309 } 310 break; 311 case NVME_LIST_LOGS_LID: 312 ret = snprintf(buf, buflen, "0x%x", nvme_log_disc_lid(disc)); 313 break; 314 case NVME_LIST_LOGS_SIZE: 315 case NVME_LIST_LOGS_MINSIZE: 316 kind = nvme_log_disc_size(disc, &alloc); 317 318 if (kind == NVME_LOG_SIZE_K_UNKNOWN) { 319 return (B_FALSE); 320 } 321 322 if (kind == NVME_LOG_SIZE_K_VAR && 323 ofmt_arg->ofmt_id == NVME_LIST_LOGS_SIZE) { 324 return (B_FALSE); 325 } 326 327 ret = snprintf(buf, buflen, "%" PRIu64, alloc); 328 break; 329 case NVME_LIST_LOGS_IMPL: 330 ret = strlcpy(buf, nvme_log_disc_impl(disc) ? "yes" : "no", 331 buflen); 332 break; 333 case NVME_LIST_LOGS_SOURCES: 334 return (nvmeadm_bits_to_str(nvme_log_disc_sources(disc), 335 nvmeadm_log_sources, ARRAY_SIZE(nvmeadm_log_sources), buf, 336 buflen)); 337 break; 338 case NVME_LIST_LOGS_KIND: 339 switch (nvme_log_disc_kind(disc)) { 340 case NVME_LOG_ID_MANDATORY: 341 ret = strlcpy(buf, "mandatory", buflen); 342 break; 343 case NVME_LOG_ID_OPTIONAL: 344 ret = strlcpy(buf, "optional", buflen); 345 break; 346 case NVME_LOG_ID_VENDOR_SPECIFIC: 347 ret = strlcpy(buf, "vendor-specific", buflen); 348 break; 349 default: 350 ret = snprintf(buf, buflen, "unknown (0x%x)", 351 nvme_log_disc_kind(disc)); 352 break; 353 } 354 break; 355 default: 356 warnx("internal programmer error: encountered unknown ofmt " 357 "argument id 0x%x", ofmt_arg->ofmt_id); 358 abort(); 359 } 360 361 return (ret < buflen); 362 } 363 364 const char *nvmeadm_list_logs_fields = "device,name,scope,fields,desc"; 365 const char *nvmeadm_list_logs_fields_impl = "device,name,scope,impl,fields," 366 "desc"; 367 const ofmt_field_t nvmeadm_list_logs_ofmt[] = { 368 { "DEVICE", 8, NVME_LIST_LOGS_DEVICE, nvmeadm_list_logs_ofmt_cb }, 369 { "NAME", 18, NVME_LIST_LOGS_NAME, nvmeadm_list_logs_ofmt_cb }, 370 { "DESC", 30, NVME_LIST_LOGS_DESC, nvmeadm_list_logs_ofmt_cb }, 371 { "SCOPE", 14, NVME_LIST_LOGS_SCOPE, nvmeadm_list_logs_ofmt_cb }, 372 { "FIELDS", 10, NVME_LIST_LOGS_FIELDS, nvmeadm_list_logs_ofmt_cb }, 373 { "CSI", 6, NVME_LIST_LOGS_CSI, nvmeadm_list_logs_ofmt_cb }, 374 { "LID", 6, NVME_LIST_LOGS_LID, nvmeadm_list_logs_ofmt_cb }, 375 { "SIZE", 10, NVME_LIST_LOGS_SIZE, nvmeadm_list_logs_ofmt_cb }, 376 { "MINSIZE", 10, NVME_LIST_LOGS_MINSIZE, nvmeadm_list_logs_ofmt_cb }, 377 { "IMPL", 6, NVME_LIST_LOGS_IMPL, nvmeadm_list_logs_ofmt_cb }, 378 { "SOURCES", 20, NVME_LIST_LOGS_SOURCES, nvmeadm_list_logs_ofmt_cb }, 379 { "KIND", 16, NVME_LIST_LOGS_KIND, nvmeadm_list_logs_ofmt_cb }, 380 { NULL, 0, 0, NULL } 381 }; 382 383 typedef enum { 384 NVME_LIST_FEATS_DEVICE, 385 NVME_LIST_FEATS_SHORT, 386 NVME_LIST_FEATS_SPEC, 387 NVME_LIST_FEATS_FID, 388 NVME_LIST_FEATS_SCOPE, 389 NVME_LIST_FEATS_KIND, 390 NVME_LIST_FEATS_CSI, 391 NVME_LIST_FEATS_FLAGS, 392 NVME_LIST_FEATS_GET_IN, 393 NVME_LIST_FEATS_SET_IN, 394 NVME_LIST_FEATS_GET_OUT, 395 NVME_LIST_FEATS_SET_OUT, 396 NVME_LIST_FEATS_DATA_LEN, 397 NVME_LIST_FEATS_IMPL 398 } nvme_list_features_ofmt_field_t; 399 400 static const nvmeadm_bitstr_t nvmeadm_feat_scopes[] = { 401 { NVME_FEAT_SCOPE_CTRL, "controller" }, 402 { NVME_FEAT_SCOPE_NS, "namespace" } 403 }; 404 405 static const nvmeadm_bitstr_t nvmeadm_feat_get_in[] = { 406 { NVME_GET_FEAT_F_CDW11, "cdw11" }, 407 { NVME_GET_FEAT_F_DATA, "data" }, 408 { NVME_GET_FEAT_F_NSID, "nsid" } 409 }; 410 411 static const nvmeadm_bitstr_t nvmeadm_feat_set_in[] = { 412 { NVME_SET_FEAT_F_CDW11, "cdw11" }, 413 { NVME_SET_FEAT_F_CDW12, "cdw12" }, 414 { NVME_SET_FEAT_F_CDW13, "cdw13" }, 415 { NVME_SET_FEAT_F_CDW14, "cdw14" }, 416 { NVME_SET_FEAT_F_CDW15, "cdw15" }, 417 { NVME_SET_FEAT_F_DATA, "data" }, 418 { NVME_SET_FEAT_F_NSID, "nsid" } 419 }; 420 421 static const nvmeadm_bitstr_t nvmeadm_feat_output[] = { 422 { NVME_FEAT_OUTPUT_CDW0, "cdw0" }, 423 { NVME_FEAT_OUTPUT_DATA, "data" } 424 }; 425 426 static const nvmeadm_bitstr_t nvmeadm_feat_flags[] = { 427 { NVME_FEAT_F_GET_BCAST_NSID, "get-bcastns" }, 428 { NVME_FEAT_F_SET_BCAST_NSID, "set-bcastns" } 429 }; 430 431 static const nvmeadm_bitstr_t nvmeadm_feat_csi[] = { 432 { NVME_FEAT_CSI_NVM, "nvm" } 433 }; 434 435 static boolean_t 436 nvmeadm_list_features_ofmt_cb(ofmt_arg_t *ofmt_arg, char *buf, uint_t buflen) 437 { 438 const nvmeadm_list_features_ofmt_arg_t *nlfo = ofmt_arg->ofmt_cbarg; 439 const nvme_feat_disc_t *feat = nlfo->nlfoa_feat; 440 size_t ret; 441 442 switch (ofmt_arg->ofmt_id) { 443 case NVME_LIST_FEATS_DEVICE: 444 ret = strlcpy(buf, nlfo->nlfoa_name, buflen); 445 break; 446 case NVME_LIST_FEATS_SHORT: 447 ret = strlcpy(buf, nvme_feat_disc_short(feat), buflen); 448 break; 449 case NVME_LIST_FEATS_SPEC: 450 ret = strlcpy(buf, nvme_feat_disc_spec(feat), buflen); 451 break; 452 case NVME_LIST_FEATS_FID: 453 ret = snprintf(buf, buflen, "0x%x", nvme_feat_disc_fid(feat)); 454 break; 455 case NVME_LIST_FEATS_SCOPE: 456 return (nvmeadm_bits_to_str(nvme_feat_disc_scope(feat), 457 nvmeadm_feat_scopes, ARRAY_SIZE(nvmeadm_feat_scopes), buf, 458 buflen)); 459 case NVME_LIST_FEATS_KIND: 460 switch (nvme_feat_disc_kind(feat)) { 461 case NVME_FEAT_MANDATORY: 462 ret = strlcpy(buf, "mandatory", buflen); 463 break; 464 case NVME_FEAT_OPTIONAL: 465 ret = strlcpy(buf, "optional", buflen); 466 break; 467 case NVME_FEAT_VENDOR_SPECIFIC: 468 ret = strlcpy(buf, "vendor-specific", buflen); 469 break; 470 default: 471 ret = snprintf(buf, buflen, "unknown (0x%x)", 472 nvme_feat_disc_kind(feat)); 473 break; 474 } 475 break; 476 case NVME_LIST_FEATS_CSI: 477 if (nvme_feat_disc_csi(feat) == NVME_FEAT_CSI_NONE) { 478 ret = strlcpy(buf, "none", buflen); 479 break; 480 } 481 482 return (nvmeadm_bits_to_str(nvme_feat_disc_csi(feat), 483 nvmeadm_feat_csi, ARRAY_SIZE(nvmeadm_feat_csi), buf, 484 buflen)); 485 case NVME_LIST_FEATS_FLAGS: 486 return (nvmeadm_bits_to_str(nvme_feat_disc_flags(feat), 487 nvmeadm_feat_flags, ARRAY_SIZE(nvmeadm_feat_flags), buf, 488 buflen)); 489 case NVME_LIST_FEATS_GET_IN: 490 return (nvmeadm_bits_to_str(nvme_feat_disc_fields_get(feat), 491 nvmeadm_feat_get_in, ARRAY_SIZE(nvmeadm_feat_get_in), buf, 492 buflen)); 493 case NVME_LIST_FEATS_SET_IN: 494 return (nvmeadm_bits_to_str(nvme_feat_disc_fields_set(feat), 495 nvmeadm_feat_set_in, ARRAY_SIZE(nvmeadm_feat_set_in), buf, 496 buflen)); 497 case NVME_LIST_FEATS_GET_OUT: 498 return (nvmeadm_bits_to_str(nvme_feat_disc_output_get(feat), 499 nvmeadm_feat_output, ARRAY_SIZE(nvmeadm_feat_output), buf, 500 buflen)); 501 case NVME_LIST_FEATS_SET_OUT: 502 return (nvmeadm_bits_to_str(nvme_feat_disc_output_set(feat), 503 nvmeadm_feat_output, ARRAY_SIZE(nvmeadm_feat_output), buf, 504 buflen)); 505 case NVME_LIST_FEATS_DATA_LEN: 506 if (nvme_feat_disc_data_size(feat) == 0) { 507 ret = strlcpy(buf, "-", buflen); 508 } else { 509 ret = snprintf(buf, buflen, "%" PRIu64, 510 nvme_feat_disc_data_size(feat)); 511 } 512 break; 513 case NVME_LIST_FEATS_IMPL: 514 switch (nvme_feat_disc_impl(feat)) { 515 case NVME_FEAT_IMPL_UNKNOWN: 516 ret = strlcpy(buf, "unknown", buflen); 517 break; 518 case NVME_FEAT_IMPL_UNSUPPORTED: 519 ret = strlcpy(buf, "no", buflen); 520 break; 521 case NVME_FEAT_IMPL_SUPPORTED: 522 ret = strlcpy(buf, "yes", buflen); 523 break; 524 default: 525 ret = snprintf(buf, buflen, "unknown (0x%x)", 526 nvme_feat_disc_impl(feat)); 527 break; 528 } 529 break; 530 default: 531 warnx("internal programmer error: encountered unknown ofmt " 532 "argument id 0x%x", ofmt_arg->ofmt_id); 533 abort(); 534 } 535 536 return (ret < buflen); 537 } 538 539 const char *nvmeadm_list_features_fields = "device,short,scope,impl,spec"; 540 const ofmt_field_t nvmeadm_list_features_ofmt[] = { 541 { "DEVICE", 8, NVME_LIST_FEATS_DEVICE, nvmeadm_list_features_ofmt_cb }, 542 { "SHORT", 14, NVME_LIST_FEATS_SHORT, nvmeadm_list_features_ofmt_cb }, 543 { "SPEC", 30, NVME_LIST_FEATS_SPEC, nvmeadm_list_features_ofmt_cb }, 544 { "FID", 6, NVME_LIST_FEATS_FID, nvmeadm_list_features_ofmt_cb }, 545 { "SCOPE", 14, NVME_LIST_FEATS_SCOPE, nvmeadm_list_features_ofmt_cb }, 546 { "KIND", 16, NVME_LIST_FEATS_KIND, nvmeadm_list_features_ofmt_cb }, 547 { "CSI", 6, NVME_LIST_FEATS_CSI, nvmeadm_list_features_ofmt_cb }, 548 { "FLAGS", 14, NVME_LIST_FEATS_FLAGS, nvmeadm_list_features_ofmt_cb }, 549 { "GET-IN", 14, NVME_LIST_FEATS_GET_IN, nvmeadm_list_features_ofmt_cb }, 550 { "SET-IN", 14, NVME_LIST_FEATS_SET_IN, nvmeadm_list_features_ofmt_cb }, 551 { "GET-OUT", 14, NVME_LIST_FEATS_GET_OUT, 552 nvmeadm_list_features_ofmt_cb }, 553 { "SET-OUT", 14, NVME_LIST_FEATS_SET_OUT, 554 nvmeadm_list_features_ofmt_cb }, 555 { "DATALEN", 8, NVME_LIST_FEATS_DATA_LEN, 556 nvmeadm_list_features_ofmt_cb }, 557 { "IMPL", 8, NVME_LIST_FEATS_IMPL, nvmeadm_list_features_ofmt_cb }, 558 { NULL, 0, 0, NULL } 559 }; 560