From 9395c5c5b1f3588bd0f506916104e690f803e511 Mon Sep 17 00:00:00 2001 From: whowechina Date: Sun, 22 Oct 2023 23:09:32 +0800 Subject: [PATCH] AIME reader (50%, not working) --- firmware/src/CMakeLists.txt | 3 +- firmware/src/aime.c | 488 +++++++++++++++++++++++++++++++++ firmware/src/aime.h | 12 + firmware/src/main.c | 6 + firmware/src/pn532.c | 38 ++- firmware/src/pn532.h | 5 + firmware/src/tusb_config.h | 2 +- firmware/src/usb_descriptors.c | 34 ++- 8 files changed, 572 insertions(+), 16 deletions(-) create mode 100644 firmware/src/aime.c create mode 100644 firmware/src/aime.h diff --git a/firmware/src/CMakeLists.txt b/firmware/src/CMakeLists.txt index f2ec018..049cb4d 100644 --- a/firmware/src/CMakeLists.txt +++ b/firmware/src/CMakeLists.txt @@ -4,7 +4,8 @@ set(LWIP_ROOT ${PICO_SDK_PATH}/lib/lwip) function(make_firmware board board_def) pico_sdk_init() add_executable(${board} - main.c slider.c air.c rgb.c save.c config.c cli.c commands.c lzfx.c + main.c slider.c air.c rgb.c save.c config.c commands.c + aime.c cli.c lzfx.c vl53l0x.c mpr121.c pn532.c usb_descriptors.c) target_compile_definitions(${board} PUBLIC ${board_def}) pico_enable_stdio_usb(${board} 1) diff --git a/firmware/src/aime.c b/firmware/src/aime.c new file mode 100644 index 0000000..b18640a --- /dev/null +++ b/firmware/src/aime.c @@ -0,0 +1,488 @@ +/* + * AIME Reader + * WHowe + * + * Use PN532 to read AIME + */ + +#include +#include + +#include "bsp/board.h" +#include "hardware/gpio.h" +#include "hardware/i2c.h" + +#include "board_defs.h" + +#include "i2c_hub.h" + +#include "pn532.h" + +#define FRAME_TIMEOUT 200000 + +enum { + CMD_GET_FW_VERSION = 0x30, + CMD_GET_HW_VERSION = 0x32, + // Card read + CMD_START_POLLING = 0x40, + CMD_STOP_POLLING = 0x41, + CMD_CARD_DETECT = 0x42, + CMD_CARD_SELECT = 0x43, + CMD_CARD_HALT = 0x44, + // MIFARE + CMD_MIFARE_KEY_SET_A = 0x50, + CMD_MIFARE_AUTHORIZE_A = 0x51, + CMD_MIFARE_READ = 0x52, + CMD_MIFARE_WRITE = 0x53, + CMD_MIFARE_KEY_SET_B = 0x54, + CMD_MIFARE_AUTHORIZE_B = 0x55, + // Boot,update + CMD_TO_UPDATER_MODE = 0x60, + CMD_SEND_HEX_DATA = 0x61, + CMD_TO_NORMAL_MODE = 0x62, + CMD_SEND_BINDATA_INIT = 0x63, + CMD_SEND_BINDATA_EXEC = 0x64, + // FeliCa + CMD_FELICA_PUSH = 0x70, + CMD_FELICA_THROUGH = 0x71, + CMD_FELICA_THROUGH_POLL = 0x00, + CMD_FELICA_THROUGH_READ = 0x06, + CMD_FELICA_THROUGH_WRITE = 0x08, + CMD_FELICA_THROUGH_GET_SYSTEM_CODE = 0x0C, + CMD_FELICA_THROUGH_NDA_A4 = 0xA4, + // LED board + CMD_EXT_BOARD_LED = 0x80, + CMD_EXT_BOARD_LED_RGB = 0x81, + CMD_EXT_BOARD_LED_RGB_UNKNOWN = 0x82, // 未知 + CMD_EXT_BOARD_INFO = 0xf0, + CMD_EXT_FIRM_SUM = 0xf2, + CMD_EXT_SEND_HEX_DATA = 0xf3, + CMD_EXT_TO_BOOT_MODE = 0xf4, + CMD_EXT_TO_NORMAL_MODE = 0xf5, +}; + +enum { // 未确认效果 + STATUS_OK = 0, + STATUS_NFCRW_INIT_ERROR = 1, + STATUS_NFCRW_FIRMWARE_UP_TO_DATE = 3, + STATUS_NFCRW_ACCESS_ERROR = 4, + STATUS_CARD_DETECT_TIMEOUT = 5, + STATUS_CARD_DETECT_ERROR = 32, + STATUS_FELICA_ERROR = 33, +}; + +const char *fw_version[] = { "TN32MSEC003S F/W Ver1.2", "\x94" }; +const char *hw_version[] = { "TN32MSEC003S H/W Ver3.0", "837-15396" }; +const char *led_info[] = { "15084\xFF\x10\x00\x12", "000-00000\xFF\x11\x40" }; +static int baudrate_mode = 0; + +static void aime_set_baudrate(int mode) +{ + baudrate_mode = (mode == 0) ? 0 : 1; +} + +static int aime_interface = -1; + +void aime_init(int interface) +{ + aime_interface = interface; + + i2c_select(I2C_PORT, 1 << 5); // PN532 on IR1 (I2C mux chn 5) + pn532_config_sam(); +} + +union __attribute__((packed)) { + struct { + uint8_t len; + uint8_t addr; + uint8_t seq; + uint8_t cmd; + uint8_t status; + uint8_t payload_len; + uint8_t payload[]; + }; + uint8_t raw[256]; +} response; + +union __attribute__((packed)) { + struct { + uint8_t len; + uint8_t addr; + uint8_t seq; + uint8_t cmd; + uint8_t payload_len; + uint8_t payload[]; + }; + uint8_t raw[256]; +} request; + +struct { + bool active; + uint8_t len; + uint8_t check_sum; + bool escaping; + uint64_t time; +} req_ctx; + +static uint8_t key_sets[2][6]; // 'KeyA' and 'KeyB' + +static void build_response(int payload_len) +{ + response.len = payload_len + 6; + response.addr = request.addr; + response.seq = request.seq; + response.cmd = request.cmd; + response.status = STATUS_OK; + response.payload_len = payload_len; +} + +static void send_response() +{ + uint8_t checksum = 0; + + uint8_t sync = 0xe0; + tud_cdc_n_write(aime_interface, &sync, 1); + + for (int i = 0; i < response.len; i++) { + uint8_t c = response.raw[i]; + checksum += c; + if (c == 0xe0 || c == 0xd0) { + uint8_t escape = 0xd0; + tud_cdc_n_write(aime_interface, &escape, 1); + c--; + } + tud_cdc_n_write(aime_interface, &c, 1); + } + + tud_cdc_n_write(aime_interface, &checksum, 1); + tud_cdc_n_write_flush(aime_interface); +} + +static void simple_response(uint8_t status) +{ + build_response(0); + response.status = status; + send_response(); +} + +static void cmd_to_normal_mode() +{ + simple_response(pn532_firmware_ver() ? STATUS_NFCRW_FIRMWARE_UP_TO_DATE + : STATUS_NFCRW_INIT_ERROR); +} + +static void cmd_fake_version(const char *version[]) +{ + int len = strlen(version[baudrate_mode]); + build_response(len); + memcpy(response.payload, version[baudrate_mode], len); + send_response(); +} + +static void cmd_get_hw_version() +{ + build_response(2); + response.payload[0] = 0x01; + response.payload[1] = 0x00; + send_response(); +} + +static void cmd_key_set(int type) +{ + memcpy(key_sets[type], request.payload, 6); + build_response(0); + send_response(); +} + +static void cmd_set_polling(bool enabled) +{ + pn532_set_rf_field(0, enabled ? 1 : 0); + simple_response(STATUS_OK); +} + +static void cmd_detect_card() +{ + typedef struct __attribute__((packed)) { + uint8_t count; + uint8_t type; + uint8_t id_len; + uint8_t uid[20]; + } card_info_t; + + card_info_t *card_info = (card_info_t *) response.payload; + + int len = sizeof(card_info->uid); + if (pn532_poll_mifare(card_info->uid, &len)) { + build_response(len > 4 ? 10 : 7); + card_info->count = 1; + card_info->type = 0x10; + card_info->id_len = len; + printf("Card Mifare %d\n", card_info->id_len); + } else if (pn532_poll_felica(card_info->uid, &len)) { + build_response(19); + card_info->count = 1; + card_info->type = 0x20; + card_info->id_len = 16; + printf("Card Felica %d\n", card_info->id_len); + } else { + build_response(1); + card_info->count = 0; + response.status = STATUS_OK; + } + send_response(); +} + +typedef struct __attribute__((packed)) { + struct { + uint8_t idm[8]; + uint8_t len; + uint8_t code; + } encap; + union { + struct { + uint8_t system_code[2]; + uint8_t request_code; + uint8_t timeout; + } poll; + struct { + uint8_t rw_idm[8]; + uint8_t service_num; + uint8_t service_code[2]; + uint8_t block_num; + uint8_t block_list[2]; + uint8_t blocks[16]; + } other; + uint8_t payload[32]; + }; +} felica_thru_req_t; + +typedef struct __attribute__((packed)) { + struct { + uint8_t len; + uint8_t code; + uint8_t idm[8]; + } encap; + union { + struct { + uint8_t pmm[8]; + uint8_t system_code[2]; + } poll; + struct { + uint8_t rw_status[2]; + uint8_t block_num; + uint8_t blocks[16]; + } other; + uint8_t payload[32]; + }; +} felica_thru_resp_t; + + +static void cmd_felica_thru() +{ + felica_thru_req_t *req = (felica_thru_req_t *) request.payload; + felica_thru_resp_t *resp = (felica_thru_resp_t *) response.payload; + + uint8_t card[20]; + int len = 20; + if (!pn532_poll_felica(card, &len)) { + simple_response(STATUS_FELICA_ERROR); + } + + printf("Felica Thru:"); + for (int i = 0; i < 16; i++) { + printf(" %02x", request.payload[i]); + } + printf("\n"); + + uint8_t code = req->encap.code; + resp->encap.code = code + 1; + + switch (code) { + case CMD_FELICA_THROUGH_POLL: + printf("\tPOLL\n"); + build_response(20); + memcpy(resp->encap.idm, card, 8); + memcpy(resp->poll.pmm, card + 8, 8); + if (len == 20) { + memcpy(resp->poll.system_code, card + 16, 2); + } else { + memcpy(resp->poll.system_code, req->poll.system_code, 2); + } + break; + case CMD_FELICA_THROUGH_GET_SYSTEM_CODE: + printf("\tGET_SYSTEM_CODE\n"); + build_response(13); + resp->payload[0] = 0x01; + resp->payload[1] = resp->poll.system_code[0]; + resp->payload[2] = resp->poll.system_code[0]; + break; + case CMD_FELICA_THROUGH_NDA_A4: + printf("\tNDA_A4\n"); + build_response(11); + resp->payload[0] = 0x00; + break; + case CMD_FELICA_THROUGH_READ: + printf("\tREAD\n"); + #if 0 + uint8_t services[2] = { req->other.service_code[0], req->other.service_code[1] }; + for (int i = 0; i < req->other.block_num; i++) { + uint16_t blockList[1] = { (uint16_t)(req->blockList[i][0] << 8 | req->blockList[i][1]) }; + if (nfc.felica_ReadWithoutEncryption(1, serviceCodeList, 1, blockList, res.blockData[i]) != 1) { + memset(res.blockData[i], 0, 16); // dummy data + } + } + res.RW_status[0] = 0; + res.RW_status[1] = 0; + res.numBlock = req->numBlock; + res_init(0x0D + req->numBlock * 16); + #endif + build_response(0); + break; + case CMD_FELICA_THROUGH_WRITE: + printf("\tWRITE\n"); + build_response(12); + resp->other.rw_status[0] = 0; + resp->other.rw_status[1] = 0; + break; + default: + printf("\tUnknown through: %02x\n", code); + build_response(0); + response.status = STATUS_OK; + } + resp->encap.len = response.payload_len; + + printf("Felica Thru Response:"); + for (int i = 0; i < response.payload_len; i++) { + printf(" %02x", response.payload[i]); + } + printf("\n"); + send_response(); +} + +static uint32_t led_color; + +static void cmd_led_rgb() +{ + printf("\t RGB: %02x %02x %02x\n", request.payload[0], request.payload[1], request.payload[2]); + led_color = request.payload[0] << 16 | request.payload[1] << 8 | request.payload[2]; + build_response(0); + send_response(); +} + +static void aime_handle_frame() +{ + i2c_select(I2C_PORT, 1 << 5); // PN532 on IR1 (I2C mux chn 5) + + switch (request.cmd) { + case CMD_TO_NORMAL_MODE: + printf("CMD_TO_NORMAL_MODE\n"); + cmd_to_normal_mode(); + break; + case CMD_GET_FW_VERSION: + printf("CMD_GET_FW_VERSION\n"); + cmd_fake_version(fw_version); + break; + case CMD_GET_HW_VERSION: + printf("CMD_GET_HW_VERSION\n"); + cmd_fake_version(hw_version); + break; + case CMD_MIFARE_KEY_SET_A: + printf("CMD_MIFARE_KEY_SET_A\n"); + cmd_key_set(0); + break; + case CMD_MIFARE_KEY_SET_B: + printf("CMD_MIFARE_KEY_SET_B\n"); + cmd_key_set(1); + break; + + case CMD_START_POLLING: + printf("CMD_START_POLLING\n"); + cmd_set_polling(true); + break; + case CMD_STOP_POLLING: + printf("CMD_TO_NORMAL_MODE\n"); + cmd_set_polling(false); + break; + case CMD_CARD_DETECT: + printf("CMD_CARD_DETECT\n"); + cmd_detect_card(); + break; + case CMD_FELICA_THROUGH: + printf("CMD_FELICA_THROUGH\n"); + cmd_felica_thru(); + break; + + case CMD_EXT_BOARD_INFO: + printf("CMD_EXT_BOARD_INFO\n"); + cmd_fake_version(led_info); + break; + case CMD_EXT_BOARD_LED_RGB: + printf("CMD_EXT_BOARD_LED_RGB\n"); + cmd_led_rgb(); + break; + + default: + printf("Unknown command: %02x [", request.cmd); + for (int i = 0; i < request.len; i++) { + printf(" %02x", request.raw[i]); + } + printf("]\n"); + simple_response(STATUS_OK); + break; + } +} + +static bool aime_feed(int c) +{ + if (c == 0xe0) { + req_ctx.active = true; + req_ctx.len = 0; + req_ctx.check_sum = 0; + req_ctx.escaping = false; + req_ctx.time = time_us_64(); + return true; + } + + if (!req_ctx.active) { + return false; + } + + if (c == 0xd0) { + req_ctx.escaping = true; + return true; + } + + if (req_ctx.escaping) { + c++; + req_ctx.escaping = false; + } + + if (req_ctx.len != 0 && req_ctx.len == request.len) { + if (req_ctx.check_sum == c) { + aime_handle_frame(); + req_ctx.active = false; + } + return true; + } + + request.raw[req_ctx.len] = c; + req_ctx.len++; + req_ctx.check_sum += c; + + return true; +} + +void aime_update() +{ + if (req_ctx.active && time_us_64() - req_ctx.time > FRAME_TIMEOUT) { + req_ctx.active = false; + } + + if (tud_cdc_n_available(aime_interface)) { + uint8_t buf[32]; + uint32_t count = tud_cdc_n_read(aime_interface, buf, sizeof(buf)); + for (int i = 0; i < count; i++) { + aime_feed(buf[i]); + } + } +} diff --git a/firmware/src/aime.h b/firmware/src/aime.h new file mode 100644 index 0000000..3525fbb --- /dev/null +++ b/firmware/src/aime.h @@ -0,0 +1,12 @@ +/* + * AIME Reader + * WHowe + */ + +#ifndef AIME_H +#define AIME_H + +void aime_init(int interface); +void aime_update(); + +#endif diff --git a/firmware/src/main.c b/firmware/src/main.c index 2fd8f11..b346e35 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -27,6 +27,7 @@ #include "config.h" #include "cli.h" #include "commands.h" +#include "aime.h" #include "slider.h" #include "air.h" @@ -151,6 +152,8 @@ static void core0_loop() tud_task(); cli_run(); + aime_update(); + save_loop(); cli_fps_count(0); @@ -179,8 +182,11 @@ void init() air_init(); rgb_init(); + aime_init(1); + cli_init("chu_pico>", "\n << Chu Pico Controller >>\n" " https://github.com/whowechina\n\n"); + commands_init(); } diff --git a/firmware/src/pn532.c b/firmware/src/pn532.c index 7294a97..21ece19 100644 --- a/firmware/src/pn532.c +++ b/firmware/src/pn532.c @@ -242,6 +242,27 @@ int pn532_read_response(uint8_t cmd, uint8_t *resp, uint8_t len) return data_len; } +uint32_t pn532_firmware_ver() +{ + int ret = pn532_write_command(0x02, NULL, 0); + if (ret < 0) { + return 0; + } + + uint8_t ver[4]; + int result = pn532_read_response(0x4a, ver, sizeof(ver)); + if (result < 4) { + return 0; + } + + uint32_t version = 0; + for (int i = 0; i < 4; i++) { + version <<= 8; + version |= ver[i]; + } + return version; +} + bool pn532_config_sam() { uint8_t param[] = {0x01, 0x14, 0x01}; @@ -250,6 +271,15 @@ bool pn532_config_sam() return pn532_read_response(0x14, NULL, 0) == 0; } + +bool pn532_set_rf_field(uint8_t auto_rf, uint8_t on_off) +{ + uint8_t param[] = { 1, auto_rf | on_off }; + int result = pn532_write_command(0x32, param, 2); + + return pn532_read_response(0x32, NULL, 0) >= 0; +} + static uint8_t card[32]; bool pn532_poll_mifare(uint8_t *uid, int *len) @@ -288,11 +318,13 @@ bool pn532_poll_felica(uint8_t *uid, int *len) } int result = pn532_read_response(0x4a, card, sizeof(card)); - if (result < 1 || card[0] != 1) { + if (card[0] != 1) { return false; } - if ((result == 20 && card[2] == 18) || + printf("Felica poll: len=%d\n", card[2]); + + if ((result == 20 && card[2] == 18) || (result == 22 && card[2] == 20)) { if (*len < card[2]) { return false; @@ -303,4 +335,4 @@ bool pn532_poll_felica(uint8_t *uid, int *len) } return false; -} \ No newline at end of file +} diff --git a/firmware/src/pn532.h b/firmware/src/pn532.h index a39f53d..698ff36 100644 --- a/firmware/src/pn532.h +++ b/firmware/src/pn532.h @@ -11,8 +11,13 @@ void pn532_init(); int pn532_write_command(uint8_t cmd, const uint8_t *param, uint8_t len); int pn532_read_response(uint8_t cmd, uint8_t *resp, uint8_t len); +uint32_t pn532_firmware_ver(); + bool pn532_config_sam(); +bool pn532_set_rf_field(uint8_t auto_rf, uint8_t on_off); + bool pn532_poll_mifare(uint8_t *uid, int *len); bool pn532_poll_felica(uint8_t *uid, int *len); + #endif diff --git a/firmware/src/tusb_config.h b/firmware/src/tusb_config.h index 7ef973c..610a6c4 100644 --- a/firmware/src/tusb_config.h +++ b/firmware/src/tusb_config.h @@ -97,7 +97,7 @@ extern "C" { //------------- CLASS -------------// #define CFG_TUD_HID 3 -#define CFG_TUD_CDC 1 +#define CFG_TUD_CDC 2 #define CFG_TUD_MSC 0 #define CFG_TUD_MIDI 0 #define CFG_TUD_VENDOR 0 diff --git a/firmware/src/usb_descriptors.c b/firmware/src/usb_descriptors.c index c2fe072..47acd5f 100644 --- a/firmware/src/usb_descriptors.c +++ b/firmware/src/usb_descriptors.c @@ -111,16 +111,24 @@ uint8_t const* tud_hid_descriptor_report_cb(uint8_t itf) // Configuration Descriptor //--------------------------------------------------------------------+ -enum { ITF_NUM_JOY, ITF_NUM_LED, ITF_NUM_NKRO, ITF_NUM_CDC, ITF_NUM_CDC_DATA, ITF_NUM_TOTAL }; +enum { ITF_NUM_JOY, ITF_NUM_LED, ITF_NUM_NKRO, + ITF_NUM_CLI, ITF_NUM_CLI_DATA, + ITF_NUM_AIME, ITF_NUM_AIME_DATA, + ITF_NUM_TOTAL }; -#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN * 3 + TUD_CDC_DESC_LEN) +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN * 3 + TUD_CDC_DESC_LEN * 2) -#define EPNUM_JOY 0x84 -#define EPNUM_LED 0x85 -#define EPNUM_KEY 0x86 -#define EPNUM_CDC_NOTIF 0x81 -#define EPNUM_CDC_OUT 0x02 -#define EPNUM_CDC_IN 0x82 +#define EPNUM_JOY 0x81 +#define EPNUM_LED 0x82 +#define EPNUM_KEY 0x83 + +#define EPNUM_CLI_NOTIF 0x85 +#define EPNUM_CLI_OUT 0x06 +#define EPNUM_CLI_IN 0x86 + +#define EPNUM_AIME_NOTIF 0x87 +#define EPNUM_AIME_OUT 0x08 +#define EPNUM_AIME_IN 0x88 uint8_t const desc_configuration_joy[] = { // Config number, interface count, string index, total length, attribute, @@ -142,8 +150,11 @@ uint8_t const desc_configuration_joy[] = { sizeof(desc_hid_report_nkro), EPNUM_KEY, CFG_TUD_HID_EP_BUFSIZE, 1), - TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 7, EPNUM_CDC_NOTIF, - 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64) + TUD_CDC_DESCRIPTOR(ITF_NUM_CLI, 7, EPNUM_CLI_NOTIF, + 8, EPNUM_CLI_OUT, EPNUM_CLI_IN, 64), + + TUD_CDC_DESCRIPTOR(ITF_NUM_AIME, 8, EPNUM_AIME_NOTIF, + 8, EPNUM_AIME_OUT, EPNUM_AIME_IN, 64), }; // Invoked when received GET CONFIGURATION DESCRIPTOR @@ -166,7 +177,8 @@ const char *string_desc_arr[] = { "Chu Pico Joystick", "Chu Pico LED", "Chu Pico NKRO", - "Chu Pico Serial Port", + "Chu Pico CLI Port", + "Chu Pico AIME Port", }; // Invoked when received GET STRING DESCRIPTOR request