Change in simtrace2[master]: cardem: use USART timeout for waiting time

laforge gerrit-no-reply at lists.osmocom.org
Sun Apr 4 17:54:48 UTC 2021


Hello tsaitgaist,

I'd like you to do a code review. Please visit

    https://gerrit.osmocom.org/c/simtrace2/+/23620

to review the following change.


Change subject: cardem: use USART timeout for waiting time
......................................................................

cardem: use USART timeout for waiting time

the reset/ATR handling has been heavily updated/fixed.
instead of using the timer counter peripheral to handle
the waiting time and corresponding timeout, the USART peripheral
internal timeout mechanism is used.
this is particularly important for the SIMtrace board since the
clock signal is not connected to the timer counter.
thus this change adds card emulation support for SIMtrace boards.

Fi and Di have been properly rename to F and D since the "i"
stands only for an "indicated" value, not the actual value.
this does not change the USB protocol (the variable have just been
renamed).
additional variables store more information about the card
capabilities

NOTE: it has only be tested for the SIMtrace board

Change-Id: Ibcb2c8cace9137695adf5fb3de43566f7cfb93b5
---
M contrib/jenkins.sh
M firmware/apps/cardem/Makefile
M firmware/apps/cardem/main.c
M firmware/apps/trace/Makefile
M firmware/libboard/qmod/include/board.h
M firmware/libboard/simtrace/include/board.h
M firmware/libcommon/include/card_emu.h
M firmware/libcommon/include/simtrace_prot.h
M firmware/libcommon/include/tc_etu.h
M firmware/libcommon/source/card_emu.c
M firmware/libcommon/source/mode_cardemu.c
M firmware/libcommon/source/tc_etu.c
M firmware/libosmocore/include/osmocom/core/utils.h
M host/src/simtrace2-cardem-pcsc.c
14 files changed, 365 insertions(+), 66 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/simtrace2 refs/changes/20/23620/1

diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh
index 1d6ff37..3f2eb7e 100755
--- a/contrib/jenkins.sh
+++ b/contrib/jenkins.sh
@@ -28,7 +28,7 @@
 export LD_LIBRARY_PATH="$inst/lib"
 
 BUILDS=""
-BUILDS+="simtrace/dfu simtrace/trace "
+BUILDS+="simtrace/dfu simtrace/trace simtrace/cardem "
 BUILDS+="qmod/dfu qmod/cardem "
 BUILDS+="owhw/dfu owhw/cardem "
 
diff --git a/firmware/apps/cardem/Makefile b/firmware/apps/cardem/Makefile
index d5f68a5..d2039a5 100644
--- a/firmware/apps/cardem/Makefile
+++ b/firmware/apps/cardem/Makefile
@@ -1,3 +1,3 @@
 C_FILES += $(C_LIBUSB_RT)
 
