#include #include #include #include #include #include #include #include // Target USB device: ffff:0035 (IC Reader) #define VENDOR_ID "ffff" #define PRODUCT_ID "0035" static int read_file_trim(const char *path, char *buf, size_t buf_size) { FILE *f = fopen(path, "r"); if (!f) return -1; if (!fgets(buf, (int)buf_size, f)) { fclose(f); return -1; } fclose(f); // Trim trailing whitespace/newline size_t len = strlen(buf); while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r' || buf[len - 1] == ' ' || buf[len - 1] == '\t')) { buf[--len] = '\0'; } return 0; } // Find the sysfs name of the target USB device under /sys/bus/usb/devices // (e.g. "1-2"), by matching idVendor/idProduct. static int find_usb_device_name(char *name_out, size_t name_out_size) { const char *base = "/sys/bus/usb/devices"; DIR *dir = opendir(base); if (!dir) { perror("opendir /sys/bus/usb/devices"); return -1; } struct dirent *ent; int found = 0; char path[512]; char vid[16]; char pid[16]; while ((ent = readdir(dir)) != NULL) { if (ent->d_name[0] == '.') continue; // Skip interface/function entries like "1-2:1.0" if (strchr(ent->d_name, ':') != NULL) continue; snprintf(path, sizeof(path), "%s/%s/idVendor", base, ent->d_name); if (read_file_trim(path, vid, sizeof(vid)) != 0) continue; snprintf(path, sizeof(path), "%s/%s/idProduct", base, ent->d_name); if (read_file_trim(path, pid, sizeof(pid)) != 0) continue; if (strcmp(vid, VENDOR_ID) == 0 && strcmp(pid, PRODUCT_ID) == 0) { strncpy(name_out, ent->d_name, name_out_size - 1); name_out[name_out_size - 1] = '\0'; found = 1; break; } } closedir(dir); if (!found) { fprintf(stderr, "USB device %s:%s not found on this system.\n", VENDOR_ID, PRODUCT_ID); return -1; } return 0; } static int write_sysfs(const char *path, const char *value) { FILE *f = fopen(path, "w"); if (!f) { fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno)); return -1; } if (fprintf(f, "%s", value) < 0) { fprintf(stderr, "Failed to write to %s: %s\n", path, strerror(errno)); fclose(f); return -1; } fclose(f); return 0; } // Disable the reader by unbinding it from the generic USB driver. static int disable_reader(const char *dev_name) { char buf[64]; snprintf(buf, sizeof(buf), "%s\n", dev_name); printf("Disabling reader %s (USB %s:%s)\n", dev_name, VENDOR_ID, PRODUCT_ID); return write_sysfs("/sys/bus/usb/drivers/usb/unbind", buf); } // Enable the reader by binding it back to the generic USB driver. static int enable_reader(const char *dev_name) { char buf[64]; snprintf(buf, sizeof(buf), "%s\n", dev_name); printf("Enabling reader %s (USB %s:%s)\n", dev_name, VENDOR_ID, PRODUCT_ID); return write_sysfs("/sys/bus/usb/drivers/usb/bind", buf); } // Trigger a one-shot card scan via PC/SC. Assumes the reader is already // bound/enabled (use the "on" subcommand first). static int trigger_scan(void) { SCARDCONTEXT ctx; LONG rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &ctx); if (rv != SCARD_S_SUCCESS) { fprintf(stderr, "SCardEstablishContext failed: %s\n", pcsc_stringify_error(rv)); return -1; } char readers[2048]; DWORD readersLen = sizeof(readers); rv = SCardListReaders(ctx, NULL, readers, &readersLen); if (rv != SCARD_S_SUCCESS) { fprintf(stderr, "SCardListReaders failed: %s\n", pcsc_stringify_error(rv)); SCardReleaseContext(ctx); return -1; } if (readersLen <= 1) { fprintf(stderr, "No smartcard readers found via PC/SC.\n"); SCardReleaseContext(ctx); return -1; } // readers is a multi-string: sequence of null-terminated strings // terminated by an extra null. Prefer a reader whose name contains // "IC Reader", otherwise use the first. const char *chosen = NULL; const char *p = readers; while (*p != '\0') { if (!chosen) { chosen = p; } if (strstr(p, "IC Reader") != NULL) { chosen = p; break; } p += strlen(p) + 1; } if (!chosen) { fprintf(stderr, "Unable to choose a reader from PC/SC list.\n"); SCardReleaseContext(ctx); return -1; } printf("Using reader: %s\n", chosen); SCARDHANDLE card; DWORD activeProt; rv = SCardConnect(ctx, chosen, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &card, &activeProt); if (rv != SCARD_S_SUCCESS) { fprintf(stderr, "SCardConnect failed: %s\n", pcsc_stringify_error(rv)); SCardReleaseContext(ctx); return -1; } // At this point the reader has powered/reset the card and read the ATR. BYTE atr[64]; DWORD atrLen = sizeof(atr); DWORD state, prot; rv = SCardStatus(card, NULL, NULL, &state, &prot, atr, &atrLen); if (rv == SCARD_S_SUCCESS) { printf("ATR (%u bytes): ", atrLen); for (DWORD i = 0; i < atrLen; i++) { printf("%02X ", atr[i]); } printf("\n"); } else { fprintf(stderr, "SCardStatus failed: %s\n", pcsc_stringify_error(rv)); } SCardDisconnect(card, SCARD_LEAVE_CARD); SCardReleaseContext(ctx); return 0; } static void usage(const char *prog) { fprintf(stderr, "Usage: %s on|off|scan\n", prog); fprintf(stderr, " off : unbind the IC Reader so the system cannot use it.\n"); fprintf(stderr, " on : bind the IC Reader back so it works again.\n"); fprintf(stderr, " scan: perform a one-shot PC/SC card scan using the reader.\n"); fprintf(stderr, "Note: on/off require root (write access to /sys/bus/usb/drivers/usb).\n"); } int main(int argc, char **argv) { if (argc != 2) { usage(argv[0]); return EXIT_FAILURE; } char dev_name[64]; if (find_usb_device_name(dev_name, sizeof(dev_name)) != 0) { return EXIT_FAILURE; } int rc; if (strcmp(argv[1], "off") == 0) { rc = disable_reader(dev_name); } else if (strcmp(argv[1], "on") == 0) { rc = enable_reader(dev_name); } else if (strcmp(argv[1], "scan") == 0) { rc = trigger_scan(); } else { usage(argv[0]); return EXIT_FAILURE; } if (rc != 0) { fprintf(stderr, "Operation failed. Are you running as root?\n"); return EXIT_FAILURE; } return EXIT_SUCCESS; }