Skip to content

Commit 9b0c7e2

Browse files
authored
Merge pull request #3 from Sped0n/fix-isodep-chaining
fix: use correct ISO-DEP block number after response chaining
2 parents 9fe45b7 + e67f33a commit 9b0c7e2

2 files changed

Lines changed: 127 additions & 6 deletions

File tree

src/nfc/isoDEP/isoDEP.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
140140
uint8_t rx_buf[MAX_FRAME_SIZE]{};
141141
uint16_t tx_off{};
142142
uint16_t rx_written{};
143+
bool received_i_block{};
144+
uint8_t last_picc_i_bn{};
143145

144146
// Transmit chaining
145147
while (tx_off < tx_inf_len) {
@@ -309,8 +311,10 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
309311
memcpy(rx_inf + rx_written, rx_buf + idx, inf_len);
310312
rx_written = (uint16_t)(rx_written + inf_len);
311313

312-
bool resp_more = i_has_more(pcb);
313-
info->more = resp_more;
314+
bool resp_more = i_has_more(pcb);
315+
info->more = resp_more;
316+
received_i_block = true;
317+
last_picc_i_bn = i_bn(pcb);
314318

315319
// Response chaining: send R-ACK and receive next I-Block (WTX may appear)
316320
while (resp_more) {
@@ -396,8 +400,9 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
396400
memcpy(rx_inf + rx_written, rx_buf + idx2, inf_len2);
397401
rx_written = (uint16_t)(rx_written + inf_len2);
398402

399-
pcb = pcb2;
400-
resp_more = i_has_more(pcb2);
403+
pcb = pcb2;
404+
resp_more = i_has_more(pcb2);
405+
last_picc_i_bn = i_bn(pcb2);
401406
break;
402407
}
403408
}
@@ -416,8 +421,8 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
416421
}
417422

418423
// next chunk
419-
tx_off = (uint16_t)(tx_off + chunk);
420-
_block_num ^= 1;
424+
tx_off = (uint16_t)(tx_off + chunk);
425+
_block_num = received_i_block ? (last_picc_i_bn ^ 0x01) : (_block_num ^ 0x01);
421426
}
422427

423428
rx_inf_len = rx_written;

test/isodep_test.cpp

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,98 @@
99
#include <gtest/gtest.h>
1010
#include <M5Unified.h>
1111
#include "nfc/isoDEP/isoDEP.hpp"
12+
#include "nfc/layer/nfc_layer.hpp"
13+
14+
#include <array>
15+
#include <cstdint>
16+
#include <cstring>
17+
#include <utility>
18+
#include <vector>
1219

1320
using namespace m5::nfc::isodep;
1421
using namespace m5::nfc::isodep::detail;
1522

23+
namespace {
24+
25+
class ScriptedLayer : public m5::nfc::NFCLayerInterface {
26+
public:
27+
explicit ScriptedLayer(std::vector<std::vector<uint8_t>> responses) : _responses{std::move(responses)}
28+
{
29+
}
30+
31+
uint16_t maximum_fifo_depth() const override
32+
{
33+
return MAX_FRAME_SIZE;
34+
}
35+
36+
bool transceive(uint8_t* rx, uint16_t& rx_len, const uint8_t* tx, const uint16_t tx_len, const uint32_t) override
37+
{
38+
_requests.emplace_back(tx, tx + tx_len);
39+
if (_response_index >= _responses.size() || rx_len < _responses[_response_index].size()) {
40+
return false;
41+
}
42+
const auto& response = _responses[_response_index++];
43+
memcpy(rx, response.data(), response.size());
44+
rx_len = static_cast<uint16_t>(response.size());
45+
return true;
46+
}
47+
48+
bool receive(uint8_t*, uint16_t&, const uint32_t) override
49+
{
50+
return false;
51+
}
52+
bool read(uint8_t*, uint16_t&, const uint16_t) override
53+
{
54+
return false;
55+
}
56+
bool write(const uint16_t, const uint8_t*, const uint16_t) override
57+
{
58+
return false;
59+
}
60+
uint16_t first_user_block() const override
61+
{
62+
return 0;
63+
}
64+
uint16_t last_user_block() const override
65+
{
66+
return 0;
67+
}
68+
uint16_t user_area_size() const override
69+
{
70+
return 0;
71+
}
72+
uint16_t unit_size_read() const override
73+
{
74+
return 0;
75+
}
76+
uint16_t unit_size_write() const override
77+
{
78+
return 0;
79+
}
80+
81+
const std::vector<std::vector<uint8_t>>& requests() const
82+
{
83+
return _requests;
84+
}
85+
86+
private:
87+
std::vector<std::vector<uint8_t>> _responses;
88+
std::vector<std::vector<uint8_t>> _requests;
89+
size_t _response_index{};
90+
};
91+
92+
config_t test_config()
93+
{
94+
config_t cfg{};
95+
cfg.fsc = 64;
96+
cfg.pcd_max_frame_tx = 64;
97+
cfg.pcd_max_frame_rx = 64;
98+
cfg.rx_crc = false;
99+
return cfg;
100+
}
101+
102+
} // namespace
103+
16104
TEST(IsoDEP, FsciToFsc)
17105
{
18106
EXPECT_EQ(fsci_to_fsc(0), 16u);
@@ -104,3 +192,31 @@ TEST(IsoDEP, FwiToMs)
104192
EXPECT_GT(fwi_to_ms(0, 13.56e6f), 0u);
105193
EXPECT_GT(fwi_to_ms(5, 13.56e6f), 0u);
106194
}
195+
196+
TEST(IsoDEP, ResponseChainingUsesNextExpectedBlockNumberAndSyncsSession)
197+
{
198+
ScriptedLayer layer({{make_i_pcb(0, true, false, false), 0xAA},
199+
{make_i_pcb(1, false, false, false), 0xBB},
200+
{make_i_pcb(1, false, false, false), 0xCC}});
201+
IsoDEP iso_dep{layer, test_config()};
202+
203+
std::array<uint8_t, 8> rx{};
204+
uint16_t rx_len = rx.size();
205+
const std::array<uint8_t, 1> first_tx{0xCA};
206+
207+
ASSERT_TRUE(iso_dep.transceiveINF(rx.data(), rx_len, first_tx.data(), first_tx.size()));
208+
ASSERT_EQ(rx_len, 2u);
209+
EXPECT_EQ(rx[0], 0xAA);
210+
EXPECT_EQ(rx[1], 0xBB);
211+
212+
ASSERT_EQ(layer.requests().size(), 2u);
213+
EXPECT_EQ(layer.requests()[0][0], make_i_pcb(0, false, false, false));
214+
EXPECT_EQ(layer.requests()[1][0], make_r_ack(1, false));
215+
216+
rx_len = rx.size();
217+
const std::array<uint8_t, 1> second_tx{0xCB};
218+
ASSERT_TRUE(iso_dep.transceiveINF(rx.data(), rx_len, second_tx.data(), second_tx.size()));
219+
220+
ASSERT_EQ(layer.requests().size(), 3u);
221+
EXPECT_EQ(layer.requests()[2][0], make_i_pcb(0, false, false, false));
222+
}

0 commit comments

Comments
 (0)