-C_FILES += card_emu.c cciddriver.c iso7816_3.c iso7816_4.c iso7816_fidi.c mitm.c mode_cardemu.c mode_ccid.c simtrace_iso7816.c sniffer.c tc_etu.c usb.c
+C_FILES += card_emu.c iso7816_fidi.c iso7816_3.c iso7816_4.c mode_cardemu.c simtrace_iso7816.c tc_etu.c usb.c
diff --git a/firmware/apps/cardem/main.c b/firmware/apps/cardem/main.c
index a30a211..e1ab8fb 100644
--- a/firmware/apps/cardem/main.c
+++ b/firmware/apps/cardem/main.c
@@ -165,7 +165,7 @@
 	while (USBD_GetState() < USBD_STATE_CONFIGURED) {
 		WDT_Restart(WDT);
 		check_exec_dbg_cmd();
-#if 0
+#if 1
 		if (i >= MAX_USB_ITER * 3) {
 			TRACE_ERROR("Resetting board (USB could "
 				    "not be configured)\n\r");
diff --git a/firmware/apps/trace/Makefile b/firmware/apps/trace/Makefile
index 75c43e8..ddea898 100644
--- a/firmware/apps/trace/Makefile
+++ b/firmware/apps/trace/Makefile
@@ -1,3 +1,3 @@
 C_FILES += $(C_LIBUSB_RT)
 
-C_FILES += card_emu.c cciddriver.c iso7816_4.c iso7816_fidi.c mitm.c mode_cardemu.c mode_ccid.c simtrace_iso7816.c sniffer.c tc_etu.c usb.c
+C_FILES += iso7816_4.c iso7816_fidi.c simtrace_iso7816.c sniffer.c usb.c
diff --git a/firmware/libboard/qmod/include/board.h b/firmware/libboard/qmod/include/board.h
index 5165434..6f95d12 100644
--- a/firmware/libboard/qmod/include/board.h
+++ b/firmware/libboard/qmod/include/board.h
@@ -111,4 +111,7 @@
 #define CARDEMU_SECOND_UART
 #define DETECT_VCC_BY_ADC
 
+/** sysmoQMOD only supports card emulation */
+#ifdef APPLICATION_cardem
 #define HAVE_CARDEM
+#endif
diff --git a/firmware/libboard/simtrace/include/board.h b/firmware/libboard/simtrace/include/board.h
index 5302c8b..65e6197 100644
--- a/firmware/libboard/simtrace/include/board.h
+++ b/firmware/libboard/simtrace/include/board.h
@@ -153,10 +153,18 @@
 
 /** Supported modes */
 /* SIMtrace board supports sniffer mode */
+#ifdef APPLICATION_trace
 #define HAVE_SNIFFER
+#endif
 /* SIMtrace board supports CCID mode */
+#ifdef APPLICATION_ccid
 //#define HAVE_CCID
+#endif
 /* SIMtrace board supports card emulation mode */
-//#define HAVE_CARDEM
+#ifdef APPLICATION_cardem
+#define HAVE_CARDEM
+#endif
 /* SIMtrace board supports man-in-the-middle mode */
+#ifdef APPLICATION_mitm
 //#define HAVE_MITM
+#endif
diff --git a/firmware/libcommon/include/card_emu.h b/firmware/libcommon/include/card_emu.h
index 8a73d5c..494d1e6 100644
--- a/firmware/libcommon/include/card_emu.h
+++ b/firmware/libcommon/include/card_emu.h
@@ -58,10 +58,43 @@
 void card_emu_have_new_uart_tx(struct card_handle *ch);
 void card_emu_report_status(struct card_handle *ch, bool report_on_irq);
 
+/*! call when the waiting time has half-expired
+ *  param[in] ch card for which the waiting time half expired
+ */
+void card_emu_wt_halfed(void *ch);
+/*! call when the waiting time has expired
+ *  param[in] ch card for which the waiting time expired
+ */
+void card_emu_wt_expired(void *ch);
+
 #define ENABLE_TX	0x01
 #define ENABLE_RX	0x02
 
+// the following functions are callbacks implement in mode_cardemu.c
+
 int card_emu_uart_update_fidi(uint8_t uart_chan, unsigned int fidi);
+
+/*! update F and D on USART peripheral
+ *  @param[in] usart USART peripheral to configure
+ *  @param[in] f clock rate conversion integer F value
+ *  @param[in] d baud rate adjustment factor D value
+ *  @note this should happen after reset and protocol select (through PPS or implicit)
+ */
+void card_emu_uart_update_fd(uint8_t uart_chan, uint16_t f, uint8_t d);
+/*! update WT on USART peripheral
+ *  @param[in] usart USART peripheral to configure
+ *  @param[in] wt inactivity Waiting Time before card_emu_wt_expired is called (0 to disable)
+ */
+void card_emu_uart_update_wt(uint8_t uart_chan, uint32_t wt);
+/*! reset waiting timeout count down on USART peripheral
+ *  @param[in] usart USART peripheral to configure
+ */
+void card_emu_uart_reset_wt(uint8_t uart_chan);
+/*! set I/O line high
+ *  @param[in] usart USART peripheral to configure
+ *  @param[in] set if I/O line should be set high (true), or cleared low (false)
+ */
+void card_emu_uart_io_set(uint8_t uart_chan, bool set);
 int card_emu_uart_tx(uint8_t uart_chan, uint8_t byte);
 void card_emu_uart_enable(uint8_t uart_chan, uint8_t rxtx);
 void card_emu_uart_wait_tx_idle(uint8_t uart_chan);
diff --git a/firmware/libcommon/include/simtrace_prot.h b/firmware/libcommon/include/simtrace_prot.h
index eca844a..e4832bb 100644
--- a/firmware/libcommon/include/simtrace_prot.h
+++ b/firmware/libcommon/include/simtrace_prot.h
@@ -230,11 +230,10 @@
 	uint32_t flags;
 	/* phone-applied target voltage in mV */
 	uint16_t voltage_mv;
-	/* Fi/Di related information */
-	uint8_t fi;
-	uint8_t di;
-	uint8_t wi;
-	uint32_t waiting_time;
+	uint8_t f; /*!< index of F and f_max values as encoded in ISO/IEC 7816-3:2006(E) Table 7 */
+	uint8_t d; /*!< index of D value as encoded in ISO/IEC 7816-3:2006(E) Table 8 */
+	uint8_t wi; /*!< Waiting Integer as defined in ISO/IEC 7816-3:2006(E) Section 10.2 */
+	uint32_t wt; /*!< Waiting Time in ETU as defined in ISO/IEC 7816-3:2006(E) Section 8.1 */
 } __attribute__ ((packed));
 
 /* CEMU_USB_MSGT_DO_PTS */
diff --git a/firmware/libcommon/include/tc_etu.h b/firmware/libcommon/include/tc_etu.h
index 663ae5a..8817a41 100644
--- a/firmware/libcommon/include/tc_etu.h
+++ b/firmware/libcommon/include/tc_etu.h
@@ -7,5 +7,3 @@
 void tc_etu_enable(uint8_t chan_nr);
 void tc_etu_disable(uint8_t chan_nr);
 
-extern void tc_etu_wtime_half_expired(void *handle);
-extern void tc_etu_wtime_expired(void *handle);
diff --git a/firmware/libcommon/source/card_emu.c b/firmware/libcommon/source/card_emu.c
index 269c7c6..fb532d5 100644
--- a/firmware/libcommon/source/card_emu.c
+++ b/firmware/libcommon/source/card_emu.c
@@ -1,7 +1,7 @@
 /* ISO7816-3 state machine for the card side
  *
  * (C) 2010-2019 by Harald Welte <laforge at gnumonks.org>
- * (C) 2018 by sysmocom -s.f.m.c. GmbH, Author: Kevin Redon <kredon at sysmocom.de>
+ * (C) 2018-2019 by sysmocom -s.f.m.c. GmbH, Author: Kevin Redon <kredon at sysmocom.de>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@
 
 #include "utils.h"
 #include "trace.h"
+#include "iso7816_3.h"
 #include "iso7816_fidi.h"
 #include "tc_etu.h"
 #include "card_emu.h"
@@ -154,18 +155,53 @@
 	bool in_reset;	/*< if card is in reset (true = RST low/asserted, false = RST high/ released) */
 	bool clocked;	/*< if clock is active ( true = active, false = inactive) */
 
-	/* timing parameters, from PTS */
-	uint8_t fi;
-	uint8_t di;
-	uint8_t wi;
-
 	uint8_t tc_chan;	/* TC channel number */
 	uint8_t uart_chan;	/* UART channel */
 
 	uint8_t in_ep;		/* USB IN EP */
 	uint8_t irq_ep;		/* USB IN EP */
 
-	uint32_t waiting_time;	/* in clocks */
+	/*! clock rate conversion integer F
+	 *  @implements ISO/IEC 7816-3:2006(E) section 7.1
+	 *  @note this represents the current value used
+	 */
+	uint16_t f;
+	/*! baud rate adjustment factor D
+	 *  @implements ISO/IEC 7816-3:2006(E) section 7.1
+	 *  @note this represents the current value used
+	 */
+	uint8_t d;
+	/*! clock frequency in Hz
+	 *  @implements ISO/IEC 7816-3:2006(E) section 7.1
+	 *  @note the USART peripheral in slave mode does not provide the current value. we could measure it but this is not really useful. instead we remember the maximum possible value corresponding to the selected F value
+	 */
+	uint32_t f_cur;
+	/*! clock rate conversion integer Fi
+	 *  @implements ISO/IEC 7816-3:2006(E) Table 7
+	 *  @note this represents the maximum value supported by the card, and can be indicated in TA1
+	 *  @note this value can be set in TA1
+	 */
+	uint16_t fi;
+	/*! baud rate adjustment factor Di
+	 *  @implements ISO/IEC 7816-3:2006(E) Table 8
+	 *  @note this represents the maximum value supported by the card, and can be indicated in TA1
+	 */
+	uint8_t di;
+	/*! clock frequency, in Hz
+	 *  @implements ISO/IEC 7816-3:2006(E) Table 7
+	 *  @note this represents the maximum value supported by the card, and can be indicated in TA1
+	 */
+	uint32_t f_max;
+	/*! Waiting Integer
+	 *  @implements ISO/IEC 7816-3:2006(E) Section 10.2
+	 *  @note this value can be set in TA2
+	 */
+	uint8_t wi;
+	/*! Waiting Time, in ETU
+	 *  @implements ISO/IEC 7816-3:2006(E) Section 8.1
+	 *  @note this depends on Fi, Di, and WI if T=0 is used
+	 */
+	uint32_t wt;
 
 	/* ATR state machine */
 	struct {
@@ -206,7 +242,9 @@
 {
 	struct msgb *msg;
 
+#ifndef BOARD_simtrace
 	tc_etu_disable(ch->tc_chan);
+#endif
 
 	/* release any buffers we may still own */
 	if (ch->uart_tx_msg) {
@@ -390,13 +428,44 @@
 	case ISO_S_WAIT_POWER:
 	case ISO_S_WAIT_CLK:
 	case ISO_S_WAIT_RST:
-		/* disable Rx and Tx of UART */
-		card_emu_uart_enable(ch->uart_chan, 0);
+		card_emu_uart_enable(ch->uart_chan, 0); // disable Rx and Tx of UART
+#ifdef BOARD_simtrace
+		card_emu_uart_update_wt(ch->uart_chan, 0); // disable timeout
+		if (ISO_S_WAIT_POWER == new_state) {
+			card_emu_uart_io_set(ch->uart_chan, false); // pull I/O line low
+		} else {
+			card_emu_uart_io_set(ch->uart_chan, true); // pull I/O line high
+		}
+#endif
 		break;
 	case ISO_S_WAIT_ATR:
+
+		// reset the ETU-related values
+		ch->f = ISO7816_3_DEFAULT_FD;
+		ch->d = ISO7816_3_DEFAULT_DD;
+
+#ifdef BOARD_simtrace
+		card_emu_uart_update_fd(ch->uart_chan, ch->f, ch->d); // set baud rate
+
+		// reset values optionally specified in the ATR
+		ch->fi = ISO7816_3_DEFAULT_FI;
+		ch->di = ISO7816_3_DEFAULT_DI;
+		ch->wi = ISO7816_3_DEFAULT_WI;
+		int32_t wt = iso7816_3_calculate_wt(ch->wi, ch->fi, ch->di, ch->f, ch->d); // get default waiting time
+		if (wt <= 0) {
+			TRACE_FATAL("%u: invalid WT %ld\r\n", ch->num, wt);
+		}
+		ch->wt = wt;
+		card_emu_uart_enable(ch->uart_chan, ENABLE_TX); // enable TX to be able to use the timeout
+		/* the ATR should only be sent 400 to 40k clock cycles after the RESET.
+		 * we use the UART timeout mechanism to wait this time.
+		 * since the initial ETU is Fd=372/Dd=1 clock cycles long, we have to wait 2-107 ETU.
+		 */
+		card_emu_uart_update_wt(ch->uart_chan, 2);
+#else
 		/* Reset to initial Fi / Di ratio */
-		ch->fi = 1;
-		ch->di = 1;
+		ch->f = 1;
+		ch->d = 1;
 		emu_update_fidi(ch);
 		/* the ATR should only be sent 400 to 40k clock cycles after the RESET.
 		 * we use the tc_etu mechanism to wait this time.
@@ -405,16 +474,20 @@
 		tc_etu_set_wtime(ch->tc_chan, 2);
 		/* enable the TC/ETU counter once reset has been released */
 		tc_etu_enable(ch->tc_chan);
+#endif
+
 		break;
 	case ISO_S_IN_ATR:
+#ifndef BOARD_simtrace
 		/* initialize to default WI, this will be overwritten if we
 		 * send TC2, and it will be programmed into hardware after
 		 * ATR is finished */
 		ch->wi = ISO7816_3_DEFAULT_WI;
 		/* update waiting time to initial waiting time */
-		ch->waiting_time = ISO7816_3_INIT_WTIME;
+		ch->wt = ISO7816_3_INIT_WTIME;
 		/* set initial waiting time */
-		tc_etu_set_wtime(ch->tc_chan, ch->waiting_time);
+		tc_etu_set_wtime(ch->tc_chan, ch->wt);
+#endif
 		/* Set ATR sub-state to initial state */
 		ch->atr.idx = 0;
 		/* enable USART transmission to reader */
@@ -489,9 +562,15 @@
 				}
 			}
 		}
+#ifdef BOARD_simtrace
+		/* FIXME update waiting time in case of card is specific mode */
+		/* reset PTS to initial state */
+		set_pts_state(ch, PTS_S_WAIT_REQ_PTSS);
+#else
 		/* update waiting time (see ISO 7816-3 10.2) */
-		ch->waiting_time = ch->wi * 960 * ch->fi;
-		tc_etu_set_wtime(ch->tc_chan, ch->waiting_time);
+		ch->wt = ch->wi * 960 * ch->fi;
+		tc_etu_set_wtime(ch->tc_chan, ch->wt);
+#endif
 		/* go to next state */
 		card_set_state(ch, ISO_S_WAIT_TPDU);
 		return 0;
@@ -557,9 +636,12 @@
 	return PTS_S_WAIT_REQ_PCK | is_resp;
 }
 
