Hoernchen has uploaded this change for review.

View Change

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);

To view, visit change 42779. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-MessageType: newchange
Gerrit-Project: osmo-ccid-firmware
Gerrit-Branch: master
Gerrit-Change-Id: Iac8bd7f4f0eecccc9acce149277a4f5016fec7c1
Gerrit-Change-Number: 42779
Gerrit-PatchSet: 1
Gerrit-Owner: Hoernchen <ewild@sysmocom.de>