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 2019, Joyent, Inc. 14 * Copyright 2024 Oxide Computer Company 15 */ 16 17 /* 18 * Print out information about a CCID device. 19 */ 20 21 #include <sys/types.h> 22 #include <sys/stat.h> 23 #include <fcntl.h> 24 #include <err.h> 25 #include <stdlib.h> 26 #include <strings.h> 27 #include <unistd.h> 28 #include <ofmt.h> 29 #include <libgen.h> 30 #include <ctype.h> 31 #include <errno.h> 32 #include <limits.h> 33 #include <libcmdutils.h> 34 #include <fts.h> 35 #include <sys/ilstr.h> 36 37 #include <sys/usb/clients/ccid/uccid.h> 38 #include <atr.h> 39 40 #define EXIT_USAGE 2 41 42 static const char *ccidadm_pname; 43 44 #define CCID_ROOT "/dev/ccid/" 45 46 typedef enum { 47 CCIDADM_LIST_DEVICE, 48 CCIDADM_LIST_PRODUCT, 49 CCIDADM_LIST_STATE, 50 CCIDADM_LIST_TRANSPORT, 51 CCIDADM_LIST_SUPPORTED, 52 } ccidadm_list_index_t; 53 54 typedef struct ccidadm_pair { 55 uint32_t ccp_val; 56 const char *ccp_name; 57 } ccidadm_pair_t; 58 59 typedef struct ccid_list_ofmt_arg { 60 const char *cloa_name; 61 uccid_cmd_status_t *cloa_status; 62 } ccid_list_ofmt_arg_t; 63 64 /* 65 * Attempt to open a CCID slot specified by a user. In general, we expect that 66 * users will use a path like "ccid0/slot0". However, they may also specify a 67 * full path. If the card boolean is set to true, that means that they may have 68 * just specified "ccid0", so we need to try to open up the default slot. 69 */ 70 static int 71 ccidadm_open(const char *base, boolean_t card) 72 { 73 int fd; 74 char buf[PATH_MAX]; 75 76 /* 77 * If it's an absolute path, just try to open it. 78 */ 79 if (base[0] == '/') { 80 return (open(base, O_RDWR)); 81 } 82 83 /* 84 * For a card, try to append slot0 first. 85 */ 86 if (card) { 87 if (snprintf(buf, sizeof (buf), "%s/%s/slot0", CCID_ROOT, 88 base) >= sizeof (buf)) { 89 errno = ENAMETOOLONG; 90 return (-1); 91 } 92 93 if ((fd = open(buf, O_RDWR)) >= 0) { 94 return (fd); 95 } 96 97 if (errno != ENOENT && errno != ENOTDIR) { 98 return (fd); 99 } 100 } 101 102 if (snprintf(buf, sizeof (buf), "%s/%s", CCID_ROOT, base) >= 103 sizeof (buf)) { 104 errno = ENAMETOOLONG; 105 return (-1); 106 } 107 108 return (open(buf, O_RDWR)); 109 } 110 111 static void 112 ccidadm_iter(boolean_t readeronly, boolean_t newline, 113 void(*cb)(int, const char *, void *), void *arg) 114 { 115 FTS *fts; 116 FTSENT *ent; 117 char *const paths[] = { CCID_ROOT, NULL }; 118 int fd; 119 boolean_t first = B_TRUE; 120 121 fts = fts_open(paths, FTS_LOGICAL | FTS_NOCHDIR, NULL); 122 if (fts == NULL) { 123 err(EXIT_FAILURE, "failed to create directory stream"); 124 } 125 126 while ((ent = fts_read(fts)) != NULL) { 127 const char *name; 128 129 /* Skip the root and post-order dirs */ 130 if (ent->fts_level == 0 || ent->fts_info == FTS_DP) { 131 continue; 132 } 133 if (readeronly && ent->fts_level != 1) { 134 continue; 135 } else if (!readeronly && ent->fts_level != 2) { 136 continue; 137 } 138 139 if (ent->fts_info == FTS_ERR || ent->fts_info == FTS_NS) { 140 warn("skipping %s, failed to get information: %s", 141 ent->fts_name, strerror(ent->fts_errno)); 142 continue; 143 } 144 145 name = ent->fts_path + strlen(CCID_ROOT); 146 if ((fd = ccidadm_open(name, readeronly)) < 0) { 147 err(EXIT_FAILURE, "failed to open %s", name); 148 } 149 150 if (!first && newline) { 151 (void) printf("\n"); 152 } 153 first = B_FALSE; 154 cb(fd, name, arg); 155 (void) close(fd); 156 } 157 158 (void) fts_close(fts); 159 } 160 161 static void 162 ccidadm_list_slot_status_str(uccid_cmd_status_t *ucs, ilstr_t *s) 163 { 164 if (!(ucs->ucs_status & UCCID_STATUS_F_CARD_PRESENT)) { 165 ilstr_append_str(s, "missing"); 166 return; 167 } 168 169 if (!(ucs->ucs_status & UCCID_STATUS_F_CARD_ACTIVE)) { 170 ilstr_append_str(s, "un"); 171 } 172 173 ilstr_append_str(s, "activated"); 174 } 175 176 static void 177 ccidadm_list_slot_transport_str(uccid_cmd_status_t *ucs, ilstr_t *s) 178 { 179 uint_t bits = CCID_CLASS_F_TPDU_XCHG | CCID_CLASS_F_SHORT_APDU_XCHG | 180 CCID_CLASS_F_EXT_APDU_XCHG; 181 182 switch (ucs->ucs_class.ccd_dwFeatures & bits) { 183 case 0: 184 ilstr_append_str(s, "character"); 185 break; 186 case CCID_CLASS_F_TPDU_XCHG: 187 ilstr_append_str(s, "TPDU"); 188 break; 189 case CCID_CLASS_F_SHORT_APDU_XCHG: 190 case CCID_CLASS_F_EXT_APDU_XCHG: 191 ilstr_append_str(s, "APDU"); 192 break; 193 default: 194 ilstr_append_str(s, "unknown"); 195 break; 196 } 197 198 if ((ucs->ucs_status & UCCID_STATUS_F_PARAMS_VALID) != 0) { 199 switch (ucs->ucs_prot) { 200 case UCCID_PROT_T0: 201 ilstr_append_str(s, " (T=0)"); 202 break; 203 case UCCID_PROT_T1: 204 ilstr_append_str(s, " (T=1)"); 205 break; 206 default: 207 break; 208 } 209 } 210 } 211 212 static void 213 ccidadm_list_slot_usable_str(uccid_cmd_status_t *ucs, ilstr_t *s) 214 { 215 ccid_class_features_t feat; 216 uint_t prot = CCID_CLASS_F_SHORT_APDU_XCHG | CCID_CLASS_F_EXT_APDU_XCHG; 217 uint_t param = CCID_CLASS_F_AUTO_PARAM_NEG | CCID_CLASS_F_AUTO_PPS; 218 uint_t clock = CCID_CLASS_F_AUTO_BAUD | CCID_CLASS_F_AUTO_ICC_CLOCK; 219 220 feat = ucs->ucs_class.ccd_dwFeatures; 221 222 if ((feat & prot) == 0 || 223 (feat & param) != param || 224 (feat & clock) != clock) { 225 ilstr_append_str(s, "un"); 226 } 227 228 ilstr_append_str(s, "supported"); 229 } 230 231 static boolean_t 232 ccidadm_list_ofmt_cb(ofmt_arg_t *ofmt, char *buf, uint_t buflen) 233 { 234 ccid_list_ofmt_arg_t *cloa = ofmt->ofmt_cbarg; 235 ilstr_t s; 236 237 ilstr_init_prealloc(&s, buf, buflen); 238 239 switch (ofmt->ofmt_id) { 240 case CCIDADM_LIST_DEVICE: 241 ilstr_append_str(&s, cloa->cloa_name); 242 break; 243 case CCIDADM_LIST_PRODUCT: 244 ilstr_append_str(&s, cloa->cloa_status->ucs_product); 245 break; 246 case CCIDADM_LIST_STATE: 247 ccidadm_list_slot_status_str(cloa->cloa_status, &s); 248 break; 249 case CCIDADM_LIST_TRANSPORT: 250 ccidadm_list_slot_transport_str(cloa->cloa_status, &s); 251 break; 252 case CCIDADM_LIST_SUPPORTED: 253 ccidadm_list_slot_usable_str(cloa->cloa_status, &s); 254 break; 255 default: 256 return (B_FALSE); 257 } 258 259 return (ilstr_errno(&s) == ILSTR_ERROR_OK); 260 } 261 262 static void 263 ccidadm_list_slot(int slotfd, const char *name, void *arg) 264 { 265 uccid_cmd_status_t ucs; 266 ofmt_handle_t ofmt = arg; 267 ccid_list_ofmt_arg_t cloa; 268 269 bzero(&ucs, sizeof (ucs)); 270 ucs.ucs_version = UCCID_CURRENT_VERSION; 271 272 if (ioctl(slotfd, UCCID_CMD_STATUS, &ucs) != 0) { 273 err(EXIT_FAILURE, "failed to issue status ioctl to %s", name); 274 } 275 276 if ((ucs.ucs_status & UCCID_STATUS_F_PRODUCT_VALID) == 0) { 277 (void) strlcpy(ucs.ucs_product, "<unknown>", 278 sizeof (ucs.ucs_product)); 279 } 280 281 cloa.cloa_name = name; 282 cloa.cloa_status = &ucs; 283 ofmt_print(ofmt, &cloa); 284 } 285 286 static ofmt_field_t ccidadm_list_fields[] = { 287 { "PRODUCT", 24, CCIDADM_LIST_PRODUCT, ccidadm_list_ofmt_cb }, 288 { "DEVICE", 16, CCIDADM_LIST_DEVICE, ccidadm_list_ofmt_cb }, 289 { "CARD STATE", 12, CCIDADM_LIST_STATE, ccidadm_list_ofmt_cb }, 290 { "TRANSPORT", 12, CCIDADM_LIST_TRANSPORT, ccidadm_list_ofmt_cb }, 291 { "SUPPORTED", 12, CCIDADM_LIST_SUPPORTED, ccidadm_list_ofmt_cb }, 292 { NULL, 0, 0, NULL } 293 }; 294 295 static void 296 ccidadm_do_list(int argc, char *argv[]) 297 { 298 ofmt_handle_t ofmt; 299 300 if (argc != 0) { 301 errx(EXIT_USAGE, "list command does not take arguments\n"); 302 } 303 304 if (ofmt_open(NULL, ccidadm_list_fields, 0, 0, &ofmt) != OFMT_SUCCESS) { 305 errx(EXIT_FAILURE, "failed to initialize ofmt state"); 306 } 307 308 ccidadm_iter(B_FALSE, B_FALSE, ccidadm_list_slot, ofmt); 309 ofmt_close(ofmt); 310 } 311 312 static void 313 ccidadm_list_usage(FILE *out) 314 { 315 (void) fprintf(out, "\tlist\n"); 316 } 317 318 /* 319 * Print out logical information about the ICC's ATR. This includes information 320 * about what protocols it supports, required negotiation, etc. 321 */ 322 static void 323 ccidadm_atr_props(uccid_cmd_status_t *ucs) 324 { 325 int ret; 326 atr_data_t *data; 327 atr_protocol_t prots, defprot; 328 boolean_t negotiate; 329 atr_data_rate_choice_t rate; 330 uint32_t bps; 331 332 if ((data = atr_data_alloc()) == NULL) { 333 err(EXIT_FAILURE, "failed to allocate memory for " 334 "ATR data"); 335 } 336 337 ret = atr_parse(ucs->ucs_atr, ucs->ucs_atrlen, data); 338 if (ret != ATR_CODE_OK) { 339 errx(EXIT_FAILURE, "failed to parse ATR data: %s", 340 atr_strerror(ret)); 341 } 342 343 prots = atr_supported_protocols(data); 344 (void) printf("ICC supports protocol(s): "); 345 if (prots == ATR_P_NONE) { 346 (void) printf("none\n"); 347 atr_data_free(data); 348 return; 349 } 350 351 (void) printf("%s\n", atr_protocol_to_string(prots)); 352 353 negotiate = atr_params_negotiable(data); 354 defprot = atr_default_protocol(data); 355 356 if (negotiate) { 357 (void) printf("Card protocol is negotiable; starts with " 358 "default %s parameters\n", atr_protocol_to_string(defprot)); 359 } else { 360 (void) printf("Card protocol is not negotiable; starts with " 361 "specific %s parameters\n", 362 atr_protocol_to_string(defprot)); 363 } 364 365 /* 366 * For each supported protocol, figure out parameters we would 367 * negotiate. We only need to warn about auto-negotiation if this 368 * is TPDU or character and specific bits are missing. 369 */ 370 if (((ucs->ucs_class.ccd_dwFeatures & (CCID_CLASS_F_SHORT_APDU_XCHG | 371 CCID_CLASS_F_EXT_APDU_XCHG)) == 0) && 372 ((ucs->ucs_class.ccd_dwFeatures & (CCID_CLASS_F_AUTO_PARAM_NEG | 373 CCID_CLASS_F_AUTO_PPS)) == 0)) { 374 (void) printf("CCID/ICC require explicit TPDU parameter/PPS " 375 "negotiation\n"); 376 } 377 378 /* 379 * Determine which set of Di/Fi values we should use and how we should 380 * get there (note a reader may not have to set them). 381 */ 382 rate = atr_data_rate(data, &ucs->ucs_class, NULL, 0, &bps); 383 switch (rate) { 384 case ATR_RATE_USEDEFAULT: 385 (void) printf("Reader will run ICC at the default (Di=1/Fi=1) " 386 "speed\n"); 387 break; 388 case ATR_RATE_USEATR: 389 (void) printf("Reader will run ICC at ICC's Di/Fi values\n"); 390 break; 391 case ATR_RATE_USEATR_SETRATE: 392 (void) printf("Reader will run ICC at ICC's Di/Fi values, but " 393 "must set data rate to %u bps\n", bps); 394 break; 395 case ATR_RATE_UNSUPPORTED: 396 (void) printf("Reader cannot run ICC due to Di/Fi mismatch\n"); 397 break; 398 default: 399 (void) printf("Cannot determine Di/Fi rate, unexpected " 400 "value: %u\n", rate); 401 break; 402 } 403 if (prots & ATR_P_T0) { 404 uint8_t fi, di; 405 atr_convention_t conv; 406 atr_clock_stop_t clock; 407 408 fi = atr_fi_index(data); 409 di = atr_di_index(data); 410 conv = atr_convention(data); 411 clock = atr_clock_stop(data); 412 (void) printf("T=0 properties that would be negotiated:\n"); 413 (void) printf(" + Fi/Fmax Index: %u (Fi %s/Fmax %s MHz)\n", 414 fi, atr_fi_index_to_string(fi), 415 atr_fmax_index_to_string(fi)); 416 (void) printf(" + Di Index: %u (Di %s)\n", di, 417 atr_di_index_to_string(di)); 418 (void) printf(" + Clock Convention: %u (%s)\n", conv, 419 atr_convention_to_string(conv)); 420 (void) printf(" + Extra Guardtime: %u\n", 421 atr_extra_guardtime(data)); 422 (void) printf(" + WI: %u\n", atr_t0_wi(data)); 423 (void) printf(" + Clock Stop: %u (%s)\n", clock, 424 atr_clock_stop_to_string(clock)); 425 } 426 427 if (prots & ATR_P_T1) { 428 uint8_t fi, di; 429 atr_clock_stop_t clock; 430 atr_t1_checksum_t cksum; 431 432 fi = atr_fi_index(data); 433 di = atr_di_index(data); 434 clock = atr_clock_stop(data); 435 cksum = atr_t1_checksum(data); 436 (void) printf("T=1 properties that would be negotiated:\n"); 437 (void) printf(" + Fi/Fmax Index: %u (Fi %s/Fmax %s MHz)\n", 438 fi, atr_fi_index_to_string(fi), 439 atr_fmax_index_to_string(fi)); 440 (void) printf(" + Di Index: %u (Di %s)\n", di, 441 atr_di_index_to_string(di)); 442 (void) printf(" + Checksum: %s\n", 443 cksum == ATR_T1_CHECKSUM_CRC ? "CRC" : "LRC"); 444 (void) printf(" + Extra Guardtime: %u\n", 445 atr_extra_guardtime(data)); 446 (void) printf(" + BWI: %u\n", atr_t1_bwi(data)); 447 (void) printf(" + CWI: %u\n", atr_t1_cwi(data)); 448 (void) printf(" + Clock Stop: %u (%s)\n", clock, 449 atr_clock_stop_to_string(clock)); 450 (void) printf(" + IFSC: %u\n", atr_t1_ifsc(data)); 451 (void) printf(" + CCID Supports NAD: %s\n", 452 ucs->ucs_class.ccd_dwFeatures & CCID_CLASS_F_ALTNAD_SUP ? 453 "yes" : "no"); 454 } 455 456 atr_data_free(data); 457 } 458 459 static void 460 ccidadm_atr_verbose(uccid_cmd_status_t *ucs) 461 { 462 int ret; 463 atr_data_t *data; 464 465 if ((data = atr_data_alloc()) == NULL) { 466 err(EXIT_FAILURE, "failed to allocate memory for " 467 "ATR data"); 468 } 469 470 ret = atr_parse(ucs->ucs_atr, ucs->ucs_atrlen, data); 471 if (ret != ATR_CODE_OK) { 472 errx(EXIT_FAILURE, "failed to parse ATR data: %s", 473 atr_strerror(ret)); 474 } 475 atr_data_dump(data, stdout); 476 atr_data_free(data); 477 } 478 479 typedef struct cciadm_atr_args { 480 boolean_t caa_hex; 481 boolean_t caa_props; 482 boolean_t caa_verbose; 483 } ccidadm_atr_args_t; 484 485 static void 486 ccidadm_atr_fetch(int fd, const char *name, void *arg) 487 { 488 uccid_cmd_status_t ucs; 489 ccidadm_atr_args_t *caa = arg; 490 491 bzero(&ucs, sizeof (ucs)); 492 ucs.ucs_version = UCCID_CURRENT_VERSION; 493 494 if (ioctl(fd, UCCID_CMD_STATUS, &ucs) != 0) { 495 err(EXIT_FAILURE, "failed to issue status ioctl to %s", 496 name); 497 } 498 499 if (ucs.ucs_atrlen == 0) { 500 warnx("slot %s has no card inserted or activated", name); 501 return; 502 } 503 504 (void) printf("ATR for %s (%u bytes):\n", name, ucs.ucs_atrlen); 505 if (caa->caa_props) { 506 ccidadm_atr_props(&ucs); 507 } 508 509 if (caa->caa_hex) { 510 atr_data_hexdump(ucs.ucs_atr, ucs.ucs_atrlen, stdout); 511 } 512 513 if (caa->caa_verbose) { 514 ccidadm_atr_verbose(&ucs); 515 } 516 } 517 518 static void 519 ccidadm_do_atr(int argc, char *argv[]) 520 { 521 uint_t i; 522 int c; 523 ccidadm_atr_args_t caa; 524 525 bzero(&caa, sizeof (caa)); 526 optind = 0; 527 while ((c = getopt(argc, argv, "vx")) != -1) { 528 switch (c) { 529 case 'v': 530 caa.caa_verbose = B_TRUE; 531 break; 532 case 'x': 533 caa.caa_hex = B_TRUE; 534 break; 535 case ':': 536 errx(EXIT_USAGE, "Option -%c requires an argument\n", 537 optopt); 538 break; 539 case '?': 540 errx(EXIT_USAGE, "Unknown option: -%c\n", optopt); 541 break; 542 } 543 } 544 545 if (!caa.caa_verbose && !caa.caa_props && !caa.caa_hex) { 546 caa.caa_props = B_TRUE; 547 } 548 549 argc -= optind; 550 argv += optind; 551 552 if (argc == 0) { 553 ccidadm_iter(B_FALSE, B_TRUE, ccidadm_atr_fetch, &caa); 554 return; 555 } 556 557 for (i = 0; i < argc; i++) { 558 int fd; 559 560 if ((fd = ccidadm_open(argv[i], B_FALSE)) < 0) { 561 warn("failed to open %s", argv[i]); 562 errx(EXIT_FAILURE, "valid CCID slot?"); 563 } 564 565 ccidadm_atr_fetch(fd, argv[i], &caa); 566 (void) close(fd); 567 if (i + 1 < argc) { 568 (void) printf("\n"); 569 } 570 } 571 } 572 573 static void 574 ccidadm_atr_usage(FILE *out) 575 { 576 (void) fprintf(out, "\tatr [-vx]\t[device] ...\n"); 577 } 578 579 static void 580 ccidadm_print_pairs(uint32_t val, ccidadm_pair_t *ccp) 581 { 582 while (ccp->ccp_name != NULL) { 583 if ((val & ccp->ccp_val) == ccp->ccp_val) { 584 (void) printf(" + %s\n", ccp->ccp_name); 585 } 586 ccp++; 587 } 588 } 589 590 static ccidadm_pair_t ccidadm_p_protocols[] = { 591 { 0x01, "T=0" }, 592 { 0x02, "T=1" }, 593 { 0x0, NULL } 594 }; 595 596 static ccidadm_pair_t ccidadm_p_voltages[] = { 597 { CCID_CLASS_VOLT_5_0, "5.0 V" }, 598 { CCID_CLASS_VOLT_3_0, "3.0 V" }, 599 { CCID_CLASS_VOLT_1_8, "1.8 V" }, 600 { 0x0, NULL } 601 }; 602 603 static ccidadm_pair_t ccidadm_p_syncprots[] = { 604 { 0x01, "2-Wire Support" }, 605 { 0x02, "3-Wire Support" }, 606 { 0x04, "I2C Support" }, 607 { 0x0, NULL } 608 }; 609 610 static ccidadm_pair_t ccidadm_p_mechanical[] = { 611 { CCID_CLASS_MECH_CARD_ACCEPT, "Card Accept Mechanism" }, 612 { CCID_CLASS_MECH_CARD_EJECT, "Card Eject Mechanism" }, 613 { CCID_CLASS_MECH_CARD_CAPTURE, "Card Capture Mechanism" }, 614 { CCID_CLASS_MECH_CARD_LOCK, "Card Lock/Unlock Mechanism" }, 615 { 0x0, NULL } 616 }; 617 618 static ccidadm_pair_t ccidadm_p_features[] = { 619 { CCID_CLASS_F_AUTO_PARAM_ATR, 620 "Automatic parameter configuration based on ATR data" }, 621 { CCID_CLASS_F_AUTO_ICC_ACTIVATE, 622 "Automatic activation on ICC insertion" }, 623 { CCID_CLASS_F_AUTO_ICC_VOLTAGE, "Automatic ICC voltage selection" }, 624 { CCID_CLASS_F_AUTO_ICC_CLOCK, 625 "Automatic ICC clock frequency change" }, 626 { CCID_CLASS_F_AUTO_BAUD, "Automatic baud rate change" }, 627 { CCID_CLASS_F_AUTO_PARAM_NEG, 628 "Automatic parameter negotiation by CCID" }, 629 { CCID_CLASS_F_AUTO_PPS, "Automatic PPS made by CCID" }, 630 { CCID_CLASS_F_ICC_CLOCK_STOP, "CCID can set ICC in clock stop mode" }, 631 { CCID_CLASS_F_ALTNAD_SUP, "NAD value other than zero accepted" }, 632 { CCID_CLASS_F_AUTO_IFSD, "Automatic IFSD exchange" }, 633 { CCID_CLASS_F_TPDU_XCHG, "TPDU support" }, 634 { CCID_CLASS_F_SHORT_APDU_XCHG, "Short APDU support" }, 635 { CCID_CLASS_F_EXT_APDU_XCHG, "Short and Extended APDU support" }, 636 { CCID_CLASS_F_WAKE_UP, "USB Wake Up signaling support" }, 637 { 0x0, NULL } 638 }; 639 640 static ccidadm_pair_t ccidadm_p_pin[] = { 641 { CCID_CLASS_PIN_VERIFICATION, "PIN verification" }, 642 { CCID_CLASS_PIN_MODIFICATION, "PIN modification" }, 643 { 0x0, NULL } 644 }; 645 646 static void 647 ccidadm_reader_print(int fd, const char *name, void *unused __unused) 648 { 649 uccid_cmd_status_t ucs; 650 ccid_class_descr_t *cd; 651 char nnbuf[NN_NUMBUF_SZ + 1]; 652 653 bzero(&ucs, sizeof (uccid_cmd_status_t)); 654 ucs.ucs_version = UCCID_CURRENT_VERSION; 655 656 if (ioctl(fd, UCCID_CMD_STATUS, &ucs) != 0) { 657 err(EXIT_FAILURE, "failed to issue status ioctl to %s", 658 name); 659 } 660 661 cd = &ucs.ucs_class; 662 (void) printf("Reader %s, CCID class v%u.%u device:\n", name, 663 CCID_VERSION_MAJOR(cd->ccd_bcdCCID), 664 CCID_VERSION_MINOR(cd->ccd_bcdCCID)); 665 666 if ((ucs.ucs_status & UCCID_STATUS_F_PRODUCT_VALID) == 0) { 667 (void) strlcpy(ucs.ucs_product, "<unknown>", 668 sizeof (ucs.ucs_product)); 669 } 670 671 if ((ucs.ucs_status & UCCID_STATUS_F_SERIAL_VALID) == 0) { 672 (void) strlcpy(ucs.ucs_serial, "<unknown>", 673 sizeof (ucs.ucs_serial)); 674 } 675 676 (void) printf(" Product: %s\n", ucs.ucs_product); 677 (void) printf(" Serial: %s\n", ucs.ucs_serial); 678 (void) printf(" Slots Present: %u\n", cd->ccd_bMaxSlotIndex + 1); 679 (void) printf(" Maximum Busy Slots: %u\n", cd->ccd_bMaxCCIDBusySlots); 680 (void) printf(" Supported Voltages:\n"); 681 ccidadm_print_pairs(cd->ccd_bVoltageSupport, ccidadm_p_voltages); 682 (void) printf(" Supported Protocols:\n"); 683 ccidadm_print_pairs(cd->ccd_dwProtocols, ccidadm_p_protocols); 684 nicenum_scale(cd->ccd_dwDefaultClock, 1000, nnbuf, 685 sizeof (nnbuf), NN_DIVISOR_1000 | NN_UNIT_SPACE); 686 (void) printf(" Default Clock: %sHz\n", nnbuf); 687 nicenum_scale(cd->ccd_dwMaximumClock, 1000, nnbuf, 688 sizeof (nnbuf), NN_DIVISOR_1000 | NN_UNIT_SPACE); 689 (void) printf(" Maximum Clock: %sHz\n", nnbuf); 690 (void) printf(" Supported Clock Rates: %u\n", 691 cd->ccd_bNumClockSupported); 692 nicenum_scale(cd->ccd_dwDataRate, 1, nnbuf, sizeof (nnbuf), 693 NN_DIVISOR_1000 | NN_UNIT_SPACE); 694 (void) printf(" Default Data Rate: %sbps\n", nnbuf); 695 nicenum_scale(cd->ccd_dwMaxDataRate, 1, nnbuf, sizeof (nnbuf), 696 NN_DIVISOR_1000 | NN_UNIT_SPACE); 697 (void) printf(" Maximum Data Rate: %sbps\n", nnbuf); 698 (void) printf(" Supported Data Rates: %u\n", 699 cd->ccd_bNumDataRatesSupported); 700 (void) printf(" Maximum IFSD (T=1 only): %u\n", cd->ccd_dwMaxIFSD); 701 if (cd->ccd_dwSyncProtocols != 0) { 702 (void) printf(" Synchronous Protocols Supported:\n"); 703 ccidadm_print_pairs(cd->ccd_dwSyncProtocols, 704 ccidadm_p_syncprots); 705 } 706 if (cd->ccd_dwMechanical != 0) { 707 (void) printf(" Mechanical Features:\n"); 708 ccidadm_print_pairs(cd->ccd_dwMechanical, ccidadm_p_mechanical); 709 } 710 if (cd->ccd_dwFeatures != 0) { 711 (void) printf(" Device Features:\n"); 712 ccidadm_print_pairs(cd->ccd_dwFeatures, ccidadm_p_features); 713 } 714 (void) printf(" Maximum Message Length: %u bytes\n", 715 cd->ccd_dwMaxCCIDMessageLength); 716 if (cd->ccd_dwFeatures & CCID_CLASS_F_EXT_APDU_XCHG) { 717 if (cd->ccd_bClassGetResponse == 0xff) { 718 (void) printf(" Default Get Response Class: echo\n"); 719 } else { 720 (void) printf(" Default Get Response Class: %u\n", 721 cd->ccd_bClassGetResponse); 722 } 723 if (cd->ccd_bClassEnvelope == 0xff) { 724 (void) printf(" Default Envelope Class: echo\n"); 725 } else { 726 (void) printf(" Default Envelope Class: %u\n", 727 cd->ccd_bClassEnvelope); 728 } 729 } 730 if (cd->ccd_wLcdLayout != 0) { 731 (void) printf(" %2ux%2u LCD present\n", 732 cd->ccd_wLcdLayout >> 8, cd->ccd_wLcdLayout & 0xff); 733 } 734 735 if (cd->ccd_bPinSupport) { 736 (void) printf(" Pin Support:\n"); 737 ccidadm_print_pairs(cd->ccd_bPinSupport, ccidadm_p_pin); 738 } 739 } 740 741 static void 742 ccidadm_do_reader(int argc, char *argv[]) 743 { 744 int i; 745 746 if (argc == 0) { 747 ccidadm_iter(B_TRUE, B_TRUE, ccidadm_reader_print, NULL); 748 return; 749 } 750 751 for (i = 0; i < argc; i++) { 752 int fd; 753 754 if ((fd = ccidadm_open(argv[i], B_TRUE)) < 0) { 755 warn("failed to open %s", argv[i]); 756 errx(EXIT_FAILURE, "valid ccid reader"); 757 } 758 759 ccidadm_reader_print(fd, argv[i], NULL); 760 (void) close(fd); 761 if (i + 1 < argc) { 762 (void) printf("\n"); 763 } 764 } 765 } 766 767 static void 768 ccidadm_reader_usage(FILE *out) 769 { 770 (void) fprintf(out, "\treader\t\t[reader] ...\n"); 771 } 772 773 typedef struct ccidadm_cmdtab { 774 const char *cc_name; 775 void (*cc_op)(int, char *[]); 776 void (*cc_usage)(FILE *); 777 } ccidadm_cmdtab_t; 778 779 static ccidadm_cmdtab_t ccidadm_cmds[] = { 780 { "list", ccidadm_do_list, ccidadm_list_usage }, 781 { "atr", ccidadm_do_atr, ccidadm_atr_usage }, 782 { "reader", ccidadm_do_reader, ccidadm_reader_usage }, 783 { NULL } 784 }; 785 786 static int 787 ccidadm_usage(const char *format, ...) 788 { 789 ccidadm_cmdtab_t *tab; 790 791 if (format != NULL) { 792 va_list ap; 793 794 va_start(ap, format); 795 (void) fprintf(stderr, "%s: ", ccidadm_pname); 796 (void) vfprintf(stderr, format, ap); 797 (void) fprintf(stderr, "\n"); 798 va_end(ap); 799 } 800 801 (void) fprintf(stderr, "usage: %s <subcommand> <args> ...\n\n", 802 ccidadm_pname); 803 (void) fprintf(stderr, "Subcommands:\n"); 804 for (tab = ccidadm_cmds; tab->cc_name != NULL; tab++) { 805 tab->cc_usage(stderr); 806 } 807 808 return (EXIT_USAGE); 809 } 810 811 int 812 main(int argc, char *argv[]) 813 { 814 ccidadm_cmdtab_t *tab; 815 816 ccidadm_pname = basename(argv[0]); 817 if (argc < 2) { 818 return (ccidadm_usage("missing required subcommand")); 819 } 820 821 for (tab = ccidadm_cmds; tab->cc_name != NULL; tab++) { 822 if (strcmp(argv[1], tab->cc_name) == 0) { 823 argc -= 2; 824 argv += 2; 825 tab->cc_op(argc, argv); 826 return (EXIT_SUCCESS); 827 } 828 } 829 830 return (ccidadm_usage("unknown command: %s", argv[1])); 831 } 832