M5Unit-NFC 0.0.3 git rev:59f5362
Loading...
Searching...
No Matches
isoDEP.hpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
3 *
4 * SPDX-License-Identifier: MIT
5 */
10#ifndef M5_UNIT_UNIFIED_NFC_NFC_ISODEP_ISODEP_HPP
11#define M5_UNIT_UNIFIED_NFC_NFC_ISODEP_ISODEP_HPP
12#include <cstdint>
13#include <vector>
14
15namespace m5 {
16namespace nfc {
17class NFCLayerInterface;
22namespace isodep {
23
25uint32_t fwi_to_ms(const uint8_t fwi, const float fc);
26
27constexpr uint16_t MAX_FRAME_SIZE{256};
28
29namespace detail {
30
31inline bool is_i_block(uint8_t pcb)
32{
33 return (pcb & 0xC0) == 0x00;
34}
35
36inline bool is_r_block(uint8_t pcb)
37{
38 return (pcb & 0xC0) == 0x80;
39}
40
41inline bool is_s_block(uint8_t pcb)
42{
43 return (pcb & 0xC0) == 0xC0;
44}
45
46inline bool i_has_more(uint8_t pcb)
47{
48 return (pcb & 0x10) != 0;
49}
50
51inline uint8_t i_bn(uint8_t pcb)
52{
53 return (pcb >> 0) & 0x01;
54}
55
56inline bool is_s_wtx(uint8_t pcb)
57{
58 return (pcb & 0xC0) == 0xC0 && (pcb & 0x30) == 0x30; // S-Block & WTX
59}
60
61inline bool is_valid_rblock(uint8_t pcb)
62{
63 // R-Block MUST bits: b6=1, b3=0, b2=1 (mask 0x26, val 0x22), type=0x80
64 return ((pcb & 0xC0) == 0x80) && ((pcb & 0x26) == 0x22);
65}
66
67inline bool r_is_nak(uint8_t pcb)
68{
69 return (pcb & 0x10) != 0; // bit4 distinguishes ACK/NAK (0x10)
70}
71
72inline bool r_is_ack(uint8_t pcb)
73{
74 return !r_is_nak(pcb);
75}
76
77inline uint8_t get_wtxm(uint8_t inf)
78{
79 return inf & 0x3F;
80}
81
82inline bool is_valid_wtxm(uint8_t wtxm)
83{
84 return (wtxm >= 1) && (wtxm <= 59);
85}
86
87inline uint32_t mul_clamp_u32(uint32_t a, uint32_t b, uint32_t maxv)
88{
89 if (!a || !b) {
90 return 0;
91 }
92 if (a > maxv / b) {
93 return maxv;
94 }
95 uint32_t v = a * b;
96 return (v > maxv) ? maxv : v;
97}
98
99// I-Block PCB
100inline uint8_t make_i_pcb(uint8_t bn, bool more, bool has_cid, bool has_nad)
101{
102 uint8_t pcb = 0x02; // I-Block base (0x00/0x02?)
103 pcb &= ~0x01;
104 pcb |= (bn & 0x01);
105 pcb |= more ? 0x10 : 0x00;
106 pcb |= has_cid ? 0x08 : 0x00;
107 pcb |= has_nad ? 0x04 : 0x00;
108 return pcb;
109}
110
111// R-Block ACK
112inline uint8_t make_r_ack(uint8_t bn, bool has_cid)
113{
114 uint8_t pcb = 0xA2; // 0xA0 or 0xA2?
115 pcb &= ~0x01;
116 pcb |= (bn & 0x01);
117 pcb |= has_cid ? 0x08 : 0x00;
118 return pcb;
119}
120
121// S-Block WTX-ACK
122inline uint8_t make_s_wtx_ack(bool has_cid)
123{
124 uint8_t pcb = 0xF2; // S(WTX)
125 pcb |= has_cid ? 0x08 : 0x00;
126 return pcb;
127}
128
129} // namespace detail
130
132inline uint16_t fsci_to_fsc(const uint8_t fsci)
133{
134 static constexpr uint16_t table[] = {16, 24, 32, 40, 48, 64, 96, 128, 256};
135 return (fsci < (sizeof(table) / sizeof(table[0]))) ? table[fsci] : 0;
136}
137
142struct config_t {
143 uint16_t fsc{};
144 uint16_t pcd_max_frame_tx{};
145 uint16_t pcd_max_frame_rx{};
146 uint32_t fwt_ms{100};
147 uint32_t wtx_max_ms{5000};
148
149 // options
150 bool use_cid{};
151 uint8_t cid{};
152 bool use_nad{};
153 uint8_t nad{};
154
155 uint8_t max_retries{2};
156 bool rx_crc{true}; // Remove CRC if true in INF
157
158 inline uint16_t max_frame_cap_tx() const
159 {
160 const auto max_frame = std::min<uint16_t>(pcd_max_frame_tx, fsc);
161 return (max_frame > (overhead() + 2)) ? (max_frame - overhead() - 2) : 0;
162 }
163 inline uint16_t max_frame_size_rx() const
164 {
165 return std::min<uint16_t>(pcd_max_frame_rx, fsc);
166 }
167 inline uint16_t fsc_inf_cap() const
168 {
169 return (fsc > overhead()) ? static_cast<uint16_t>(fsc - overhead()) : 0;
170 }
171 inline uint16_t overhead() const
172 {
173 return 1 + (use_cid ? 1 : 0) + (use_nad ? 1 : 0);
174 }
175};
176
181struct RxInfo {
182 bool more{}; // Continue chaining?
183 bool wtx_seen{}; // WTX?
184};
185
190class IsoDEP {
191public:
192 explicit IsoDEP(NFCLayerInterface& layer) : _layer{layer}
193 {
194 }
195 IsoDEP(NFCLayerInterface& layer, const config_t& c) : _layer{layer}, _cfg{c}
196 {
197 }
198
199 inline config_t config() const
200 {
201 return _cfg;
202 }
203 inline void config(const config_t& cfg)
204 {
205 _cfg = cfg;
206 _block_num = 0;
207 }
208
210 bool transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t* tx_inf, const uint16_t tx_inf_len,
211 RxInfo* info = nullptr);
213 bool transceiveAPDU(uint8_t* rx, uint16_t& rx_len, const uint8_t* cmd, const uint16_t cmd_len);
215 bool transceive(uint8_t* rx, uint16_t& rx_len, const uint8_t* tx, const uint16_t tx_len, const uint32_t timeout_ms);
216
217private:
218 NFCLayerInterface& _layer;
219 config_t _cfg{};
220 uint8_t _block_num{}; // I-Block BN (0/1)
221};
222
223} // namespace isodep
224} // namespace nfc
225} // namespace m5
226#endif
Common interface for NFC layer.
Definition nfc_layer.hpp:26
ISO Data Exchange Protocol.
Definition isoDEP.hpp:190
bool transceive(uint8_t *rx, uint16_t &rx_len, const uint8_t *tx, const uint16_t tx_len, const uint32_t timeout_ms)
Transceive normal.
Definition isoDEP.cpp:522
bool transceiveINF(uint8_t *rx_inf, uint16_t &rx_inf_len, const uint8_t *tx_inf, const uint16_t tx_inf_len, RxInfo *info=nullptr)
Transceive INF.
Definition isoDEP.cpp:111
bool transceiveAPDU(uint8_t *rx, uint16_t &rx_len, const uint8_t *cmd, const uint16_t cmd_len)
Transceive APDU.
Definition isoDEP.cpp:430
uint32_t fwi_to_ms(const uint8_t fwi, const float fc)
Calculate waiting time(ms) by fwi and fc.
Definition isoDEP.cpp:92
uint16_t fsci_to_fsc(const uint8_t fsci)
Convert FSCI to FSC (ISO/IEC 14443-4)
Definition isoDEP.hpp:132
NFC-A definitions.
NFC-B definitions.
For ISO-DEP.
Top level namespace of M5stack.
NFC related definitions.
NFC-V definitions.
RX information.
Definition isoDEP.hpp:181
ISO-DEP configuration.
Definition isoDEP.hpp:142