#include #include "../esp32/main/pn532.c" static uint8_t stream[1024]; static size_t total, pos, chunk = 1; static int64_t now; static unsigned writes; static uint8_t last_tx[300]; static size_t last_tx_len; static uint8_t tx_history[16][300]; static bool held, deep_hold, sleep_requested; static bool auto_reply; static void response(const uint8_t *d, uint8_t n); bool presence_sleep_requested(void) { return sleep_requested; } int gpio_hold_en(int pin) { assert(pin == PN532_TX_GPIO); held = true; return ESP_OK; } int gpio_hold_dis(int pin) { assert(pin == PN532_TX_GPIO); held = false; return ESP_OK; } void gpio_deep_sleep_hold_en(void) { deep_hold = true; } void gpio_deep_sleep_hold_dis(void) { deep_hold = false; } bool relay_ecp_group(uint8_t out[8]) { (void)out; return false; } int64_t esp_timer_get_time(void) { return now; } void vTaskDelay(TickType_t t) { now += t * 1000; } int uart_read_bytes(int u, void *dst, size_t len, TickType_t t) { (void)u; now += 100; if (pos == total) { now += t * 1000; return 0; } size_t n = total - pos; if (n > len) n = len; if (n > chunk) n = chunk; memcpy(dst, stream + pos, n); pos += n; return (int)n; } int uart_write_bytes(int u, const void *d, size_t n) { (void)u; assert(n <= sizeof(last_tx)); memcpy(last_tx, d, n); last_tx_len = n; assert(writes < 16); memcpy(tx_history[writes], d, n); ++writes; if (auto_reply && n > 2) { const uint8_t *frame = d; if (frame[0] == 0x55) frame += 5; uint8_t cmd = frame[6]; uint8_t reply[] = {0xd5, (uint8_t)(cmd + 1), 0, 0, 0, 0}; if (cmd == 0x02) { assert(now >= 10000); /* Explicit oscillator delay preceded firmware. */ reply[2] = 0x32; response(reply, 6); } else { response(reply, cmd == 0x16 ? 3 : 2); } } return (int)n; } int uart_wait_tx_done(int u, TickType_t t) { (void)u; (void)t; return 0; } int uart_flush_input(int u) { (void)u; pos = total; return 0; } int uart_driver_install(int a, int b, int c, int d, void *e, int f) { (void)a; (void)b; (void)c; (void)d; (void)e; (void)f; return 0; } int uart_param_config(int u, const uart_config_t *c) { (void)u; (void)c; return 0; } int uart_set_pin(int a, int b, int c, int d, int e) { (void)a; (void)b; (void)c; (void)d; (void)e; return 0; } static void reset(void) { total = pos = 0; now = 0; writes = 0; held = deep_hold = false; auto_reply = sleep_requested = false; } static void append(const uint8_t *d, size_t n) { memcpy(stream + total, d, n); total += n; } static void response(const uint8_t *d, uint8_t n) { uint8_t h[] = {0, 0, 255, n, (uint8_t)(0 - n)}; append(ack, sizeof(ack)); append(h, 5); append(d, n); uint8_t sum = 0; for (unsigned i = 0; i < n; i++) sum += d[i]; uint8_t tail[] = {(uint8_t)(0 - sum), 0}; append(tail, 2); } int main(void) { uint8_t out[128]; const uint8_t fw[] = {0xd5, 3, 0x32, 1, 6, 7}, cmd[] = {2}; for (chunk = 1; chunk <= 64; chunk *= 2) { reset(); response(fw, 6); assert(command(cmd, 1, out) == 6); assert(memcmp(out, fw, 6) == 0); assert(writes == 1); assert(last_tx_len == 14 && memcmp(last_tx, "\x55\x55\0\0\0", 5) == 0); } reset(); response(fw, 6); stream[total - 2] ^= 1; assert(command(cmd, 1, out) == -1); assert(writes == 2); reset(); response(fw, 6); stream[sizeof(ack) + 4] ^= 1; assert(command(cmd, 1, out) == -1); reset(); response(fw, 6); --total; assert(command(cmd, 1, out) == -1); reset(); append(ack, sizeof(ack)); assert(command(cmd, 1, out) == -1); const uint8_t rf[] = {0xd5, 0x33}; const unsigned lens[] = {4, 7, 10}; for (unsigned k = 0; k < 3; k++) { uint8_t target[18] = {0xd5, 0x4b, 1, 1, 0, 4, 0}; target[7] = lens[k]; for (unsigned j = 0; j < lens[k]; j++) target[8 + j] = j + 1; reset(); response(target, 8 + lens[k]); response(rf, 2); nfc_card_t card = {0}; assert(poll_card(&card) == 1); assert(card.uid_len == lens[k]); assert(memcmp(card.uid, target + 8, lens[k]) == 0); } uint8_t absent[] = {0xd5, 0x4b, 0}; reset(); response(absent, 3); response(rf, 2); nfc_card_t card = {0}; assert(poll_card(&card) == 0); uint8_t short_uid[] = {0xd5, 0x4b, 1, 1, 0, 4, 0, 10, 1}; reset(); response(short_uid, sizeof(short_uid)); response(rf, 2); assert(poll_card(&card) == -1); reset(); response(absent, 3); stream[total - 2] ^= 1; assert(poll_card(&card) == -1); uint8_t apdu[114] = {0x80, 0x80, 0, 1}, reply[] = {0xd5, 0x41, 0, 0x90, 0}; size_t out_length = 0; reset(); response(reply, sizeof(reply)); assert(pn532_exchange(1, apdu, sizeof(apdu), out, sizeof(out), &out_length) == 0); assert(out_length == 2 && out[0] == 0x90); assert(last_tx_len == sizeof(apdu) + 10); assert(last_tx[5] == 0xd4 && last_tx[6] == 0x40 && last_tx[7] == 1); assert(memcmp(last_tx + 8, apdu, sizeof(apdu)) == 0); reset(); reply[2] = 1; response(reply, sizeof(reply)); assert(pn532_exchange(1, apdu, sizeof(apdu), out, sizeof(out), &out_length) == 3); reset(); reply[2] = 0x40; response(reply, sizeof(reply)); assert(pn532_exchange(1, apdu, sizeof(apdu), out, sizeof(out), &out_length) == 3); assert(pn532_exchange(1, apdu, RELAY_APDU_MAX + 1, out, sizeof(out), &out_length) == 1); const uint8_t gid[] = {0x94, 0x62, 0x5f, 0xd2, 0xc3, 0x2c, 0xf3, 0xc2}; uint8_t ecp[18]; build_ecp(ecp, gid); assert(memcmp(ecp, "\x6a\x02\xcb\x02\x06\x02\x11\x00", 8) == 0); assert(memcmp(ecp + 8, gid, 8) == 0); /* Independent CRC-A vector for the Home Key subtype 06. */ assert(ecp[16] == 0x66 && ecp[17] == 0x3a); const uint8_t reg_ok[] = {0xd5, 0x09}, timeout[] = {0xd5, 0x43, 1}; reset(); response(reg_ok, 2); response(rf, 2); response(rf, 2); response(timeout, 3); response(rf, 2); assert(broadcast_ecp(gid)); assert(writes == 5); /* Expected RF timeout never causes an abort ACK. */ assert(tx_history[0][6] == 0x08 && tx_history[0][9] == 0); assert(tx_history[3][6] == 0x42); assert(memcmp(tx_history[3] + 7, ecp, 18) == 0); reset(); response(reg_ok, 2); response(rf, 2); response(rf, 2); const uint8_t bad_status[] = {0xd5, 0x43, 2}; response(bad_status, 3); response(rf, 2); assert(!broadcast_ecp(gid)); assert(writes == 5); /* Deep sleep is allowed only after a complete success response. */ const uint8_t sleeping[] = {0xd5, 0x17, 0}; reset(); response(rf, 2); response(sleeping, 3); assert(power_down_reader()); assert(held && deep_hold && writes == 2); assert(tx_history[0][6] == 0x32 && tx_history[0][8] == 0); assert(tx_history[1][6] == 0x16 && tx_history[1][7] == 0x10); assert(now >= 10000); reset(); response(rf, 2); uint8_t refused[] = {0xd5, 0x17, 0x27}; response(refused, 3); assert(!power_down_reader()); assert(!held && !deep_hold); reset(); response(rf, 2); response(sleeping, 2); /* Truncated status must not authorize sleep. */ assert(!power_down_reader()); assert(!held && !deep_hold); reset(); response(rf, 2); /* PowerDown response lost. */ assert(!power_down_reader()); assert(!held && !deep_hold); pn532_uart_init(); assert(!held && !deep_hold); reset(); auto_reply = true; assert(initialize_reader()); assert(writes == 4 && tx_history[0][0] == 0x55 && tx_history[0][1] == 0x55); reset(); auto_reply = sleep_requested = true; start_polling(NULL); /* Absence path returns before any card callback. */ assert(held && deep_hold && writes == 6); assert(tx_history[5][6] == 0x16); puts("PN532: ECP frame/CRC-A, raw transmit and expected timeout; startup prefix preserved; APDU >64 bytes; status/chaining rejection; fragmented " "frames, checksums, timeout and UID parsing passed"); }