Initial commit.
This commit is contained in:
19
Makefile
Normal file
19
Makefile
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
CC=gcc
|
||||||
|
CFLAGS=-Wall -Wextra -O2
|
||||||
|
LDFLAGS=
|
||||||
|
LDLIBS=-lusb-1.0
|
||||||
|
|
||||||
|
TARGETS=desk_reminder scan
|
||||||
|
|
||||||
|
all: $(TARGETS)
|
||||||
|
|
||||||
|
desk_reminder: desk_reminder.c
|
||||||
|
$(CC) $(CFLAGS) desk_reminder.c -o desk_reminder $(LDLIBS)
|
||||||
|
|
||||||
|
scan: scan.c
|
||||||
|
$(CC) $(CFLAGS) scan.c -o scan -lpcsclite
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(TARGETS)
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
47
README.md
Normal file
47
README.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# desk-reminder
|
||||||
|
|
||||||
|
Simple C program that listens for events from the Alcor Micro AU9540 USB smartcard reader.
|
||||||
|
|
||||||
|
- USB device: `058f:9540` (Alcor Micro Corp. AU9540 Smartcard Reader)
|
||||||
|
- Implementation: raw USB using `libusb-1.0`
|
||||||
|
|
||||||
|
The program opens the reader by VID/PID, locates the first interrupt IN endpoint, and prints any data received (typically slot change / card insertion or removal notifications). It does **not** implement full CCID or smartcard protocols, but is useful for observing low-level traffic.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
On Debian/Ubuntu and similar, install the development package:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install libusb-1.0-0-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Then build the program:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd desk-reminder
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
This produces a `desk_reminder` binary.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
You may need root or appropriate udev rules to access USB devices directly:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd desk-reminder
|
||||||
|
sudo ./desk_reminder
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected behavior:
|
||||||
|
|
||||||
|
- The program prints which bus/device it opened.
|
||||||
|
- It reports which interrupt IN endpoint it is listening on.
|
||||||
|
- When the AU9540 reports an event (for example, card insertion/removal), the program prints the raw bytes received.
|
||||||
|
|
||||||
|
Press `Ctrl+C` to stop the program.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
For higher-level smartcard operations (APDU exchange, PIN verification, etc.), you would typically use PC/SC (`pcscd`, `libpcsclite`) instead of talking to the reader via raw USB. This example focuses on listening to the USB interrupt endpoint.
|
||||||
229
desk_reminder.c
Normal file
229
desk_reminder.c
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
// Simple listener for Alcor AU9540 smartcard reader using libusb-1.0
|
||||||
|
// Device: Bus 001 Device 002: ID 058f:9540 Alcor Micro Corp. AU9540 Smartcard Reader
|
||||||
|
//
|
||||||
|
// This program opens the USB device by VID/PID, finds the first
|
||||||
|
// interrupt IN endpoint, and listens for slot change / card events.
|
||||||
|
// It does not implement full CCID or smartcard protocols; it just
|
||||||
|
// prints raw bytes received from the interrupt endpoint.
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <libusb-1.0/libusb.h>
|
||||||
|
|
||||||
|
// Target USB device: IC Reader (Bus 001 Device 011: ID ffff:0035)
|
||||||
|
#define VENDOR_ID 0xffff
|
||||||
|
#define PRODUCT_ID 0x0035
|
||||||
|
|
||||||
|
static volatile int keep_running = 1;
|
||||||
|
|
||||||
|
static void handle_signal(int signum)
|
||||||
|
{
|
||||||
|
(void)signum;
|
||||||
|
keep_running = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct listener_context {
|
||||||
|
libusb_device_handle *handle;
|
||||||
|
unsigned char endpoint_addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void LIBUSB_CALL transfer_callback(struct libusb_transfer *transfer)
|
||||||
|
{
|
||||||
|
struct listener_context *ctx = (struct listener_context *)transfer->user_data;
|
||||||
|
|
||||||
|
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
|
||||||
|
printf("[event] %d bytes: ", transfer->actual_length);
|
||||||
|
for (int i = 0; i < transfer->actual_length; ++i) {
|
||||||
|
printf("%02x ", transfer->buffer[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
if (keep_running) {
|
||||||
|
int rc = libusb_submit_transfer(transfer);
|
||||||
|
if (rc != 0) {
|
||||||
|
fprintf(stderr, "Failed to resubmit transfer: %s\n", libusb_error_name(rc));
|
||||||
|
libusb_free_transfer(transfer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
libusb_free_transfer(transfer);
|
||||||
|
}
|
||||||
|
} else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
|
||||||
|
libusb_free_transfer(transfer);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Transfer error: status=%d (%s)\n",
|
||||||
|
transfer->status, libusb_error_name(transfer->status));
|
||||||
|
libusb_free_transfer(transfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)ctx; // currently unused, kept for future extensions
|
||||||
|
}
|
||||||
|
|
||||||
|
static int find_interrupt_in_endpoint(libusb_device_handle *handle,
|
||||||
|
unsigned char *endpoint_addr_out)
|
||||||
|
{
|
||||||
|
libusb_device *dev = libusb_get_device(handle);
|
||||||
|
struct libusb_config_descriptor *config = NULL;
|
||||||
|
int rc = libusb_get_active_config_descriptor(dev, &config);
|
||||||
|
if (rc != 0) {
|
||||||
|
fprintf(stderr, "Failed to get config descriptor: %s\n", libusb_error_name(rc));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int found = 0;
|
||||||
|
for (int i = 0; i < config->bNumInterfaces && !found; ++i) {
|
||||||
|
const struct libusb_interface *interface = &config->interface[i];
|
||||||
|
for (int j = 0; j < interface->num_altsetting && !found; ++j) {
|
||||||
|
const struct libusb_interface_descriptor *alt = &interface->altsetting[j];
|
||||||
|
for (int k = 0; k < alt->bNumEndpoints && !found; ++k) {
|
||||||
|
const struct libusb_endpoint_descriptor *ep = &alt->endpoint[k];
|
||||||
|
if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_INTERRUPT &&
|
||||||
|
(ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) {
|
||||||
|
*endpoint_addr_out = ep->bEndpointAddress;
|
||||||
|
found = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
libusb_free_config_descriptor(config);
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
fprintf(stderr, "No interrupt IN endpoint found on device.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
libusb_context *ctx = NULL;
|
||||||
|
libusb_device_handle *handle = NULL;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
signal(SIGINT, handle_signal);
|
||||||
|
signal(SIGTERM, handle_signal);
|
||||||
|
|
||||||
|
rc = libusb_init(&ctx);
|
||||||
|
if (rc != 0) {
|
||||||
|
fprintf(stderr, "libusb_init failed: %s\n", libusb_error_name(rc));
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO);
|
||||||
|
|
||||||
|
handle = libusb_open_device_with_vid_pid(ctx, VENDOR_ID, PRODUCT_ID);
|
||||||
|
if (!handle) {
|
||||||
|
fprintf(stderr, "Unable to open device %04x:%04x. Are you running as root or with correct udev rules?\n",
|
||||||
|
VENDOR_ID, PRODUCT_ID);
|
||||||
|
libusb_exit(ctx);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
libusb_device *dev = libusb_get_device(handle);
|
||||||
|
uint8_t bus = libusb_get_bus_number(dev);
|
||||||
|
uint8_t addr = libusb_get_device_address(dev);
|
||||||
|
printf("Opened device on bus %03u device %03u (VID:PID %04x:%04x)\n",
|
||||||
|
bus, addr, VENDOR_ID, PRODUCT_ID);
|
||||||
|
|
||||||
|
// Try to claim interface 0 (most CCID readers use it). Detach kernel driver if needed.
|
||||||
|
int iface = 0;
|
||||||
|
if (libusb_kernel_driver_active(handle, iface) == 1) {
|
||||||
|
printf("Kernel driver active on interface %d, detaching...\n", iface);
|
||||||
|
rc = libusb_detach_kernel_driver(handle, iface);
|
||||||
|
if (rc != 0) {
|
||||||
|
fprintf(stderr, "Failed to detach kernel driver: %s\n", libusb_error_name(rc));
|
||||||
|
libusb_close(handle);
|
||||||
|
libusb_exit(ctx);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = libusb_claim_interface(handle, iface);
|
||||||
|
if (rc != 0) {
|
||||||
|
fprintf(stderr, "Failed to claim interface %d: %s\n", iface, libusb_error_name(rc));
|
||||||
|
libusb_close(handle);
|
||||||
|
libusb_exit(ctx);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char endpoint_addr = 0;
|
||||||
|
if (find_interrupt_in_endpoint(handle, &endpoint_addr) != 0) {
|
||||||
|
libusb_release_interface(handle, iface);
|
||||||
|
libusb_close(handle);
|
||||||
|
libusb_exit(ctx);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Listening on interrupt IN endpoint 0x%02x. Press Ctrl+C to stop.\n",
|
||||||
|
endpoint_addr);
|
||||||
|
|
||||||
|
const int buf_size = 64; // typical interrupt packet size
|
||||||
|
unsigned char *buffer = (unsigned char *)malloc(buf_size);
|
||||||
|
if (!buffer) {
|
||||||
|
fprintf(stderr, "Failed to allocate buffer.\n");
|
||||||
|
libusb_release_interface(handle, iface);
|
||||||
|
libusb_close(handle);
|
||||||
|
libusb_exit(ctx);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
|
||||||
|
if (!transfer) {
|
||||||
|
fprintf(stderr, "Failed to allocate transfer.\n");
|
||||||
|
free(buffer);
|
||||||
|
libusb_release_interface(handle, iface);
|
||||||
|
libusb_close(handle);
|
||||||
|
libusb_exit(ctx);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct listener_context listener_ctx;
|
||||||
|
listener_ctx.handle = handle;
|
||||||
|
listener_ctx.endpoint_addr = endpoint_addr;
|
||||||
|
|
||||||
|
libusb_fill_interrupt_transfer(transfer,
|
||||||
|
handle,
|
||||||
|
endpoint_addr,
|
||||||
|
buffer,
|
||||||
|
buf_size,
|
||||||
|
transfer_callback,
|
||||||
|
&listener_ctx,
|
||||||
|
0); // timeout 0 = infinite
|
||||||
|
|
||||||
|
rc = libusb_submit_transfer(transfer);
|
||||||
|
if (rc != 0) {
|
||||||
|
fprintf(stderr, "Failed to submit transfer: %s\n", libusb_error_name(rc));
|
||||||
|
libusb_free_transfer(transfer);
|
||||||
|
free(buffer);
|
||||||
|
libusb_release_interface(handle, iface);
|
||||||
|
libusb_close(handle);
|
||||||
|
libusb_exit(ctx);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (keep_running) {
|
||||||
|
rc = libusb_handle_events(ctx);
|
||||||
|
if (rc != 0) {
|
||||||
|
fprintf(stderr, "libusb_handle_events error: %s\n", libusb_error_name(rc));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up: ask libusb to cancel the transfer; the callback is
|
||||||
|
// responsible for freeing the transfer and buffer when cancellation
|
||||||
|
// completes or if an error occurred.
|
||||||
|
libusb_cancel_transfer(transfer);
|
||||||
|
|
||||||
|
libusb_release_interface(handle, iface);
|
||||||
|
libusb_close(handle);
|
||||||
|
libusb_exit(ctx);
|
||||||
|
|
||||||
|
printf("Exiting.\n");
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
232
scan.c
Normal file
232
scan.c
Normal 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user