Auto PIN-entry feature

This commit is contained in:
whowechina
2025-02-22 12:40:06 +08:00
parent 1d06678453
commit 71062bd222
7 changed files with 424 additions and 104 deletions
Binary file not shown.
+1 -1
View File
@@ -12,7 +12,7 @@ function(make_firmware board board_def)
endif()
add_executable(${board}
main.c save.c config.c commands.c light.c keypad.c
main.c save.c config.c commands.c light.c keypad.c cardio.c
cst816t.c st7789.c gui.c gfx.c rle.c
cli.c usb_descriptors.c)
target_compile_definitions(${board} PUBLIC ${board_def})
+214
View File
@@ -0,0 +1,214 @@
/*
* Cardio processing and auto PIN-entry
* WHowe <github.com/whowechina>
*/
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "tusb.h"
#include "usb_descriptors.h"
#include "board_defs.h"
#include "nfc.h"
#include "gui.h"
#include "light.h"
#define LAST_CARD_TIMEOUT_US 30000000
static struct {
uint8_t current[9];
uint8_t reported[9];
uint64_t report_time;
} hid_cardio;
static struct {
uint8_t uid_len;
uint8_t uid[8];
uint64_t time;
} last_card;
static struct {
bool active;
uint8_t seq_len; // not pin length
uint8_t index;
struct {
uint8_t key;
uint8_t duration; // in cycles, most cases 1 cycle = 1ms
} seq[32];
uint64_t pin_time;
} autopin_ctx;
void cardio_report_cardio()
{
if (!tud_hid_ready()) {
return;
}
uint64_t now = time_us_64();
if ((memcmp(hid_cardio.current, hid_cardio.reported, 9) != 0) &&
(now - hid_cardio.report_time > 1000000)) {
tud_hid_n_report(0x00, hid_cardio.current[0], hid_cardio.current + 1, 8);
memcpy(hid_cardio.reported, hid_cardio.current, 9);
hid_cardio.report_time = now;
}
}
static void start_autopin(const char *pin, uint8_t delay)
{
const char *keymap = KEYPAD_NKRO_MAP;
autopin_ctx.active = true;
autopin_ctx.index = 0;
autopin_ctx.seq_len = strlen(pin);
autopin_ctx.pin_time = time_us_64() + delay * 1000000;
for (int i = 0; i < autopin_ctx.seq_len; i++) {
int key = pin[i] - '0';
int code = (key > 0) ? keymap[key - 1] : keymap[9];
autopin_ctx.seq[i].key = code;
autopin_ctx.seq[i].duration = 150;
}
}
static void check_autopin()
{
if (!aic_cfg->autopin.enabled) {
return;
}
for (int i = 0; i < 4; i++) {
if (aic_cfg->autopin.entries[i].uidlen != last_card.uid_len) {
continue;
}
if (memcmp(aic_cfg->autopin.entries[i].uid, last_card.uid, last_card.uid_len) != 0) {
continue;
}
printf(" (Auto PIN-entry)");
start_autopin(aic_cfg->autopin.entries[i].pin, aic_cfg->autopin.entries[i].delay);
break;
}
}
int cardio_get_pin_key()
{
if (!aic_cfg->autopin.enabled || !autopin_ctx.active) {
return -1;
}
if (time_us_64() < autopin_ctx.pin_time) {
return -1;
}
if (autopin_ctx.index >= autopin_ctx.seq_len) {
autopin_ctx.active = false;
return -1;
}
int key = autopin_ctx.seq[autopin_ctx.index].key;
if (autopin_ctx.seq[autopin_ctx.index].duration > 0) {
autopin_ctx.seq[autopin_ctx.index].duration--;
} else {
autopin_ctx.index++;
}
return key;
}
static void update_cardio(nfc_card_t *card)
{
switch (card->card_type) {
case NFC_CARD_MIFARE:
hid_cardio.current[0] = REPORT_ID_EAMU;
hid_cardio.current[1] = 0xe0;
hid_cardio.current[2] = 0x04;
if (card->len == 4) {
memcpy(hid_cardio.current + 3, card->uid, 4);
memcpy(hid_cardio.current + 7, card->uid, 2);
} else if (card->len == 7) {
memcpy(hid_cardio.current + 3, card->uid + 1, 6);
}
break;
case NFC_CARD_FELICA:
hid_cardio.current[0] = REPORT_ID_FELICA;
memcpy(hid_cardio.current + 1, card->uid, 8);
break;
case NFC_CARD_VICINITY:
hid_cardio.current[0] = REPORT_ID_EAMU;
memcpy(hid_cardio.current + 1, card->uid, 8);
break;
default:
memset(hid_cardio.current, 0, 9);
return;
}
last_card.time = time_us_64();
last_card.uid_len = card->len;
memcpy(last_card.uid, card->uid, 8);
gui_report_card_id(hid_cardio.current + 1, 8, true);
printf(" -> CardIO ");
for (int i = 1; i < 9; i++) {
printf("%02X", hid_cardio.current[i]);
}
check_autopin();
}
void cardio_run(bool hid_light)
{
static nfc_card_t old_card = { 0 };
nfc_rf_field(true);
nfc_card_t card = nfc_detect_card();
if (card.card_type != NFC_CARD_NONE) {
nfc_identify_last_card();
gui_report_card_id(card.uid, card.len, false);
}
nfc_rf_field(false);
if (memcmp(&old_card, &card, sizeof(old_card)) == 0) {
return;
}
old_card = card;
if (!hid_light) {
if (card.card_type != NFC_CARD_NONE) {
light_rainbow(30, 0, aic_cfg->light.level_active);
} else {
light_rainbow(1, 3000, aic_cfg->light.level_idle);
}
}
display_card(&card);
update_cardio(&card);
}
void cardio_clear()
{
memset(hid_cardio.current, 0, 9);
memset(&autopin_ctx, 0, sizeof(autopin_ctx));
}
bool cardio_get_last(uint8_t uid[8], uint8_t *len)
{
if (time_us_64() - last_card.time > LAST_CARD_TIMEOUT_US) {
return false;
}
if (last_card.uid_len == 0) {
return false;
}
*len = last_card.uid_len;
memcpy(uid, last_card.uid, last_card.uid_len);
return true;
}
+15
View File
@@ -0,0 +1,15 @@
/*
* Cardio processing and auto PIN-entry
* WHowe <github.com/whowechina>
*/
#ifndef CARDIO_H
#define CARDIO_H
void cardio_run(bool hid_light);
void cardio_clear();
void cardio_report_cardio();
bool cardio_get_last(uint8_t uid[8], uint8_t *len);
int cardio_get_pin_key();
#endif
+159
View File
@@ -17,6 +17,8 @@
#include "bana.h"
#include "nfc.h"
#include "cardio.h"
static int fps[2];
void fps_count(int core)
{
@@ -71,6 +73,30 @@ static void display_reader()
}
}
static void disp_list()
{
for (int i = 0; i < 4; i++) {
printf(" SLOT %d:", i);
if (aic_cfg->autopin.entries[i].uidlen == 0) {
printf(" Empty\n");
continue;
}
printf(" UID: ");
for (int j = 0; j < aic_cfg->autopin.entries[i].uidlen; j++) {
printf("%02x", aic_cfg->autopin.entries[i].uid[j]);
}
printf(" PIN: %.15s", aic_cfg->autopin.entries[i].pin);
printf(" Delay: %ds\n", aic_cfg->autopin.entries[i].delay);
}
}
static void display_autopin()
{
printf("[AUTO PIN-Entry]\n");
printf(" Status: %s\n", aic_cfg->autopin.enabled ? "ON" : "OFF");
disp_list();
}
static void display_warning()
{
if (keypad_is_stuck()) {
@@ -84,6 +110,7 @@ static void handle_display()
display_light();
display_lcd();
display_reader();
display_autopin();
display_warning();
}
@@ -253,6 +280,137 @@ static void handle_lcd(int argc, char *argv[])
display_lcd();
}
static void autopin_onoff(bool on)
{
aic_cfg->autopin.enabled = on;
config_changed();
printf("Auto Pin-entry: %s\n", on ? "ON" : "OFF");
}
static void autopin_delete(int argc, char *argv[])
{
const char *usage = "Usage: autopin delete <SLOT>\n"
" SLOT: [0..3], all";
if (argc != 1) {
printf("%s", usage);
return;
}
const char *slots[] = { "0", "1", "2", "3", "all" };
int match = cli_match_prefix(slots, 5, argv[0]);
if (match < 0) {
printf("%s", usage);
return;
}
if (match == 4) {
memset(&aic_cfg->autopin.entries, 0, sizeof(aic_cfg->autopin.entries));
printf("All slots cleared.\n");
} else {
memset(&aic_cfg->autopin.entries[match], 0,
sizeof(aic_cfg->autopin.entries[match]));
printf("Slot %d cleared.\n", match);
}
config_changed();
}
static int find_slot(const uint8_t uid[8], uint8_t uidlen)
{
int slot = -1;
for (int i = 0; i < 4; i++) {
if ((aic_cfg->autopin.entries[i].uidlen == uidlen) &&
(memcmp(aic_cfg->autopin.entries[i].uid, uid, uidlen) == 0)) {
return i;
}
if ((slot < 0) && (aic_cfg->autopin.entries[i].uidlen == 0)) {
slot = i;
}
}
return slot;
}
static void autopin_add(int argc, char *argv[])
{
const char *usage = "Usage: autopin add <PIN> <DELAY>\n"
" PIN: 4 digits\n"
" DELAY: [0..99] seconds\n";
if (argc != 2) {
printf("%s", usage);
return;
}
uint8_t uid[8];
uint8_t uidlen;
if (!cardio_get_last(uid, &uidlen)) {
printf("No card detected.\n");
return;
}
int slot = find_slot(uid, uidlen);
if (slot < 0) {
printf("No empty slots available.\n");
return;
}
int delay = cli_extract_non_neg_int(argv[1], 0);
if (delay < 0 || delay > 99) {
printf("%s", usage);
return;
}
if (strlen(argv[0]) != 4) {
printf("PIN must be 4 digits.\n");
return;
}
for (int i = 0; i < 4; i++) {
if (isdigit((int)argv[0][i]) == 0) {
printf("PIN must be 4 digits.\n");
return;
}
}
strcpy(aic_cfg->autopin.entries[slot].pin, argv[0]);
aic_cfg->autopin.entries[slot].delay = delay;
config_changed();
printf("Added to slot %d.\n", slot);
disp_list();
}
static void handle_autopin(int argc, char *argv[])
{
const char *usage = "Usage: autopin <on|off>\n"
" autopin list\n"
" autopin delete <SLOT>\n"
" autopin add <PIN> <DELAY>\n";
if (argc < 1) {
printf("%s", usage);
return;
}
const char *commands[] = { "on", "off", "list", "delete", "add" };
int match = cli_match_prefix(commands, 5, argv[0]);
if ((match == 0) || (match == 1)) {
if (argc == 1) {
autopin_onoff(match == 0);
return;
}
} else if (match == 2) {
if (argc == 1) {
disp_list();
return;
}
} else if (match == 3) {
autopin_delete(argc - 1, argv + 1);
return;
} else if (match == 4) {
autopin_add(argc - 1, argv + 1);
return;
}
printf("%s", usage);
}
static void handle_pn5180_tweak(int argc, char *argv[])
{
const char *usage = "Usage: pn5180_tweak <on|off>\n";
@@ -290,6 +448,7 @@ void commands_init()
cli_register("light", handle_light, "Turn on/off lights.");
cli_register("level", handle_level, "Set light level.");
cli_register("lcd", handle_lcd, "Touch LCD settings.");
cli_register("autopin", handle_autopin, "Auto pin-entry.");
cli_register("pn5180_tweak", handle_pn5180_tweak, "PN5180 TX tweak.");
cli_register("debug", handle_debug, "Toggle debug.");
}
+10
View File
@@ -27,7 +27,17 @@ typedef struct __attribute__((packed)) {
} lcd;
struct {
bool pn5180_tx;
uint8_t reserved[7];
} tweak;
struct {
struct {
uint8_t uidlen;
uint8_t delay;
uint8_t uid[8];
char pin[16];
} entries[4];
bool enabled;
} autopin;
uint32_t reserved;
} aic_cfg_t;
+25 -103
View File
@@ -33,35 +33,13 @@
#include "config.h"
#include "cli.h"
#include "commands.h"
#include "cardio.h"
#include "light.h"
#include "keypad.h"
#include "gui.h"
#define DEBUG(...) if (aic_runtime.debug) printf(__VA_ARGS__)
static struct {
uint8_t current[9];
uint8_t reported[9];
uint64_t report_time;
} hid_cardio;
void report_hid_cardio()
{
if (!tud_hid_ready()) {
return;
}
uint64_t now = time_us_64();
if ((memcmp(hid_cardio.current, hid_cardio.reported, 9) != 0) &&
(now - hid_cardio.report_time > 1000000)) {
tud_hid_n_report(0x00, hid_cardio.current[0], hid_cardio.current + 1, 8);
memcpy(hid_cardio.reported, hid_cardio.current, 9);
hid_cardio.report_time = now;
}
}
struct __attribute__((packed)) {
uint8_t modifier;
uint8_t keymap[15];
@@ -69,6 +47,11 @@ struct __attribute__((packed)) {
static const char keymap[12] = KEYPAD_NKRO_MAP;
static inline void set_nkro_bit(uint8_t code)
{
hid_nkro.keymap[code / 8] |= (1 << (code % 8));
}
void report_hid_key()
{
if (!tud_hid_ready()) {
@@ -77,28 +60,30 @@ void report_hid_key()
uint16_t keys = aic_runtime.touch ? gui_keypad_read() : keypad_read();
memset(&hid_nkro, 0, sizeof(hid_nkro));
for (int i = 0; i < keypad_key_num(); i++) {
uint8_t code = keymap[i];
uint8_t byte = code / 8;
uint8_t bit = code % 8;
if (keys & (1 << i)) {
hid_nkro.keymap[byte] |= (1 << bit);
} else {
hid_nkro.keymap[byte] &= ~(1 << bit);
if (keys & (1 << i)) {
set_nkro_bit(keymap[i]);
}
}
int auto_pin_key = cardio_get_pin_key();
if (auto_pin_key > 0) {
set_nkro_bit(auto_pin_key);
}
tud_hid_n_report(1, 0, &hid_nkro, sizeof(hid_nkro));
}
void report_usb_hid()
{
report_hid_cardio();
cardio_report_cardio();
report_hid_key();
}
static uint64_t last_hid_time = 0;
static bool hid_is_active()
static bool hid_light_is_active()
{
if (last_hid_time == 0) {
return false;
@@ -114,7 +99,7 @@ static bool reader_is_active()
static void light_mode_update()
{
static bool was_cardio = true;
bool cardio = !reader_is_active() && !hid_is_active();
bool cardio = !reader_is_active() && !hid_light_is_active();
static uint8_t last_level;
bool level_changed = (last_level != aic_cfg->light.level_idle);
@@ -161,75 +146,6 @@ void card_name_update_cb(nfc_card_name card_name)
gui_report_card_name(card_name);
}
static void update_cardio(nfc_card_t *card)
{
switch (card->card_type) {
case NFC_CARD_MIFARE:
hid_cardio.current[0] = REPORT_ID_EAMU;
hid_cardio.current[1] = 0xe0;
hid_cardio.current[2] = 0x04;
if (card->len == 4) {
memcpy(hid_cardio.current + 3, card->uid, 4);
memcpy(hid_cardio.current + 7, card->uid, 2);
} else if (card->len == 7) {
memcpy(hid_cardio.current + 3, card->uid + 1, 6);
}
break;
case NFC_CARD_FELICA:
hid_cardio.current[0] = REPORT_ID_FELICA;
memcpy(hid_cardio.current + 1, card->uid, 8);
break;
case NFC_CARD_VICINITY:
hid_cardio.current[0] = REPORT_ID_EAMU;
memcpy(hid_cardio.current + 1, card->uid, 8);
break;
default:
memset(hid_cardio.current, 0, 9);
return;
}
gui_report_card_id(hid_cardio.current + 1, 8, true);
printf(" -> CardIO ");
for (int i = 1; i < 9; i++) {
printf("%02X", hid_cardio.current[i]);
}
}
static void cardio_run()
{
if (aime_is_active() || bana_is_active()) {
memset(hid_cardio.current, 0, 9);
return;
}
static nfc_card_t old_card = { 0 };
nfc_rf_field(true);
nfc_card_t card = nfc_detect_card();
if (card.card_type != NFC_CARD_NONE) {
nfc_identify_last_card();
gui_report_card_id(card.uid, card.len, false);
}
nfc_rf_field(false);
if (memcmp(&old_card, &card, sizeof(old_card)) == 0) {
return;
}
old_card = card;
if (!reader_is_active() && !hid_is_active()) {
if (card.card_type != NFC_CARD_NONE) {
light_rainbow(30, 0, aic_cfg->light.level_active);
} else {
light_rainbow(1, 3000, aic_cfg->light.level_idle);
}
}
display_card(&card);
update_cardio(&card);
}
const int reader_intf = 1;
static struct {
uint8_t buf[64];
@@ -348,8 +264,14 @@ static void core0_loop()
cli_run();
reader_run();
cardio_run();
if (reader_is_active()) {
cardio_clear();
}
else {
cardio_run(hid_light_is_active());
}
keypad_update();
report_usb_hid();