diff --git a/Production/Firmware/aic_pico.uf2 b/Production/Firmware/aic_pico.uf2 index daf1570..e7afd9c 100644 Binary files a/Production/Firmware/aic_pico.uf2 and b/Production/Firmware/aic_pico.uf2 differ diff --git a/firmware/src/CMakeLists.txt b/firmware/src/CMakeLists.txt index 457b636..2517e1d 100644 --- a/firmware/src/CMakeLists.txt +++ b/firmware/src/CMakeLists.txt @@ -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}) diff --git a/firmware/src/cardio.c b/firmware/src/cardio.c new file mode 100644 index 0000000..f700e89 --- /dev/null +++ b/firmware/src/cardio.c @@ -0,0 +1,214 @@ +/* + * Cardio processing and auto PIN-entry + * WHowe + */ + +#include +#include +#include + +#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; +} diff --git a/firmware/src/cardio.h b/firmware/src/cardio.h new file mode 100644 index 0000000..ca0f135 --- /dev/null +++ b/firmware/src/cardio.h @@ -0,0 +1,15 @@ +/* + * Cardio processing and auto PIN-entry + * WHowe + */ + +#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 \ No newline at end of file diff --git a/firmware/src/commands.c b/firmware/src/commands.c index ac55a01..1382b0f 100644 --- a/firmware/src/commands.c +++ b/firmware/src/commands.c @@ -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 \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 \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 \n" + " autopin list\n" + " autopin delete \n" + " autopin add \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 \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."); } diff --git a/firmware/src/config.h b/firmware/src/config.h index 93f5a36..13e333f 100644 --- a/firmware/src/config.h +++ b/firmware/src/config.h @@ -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; diff --git a/firmware/src/main.c b/firmware/src/main.c index 77ed48c..04f7597 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -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();