-
-static int
-process_byte_pts(struct card_handle *ch, uint8_t byte)
+/*! process incoming PTS byte
+ *  @param[in] ch card handle on which the byte has been received
+ *  @param[in] byte received PTS byte
+ *  @return new iso7816_3_card_state or -1 at the end of PTS request
+ */
+static int process_byte_pts(struct card_handle *ch, uint8_t byte)
 {
 	switch (ch->pts.state) {
 	case PTS_S_WAIT_REQ_PTSS:
@@ -625,11 +707,17 @@
 		break;
 	case PTS_S_WAIT_RESP_PTS1:
 		byte = ch->pts.resp[_PTS1];
-		/* This must be TA1 */
-		ch->fi = byte >> 4;
-		ch->di = byte & 0xf;
-		TRACE_DEBUG("%u: found Fi=%u Di=%u\r\n", ch->num,
-			    ch->fi, ch->di);
+		// TODO the value should have been validated when receiving the request
+		ch->f = iso7816_3_fi_table[byte >> 4]; // save selected Fn
+		if (0 == ch->f) {
+			TRACE_ERROR("%u: invalid F index in PPS response: %u\r\n", ch->num, byte >> 4);
+			// TODO become unresponsive to signal error condition
+		}
+		ch->d = iso7816_3_di_table[byte & 0xf]; // save selected Dn
+		if (0 == ch->d) {
+			TRACE_ERROR("%u: invalid D index in PPS response: %u\r\n", ch->num, byte & 0xf);
+			// TODO become unresponsive to signal error condition
+		}
 		break;
 	case PTS_S_WAIT_RESP_PTS2:
 		byte = ch->pts.resp[_PTS2];
@@ -654,10 +742,23 @@
 	switch (ch->pts.state) {
 	case PTS_S_WAIT_RESP_PCK:
 		card_emu_uart_wait_tx_idle(ch->uart_chan);
+#ifdef BOARD_simtrace
+		card_emu_uart_update_fd(ch->uart_chan, ch->f, ch->d); // set selected baud rate
+		int32_t wt = iso7816_3_calculate_wt(ch->wi, ch->fi, ch->di, ch->f, ch->d); // get new waiting time
+		if (wt <= 0) {
+			TRACE_ERROR("%u: invalid WT calculated: %ld\r\n", ch->num, wt);
+			// TODO become unresponsive to signal error condition
+		} else {
+			ch->wt = wt;
+		}
+		// FIXME disable WT
+#else
 		/* update baud rate generator with Fi/Di */
 		emu_update_fidi(ch);
+#endif
 		/* Wait for the next TPDU */
 		card_set_state(ch, ISO_S_WAIT_TPDU);
+		set_pts_state(ch, PTS_S_WAIT_REQ_PTSS);
 		break;
 	default:
 		/* calculate the next state and set it */
@@ -725,6 +826,10 @@
 {
 	if (ch->tpdu.state == new_ts)
 		return;
+	if (ISO_S_IN_TPDU != ch->state) {
+		TRACE_ERROR("%u: setting TPDU state in %s state\r\n", ch->num,
+			get_value_string(iso7816_3_card_state_names, ch->state));
+	}
 
 	TRACE_DEBUG("%u: 7816 TPDU state %s -> %s\r\n", ch->num,
 		get_value_string(tpdu_state_names, ch->tpdu.state),
@@ -732,15 +837,20 @@
 	ch->tpdu.state = new_ts;
 
 	switch (new_ts) {
-	case TPDU_S_WAIT_CLA:
-	case TPDU_S_WAIT_RX:
-		card_emu_uart_enable(ch->uart_chan, ENABLE_RX);
+	case TPDU_S_WAIT_CLA: // we will be waiting for the next incoming TDPU
+		card_emu_uart_enable(ch->uart_chan, ENABLE_RX); // switch back to receiving mode
+		card_emu_uart_update_wt(ch->uart_chan, 0); // disable waiting time since we don't expect any data
+		break;
+	case TPDU_S_WAIT_INS: // the reader started sending the TPDU header
+		card_emu_uart_update_wt(ch->uart_chan, ch->wt); // start waiting for the rest of the header/body
+		break;
+	case TPDU_S_WAIT_RX: // the reader should send us the TPDU body data
+		card_emu_uart_enable(ch->uart_chan, ENABLE_RX); // switch to receive mode to receive the body
+		card_emu_uart_update_wt(ch->uart_chan, ch->wt); // start waiting for the rest body
 		break;
 	case TPDU_S_WAIT_PB:
-		/* we just completed the TPDU header from reader to card
-		 * and now need to disable the receiver, enable the
-		 * transmitter and transmit the procedure byte */
-		card_emu_uart_enable(ch->uart_chan, ENABLE_TX);
+		card_emu_uart_enable(ch->uart_chan, ENABLE_TX); // header is completely received, now we need to transmit the procedure byte
+		card_emu_uart_update_wt(ch->uart_chan, ch->wt); // prepare to extend the waiting time once half of it is reached
 		break;
 	default:
 		break;
@@ -1024,11 +1134,11 @@
 		sts->flags |= CEMU_STATUS_F_CLK_ACTIVE;
 	if (ch->in_reset)
 		sts->flags |= CEMU_STATUS_F_RESET_ACTIVE;
-	/* FIXME: voltage + card insert */
-	sts->fi = ch->fi;
-	sts->di = ch->di;
+	/* FIXME set voltage and card insert values */
+	sts->f = ch->f;
+	sts->d = ch->d;
 	sts->wi = ch->wi;
-	sts->waiting_time = ch->waiting_time;
+	sts->wt = ch->wt;
 
 	usb_buf_upd_len_and_submit(msg);
 }
@@ -1083,9 +1193,7 @@
 	case CARD_IO_RST:
 		if (active == 0 && ch->in_reset) {
 			TRACE_INFO("%u: RST released\r\n", ch->num);
-			if (ch->vcc_active && ch->clocked) {
-				/* enable the TC/ETU counter once reset has been released */
-				tc_etu_enable(ch->tc_chan);
+			if (ch->vcc_active && ch->clocked && ISO_S_WAIT_RST == ch->state) {
 				/* prepare to send the ATR */
 				card_set_state(ch, ISO_S_WAIT_ATR);
 			}
@@ -1094,9 +1202,14 @@
 			TRACE_INFO("%u: RST asserted\r\n", ch->num);
 			card_handle_reset(ch);
 			chg_mask |= CEMU_STATUS_F_RESET_ACTIVE;
+#ifdef BOARD_simtrace
+			card_set_state(ch, ISO_S_WAIT_RST);
+#endif
 		}
 		ch->in_reset = active;
 		break;
+	default:
+		break;
 	}
 
 	switch (ch->state) {
@@ -1143,7 +1256,7 @@
 }
 
 /* hardware driver informs us that one (more) ETU has expired */
-void tc_etu_wtime_half_expired(void *handle)
+void card_emu_wt_halfed(void *handle)
 {
 	struct card_handle *ch = handle;
 	/* transmit NULL procedure byte well before waiting time expires */
@@ -1153,7 +1266,8 @@
 		case TPDU_S_WAIT_PB:
 		case TPDU_S_WAIT_TX:
 			putchar('N');
-			card_emu_uart_tx(ch->uart_chan, ISO7816_3_PB_NULL);
+			card_emu_uart_tx(ch->uart_chan, ISO7816_3_PB_NULL); // we are waiting for data from the user. send a procedure byte to ask the reader to wait more time
+			card_emu_uart_reset_wt(ch->uart_chan); // reset WT
 			break;
 		default:
 			break;
@@ -1165,7 +1279,7 @@
 }
 
 /* hardware driver informs us that one (more) ETU has expired */
-void tc_etu_wtime_expired(void *handle)
+void card_emu_wt_expired(void *handle)
 {
 	struct card_handle *ch = handle;
 	switch (ch->state) {
@@ -1174,6 +1288,7 @@
 		card_set_state(ch, ISO_S_IN_ATR);
 		break;
 	default:
+		// TODO become unresponsive
 		TRACE_ERROR("%u: wtime_exp\r\n", ch->num);
 		break;
 	}
@@ -1224,6 +1339,7 @@
 
 	INIT_LLIST_HEAD(&ch->uart_tx_queue);
 
+	/* initialize the card_handle with reasonable defaults */
 	ch->num = slot_num;
 	ch->irq_ep = irq_ep;
 	ch->in_ep = in_ep;
@@ -1232,21 +1348,26 @@
 	ch->in_reset = in_reset;
 	ch->clocked = clocked;
 
-	ch->fi = 0;
-	ch->di = 1;
+	ch->fi = ISO7816_3_DEFAULT_FI;
+	ch->di = ISO7816_3_DEFAULT_DI;
 	ch->wi = ISO7816_3_DEFAULT_WI;
+	ch->wt = ISO7816_3_DEFAULT_WT;;
 
 	ch->tc_chan = tc_chan;
 	ch->uart_chan = uart_chan;
-	ch->waiting_time = ISO7816_3_INIT_WTIME;
 
 	ch->atr.idx = 0;
 	ch->atr.len = sizeof(default_atr);
 	memcpy(ch->atr.atr, default_atr, ch->atr.len);
 
-	card_handle_reset(ch);
+	ch->pts.state = PTS_S_WAIT_REQ_PTSS;
+	ch->tpdu.state = TPDU_S_WAIT_CLA;
 
+	card_handle_reset(ch);
+#ifndef BOARD_simtrace
+	/* simtrace uses uart timer instead */
 	tc_etu_init(ch->tc_chan, ch);
+#endif
 
 	return ch;
 }
diff --git a/firmware/libcommon/source/mode_cardemu.c b/firmware/libcommon/source/mode_cardemu.c
index c6c129d..32d3b17 100644
--- a/firmware/libcommon/source/mode_cardemu.c
+++ b/firmware/libcommon/source/mode_cardemu.c
@@ -22,6 +22,7 @@
 #include "simtrace.h"
 #include "ringbuffer.h"
 #include "card_emu.h"
+#include "iso7816_3.h"
 #include "iso7816_fidi.h"
 #include "utils.h"
 #include <osmocom/core/linuxlist.h>
@@ -54,11 +55,15 @@
 	struct card_handle *ch;
 	struct llist_head usb_out_queue;
 	struct ringbuf rb;
+	uint32_t wt; /*!< receiver waiting time to trigger timeout (0 to deactivate it) */
+	uint32_t wt_remaining; /*!< remaining waiting time */
+	bool wt_halfed; /*!< if at least half of the waiting time passed */
 	struct Usart_info usart_info;
 	int usb_pending_old;
 	uint8_t ep_out;
 	uint8_t ep_in;
 	uint8_t ep_int;
+	const Pin pin_io;
 	const Pin pin_insert;
 #ifdef DETECT_VCC_BY_ADC
 	uint32_t vcc_uv;
@@ -81,6 +86,7 @@
 		.ep_out = SIMTRACE_CARDEM_USB_EP_USIM1_DATAOUT,
 		.ep_in = SIMTRACE_CARDEM_USB_EP_USIM1_DATAIN,
 		.ep_int = SIMTRACE_CARDEM_USB_EP_USIM1_INT,
+		.pin_io = PIN_USIM1_IO,
 #ifdef PIN_SET_USIM1_PRES
 		.pin_insert = PIN_SET_USIM1_PRES,
 #endif
@@ -96,6 +102,7 @@
 		.ep_out = SIMTRACE_CARDEM_USB_EP_USIM2_DATAOUT,
 		.ep_in = SIMTRACE_CARDEM_USB_EP_USIM2_DATAIN,
 		.ep_int = SIMTRACE_CARDEM_USB_EP_USIM2_INT,
+		.pin_io = PIN_USIM2_IO,
 #ifdef PIN_SET_USIM2_PRES
 		.pin_insert = PIN_SET_USIM2_PRES,
 #endif
@@ -146,7 +153,11 @@
 		 * receiver enabled during transmit */
 		USART_SetReceiverEnabled(usart, 1);
 		usart->US_CR = US_CR_RSTSTA | US_CR_RSTIT | US_CR_RSTNACK;
+#ifdef BOARD_simtrace
+		USART_EnableIt(usart, US_IER_TXRDY | US_IER_TIMEOUT);
+#else
 		USART_EnableIt(usart, US_IER_TXRDY);
+#endif
 		USART_SetTransmitterEnabled(usart, 1);
 		break;
 	case ENABLE_RX:
@@ -156,7 +167,11 @@
 		USART_SetTransmitterEnabled(usart, 1);
 		wait_tx_idle(usart);
 		usart->US_CR = US_CR_RSTSTA | US_CR_RSTIT | US_CR_RSTNACK;
+#ifdef BOARD_simtrace
+		USART_EnableIt(usart, US_IER_RXRDY | US_IER_TIMEOUT);
+#else
 		USART_EnableIt(usart, US_IER_RXRDY);
+#endif
 		USART_SetReceiverEnabled(usart, 1);
 		break;
 	case 0:
@@ -198,11 +213,18 @@
 /* FIXME: integrate this with actual irq handler */
 static void usart_irq_rx(uint8_t inst_num)
 {
-	OSMO_ASSERT(inst_num < ARRAY_SIZE(cardem_inst));
+	if (inst_num >= ARRAY_SIZE(cardem_inst)) {
+		TRACE_ERROR("%u: UART channel out of bounds\r\n", inst_num);
+		return;
+	}
 	Usart *usart = get_usart_by_chan(inst_num);
 	struct cardem_inst *ci = &cardem_inst[inst_num];
 	uint32_t csr;
 	uint8_t byte = 0;
+	uint32_t errflags = (US_CSR_OVRE | US_CSR_FRAME | US_CSR_PARE | US_CSR_NACK | (1 << 10));
+#ifndef BOARD_simtrace
+	errflags |= US_CSR_TIMEOUT;
+#endif
 
 	csr = usart->US_CSR & usart->US_IMR; // save state/flags before they get changed
 
@@ -217,10 +239,33 @@
 			USART_DisableIt(usart, US_IER_TXRDY); // stop the TX ready signal if not byte has been transmitted
 	}
 
-	if (csr & (US_CSR_OVRE|US_CSR_FRAME|US_CSR_PARE|US_CSR_TIMEOUT|US_CSR_NACK|(1<<10))) { // error flag set
+	if (csr & errflags) { // error flag set
 		usart->US_CR = US_CR_RSTSTA | US_CR_RSTIT | US_CR_RSTNACK; // reset UART state to clear flag
 		TRACE_ERROR("%u USART error on 0x%x status: 0x%lx\n", ci->num, byte, csr); // warn user about error
 	}
+#ifdef BOARD_simtrace
+	// handle timeout
+	if (csr & US_CSR_TIMEOUT) { // RX has been inactive for some time
+		if (ci->wt_remaining <= (usart->US_RTOR & 0xffff)) { // waiting time has passed
+			ci->wt_remaining = 0; // timeout reached (will stop the timer)
+		} else {
+			ci->wt_remaining -= (usart->US_RTOR & 0xffff); // be sure to subtract the actual timeout since the new might not have been set and reloaded yet
+		}
+		if (0 == ci->wt_remaining) {
+			card_emu_wt_expired(ci->ch); // let the state know WT has expired
+		} else if (ci->wt_remaining <= ci->wt / 2 && !ci->wt_halfed) {
+			ci->wt_halfed = true;
+			card_emu_wt_halfed(ci->ch); // let the state know WT has half expired
+		}
+		if (ci->wt_remaining > 0xffff) { // value exceeds the USART TO range
+			usart->US_RTOR = 0xffff; // use the MAX
+		} else {
+			usart->US_RTOR = ci->wt_remaining;
+		}
+		usart->US_CR |= US_CR_STTTO; // clear timeout flag (and stop timeout until next character is received)
+		usart->US_CR |= US_CR_RETTO; // restart the counter (it wt is 0, the timeout is not started)
+	}
+#endif
 }
 
 /*! ISR called for USART0 */
@@ -249,6 +294,91 @@
 	return 0;
 }
 
+// call-back from card_emu.c to change UART baud rate
+void card_emu_uart_update_fd(uint8_t uart_chan, uint16_t f, uint8_t d)
+{
+	Usart *usart = get_usart_by_chan(uart_chan); // get the USART based on the card handle
+	if (NULL == usart) {
+		TRACE_ERROR("%u: USART not found by chan\r\n", uart_chan);
+		return;
+	}
+	if (!iso7816_3_valid_f(f)) {
+		TRACE_ERROR("%u: invalid F: %u\r\n", uart_chan, f);
+		return;
+	}
+	if (!iso7816_3_valid_d(d)) {
+		TRACE_ERROR("%u: invalid D: %u\r\n", uart_chan, d);
+		return;
+	}
+
+	uint16_t ratio = f / d;
+	if (ratio > 0 && ratio < 2048) {
+		/* make sure USART uses new F/D ratio */
+		usart->US_CR |= US_CR_RXDIS | US_CR_RSTRX; // disable USART before changing baud rate
+		usart->US_FIDI = (ratio & 0x7ff); // change baud rate (ratio)
+		usart->US_CR |= US_CR_RXEN | US_CR_STTTO; // re-enable USART (and stop timeout)
+		TRACE_INFO("%u: USART F/D set to %u/%u\r\n", uart_chan, f, d);
+	} else {
+		TRACE_ERROR("%u: USART could not set F/D to %u/%u\r\n", uart_chan, f, d);
+		// TODO become unresponsive
+	}
+}
+
+void card_emu_uart_update_wt(uint8_t uart_chan, uint32_t wt)
+{
+	if (uart_chan >= ARRAY_SIZE(cardem_inst)) {
+		TRACE_ERROR("%u: UART channel out of bounds\r\n", uart_chan);
+		return;
+	}
+	struct cardem_inst *ci = &cardem_inst[uart_chan];
+	Usart *usart = get_usart_by_chan(uart_chan); // get the USART based on the card handle
+	if (NULL == usart) {
+		TRACE_ERROR("%u: USART not found by chan\r\n", uart_chan);
+		return;
+	}
+
+	ci->wt = wt; // save value
+	card_emu_uart_reset_wt(uart_chan); // reset and start timer
+	TRACE_INFO("%u: USART WT set to %lu ETU\r\n", uart_chan, wt);
+}
+
+void card_emu_uart_reset_wt(uint8_t uart_chan)
+{
+	if (uart_chan >= ARRAY_SIZE(cardem_inst)) {
+		TRACE_ERROR("%u: UART channel out of bounds\r\n", uart_chan);
+		return;
+	}
+	struct cardem_inst *ci = &cardem_inst[uart_chan];
+	Usart *usart = get_usart_by_chan(uart_chan); // get the USART based on the card handle
+	if (NULL == usart) {
+		TRACE_ERROR("%u: USART not found by chan\r\n", uart_chan);
+		return;
+	}
+
+	ci->wt_remaining = ci->wt; // reload WT value
+	ci->wt_halfed = false; // reset half expired
+	if (ci->wt_remaining > 0xffff) { // value exceeds the USART TO range
+		usart->US_RTOR = 0xffff; // use the MAX
+	} else {
+		usart->US_RTOR = ci->wt_remaining;
+	}
+	usart->US_CR |= US_CR_RETTO; // restart the counter (if wt is 0, the timeout is not started)
+}
+
+void card_emu_uart_io_set(uint8_t uart_chan, bool set)
+{
+	if (uart_chan >= ARRAY_SIZE(cardem_inst)) {
+		TRACE_ERROR("%u: UART channel out of bounds\r\n", uart_chan);
+		return;
+	}
+	struct cardem_inst *ci = &cardem_inst[uart_chan];
+	if (set) {
+		PIO_Set(&ci->pin_io);
+	} else {
+		PIO_Clear(&ci->pin_io);
+	}
+}
+
 /* call-back from card_emu.c to force a USART interrupt */
 void card_emu_uart_interrupt(uint8_t uart_chan)
 {
@@ -462,6 +592,10 @@
 					  SIMTRACE_CARDEM_USB_EP_USIM1_INT, cardem_inst[0].vcc_active,
 					  cardem_inst[0].rst_active, cardem_inst[0].vcc_active);
 	sim_switch_use_physical(0, 1);
+#ifndef DETECT_VCC_BY_ADC
+	usim1_vcc_irqhandler(NULL); // check VCC/CLK state
+#endif
+	usim1_rst_irqhandler(NULL); // force RST state
 
 #ifdef CARDEMU_SECOND_UART
 	INIT_LLIST_HEAD(&cardem_inst[1].usb_out_queue);
diff --git a/firmware/libcommon/source/tc_etu.c b/firmware/libcommon/source/tc_etu.c
index 04a7324..f5f8918 100644
--- a/firmware/libcommon/source/tc_etu.c
+++ b/firmware/libcommon/source/tc_etu.c
@@ -23,6 +23,9 @@
 
 #include "chip.h"
 
+void card_emu_wt_halfed(void *handle);
+void card_emu_wt_expired(void *handle);
+
 /* pins for Channel 0 of TC-block 0, we only use TCLK + TIOB */
 #define PIN_TCLK0	{PIO_PA4, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT }
 #define PIN_TIOA0	{PIO_PA0, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT}
@@ -85,7 +88,7 @@
 		te->nr_events++;
 		if (te->nr_events == te->wait_events/2) {
 			/* Indicate that half the waiting tim has expired */
-			tc_etu_wtime_half_expired(te->handle);
+			card_emu_wt_halfed(te->handle);
 		}
 		if (te->nr_events >= te->wait_events) {
 			TcChannel *chan = te->chan;
@@ -96,7 +99,7 @@
 			chan->TC_CCR = TC_CCR_CLKEN;
 
 			/* Indicate that the waiting tim has expired */
-			tc_etu_wtime_expired(te->handle);
+			card_emu_wt_expired(te->handle);
 		}
 	}
 }
diff --git a/firmware/libosmocore/include/osmocom/core/utils.h b/firmware/libosmocore/include/osmocom/core/utils.h
index 63a73ab..8f0e385 100644
--- a/firmware/libosmocore/include/osmocom/core/utils.h
+++ b/firmware/libosmocore/include/osmocom/core/utils.h
@@ -36,7 +36,7 @@
 int get_string_value(const struct value_string *vs, const char *str);
 
 char osmo_bcd2char(uint8_t bcd);
-/* only works for numbers in ascci */
+/* only works for numbers in ASCII */
 uint8_t osmo_char2bcd(char c);
 
 int osmo_hexparse(const char *str, uint8_t *b, int max_len);
@@ -60,7 +60,7 @@
 	rem -= ret;						\
 } while (0)
 
-/*! Helper macro to terminate when an assertion failes
+/*! Helper macro to terminate when an assertion fails
  *  \param[in] exp Predicate to verify
  *  This function will generate a backtrace and terminate the program if
  *  the predicate evaluates to false (0).
@@ -75,7 +75,7 @@
 /*! duplicate a string using talloc and release its prior content (if any)
  * \param[in] ctx Talloc context to use for allocation
  * \param[out] dst pointer to string, will be updated with ptr to new string
- * \param[in] newstr String that will be copieed to newly allocated string */
+ * \param[in] newstr String that will be copied to newly allocated string */
 static inline void osmo_talloc_replace_string(void *ctx, char **dst, const char *newstr)
 {
 	if (*dst)
diff --git a/host/src/simtrace2-cardem-pcsc.c b/host/src/simtrace2-cardem-pcsc.c
index f11330c..d8fb531 100644
--- a/host/src/simtrace2-cardem-pcsc.c
+++ b/host/src/simtrace2-cardem-pcsc.c
@@ -93,9 +93,9 @@
 	struct cardemu_usb_msg_status *status;
 	status = (struct cardemu_usb_msg_status *) buf;
 
-	printf("=> STATUS: flags=0x%x, fi=%u, di=%u, wi=%u wtime=%u\n",
-		status->flags, status->fi, status->di, status->wi,
-		status->waiting_time);
+	printf("=> STATUS: flags=0x%x, F=%u, D=%u, WI=%u WT=%u\n",
+		status->flags, status->f, status->d, status->wi,
+		status->wt);
 
 	return 0;
 }

-- 
To view, visit https://gerrit.osmocom.org/c/simtrace2/+/23620
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: simtrace2
Gerrit-Branch: master
Gerrit-Change-Id: Ibcb2c8cace9137695adf5fb3de43566f7cfb93b5
Gerrit-Change-Number: 23620
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <laforge at osmocom.org>
Gerrit-Reviewer: tsaitgaist <kredon at sysmocom.de>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20210404/ee0c699f/attachment.htm>


More information about the gerrit-log mailing list