mirror of
https://github.com/esphome/esphome.git
synced 2026-09-29 16:00:23 +00:00
[pn71xx] Remove heap allocation from tag handling and NCI transport (#19738)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
bd69b17ef6
commit
bb7af14012
@@ -2,56 +2,64 @@
|
||||
#include "nci_message.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace esphome::nfc {
|
||||
|
||||
static const char *const TAG = "NciMessage";
|
||||
|
||||
NciMessage::NciMessage(const uint8_t message_type, const std::vector<uint8_t> &payload) {
|
||||
NciMessage::NciMessage(const uint8_t message_type, const std::span<const uint8_t> payload) {
|
||||
this->set_message(message_type, payload);
|
||||
}
|
||||
|
||||
NciMessage::NciMessage(const uint8_t message_type, const std::initializer_list<uint8_t> payload)
|
||||
: NciMessage(message_type, std::span<const uint8_t>(payload.begin(), payload.size())) {}
|
||||
|
||||
NciMessage::NciMessage(const uint8_t message_type, const uint8_t gid, const uint8_t oid) {
|
||||
this->reset();
|
||||
this->set_header(message_type, gid, oid);
|
||||
}
|
||||
|
||||
NciMessage::NciMessage(const uint8_t message_type, const uint8_t gid, const uint8_t oid,
|
||||
const std::vector<uint8_t> &payload) {
|
||||
const std::span<const uint8_t> payload) {
|
||||
this->set_message(message_type, gid, oid, payload);
|
||||
}
|
||||
|
||||
NciMessage::NciMessage(const std::vector<uint8_t> &raw_packet) { this->nci_message_ = raw_packet; };
|
||||
NciMessage::NciMessage(const uint8_t message_type, const uint8_t gid, const uint8_t oid,
|
||||
const std::initializer_list<uint8_t> payload)
|
||||
: NciMessage(message_type, gid, oid, std::span<const uint8_t>(payload.begin(), payload.size())) {}
|
||||
|
||||
std::vector<uint8_t> NciMessage::encode() {
|
||||
this->nci_message_[nfc::NCI_PKT_LENGTH_OFFSET] = this->nci_message_.size() - nfc::NCI_PKT_HEADER_SIZE;
|
||||
std::vector<uint8_t> message = this->nci_message_;
|
||||
return message;
|
||||
NciMessage::NciMessage(const std::span<const uint8_t> raw_packet) {
|
||||
this->nci_message_.assign(raw_packet.begin(), raw_packet.end());
|
||||
}
|
||||
|
||||
void NciMessage::reset() { this->nci_message_ = {0, 0, 0}; }
|
||||
|
||||
uint8_t NciMessage::get_message_type() const {
|
||||
return this->nci_message_[nfc::NCI_PKT_MT_GID_OFFSET] & nfc::NCI_PKT_MT_MASK;
|
||||
std::span<const uint8_t> NciMessage::encode() {
|
||||
this->nci_message_[NCI_PKT_LENGTH_OFFSET] = this->nci_message_.size() - NCI_PKT_HEADER_SIZE;
|
||||
return this->nci_message_;
|
||||
}
|
||||
|
||||
uint8_t NciMessage::get_gid() const { return this->nci_message_[nfc::NCI_PKT_MT_GID_OFFSET] & nfc::NCI_PKT_GID_MASK; }
|
||||
void NciMessage::reset() {
|
||||
this->nci_message_.resize(NCI_PKT_HEADER_SIZE);
|
||||
this->nci_message_[NCI_PKT_MT_GID_OFFSET] = 0;
|
||||
this->nci_message_[NCI_PKT_OID_OFFSET] = 0;
|
||||
this->nci_message_[NCI_PKT_LENGTH_OFFSET] = 0;
|
||||
}
|
||||
|
||||
uint8_t NciMessage::get_oid() const { return this->nci_message_[nfc::NCI_PKT_OID_OFFSET] & nfc::NCI_PKT_OID_MASK; }
|
||||
uint8_t NciMessage::get_message_type() const { return this->nci_message_[NCI_PKT_MT_GID_OFFSET] & NCI_PKT_MT_MASK; }
|
||||
|
||||
uint8_t NciMessage::get_gid() const { return this->nci_message_[NCI_PKT_MT_GID_OFFSET] & NCI_PKT_GID_MASK; }
|
||||
|
||||
uint8_t NciMessage::get_oid() const { return this->nci_message_[NCI_PKT_OID_OFFSET] & NCI_PKT_OID_MASK; }
|
||||
|
||||
uint8_t NciMessage::get_payload_size(const bool recompute) {
|
||||
if (!this->nci_message_.empty()) {
|
||||
if (recompute) {
|
||||
this->nci_message_[nfc::NCI_PKT_LENGTH_OFFSET] = this->nci_message_.size() - nfc::NCI_PKT_HEADER_SIZE;
|
||||
this->nci_message_[NCI_PKT_LENGTH_OFFSET] = this->nci_message_.size() - NCI_PKT_HEADER_SIZE;
|
||||
}
|
||||
return this->nci_message_[nfc::NCI_PKT_LENGTH_OFFSET];
|
||||
return this->nci_message_[NCI_PKT_LENGTH_OFFSET];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t NciMessage::get_simple_status_response() const {
|
||||
if (this->nci_message_.size() > nfc::NCI_PKT_PAYLOAD_OFFSET) {
|
||||
return this->nci_message_[nfc::NCI_PKT_PAYLOAD_OFFSET];
|
||||
if (this->nci_message_.size() > NCI_PKT_PAYLOAD_OFFSET) {
|
||||
return this->nci_message_[NCI_PKT_PAYLOAD_OFFSET];
|
||||
}
|
||||
return STATUS_FAILED;
|
||||
}
|
||||
@@ -63,104 +71,112 @@ uint8_t NciMessage::get_message_byte(const uint8_t offset) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> &NciMessage::get_message() { return this->nci_message_; }
|
||||
std::span<const uint8_t> NciMessage::get_payload() const {
|
||||
if (this->nci_message_.size() <= NCI_PKT_HEADER_SIZE) {
|
||||
return {};
|
||||
}
|
||||
return {this->nci_message_.data() + NCI_PKT_HEADER_SIZE, this->nci_message_.size() - NCI_PKT_HEADER_SIZE};
|
||||
}
|
||||
|
||||
bool NciMessage::has_payload() const { return this->nci_message_.size() > nfc::NCI_PKT_HEADER_SIZE; }
|
||||
bool NciMessage::has_payload() const { return this->nci_message_.size() > NCI_PKT_HEADER_SIZE; }
|
||||
|
||||
bool NciMessage::message_type_is(const uint8_t message_type) const {
|
||||
if (!this->nci_message_.empty()) {
|
||||
return message_type == (this->nci_message_[nfc::NCI_PKT_MT_GID_OFFSET] & nfc::NCI_PKT_MT_MASK);
|
||||
return message_type == (this->nci_message_[NCI_PKT_MT_GID_OFFSET] & NCI_PKT_MT_MASK);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NciMessage::message_length_is(const uint8_t message_length, const bool recompute) {
|
||||
if (this->nci_message_.size() > nfc::NCI_PKT_LENGTH_OFFSET) {
|
||||
if (this->nci_message_.size() > NCI_PKT_LENGTH_OFFSET) {
|
||||
if (recompute) {
|
||||
this->nci_message_[nfc::NCI_PKT_LENGTH_OFFSET] = this->nci_message_.size() - nfc::NCI_PKT_HEADER_SIZE;
|
||||
this->nci_message_[NCI_PKT_LENGTH_OFFSET] = this->nci_message_.size() - NCI_PKT_HEADER_SIZE;
|
||||
}
|
||||
return message_length == this->nci_message_[nfc::NCI_PKT_LENGTH_OFFSET];
|
||||
return message_length == this->nci_message_[NCI_PKT_LENGTH_OFFSET];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NciMessage::gid_is(const uint8_t gid) const {
|
||||
if (this->nci_message_.size() > nfc::NCI_PKT_MT_GID_OFFSET) {
|
||||
return gid == (this->nci_message_[nfc::NCI_PKT_MT_GID_OFFSET] & nfc::NCI_PKT_GID_MASK);
|
||||
if (this->nci_message_.size() > NCI_PKT_MT_GID_OFFSET) {
|
||||
return gid == (this->nci_message_[NCI_PKT_MT_GID_OFFSET] & NCI_PKT_GID_MASK);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NciMessage::oid_is(const uint8_t oid) const {
|
||||
if (this->nci_message_.size() > nfc::NCI_PKT_OID_OFFSET) {
|
||||
return oid == (this->nci_message_[nfc::NCI_PKT_OID_OFFSET] & nfc::NCI_PKT_OID_MASK);
|
||||
if (this->nci_message_.size() > NCI_PKT_OID_OFFSET) {
|
||||
return oid == (this->nci_message_[NCI_PKT_OID_OFFSET] & NCI_PKT_OID_MASK);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NciMessage::simple_status_response_is(const uint8_t response) const {
|
||||
if (this->nci_message_.size() > nfc::NCI_PKT_PAYLOAD_OFFSET) {
|
||||
return response == this->nci_message_[nfc::NCI_PKT_PAYLOAD_OFFSET];
|
||||
if (this->nci_message_.size() > NCI_PKT_PAYLOAD_OFFSET) {
|
||||
return response == this->nci_message_[NCI_PKT_PAYLOAD_OFFSET];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NciMessage::set_header(const uint8_t message_type, const uint8_t gid, const uint8_t oid) {
|
||||
if (this->nci_message_.size() < nfc::NCI_PKT_HEADER_SIZE) {
|
||||
this->nci_message_.resize(nfc::NCI_PKT_HEADER_SIZE);
|
||||
if (this->nci_message_.size() < NCI_PKT_HEADER_SIZE) {
|
||||
this->nci_message_.resize(NCI_PKT_HEADER_SIZE);
|
||||
}
|
||||
this->nci_message_[nfc::NCI_PKT_MT_GID_OFFSET] =
|
||||
(message_type & nfc::NCI_PKT_MT_MASK) | (gid & nfc::NCI_PKT_GID_MASK);
|
||||
this->nci_message_[nfc::NCI_PKT_OID_OFFSET] = oid & nfc::NCI_PKT_OID_MASK;
|
||||
this->nci_message_[NCI_PKT_MT_GID_OFFSET] = (message_type & NCI_PKT_MT_MASK) | (gid & NCI_PKT_GID_MASK);
|
||||
this->nci_message_[NCI_PKT_OID_OFFSET] = oid & NCI_PKT_OID_MASK;
|
||||
}
|
||||
|
||||
void NciMessage::set_message(const uint8_t message_type, const std::vector<uint8_t> &payload) {
|
||||
this->nci_message_.resize(nfc::NCI_PKT_HEADER_SIZE);
|
||||
this->nci_message_[nfc::NCI_PKT_MT_GID_OFFSET] = message_type & nfc::NCI_PKT_MT_MASK;
|
||||
this->nci_message_[nfc::NCI_PKT_OID_OFFSET] = 0;
|
||||
this->nci_message_[nfc::NCI_PKT_LENGTH_OFFSET] = payload.size();
|
||||
this->nci_message_.insert(this->nci_message_.end(), payload.begin(), payload.end());
|
||||
void NciMessage::set_message(const uint8_t message_type, const std::span<const uint8_t> payload) {
|
||||
this->nci_message_.resize(NCI_PKT_HEADER_SIZE);
|
||||
this->nci_message_[NCI_PKT_MT_GID_OFFSET] = message_type & NCI_PKT_MT_MASK;
|
||||
this->nci_message_[NCI_PKT_OID_OFFSET] = 0;
|
||||
this->append(payload);
|
||||
this->nci_message_[NCI_PKT_LENGTH_OFFSET] = this->nci_message_.size() - NCI_PKT_HEADER_SIZE;
|
||||
}
|
||||
|
||||
void NciMessage::set_message(const uint8_t message_type, const uint8_t gid, const uint8_t oid,
|
||||
const std::vector<uint8_t> &payload) {
|
||||
this->nci_message_.resize(nfc::NCI_PKT_HEADER_SIZE);
|
||||
this->nci_message_[nfc::NCI_PKT_MT_GID_OFFSET] =
|
||||
(message_type & nfc::NCI_PKT_MT_MASK) | (gid & nfc::NCI_PKT_GID_MASK);
|
||||
this->nci_message_[nfc::NCI_PKT_OID_OFFSET] = oid & nfc::NCI_PKT_OID_MASK;
|
||||
this->nci_message_[nfc::NCI_PKT_LENGTH_OFFSET] = payload.size();
|
||||
this->nci_message_.insert(this->nci_message_.end(), payload.begin(), payload.end());
|
||||
const std::span<const uint8_t> payload) {
|
||||
this->nci_message_.resize(NCI_PKT_HEADER_SIZE);
|
||||
this->set_header(message_type, gid, oid);
|
||||
this->append(payload);
|
||||
this->nci_message_[NCI_PKT_LENGTH_OFFSET] = this->nci_message_.size() - NCI_PKT_HEADER_SIZE;
|
||||
}
|
||||
|
||||
void NciMessage::set_message_type(const uint8_t message_type) {
|
||||
if (this->nci_message_.size() < nfc::NCI_PKT_HEADER_SIZE) {
|
||||
this->nci_message_.resize(nfc::NCI_PKT_HEADER_SIZE);
|
||||
if (this->nci_message_.size() < NCI_PKT_HEADER_SIZE) {
|
||||
this->nci_message_.resize(NCI_PKT_HEADER_SIZE);
|
||||
}
|
||||
auto mt_masked = this->nci_message_[nfc::NCI_PKT_MT_GID_OFFSET] & ~nfc::NCI_PKT_MT_MASK;
|
||||
this->nci_message_[nfc::NCI_PKT_MT_GID_OFFSET] = mt_masked | (message_type & nfc::NCI_PKT_MT_MASK);
|
||||
auto mt_masked = this->nci_message_[NCI_PKT_MT_GID_OFFSET] & ~NCI_PKT_MT_MASK;
|
||||
this->nci_message_[NCI_PKT_MT_GID_OFFSET] = mt_masked | (message_type & NCI_PKT_MT_MASK);
|
||||
}
|
||||
|
||||
void NciMessage::set_gid(const uint8_t gid) {
|
||||
if (this->nci_message_.size() < nfc::NCI_PKT_HEADER_SIZE) {
|
||||
this->nci_message_.resize(nfc::NCI_PKT_HEADER_SIZE);
|
||||
if (this->nci_message_.size() < NCI_PKT_HEADER_SIZE) {
|
||||
this->nci_message_.resize(NCI_PKT_HEADER_SIZE);
|
||||
}
|
||||
auto gid_masked = this->nci_message_[nfc::NCI_PKT_MT_GID_OFFSET] & ~nfc::NCI_PKT_GID_MASK;
|
||||
this->nci_message_[nfc::NCI_PKT_MT_GID_OFFSET] = gid_masked | (gid & nfc::NCI_PKT_GID_MASK);
|
||||
auto gid_masked = this->nci_message_[NCI_PKT_MT_GID_OFFSET] & ~NCI_PKT_GID_MASK;
|
||||
this->nci_message_[NCI_PKT_MT_GID_OFFSET] = gid_masked | (gid & NCI_PKT_GID_MASK);
|
||||
}
|
||||
|
||||
void NciMessage::set_oid(const uint8_t oid) {
|
||||
if (this->nci_message_.size() < nfc::NCI_PKT_HEADER_SIZE) {
|
||||
this->nci_message_.resize(nfc::NCI_PKT_HEADER_SIZE);
|
||||
if (this->nci_message_.size() < NCI_PKT_HEADER_SIZE) {
|
||||
this->nci_message_.resize(NCI_PKT_HEADER_SIZE);
|
||||
}
|
||||
this->nci_message_[nfc::NCI_PKT_OID_OFFSET] = oid & nfc::NCI_PKT_OID_MASK;
|
||||
this->nci_message_[NCI_PKT_OID_OFFSET] = oid & NCI_PKT_OID_MASK;
|
||||
}
|
||||
|
||||
void NciMessage::set_payload(const std::vector<uint8_t> &payload) {
|
||||
std::vector<uint8_t> message(this->nci_message_.begin(), this->nci_message_.begin() + nfc::NCI_PKT_HEADER_SIZE);
|
||||
|
||||
message.insert(message.end(), payload.begin(), payload.end());
|
||||
message[nfc::NCI_PKT_LENGTH_OFFSET] = payload.size();
|
||||
this->nci_message_ = message;
|
||||
void NciMessage::set_payload(const std::span<const uint8_t> payload) {
|
||||
this->nci_message_.resize(NCI_PKT_HEADER_SIZE);
|
||||
this->append(payload);
|
||||
this->nci_message_[NCI_PKT_LENGTH_OFFSET] = this->nci_message_.size() - NCI_PKT_HEADER_SIZE;
|
||||
}
|
||||
|
||||
void NciMessage::append(const std::span<const uint8_t> data) {
|
||||
for (const uint8_t byte : data) {
|
||||
this->nci_message_.push_back(byte);
|
||||
}
|
||||
}
|
||||
|
||||
void NciMessage::set_payload_size(const uint8_t size) { this->nci_message_.resize(NCI_PKT_HEADER_SIZE + size); }
|
||||
|
||||
} // namespace esphome::nfc
|
||||
|
||||
@@ -3,19 +3,30 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include <vector>
|
||||
#include <initializer_list>
|
||||
#include <span>
|
||||
|
||||
namespace esphome::nfc {
|
||||
|
||||
// An NCI packet is a three-byte header followed by up to 255 payload bytes
|
||||
static constexpr size_t NCI_PKT_MAX_PAYLOAD_SIZE = 255;
|
||||
static constexpr size_t NCI_PKT_MAX_SIZE = 3 + NCI_PKT_MAX_PAYLOAD_SIZE;
|
||||
|
||||
/// One NCI packet, held in a fixed buffer so building and reading messages never allocates
|
||||
class NciMessage {
|
||||
public:
|
||||
NciMessage() {}
|
||||
NciMessage(uint8_t message_type, const std::vector<uint8_t> &payload);
|
||||
NciMessage(uint8_t message_type, uint8_t gid, uint8_t oid);
|
||||
NciMessage(uint8_t message_type, uint8_t gid, uint8_t oid, const std::vector<uint8_t> &payload);
|
||||
NciMessage(const std::vector<uint8_t> &raw_packet);
|
||||
using Buffer = StaticVector<uint8_t, NCI_PKT_MAX_SIZE>;
|
||||
|
||||
std::vector<uint8_t> encode();
|
||||
NciMessage() { this->reset(); }
|
||||
NciMessage(uint8_t message_type, std::span<const uint8_t> payload);
|
||||
NciMessage(uint8_t message_type, std::initializer_list<uint8_t> payload);
|
||||
NciMessage(uint8_t message_type, uint8_t gid, uint8_t oid);
|
||||
NciMessage(uint8_t message_type, uint8_t gid, uint8_t oid, std::span<const uint8_t> payload);
|
||||
NciMessage(uint8_t message_type, uint8_t gid, uint8_t oid, std::initializer_list<uint8_t> payload);
|
||||
explicit NciMessage(std::span<const uint8_t> raw_packet);
|
||||
|
||||
/// Stamps the payload length into the header and returns the packet ready to send
|
||||
std::span<const uint8_t> encode();
|
||||
void reset();
|
||||
|
||||
uint8_t get_message_type() const;
|
||||
@@ -24,7 +35,10 @@ class NciMessage {
|
||||
uint8_t get_payload_size(bool recompute = false);
|
||||
uint8_t get_simple_status_response() const;
|
||||
uint8_t get_message_byte(uint8_t offset) const;
|
||||
std::vector<uint8_t> &get_message();
|
||||
Buffer &get_message() { return this->nci_message_; }
|
||||
const Buffer &get_message() const { return this->nci_message_; }
|
||||
/// The payload bytes that follow the header
|
||||
std::span<const uint8_t> get_payload() const;
|
||||
|
||||
bool has_payload() const;
|
||||
bool message_type_is(uint8_t message_type) const;
|
||||
@@ -34,15 +48,25 @@ class NciMessage {
|
||||
bool simple_status_response_is(uint8_t response) const;
|
||||
|
||||
void set_header(uint8_t message_type, uint8_t gid, uint8_t oid);
|
||||
void set_message(uint8_t message_type, const std::vector<uint8_t> &payload);
|
||||
void set_message(uint8_t message_type, uint8_t gid, uint8_t oid, const std::vector<uint8_t> &payload);
|
||||
void set_message(uint8_t message_type, std::span<const uint8_t> payload);
|
||||
void set_message(uint8_t message_type, uint8_t gid, uint8_t oid, std::span<const uint8_t> payload);
|
||||
void set_message_type(uint8_t message_type);
|
||||
void set_gid(uint8_t gid);
|
||||
void set_oid(uint8_t oid);
|
||||
void set_payload(const std::vector<uint8_t> &payload);
|
||||
void set_payload(std::span<const uint8_t> payload);
|
||||
void set_payload(std::initializer_list<uint8_t> payload) {
|
||||
this->set_payload(std::span<const uint8_t>(payload.begin(), payload.size()));
|
||||
}
|
||||
/// Appends bytes to the payload; bytes that do not fit are dropped
|
||||
void append(std::span<const uint8_t> data);
|
||||
void append(std::initializer_list<uint8_t> data) {
|
||||
this->append(std::span<const uint8_t>(data.begin(), data.size()));
|
||||
}
|
||||
/// Sets the packet size to the header plus `size` payload bytes, for a bus driver filling the buffer directly
|
||||
void set_payload_size(uint8_t size);
|
||||
|
||||
protected:
|
||||
std::vector<uint8_t> nci_message_{0, 0, 0}; // three bytes, MT/PBF/GID, OID, payload length/size
|
||||
Buffer nci_message_; // MT/PBF/GID, OID, payload length, then the payload
|
||||
};
|
||||
|
||||
} // namespace esphome::nfc
|
||||
|
||||
@@ -12,17 +12,15 @@ uint8_t PN7150I2C::read_nfcc(nfc::NciMessage &rx, const uint16_t timeout) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
rx.get_message().resize(nfc::NCI_PKT_HEADER_SIZE);
|
||||
rx.reset();
|
||||
if (!this->read_bytes_raw(rx.get_message().data(), nfc::NCI_PKT_HEADER_SIZE)) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
uint8_t length = rx.get_payload_size();
|
||||
if (length > 0) {
|
||||
rx.get_message().resize(length + nfc::NCI_PKT_HEADER_SIZE);
|
||||
if (!this->read_bytes_raw(rx.get_message().data() + nfc::NCI_PKT_HEADER_SIZE, length)) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
const uint8_t length = rx.get_payload_size();
|
||||
rx.set_payload_size(length);
|
||||
if (length > 0 && !this->read_bytes_raw(rx.get_message().data() + nfc::NCI_PKT_HEADER_SIZE, length)) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
// IRQ normally drops at the end of the read. If another message is queued it rises again at once, and the short
|
||||
// low pulse may be missed; that means more data is waiting, not that this read failed (UM10936, 3.4).
|
||||
@@ -33,7 +31,7 @@ uint8_t PN7150I2C::read_nfcc(nfc::NciMessage &rx, const uint16_t timeout) {
|
||||
}
|
||||
|
||||
uint8_t PN7150I2C::write_nfcc(nfc::NciMessage &tx) {
|
||||
auto encoded = tx.encode();
|
||||
const auto encoded = tx.encode();
|
||||
if (this->write(encoded.data(), encoded.size()) == i2c::ERROR_OK) {
|
||||
return nfc::STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -68,9 +68,8 @@ uint8_t PN7160::verify_reset(nfc::NciMessage &rx, const bool reset_config) {
|
||||
uint8_t PN7160::process_init_response(nfc::NciMessage &rx) {
|
||||
// the chip's version information is logged from CORE_RESET_NTF in verify_reset()
|
||||
if (rx.get_message().size() >= 8) {
|
||||
std::vector<uint8_t> features(rx.get_message().begin() + 4, rx.get_message().begin() + 8);
|
||||
char feat_buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
|
||||
ESP_LOGD(TAG, "NFCC features: %s", nfc::format_bytes_to(feat_buf, features));
|
||||
ESP_LOGD(TAG, "NFCC features: %s", nfc::format_bytes_to(feat_buf, rx.get_payload().subspan(1, 4)));
|
||||
}
|
||||
|
||||
return rx.get_simple_status_response();
|
||||
|
||||
@@ -12,17 +12,15 @@ uint8_t PN7160I2C::read_nfcc(nfc::NciMessage &rx, const uint16_t timeout) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
rx.get_message().resize(nfc::NCI_PKT_HEADER_SIZE);
|
||||
rx.reset();
|
||||
if (!this->read_bytes_raw(rx.get_message().data(), nfc::NCI_PKT_HEADER_SIZE)) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
uint8_t length = rx.get_payload_size();
|
||||
if (length > 0) {
|
||||
rx.get_message().resize(length + nfc::NCI_PKT_HEADER_SIZE);
|
||||
if (!this->read_bytes_raw(rx.get_message().data() + nfc::NCI_PKT_HEADER_SIZE, length)) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
const uint8_t length = rx.get_payload_size();
|
||||
rx.set_payload_size(length);
|
||||
if (length > 0 && !this->read_bytes_raw(rx.get_message().data() + nfc::NCI_PKT_HEADER_SIZE, length)) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
// IRQ normally drops at the end of the read. If another message is queued it rises again at once, and the short
|
||||
// low pulse may be missed; that means more data is waiting, not that this read failed (UM11495, 6.2.4).
|
||||
@@ -33,7 +31,7 @@ uint8_t PN7160I2C::read_nfcc(nfc::NciMessage &rx, const uint16_t timeout) {
|
||||
}
|
||||
|
||||
uint8_t PN7160I2C::write_nfcc(nfc::NciMessage &tx) {
|
||||
auto encoded = tx.encode();
|
||||
const auto encoded = tx.encode();
|
||||
if (this->write(encoded.data(), encoded.size()) == i2c::ERROR_OK) {
|
||||
return nfc::STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -16,14 +16,14 @@ uint8_t PN7160Spi::read_nfcc(nfc::NciMessage &rx, const uint16_t timeout) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
rx.get_message().resize(nfc::NCI_PKT_HEADER_SIZE);
|
||||
rx.reset();
|
||||
this->enable();
|
||||
this->write_byte(TDD_SPI_READ); // send "transfer direction detector"
|
||||
this->read_array(rx.get_message().data(), nfc::NCI_PKT_HEADER_SIZE);
|
||||
|
||||
uint8_t length = rx.get_payload_size();
|
||||
const uint8_t length = rx.get_payload_size();
|
||||
rx.set_payload_size(length);
|
||||
if (length > 0) {
|
||||
rx.get_message().resize(length + nfc::NCI_PKT_HEADER_SIZE);
|
||||
this->read_array(rx.get_message().data() + nfc::NCI_PKT_HEADER_SIZE, length);
|
||||
}
|
||||
this->disable();
|
||||
@@ -36,7 +36,7 @@ uint8_t PN7160Spi::read_nfcc(nfc::NciMessage &rx, const uint16_t timeout) {
|
||||
}
|
||||
|
||||
uint8_t PN7160Spi::write_nfcc(nfc::NciMessage &tx) {
|
||||
auto encoded = tx.encode();
|
||||
const auto encoded = tx.encode();
|
||||
this->enable();
|
||||
// send "transfer direction detector"; the NFCC answers 0xFF when it is ready to receive (UM11495, 6.3.3)
|
||||
const uint8_t status = this->transfer_byte(TDD_SPI_WRITE);
|
||||
|
||||
@@ -117,6 +117,12 @@ _CALLBACK_AUTOMATIONS = (
|
||||
)
|
||||
|
||||
|
||||
_request_ontag_trigger_slot = cg.slot_counter("PN71XX_ON_TAG_TRIGGER_COUNT")
|
||||
_request_ontagremoved_trigger_slot = cg.slot_counter(
|
||||
"PN71XX_ON_TAG_REMOVED_TRIGGER_COUNT"
|
||||
)
|
||||
|
||||
|
||||
async def setup_pn71xx(var: MockObj, config: ConfigType) -> None:
|
||||
await cg.register_component(var, config)
|
||||
|
||||
@@ -135,6 +141,7 @@ async def setup_pn71xx(var: MockObj, config: ConfigType) -> None:
|
||||
|
||||
for conf in config.get(CONF_ON_TAG, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
|
||||
_request_ontag_trigger_slot(str(var))
|
||||
cg.add(var.register_ontag_trigger(trigger))
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.std_string, "x"), (nfc.NfcTag, "tag")], conf
|
||||
@@ -142,6 +149,7 @@ async def setup_pn71xx(var: MockObj, config: ConfigType) -> None:
|
||||
|
||||
for conf in config.get(CONF_ON_TAG_REMOVED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
|
||||
_request_ontagremoved_trigger_slot(str(var))
|
||||
cg.add(var.register_ontagremoved_trigger(trigger))
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.std_string, "x"), (nfc.NfcTag, "tag")], conf
|
||||
|
||||
@@ -56,7 +56,10 @@ void PN71xx::set_tag_emulation_message(const std::shared_ptr<nfc::NdefMessage> &
|
||||
ESP_LOGE(TAG, "Tag emulation message too long: %zu > %u bytes", encoded.size(), CARD_EMU_T4T_MAX_NDEF_SIZE);
|
||||
return;
|
||||
}
|
||||
this->card_emulation_ndef_ = std::move(encoded);
|
||||
this->card_emulation_ndef_.init(encoded.size());
|
||||
for (const uint8_t byte : encoded) {
|
||||
this->card_emulation_ndef_.push_back(byte);
|
||||
}
|
||||
ESP_LOGD(TAG, "Tag emulation message set");
|
||||
}
|
||||
|
||||
@@ -190,8 +193,9 @@ uint8_t PN71xx::set_test_mode(const TestMode test_mode, const std::vector<uint8_
|
||||
this->nci_fsm_set_state_(NCIState::NFCC_RESET);
|
||||
result.clear();
|
||||
} else {
|
||||
result = rx.get_message();
|
||||
result.erase(result.begin(), result.begin() + 4); // remove NCI header
|
||||
// the payload after the status byte, if the NFCC sent one
|
||||
const auto payload = rx.get_payload();
|
||||
result.assign(payload.begin() + std::min<size_t>(1, payload.size()), payload.end());
|
||||
if (!result.empty()) {
|
||||
char buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
|
||||
ESP_LOGW(TAG, "Test results: %s", nfc::format_bytes_to(buf, result));
|
||||
@@ -256,9 +260,7 @@ uint8_t PN71xx::send_init_config_() {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
const auto pmu_config = this->pmu_config();
|
||||
tx.set_message(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::NCI_CORE_GID, nfc::NCI_CORE_SET_CONFIG_OID,
|
||||
std::vector<uint8_t>(pmu_config.begin(), pmu_config.end()));
|
||||
tx.set_message(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::NCI_CORE_GID, nfc::NCI_CORE_SET_CONFIG_OID, this->pmu_config());
|
||||
|
||||
if (this->transceive_(tx, rx) != nfc::STATUS_OK) {
|
||||
ESP_LOGE(TAG, "Error sending PMU config");
|
||||
@@ -269,19 +271,16 @@ uint8_t PN71xx::send_init_config_() {
|
||||
}
|
||||
|
||||
uint8_t PN71xx::send_core_config_() {
|
||||
const auto *core_config_begin = std::begin(CORE_CONFIG_SOLO);
|
||||
const auto *core_config_end = std::end(CORE_CONFIG_SOLO);
|
||||
std::span<const uint8_t> core_config = CORE_CONFIG_SOLO;
|
||||
this->core_config_is_solo_ = true;
|
||||
|
||||
if (this->listening_enabled_ && this->polling_enabled_) {
|
||||
core_config_begin = std::begin(CORE_CONFIG_RW_CE);
|
||||
core_config_end = std::end(CORE_CONFIG_RW_CE);
|
||||
core_config = CORE_CONFIG_RW_CE;
|
||||
this->core_config_is_solo_ = false;
|
||||
}
|
||||
|
||||
nfc::NciMessage rx;
|
||||
nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::NCI_CORE_GID, nfc::NCI_CORE_SET_CONFIG_OID,
|
||||
std::vector<uint8_t>(core_config_begin, core_config_end));
|
||||
nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::NCI_CORE_GID, nfc::NCI_CORE_SET_CONFIG_OID, core_config);
|
||||
|
||||
if (this->transceive_(tx, rx) != nfc::STATUS_OK) {
|
||||
ESP_LOGW(TAG, "Error sending core config");
|
||||
@@ -313,11 +312,10 @@ uint8_t PN71xx::refresh_core_config_() {
|
||||
}
|
||||
|
||||
uint8_t PN71xx::set_discover_map_() {
|
||||
std::vector<uint8_t> discover_map = {sizeof(RF_DISCOVER_MAP_CONFIG) / 3};
|
||||
discover_map.insert(discover_map.end(), std::begin(RF_DISCOVER_MAP_CONFIG), std::end(RF_DISCOVER_MAP_CONFIG));
|
||||
|
||||
nfc::NciMessage rx;
|
||||
nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::RF_GID, nfc::RF_DISCOVER_MAP_OID, discover_map);
|
||||
nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::RF_GID, nfc::RF_DISCOVER_MAP_OID,
|
||||
{sizeof(RF_DISCOVER_MAP_CONFIG) / 3});
|
||||
tx.append(RF_DISCOVER_MAP_CONFIG);
|
||||
|
||||
if (this->transceive_(tx, rx, NFCC_INIT_TIMEOUT) != nfc::STATUS_OK) {
|
||||
ESP_LOGE(TAG, "Error sending discover map poll config");
|
||||
@@ -327,10 +325,9 @@ uint8_t PN71xx::set_discover_map_() {
|
||||
}
|
||||
|
||||
uint8_t PN71xx::set_listen_mode_routing_() {
|
||||
const auto routing_config = this->listen_mode_routing_config();
|
||||
nfc::NciMessage rx;
|
||||
nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::RF_GID, nfc::RF_SET_LISTEN_MODE_ROUTING_OID,
|
||||
std::vector<uint8_t>(routing_config.begin(), routing_config.end()));
|
||||
this->listen_mode_routing_config());
|
||||
|
||||
if (this->transceive_(tx, rx, NFCC_INIT_TIMEOUT) != nfc::STATUS_OK) {
|
||||
ESP_LOGE(TAG, "Error setting listen mode routing config");
|
||||
@@ -340,27 +337,20 @@ uint8_t PN71xx::set_listen_mode_routing_() {
|
||||
}
|
||||
|
||||
uint8_t PN71xx::start_discovery_() {
|
||||
const uint8_t *rf_discovery_config = RF_DISCOVERY_CONFIG;
|
||||
uint8_t length = sizeof(RF_DISCOVERY_CONFIG);
|
||||
std::span<const uint8_t> rf_discovery_config = RF_DISCOVERY_CONFIG;
|
||||
|
||||
if (!this->listening_enabled_) {
|
||||
length = sizeof(RF_DISCOVERY_POLL_CONFIG);
|
||||
rf_discovery_config = RF_DISCOVERY_POLL_CONFIG;
|
||||
} else if (!this->polling_enabled_) {
|
||||
length = sizeof(RF_DISCOVERY_LISTEN_CONFIG);
|
||||
rf_discovery_config = RF_DISCOVERY_LISTEN_CONFIG;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> discover_config = std::vector<uint8_t>((length * 2) + 1);
|
||||
|
||||
discover_config[0] = length;
|
||||
for (uint8_t i = 0; i < length; i++) {
|
||||
discover_config[(i * 2) + 1] = rf_discovery_config[i];
|
||||
discover_config[(i * 2) + 2] = 0x01; // RF Technology and Mode will be executed in every discovery period
|
||||
}
|
||||
|
||||
nfc::NciMessage rx;
|
||||
nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::RF_GID, nfc::RF_DISCOVER_OID, discover_config);
|
||||
nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::RF_GID, nfc::RF_DISCOVER_OID,
|
||||
{static_cast<uint8_t>(rf_discovery_config.size())});
|
||||
for (const uint8_t mode_tech : rf_discovery_config) {
|
||||
tx.append({mode_tech, 0x01}); // RF Technology and Mode will be executed in every discovery period
|
||||
}
|
||||
|
||||
if (this->transceive_(tx, rx) != nfc::STATUS_OK) {
|
||||
switch (rx.get_simple_status_response()) {
|
||||
@@ -416,10 +406,9 @@ void PN71xx::select_endpoint_() {
|
||||
} else if (endpoint.protocol == nfc::PROT_MIFARE) {
|
||||
interface = nfc::INTF_TAGCMD;
|
||||
}
|
||||
std::vector<uint8_t> endpoint_data = {endpoint.id, endpoint.protocol, interface};
|
||||
|
||||
nfc::NciMessage rx;
|
||||
nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::RF_GID, nfc::RF_DISCOVER_SELECT_OID, endpoint_data);
|
||||
nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::RF_GID, nfc::RF_DISCOVER_SELECT_OID,
|
||||
{endpoint.id, endpoint.protocol, interface});
|
||||
|
||||
if (this->transceive_(tx, rx) != nfc::STATUS_OK) {
|
||||
ESP_LOGE(TAG, "Error selecting endpoint");
|
||||
@@ -491,31 +480,57 @@ uint8_t PN71xx::write_endpoint_(const uint8_t protocol, nfc::NfcTagUid &uid,
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> PN71xx::build_tag_(const uint8_t mode_tech, const uint8_t protocol,
|
||||
const std::vector<uint8_t> &data) {
|
||||
switch (mode_tech) {
|
||||
case (nfc::MODE_POLL | nfc::TECH_PASSIVE_NFCA): {
|
||||
// RF technology parameters: SENS_RES (2 bytes), NFCID1 length, NFCID1, ...
|
||||
if (data.size() < 3) {
|
||||
ESP_LOGE(TAG, "NFC-A parameters too short");
|
||||
return nullptr;
|
||||
}
|
||||
uint8_t uid_length = data[2];
|
||||
if (uid_length == 0 || uid_length > nfc::NFC_UID_MAX_LENGTH || data.size() < 3u + uid_length) {
|
||||
ESP_LOGE(TAG, "Invalid UID length: %u", uid_length);
|
||||
return nullptr;
|
||||
}
|
||||
nfc::NfcTagUid uid(data.begin() + 3, data.begin() + 3 + uid_length);
|
||||
if (protocol == nfc::PROT_MIFARE) {
|
||||
return make_unique<nfc::NfcTag>(uid, nfc::MIFARE_CLASSIC);
|
||||
}
|
||||
if (protocol == nfc::PROT_T2T) {
|
||||
return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2);
|
||||
}
|
||||
return make_unique<nfc::NfcTag>(uid);
|
||||
}
|
||||
bool PN71xx::parse_uid_(const uint8_t mode_tech, const std::span<const uint8_t> rf_tech_params, nfc::NfcTagUid &uid) {
|
||||
if (mode_tech != (nfc::MODE_POLL | nfc::TECH_PASSIVE_NFCA)) {
|
||||
return false;
|
||||
}
|
||||
return nullptr;
|
||||
// RF technology parameters: SENS_RES (2 bytes), NFCID1 length, NFCID1, ...
|
||||
if (rf_tech_params.size() < 3) {
|
||||
ESP_LOGE(TAG, "NFC-A parameters too short");
|
||||
return false;
|
||||
}
|
||||
const uint8_t uid_length = rf_tech_params[2];
|
||||
if (uid_length == 0 || uid_length > nfc::NFC_UID_MAX_LENGTH || rf_tech_params.size() < 3u + uid_length) {
|
||||
ESP_LOGE(TAG, "Invalid UID length: %u", uid_length);
|
||||
return false;
|
||||
}
|
||||
uid.assign(rf_tech_params.begin() + 3, rf_tech_params.begin() + 3 + uid_length);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> PN71xx::build_tag_(const uint8_t protocol, const nfc::NfcTagUid &uid) {
|
||||
if (protocol == nfc::PROT_MIFARE) {
|
||||
return make_unique<nfc::NfcTag>(uid, nfc::MIFARE_CLASSIC);
|
||||
}
|
||||
if (protocol == nfc::PROT_T2T) {
|
||||
return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2);
|
||||
}
|
||||
return make_unique<nfc::NfcTag>(uid);
|
||||
}
|
||||
|
||||
size_t PN71xx::find_or_add_tag_(const uint8_t protocol, const nfc::NfcTagUid &uid) {
|
||||
const auto tag_loc = this->find_tag_uid_(uid);
|
||||
if (tag_loc.has_value()) {
|
||||
ESP_LOGVV(TAG, "Tag cache updated");
|
||||
return tag_loc.value();
|
||||
}
|
||||
if (this->discovered_endpoint_.size() >= MAX_DISCOVERED_ENDPOINTS) {
|
||||
size_t oldest = 0;
|
||||
for (size_t i = 1; i < this->discovered_endpoint_.size(); i++) {
|
||||
if (this->discovered_endpoint_[i].last_seen < this->discovered_endpoint_[oldest].last_seen) {
|
||||
oldest = i;
|
||||
}
|
||||
}
|
||||
ESP_LOGW(TAG, "Tag cache full; dropping the tag seen longest ago");
|
||||
this->erase_tag_(oldest);
|
||||
}
|
||||
this->discovered_endpoint_.emplace_next() = DiscoveredEndpoint{.last_seen = App.get_loop_component_start_time(),
|
||||
.tag = this->build_tag_(protocol, uid),
|
||||
.id = 0,
|
||||
.protocol = protocol,
|
||||
.trig_called = false};
|
||||
ESP_LOGVV(TAG, "Tag added to cache");
|
||||
return this->discovered_endpoint_.size() - 1;
|
||||
}
|
||||
|
||||
optional<size_t> PN71xx::find_tag_uid_(const nfc::NfcTagUid &uid) {
|
||||
@@ -549,15 +564,23 @@ void PN71xx::purge_old_tags_() {
|
||||
|
||||
void PN71xx::erase_tag_(const uint8_t tag_index) {
|
||||
if (tag_index < this->discovered_endpoint_.size()) {
|
||||
#ifdef PN71XX_ON_TAG_REMOVED_TRIGGER_COUNT
|
||||
for (auto *trigger : this->triggers_ontagremoved_) {
|
||||
trigger->process(this->discovered_endpoint_[tag_index].tag);
|
||||
}
|
||||
#endif
|
||||
for (auto *listener : this->tag_listeners_) {
|
||||
listener->tag_off(*this->discovered_endpoint_[tag_index].tag);
|
||||
}
|
||||
char uid_buf[nfc::FORMAT_UID_BUFFER_SIZE];
|
||||
ESP_LOGI(TAG, "Tag %s removed", nfc::format_uid_to(uid_buf, this->discovered_endpoint_[tag_index].tag->get_uid()));
|
||||
this->discovered_endpoint_.erase(this->discovered_endpoint_.begin() + tag_index);
|
||||
// keep the remaining entries in order; selecting_endpoint_ indexes into this list
|
||||
for (size_t i = tag_index; i + 1 < this->discovered_endpoint_.size(); i++) {
|
||||
this->discovered_endpoint_[i] = std::move(this->discovered_endpoint_[i + 1]);
|
||||
}
|
||||
// StaticVector::resize() does not destroy the dropped slot; free its tag now, not when the slot is reused
|
||||
this->discovered_endpoint_[this->discovered_endpoint_.size() - 1].tag.reset();
|
||||
this->discovered_endpoint_.resize(this->discovered_endpoint_.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -815,31 +838,16 @@ void PN71xx::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoi
|
||||
this->nci_fsm_set_state_(NCIState::EP_DEACTIVATING);
|
||||
return;
|
||||
}
|
||||
auto incoming_tag =
|
||||
this->build_tag_(mode_tech, protocol,
|
||||
std::vector<uint8_t>(rx.get_message().begin() + nfc::RF_INTF_ACTIVATED_NTF_RF_TECH_PARAMS,
|
||||
rx.get_message().end()));
|
||||
|
||||
if (incoming_tag == nullptr) {
|
||||
nfc::NfcTagUid uid;
|
||||
if (!this->parse_uid_(mode_tech,
|
||||
std::span<const uint8_t>(rx.get_message()).subspan(nfc::RF_INTF_ACTIVATED_NTF_RF_TECH_PARAMS),
|
||||
uid)) {
|
||||
ESP_LOGE(TAG, "Could not build tag");
|
||||
} else {
|
||||
auto tag_loc = this->find_tag_uid_(incoming_tag->get_uid());
|
||||
if (tag_loc.has_value()) {
|
||||
this->discovered_endpoint_[tag_loc.value()].id = discovery_id;
|
||||
this->discovered_endpoint_[tag_loc.value()].protocol = protocol;
|
||||
this->discovered_endpoint_[tag_loc.value()].last_seen = App.get_loop_component_start_time();
|
||||
ESP_LOGVV(TAG, "Tag cache updated");
|
||||
} else {
|
||||
this->discovered_endpoint_.emplace_back(DiscoveredEndpoint{.last_seen = App.get_loop_component_start_time(),
|
||||
.tag = std::move(incoming_tag),
|
||||
.id = discovery_id,
|
||||
.protocol = protocol,
|
||||
.trig_called = false});
|
||||
tag_loc = this->discovered_endpoint_.size() - 1;
|
||||
ESP_LOGVV(TAG, "Tag added to cache");
|
||||
}
|
||||
|
||||
auto &working_endpoint = this->discovered_endpoint_[tag_loc.value()];
|
||||
auto &working_endpoint = this->discovered_endpoint_[this->find_or_add_tag_(protocol, uid)];
|
||||
working_endpoint.id = discovery_id;
|
||||
working_endpoint.protocol = protocol;
|
||||
working_endpoint.last_seen = App.get_loop_component_start_time();
|
||||
|
||||
switch (this->next_task_) {
|
||||
case EP_CLEAN:
|
||||
@@ -886,8 +894,8 @@ void PN71xx::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoi
|
||||
if (this->read_endpoint_data_(working_endpoint.protocol, *working_endpoint.tag) != nfc::STATUS_OK) {
|
||||
ESP_LOGW(TAG, " Unable to read NDEF record(s)");
|
||||
} else if (working_endpoint.tag->has_ndef_message()) {
|
||||
const auto message = working_endpoint.tag->get_ndef_message();
|
||||
const auto records = message->get_records();
|
||||
const auto &message = working_endpoint.tag->get_ndef_message();
|
||||
const auto &records = message->get_records();
|
||||
ESP_LOGD(TAG, " NDEF record(s):");
|
||||
for (const auto &record : records) {
|
||||
ESP_LOGD(TAG, " %s - %s", record->get_type().c_str(), record->get_payload().c_str());
|
||||
@@ -895,9 +903,11 @@ void PN71xx::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoi
|
||||
} else {
|
||||
ESP_LOGW(TAG, " No NDEF records found");
|
||||
}
|
||||
#ifdef PN71XX_ON_TAG_TRIGGER_COUNT
|
||||
for (auto *trigger : this->triggers_ontag_) {
|
||||
trigger->process(working_endpoint.tag);
|
||||
}
|
||||
#endif
|
||||
for (auto *listener : this->tag_listeners_) {
|
||||
listener->tag_on(*working_endpoint.tag);
|
||||
}
|
||||
@@ -924,31 +934,20 @@ void PN71xx::process_rf_discover_oid_(nfc::NciMessage &rx) {
|
||||
ESP_LOGE(TAG, "RF_DISCOVER_NTF too short");
|
||||
return;
|
||||
}
|
||||
auto incoming_tag = this->build_tag_(
|
||||
rx.get_message_byte(nfc::RF_DISCOVER_NTF_MODE_TECH), rx.get_message_byte(nfc::RF_DISCOVER_NTF_PROTOCOL),
|
||||
std::vector<uint8_t>(rx.get_message().begin() + nfc::RF_DISCOVER_NTF_RF_TECH_PARAMS, rx.get_message().end()));
|
||||
|
||||
if (incoming_tag == nullptr) {
|
||||
const uint8_t protocol = rx.get_message_byte(nfc::RF_DISCOVER_NTF_PROTOCOL);
|
||||
nfc::NfcTagUid uid;
|
||||
if (!this->parse_uid_(rx.get_message_byte(nfc::RF_DISCOVER_NTF_MODE_TECH),
|
||||
std::span<const uint8_t>(rx.get_message()).subspan(nfc::RF_DISCOVER_NTF_RF_TECH_PARAMS), uid)) {
|
||||
ESP_LOGE(TAG, "Could not build tag!");
|
||||
} else {
|
||||
auto tag_loc = this->find_tag_uid_(incoming_tag->get_uid());
|
||||
if (tag_loc.has_value()) {
|
||||
this->discovered_endpoint_[tag_loc.value()].id = rx.get_message_byte(nfc::RF_DISCOVER_NTF_DISCOVERY_ID);
|
||||
this->discovered_endpoint_[tag_loc.value()].protocol = rx.get_message_byte(nfc::RF_DISCOVER_NTF_PROTOCOL);
|
||||
this->discovered_endpoint_[tag_loc.value()].last_seen = App.get_loop_component_start_time();
|
||||
ESP_LOGVV(TAG, "Tag found & updated");
|
||||
} else {
|
||||
this->discovered_endpoint_.emplace_back(
|
||||
DiscoveredEndpoint{.last_seen = App.get_loop_component_start_time(),
|
||||
.tag = std::move(incoming_tag),
|
||||
.id = rx.get_message_byte(nfc::RF_DISCOVER_NTF_DISCOVERY_ID),
|
||||
.protocol = rx.get_message_byte(nfc::RF_DISCOVER_NTF_PROTOCOL),
|
||||
.trig_called = false});
|
||||
ESP_LOGVV(TAG, "Tag saved");
|
||||
}
|
||||
auto &endpoint = this->discovered_endpoint_[this->find_or_add_tag_(protocol, uid)];
|
||||
endpoint.id = rx.get_message_byte(nfc::RF_DISCOVER_NTF_DISCOVERY_ID);
|
||||
endpoint.protocol = protocol;
|
||||
endpoint.last_seen = App.get_loop_component_start_time();
|
||||
}
|
||||
|
||||
if (rx.get_message().back() != nfc::RF_DISCOVER_NTF_NT_MORE) {
|
||||
const auto &ntf = rx.get_message();
|
||||
if (ntf[ntf.size() - 1] != nfc::RF_DISCOVER_NTF_NT_MORE) {
|
||||
this->nci_fsm_set_state_(NCIState::RFST_W4_HOST_SELECT);
|
||||
ESP_LOGVV(TAG, "Discovered %zu endpoints", this->discovered_endpoint_.size());
|
||||
}
|
||||
@@ -986,17 +985,12 @@ void PN71xx::process_data_message_(nfc::NciMessage &rx) {
|
||||
char buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
|
||||
ESP_LOGVV(TAG, "Received data message: %s", nfc::format_bytes_to(buf, rx.get_message()));
|
||||
|
||||
std::vector<uint8_t> ndef_response;
|
||||
CardEmuResponse ndef_response;
|
||||
this->card_emu_t4t_get_response_(rx.get_message(), ndef_response);
|
||||
|
||||
if (ndef_response.empty()) {
|
||||
return; // no message returned, we cannot respond
|
||||
}
|
||||
// the CC limits MLe so every response fits in a single data packet (payload length is one byte)
|
||||
if (ndef_response.size() > UINT8_MAX) {
|
||||
ESP_LOGE(TAG, "Card emulation response too long: %zu bytes", ndef_response.size());
|
||||
return;
|
||||
}
|
||||
|
||||
nfc::NciMessage tx(nfc::NCI_PKT_MT_DATA, ndef_response);
|
||||
ESP_LOGVV(TAG, "Sending data message: %s", nfc::format_bytes_to(buf, tx.get_message()));
|
||||
@@ -1005,12 +999,14 @@ void PN71xx::process_data_message_(nfc::NciMessage &rx) {
|
||||
}
|
||||
}
|
||||
|
||||
bool PN71xx::card_emu_t4t_read_ndef_(const uint16_t offset, const uint8_t length, std::vector<uint8_t> &ndef_response) {
|
||||
bool PN71xx::card_emu_t4t_read_ndef_(const uint16_t offset, const uint8_t length, CardEmuResponse &ndef_response) {
|
||||
const auto &ndef_message = this->card_emulation_ndef_;
|
||||
// the NDEF file is a two-byte big-endian length (NLEN) followed by the message
|
||||
const uint16_t ndef_msg_size = ndef_message.size();
|
||||
const uint32_t file_size = ndef_msg_size + 2;
|
||||
if (offset + static_cast<uint32_t>(length) > file_size) {
|
||||
// the reply must also hold the two status bytes; the CC's MLe keeps well-behaved readers below this
|
||||
if (offset + static_cast<uint32_t>(length) > file_size ||
|
||||
length + sizeof(CARD_EMU_T4T_OK) > CardEmuResponse::capacity()) {
|
||||
return false;
|
||||
}
|
||||
for (uint32_t i = offset; i < offset + static_cast<uint32_t>(length); i++) {
|
||||
@@ -1029,7 +1025,7 @@ bool PN71xx::card_emu_t4t_read_ndef_(const uint16_t offset, const uint8_t length
|
||||
return true;
|
||||
}
|
||||
|
||||
void PN71xx::card_emu_t4t_get_response_(const std::vector<uint8_t> &response, std::vector<uint8_t> &ndef_response) {
|
||||
void PN71xx::card_emu_t4t_get_response_(const std::span<const uint8_t> response, CardEmuResponse &ndef_response) {
|
||||
ndef_response.clear();
|
||||
if (this->card_emulation_ndef_.empty()) {
|
||||
ESP_LOGE(TAG, "No NDEF message is set; tag emulation not possible");
|
||||
@@ -1047,6 +1043,11 @@ void PN71xx::card_emu_t4t_get_response_(const std::vector<uint8_t> &response, st
|
||||
auto apdu_starts_with = [&](const uint8_t *cmd, size_t cmd_size) {
|
||||
return apdu_size >= cmd_size && std::equal(cmd, cmd + cmd_size, apdu_begin);
|
||||
};
|
||||
auto append = [&](std::span<const uint8_t> bytes) {
|
||||
for (const uint8_t byte : bytes) {
|
||||
ndef_response.push_back(byte);
|
||||
}
|
||||
};
|
||||
bool ok = false;
|
||||
|
||||
if (apdu_is(CARD_EMU_T4T_APP_SELECT, sizeof(CARD_EMU_T4T_APP_SELECT)) ||
|
||||
@@ -1071,8 +1072,7 @@ void PN71xx::card_emu_t4t_get_response_(const std::vector<uint8_t> &response, st
|
||||
if (this->ce_state_ == CardEmulationState::CARD_EMU_CC_SELECTED) {
|
||||
ESP_LOGVV(TAG, "CARD_EMU_T4T_READ with CARD_EMU_CC_SELECTED");
|
||||
if (offset + static_cast<uint32_t>(length) <= sizeof(CARD_EMU_T4T_CC)) {
|
||||
ndef_response.insert(ndef_response.end(), std::begin(CARD_EMU_T4T_CC) + offset,
|
||||
std::begin(CARD_EMU_T4T_CC) + offset + length);
|
||||
append(std::span<const uint8_t>(CARD_EMU_T4T_CC).subspan(offset, length));
|
||||
ok = true;
|
||||
}
|
||||
} else if (this->ce_state_ == CardEmulationState::CARD_EMU_NDEF_SELECTED) {
|
||||
@@ -1084,18 +1084,18 @@ void PN71xx::card_emu_t4t_get_response_(const std::vector<uint8_t> &response, st
|
||||
const uint8_t length = apdu_begin[4];
|
||||
if (this->ce_state_ == CardEmulationState::CARD_EMU_NDEF_SELECTED && apdu_size >= 5u + length) {
|
||||
ESP_LOGVV(TAG, "CARD_EMU_T4T_WRITE");
|
||||
std::vector<uint8_t> ndef_msg_written(apdu_begin + 5, apdu_begin + 5 + length);
|
||||
char write_buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
|
||||
ESP_LOGD(TAG, "Received %u-byte NDEF message: %s", length, nfc::format_bytes_to(write_buf, ndef_msg_written));
|
||||
ESP_LOGD(TAG, "Received %u-byte NDEF message: %s", length,
|
||||
nfc::format_bytes_to(write_buf, response.subspan(nfc::NCI_PKT_HEADER_SIZE + 5, length)));
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
ndef_response.insert(ndef_response.end(), std::begin(CARD_EMU_T4T_OK), std::end(CARD_EMU_T4T_OK));
|
||||
append(CARD_EMU_T4T_OK);
|
||||
} else {
|
||||
ndef_response.clear();
|
||||
ndef_response.insert(ndef_response.end(), std::begin(CARD_EMU_T4T_NOK), std::end(CARD_EMU_T4T_NOK));
|
||||
append(CARD_EMU_T4T_NOK);
|
||||
this->ce_state_ = CardEmulationState::CARD_EMU_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "esphome/core/gpio.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <span>
|
||||
|
||||
@@ -68,7 +69,8 @@ static constexpr uint8_t CORE_CONFIG_RW_CE[] = {0x01, // Number of parameter f
|
||||
0xF8, // TOTAL_DURATION (low)...
|
||||
0x02}; // TOTAL_DURATION (high): 760 ms
|
||||
|
||||
static constexpr uint8_t RF_DISCOVER_MAP_CONFIG[] = { // poll modes
|
||||
static constexpr uint8_t RF_DISCOVER_MAP_CONFIG[] = {
|
||||
// poll modes
|
||||
nfc::PROT_T1T, nfc::RF_DISCOVER_MAP_MODE_POLL,
|
||||
nfc::INTF_FRAME, // poll mode
|
||||
nfc::PROT_T2T, nfc::RF_DISCOVER_MAP_MODE_POLL,
|
||||
@@ -131,6 +133,16 @@ enum class TestMode : uint8_t {
|
||||
TEST_GET_REGISTER,
|
||||
};
|
||||
|
||||
/// A card emulation reply; the CC limits reads so every reply fits one NCI data packet
|
||||
using CardEmuResponse = StaticVector<uint8_t, nfc::NCI_PKT_MAX_PAYLOAD_SIZE>;
|
||||
/// Holds pages 3 to 6 (16 bytes) plus an NDEF message of up to 255 bytes and its TLV header, rounded up to whole reads
|
||||
using UltralightReadBuffer = StaticVector<uint8_t, 272>;
|
||||
/// Longest NDEF message accepted from a MIFARE Classic tag (the capacity of a 4K tag)
|
||||
static constexpr uint32_t MIFARE_CLASSIC_MAX_NDEF_SIZE = 3440;
|
||||
/// Tags tracked at once. A device with a random UID looks like a new tag on every activation, but each entry
|
||||
/// expires after tag_ttl, so a handful is enough; when the cache is full the entry seen longest ago is evicted.
|
||||
static constexpr size_t MAX_DISCOVERED_ENDPOINTS = 8;
|
||||
|
||||
struct DiscoveredEndpoint {
|
||||
uint32_t last_seen;
|
||||
std::unique_ptr<nfc::NfcTag> tag;
|
||||
@@ -162,8 +174,12 @@ class PN71xx : public nfc::Nfcc, public Component {
|
||||
void set_polling_on();
|
||||
bool polling_enabled() { return this->polling_enabled_; }
|
||||
|
||||
#ifdef PN71XX_ON_TAG_TRIGGER_COUNT
|
||||
void register_ontag_trigger(nfc::NfcOnTagTrigger *trig) { this->triggers_ontag_.push_back(trig); }
|
||||
#endif
|
||||
#ifdef PN71XX_ON_TAG_REMOVED_TRIGGER_COUNT
|
||||
void register_ontagremoved_trigger(nfc::NfcOnTagTrigger *trig) { this->triggers_ontagremoved_.push_back(trig); }
|
||||
#endif
|
||||
|
||||
template<typename F> void add_on_emulated_tag_scan_callback(F &&callback) {
|
||||
this->on_emulated_tag_scan_callback_.add(std::forward<F>(callback));
|
||||
@@ -219,7 +235,11 @@ class PN71xx : public nfc::Nfcc, public Component {
|
||||
uint8_t format_endpoint_(uint8_t protocol);
|
||||
uint8_t write_endpoint_(uint8_t protocol, nfc::NfcTagUid &uid, std::shared_ptr<nfc::NdefMessage> &message);
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> build_tag_(uint8_t mode_tech, uint8_t protocol, const std::vector<uint8_t> &data);
|
||||
/// Reads the UID from the RF technology parameters of a discovery or activation notification
|
||||
bool parse_uid_(uint8_t mode_tech, std::span<const uint8_t> rf_tech_params, nfc::NfcTagUid &uid);
|
||||
std::unique_ptr<nfc::NfcTag> build_tag_(uint8_t protocol, const nfc::NfcTagUid &uid);
|
||||
/// Finds a cached endpoint by UID, or caches a new one, evicting the entry seen longest ago if the cache is full
|
||||
size_t find_or_add_tag_(uint8_t protocol, const nfc::NfcTagUid &uid);
|
||||
optional<size_t> find_tag_uid_(const nfc::NfcTagUid &uid);
|
||||
void purge_old_tags_();
|
||||
void erase_tag_(uint8_t tag_index);
|
||||
@@ -237,8 +257,8 @@ class PN71xx : public nfc::Nfcc, public Component {
|
||||
void process_rf_deactivate_oid_(nfc::NciMessage &rx);
|
||||
void process_data_message_(nfc::NciMessage &rx);
|
||||
|
||||
void card_emu_t4t_get_response_(const std::vector<uint8_t> &response, std::vector<uint8_t> &ndef_response);
|
||||
bool card_emu_t4t_read_ndef_(uint16_t offset, uint8_t length, std::vector<uint8_t> &ndef_response);
|
||||
void card_emu_t4t_get_response_(std::span<const uint8_t> response, CardEmuResponse &ndef_response);
|
||||
bool card_emu_t4t_read_ndef_(uint16_t offset, uint8_t length, CardEmuResponse &ndef_response);
|
||||
|
||||
uint8_t transceive_(nfc::NciMessage &tx, nfc::NciMessage &rx, uint16_t timeout = NFCC_DEFAULT_TIMEOUT,
|
||||
bool expect_notification = true);
|
||||
@@ -248,7 +268,7 @@ class PN71xx : public nfc::Nfcc, public Component {
|
||||
uint8_t wait_for_irq_(uint16_t timeout = NFCC_DEFAULT_TIMEOUT, bool pin_state = true);
|
||||
|
||||
uint8_t read_mifare_classic_tag_(nfc::NfcTag &tag);
|
||||
uint8_t read_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &data);
|
||||
uint8_t read_mifare_classic_block_(uint8_t block_num, std::array<uint8_t, nfc::MIFARE_CLASSIC_BLOCK_SIZE> &data);
|
||||
uint8_t write_mifare_classic_block_(uint8_t block_num, const uint8_t *data, size_t len);
|
||||
uint8_t auth_mifare_classic_block_(uint8_t block_num, uint8_t key_num, const uint8_t *key);
|
||||
uint8_t sect_to_auth_(uint8_t block_num);
|
||||
@@ -258,10 +278,10 @@ class PN71xx : public nfc::Nfcc, public Component {
|
||||
uint8_t halt_mifare_classic_tag_();
|
||||
|
||||
uint8_t read_mifare_ultralight_tag_(nfc::NfcTag &tag);
|
||||
uint8_t read_mifare_ultralight_bytes_(uint8_t start_page, uint16_t num_bytes, std::vector<uint8_t> &data);
|
||||
bool is_mifare_ultralight_formatted_(const std::vector<uint8_t> &page_3_to_6);
|
||||
uint8_t read_mifare_ultralight_bytes_(uint8_t start_page, uint16_t num_bytes, UltralightReadBuffer &data);
|
||||
bool is_mifare_ultralight_formatted_(std::span<const uint8_t> page_3_to_6);
|
||||
uint16_t read_mifare_ultralight_capacity_();
|
||||
uint8_t find_mifare_ultralight_ndef_(const std::vector<uint8_t> &page_3_to_6, uint8_t &message_length,
|
||||
uint8_t find_mifare_ultralight_ndef_(std::span<const uint8_t> page_3_to_6, uint8_t &message_length,
|
||||
uint8_t &message_start_index);
|
||||
uint8_t write_mifare_ultralight_page_(uint8_t page_num, const uint8_t *write_data, size_t len);
|
||||
uint8_t write_mifare_ultralight_tag_(nfc::NfcTagUid &uid, const std::shared_ptr<nfc::NdefMessage> &message);
|
||||
@@ -275,13 +295,17 @@ class PN71xx : public nfc::Nfcc, public Component {
|
||||
};
|
||||
|
||||
// members are ordered by alignment, widest first, to minimize padding
|
||||
CallbackManager<void()> on_emulated_tag_scan_callback_;
|
||||
CallbackManager<void()> on_finished_write_callback_;
|
||||
LazyCallbackManager<void()> on_emulated_tag_scan_callback_;
|
||||
LazyCallbackManager<void()> on_finished_write_callback_;
|
||||
|
||||
std::vector<DiscoveredEndpoint> discovered_endpoint_;
|
||||
std::vector<uint8_t> card_emulation_ndef_; // encoded emulation message; empty when none is set
|
||||
std::vector<nfc::NfcOnTagTrigger *> triggers_ontag_;
|
||||
std::vector<nfc::NfcOnTagTrigger *> triggers_ontagremoved_;
|
||||
StaticVector<DiscoveredEndpoint, MAX_DISCOVERED_ENDPOINTS> discovered_endpoint_;
|
||||
FixedVector<uint8_t> card_emulation_ndef_; // encoded emulation message; empty when none is set
|
||||
#ifdef PN71XX_ON_TAG_TRIGGER_COUNT
|
||||
StaticVector<nfc::NfcOnTagTrigger *, PN71XX_ON_TAG_TRIGGER_COUNT> triggers_ontag_;
|
||||
#endif
|
||||
#ifdef PN71XX_ON_TAG_REMOVED_TRIGGER_COUNT
|
||||
StaticVector<nfc::NfcOnTagTrigger *, PN71XX_ON_TAG_REMOVED_TRIGGER_COUNT> triggers_ontagremoved_;
|
||||
#endif
|
||||
std::shared_ptr<nfc::NdefMessage> next_task_message_to_write_;
|
||||
|
||||
GPIOPin *irq_pin_{nullptr};
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
|
||||
#include "pn71xx.h"
|
||||
@@ -17,56 +19,59 @@ uint8_t PN71xx::read_mifare_classic_tag_(nfc::NfcTag &tag) {
|
||||
ESP_LOGE(TAG, "Tag auth failed while attempting to read tag data");
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
std::vector<uint8_t> data;
|
||||
std::array<uint8_t, nfc::MIFARE_CLASSIC_BLOCK_SIZE> block_data;
|
||||
|
||||
if (this->read_mifare_classic_block_(current_block, data) == nfc::STATUS_OK) {
|
||||
if (!nfc::decode_mifare_classic_tlv(data, message_length, message_start_index)) {
|
||||
if (this->read_mifare_classic_block_(current_block, block_data) == nfc::STATUS_OK) {
|
||||
if (!nfc::decode_mifare_classic_tlv(block_data, message_length, message_start_index)) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to read block %u", current_block);
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
if (message_length > MIFARE_CLASSIC_MAX_NDEF_SIZE) {
|
||||
ESP_LOGE(TAG, "NDEF message too long: %" PRIu32 " bytes", message_length);
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
uint32_t index = 0;
|
||||
uint32_t buffer_size = nfc::get_mifare_classic_buffer_size(message_length);
|
||||
std::vector<uint8_t> buffer;
|
||||
const uint32_t buffer_size = nfc::get_mifare_classic_buffer_size(message_length);
|
||||
FixedVector<uint8_t> buffer;
|
||||
if (!buffer.try_init(buffer_size)) {
|
||||
ESP_LOGE(TAG, "Out of memory reading NDEF message of %" PRIu32 " bytes", buffer_size);
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
while (index < buffer_size) {
|
||||
while (buffer.size() < buffer_size) {
|
||||
if (nfc::mifare_classic_is_first_block(current_block)) {
|
||||
if (this->auth_mifare_classic_block_(current_block, nfc::MIFARE_CMD_AUTH_A, nfc::NDEF_KEY) != nfc::STATUS_OK) {
|
||||
ESP_LOGE(TAG, "Block authentication failed for %u", current_block);
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
}
|
||||
std::vector<uint8_t> block_data;
|
||||
if (this->read_mifare_classic_block_(current_block, block_data) != nfc::STATUS_OK) {
|
||||
ESP_LOGE(TAG, "Error reading block %u", current_block);
|
||||
return nfc::STATUS_FAILED;
|
||||
} else {
|
||||
buffer.insert(buffer.end(), block_data.begin(), block_data.end());
|
||||
}
|
||||
for (const uint8_t byte : block_data) {
|
||||
buffer.push_back(byte);
|
||||
}
|
||||
|
||||
index += nfc::MIFARE_CLASSIC_BLOCK_SIZE;
|
||||
current_block++;
|
||||
|
||||
if (nfc::mifare_classic_is_trailer_block(current_block)) {
|
||||
current_block++;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer.begin() + message_start_index < buffer.end()) {
|
||||
buffer.erase(buffer.begin(), buffer.begin() + message_start_index);
|
||||
} else {
|
||||
if (message_start_index >= buffer.size()) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
tag.set_ndef_message(make_unique<nfc::NdefMessage>(buffer));
|
||||
tag.set_ndef_message(make_unique<nfc::NdefMessage>(std::span<const uint8_t>(buffer).subspan(message_start_index)));
|
||||
|
||||
return nfc::STATUS_OK;
|
||||
}
|
||||
|
||||
uint8_t PN71xx::read_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &data) {
|
||||
uint8_t PN71xx::read_mifare_classic_block_(uint8_t block_num,
|
||||
std::array<uint8_t, nfc::MIFARE_CLASSIC_BLOCK_SIZE> &data) {
|
||||
nfc::NciMessage rx;
|
||||
nfc::NciMessage tx(nfc::NCI_PKT_MT_DATA, {XCHG_DATA_OID, nfc::MIFARE_CMD_READ, block_num});
|
||||
char buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
|
||||
@@ -84,32 +89,35 @@ uint8_t PN71xx::read_mifare_classic_block_(uint8_t block_num, std::vector<uint8_
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
data.insert(data.begin(), rx.get_message().begin() + 4, rx.get_message().end() - 1);
|
||||
// payload: XCHG_DATA status byte, 16 block bytes, one trailing status byte
|
||||
std::copy_n(rx.get_message().begin() + 4, data.size(), data.begin());
|
||||
|
||||
ESP_LOGVV(TAG, " Block %u: %s", block_num, nfc::format_bytes_to(buf, data));
|
||||
return nfc::STATUS_OK;
|
||||
}
|
||||
|
||||
uint8_t PN71xx::auth_mifare_classic_block_(uint8_t block_num, uint8_t key_num, const uint8_t *key) {
|
||||
nfc::NciMessage rx;
|
||||
nfc::NciMessage tx(nfc::NCI_PKT_MT_DATA, {MFC_AUTHENTICATE_OID, this->sect_to_auth_(block_num), key_num});
|
||||
|
||||
uint8_t key_select = key_num;
|
||||
switch (key_num) {
|
||||
case nfc::MIFARE_CMD_AUTH_A:
|
||||
tx.get_message().back() = MFC_AUTHENTICATE_PARAM_KS_A;
|
||||
key_select = MFC_AUTHENTICATE_PARAM_KS_A;
|
||||
break;
|
||||
|
||||
case nfc::MIFARE_CMD_AUTH_B:
|
||||
tx.get_message().back() = MFC_AUTHENTICATE_PARAM_KS_B;
|
||||
key_select = MFC_AUTHENTICATE_PARAM_KS_B;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (key != nullptr) {
|
||||
tx.get_message().back() |= MFC_AUTHENTICATE_PARAM_EMBED_KEY;
|
||||
tx.get_message().insert(tx.get_message().end(), key, key + 6);
|
||||
key_select |= MFC_AUTHENTICATE_PARAM_EMBED_KEY;
|
||||
}
|
||||
|
||||
nfc::NciMessage rx;
|
||||
nfc::NciMessage tx(nfc::NCI_PKT_MT_DATA, {MFC_AUTHENTICATE_OID, this->sect_to_auth_(block_num), key_select});
|
||||
if (key != nullptr) {
|
||||
tx.append(std::span<const uint8_t>(key, 6));
|
||||
}
|
||||
|
||||
char buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
|
||||
@@ -249,7 +257,7 @@ uint8_t PN71xx::write_mifare_classic_block_(uint8_t block_num, const uint8_t *da
|
||||
}
|
||||
// write command part two
|
||||
tx.set_payload({XCHG_DATA_OID});
|
||||
tx.get_message().insert(tx.get_message().end(), data, data + len);
|
||||
tx.append(std::span<const uint8_t>(data, len));
|
||||
|
||||
ESP_LOGVV(TAG, "Write XCHG_DATA_REQ 2: %s", nfc::format_bytes_to(buf, tx.get_message()));
|
||||
if (this->transceive_(tx, rx, NFCC_TAG_WRITE_TIMEOUT) != nfc::STATUS_OK) {
|
||||
@@ -268,22 +276,10 @@ uint8_t PN71xx::write_mifare_classic_block_(uint8_t block_num, const uint8_t *da
|
||||
}
|
||||
|
||||
uint8_t PN71xx::write_mifare_classic_tag_(const std::shared_ptr<nfc::NdefMessage> &message) {
|
||||
auto encoded = message->encode();
|
||||
|
||||
uint32_t message_length = encoded.size();
|
||||
uint32_t buffer_length = nfc::get_mifare_classic_buffer_size(message_length);
|
||||
|
||||
encoded.insert(encoded.begin(), 0x03);
|
||||
if (message_length < 255) {
|
||||
encoded.insert(encoded.begin() + 1, message_length);
|
||||
} else {
|
||||
encoded.insert(encoded.begin() + 1, 0xFF);
|
||||
encoded.insert(encoded.begin() + 2, (message_length >> 8) & 0xFF);
|
||||
encoded.insert(encoded.begin() + 3, message_length & 0xFF);
|
||||
}
|
||||
encoded.push_back(0xFE);
|
||||
|
||||
encoded.resize(buffer_length, 0);
|
||||
const auto encoded = message->encode();
|
||||
const uint32_t buffer_length = nfc::get_mifare_classic_buffer_size(encoded.size());
|
||||
FixedVector<uint8_t> buffer;
|
||||
nfc::fill_ndef_tlv(encoded, buffer_length, buffer);
|
||||
|
||||
uint32_t index = 0;
|
||||
uint8_t current_block = 4;
|
||||
@@ -295,7 +291,7 @@ uint8_t PN71xx::write_mifare_classic_tag_(const std::shared_ptr<nfc::NdefMessage
|
||||
}
|
||||
}
|
||||
|
||||
if (this->write_mifare_classic_block_(current_block, encoded.data() + index, nfc::MIFARE_CLASSIC_BLOCK_SIZE) !=
|
||||
if (this->write_mifare_classic_block_(current_block, &buffer[index], nfc::MIFARE_CLASSIC_BLOCK_SIZE) !=
|
||||
nfc::STATUS_OK) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
@@ -10,7 +11,7 @@ namespace esphome::pn71xx {
|
||||
static const char *const TAG = "pn71xx.mifare_ultralight";
|
||||
|
||||
uint8_t PN71xx::read_mifare_ultralight_tag_(nfc::NfcTag &tag) {
|
||||
std::vector<uint8_t> data;
|
||||
UltralightReadBuffer data;
|
||||
// pages 3 to 6 contain various info we are interested in -- do one read to grab it all
|
||||
if (this->read_mifare_ultralight_bytes_(3, nfc::MIFARE_ULTRALIGHT_PAGE_SIZE * nfc::MIFARE_ULTRALIGHT_READ_SIZE,
|
||||
data) != nfc::STATUS_OK) {
|
||||
@@ -42,21 +43,24 @@ uint8_t PN71xx::read_mifare_ultralight_tag_(nfc::NfcTag &tag) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
}
|
||||
// we need to trim off page 3 as well as any bytes ahead of message_start_index
|
||||
data.erase(data.begin(), data.begin() + message_start_index + nfc::MIFARE_ULTRALIGHT_PAGE_SIZE);
|
||||
|
||||
tag.set_ndef_message(make_unique<nfc::NdefMessage>(data));
|
||||
// skip page 3 as well as any bytes ahead of message_start_index
|
||||
const size_t skip = message_start_index + nfc::MIFARE_ULTRALIGHT_PAGE_SIZE;
|
||||
if (skip >= data.size()) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
tag.set_ndef_message(make_unique<nfc::NdefMessage>(std::span<const uint8_t>(data).subspan(skip)));
|
||||
|
||||
return nfc::STATUS_OK;
|
||||
}
|
||||
|
||||
uint8_t PN71xx::read_mifare_ultralight_bytes_(uint8_t start_page, uint16_t num_bytes, std::vector<uint8_t> &data) {
|
||||
uint8_t PN71xx::read_mifare_ultralight_bytes_(uint8_t start_page, uint16_t num_bytes, UltralightReadBuffer &data) {
|
||||
const uint8_t read_increment = nfc::MIFARE_ULTRALIGHT_READ_SIZE * nfc::MIFARE_ULTRALIGHT_PAGE_SIZE;
|
||||
nfc::NciMessage rx;
|
||||
nfc::NciMessage tx(nfc::NCI_PKT_MT_DATA, {nfc::MIFARE_CMD_READ, start_page});
|
||||
|
||||
for (size_t i = 0; i * read_increment < num_bytes; i++) {
|
||||
tx.get_message().back() = i * nfc::MIFARE_ULTRALIGHT_READ_SIZE + start_page;
|
||||
const uint8_t page = i * nfc::MIFARE_ULTRALIGHT_READ_SIZE + start_page;
|
||||
tx.set_payload({nfc::MIFARE_CMD_READ, page});
|
||||
// a short answer (e.g. a NAK for a page beyond the end of the tag) is retried a limited number of times
|
||||
uint8_t attempts = 0;
|
||||
do {
|
||||
@@ -65,16 +69,15 @@ uint8_t PN71xx::read_mifare_ultralight_bytes_(uint8_t start_page, uint16_t num_b
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
if (++attempts > NFCC_MAX_COMM_FAILS && rx.get_payload_size() < read_increment) {
|
||||
ESP_LOGE(TAG, "Short read from page %u", tx.get_message().back());
|
||||
ESP_LOGE(TAG, "Short read from page %u", page);
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
} while (rx.get_payload_size() < read_increment);
|
||||
uint16_t bytes_offset = (i + 1) * read_increment;
|
||||
auto pages_in_end_itr = bytes_offset <= num_bytes ? rx.get_message().end() - 1
|
||||
: rx.get_message().end() - (bytes_offset - num_bytes + 1);
|
||||
|
||||
if ((pages_in_end_itr > rx.get_message().begin()) && (pages_in_end_itr < rx.get_message().end())) {
|
||||
data.insert(data.end(), rx.get_message().begin() + nfc::NCI_PKT_HEADER_SIZE, pages_in_end_itr);
|
||||
// the payload ends with a status byte; keep only the bytes still wanted from this read
|
||||
const uint16_t wanted = num_bytes - i * read_increment;
|
||||
const size_t count = std::min<size_t>(read_increment, wanted);
|
||||
for (const uint8_t byte : rx.get_payload().subspan(0, count)) {
|
||||
data.push_back(byte);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +87,7 @@ uint8_t PN71xx::read_mifare_ultralight_bytes_(uint8_t start_page, uint16_t num_b
|
||||
return nfc::STATUS_OK;
|
||||
}
|
||||
|
||||
bool PN71xx::is_mifare_ultralight_formatted_(const std::vector<uint8_t> &page_3_to_6) {
|
||||
bool PN71xx::is_mifare_ultralight_formatted_(const std::span<const uint8_t> page_3_to_6) {
|
||||
const uint8_t p4_offset = nfc::MIFARE_ULTRALIGHT_PAGE_SIZE; // page 4 will begin 4 bytes into the vector
|
||||
|
||||
return (page_3_to_6.size() > p4_offset + 3) &&
|
||||
@@ -93,7 +96,7 @@ bool PN71xx::is_mifare_ultralight_formatted_(const std::vector<uint8_t> &page_3_
|
||||
}
|
||||
|
||||
uint16_t PN71xx::read_mifare_ultralight_capacity_() {
|
||||
std::vector<uint8_t> data;
|
||||
UltralightReadBuffer data;
|
||||
if (this->read_mifare_ultralight_bytes_(3, nfc::MIFARE_ULTRALIGHT_PAGE_SIZE, data) == nfc::STATUS_OK) {
|
||||
ESP_LOGV(TAG, "Tag capacity is %u bytes", data[2] * 8U);
|
||||
return data[2] * 8U;
|
||||
@@ -101,7 +104,7 @@ uint16_t PN71xx::read_mifare_ultralight_capacity_() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t PN71xx::find_mifare_ultralight_ndef_(const std::vector<uint8_t> &page_3_to_6, uint8_t &message_length,
|
||||
uint8_t PN71xx::find_mifare_ultralight_ndef_(const std::span<const uint8_t> page_3_to_6, uint8_t &message_length,
|
||||
uint8_t &message_start_index) {
|
||||
const uint8_t p4_offset = nfc::MIFARE_ULTRALIGHT_PAGE_SIZE; // page 4 will begin 4 bytes into the vector
|
||||
|
||||
@@ -124,33 +127,22 @@ uint8_t PN71xx::find_mifare_ultralight_ndef_(const std::vector<uint8_t> &page_3_
|
||||
uint8_t PN71xx::write_mifare_ultralight_tag_(nfc::NfcTagUid &uid, const std::shared_ptr<nfc::NdefMessage> &message) {
|
||||
uint32_t capacity = this->read_mifare_ultralight_capacity_();
|
||||
|
||||
auto encoded = message->encode();
|
||||
|
||||
uint32_t message_length = encoded.size();
|
||||
uint32_t buffer_length = nfc::get_mifare_ultralight_buffer_size(message_length);
|
||||
const auto encoded = message->encode();
|
||||
const uint32_t buffer_length = nfc::get_mifare_ultralight_buffer_size(encoded.size());
|
||||
|
||||
if (buffer_length > capacity) {
|
||||
ESP_LOGE(TAG, "Message length exceeds tag capacity %" PRIu32 " > %" PRIu32, buffer_length, capacity);
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
encoded.insert(encoded.begin(), 0x03);
|
||||
if (message_length < 255) {
|
||||
encoded.insert(encoded.begin() + 1, message_length);
|
||||
} else {
|
||||
encoded.insert(encoded.begin() + 1, 0xFF);
|
||||
encoded.insert(encoded.begin() + 2, (message_length >> 8) & 0xFF);
|
||||
encoded.insert(encoded.begin() + 3, message_length & 0xFF);
|
||||
}
|
||||
encoded.push_back(0xFE);
|
||||
|
||||
encoded.resize(buffer_length, 0);
|
||||
FixedVector<uint8_t> buffer;
|
||||
nfc::fill_ndef_tlv(encoded, buffer_length, buffer);
|
||||
|
||||
uint32_t index = 0;
|
||||
uint8_t current_page = nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE;
|
||||
|
||||
while (index < buffer_length) {
|
||||
if (this->write_mifare_ultralight_page_(current_page, encoded.data() + index, nfc::MIFARE_ULTRALIGHT_PAGE_SIZE) !=
|
||||
if (this->write_mifare_ultralight_page_(current_page, &buffer[index], nfc::MIFARE_ULTRALIGHT_PAGE_SIZE) !=
|
||||
nfc::STATUS_OK) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
@@ -175,11 +167,9 @@ uint8_t PN71xx::clean_mifare_ultralight_() {
|
||||
}
|
||||
|
||||
uint8_t PN71xx::write_mifare_ultralight_page_(uint8_t page_num, const uint8_t *write_data, size_t len) {
|
||||
std::vector<uint8_t> payload = {nfc::MIFARE_CMD_WRITE_ULTRALIGHT, page_num};
|
||||
payload.insert(payload.end(), write_data, write_data + len);
|
||||
|
||||
nfc::NciMessage rx;
|
||||
nfc::NciMessage tx(nfc::NCI_PKT_MT_DATA, payload);
|
||||
nfc::NciMessage tx(nfc::NCI_PKT_MT_DATA, {nfc::MIFARE_CMD_WRITE_ULTRALIGHT, page_num});
|
||||
tx.append(std::span<const uint8_t>(write_data, len));
|
||||
|
||||
if (this->transceive_(tx, rx, NFCC_TAG_WRITE_TIMEOUT) != nfc::STATUS_OK) {
|
||||
ESP_LOGE(TAG, "Error writing page %u", page_num);
|
||||
|
||||
@@ -144,6 +144,8 @@
|
||||
#define PN532_BINARY_SENSOR_COUNT 1
|
||||
#define PN532_ON_TAG_REMOVED_TRIGGER_COUNT 1
|
||||
#define PN532_ON_TAG_TRIGGER_COUNT 1
|
||||
#define PN71XX_ON_TAG_REMOVED_TRIGGER_COUNT 1
|
||||
#define PN71XX_ON_TAG_TRIGGER_COUNT 1
|
||||
#define REMOTE_BASE_DUMPER_COUNT 1
|
||||
#define REMOTE_BASE_LISTENER_COUNT 1
|
||||
#define USE_REMOTE_PROTOCOL_ABBWELCOME
|
||||
|
||||
@@ -12,6 +12,9 @@ namespace {
|
||||
class FakePN71xx : public PN71xx {
|
||||
public:
|
||||
using PN71xx::card_emu_t4t_get_response_;
|
||||
using PN71xx::discovered_endpoint_;
|
||||
using PN71xx::erase_tag_;
|
||||
using PN71xx::find_or_add_tag_;
|
||||
using PN71xx::transceive_;
|
||||
|
||||
std::deque<std::vector<uint8_t>> to_read;
|
||||
@@ -36,7 +39,8 @@ class FakePN71xx : public PN71xx {
|
||||
this->write_failures--;
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
this->written.push_back(tx.encode());
|
||||
const auto encoded = tx.encode();
|
||||
this->written.emplace_back(encoded.begin(), encoded.end());
|
||||
return nfc::STATUS_OK;
|
||||
}
|
||||
};
|
||||
@@ -48,9 +52,9 @@ std::vector<uint8_t> apdu(std::initializer_list<uint8_t> bytes) {
|
||||
}
|
||||
|
||||
std::vector<uint8_t> respond(FakePN71xx &nfcc, std::initializer_list<uint8_t> bytes) {
|
||||
std::vector<uint8_t> response;
|
||||
CardEmuResponse response;
|
||||
nfcc.card_emu_t4t_get_response_(apdu(bytes), response);
|
||||
return response;
|
||||
return {response.begin(), response.end()};
|
||||
}
|
||||
|
||||
void select_ndef_file(FakePN71xx &nfcc) {
|
||||
@@ -58,6 +62,10 @@ void select_ndef_file(FakePN71xx &nfcc) {
|
||||
respond(nfcc, {0x00, 0xA4, 0x00, 0x0C, 0x02, 0xE1, 0x04});
|
||||
}
|
||||
|
||||
std::vector<uint8_t> bytes_of(const nfc::NciMessage &msg) {
|
||||
return {msg.get_message().begin(), msg.get_message().end()};
|
||||
}
|
||||
|
||||
const std::vector<uint8_t> SW_OK = {0x90, 0x00};
|
||||
const std::vector<uint8_t> SW_NOT_FOUND = {0x6A, 0x82};
|
||||
|
||||
@@ -80,7 +88,7 @@ TEST(PN71xxTransceive, SkipsNotificationAheadOfResponse) {
|
||||
nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::RF_GID, nfc::RF_DEACTIVATE_OID, {0x00});
|
||||
nfc::NciMessage rx;
|
||||
EXPECT_EQ(nfcc.transceive_(tx, rx), nfc::STATUS_OK);
|
||||
EXPECT_EQ(rx.get_message(), (std::vector<uint8_t>{0x41, 0x06, 0x01, 0x00}));
|
||||
EXPECT_EQ(bytes_of(rx), (std::vector<uint8_t>{0x41, 0x06, 0x01, 0x00}));
|
||||
EXPECT_EQ(nfcc.written.size(), 1u);
|
||||
}
|
||||
|
||||
@@ -100,10 +108,65 @@ TEST(PN71xxTransceive, SkipsStaleResponseFromEarlierCommand) {
|
||||
nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::RF_GID, nfc::RF_DISCOVER_OID, {0x00});
|
||||
nfc::NciMessage rx;
|
||||
EXPECT_EQ(nfcc.transceive_(tx, rx), nfc::STATUS_OK);
|
||||
EXPECT_EQ(rx.get_message(), (std::vector<uint8_t>{0x41, 0x03, 0x01, 0x00}));
|
||||
EXPECT_EQ(bytes_of(rx), (std::vector<uint8_t>{0x41, 0x03, 0x01, 0x00}));
|
||||
EXPECT_EQ(nfcc.written.size(), 1u);
|
||||
}
|
||||
|
||||
nfc::NfcTagUid uid_of(uint8_t last) { return {0x04, 0x00, 0x00, last}; }
|
||||
|
||||
// Erasing an entry keeps the others in order and frees the tag of the slot that is dropped.
|
||||
TEST(PN71xxTagCache, EraseKeepsOrderAndFreesTail) {
|
||||
FakePN71xx nfcc;
|
||||
for (uint8_t i = 0; i < 3; i++) {
|
||||
nfcc.find_or_add_tag_(nfc::PROT_T2T, uid_of(i));
|
||||
}
|
||||
nfcc.erase_tag_(1);
|
||||
ASSERT_EQ(nfcc.discovered_endpoint_.size(), 2u);
|
||||
EXPECT_EQ(nfcc.discovered_endpoint_[0].tag->get_uid()[3], 0);
|
||||
EXPECT_EQ(nfcc.discovered_endpoint_[1].tag->get_uid()[3], 2);
|
||||
EXPECT_EQ(nfcc.discovered_endpoint_.data()[2].tag, nullptr);
|
||||
nfcc.erase_tag_(1);
|
||||
ASSERT_EQ(nfcc.discovered_endpoint_.size(), 1u);
|
||||
EXPECT_EQ(nfcc.discovered_endpoint_.data()[1].tag, nullptr);
|
||||
}
|
||||
|
||||
// A full cache evicts the entry seen longest ago instead of refusing the new tag.
|
||||
TEST(PN71xxTagCache, FullCacheEvictsOldest) {
|
||||
FakePN71xx nfcc;
|
||||
for (uint8_t i = 0; i < MAX_DISCOVERED_ENDPOINTS; i++) {
|
||||
const size_t loc = nfcc.find_or_add_tag_(nfc::PROT_T2T, uid_of(i));
|
||||
nfcc.discovered_endpoint_[loc].last_seen = 100 + i;
|
||||
}
|
||||
nfcc.discovered_endpoint_[3].last_seen = 1; // seen longest ago
|
||||
const size_t loc = nfcc.find_or_add_tag_(nfc::PROT_T2T, uid_of(0x99));
|
||||
ASSERT_EQ(nfcc.discovered_endpoint_.size(), MAX_DISCOVERED_ENDPOINTS);
|
||||
EXPECT_EQ(nfcc.discovered_endpoint_[loc].tag->get_uid()[3], 0x99);
|
||||
for (const auto &endpoint : nfcc.discovered_endpoint_) {
|
||||
EXPECT_NE(endpoint.tag->get_uid()[3], 3);
|
||||
}
|
||||
// a known UID is found, not added again
|
||||
EXPECT_EQ(nfcc.find_or_add_tag_(nfc::PROT_T2T, uid_of(0x99)), loc);
|
||||
EXPECT_EQ(nfcc.discovered_endpoint_.size(), MAX_DISCOVERED_ENDPOINTS);
|
||||
}
|
||||
|
||||
// Bytes that do not fit the packet are dropped and the length byte stays consistent.
|
||||
TEST(PN71xxNciMessage, AppendStopsAtPacketSize) {
|
||||
nfc::NciMessage msg(nfc::NCI_PKT_MT_DATA, {0x01});
|
||||
std::vector<uint8_t> big(300, 0xAA);
|
||||
msg.append(big);
|
||||
const auto encoded = msg.encode();
|
||||
EXPECT_EQ(encoded.size(), nfc::NCI_PKT_MAX_SIZE);
|
||||
EXPECT_EQ(msg.get_payload_size(), nfc::NCI_PKT_MAX_PAYLOAD_SIZE);
|
||||
}
|
||||
|
||||
// A read that could not fit the status bytes into one packet is refused.
|
||||
TEST(PN71xxCardEmulation, OversizedReadIsRejected) {
|
||||
FakePN71xx nfcc;
|
||||
nfcc.set_tag_emulation_message("https://www.home-assistant.io/tag/pulse_ce");
|
||||
select_ndef_file(nfcc);
|
||||
EXPECT_EQ(respond(nfcc, {0x00, 0xB0, 0x00, 0x00, 0xFE}), SW_NOT_FOUND);
|
||||
}
|
||||
|
||||
// A refused write (e.g. NFCC in standby) is sent again.
|
||||
TEST(PN71xxTransceive, RefusedWriteIsRetried) {
|
||||
FakePN71xx nfcc;
|
||||
|
||||
Reference in New Issue
Block a user