Hoernchen has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-ccid-firmware/+/42779?usp=email )
Change subject: 7816fsm: reset stale cuart state on FSM RESET entry ......................................................................
7816fsm: reset stale cuart state on FSM RESET entry
Reset paths reached without power-cycling (WTIME_EXP, HW_ERR, CARD_REMOVAL during a warm reset) leave the cuart with stale tx_busy, rx_threshold and wtime_etu from the prior transaction. The next ATR then hits card_uart_tx tx_busy assertion, or the ATR receive stalls because the 33-byte ATR can never reach a multi-byte rx_threshold left from a TPDU.
The new card_uart_tx_abort() clears tx_busy + rx_after_tx_compl + WT, without driving a synthetic TX_COMPLETE through the FSM.
iso7816_3_reset_onenter is the right place to do this alongside rx_threshold=1 and wtime_etu=default, this mirrors what card_uart_ctrl(POWER_*=0) already does, but for the warm-reset paths that don't touch power.
Change-Id: Iac8bd7f4f0eecccc9acce149277a4f5016fec7c1 --- M ccid_common/ccid_slot_fsm.c M ccid_common/cuart.c M ccid_common/cuart.h M ccid_common/iso7816_fsm.c 4 files changed, 40 insertions(+), 0 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-ccid-firmware refs/changes/79/42779/1
diff --git a/ccid_common/ccid_slot_fsm.c b/ccid_common/ccid_slot_fsm.c index 22d3f70..fae2c3b 100644 --- a/ccid_common/ccid_slot_fsm.c +++ b/ccid_common/ccid_slot_fsm.c @@ -54,6 +54,8 @@
static struct iso_fsm_slot_instance g_si;
+static void iso_fsm_slot_abort(struct ccid_slot *cs); + static struct iso_fsm_slot *ccid_slot2iso_fsm_slot(struct ccid_slot *cs) { OSMO_ASSERT(cs->slot_nr < ARRAY_SIZE(g_si.slot)); diff --git a/ccid_common/cuart.c b/ccid_common/cuart.c index 737a392..bb47da4 100644 --- a/ccid_common/cuart.c +++ b/ccid_common/cuart.c @@ -167,6 +167,24 @@ return rc; }
+/*! Abort any in-flight TX on the cuart side. + * + * Clears tx_busy and rx_after_tx_compl without driving CUART_E_TX_COMPLETE + * through the FSM. Use this when the layer above (the ISO 7816-3 FSM) has + * given up on the current transaction (e.g. WTIME / HW_ERR / CARD_REMOVAL) + * and is moving to reset state. Any in-flight hardware TX is no longer of + * interest to the FSM; if the driver-level DMA happens to complete later, + * the resulting TX_COMPLETE notification will see tx_busy already false + * and rx_after_tx_compl already false, so it won't spuriously re-enable + * the receiver. */ +void card_uart_tx_abort(struct card_uart *cuart) +{ + OSMO_ASSERT(cuart); + card_uart_wtime_stop(cuart); + cuart->tx_busy = false; + cuart->rx_after_tx_compl = false; +} + int card_uart_tx(struct card_uart *cuart, const uint8_t *data, size_t len, bool rx_after_complete) { OSMO_ASSERT(cuart); diff --git a/ccid_common/cuart.h b/ccid_common/cuart.h index a9afc1d..a53dc18 100644 --- a/ccid_common/cuart.h +++ b/ccid_common/cuart.h @@ -153,6 +153,12 @@ /*! Schedule (asynchronous) transmit data via UART; optionally enable Rx after completion */ int card_uart_tx(struct card_uart *cuart, const uint8_t *data, size_t len, bool rx_after_complete);
+/*! Abort any in-flight TX. Clears tx_busy + stops WT timer. Used by the + * ISO7816-3 FSM when transitioning to RESET, so subsequent transactions + * don't trip card_uart_tx's "TX already in flight" assertion on the stale + * tx_busy left behind by an aborted transfer. */ +void card_uart_tx_abort(struct card_uart *cuart); + /*! Schedule (asynchronous) receive data via UART (after CUART_E_RX_COMPLETE) */ int card_uart_rx(struct card_uart *cuart, uint8_t *data, size_t len);
diff --git a/ccid_common/iso7816_fsm.c b/ccid_common/iso7816_fsm.c index 3202a80..ffa895e 100644 --- a/ccid_common/iso7816_fsm.c +++ b/ccid_common/iso7816_fsm.c @@ -31,6 +31,7 @@
#include "logging.h" #include "cuart.h" +#include "iso7816_3.h" #include "iso7816_fsm.h"
/* unionize to ensure at least properly aligned msgb struct */ @@ -312,7 +313,20 @@ struct iso7816_3_priv *ip = get_iso7816_3_priv(fi); OSMO_ASSERT(fi->fsm == &iso7816_3_fsm);
+ /* Bring the cuart back to its default per-transaction state, matching + * what card_uart_ctrl(CUART_CTL_POWER_*, 0) does, but without + * power-cycling (warm reset, HW_ERR, WTIME paths reach S_RESET without + * touching power). Any reset path could land here mid-transaction with + * stale state: tx_busy still set from an aborted TX, rx_threshold left + * at e.g. 256 from a multi-byte RX setup, wtime_etu still at a + * PPS-negotiated value rather than the ATR default. Leaving those + * stale breaks the next ATR (next card_uart_tx asserts; or ATR bytes + * pile up in the ringbuffer waiting for a threshold that ATR's max 33 + * bytes can never reach). */ card_uart_ctrl(ip->uart, CUART_CTL_RX_TIMER_HINT, 0); + card_uart_tx_abort(ip->uart); + card_uart_set_rx_threshold(ip->uart, 1); + card_uart_ctrl(ip->uart, CUART_CTL_WTIME, ISO7816_3_DEFAULT_WT);
/* go back to initial state in child FSMs */ osmo_fsm_inst_state_chg(ip->atr_fi, ATR_S_WAIT_TS, 0, 0);