Skip to content

Commit 5d995e9

Browse files
committed
Fixes isoDEP chain RX R-ACK block number and NFC-B ATTRIB PARAM2
1 parent 114f1d0 commit 5d995e9

5 files changed

Lines changed: 81 additions & 39 deletions

File tree

src/nfc/b/nfcb.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ enum class Command : uint8_t {
186186
///@name Timeout
187187
///@{
188188
constexpr uint32_t TIMEOUT_REQ_WUP_B{5};
189-
constexpr uint32_t TIMEOUT_ATTRIB{5};
189+
constexpr uint32_t TIMEOUT_ATTRIB{50};
190190
constexpr uint32_t TIMEOUT_HLTB{5};
191191
constexpr uint32_t TIMEOUT_DESELECT{5};
192192
///@}

src/nfc/isoDEP/file_system.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ bool FileSystem::readBinary(std::vector<uint8_t>& out, const uint16_t offset,
276276
// m5::utility::log::dump(cmd.data(), cmd.size(), false);
277277

278278
std::vector<uint8_t> rx;
279-
rx.resize(le + 2 + 16);
279+
rx.resize(le * 2 + 64); // generous margin for PICC chain overshoot
280280

281281
uint16_t rx_len = clamp_u16_size(rx.size());
282282
if (!_isoDEP.transceiveAPDU(rx.data(), rx_len, cmd.data(), static_cast<uint16_t>(cmd.size())) || rx_len < 2) {

src/nfc/isoDEP/isoDEP.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
169169
const uint32_t timeout_ms = _cfg.fwt_ms;
170170

171171
// Send I-Block and receive first frame
172-
// M5_LIB_LOGE("I-Block TX: %u bytes, timeout=%u", tpos, timeout_ms);
172+
M5_LIB_LOGV("isoDEP TX I-block[%u] timeout=%u max_rx=%u", tpos, timeout_ms, max_frame_size_rx);
173173
if (!_layer.transceive(rx_buf, rlen, tx_buf, tpos, timeout_ms)) {
174174
M5_LIB_LOGE("transceive failed, rlen=%u", rlen);
175175
if (rlen > 0) {
@@ -181,6 +181,7 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
181181
PRINT_ERROR(">>>>ERROR 1 %u %02X", rlen, rx_buf[0]);
182182
return false;
183183
}
184+
M5_LIB_LOGV("isoDEP RX[%u] PCB=%02X", rlen, rx_buf[0]);
184185

185186
// Parse loop: WTX can replace rx_buf/rlen and we continue parsing without re-sending I-Block.
186187
for (;;) {
@@ -206,7 +207,7 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
206207
return false;
207208
}
208209

209-
const uint8_t pcb = rx_buf[0];
210+
uint8_t pcb = rx_buf[0];
210211

211212
// --- S-Block (WTX) ---
212213
if (is_s_wtx(pcb)) {
@@ -314,15 +315,19 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
314315
// Response chaining: send R-ACK and receive next I-Block (WTX may appear)
315316
while (resp_more) {
316317
uint8_t r_ack[2]{};
317-
uint16_t rp = 0;
318-
r_ack[rp++] = make_r_ack(i_bn(pcb), _cfg.use_cid);
318+
uint16_t rp = 0;
319+
const uint8_t ack_bn = i_bn(pcb) ^ 0x01;
320+
r_ack[rp++] = make_r_ack(ack_bn, _cfg.use_cid);
319321
if (_cfg.use_cid) r_ack[rp++] = (uint8_t)(_cfg.cid & 0x0F);
320322

323+
M5_LIB_LOGV("isoDEP chain TX R-ACK bn=%u (collected=%u)", ack_bn, rx_written);
321324
uint16_t rlen2 = sizeof(rx_buf);
322325
if (!_layer.transceive(rx_buf, rlen2, r_ack, rp, _cfg.fwt_ms)) {
326+
M5_LIB_LOGE("isoDEP chain RX failed, rlen=%u", rlen2);
323327
PRINT_ERROR(">>>>ERROR 12");
324328
return false;
325329
}
330+
M5_LIB_LOGV("isoDEP chain RX[%u] PCB=%02X", rlen2, rx_buf[0]);
326331

327332
for (;;) {
328333
if (_cfg.rx_crc && rlen2 >= 3) rlen2 -= 2;
@@ -381,8 +386,8 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
381386

382387
const uint16_t inf_len2 = (uint16_t)(rlen2 - idx2);
383388
if (rx_written + inf_len2 > rx_inf_len_org) {
384-
PRINT_ERROR("rx_written %u inf_len2 %u rx_inf_len %u", rx_written, inf_len2,
385-
rx_inf_len);
389+
PRINT_ERROR("rx_written %u inf_len2 %u rx_inf_len_org %u", rx_written, inf_len2,
390+
rx_inf_len_org);
386391
// m5::utility::log::dump(rx_inf, rx_written, false);
387392

388393
return false;
@@ -391,6 +396,7 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
391396
memcpy(rx_inf + rx_written, rx_buf + idx2, inf_len2);
392397
rx_written = (uint16_t)(rx_written + inf_len2);
393398

399+
pcb = pcb2;
394400
resp_more = i_has_more(pcb2);
395401
break;
396402
}

src/nfc/layer/b/nfc_layer_b.cpp

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,20 @@ constexpr uint8_t required_slots(const Require slots)
4343
{
4444
return 1U << m5::stl::to_underlying(slots);
4545
}
46+
47+
// ISO/IEC 14443-4 FSDI table (Frame Size for Device Index, bit[7:4] of ATTRIB PARAM2)
48+
constexpr uint8_t fsdi_for_size(const uint16_t bytes)
49+
{
50+
return (bytes >= 256) ? 8
51+
: (bytes >= 128) ? 7
52+
: (bytes >= 96) ? 6
53+
: (bytes >= 64) ? 5
54+
: (bytes >= 48) ? 4
55+
: (bytes >= 40) ? 3
56+
: (bytes >= 32) ? 2
57+
: (bytes >= 24) ? 1
58+
: 0;
59+
}
4660
} // namespace
4761

4862
namespace m5 {
@@ -69,26 +83,27 @@ bool NFCLayerB::receive(uint8_t* rx, uint16_t& rx_len, const uint32_t timeout_ms
6983
return _impl->receive(rx, rx_len, timeout_ms);
7084
}
7185

72-
bool NFCLayerB::detect(m5::nfc::b::PICC& picc, const uint8_t afi, const uint32_t timeout_ms)
86+
bool NFCLayerB::detect(m5::nfc::b::PICC& picc, const uint8_t afi, const uint32_t timeout_ms,
87+
const uint32_t req_timeout_ms)
7388
{
7489
std::vector<PICC> piccs{};
75-
if (detect(piccs, afi, 1, timeout_ms)) {
90+
if (detect(piccs, afi, 1, timeout_ms, req_timeout_ms)) {
7691
picc = piccs.front();
7792
return true;
7893
}
7994
return false;
8095
}
8196

8297
bool NFCLayerB::detect(std::vector<m5::nfc::b::PICC>& piccs, const uint8_t afi, const uint8_t max_piccs,
83-
const uint32_t timeout_ms)
98+
const uint32_t timeout_ms, const uint32_t req_timeout_ms)
8499
{
85100
piccs.clear();
86101

87102
auto timeout_at = m5::utility::millis() + timeout_ms;
88103
do {
89104
uint8_t rx[ATQB_LENGTH]{};
90105
uint16_t rx_len = sizeof(rx);
91-
if (!request(rx, rx_len, afi, Require::Slot1)) {
106+
if (!request(rx, rx_len, afi, Require::Slot1, req_timeout_ms)) {
92107
continue;
93108
}
94109

@@ -110,44 +125,58 @@ bool NFCLayerB::detect(std::vector<m5::nfc::b::PICC>& piccs, const uint8_t afi,
110125
return !piccs.empty();
111126
}
112127

113-
bool NFCLayerB::select(m5::nfc::b::PICC& picc)
128+
bool NFCLayerB::select(m5::nfc::b::PICC& picc, const uint32_t timeout_ms)
114129
{
115130
// Wakeup for READY
116131
uint16_t len = ATQB_LENGTH;
117132
if (!wakeup(picc.atqb, len)) {
118133
return false;
119134
}
120135

136+
M5_LIB_LOGI("ATQB protocol: %02X %02X %02X (FSCI=%u, ISO14443-4=%d, FWI=%u)", picc.protocol[0], picc.protocol[1],
137+
picc.protocol[2], (picc.protocol[1] >> 4) & 0x0F, (picc.protocol[1] & 0x01),
138+
(picc.protocol[2] >> 4) & 0x0F);
139+
140+
// ATTRIB PARAM2 (per ISO/IEC 14443-3 §7.10.2 with b1 = LSB):
141+
// bit[3:0] = FSDI (PCD's max receive frame size index)
142+
// bit[5:4] = PCD->PICC bit rate divisor
143+
// bit[7:6] = PICC->PCD bit rate divisor
144+
// Take min(PCD FIFO, PICC FSCI) so the PICC chains I-blocks within what we can receive.
145+
const uint16_t pcd_rx_cap = maximum_fifo_depth() - 2 /*CRC*/;
146+
const uint8_t pcd_fsdi = fsdi_for_size(pcd_rx_cap);
147+
const uint8_t picc_fsci = picc.maximumFrameLengthBits();
148+
const uint8_t fsdi = std::min<uint8_t>(pcd_fsdi, picc_fsci);
149+
121150
// ATTRIB
122151
uint8_t cmd[1 + 4 + 1 + 1 + 1 + 1] = {m5::stl::to_underlying(Command::ATTRIB)}; // without option
123152
memcpy(cmd + 1, picc.pupi, 4);
124-
cmd[5] = 0x00; // PARAM1
125-
cmd[6] = picc.maximumFrameLengthBits(); // PARAM2 | com speed
126-
cmd[7] = picc.protocol[1] & 0x0F; // PARAM 3protocol type
127-
cmd[8] = 0x00; // PARAM 4
153+
cmd[5] = 0x00; // PARAM1
154+
cmd[6] = (uint8_t)(fsdi & 0x0F); // PARAM2: FSDI(PCD cap) bit[3:0] | rates=0 (106k both)
155+
cmd[7] = picc.protocol[1] & 0x0F; // PARAM3 protocol type
156+
cmd[8] = 0x00; // PARAM 4
128157

129158
std::vector<uint8_t> frame;
130159
frame.assign(cmd, cmd + sizeof(cmd));
131160

132161
uint8_t rx[128]{};
133162
uint16_t rx_len = sizeof(rx);
134-
if (!transceive(rx, rx_len, frame.data(), frame.size(), TIMEOUT_ATTRIB) || !rx_len) {
135-
M5_LIB_LOGE("Failed to select");
163+
if (!transceive(rx, rx_len, frame.data(), frame.size(), timeout_ms) || !rx_len) {
164+
M5_LIB_LOGE("Failed to select: rx_len=%u", rx_len);
136165
return false;
137166
}
138167

139168
_activePICC = picc;
140169
return true;
141170
}
142171

143-
bool NFCLayerB::hlt(const uint8_t pupi[4])
172+
bool NFCLayerB::hlt(const uint8_t pupi[4], const uint32_t timeout_ms)
144173
{
145174
if (pupi) {
146175
uint8_t cmd[1 + 4] = {m5::stl::to_underlying(Command::HLTB)};
147176
memcpy(cmd + 1, pupi, 4);
148-
uint8_t rx[1]{};
177+
uint8_t rx[1 + 2]{}; // 1 byte payload + 2 byte CRC_B
149178
uint16_t rx_len = sizeof(rx);
150-
if (!transceive(rx, rx_len, cmd, sizeof(cmd), TIMEOUT_HLTB) || rx_len < 1) {
179+
if (!transceive(rx, rx_len, cmd, sizeof(cmd), timeout_ms) || rx_len < 1) {
151180
M5_LIB_LOGE("Failed to hlt %02X%02X%02X%02X", cmd[1], cmd[2], cmd[3], cmd[4]);
152181
return false;
153182
}
@@ -156,17 +185,18 @@ bool NFCLayerB::hlt(const uint8_t pupi[4])
156185
return false;
157186
}
158187

159-
bool NFCLayerB::deselect(const uint8_t pupi[4], const uint8_t cid)
188+
bool NFCLayerB::deselect(const uint8_t pupi[4], const uint8_t cid, const uint32_t timeout_ms)
160189
{
161190
uint8_t cmd[2] = {m5::stl::to_underlying(cid != 0xFF ? Command::DESELECT_WITH_CID : Command::DESELECT)};
162191
uint16_t cmd_len = 1 + (cid != 0xFF);
163192
if (cid != 0xFF) {
164193
cmd[1] = cid;
165194
}
166-
uint8_t rx[2]{};
167-
uint16_t rx_len = cmd_len;
195+
uint8_t rx[2 + 2]{}; // payload (1 or 2) + 2 byte CRC_B
196+
uint16_t rx_len =
197+
cmd_len + 2; // Match actual response size to keep wait_for_FIFO fallback equivalent to the old behavior
168198

169-
if (!transceive(rx, rx_len, cmd, cmd_len, TIMEOUT_DESELECT) || rx_len < cmd_len) {
199+
if (!transceive(rx, rx_len, cmd, cmd_len, timeout_ms) || rx_len < cmd_len) {
170200
M5_LIB_LOGE("Failed to deselecte %02X:%02X", cmd[0], cmd[1]);
171201
return false;
172202
}
@@ -188,7 +218,7 @@ bool NFCLayerB::deactivate()
188218

189219
//
190220
bool NFCLayerB::request_wakeup(uint8_t* atqb, uint16_t& atqb_len, const uint8_t afi, const Require slots,
191-
const bool wakeup)
221+
const bool wakeup, const uint32_t timeout_ms)
192222
{
193223
if (!atqb || atqb_len < ATQB_LENGTH) {
194224
return false;
@@ -204,7 +234,7 @@ bool NFCLayerB::request_wakeup(uint8_t* atqb, uint16_t& atqb_len, const uint8_t
204234
atqb_len = 0;
205235

206236
// Ignore non-responsive slots and proceed to the next one.
207-
if (transceive(rx, rx_len, cmd, sizeof(cmd), TIMEOUT_REQ_WUP_B) && rx_len == sizeof(rx) && rx[0] == 0x50) {
237+
if (transceive(rx, rx_len, cmd, sizeof(cmd), timeout_ms) && rx_len == sizeof(rx) && rx[0] == 0x50) {
208238
// Occur collision if CRC error
209239
const uint16_t crc = crc16.range(rx, ATQB_LENGTH + 1);
210240
if (crc == ((uint16_t)rx[13] << 8 | rx[12])) {
@@ -219,7 +249,7 @@ bool NFCLayerB::request_wakeup(uint8_t* atqb, uint16_t& atqb_len, const uint8_t
219249
rx_len = sizeof(rx);
220250
slot_marker[0] = ((uint8_t)i << 4) | 0x05;
221251
// Ignore non-responsive slots and proceed to the next one.
222-
if (!transceive(rx, rx_len, slot_marker, sizeof(slot_marker), TIMEOUT_REQ_WUP_B) || rx[0] != 0x50 ||
252+
if (!transceive(rx, rx_len, slot_marker, sizeof(slot_marker), timeout_ms) || rx[0] != 0x50 ||
223253
rx_len < sizeof(rx)) {
224254
continue;
225255
}

src/nfc/layer/b/nfc_layer_b.hpp

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ namespace m5 {
2828
namespace unit {
2929
class UnitST25R3916;
3030
class CapST25R3916;
31+
class UnitWS1850S;
3132
} // namespace unit
3233
namespace nfc {
3334

@@ -40,6 +41,7 @@ class NFCLayerB : public NFCLayerInterface {
4041
struct Adapter;
4142
explicit NFCLayerB(m5::unit::UnitST25R3916& u);
4243
explicit NFCLayerB(m5::unit::CapST25R3916& u);
44+
explicit NFCLayerB(m5::unit::UnitWS1850S& u);
4345

4446
virtual bool transceive(uint8_t* rx, uint16_t& rx_len, const uint8_t* tx, const uint16_t tx_len,
4547
const uint32_t timeout_ms) override;
@@ -83,9 +85,10 @@ class NFCLayerB : public NFCLayerInterface {
8385
@post PICC transitions: IDLE -> READY on successful response
8486
*/
8587
inline bool request(uint8_t* atqb, uint16_t& atqb_len, const uint8_t afi = 0x00,
86-
const m5::nfc::b::Require slots = m5::nfc::b::Require::Slot1)
88+
const m5::nfc::b::Require slots = m5::nfc::b::Require::Slot1,
89+
const uint32_t timeout_ms = m5::nfc::b::TIMEOUT_REQ_WUP_B)
8790
{
88-
return request_wakeup(atqb, atqb_len, afi, slots, false);
91+
return request_wakeup(atqb, atqb_len, afi, slots, false, timeout_ms);
8992
}
9093
/*!
9194
@brief Send WUPB to wake a PICC from IDLE or HALT
@@ -98,9 +101,10 @@ class NFCLayerB : public NFCLayerInterface {
98101
@post PICC transitions: IDLE/HALT -> READY on successful response
99102
*/
100103
inline bool wakeup(uint8_t* atqb, uint16_t& atqb_len, const uint8_t afi = 0x00,
101-
const m5::nfc::b::Require slots = m5::nfc::b::Require::Slot1)
104+
const m5::nfc::b::Require slots = m5::nfc::b::Require::Slot1,
105+
const uint32_t timeout_ms = m5::nfc::b::TIMEOUT_REQ_WUP_B)
102106
{
103-
return request_wakeup(atqb, atqb_len, afi, slots, true);
107+
return request_wakeup(atqb, atqb_len, afi, slots, true, timeout_ms);
104108
}
105109

106110
/*!
@@ -111,7 +115,8 @@ class NFCLayerB : public NFCLayerInterface {
111115
@return True if detected
112116
@note The detected PICC is typically put into HALT during enumeration to allow discovering others
113117
*/
114-
bool detect(m5::nfc::b::PICC& picc, const uint8_t afi = 0x00, const uint32_t timeout_ms = 100U);
118+
bool detect(m5::nfc::b::PICC& picc, const uint8_t afi = 0x00, const uint32_t timeout_ms = 50U,
119+
const uint32_t req_timeout_ms = m5::nfc::b::TIMEOUT_REQ_WUP_B);
115120
/*!
116121
@brief Detect idle PICCs
117122
@param[out] piccs Detected PICC PICCs (one per activated PICC candidate)
@@ -122,11 +127,11 @@ class NFCLayerB : public NFCLayerInterface {
122127
@note The detected PICC is typically put into HALT during enumeration to allow discovering others
123128
*/
124129
bool detect(std::vector<m5::nfc::b::PICC>& piccs, const uint8_t afi = 0x00, const uint8_t max_piccs = 4,
125-
const uint32_t timeout_ms = 1000U);
130+
const uint32_t timeout_ms = 1000U, const uint32_t req_timeout_ms = m5::nfc::b::TIMEOUT_REQ_WUP_B);
126131

127132
/*!
128133
*/
129-
bool select(m5::nfc::b::PICC& picc);
134+
bool select(m5::nfc::b::PICC& picc, const uint32_t timeout_ms = m5::nfc::b::TIMEOUT_ATTRIB);
130135
#if 0
131136
bool activate(m5::nfc::b::PICC& picc);
132137
bool reactivate(const m5::nfc::b::PICC& picc);
@@ -140,14 +145,15 @@ class NFCLayerB : public NFCLayerInterface {
140145

141146
///@name For activated PICC
142147
///@{
143-
bool hlt(const uint8_t pupi[4]);
144-
bool deselect(const uint8_t pupi[4], const uint8_t cid = 0xFF);
148+
bool hlt(const uint8_t pupi[4], const uint32_t timeout_ms = m5::nfc::b::TIMEOUT_HLTB);
149+
bool deselect(const uint8_t pupi[4], const uint8_t cid = 0xFF,
150+
const uint32_t timeout_ms = m5::nfc::b::TIMEOUT_DESELECT);
145151
bool deactivate();
146152
///@}
147153

148154
protected:
149155
bool request_wakeup(uint8_t* atqb, uint16_t& atqb_len, const uint8_t afi, const m5::nfc::b::Require slots,
150-
const bool wakeup);
156+
const bool wakeup, const uint32_t timeout_ms = m5::nfc::b::TIMEOUT_REQ_WUP_B);
151157

152158
virtual bool read(uint8_t* rx, uint16_t& rx_len, const uint16_t saddr) override
153159
{

0 commit comments

Comments
 (0)