Initial commit.

This commit is contained in:
2026-02-19 17:45:10 +00:00
commit c44292b744
4 changed files with 527 additions and 0 deletions

232
scan.c Normal file
View File

@@ -0,0 +1,232 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <unistd.h>
#include <winscard.h>
#include <wintypes.h>
// 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;
}