aboutsummaryrefslogtreecommitdiffstats
path: root/firmware
diff options
context:
space:
mode:
Diffstat (limited to 'firmware')
-rw-r--r--firmware/Makefile3
-rw-r--r--firmware/src_simtrace/card_emu.c694
-rw-r--r--firmware/src_simtrace/card_emu.h33
-rw-r--r--firmware/src_simtrace/cardemu_prot.h121
-rw-r--r--firmware/src_simtrace/req_ctx.c210
-rw-r--r--firmware/src_simtrace/req_ctx.h48
-rw-r--r--firmware/src_simtrace/tc_etu.c194
-rw-r--r--firmware/src_simtrace/tc_etu.h9
-rw-r--r--firmware/src_simtrace/utils.h14
-rw-r--r--firmware/test/Makefile13
-rw-r--r--firmware/test/card_emu_tests.c160
-rw-r--r--firmware/test/trace.h5
12 files changed, 1503 insertions, 1 deletions
diff --git a/firmware/Makefile b/firmware/Makefile
index 785837f..b1a1cef 100644
--- a/firmware/Makefile
+++ b/firmware/Makefile
@@ -118,6 +118,7 @@ CFLAGS += -Dprintf=iprintf
# -mlong-calls -Wall
#CFLAGS += -save-temps -fverbose-asm
#CFLAGS += -Wa,-a,-ad
+CFLAGS += -D__ARM
CFLAGS += --param max-inline-insns-single=500 -mcpu=cortex-m3 -mthumb # -mfix-cortex-m3-ldrd
CFLAGS += -ffunction-sections -g $(OPTIMIZATION) $(INCLUDES) -D$(CHIP) -DTRACE_LEVEL=$(TRACE_LEVEL) -DDEBUG_PHONE_SNIFF=$(DEBUG_PHONE_SNIFF)
ASFLAGS = -mcpu=cortex-m3 -mthumb -Wall -g $(OPTIMIZATION) $(INCLUDES) -D$(CHIP) -D__ASSEMBLY__
@@ -138,7 +139,7 @@ C_CMSIS = core_cm3.o
C_LOWLEVEL = board_cstartup_gnu.o board_lowlevel.o syscalls.o exceptions.o
C_LIBLEVEL = spi.o pio.o pmc.o usart.o pio_it.o pio_capture.o uart_console.o iso7816_4.o wdt.o led.o tc.o
C_CCID = cciddriver.o USBD.o USBDDriver.o USBD_HAL.o USBRequests.o USBDCallbacks.o USBDescriptors.o USBDDriverCallbacks.o
-C_SIMTRACE = simtrace_iso7816.o usb.o ccid.o sniffer.o phone.o mitm.o ringbuffer.o host_communication.o iso7816_fidi.o #iso7816_uart.o
+C_SIMTRACE = simtrace_iso7816.o usb.o ccid.o sniffer.o phone.o mitm.o ringbuffer.o host_communication.o iso7816_fidi.o tc_etu.o req_ctx.o #iso7816_uart.o
C_APPLEVEL = main.o
C_OBJECTS = $(C_CMSIS) $(C_LOWLEVEL) $(C_LIBLEVEL) $(C_APPLEVEL) $(C_CCID) $(C_SIMTRACE)
diff --git a/firmware/src_simtrace/card_emu.c b/firmware/src_simtrace/card_emu.c
new file mode 100644
index 0000000..cf82b72
--- /dev/null
+++ b/firmware/src_simtrace/card_emu.c
@@ -0,0 +1,694 @@
+/* ISO7816-3 state machine for the card side */
+/* (C) 2010-2015 by Harald Welte <hwelte@hmw-consulting.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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "utils.h"
+#include "trace.h"
+#include "iso7816_fidi.h"
+#include "tc_etu.h"
+#include "card_emu.h"
+#include "req_ctx.h"
+#include "cardemu_prot.h"
+
+
+#define NUM_SLOTS 2
+
+#define ISO7816_3_INIT_WTIME 9600
+#define ISO7816_3_DEFAULT_WI 10
+#define ISO7816_3_ATR_LEN_MAX (1+32) /* TS plus 32 chars */
+
+#define ISO7816_3_PB_NULL 0x60
+
+enum iso7816_3_card_state {
+ ISO_S_WAIT_POWER, /* waiting for power being applied */
+ ISO_S_WAIT_CLK, /* waiting for clock being applied */
+ ISO_S_WAIT_RST, /* waiting for reset being released */
+ ISO_S_WAIT_ATR, /* waiting for start of ATR */
+ ISO_S_IN_ATR, /* transmitting ATR to reader */
+ ISO_S_IN_PTS, /* transmitting ATR to reader */
+ ISO_S_WAIT_TPDU, /* waiting for data from reader */
+ ISO_S_IN_TPDU, /* inside a TPDU */
+};
+
+/* detailed sub-states of ISO_S_IN_PTS */
+enum pts_state {
+ PTS_S_WAIT_REQ_PTSS,
+ PTS_S_WAIT_REQ_PTS0,
+ PTS_S_WAIT_REQ_PTS1,
+ PTS_S_WAIT_REQ_PTS2,
+ PTS_S_WAIT_REQ_PTS3,
+ PTS_S_WAIT_REQ_PCK,
+ PTS_S_WAIT_RESP_PTSS = PTS_S_WAIT_REQ_PTSS | 0x10,
+ PTS_S_WAIT_RESP_PTS0 = PTS_S_WAIT_REQ_PTS0 | 0x10,
+ PTS_S_WAIT_RESP_PTS1 = PTS_S_WAIT_REQ_PTS1 | 0x10,
+ PTS_S_WAIT_RESP_PTS2 = PTS_S_WAIT_REQ_PTS2 | 0x10,
+ PTS_S_WAIT_RESP_PTS3 = PTS_S_WAIT_REQ_PTS3 | 0x10,
+ PTS_S_WAIT_RESP_PCK = PTS_S_WAIT_REQ_PCK | 0x10,
+};
+
+#define _PTSS 0
+#define _PTS0 1
+#define _PTS1 2
+#define _PTS2 3
+#define _PTS3 4
+#define _PCK 5
+
+enum tpdu_state {
+ TPDU_S_WAIT_CLA,
+ TPDU_S_WAIT_INS,
+ TPDU_S_WAIT_P1,
+ TPDU_S_WAIT_P2,
+ TPDU_S_WAIT_P3,
+ TPDU_S_WAIT_PB, /* waiting for Tx of procedure byte */
+ TPDU_S_WAIT_RX, /* waiitng for more data from reader */
+ TPDU_S_WAIT_TX, /* waiting for more data to reader */
+};
+
+#define _CLA 0
+#define _INS 1
+#define _P1 2
+#define _P2 3
+#define _P3 4
+
+struct card_handle {
+ enum iso7816_3_card_state state;
+
+ /* signal levels */
+ uint8_t vcc_active; /* 1 = on, 0 = off */
+ uint8_t in_reset; /* 1 = RST low, 0 = RST high */
+ uint8_t clocked; /* 1 = active, 0 = inactive */
+
+ /* timing */
+ uint8_t fi;
+ uint8_t di;
+ uint8_t wi;
+
+ uint8_t tc_chan; /* TC channel number */
+ uint8_t uart_chan; /* UART channel */
+
+ uint32_t waiting_time; /* in clocks */
+
+ /* ATR state machine */
+ struct {
+ uint8_t idx;
+ uint8_t len;
+ //uint8_t hist_len;
+ //uint8_t last_td;
+ uint8_t atr[ISO7816_3_ATR_LEN_MAX];
+ } atr;
+
+ /* PPS / PTS support */
+ struct {
+ enum pts_state state;
+ uint8_t req[6];
+ uint8_t resp[6];
+ } pts;
+
+ /* TPDU */
+ struct {
+ enum tpdu_state state;
+ uint8_t hdr[5];
+ } tpdu;
+
+ struct req_ctx *uart_rx_ctx;
+ struct req_ctx *uart_tx_ctx;
+
+ struct {
+ uint32_t tx_bytes;
+ uint32_t rx_bytes;
+ uint32_t pps;
+ } stats;
+};
+
+static int update_fidi(struct card_handle *ch)
+{
+ int rc;
+
+ rc = compute_fidi_ratio(ch->fi, ch->di);
+ if (rc > 0 && rc < 0x400) {
+ TRACE_DEBUG("computed Fi(%u) Di(%u) ratio: %d\n",
+ ch->fi, ch->di, rc);
+ /* make sure UART uses new F/D ratio */
+ card_emu_uart_update_fidi(ch->uart_chan, rc);
+ /* notify ETU timer about this */
+ tc_etu_set_etu(ch->tc_chan, rc);
+ } else
+ TRACE_DEBUG("computed FiDi ration %d unsupported\n", rc);
+}
+
+/* Update the ISO 7816-3 TPDU receiver state */
+static void card_set_state(struct card_handle *ch,
+ enum iso7816_3_card_state new_state)
+{
+ switch (new_state) {
+ 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);
+ break;
+ case ISO_S_WAIT_ATR:
+ /* Reset to initial Fi / Di ratio */
+ ch->fi = 1;
+ ch->di = 1;
+ update_fidi(ch);
+ /* initialize todefault WI, this will be overwritten if we
+ * receive 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;
+ tc_etu_set_wtime(ch->tc_chan, ch->waiting_time);
+ /* Set ATR sub-state to initial state */
+ ch->atr.idx = 0;
+ //set_atr_state(ch, ATR_S_WAIT_TS);
+ /* Notice that we are just coming out of reset */
+ //ch->sh.flags |= SIMTRACE_FLAG_ATR;
+ card_emu_uart_enable(ch->uart_chan, ENABLE_TX);
+ break;
+ break;
+ case ISO_S_WAIT_TPDU:
+ /* enable the receiver, disable transmitter */
+ card_emu_uart_enable(ch->uart_chan, ENABLE_RX);
+ break;
+ case ISO_S_IN_ATR:
+ case ISO_S_IN_PTS:
+ case ISO_S_IN_TPDU:
+ /* do nothing */
+ break;
+ }
+
+ if (ch->state == new_state)
+ return;
+
+ TRACE_DEBUG("7816 card state %u -> %u\n", ch->state, new_state);
+ ch->state = new_state;
+}
+
+
+/**********************************************************************
+ * PTS / PPS handling
+ **********************************************************************/
+
+/* Update the ATR sub-state */
+static void set_pts_state(struct card_handle *ch, enum pts_state new_ptss)
+{
+ TRACE_DEBUG("7816 PTS state %u -> %u\n", ch->pts.state, new_ptss);
+ ch->pts.state = new_ptss;
+}
+
+/* Determine the next PTS state */
+static enum pts_state next_pts_state(struct card_handle *ch)
+{
+ uint8_t is_resp = ch->pts.state & 0x10;
+ uint8_t sstate = ch->pts.state & 0x0f;
+ uint8_t *pts_ptr;
+
+ if (!is_resp)
+ pts_ptr = ch->pts.req;
+ else
+ pts_ptr = ch->pts.resp;
+
+ switch (sstate) {
+ case PTS_S_WAIT_REQ_PTSS:
+ goto from_ptss;
+ case PTS_S_WAIT_REQ_PTS0:
+ goto from_pts0;
+ case PTS_S_WAIT_REQ_PTS1:
+ goto from_pts1;
+ case PTS_S_WAIT_REQ_PTS2:
+ goto from_pts2;
+ case PTS_S_WAIT_REQ_PTS3:
+ goto from_pts3;
+ }
+
+ if (ch->pts.state == PTS_S_WAIT_REQ_PCK)
+ return PTS_S_WAIT_RESP_PTSS;
+
+from_ptss:
+ return PTS_S_WAIT_REQ_PTS0 | is_resp;
+from_pts0:
+ if (pts_ptr[_PTS0] & (1 << 4))
+ return PTS_S_WAIT_REQ_PTS1 | is_resp;
+from_pts1:
+ if (pts_ptr[_PTS0] & (1 << 5))
+ return PTS_S_WAIT_REQ_PTS2 | is_resp;
+from_pts2:
+ if (pts_ptr[_PTS0] & (1 << 6))
+ return PTS_S_WAIT_REQ_PTS3 | is_resp;
+from_pts3:
+ return PTS_S_WAIT_REQ_PCK | is_resp;
+}
+
+
+static enum iso7816_3_card_state
+process_byte_pts(struct card_handle *ch, uint8_t byte)
+{
+ switch (ch->pts.state) {
+ case PTS_S_WAIT_REQ_PTSS:
+ ch->pts.req[_PTSS] = byte;
+ break;
+ case PTS_S_WAIT_REQ_PTS0:
+ ch->pts.req[_PTS0] = byte;
+ break;
+ case PTS_S_WAIT_REQ_PTS1:
+ ch->pts.req[_PTS1] = byte;
+ break;
+ case PTS_S_WAIT_REQ_PTS2:
+ ch->pts.req[_PTS2] = byte;
+ break;
+ case PTS_S_WAIT_REQ_PTS3:
+ ch->pts.req[_PTS3] = byte;
+ break;
+ case PTS_S_WAIT_REQ_PCK:
+ ch->pts.req[_PCK] = byte;
+ /* FIXME: check PCK */
+ memcpy(ch->pts.resp, ch->pts.req, sizeof(ch->pts.resp));
+ break;
+ }
+ /* calculate the next state and set it */
+ set_pts_state(ch, next_pts_state(ch));
+
+ return ISO_S_IN_PTS;
+}
+
+/* return a single byte to be transmitted to the reader */
+static int get_byte_pps(struct card_handle *ch, uint8_t *byte)
+{
+ /* FIXME */
+#if 0
+ switch (ch->pts.state) {
+ case PTS_S_WAIT_RESP_PTSS:
+ ch->pts.resp[_PTSS] = byte;
+ break;
+ case PTS_S_WAIT_RESP_PTS0:
+ ch->pts.resp[_PTS0] = byte;
+ break;
+ case PTS_S_WAIT_RESP_PTS1:
+ /* This must be TA1 */
+ ch->fi = byte >> 4;
+ ch->di = byte & 0xf;
+ TRACE_DEBUG("found Fi=%u Di=%u\n", ch->fi, ch->di);
+ ch->sh.flags |= SIMTRACE_FLAG_PPS_FIDI;
+ ch->pts.resp[_PTS1] = byte;
+ break;
+ case PTS_S_WAIT_RESP_PTS2:
+ ch->pts.resp[_PTS2] = byte;
+ break;
+ case PTS_S_WAIT_RESP_PTS3:
+ ch->pts.resp[_PTS3] = byte;
+ break;
+ case PTS_S_WAIT_RESP_PCK:
+ ch->pts.resp[_PCK] = byte;
+ /* FIXME: check PCK */
+ set_pts_state(ch, PTS_S_WAIT_REQ_PTSS);
+ /* update baud rate generator with Fi/Di */
+ update_fidi(ch);
+ /* Wait for the next TPDU */
+ card_set_state(ch, ISO_S_WAIT_TPDU);
+ }
+#endif
+ /* calculate the next state and set it */
+ set_pts_state(ch, next_pts_state(ch));
+
+ return 0;
+}
+
+
+/**********************************************************************
+ * TPDU handling
+ **********************************************************************/
+
+/* add a just-received TPDU byte (from reader) to USB buffer */
+static void add_tpdu_byte(struct card_handle *ch, uint8_t byte)
+{
+ struct req_ctx *rctx;
+ struct cardemu_usb_msg_rx_data *rd;
+
+ /* ensure we have a buffer */
+ if (!ch->uart_rx_ctx) {
+ ch->uart_rx_ctx = req_ctx_find_get(1, RCTX_S_FREE, RCTX_S_UART_RX_BUSY);
+ if (!ch->uart_rx_ctx)
+ return;
+ rd = (struct cardemu_usb_msg_rx_data *) ch->uart_rx_ctx->data;
+ cardemu_hdr_set(&rd->hdr, CEMU_USB_MSGT_DO_RX_DATA);
+ rctx->tot_len = sizeof(*rd);
+ rctx->idx = 0;
+ } else
+ rctx = ch->uart_rx_ctx;
+
+ rd = (struct cardemu_usb_msg_rx_data *) rctx->data;
+
+ rd->data[rctx->idx++] = byte;
+ rctx->tot_len++;
+
+ /* check if the buffer is full. If so, send it */
+ if (rctx->tot_len >= rctx->size) {
+ /* store length of data payload fild in header */
+ rd->hdr.data_len = rctx->idx;
+ req_ctx_set_state(rctx, RCTX_S_USB_TX_PENDING);
+ ch->uart_rx_ctx = NULL;
+ /* FIXME: call into USB code to see if this buffer can
+ * be transmitted now */
+ }
+}
+
+static void set_tpdu_state(struct card_handle *ch, enum tpdu_state new_ts)
+{
+ TRACE_DEBUG("7816 TPDU state %u -> %u\n", ch->tpdu.state, new_ts);
+ switch (new_ts) {
+ case TPDU_S_WAIT_CLA:
+ card_emu_uart_enable(ch->uart_chan, ENABLE_RX);
+ 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);
+ break;
+ }
+ ch->tpdu.state = new_ts;
+}
+
+static enum tpdu_state next_tpdu_state(struct card_handle *ch)
+{
+ switch (ch->tpdu.state) {
+ case TPDU_S_WAIT_CLA:
+ return TPDU_S_WAIT_INS;
+ case TPDU_S_WAIT_INS:
+ return TPDU_S_WAIT_P1;
+ case TPDU_S_WAIT_P1:
+ return TPDU_S_WAIT_P2;
+ case TPDU_S_WAIT_P2:
+ return TPDU_S_WAIT_P3;
+ case TPDU_S_WAIT_P3:
+ return TPDU_S_WAIT_PB;
+ /* simply stay in Rx or Tx by default */
+ case TPDU_S_WAIT_PB:
+ return TPDU_S_WAIT_PB;
+ case TPDU_S_WAIT_RX:
+ return TPDU_S_WAIT_RX;
+ case TPDU_S_WAIT_TX:
+ return TPDU_S_WAIT_TX;
+ }
+}
+
+static void send_tpdu_header(struct card_handle *ch)
+{
+ struct req_ctx *rctx;
+ struct cardemu_usb_msg_rx_data *rd;
+
+ /* if we already/still have a context, send it off */
+ if (ch->uart_rx_ctx && rctx->idx) {
+ ch->uart_rx_ctx = NULL;
+ }
+
+ /* ensure we have a new buffer */
+ ch->uart_rx_ctx = req_ctx_find_get(1, RCTX_S_FREE, RCTX_S_UART_RX_BUSY);
+ if (!ch->uart_rx_ctx)
+ return;
+ rctx = ch->uart_rx_ctx;
+ rd = (struct cardemu_usb_msg_rx_data *) rctx->data;
+
+ /* initializ header */
+ cardemu_hdr_set(&rd->hdr, CEMU_USB_MSGT_DO_RX_DATA);
+ rd->flags = CEMU_DATA_F_TPDU_HDR;
+ rctx->tot_len = sizeof(*rd) + sizeof(ch->tpdu.hdr);
+ rctx->idx = 0;
+
+ /* copy TPDU header to data field */
+ memcpy(rd->data, ch->tpdu.hdr, sizeof(ch->tpdu.hdr));
+ rd->hdr.data_len = sizeof(ch->tpdu.hdr);
+
+ req_ctx_set_state(rctx, RCTX_S_USB_TX_PENDING);
+}
+
+static enum iso7816_3_card_state
+process_byte_tpdu(struct card_handle *ch, uint8_t byte)
+{
+ switch (ch->tpdu.state) {
+ case TPDU_S_WAIT_CLA:
+ ch->tpdu.hdr[_CLA] = byte;
+ break;
+ case TPDU_S_WAIT_INS:
+ ch->tpdu.hdr[_INS] = byte;
+ break;
+ case TPDU_S_WAIT_P1:
+ ch->tpdu.hdr[_P1] = byte;
+ break;
+ case TPDU_S_WAIT_P2:
+ ch->tpdu.hdr[_P2] = byte;
+ break;
+ case TPDU_S_WAIT_P3:
+ ch->tpdu.hdr[_P3] = byte;
+ /* FIXME: start timer to transmit further 0x60 */
+ /* send the TPDU header as part of a procedure byte
+ * request to the USB host */
+ send_tpdu_header(ch);
+ break;
+ case TPDU_S_WAIT_RX:
+ add_tpdu_byte(ch, byte);
+ break;
+ default:
+ TRACE_DEBUG("process_byte_tpdu() in invalid state %u\n",
+ ch->tpdu.state);
+ }
+ set_tpdu_state(ch, next_tpdu_state(ch));
+
+ /* ensure we stay in TPDU ISO state */
+ return ISO_S_IN_TPDU;
+}
+
+/* return a single byte to be transmitted to the reader */
+static int get_byte_tpdu(struct card_handle *ch, uint8_t *byte)
+{
+ struct req_ctx *rctx;
+ struct cardemu_usb_msg_tx_data *td;
+
+ /* ensure we are aware of any data that might be pending for
+ * transmit */
+ if (!ch->uart_tx_ctx) {
+ ch->uart_tx_ctx = req_ctx_find_get(1, RCTX_S_UART_TX_PENDING,
+ RCTX_S_UART_TX_BUSY);
+ if (!ch->uart_tx_ctx)
+ return 0;
+
+ /* start with index zero */
+ ch->uart_tx_ctx->idx = 0;
+
+ }
+ rctx = ch->uart_tx_ctx;
+ td = (struct cardemu_usb_msg_tx_data *) rctx->data;
+
+#if 0
+ /* this must happen _after_ the byte has been transmittd */
+ switch (ch->tpdu.state) {
+ case TPDU_S_WAIT_PB:
+ if (td->flags & CEMU_DATA_F_PB_AND_TX)
+ set_tpdu_state(ch, TPDU_S_WAIT_TX);
+ else if (td->flags & CEMU_DATA_F_PB_AND_RX)
+ set_tpdu_state(ch, TPDU_S_WAIT_RX);
+ break;
+ }
+#endif
+
+ /* take the next pending byte out of the rctx */
+ *byte = td->data[rctx->idx++];
+
+ /* check if the buffer has now been fully transmitted */
+ if ((rctx->idx >= td->hdr.data_len) ||
+ (rctx->idx + sizeof(*td) - sizeof(td->hdr) >= rctx->tot_len)) {
+ req_ctx_set_state(rctx, RCTX_S_FREE);
+ ch->uart_tx_ctx = NULL;
+ /* FIXME: call into USB code to chec if we need to
+ * submit a free buffer to accept furthe data on bulk
+ * out endpoint */
+ }
+
+ return 1;
+}
+
+/**********************************************************************
+ * Public API
+ **********************************************************************/
+
+/* process a single byte received from the reader */
+void card_emu_process_rx_byte(struct card_handle *ch, uint8_t byte)
+{
+ int new_state = -1;
+
+ ch->stats.rx_bytes++;
+
+ switch (ch->state) {
+ case ISO_S_WAIT_POWER:
+ case ISO_S_WAIT_CLK:
+ case ISO_S_WAIT_RST:
+ case ISO_S_WAIT_ATR:
+ /* we shouldn't receive any data from the reader yet! */
+ break;
+ case ISO_S_WAIT_TPDU:
+ if (byte == 0xff) {
+ new_state = process_byte_pts(ch, byte);
+ ch->stats.pps++;
+ goto out_silent;
+ }
+ /* fall-through */
+ case ISO_S_IN_TPDU:
+ new_state = process_byte_tpdu(ch, byte);
+ break;
+ case ISO_S_IN_PTS:
+ new_state = process_byte_pts(ch, byte);
+ goto out_silent;
+ }
+
+out_silent:
+ if (new_state != -1)
+ card_set_state(ch, new_state);
+}
+
+/* return a single byte to be transmitted to the reader */
+int card_emu_get_tx_byte(struct card_handle *ch, uint8_t *byte)
+{
+ int rc = 0;
+
+ switch (ch->state) {
+ case ISO_S_IN_ATR:
+ if (ch->atr.idx < ch->atr.len) {
+ *byte = ch->atr.atr[ch->atr.idx++];
+ rc = 1;
+ /* detect end of ATR */
+ if (ch->atr.idx >= ch->atr.len)
+ card_set_state(ch, ISO_S_WAIT_TPDU);
+ }
+ break;
+ case ISO_S_IN_PTS:
+ rc = get_byte_pps(ch, byte);
+ break;
+ case ISO_S_IN_TPDU:
+ rc = get_byte_tpdu(ch, byte);
+ break;
+ }
+
+ if (rc)
+ ch->stats.tx_bytes++;
+
+ return rc;
+}
+
+/* hardware driver informs us that a card I/O signal has changed */
+void card_emu_io_statechg(struct card_handle *ch, enum card_io io, int active)
+{
+ switch (io) {
+ case CARD_IO_VCC:
+ if (active == 0)
+ card_set_state(ch, ISO_S_WAIT_POWER);
+ else if (active == 1 && ch->vcc_active == 0)
+ card_set_state(ch, ISO_S_WAIT_CLK);
+ ch->vcc_active = active;
+ break;
+ case CARD_IO_CLK:
+ if (active == 1 && ch->state == ISO_S_WAIT_CLK)
+ card_set_state(ch, ISO_S_WAIT_RST);
+ ch->clocked = active;
+ break;
+ case CARD_IO_RST:
+ if (active == 0 && ch->in_reset &&
+ ch->vcc_active && ch->clocked) {
+ /* FIXME: wait 400 clocks */
+ //card_set_state(ch, ISO_S_WAIT_ATR);
+ card_set_state(ch, ISO_S_IN_ATR);
+ }
+ ch->in_reset = active;
+ break;
+ }
+}
+
+/* User sets a new ATR to be returned during next card reset */
+int card_emu_set_atr(struct card_handle *ch, const uint8_t *atr, uint8_t len)
+{
+ if (len > sizeof(ch->atr.atr))
+ return -1;
+
+ memcpy(ch->atr.atr, atr, len);
+ ch->atr.len = len;
+ ch->atr.idx = 0;
+
+ /* FIXME: race condition with trasmitting ATR to reader? */
+
+ return 0;
+}
+
+/* hardware driver informs us that one (more) ETU has expired */
+void tc_etu_wtime_half_expired(void *handle)
+{
+ struct card_handle *ch = handle;
+ /* transmit NULL procedure byte well before waiting time expires */
+ card_emu_uart_tx(ch->uart_chan, ISO7816_3_PB_NULL);
+}
+
+/* hardware driver informs us that one (more) ETU has expired */
+void tc_etu_wtime_expired(void *handle)
+{
+}
+
+/* shortest ATR found in smartcard_list.txt */
+static const uint8_t default_atr[] = { 0x3B, 0x02, 0x14, 0x50 };
+
+static struct card_handle card_handles[NUM_SLOTS];
+
+struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uart_chan)
+{
+ struct card_handle *ch;
+
+ if (slot_num >= ARRAY_SIZE(card_handles))
+ return NULL;
+
+ ch = &card_handles[slot_num];
+
+ memset(ch, 0, sizeof(*ch));
+
+ /* initialize the card_handle with reasonabe defaults */
+ ch->state = ISO_S_WAIT_POWER;
+ ch->vcc_active = 0;
+ ch->in_reset = 1;
+ ch->clocked = 0;
+
+ ch->fi = 0;
+ ch->di = 1;
+ ch->wi = ISO7816_3_DEFAULT_WI;
+
+ 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);
+
+ ch->pts.state = PTS_S_WAIT_REQ_PTSS;
+ ch->tpdu.state = TPDU_S_WAIT_CLA;
+
+ tc_etu_init(ch->tc_chan, ch);
+
+ return ch;
+}
diff --git a/firmware/src_simtrace/card_emu.h b/firmware/src_simtrace/card_emu.h
new file mode 100644
index 0000000..c313356
--- /dev/null
+++ b/firmware/src_simtrace/card_emu.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <stdint.h>
+
+struct card_handle;
+
+enum card_io {
+ CARD_IO_VCC,
+ CARD_IO_RST,
+ CARD_IO_CLK,
+};
+
+struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uart_chan);
+
+/* process a single byte received from the reader */
+void card_emu_process_rx_byte(struct card_handle *ch, uint8_t byte);
+
+/* return a single byte to be transmitted to the reader */
+int card_emu_get_tx_byte(struct card_handle *ch, uint8_t *byte);
+
+/* hardware driver informs us that a card I/O signal has changed */
+void card_emu_io_statechg(struct card_handle *ch, enum card_io io, int active);
+
+/* User sets a new ATR to be returned during next card reset */
+int card_emu_set_atr(struct card_handle *ch, const uint8_t *atr, uint8_t len);
+
+
+#define ENABLE_TX 0x01
+#define ENABLE_RX 0x02
+
+int card_emu_uart_update_fidi(uint8_t uart_chan, unsigned int fidi);
+int card_emu_uart_tx(uint8_t uart_chan, uint8_t byte);
+void card_emu_uart_enable(uint8_t uart_chan, uint8_t rxtx);
diff --git a/firmware/src_simtrace/cardemu_prot.h b/firmware/src_simtrace/cardemu_prot.h
new file mode 100644
index 0000000..2d41c01
--- /dev/null
+++ b/firmware/src_simtrace/cardemu_prot.h
@@ -0,0 +1,121 @@
+#pragma once
+
+/* Smart Card Emulation USB protocol */
+
+/* (C) 2015 by Harald Welte <hwelte@hmw-consulting.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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdint.h>
+
+/* DT = Device Terminated. DO = Device Originated */
+enum cardemu_usb_msg_type {
+ /* Bulk out pipe */
+ CEMU_USB_MSGT_DT_TX_DATA, /* TPDU Date */
+ CEMU_USB_MSGT_DT_SET_ATR, /* Set the ATR stored in simulator */
+ CEMU_USB_MSGT_DT_GET_STATS, /* request DO_STATS */
+ CEMU_USB_MSGT_DT_GET_STATUS, /* request DO_STATUS */
+
+ /* Bulk in pipe */
+ CEMU_USB_MSGT_DO_RX_DATA, /* TPDU data */
+ CEMU_USB_MSGT_DO_STATUS, /* Status information */
+ CEMU_USB_MSGT_DO_STATS, /* Statistics */
+ CEMU_USB_MSGT_DO_PTS, /* Information about PTS */
+ CEMU_USB_MSGT_DO_ERROR, /* Error message */
+};
+
+/* generic header, shared by all messages */
+struct cardemu_usb_msg_hdr {
+ uint8_t msg_type; /* enum cardemu_usb_msg_type */
+ uint8_t seq_nr; /* sequence number */
+ uint16_t data_len; /* length of optional data field */
+} __attribute__ ((packed));
+
+/* indicates a TPDU header is present in this message */
+#define CEMU_DATA_F_TPDU_HDR 0x00000001
+/* indicates last part of transmission in this direction */
+#define CEMU_DATA_F_FINAL 0x00000002
+/* incdicates a PB is present and we should continue with TX */
+#define CEMU_DATA_F_PB_AND_TX 0x00000004
+/* incdicates a PB is present and we should continue with RX */
+#define CEMU_DATA_F_PB_AND_RX 0x00000008
+
+/* CEMU_USB_MSGT_DT_SET_ATR */
+struct cardemu_usb_msg_set_atr {
+ struct cardemu_usb_msg_hdr hdr;
+ /* variable-length ATR data (hdr.data_len) */
+ uint8_t atr[0];
+} __attribute__ ((packed));
+
+/* CEMU_USB_MSGT_DT_TX_DATA */
+struct cardemu_usb_msg_tx_data {
+ struct cardemu_usb_msg_hdr hdr;
+ uint32_t flags;
+ /* variable-length TPDU data (hdr.data_len) */
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+/* CEMU_USB_MSGT_DO_RX_DATA */
+struct cardemu_usb_msg_rx_data {
+ struct cardemu_usb_msg_hdr hdr;
+ uint32_t flags;
+ /* variable-length TPDU data (hdr.data_len) */
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+#define CEMU_STATUS_F_VCC_PRESENT 0x00000001
+#define CEMU_STATUS_F_CLK_ACTIVE 0x00000002
+#define CEMU_STATUS_F_RCEMU_ACTIVE 0x00000004
+#define CEMU_STATUS_F_CARD_INSERT 0x00000008
+
+/* CEMU_USB_MSGT_DO_STATUS */
+struct cardemu_usb_msg_status {
+ struct cardemu_usb_msg_hdr hdr;
+ 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;
+} __attribute__ ((packed));
+
+/* CEMU_USB_MSGT_DO_PTS */
+struct cardemu_usb_msg_pts_info {
+ struct cardemu_usb_msg_hdr hdr;
+ /* PTS request as sent from reader */
+ uint8_t req[6];
+ /* PTS response as sent by card */
+ uint8_t resp[6];
+} __attribute__ ((packed));
+
+/* CEMU_USB_MSGT_DO_ERROR */
+struct cardemu_usb_msg_error {
+ struct cardemu_usb_msg_hdr hdr;
+ uint8_t severity;
+ uint8_t subsystem;
+ uint16_t code;
+ /* human-readable error message */
+ uint8_t msg[0];
+} __attribute__ ((packed));
+
+static inline void cardemu_hdr_set(struct cardemu_usb_msg_hdr *hdr, uint16_t msgt)
+{
+ memset(hdr, 0, sizeof(*hdr));
+ hdr->msg_type = msgt;
+}
diff --git a/firmware/src_simtrace/req_ctx.c b/firmware/src_simtrace/req_ctx.c
new file mode 100644
index 0000000..2759b55
--- /dev/null
+++ b/firmware/src_simtrace/req_ctx.c
@@ -0,0 +1,210 @@
+/* USB Request Context for OpenPCD / OpenPICC / SIMtrace
+ * (C) 2006-2015 by Harald Welte <hwelte@hmw-consulting.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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "utils.h"
+#include "trace.h"
+#include "req_ctx.h"
+
+#define NUM_RCTX_SMALL 0
+#define NUM_RCTX_LARGE 20
+
+#define NUM_REQ_CTX (NUM_RCTX_SMALL+NUM_RCTX_LARGE)
+
+static uint8_t rctx_data[NUM_RCTX_SMALL][RCTX_SIZE_SMALL];
+static uint8_t rctx_data_large[NUM_RCTX_LARGE][RCTX_SIZE_LARGE];
+
+static struct req_ctx req_ctx[NUM_REQ_CTX];
+
+#ifdef REQ_CTX_LISTS
+/* queue of RCTX indexed by their current state */
+static struct req_ctx *req_ctx_queues[RCTX_STATE_COUNT], *req_ctx_tails[RCTX_STATE_COUNT];
+static unsigned req_counts[RCTX_STATE_COUNT];
+#endif
+
+struct req_ctx __ramfunc *
+req_ctx_find_get(int large, uint32_t old_state, uint32_t new_state)
+{
+ struct req_ctx *toReturn = NULL;
+ unsigned long flags;
+
+ if (old_state >= RCTX_STATE_COUNT || new_state >= RCTX_STATE_COUNT) {
+ TRACE_DEBUG("Invalid parameters for req_ctx_find_get");
+ return NULL;
+ }
+ local_irq_save(flags);
+#ifdef REQ_CTX_LISTS
+ toReturn = req_ctx_queues[old_state];
+ if (toReturn) {
+ if ((req_ctx_queues[old_state] = toReturn->next))
+ toReturn->next->prev = NULL;
+ else
+ req_ctx_tails[old_state] = NULL;
+ req_counts[old_state]--;
+ if ((toReturn->prev = req_ctx_tails[new_state]))
+ toReturn->prev->next = toReturn;
+ else
+ req_ctx_queues[new_state] = toReturn;
+ req_ctx_tails[new_state] = toReturn;
+ toReturn->next = NULL;
+ req_counts[new_state]++;
+ toReturn->state = new_state;
+ }
+#else
+ {
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(req_ctx); i++) {
+ if (req_ctx[i].state == old_state) {
+ toReturn = &req_ctx[i];
+ toReturn->state = new_state;
+ }
+ }
+ }
+#endif
+ local_irq_restore(flags);
+ return toReturn;
+}
+
+uint8_t req_ctx_num(struct req_ctx *ctx)
+{
+ return ctx - req_ctx;
+}
+
+void req_ctx_set_state(struct req_ctx *ctx, uint32_t new_state)
+{
+ unsigned long flags;
+ unsigned old_state;
+
+ TRACE_DEBUG("rctx_set_state(ctx=%p, new_state=%u)\n", ctx, new_state);
+
+ if (new_state >= RCTX_STATE_COUNT) {
+ TRACE_DEBUG("Invalid new_state for req_ctx_set_state\n");
+ return;
+ }
+ local_irq_save(flags);
+ old_state = ctx->state;
+#ifdef REQ_CTX_LISTS
+ if (ctx->prev)
+ ctx->prev->next = ctx->next;
+ else
+ req_ctx_queues[old_state] = ctx->next;
+ if (ctx->next)
+ ctx->next->prev = ctx->prev;
+ else
+ req_ctx_tails[old_state] = ctx->prev;
+ req_counts[old_state]--;
+ if ((ctx->prev = req_ctx_tails[new_state]))
+ ctx->prev->next = ctx;
+ else
+ req_ctx_queues[new_state] = ctx;
+ req_ctx_tails[new_state] = ctx;
+ ctx->next = NULL;
+ req_counts[new_state]++;
+#endif
+ ctx->state = new_state;
+ local_irq_restore(flags);
+}
+
+#ifdef DEBUG_REQCTX
+void req_print(int state) {
+ int count = 0;
+ struct req_ctx *ctx, *last = NULL;
+ DEBUGP("State [%02i] start <==> ", state);
+ ctx = req_ctx_queues[state];
+ while (ctx) {
+ if (last != ctx->prev)
+ DEBUGP("*INV_PREV* ");
+ DEBUGP("%08X => ", ctx);
+ last = ctx;
+ ctx = ctx->next;
+ count++;
+ if (count > NUM_REQ_CTX) {
+ DEBUGP("*WILD POINTER* => ");
+ break;
+ }
+ }
+ TRACE_DEBUG("NULL");
+ if (!req_ctx_queues[state] && req_ctx_tails[state]) {
+ TRACE_DEBUG("NULL head, NON-NULL tail");
+ }
+ if (last != req_ctx_tails[state]) {
+ TRACE_DEBUG("Tail does not match last element");
+ }
+}
+#endif
+
+void req_ctx_put(struct req_ctx *ctx)
+{
+ return req_ctx_set_state(ctx, RCTX_S_FREE);
+}
+
+#ifdef REQ_CTX_LISTS
+unsigned int req_ctx_count(uint32_t state)
+{
+ if (state >= RCTX_STATE_COUNT)
+ return 0;
+ return req_counts[state];
+}
+#endif
+
+void req_ctx_init(void)
+{
+ int i;
+ for (i = 0; i < NUM_RCTX_SMALL; i++) {
+#ifdef REQ_CTX_LISTS
+ req_ctx[i].prev = req_ctx + i - 1;
+ req_ctx[i].next = req_ctx + i + 1;
+#endif
+ req_ctx[i].size = RCTX_SIZE_SMALL;
+ req_ctx[i].tot_len = 0;
+ req_ctx[i].data = rctx_data[i];
+ req_ctx[i].state = RCTX_S_FREE;
+ TRACE_DEBUG("SMALL req_ctx[%02i] initialized at %08X, Data: %08X => %08X\n",
+ i, req_ctx + i, req_ctx[i].data, req_ctx[i].data + RCTX_SIZE_SMALL);
+ }
+
+ for (; i < NUM_REQ_CTX; i++) {
+#ifdef REQ_CTX_LISTS
+ req_ctx[i].prev = req_ctx + i - 1;
+ req_ctx[i].next = req_ctx + i + 1;
+#endif
+ req_ctx[i].size = RCTX_SIZE_LARGE;
+ req_ctx[i].tot_len = 0;
+ req_ctx[i].data = rctx_data_large[i];
+ req_ctx[i].state = RCTX_S_FREE;
+ TRACE_DEBUG("LARGE req_ctx[%02i] initialized at %08X, Data: %08X => %08X\n",
+ i, req_ctx + i, req_ctx[i].data, req_ctx[i].data + RCTX_SIZE_LARGE);
+ }
+#ifdef REQ_CTX_LISTS
+ req_ctx[0].prev = NULL;
+ req_ctx[NUM_REQ_CTX - 1].next = NULL;
+
+ req_ctx_queues[RCTX_S_FREE] = req_ctx;
+ req_ctx_tails[RCTX_S_FREE] = req_ctx + NUM_REQ_CTX - 1;
+ req_counts[RCTX_S_FREE] = NUM_REQ_CTX;
+
+ for (i = RCTX_S_FREE + 1; i < RCTX_STATE_COUNT; i++) {
+ req_ctx_queues[i] = req_ctx_tails[i] = NULL;
+ req_counts[i] = 0;
+ }
+#endif
+}
diff --git a/firmware/src_simtrace/req_ctx.h b/firmware/src_simtrace/req_ctx.h
new file mode 100644
index 0000000..21149aa
--- /dev/null
+++ b/firmware/src_simtrace/req_ctx.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#define RCTX_SIZE_LARGE 960
+#define RCTX_SIZE_SMALL 320
+#define MAX_HDRSIZE sizeof(struct openpcd_hdr)
+
+#include <stdint.h>
+
+#define __ramfunc
+
+enum req_ctx_state {
+ RCTX_S_FREE,
+ /* USB -> UART */
+ RCTX_S_USB_RX_BUSY,
+ RCTX_S_MAIN_PROCESSING,
+ RCTX_S_UART_TX_PENDING,
+ RCTX_S_UART_TX_BUSY,
+ /* UART -> USB */
+ RCTX_S_UART_RX_BUSY,
+ RCTX_S_USB_TX_PENDING,
+ RCTX_S_USB_TX_BUSY,
+ /* number of states */
+ RCTX_STATE_COUNT
+};
+
+struct req_ctx {
+ /* enum req_ctx_state */
+ volatile uint32_t state;
+#ifdef REQ_CTX_LISTS
+ /* pointers for queues */
+ volatile struct req_ctx *prev, *next;
+#endif
+ /* size of th 'data' buffer */
+ uint16_t size;
+ /* total number of used bytes in buffer */
+ uint16_t tot_len;
+ /* index into the buffer, user specific */
+ uint16_t idx;
+ /* actual data buffer */
+ uint8_t *data;
+};
+
+extern struct req_ctx __ramfunc *req_ctx_find_get(int large, uint32_t old_state, uint32_t new_state);
+extern struct req_ctx *req_ctx_find_busy(void);
+extern void req_ctx_set_state(struct req_ctx *ctx, uint32_t new_state);
+extern void req_ctx_put(struct req_ctx *ctx);
+extern uint8_t req_ctx_num(struct req_ctx *ctx);
+unsigned int req_ctx_count(uint32_t state);
diff --git a/firmware/src_simtrace/tc_etu.c b/firmware/src_simtrace/tc_etu.c
new file mode 100644
index 0000000..9d302d1
--- /dev/null
+++ b/firmware/src_simtrace/tc_etu.c
@@ -0,0 +1,194 @@
+/* SIMtrace TC (Timer / Clock) code for ETU tracking */
+
+/* (C) 2006-2015 by Harald Welte <hwelte@hmw-consulting.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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdint.h>
+
+#include "utils.h"
+#include "tc_etu.h"
+
+#include "chip.h"
+
+/* pins for Channel 0 of TC-block 0 */
+#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}
+#define PIN_TIOB0 {PIO_PA1, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT}
+static const Pin pins_tc0[] = { PIN_TCLK0, PIN_TIOA0, PIN_TIOB0 };
+
+/* pins for Channel 2 of TC-block 0 */
+#define PIN_TCLK2 {PIO_PA29, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT}
+#define PIN_TIOA2 {PIO_PA26, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT}
+#define PIN_TIOB2 {PIO_PA27, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT}
+static const Pin pins_tc2[] = { PIN_TCLK2, PIN_TIOA2, PIN_TIOB2 };
+
+struct tc_etu_state {
+ /* total negotiated waiting time (default = 9600) */
+ uint16_t waiting_time;
+ /* how many clock cycles per ETU (default = 372) */
+ uint16_t clocks_per_etu;
+ /* how many ETUs does waiting time correspond ? */
+ uint16_t wait_events;
+ /* how many ETUs have we seen expire so far? */
+ uint16_t nr_events;
+ /* channel number */
+ uint8_t chan_nr;
+ /* Timer/Counter register pointer */
+ TcChannel *chan;
+ /* User reference */
+ void *handle;
+};
+
+#define INIT_TE_STATE(n) { \
+ .waiting_time = 9600, \
+ .clocks_per_etu = 372, \
+ .wait_events = 10, \
+ .nr_events = 0, \
+ .chan_nr = n, \
+}
+
+static struct tc_etu_state te_state0 = INIT_TE_STATE(0);
+static struct tc_etu_state te_state2 = INIT_TE_STATE(2);
+
+static struct tc_etu_state *get_te(uint8_t chan_nr)
+{
+ if (chan_nr == 0)
+ return &te_state0;
+ else
+ return &te_state2;
+}
+
+static void tc_etu_irq(struct tc_etu_state *te)
+{
+ uint32_t sr = te->chan->TC_SR;
+
+ if (sr & TC_SR_ETRGS) {
+ /* external trigger, i.e. we have seen a bit on I/O */
+ te->nr_events = 0;
+ }
+
+ if (sr & TC_SR_CPCS) {
+ /* Compare C event has occurred, i.e. 1 ETU expired */
+ 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);
+ }
+ if (te->nr_events >= te->wait_events) {
+ TcChannel *chan = te->chan;
+ chan->TC_CMR |= TC_CMR_ENETRG;
+
+ /* disable and re-enable clock to make it stop */
+ chan->TC_CCR = TC_CCR_CLKDIS;
+ chan->TC_CCR = TC_CCR_CLKEN;
+
+ /* Indicate that the waiting tim has expired */
+ tc_etu_wtime_expired(te->handle);
+ }
+ }
+}
+
+void TC0_IrqHandler(void)
+{
+ tc_etu_irq(&te_state0);
+}
+
+void TC2_IrqHandler(void)
+{
+ tc_etu_irq(&te_state2);
+}
+
+static void recalc_nr_events(struct tc_etu_state *te)
+{
+ te->wait_events = te->waiting_time / 12;
+ te->chan->TC_RC = te->clocks_per_etu * 12;
+}
+
+void tc_etu_set_wtime(uint8_t chan_nr, uint16_t wtime)
+{
+ struct tc_etu_state *te = get_te(chan_nr);
+ te->waiting_time = wtime;
+ recalc_nr_events(te);
+}
+
+void tc_etu_set_etu(uint8_t chan_nr, uint16_t etu)
+{
+ struct tc_etu_state *te = get_te(chan_nr);
+ te->clocks_per_etu = etu;
+ recalc_nr_events(te);
+}
+
+void tc_etu_init(uint8_t chan_nr, void *handle)
+{
+ struct tc_etu_state *te = get_te(chan_nr);
+ uint32_t tc_clks;
+
+ te->handle = handle;
+
+ switch (chan_nr) {
+ case 0:
+ /* Configure PA4(TCLK0), PA0(TIOA0), PA1(TIB0) */
+ PIO_Configure(pins_tc0, ARRAY_SIZE(pins_tc0));
+ PMC_EnablePeripheral(ID_TC0);
+ /* route TCLK0 to XC2 */
+ TC0->TC_BMR &= ~TC_BMR_TC0XC0S_Msk;
+ TC0->TC_BMR |= TC_BMR_TC0XC0S_TCLK0;
+ tc_clks = TC_CMR_TCCLKS_XC0;
+ /* register interrupt handler */
+ NVIC_EnableIRQ(TC0_IRQn);
+
+ te->chan = &TC0->TC_CHANNEL[0];
+ break;
+ case 2:
+ /* Configure PA29(TCLK2), PA26(TIOA2), PA27(TIOB2) */
+ PIO_Configure(pins_tc2, ARRAY_SIZE(pins_tc2));
+ PMC_EnablePeripheral(ID_TC2);
+ /* route TCLK2 to XC2. TC0 really means TCA in this case */
+ TC0->TC_BMR &= ~TC_BMR_TC2XC2S_Msk;
+ TC0->TC_BMR |= TC_BMR_TC2XC2S_TCLK2;
+ tc_clks = TC_CMR_TCCLKS_XC2;
+ /* register interrupt handler */
+ NVIC_EnableIRQ(TC2_IRQn);
+
+ te->chan = &TC0->TC_CHANNEL[2];
+ break;
+ default:
+ return;
+ }
+
+ /* enable interrupts for Compare-C and external trigger */
+ te->chan->TC_IER = TC_IER_CPCS | TC_IER_ETRGS;
+
+ te->chan->TC_CMR = tc_clks | /* XC(TCLK) clock */
+ TC_CMR_WAVE | /* wave mode */
+ TC_CMR_ETRGEDG_FALLING | /* ext trig on falling edge */
+ TC_CMR_EEVT_TIOB | /* ext trig is TIOB0 */
+ TC_CMR_ENETRG | /* enable ext trig */
+ TC_CMR_WAVSEL_UP_RC | /* wave mode up */
+ TC_CMR_ACPA_SET | /* set TIOA on a compare */
+ TC_CMR_ACPC_CLEAR | /* clear TIOA on C compare */
+ TC_CMR_ASWTRG_CLEAR; /* Clear TIOA on sw trig */
+
+ tc_etu_set_etu(chan_nr, 372);
+
+ /* enable master clock for TC */
+ te->chan->TC_CCR = TC_CCR_CLKEN;
+
+ /* Reset to start timers */
+ TC0->TC_BCR = TC_BCR_SYNC;
+}
diff --git a/firmware/src_simtrace/tc_etu.h b/firmware/src_simtrace/tc_etu.h
new file mode 100644
index 0000000..88cabdf
--- /dev/null
+++ b/firmware/src_simtrace/tc_etu.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <stdint.h>
+void tc_etu_set_wtime(uint8_t chan_nr, uint16_t wtime);
+void tc_etu_set_etu(uint8_t chan_nr, uint16_t etu);
+void tc_etu_init(uint8_t chan_nr, void *handle);
+
+extern void tc_etu_wtime_half_expired(void *handle);
+extern void tc_etu_wtime_expired(void *handle);
diff --git a/firmware/src_simtrace/utils.h b/firmware/src_simtrace/utils.h
index 2eff30d..b9af1f6 100644
--- a/firmware/src_simtrace/utils.h
+++ b/firmware/src_simtrace/utils.h
@@ -2,3 +2,17 @@
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#ifdef __ARM
+#define local_irq_save(x) \
+ ({ \
+ x = __get_PRIMASK(); \
+ __disable_irq(); \
+ })
+
+#define local_irq_restore(x) \
+ __set_PRIMASK(x)
+#else
+#warning "local_irq_{save,restore}() not implemented"
+#define local_irq_save(x)
+#define local_irq_restore(x)
+#endif
diff --git a/firmware/test/Makefile b/firmware/test/Makefile
new file mode 100644
index 0000000..eb6d61f
--- /dev/null
+++ b/firmware/test/Makefile
@@ -0,0 +1,13 @@
+CFLAGS=-g -Wall -I../src_simtrace -I.
+
+VPATH=../src_simtrace
+
+card_emu_test: card_emu_tests.hobj card_emu.hobj req_ctx.hobj iso7816_fidi.hobj
+ $(CC) $(LDFLAGS) -o $@ $^
+
+%.hobj: %.c
+ $(CC) $(CFLAGS) -o $@ -c $^
+
+clean:
+ @rm -f *.hobj
+ @rm -f card_emu_test
diff --git a/firmware/test/card_emu_tests.c b/firmware/test/card_emu_tests.c
new file mode 100644
index 0000000..82f22e2
--- /dev/null
+++ b/firmware/test/card_emu_tests.c
@@ -0,0 +1,160 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "card_emu.h"
+#include "cardemu_prot.h"
+#include "tc_etu.h"
+#include "req_ctx.h"
+
+/* stub functions required by card_emu.c */
+
+int card_emu_uart_update_fidi(uint8_t uart_chan, unsigned int fidi)
+{
+ printf("uart_update_fidi(uart_chan=%u, fidi=%u)\n", uart_chan, fidi);
+ return 0;
+}
+
+int card_emu_uart_tx(uint8_t uart_chan, uint8_t byte)
+{
+ printf("TX: 0x%02x\n", byte);
+ return 1;
+}
+
+void card_emu_uart_enable(uint8_t uart_chan, uint8_t rxtx)
+{
+ printf("uart_enable(uart_chan=%u, rxtx=0x%02x)\n", uart_chan, rxtx);
+}
+
+void tc_etu_set_wtime(uint8_t tc_chan, uint16_t wtime)
+{
+ printf("tc_etu_set_wtime(tc_chan=%u, wtime=%u)\n", tc_chan, wtime);
+}
+
+void tc_etu_set_etu(uint8_t tc_chan, uint16_t etu)
+{
+ printf("tc_etu_set_etu(tc_chan=%u, etu=%u)\n", tc_chan, etu);
+}
+
+void tc_etu_init(uint8_t chan_nr, void *handle)
+{
+}
+
+
+
+
+#if 0
+/* process a single byte received from the reader */
+void card_emu_process_rx_byte(struct card_handle *ch, uint8_t byte);
+
+/* return a single byte to be transmitted to the reader */
+int card_emu_get_tx_byte(struct card_handle *ch, uint8_t *byte);
+
+/* hardware driver informs us that a card I/O signal has changed */
+void card_emu_io_statechg(struct card_handle *ch, enum card_io io, int active);
+
+/* User sets a new ATR to be returned during next card reset */
+int card_emu_set_atr(struct card_handle *ch, const uint8_t *atr, uint8_t len);
+#endif
+
+
+static int verify_atr(struct card_handle *ch)
+{
+ uint8_t atr[4];
+ uint8_t byte;
+ unsigned int i;
+
+ printf("receiving + verifying ATR:");
+ for (i = 0; i < sizeof(atr); i++) {
+ assert(card_emu_get_tx_byte(ch, &atr[i]) == 1);
+ printf(" %02x", atr[i]);
+ }
+ printf("\n");
+ assert(card_emu_get_tx_byte(ch, &byte) == 0);
+
+ return 1;
+}
+
+static void io_start_card(struct card_handle *ch)
+{
+ uint8_t byte;
+
+ /* bring the card up from the dead */
+ card_emu_io_statechg(ch, CARD_IO_VCC, 1);
+ assert(card_emu_get_tx_byte(ch, &byte) == 0);
+ card_emu_io_statechg(ch, CARD_IO_CLK, 1);
+ assert(card_emu_get_tx_byte(ch, &byte) == 0);
+ card_emu_io_statechg(ch, CARD_IO_RST, 1);
+ assert(card_emu_get_tx_byte(ch, &byte) == 0);
+
+ /* release from reset and verify th ATR */
+ card_emu_io_statechg(ch, CARD_IO_RST, 0);
+ verify_atr(ch);
+}
+
+static void send_bytes(struct card_handle *ch, const uint8_t *bytes, unsigned int len)
+{
+ unsigned int i;
+ for (i = 0; i < len; i++)
+ card_emu_process_rx_byte(ch, bytes[i]);
+}
+
+static void dump_rctx(struct req_ctx *rctx)
+{
+ struct cardemu_usb_msg_hdr *mh =
+ (struct cardemu_usb_msg_hdr *) rctx->data;
+ struct cardemu_usb_msg_rx_data *rxd;
+ int i;
+
+ printf("req_ctx(%p): state=%u, size=%u, tot_len=%u, idx=%u, data=%p\n",
+ rctx, rctx->state, rctx->size, rctx->tot_len, rctx->idx, rctx->data);
+ printf(" msg_type=%u, seq_nr=%u, data_len=%u\n",
+ mh->msg_type, mh->seq_nr, mh->data_len);
+
+ switch (mh->msg_type) {
+ case CEMU_USB_MSGT_DO_RX_DATA:
+ rxd = (struct cardemu_usb_msg_rx_data *)mh;
+ printf(" flags=%x, data=", rxd->flags);
+ for (i = 0; i < mh->data_len; i++)
+ printf(" %02x", rxd->data[i]);
+ printf("\n");
+ break;
+ }
+}
+
+static void send_tpdu_hdr(struct card_handle *ch, const uint8_t *tpdu_hdr)
+{
+ struct req_ctx *rctx;
+
+ /* we don't want a receive context to become available during
+ * the first four bytes */
+ send_bytes(ch, tpdu_hdr, 4);
+ assert(!req_ctx_find_get(1, RCTX_S_USB_TX_PENDING, RCTX_S_USB_TX_BUSY));
+
+ send_bytes(ch, tpdu_hdr+4, 1);
+ /* but then after the final byte of the TPDU header, we want a
+ * receive context to be available for USB transmission */
+ rctx = req_ctx_find_get(1, RCTX_S_USB_TX_PENDING, RCTX_S_USB_TX_BUSY);
+ assert(rctx);
+ dump_rctx(rctx);
+}
+
+const uint8_t tpdu_hdr_sel_mf[] = { 0xA0, 0xA4, 0x00, 0x00, 0x02 };
+
+int main(int argc, char **argv)
+{
+ struct card_handle *ch;
+
+ req_ctx_init();
+
+ ch = card_emu_init(0, 23, 42);
+ assert(ch);
+
+ io_start_card(ch);
+
+ send_tpdu_hdr(ch, tpdu_hdr_sel_mf);
+
+ exit(0);
+}
diff --git a/firmware/test/trace.h b/firmware/test/trace.h
new file mode 100644
index 0000000..3715643
--- /dev/null
+++ b/firmware/test/trace.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#include <stdio.h>
+
+#define TRACE_DEBUG(x, args ...) printf(x, ## args)