58[[nodiscard]]
inline constexpr bool IsIBlock(std::uint8_t pcb)
noexcept {
59 return (pcb & 0x80U) == 0U;
61[[nodiscard]]
inline constexpr bool IsRBlock(std::uint8_t pcb)
noexcept {
62 return (pcb & 0xC0U) == 0x80U;
64[[nodiscard]]
inline constexpr bool IsSBlock(std::uint8_t pcb)
noexcept {
65 return (pcb & 0xC0U) == 0xC0U;
67[[nodiscard]]
inline constexpr bool IBlockMore(std::uint8_t pcb)
noexcept {
68 return (pcb & 0x20U) != 0U;
70[[nodiscard]]
inline constexpr std::uint8_t
IBlockPcb(std::uint8_t ns_bit,
bool more)
noexcept {
71 return static_cast<std::uint8_t
>(((ns_bit & 1U) << 6) | (more ? 0x20U : 0U));
73[[nodiscard]]
inline constexpr std::uint8_t
RBlockAckPcb(std::uint8_t nr_bit)
noexcept {
74 return static_cast<std::uint8_t
>(0x80U | ((nr_bit & 1U) << 4));
76[[nodiscard]]
inline constexpr std::uint8_t
IBlockSeqFromPcb(std::uint8_t pcb)
noexcept {
77 return static_cast<std::uint8_t
>((pcb >> 6) & 1U);
80[[nodiscard]]
inline constexpr std::uint8_t
RBlockNrFromPcb(std::uint8_t pcb)
noexcept {
81 return static_cast<std::uint8_t
>((pcb >> 4) & 1U);
88[[nodiscard]]
inline constexpr std::uint8_t
RBlockNackPcb(std::uint8_t nr_bit, std::uint8_t err)
noexcept {
89 return static_cast<std::uint8_t
>(0x80U | ((nr_bit & 1U) << 4) | (err & 0x0FU));
100template <
typename TransportT>
103 explicit T1Session(TransportT& transport) noexcept : transport_(transport) {}
108 [[nodiscard]] std::uint32_t
InterFrameDelayMs() const noexcept {
return inter_frame_delay_ms_; }
111 void SetReadRetries(std::uint8_t retries)
noexcept { max_read_retries_ = retries; }
113 [[nodiscard]] std::uint8_t
ReadRetries() const noexcept {
return max_read_retries_; }
118 [[nodiscard]] std::uint32_t
ReadRetryDelayMs() const noexcept {
return read_retry_delay_ms_; }
121 void SetMaxWtxRequests(std::uint8_t max_wtx_requests)
noexcept { max_wtx_requests_ = max_wtx_requests; }
123 [[nodiscard]] std::uint8_t
MaxWtxRequests() const noexcept {
return max_wtx_requests_; }
133 [[nodiscard]] std::size_t
Ifsc() const noexcept {
return ifsc_; }
144 const TransportT&
Transport() const noexcept {
return transport_; }
156 std::size_t raw_len = 0;
157 const Error re = RecvT1Block(raw,
sizeof(raw), &raw_len, timeout_ms);
169 std::uint32_t timeout_ms)
noexcept {
170 if (len_out ==
nullptr) {
180 std::size_t raw_len = 0;
181 const Error re = RecvT1Block(raw,
sizeof(raw), &raw_len, timeout_ms);
191 const std::uint8_t ln = raw[2];
192 if (3U +
static_cast<std::size_t
>(ln) + 2U != raw_len) {
195 if (
static_cast<std::size_t
>(ln) > cap) {
199 std::memcpy(buf, raw + 3U, ln);
221 if (!transport_.EnsureInitialized()) {
225 std::size_t raw_len = 0;
227 (void)RecvT1Block(raw,
sizeof(raw), &raw_len, timeout_ms);
233 std::uint8_t wtx_count = 0;
234 for (
int guard = 0; guard < 16; ++guard) {
235 const Error re = RecvT1Block(raw,
sizeof(raw), &raw_len, timeout_ms);
249 const Error w = HandleSBlock(raw, raw_len, timeout_ms, &wtx_count);
273 std::size_t rsp_cap, std::size_t* rsp_len,
274 std::uint32_t timeout_ms)
noexcept {
275 if (rsp_len ==
nullptr) {
279 if (!transport_.EnsureInitialized()) {
283 std::uint8_t retransmissions = 0;
285 while (off < cmd_len) {
286 const std::size_t chunk = std::min(cmd_len - off, ifsc_);
287 const bool more_chain = (off + chunk) < cmd_len;
288 const std::uint8_t pcb =
t1::IBlockPcb(host_ns_, more_chain);
289 const Error w = SendRawBlock(pcb, cmd + off,
static_cast<std::uint8_t
>(chunk), timeout_ms);
296 const Error rr = ExpectChainAck(timeout_ms, &retransmissions);
303 std::size_t total_rx = 0;
304 std::uint8_t wtx_count = 0;
305 for (
int guard = 0; guard < 64; ++guard) {
307 std::size_t raw_len = 0;
308 const Error rb = RecvT1Block(raw,
sizeof(raw), &raw_len, timeout_ms);
311 if (++retransmissions > max_retransmissions_) {
331 const Error sb = HandleSBlock(raw, raw_len, timeout_ms, &wtx_count);
340 last_iblock_len_ == 0U) {
343 if (++retransmissions > max_retransmissions_) {
346 const Error rt = RetransmitLastIBlock(timeout_ms);
353 if (seq != card_ns_) {
356 const std::uint8_t ln = raw[2];
357 if (3U +
static_cast<std::size_t
>(ln) + 2U != raw_len) {
360 if (total_rx +
static_cast<std::size_t
>(ln) > rsp_cap) {
364 std::memcpy(rsp + total_rx, raw + 3U, ln);
387 [[nodiscard]]
Error SendSBlock(std::uint8_t pcb, std::uint32_t timeout_ms)
noexcept {
388 if (!transport_.EnsureInitialized()) {
391 return SendRawBlock(pcb,
nullptr, 0, timeout_ms);
394 [[nodiscard]]
Error SendRawBlock(std::uint8_t pcb,
const std::uint8_t* inf, std::uint8_t inf_len,
395 std::uint32_t timeout_ms)
noexcept {
403 if (inf_len > 0 && inf !=
nullptr) {
404 std::memcpy(frame + 3U, inf, inf_len);
408 std::memcpy(last_iblock_, frame, 3U + inf_len + 2U);
409 last_iblock_len_ = 3U + inf_len + 2U;
411 const Error e = transport_.Write(frame, 3U + inf_len + 2U, timeout_ms);
415 transport_.delay_ms(inter_frame_delay_ms_);
419 [[nodiscard]]
Error SendRBlockAck(std::uint8_t pcb, std::uint32_t timeout_ms)
noexcept {
420 return SendRawBlock(pcb,
nullptr, 0, timeout_ms);
423 [[nodiscard]]
Error RecvT1Block(std::uint8_t* out, std::size_t cap, std::size_t* total_len,
424 std::uint32_t timeout_ms)
noexcept {
428 Error e = ReadWithRetry(out, 3U, timeout_ms);
432 const std::uint8_t ln = out[2];
433 if (3U +
static_cast<std::size_t
>(ln) + 2U > cap) {
437 e = ReadWithRetry(out + 3U, ln + 2U, timeout_ms);
442 const std::size_t tot = 3U + ln + 2U;
450 [[nodiscard]]
Error RetransmitLastIBlock(std::uint32_t timeout_ms)
noexcept {
451 const Error e = transport_.Write(last_iblock_, last_iblock_len_, timeout_ms);
455 transport_.delay_ms(inter_frame_delay_ms_);
464 [[nodiscard]]
Error ExpectChainAck(std::uint32_t timeout_ms, std::uint8_t* retransmissions)
noexcept {
465 std::uint8_t wtx_count = 0;
466 for (
int guard = 0; guard < 16; ++guard) {
468 std::size_t raw_len = 0;
469 const Error e = RecvT1Block(raw,
sizeof(raw), &raw_len, timeout_ms);
471 if (++(*retransmissions) > max_retransmissions_) {
488 const Error sb = HandleSBlock(raw, raw_len, timeout_ms, &wtx_count);
500 if (++(*retransmissions) > max_retransmissions_) {
503 const Error rt = RetransmitLastIBlock(timeout_ms);
511 [[nodiscard]]
static constexpr bool IsRetryableReadError(
Error e)
noexcept {
515 [[nodiscard]]
Error ReadWithRetry(std::uint8_t* rx, std::size_t rx_len, std::uint32_t timeout_ms)
noexcept {
516 for (std::uint8_t attempt = 0; attempt <= max_read_retries_; ++attempt) {
517 const Error e = transport_.Read(rx, rx_len, timeout_ms);
521 if (!IsRetryableReadError(e) || attempt == max_read_retries_) {
524 transport_.delay_ms(read_retry_delay_ms_);
529 [[nodiscard]]
Error HandleSBlock(
const std::uint8_t* raw, std::size_t raw_len, std::uint32_t timeout_ms,
530 std::uint8_t* wtx_count)
noexcept {
531 if (raw ==
nullptr || raw_len < 5U) {
534 const std::uint8_t ln = raw[2];
535 if (3U +
static_cast<std::size_t
>(ln) + 2U != raw_len) {
539 if (wtx_count ==
nullptr || *wtx_count >= max_wtx_requests_) {
543 return SendRawBlock(
t1::kPcbSWtxResp, (ln > 0U) ? (raw + 3U) : nullptr, ln, timeout_ms);
552 TransportT& transport_;
553 std::uint32_t inter_frame_delay_ms_{2};
554 std::uint8_t max_read_retries_{6};
555 std::uint32_t read_retry_delay_ms_{2};
556 std::uint8_t max_wtx_requests_{8};
557 std::uint8_t host_ns_{0};
558 std::uint8_t card_ns_{0};
562 std::size_t last_iblock_len_{0};
Stateful T=1 session bound to a concrete I²C transport.
Definition se050_t1_session.hpp:101
Error ExchangeInformation(const std::uint8_t *cmd, std::size_t cmd_len, std::uint8_t *rsp, std::size_t rsp_cap, std::size_t *rsp_len, std::uint32_t timeout_ms) noexcept
Exchange a raw information field payload (APDU bytes) with the SE using I-blocks.
Definition se050_t1_session.hpp:272
std::uint8_t MaxRetransmissions() const noexcept
Definition se050_t1_session.hpp:141
void ResetSequenceState() noexcept
Reset sequence counters (call after electrical / protocol reset).
Definition se050_t1_session.hpp:381
void SetMaxRetransmissions(std::uint8_t n) noexcept
How many times one block is requested or sent again within an exchange before it fails (ISO/IEC 7816-...
Definition se050_t1_session.hpp:139
void SetReadRetryDelayMs(std::uint32_t ms) noexcept
Delay between retryable read attempts (transport polling interval).
Definition se050_t1_session.hpp:116
std::uint8_t MaxWtxRequests() const noexcept
Definition se050_t1_session.hpp:123
T1Session(TransportT &transport) noexcept
Definition se050_t1_session.hpp:103
std::uint32_t ReadRetryDelayMs() const noexcept
Definition se050_t1_session.hpp:118
void SetIfsc(std::size_t ifsc) noexcept
Information field size of the SE (IFSC): the largest INF the host puts in one I-block.
Definition se050_t1_session.hpp:129
std::uint8_t ReadRetries() const noexcept
Definition se050_t1_session.hpp:113
std::size_t Ifsc() const noexcept
Definition se050_t1_session.hpp:133
const TransportT & Transport() const noexcept
Definition se050_t1_session.hpp:144
TransportT & Transport() noexcept
Definition se050_t1_session.hpp:143
void SetInterFrameDelayMs(std::uint32_t ms) noexcept
Delay between a completed Write and the first Read of the response.
Definition se050_t1_session.hpp:106
std::uint32_t InterFrameDelayMs() const noexcept
Definition se050_t1_session.hpp:108
void SetMaxWtxRequests(std::uint8_t max_wtx_requests) noexcept
Upper bound of tolerated WTX request/response cycles within a single exchange.
Definition se050_t1_session.hpp:121
Error RecoverAfterHostReset(std::uint32_t timeout_ms) noexcept
Start-up sequence for SE050 erratum I2C.1, then resynchronise the block numbers.
Definition se050_t1_session.hpp:220
Error GetAnswerToReset(std::uint8_t *buf, std::size_t cap, std::size_t *len_out, std::uint32_t timeout_ms) noexcept
Retrieve the SE050 T=1oI2C profile / ATR-like byte string (S-block GET ATR).
Definition se050_t1_session.hpp:168
Error ChipWarmReset(std::uint32_t timeout_ms) noexcept
Send an S-block warm-reset style request (PCB = t1::kPcbSWarmReset, empty INF).
Definition se050_t1_session.hpp:150
void SetReadRetries(std::uint8_t retries) noexcept
Number of retry attempts for retryable I²C read errors while waiting response blocks.
Definition se050_t1_session.hpp:111
Error Parse(const std::uint8_t *inf, std::size_t inf_len, Profile *out) noexcept
Definition se050_atr.hpp:31
bool VerifyFrameCrc(const std::uint8_t *frame, std::size_t total_len) noexcept
Verify EDC on a full received frame (includes trailing 2-byte CRC).
Definition se050_crc.hpp:52
void AppendEdc(std::uint8_t *frame, std::size_t len_without_edc) noexcept
Append EDC immediately after frame[0 .. len_without_edc-1].
Definition se050_crc.hpp:45
constexpr std::uint8_t kPcbSWtxResp
Definition se050_t1_session.hpp:56
constexpr bool IsSBlock(std::uint8_t pcb) noexcept
Definition se050_t1_session.hpp:64
constexpr std::uint8_t kPcbSResyncResp
Definition se050_t1_session.hpp:53
constexpr bool IsIBlock(std::uint8_t pcb) noexcept
Definition se050_t1_session.hpp:58
constexpr bool IBlockMore(std::uint8_t pcb) noexcept
Definition se050_t1_session.hpp:67
constexpr std::uint8_t kPcbSWtxReq
S-block request/response pair for waiting-time extension.
Definition se050_t1_session.hpp:55
constexpr std::uint8_t RBlockNrFromPcb(std::uint8_t pcb) noexcept
N(R) of an R-block: the sequence number of the I-block its sender expects next.
Definition se050_t1_session.hpp:80
constexpr std::size_t kMaxInformationField
Maximum information field size per block (ISO 7816-3 typical upper bound).
Definition se050_t1_session.hpp:43
constexpr std::uint8_t kPcbSGetAtr
S-block PCB used by NXP stacks for GET ATR / card profile read (LEN = 0 INF).
Definition se050_t1_session.hpp:50
constexpr std::uint8_t kPcbSResyncReq
S-block request/response pair for resynchronization.
Definition se050_t1_session.hpp:52
constexpr std::uint8_t kNadHostToSe
NAD value used by the host for command blocks.
Definition se050_t1_session.hpp:39
constexpr std::uint8_t kDefaultMaxRetransmissions
Default number of times one block is sent again before an exchange fails.
Definition se050_t1_session.hpp:92
constexpr std::uint8_t kRBlockErrOther
R-block error code: other error (ISO/IEC 7816-3 11.4.3).
Definition se050_t1_session.hpp:86
constexpr std::size_t kMaxRawBlockBytes
Raw block buffer high water: prologue + INF + EDC.
Definition se050_t1_session.hpp:45
constexpr bool IsRBlock(std::uint8_t pcb) noexcept
Definition se050_t1_session.hpp:61
constexpr std::uint8_t RBlockNackPcb(std::uint8_t nr_bit, std::uint8_t err) noexcept
R-block asking for the block with sequence number nr again, with error code err.
Definition se050_t1_session.hpp:88
constexpr std::uint8_t RBlockAckPcb(std::uint8_t nr_bit) noexcept
Definition se050_t1_session.hpp:73
constexpr std::uint8_t IBlockSeqFromPcb(std::uint8_t pcb) noexcept
Definition se050_t1_session.hpp:76
constexpr std::uint8_t kPcbSWarmReset
S-block PCB used by NXP stacks for a 7816-style warm reset request (LEN = 0 INF).
Definition se050_t1_session.hpp:48
constexpr std::uint8_t kNadSeToHost
NAD value returned by the SE in response blocks.
Definition se050_t1_session.hpp:41
constexpr std::uint8_t IBlockPcb(std::uint8_t ns_bit, bool more) noexcept
Definition se050_t1_session.hpp:70
constexpr std::uint8_t kRBlockErrEdc
R-block error code: EDC error (ISO/IEC 7816-3 11.4.3).
Definition se050_t1_session.hpp:84
Definition se050_apdu.hpp:15
Error
Definition se050_types.hpp:14
Parser for SE050 T=1oI2C ATR/profile INF payload.
ISO/IEC 7816-3 T=1 EDC (16-bit CRC) for NXP SE050 T1oI2C blocks.
Shared types, limits, and error codes for the HF-SE050 driver.
Definition se050_atr.hpp:16