// 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 #include #include #include #include // 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; }