aboutsummaryrefslogtreecommitdiffstats
path: root/firmware/libcommon/source
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/libcommon/source')
-rw-r--r--firmware/libcommon/source/card_emu.c98
-rw-r--r--firmware/libcommon/source/mode_cardemu.c27
2 files changed, 95 insertions, 30 deletions
diff --git a/firmware/libcommon/source/card_emu.c b/firmware/libcommon/source/card_emu.c
index 25ec36a..51fd923 100644
--- a/firmware/libcommon/source/card_emu.c
+++ b/firmware/libcommon/source/card_emu.c
@@ -1,6 +1,7 @@
/* ISO7816-3 state machine for the card side
*
* (C) 2010-2017 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2018 by sysmocom -s.f.m.c. GmbH, Author: Kevin Redon <kredon@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
@@ -296,32 +297,39 @@ static void card_set_state(struct card_handle *ch,
card_emu_uart_enable(ch->uart_chan, 0);
break;
case ISO_S_WAIT_ATR:
- set_pts_state(ch, PTS_S_WAIT_REQ_PTSS);
/* Reset to initial Fi / Di ratio */
ch->fi = 1;
ch->di = 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.
+ * since the initial ETU is Fd=372/Dd=1 clock cycles long, we have to wait 2-107 ETU.
+ */
+ tc_etu_set_wtime(ch->tc_chan, 2);
+ /* ensure the TC_ETU timer is enabled */
+ tc_etu_enable(ch->tc_chan);
+ break;
+ case ISO_S_IN_ATR:
/* initialize to default WI, this will be overwritten if we
- * receive TC2, and it will be programmed into hardware after
+ * 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;
+ /* set initial waiting time */
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;
+ /* enable USART transmission to reader */
card_emu_uart_enable(ch->uart_chan, ENABLE_TX);
- break;
+ /* trigger USART TX IRQ to sent first ATR byte TS */
+ card_emu_uart_interrupt(ch->uart_chan);
break;
case ISO_S_WAIT_TPDU:
/* enable the receiver, disable transmitter */
set_tpdu_state(ch, TPDU_S_WAIT_CLA);
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 */
@@ -329,6 +337,47 @@ static void card_set_state(struct card_handle *ch,
}
}
+/**********************************************************************
+ * ATR handling
+ **********************************************************************/
+
+/*! Transmit ATR data to reader
+ * @param[in] ch card interface connected to reader
+ * @return numbers of bytes transmitted
+ */
+static int tx_byte_atr(struct card_handle *ch)
+{
+ if (NULL == ch) {
+ TRACE_ERROR("ATR TX: no card handle provided\n\r");
+ return 0;
+ }
+ if (ISO_S_IN_ATR != ch->state) {
+ TRACE_ERROR("%u: ATR TX: no in ATR state\n\r", ch->num);
+ return 0;
+ }
+
+ /* Transmit ATR */
+ if (ch->atr.idx < ch->atr.len) {
+ uint8_t byte = ch->atr.atr[ch->atr.idx++];
+ card_emu_uart_tx(ch->uart_chan, byte);
+ TRACE_DEBUG("%u: ATR TX: %02x\n\r", ch->num, byte);
+ return 1;
+ } else { /* The ATR has been completely transmitted */
+ /* TODO update WI using optional TC2 and then update WT */
+ //ch->wi = ISO7816_3_DEFAULT_WI;
+ /* update waiting time */
+ //ch->waiting_time = ISO7816_3_INIT_WTIME;
+ //tc_etu_set_wtime(ch->tc_chan, ch->waiting_time);
+ /* reset PTS to initial state */
+ set_pts_state(ch, PTS_S_WAIT_REQ_PTSS);
+ /* go to next state */
+ card_set_state(ch, ISO_S_WAIT_TPDU);
+ return 0;
+ }
+
+ /* return number of bytes transmitted */
+ return 1;
+}
/**********************************************************************
* PTS / PPS handling
@@ -793,17 +842,7 @@ int card_emu_tx_byte(struct card_handle *ch)
switch (ch->state) {
case ISO_S_IN_ATR:
- if (ch->atr.idx < ch->atr.len) {
- uint8_t byte;
- byte = ch->atr.atr[ch->atr.idx++];
- rc = 1;
-
- card_emu_uart_tx(ch->uart_chan, byte);
-
- /* detect end of ATR */
- if (ch->atr.idx >= ch->atr.len)
- card_set_state(ch, ISO_S_WAIT_TPDU);
- }
+ rc = tx_byte_atr(ch);
break;
case ISO_S_IN_PTS:
rc = tx_byte_pts(ch);
@@ -898,9 +937,8 @@ void card_emu_io_statechg(struct card_handle *ch, enum card_io io, int active)
if (ch->vcc_active && ch->clocked) {
/* enable the TC/ETU counter once reset has been released */
tc_etu_enable(ch->tc_chan);
+ /* prepare to send the ATR */
card_set_state(ch, ISO_S_WAIT_ATR);
- /* FIXME: wait 400 to 40k clock cycles before sending ATR */
- card_set_state(ch, ISO_S_IN_ATR);
}
} else if (active && !ch->in_reset) {
TRACE_INFO("%u: RST asserted\r\n", ch->num);
@@ -921,7 +959,15 @@ int card_emu_set_atr(struct card_handle *ch, const uint8_t *atr, uint8_t len)
ch->atr.len = len;
ch->atr.idx = 0;
- /* FIXME: race condition with trasmitting ATR to reader? */
+#if TRACE_LEVEL >= TRACE_LEVEL_INFO
+ uint8_t i;
+ TRACE_INFO("%u: ATR set: ", ch->num);
+ for (i = 0; i < ch->atr.len; i++) {
+ TRACE_INFO_WP("%02x ", atr[i]);
+ }
+ TRACE_INFO_WP("\n\r");
+#endif
+ /* FIXME: race condition with transmitting ATR to reader? */
return 0;
}
@@ -952,7 +998,15 @@ void tc_etu_wtime_half_expired(void *handle)
void tc_etu_wtime_expired(void *handle)
{
struct card_handle *ch = handle;
- TRACE_ERROR("%u: wtime_exp\r\n", ch->num);
+ switch (ch->state) {
+ case ISO_S_WAIT_ATR:
+ /* ISO 7816-3 6.2.1 time tc has passed, we can now send the ATR */
+ card_set_state(ch, ISO_S_IN_ATR);
+ break;
+ default:
+ TRACE_ERROR("%u: wtime_exp\r\n", ch->num);
+ break;
+ }
}
/* shortest ATR found in smartcard_list.txt */
diff --git a/firmware/libcommon/source/mode_cardemu.c b/firmware/libcommon/source/mode_cardemu.c
index 57d541b..76b3a01 100644
--- a/firmware/libcommon/source/mode_cardemu.c
+++ b/firmware/libcommon/source/mode_cardemu.c
@@ -1,6 +1,7 @@
/* card emulation mode
*
* (C) 2015-2017 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2018 by sysmocom -s.f.m.c. GmbH, Author: Kevin Redon <kredon@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
@@ -98,15 +99,11 @@ struct cardem_inst cardem_inst[] = {
static Usart *get_usart_by_chan(uint8_t uart_chan)
{
- switch (uart_chan) {
- case 0:
- return USART1;
-#ifdef CARDEMU_SECOND_UART
- case 1:
- return USART0;
-#endif
+ if (uart_chan < ARRAY_SIZE(cardem_inst)) {
+ return cardem_inst[uart_chan].usart_info.base;
+ } else {
+ return NULL;
}
- return NULL;
}
/***********************************************************************
@@ -244,6 +241,20 @@ int card_emu_uart_update_fidi(uint8_t uart_chan, unsigned int fidi)
return 0;
}
+/* call-back from card_emu.c to force a USART interrupt */
+void card_emu_uart_interrupt(uint8_t uart_chan)
+{
+ Usart *usart = get_usart_by_chan(uart_chan);
+ if (!usart) {
+ return;
+ }
+ if (USART0 == usart) {
+ NVIC_SetPendingIRQ(USART0_IRQn);
+ } else if (USART1 == usart) {
+ NVIC_SetPendingIRQ(USART1_IRQn);
+ }
+}
+
/***********************************************************************
* ADC for VCC voltage detection
***********************************************************************/