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