12 Commits

Author SHA1 Message Date
Harald Welte
4d2ff9a173 assert: Use printf_sync() to ensure printing of assert / panic
Change-Id: Icc202e60445d9be1cdcd61176db5ed1704d583e7
2021-04-06 02:17:39 +02:00
Kévin Redon
b6e421bcbe card_emu: use edge-triggered VCC ADC logic
Before this patch, we used to st ci->vcc_active depending on the
instantaneous ADC reading of VCC.  Is it > .5v, we claim VCC is active,
and if it's below, VCC is inactive.

With this patch we move to an edge triggered approach: Only change
ci->vcc_active if the previous reading was different from the current
reading.

FIXME: why?

Change-Id: I71b703162219484e43638f1f2f692e9dd554ef55
2021-04-06 02:17:39 +02:00
Harald Welte
e145a7aed4 card_emu: Fix computation of waiting time
As we store the waiting time (WT) in 'etu', we must adjust the formula
from ISO 7816-3.  The 'Fi' component in the formula only exists to
compute clock cycles from the etu, which we don't need here.

Without this patch, the waiting time would be way too large (by a factor
of 372 in the default case).

Change-Id: Ia21bc7303f9b38834b5b1753983ed2a99bfc7d95
2021-04-06 02:17:39 +02:00
Harald Welte
f83c69ff69 card_emu: Fix USART timer, particularly in re-start situations
The existing code started the timer once (and expired once) but didn't
properly handle re-starting of the timer.  Neither did it handle
the 'half time expiration' case.  If we want to call a function after
half the WT expiring, we must of course program the hardware for half
the timeout, and not the full timeout...

Change-Id: Ia999d97f835c27597fcd1cf7ac78bac0ab9c98c1
2021-04-06 02:17:39 +02:00
Harald Welte
2619862fba card_emu: Use USART timeout for waiting time
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 there
(contrary to other boards) the I/O signal is not wired to a TIO pin
of the timer/counter block, and hence Rx/Tx data cannot reset that
timer/counter.

As a result of this migration, cardem is now supported not only on
owhw + qmod, but also on the simtrace board.

The guts of this change have been lifted out of Change-Id
Ibcb2c8cace9137695adf5fb3de43566f7cfb93b5 by Kevin Redon, which was
unfortunately touching various different topics at the same time and
hence was split up. Some improvements are the introduction of the
ENABLE_TX_TIMER_ONLY mode, which avoids the USART interrupt handler
getting hammered with TXRDY between release of RST and start of the ATR.

Change-Id: Ibcb2c8cace9137695adf5fb3de43566f7cfb93b5
2021-04-06 02:17:39 +02:00
Harald Welte
deb322c36d card_emu: explicitly initialize PTS and TPDU states
Those are already initialized at various transitions of the master
7816 FSM, but let's properly initialize them at start-up, too.

Change-Id: I81b2a8ef3284559164700d94717e4ccf008f53df
2021-04-06 02:17:39 +02:00
Harald Welte
351dfd65d9 card_emu: improve reset detection conditions
* enter ISO_S_WAIT_RST when RST is asserted
* enter ISO_S_WAIT_ATR only when we RST is released while in state ISO_S_WAIT_RST

Change-Id: I620333aa8d45561a8028b948955a27f667b58406
2021-04-06 02:17:39 +02:00
Harald Welte
bc9b927569 iso7816_fidi: Add iso7816_3_ prefix to symbols; fix terminology
Fi/Di are not the index into the table, but the contents of the table
as resolved by Fi_index / Di_index.  Let's clarify the terminology.

Change-Id: If364e08e7c9a3a9707e6d54b9267c6a7c088e415
2021-04-06 02:17:39 +02:00
Harald Welte
e5c8833a97 card_emu: Clarify and differentiate F/Fi/F_index/Fi_index
The ISO7816 spec terms are well-defined, let's not abuse them. We used
to consider "Fi" as the "index into the table of F values", while the
spec actually considers Fi as the initial value for F.

Let's make sure we use the terms quite clearly:
* Fi and Di are the initial values for F and D
* F*_index and D*_index are the indexes into the ISO7816-3 Tables

Furthermore, let's track Fi separately from F, as e.g. the waiting
time definition only considers Fi as indicated in the ATR, despite
an actually different F value might have been negotiated via PTS
meanwhile.

Change-Id: Ieb2425e8380a81b79df7b2bd072902994e9c3ee7
2021-04-06 02:17:39 +02:00
Harald Welte
1a32601be7 card_emu: Rename fi to Fi and di to Di
As we will soon introduce the distinction between Fi and F, we should
use uppercase letters, as lower-case 'f' is defined in ISO7816-3 as
the frequency, which is different from the upper-case 'F'.

Change-Id: Iaede621551520576e9b9af878fa46fbc88e59c2a
2021-04-06 02:17:39 +02:00
Kévin Redon
ed3067e2ea make sim switch board specific
the simtrace board uses a bus switch not used on qmod and owhw to
switch the SIM between physical and virtual

Change-Id: Ieaf2ed4761fc3e04f33f9aac5c04a768c9a6f71e
2021-04-06 02:17:39 +02:00
Harald Welte
841a0b70ff card_emu: waiting_time is stored in etu, not clocks.
The comment didn't reflect the source.  I checked all users and
the code consistently stores the waiting time in units of 'etu'.

Change-Id: I2bc4a7c19cee5fb487ad639ee48ecaea706f6172
2021-04-06 02:17:39 +02:00
17 changed files with 398 additions and 80 deletions

View File

@@ -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 += card_emu.c cciddriver.c iso7816_4.c iso7816_fidi.c mitm.c mode_cardemu.c mode_ccid.c simtrace_iso7816.c sniffer.c usb.c

View File

@@ -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 += card_emu.c cciddriver.c iso7816_4.c iso7816_fidi.c mitm.c mode_cardemu.c mode_ccid.c simtrace_iso7816.c sniffer.c usb.c

View File

@@ -1,3 +1,3 @@
C_FILES += $(C_LIBUSB_RT)
C_FILES += card_emu.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_4.c iso7816_fidi.c mitm.c mode_cardemu.c mode_ccid.c simtrace_iso7816.c sniffer.c usb.c

View File

@@ -14,5 +14,13 @@
*/
#pragma once
/** switch card lines to use physical or emulated card
* @param[in] nr card interface number (i.e. slot)
* @param[in] physical which physical interface to switch to (e.g. 0: physical, 1: virtual)
* @return 0 on success, negative else
*/
int sim_switch_use_physical(unsigned int nr, int physical);
/** initialise card switching capabilities
* @return number of switchable card interfaces
*/
int sim_switch_init(void);

View File

@@ -0,0 +1,90 @@
/* Code to switch between local (physical) and remote (emulated) SIM
*
* (C) 2015-2017 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 "board.h"
#include "trace.h"
#include "led.h"
#include "sim_switch.h"
#ifdef PIN_SIM_SWITCH1
static const Pin pin_conn_usim1 = {PIO_PA20, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT};
#endif
#ifdef PIN_SIM_SWITCH2
static const Pin pin_conn_usim2 = {PIO_PA28, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT};
#endif
static int initialized = 0;
int sim_switch_use_physical(unsigned int nr, int physical)
{
const Pin *pin;
enum led led;
if (!initialized) {
TRACE_ERROR("Somebody forgot to call sim_switch_init()\r\n");
sim_switch_init();
}
TRACE_INFO("Modem %d: %s SIM\n\r", nr,
physical ? "physical" : "virtual");
switch (nr) {
#ifdef PIN_SIM_SWITCH1
case 0:
pin = &pin_conn_usim1;
led = LED_USIM1;
break;
#endif
#ifdef PIN_SIM_SWITCH2
case 1:
pin = &pin_conn_usim2;
led = LED_USIM2;
break;
#endif
default:
TRACE_ERROR("Invalid SIM%u\n\r", nr);
return -1;
}
if (physical) {
TRACE_INFO("%u: Use local/physical SIM\r\n", nr);
PIO_Clear(pin);
led_blink(led, BLINK_ALWAYS_ON);
} else {
TRACE_INFO("%u: Use remote/emulated SIM\r\n", nr);
PIO_Set(pin);
led_blink(led, BLINK_ALWAYS_OFF);
}
return 0;
}
int sim_switch_init(void)
{
int num_switch = 0;
#ifdef PIN_SIM_SWITCH1
PIO_Configure(&pin_conn_usim1, 1);
num_switch++;
#endif
#ifdef PIN_SIM_SWITCH2
PIO_Configure(&pin_conn_usim2, 1);
num_switch++;
#endif
initialized = 1;
return num_switch;
}

View File

@@ -0,0 +1,54 @@
/* Code to switch between local (physical) and remote (emulated) SIM
*
* (C) 2015-2017 by Harald Welte <hwelte@hmw-consulting.de>
* (C) 2018, 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
* 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 "board.h"
#include "trace.h"
#include "led.h"
#include "sim_switch.h"
int sim_switch_use_physical(unsigned int nr, int physical)
{
const Pin pin_sc = PIN_SC_SW_DEFAULT; // pin to control bus switch for VCC/RST/CLK signals
const Pin pin_io = PIN_IO_SW_DEFAULT; // pin to control bus switch for I/O signal
if (nr > 0) {
TRACE_ERROR("SIM interface for Modem %d can't be switched\r\n", nr);
return -1;
}
TRACE_INFO("Modem %u: %s SIM\n\r", nr, physical ? "physical" : "virtual");
if (physical) {
TRACE_INFO("%u: Use local/physical SIM\r\n", nr);
PIO_Set(&pin_sc);
PIO_Set(&pin_io);
} else {
TRACE_INFO("%u: Use remote/emulated SIM\r\n", nr);
PIO_Clear(&pin_sc);
PIO_Clear(&pin_io);
}
return 0;
}
int sim_switch_init(void)
{
// the bus switch is already initialised
return 1; // SIMtrace hardware has only one switchable interface
}

View File

@@ -89,7 +89,7 @@
/// \param condition Condition to verify.
#define ASSERT(condition) { \
if (!(condition)) { \
printf("-F- ASSERT: %s %s:%d\n\r", #condition, __BASE_FILE__, __LINE__); \
printf_sync("-F- ASSERT: %s %s:%d\n\r", #condition, __BASE_FILE__, __LINE__); \
while (1); \
} \
}

View File

@@ -31,7 +31,6 @@ enum card_io {
/** initialise card slot
* @param[in] slot_num slot number (arbitrary number)
* @param[in] tc_chan timer counter channel (to measure the ETU)
* @param[in] uart_chan UART peripheral channel
* @param[in] in_ep USB IN end point number
* @param[in] irq_ep USB INTerrupt end point number
@@ -40,7 +39,7 @@ enum card_io {
* @param[in] clocked initial CLK signat state (true = active)
* @return main card handle reference
*/
struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uart_chan, uint8_t in_ep, uint8_t irq_ep, bool vcc_active, bool in_reset, bool clocked);
struct card_handle *card_emu_init(uint8_t slot_num, uint8_t uart_chan, uint8_t in_ep, uint8_t irq_ep, bool vcc_active, bool in_reset, bool clocked);
/* process a single byte received from the reader */
void card_emu_process_rx_byte(struct card_handle *ch, uint8_t byte);
@@ -58,10 +57,17 @@ struct llist_head *card_emu_get_uart_tx_queue(struct card_handle *ch);
void card_emu_have_new_uart_tx(struct card_handle *ch);
void card_emu_report_status(struct card_handle *ch, bool report_on_irq);
#define ENABLE_TX 0x01
#define ENABLE_RX 0x02
void card_emu_wtime_half_expired(void *ch);
void card_emu_wtime_expired(void *ch);
#define ENABLE_TX 0x01
#define ENABLE_RX 0x02
#define ENABLE_TX_TIMER_ONLY 0x03
int card_emu_uart_update_fidi(uint8_t uart_chan, unsigned int fidi);
void card_emu_uart_update_wt(uint8_t uart_chan, uint32_t wt);
void card_emu_uart_reset_wt(uint8_t uart_chan);
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);

View File

@@ -21,10 +21,10 @@
#include <stdint.h>
/* Table 7 of ISO 7816-3:2006 */
extern const uint16_t fi_table[];
extern const uint16_t iso7816_3_fi_table[16];
/* Table 8 from ISO 7816-3:2006 */
extern const uint8_t di_table[];
extern const uint8_t iso7816_3_di_table[16];
/* compute the F/D ratio based on Fi and Di values */
int compute_fidi_ratio(uint8_t fi, uint8_t di);
/* compute the F/D ratio based on F_index and D_index values */
int iso7816_3_compute_fd_ratio(uint8_t f_index, uint8_t d_index);

View File

@@ -230,11 +230,17 @@ struct cardemu_usb_msg_status {
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;
/* F/D related information. Not actual Fn/Dn values but indexes into tables! */
union {
uint8_t F_index; /* <! Index to ISO7816-3 Table 7 (F and f_max values) */
uint8_t fi; /* <! old, wrong name for API compatibility */
};
union {
uint8_t D_index; /* <! Index to ISO7816-3 Table 8 (D value) */
uint8_t di; /* <! old, wrong name for API compatibility */
};
uint8_t wi; /* <! Waiting Integer as defined in ISO7816-3 Section 10.2 */
uint32_t waiting_time; /* <! Waiting Time in etu as defined in ISO7816-3 Section 8.1 */
} __attribute__ ((packed));
/* CEMU_USB_MSGT_DO_PTS */

View File

@@ -1,6 +1,6 @@
/* ISO7816-3 state machine for the card side
*
* (C) 2010-2019 by Harald Welte <laforge@gnumonks.org>
* (C) 2010-2021 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
@@ -27,7 +27,6 @@
#include "utils.h"
#include "trace.h"
#include "iso7816_fidi.h"
#include "tc_etu.h"
#include "card_emu.h"
#include "simtrace_prot.h"
#include "usb_buf.h"
@@ -154,19 +153,34 @@ struct card_handle {
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;
/* All below variables with _index suffix are indexes from 0..15 into Tables 7 + 8
* of ISO7816-3. */
/*! Index to clock rate conversion integer Fi (ISO7816-3 Table 7).
* \note this represents the maximum value supported by the card, and can be indicated in TA1 */
uint8_t Fi_index;
/*! Current value of index to clock rate conversion integer F (ISO 7816-3 Section 7.1). */
uint8_t F_index;
/*! Index to baud rate adjustment factor Di (ISO7816-3 Table 8).
* \note this represents the maximum value supported by the card, and can be indicated in TA1 */
uint8_t Di_index;
/*! Current value of index to baud rate adjustment factor D (ISO 7816-3 Section 7.1). */
uint8_t D_index;
/*! Waiting Integer (ISO7816-3 Section 10.2).
* \note this value can be set in TA2 */
uint8_t wi;
uint8_t tc_chan; /* TC channel number */
/*! Waiting Time, in ETU (ISO7816-3 Section 8.1).
* \note this depends on Fi, Di, and WI if T=0 is used */
uint32_t waiting_time; /* in etu */
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 */
/* ATR state machine */
struct {
uint8_t idx;
@@ -206,7 +220,7 @@ static void card_handle_reset(struct card_handle *ch)
{
struct msgb *msg;
tc_etu_disable(ch->tc_chan);
card_emu_uart_update_wt(ch->uart_chan, 0);
/* release any buffers we may still own */
if (ch->uart_tx_msg) {
@@ -361,16 +375,14 @@ static void emu_update_fidi(struct card_handle *ch)
{
int rc;
rc = compute_fidi_ratio(ch->fi, ch->di);
rc = iso7816_3_compute_fd_ratio(ch->F_index, ch->D_index);
if (rc > 0 && rc < 0x400) {
TRACE_INFO("%u: computed Fi(%u) Di(%u) ratio: %d\r\n",
ch->num, ch->fi, ch->di, rc);
TRACE_INFO("%u: computed F(%u)/D(%u) ratio: %d\r\n", ch->num,
ch->F_index, ch->D_index, 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_INFO("%u: computed FiDi ration %d unsupported\r\n",
TRACE_INFO("%u: computed F/D ratio %d unsupported\r\n",
ch->num, rc);
}
@@ -392,19 +404,23 @@ static void card_set_state(struct card_handle *ch,
case ISO_S_WAIT_RST:
/* disable Rx and Tx of UART */
card_emu_uart_enable(ch->uart_chan, 0);
/* disable timeout */
card_emu_uart_update_wt(ch->uart_chan, 0);
break;
case ISO_S_WAIT_ATR:
/* Reset to initial Fi / Di ratio */
ch->fi = 1;
ch->di = 1;
ch->Fi_index = ch->F_index = 1;
ch->Di_index = ch->D_index = 1;
ch->wi = ISO7816_3_DEFAULT_WI;
ch->waiting_time = ISO7816_3_INIT_WTIME;
emu_update_fidi(ch);
/* enable TX to be able to use the timeout */
card_emu_uart_enable(ch->uart_chan, ENABLE_TX_TIMER_ONLY);
/* the ATR should only be sent 400 to 40k clock cycles after the RESET.
* we use the tc_etu mechanism to wait this time.
* 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.
*/
tc_etu_set_wtime(ch->tc_chan, 2);
/* enable the TC/ETU counter once reset has been released */
tc_etu_enable(ch->tc_chan);
card_emu_uart_update_wt(ch->uart_chan, 2);
break;
case ISO_S_IN_ATR:
/* initialize to default WI, this will be overwritten if we
@@ -414,7 +430,7 @@ static void card_set_state(struct card_handle *ch,
/* 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);
card_emu_uart_update_wt(ch->uart_chan, ch->waiting_time);
/* Set ATR sub-state to initial state */
ch->atr.idx = 0;
/* enable USART transmission to reader */
@@ -489,9 +505,11 @@ static int tx_byte_atr(struct card_handle *ch)
}
}
}
/* 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);
/* update waiting time (see ISO 7816-3 10.2). We can drop the Fi
* multiplier as we store the waiting time in units of 'etu', and
* don't really care what the number of clock cycles or the absolute
* wall clock time is */
ch->waiting_time = ch->wi * 960;
/* go to next state */
card_set_state(ch, ISO_S_WAIT_TPDU);
return 0;
@@ -626,10 +644,11 @@ static int tx_byte_pts(struct card_handle *ch)
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);
ch->F_index = byte >> 4;
ch->D_index = byte & 0xf;
TRACE_DEBUG("%u: found F=%u D=%u\r\n", ch->num,
iso7816_3_fi_table[ch->F_index], iso7816_3_di_table[ch->D_index]);
/* FIXME: if F or D are 0, become unresponsive to signal error condition */
break;
case PTS_S_WAIT_RESP_PTS2:
byte = ch->pts.resp[_PTS2];
@@ -654,10 +673,11 @@ static int tx_byte_pts(struct card_handle *ch)
switch (ch->pts.state) {
case PTS_S_WAIT_RESP_PCK:
card_emu_uart_wait_tx_idle(ch->uart_chan);
/* update baud rate generator with Fi/Di */
/* update baud rate generator with F/D */
emu_update_fidi(ch);
/* 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 */
@@ -733,14 +753,28 @@ static void set_tpdu_state(struct card_handle *ch, enum tpdu_state new_ts)
switch (new_ts) {
case TPDU_S_WAIT_CLA:
case TPDU_S_WAIT_RX:
/* switch back to receiving mode */
card_emu_uart_enable(ch->uart_chan, ENABLE_RX);
/* disable waiting time since we don't expect any data */
card_emu_uart_update_wt(ch->uart_chan, 0);
break;
case TPDU_S_WAIT_INS:
/* start waiting for the rest of the header/body */
card_emu_uart_update_wt(ch->uart_chan, ch->waiting_time);
break;
case TPDU_S_WAIT_RX:
/* switch to receive mode to receive the body */
card_emu_uart_enable(ch->uart_chan, ENABLE_RX);
/* start waiting for the body */
card_emu_uart_update_wt(ch->uart_chan, ch->waiting_time);
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);
/* prepare to extend the waiting time once half of it is reached */
card_emu_uart_update_wt(ch->uart_chan, ch->waiting_time);
break;
default:
break;
@@ -1025,8 +1059,8 @@ void card_emu_report_status(struct card_handle *ch, bool report_on_irq)
if (ch->in_reset)
sts->flags |= CEMU_STATUS_F_RESET_ACTIVE;
/* FIXME: voltage + card insert */
sts->fi = ch->fi;
sts->di = ch->di;
sts->F_index = ch->F_index;
sts->D_index = ch->D_index;
sts->wi = ch->wi;
sts->waiting_time = ch->waiting_time;
@@ -1083,9 +1117,7 @@ void card_emu_io_statechg(struct card_handle *ch, enum card_io io, int active)
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 && ch->state == ISO_S_WAIT_RST) {
/* prepare to send the ATR */
card_set_state(ch, ISO_S_WAIT_ATR);
}
@@ -1094,6 +1126,7 @@ void card_emu_io_statechg(struct card_handle *ch, enum card_io io, int active)
TRACE_INFO("%u: RST asserted\r\n", ch->num);
card_handle_reset(ch);
chg_mask |= CEMU_STATUS_F_RESET_ACTIVE;
card_set_state(ch, ISO_S_WAIT_RST);
}
ch->in_reset = active;
break;
@@ -1143,7 +1176,7 @@ int card_emu_set_atr(struct card_handle *ch, const uint8_t *atr, uint8_t len)
}
/* hardware driver informs us that one (more) ETU has expired */
void tc_etu_wtime_half_expired(void *handle)
void card_emu_wtime_half_expired(void *handle)
{
struct card_handle *ch = handle;
/* transmit NULL procedure byte well before waiting time expires */
@@ -1153,7 +1186,10 @@ void tc_etu_wtime_half_expired(void *handle)
case TPDU_S_WAIT_PB:
case TPDU_S_WAIT_TX:
putchar('N');
/* we are waiting for data from the user. Send a procedure byte to ask the
* reader to wait more time */
card_emu_uart_tx(ch->uart_chan, ISO7816_3_PB_NULL);
card_emu_uart_reset_wt(ch->uart_chan);
break;
default:
break;
@@ -1165,7 +1201,7 @@ void tc_etu_wtime_half_expired(void *handle)
}
/* hardware driver informs us that one (more) ETU has expired */
void tc_etu_wtime_expired(void *handle)
void card_emu_wtime_expired(void *handle)
{
struct card_handle *ch = handle;
switch (ch->state) {
@@ -1211,7 +1247,7 @@ int card_emu_set_config(struct card_handle *ch, const struct cardemu_usb_msg_con
return 0;
}
struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uart_chan, uint8_t in_ep, uint8_t irq_ep, bool vcc_active, bool in_reset, bool clocked)
struct card_handle *card_emu_init(uint8_t slot_num, uint8_t uart_chan, uint8_t in_ep, uint8_t irq_ep, bool vcc_active, bool in_reset, bool clocked)
{
struct card_handle *ch;
@@ -1232,11 +1268,10 @@ struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uar
ch->in_reset = in_reset;
ch->clocked = clocked;
ch->fi = 0;
ch->di = 1;
ch->Fi_index = ch->F_index = 1;
ch->Di_index = ch->D_index = 1;
ch->wi = ISO7816_3_DEFAULT_WI;
ch->tc_chan = tc_chan;
ch->uart_chan = uart_chan;
ch->waiting_time = ISO7816_3_INIT_WTIME;
@@ -1244,9 +1279,10 @@ struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uar
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;
tc_etu_init(ch->tc_chan, ch);
card_handle_reset(ch);
return ch;
}

View File

@@ -23,38 +23,38 @@
#include "iso7816_fidi.h"
/* Table 7 of ISO 7816-3:2006 */
const uint16_t fi_table[] = {
const uint16_t iso7816_3_fi_table[] = {
372, 372, 558, 744, 1116, 1488, 1860, 0,
0, 512, 768, 1024, 1536, 2048, 0, 0
};
/* Table 8 from ISO 7816-3:2006 */
const uint8_t di_table[] = {
const uint8_t iso7816_3_di_table[] = {
0, 1, 2, 4, 8, 16, 32, 64,
12, 20, 2, 4, 8, 16, 32, 64,
};
/* compute the F/D ratio based on Fi and Di values */
int compute_fidi_ratio(uint8_t fi, uint8_t di)
int iso7816_3_compute_fd_ratio(uint8_t f_index, uint8_t d_index)
{
uint16_t f, d;
int ret;
if (fi >= ARRAY_SIZE(fi_table) ||
di >= ARRAY_SIZE(di_table))
if (f_index >= ARRAY_SIZE(iso7816_3_fi_table) ||
d_index >= ARRAY_SIZE(iso7816_3_di_table))
return -EINVAL;
f = fi_table[fi];
f = iso7816_3_fi_table[f_index];
if (f == 0)
return -EINVAL;
d = di_table[di];
d = iso7816_3_di_table[d_index];
if (d == 0)
return -EINVAL;
/* See table 7 of ISO 7816-3: From 1000 on we divide by 1/d,
* which equals a multiplication by d */
if (di < 8)
if (d_index < 8)
ret = f / d;
else
ret = f * d;

View File

@@ -55,6 +55,14 @@ struct cardem_inst {
struct llist_head usb_out_queue;
struct ringbuf rb;
struct Usart_info usart_info;
struct {
/*! receiver waiting time to trigger timeout (0 to deactivate it) */
uint32_t total;
/*! remaining waiting time (we may need multiple timer runs to reach total */
uint32_t remaining;
/*! did we already notify about half the time having expired? */
bool half_time_notified;
} wt;
int usb_pending_old;
uint8_t ep_out;
uint8_t ep_in;
@@ -62,6 +70,7 @@ struct cardem_inst {
const Pin pin_insert;
#ifdef DETECT_VCC_BY_ADC
uint32_t vcc_uv;
uint32_t vcc_uv_last;
#endif
bool vcc_active;
bool vcc_active_last;
@@ -140,12 +149,23 @@ void card_emu_uart_enable(uint8_t uart_chan, uint8_t rxtx)
Usart *usart = get_usart_by_chan(uart_chan);
switch (rxtx) {
case ENABLE_TX:
USART_DisableIt(usart, ~US_IER_TXRDY);
USART_DisableIt(usart, ~(US_IER_TXRDY | US_IER_TIMEOUT));
/* as irritating as it is, we actually want to keep the
* receiver enabled during transmit */
USART_SetReceiverEnabled(usart, 1);
usart->US_CR = US_CR_RSTSTA | US_CR_RSTIT | US_CR_RSTNACK;
USART_EnableIt(usart, US_IER_TXRDY);
USART_EnableIt(usart, US_IER_TXRDY | US_IER_TIMEOUT);
USART_SetTransmitterEnabled(usart, 1);
break;
case ENABLE_TX_TIMER_ONLY:
/* enable the transmitter without generating TXRDY interrupts
* just so that the timer can run */
USART_DisableIt(usart, ~US_IER_TIMEOUT);
/* as irritating as it is, we actually want to keep the
* receiver enabled during transmit */
USART_SetReceiverEnabled(usart, 1);
usart->US_CR = US_CR_RSTSTA | US_CR_RSTIT | US_CR_RSTNACK;
USART_EnableIt(usart, US_IER_TIMEOUT);
USART_SetTransmitterEnabled(usart, 1);
break;
case ENABLE_RX:
@@ -193,11 +213,28 @@ int card_emu_uart_tx(uint8_t uart_chan, uint8_t byte)
return 1;
}
static uint16_t compute_next_timeout(struct cardem_inst *ci)
{
uint32_t want_to_expire;
if (ci->wt.total == 0)
return 0;
if (!ci->wt.half_time_notified) {
/* we need to make sure to expire after half the total waiting time */
OSMO_ASSERT(ci->wt.remaining > (ci->wt.total / 2));
want_to_expire = ci->wt.remaining - (ci->wt.total / 2);
} else
want_to_expire = ci->wt.remaining;
/* if value exceeds the USART TO range, use the maximum possible value for one round */
return OSMO_MIN(want_to_expire, 0xffff);
}
/*! common handler if interrupt was received.
* \param[in] inst_num Instance number, range 0..1 (some boards only '0' permitted) */
static void usart_irq_rx(uint8_t inst_num)
{
OSMO_ASSERT(inst_num < ARRAY_SIZE(cardem_inst));
Usart *usart = get_usart_by_chan(inst_num);
struct cardem_inst *ci = &cardem_inst[inst_num];
uint32_t csr;
@@ -225,11 +262,52 @@ static void usart_irq_rx(uint8_t inst_num)
}
/* check if any error flags are set */
if (csr & (US_CSR_OVRE|US_CSR_FRAME|US_CSR_PARE|US_CSR_TIMEOUT|US_CSR_NACK|(1<<10))) {
if (csr & (US_CSR_OVRE|US_CSR_FRAME|US_CSR_PARE|US_CSR_NACK|(1<<10))) {
/* clear any error flags */
usart->US_CR = US_CR_RSTSTA | US_CR_RSTIT | US_CR_RSTNACK;
TRACE_ERROR("%u USART error on 0x%x status: 0x%lx\n", ci->num, byte, csr);
}
/* check if the timeout has expired. We "abuse" the receive timer for tracking
* how many etu have expired since we last sent a byte. See section
* 33.7.3.11 "Receiver Time-out" of the SAM3S8 Data Sheet */
if (csr & US_CSR_TIMEOUT) {
/* clear timeout flag (and stop timeout until next character is received) */
usart->US_CR |= US_CR_STTTO;
/* RX has been inactive for some time */
if (ci->wt.remaining <= (usart->US_RTOR & 0xffff)) {
/* waiting time is over; will stop the timer */
ci->wt.remaining = 0;
} else {
/* subtract the actual timeout since the new might not have been set and
* reloaded yet */
ci->wt.remaining -= (usart->US_RTOR & 0xffff);
}
if (ci->wt.remaining == 0) {
/* let the FSM know that WT has expired */
card_emu_wtime_expired(ci->ch);
/* don't automatically re-start in this case */
} else {
bool half_time_just_reached = false;
if (ci->wt.remaining <= ci->wt.total / 2 && !ci->wt.half_time_notified) {
ci->wt.half_time_notified = true;
/* don't immediately call card_emu_wtime_half_expired(), as that
* in turn may calls card_emu_uart_update_wt() which will change
* the timeout but would be overridden 4 lines below */
half_time_just_reached = true;
}
/* update the counter no matter if we reached half time or not */
usart->US_RTOR = compute_next_timeout(ci);
/* restart the counter (if wt is 0, the timeout is not started) */
usart->US_CR |= US_CR_RETTO;
if (half_time_just_reached)
card_emu_wtime_half_expired(ci->ch);
}
}
}
/*! ISR called for USART0 */
@@ -258,6 +336,41 @@ int card_emu_uart_update_fidi(uint8_t uart_chan, unsigned int fidi)
return 0;
}
/*! Update WT on USART peripheral. Will automatically re-start timer with new value.
* \param[in] usart USART peripheral to configure
* \param[in] wt inactivity Waiting Time before card_emu_wtime_expired is called (0 to disable) */
void card_emu_uart_update_wt(uint8_t uart_chan, uint32_t wt)
{
OSMO_ASSERT(uart_chan < ARRAY_SIZE(cardem_inst));
struct cardem_inst *ci = &cardem_inst[uart_chan];
Usart *usart = get_usart_by_chan(uart_chan);
if (ci->wt.total != wt) {
TRACE_DEBUG("%u: USART WT changed from %lu to %lu ETU\r\n", uart_chan,
ci->wt.total, wt);
}
ci->wt.total = wt;
/* reset and start the timer */
card_emu_uart_reset_wt(uart_chan);
}
/*! Reset and re-start waiting timeout count down on USART peripheral.
* \param[in] usart USART peripheral to configure */
void card_emu_uart_reset_wt(uint8_t uart_chan)
{
OSMO_ASSERT(uart_chan < ARRAY_SIZE(cardem_inst));
struct cardem_inst *ci = &cardem_inst[uart_chan];
Usart *usart = get_usart_by_chan(uart_chan);
/* FIXME: guard against race with interrupt handler */
ci->wt.remaining = ci->wt.total;
ci->wt.half_time_notified = false;
usart->US_RTOR = compute_next_timeout(ci);
/* restart the counter (if wt is 0, the timeout is not started) */
usart->US_CR |= US_CR_RETTO;
}
/* call-back from card_emu.c to force a USART interrupt */
void card_emu_uart_interrupt(uint8_t uart_chan)
{
@@ -330,10 +443,14 @@ static int card_vcc_adc_init(void)
static void process_vcc_adc(struct cardem_inst *ci)
{
if (ci->vcc_uv >= VCC_UV_THRESH_3V)
if (ci->vcc_uv >= VCC_UV_THRESH_3V &&
ci->vcc_uv_last < VCC_UV_THRESH_3V) {
ci->vcc_active = true;
else
} else if (ci->vcc_uv < VCC_UV_THRESH_3V &&
ci->vcc_uv_last >= VCC_UV_THRESH_3V) {
ci->vcc_active = false;
}
ci->vcc_uv_last = ci->vcc_uv;
}
void ADC_IrqHandler(void)
@@ -450,7 +567,7 @@ void mode_cardemu_init(void)
do {} while (!adc_triggered); /* wait for first ADC reading */
#endif /* DETECT_VCC_BY_ADC */
cardem_inst[0].ch = card_emu_init(0, 2, 0, SIMTRACE_CARDEM_USB_EP_USIM1_DATAIN,
cardem_inst[0].ch = card_emu_init(0, 0, SIMTRACE_CARDEM_USB_EP_USIM1_DATAIN,
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);
@@ -473,7 +590,7 @@ void mode_cardemu_init(void)
do {} while (!adc_triggered); /* wait for first ADC reading */
#endif /* DETECT_VCC_BY_ADC */
cardem_inst[1].ch = card_emu_init(1, 0, 1, SIMTRACE_CARDEM_USB_EP_USIM2_DATAIN,
cardem_inst[1].ch = card_emu_init(1, 1, SIMTRACE_CARDEM_USB_EP_USIM2_DATAIN,
SIMTRACE_CARDEM_USB_EP_USIM2_INT, cardem_inst[1].vcc_active,
cardem_inst[1].rst_active, cardem_inst[1].vcc_active);
sim_switch_use_physical(1, 1);

View File

@@ -125,7 +125,7 @@ void update_fidi(Usart_info *usart, uint8_t fidi)
uint8_t fi = fidi >> 4;
uint8_t di = fidi & 0xf;
int ratio = compute_fidi_ratio(fi, di);
int ratio = iso7816_3_compute_fd_ratio(fi, di);
if (ratio > 0 && ratio < 0x8000) {
/* make sure USART uses new F/D ratio */

View File

@@ -658,9 +658,10 @@ static void process_byte_pps(uint8_t byte)
fn = 1;
dn = 1;
}
TRACE_INFO("PPS negotiation successful: Fn=%u Dn=%u\n\r", fi_table[fn], di_table[dn]);
TRACE_INFO("PPS negotiation successful: Fn=%u Dn=%u\n\r",
iso7816_3_fi_table[fn], iso7816_3_di_table[dn]);
update_fidi(&sniff_usart, pps_cur[2]);
update_wt(0, di_table[dn]);
update_wt(0, iso7816_3_di_table[dn]);
usb_send_fidi(pps_cur[2]); /* send Fi/Di change notification to host software over USB */
} else { /* checksum is invalid */
TRACE_INFO("PPS negotiation failed\n\r");

View File

@@ -46,7 +46,7 @@ static osmo_panic_handler_t osmo_panic_handler = (void*)0;
__attribute__ ((format (printf, 1, 0)))
static void osmo_panic_default(const char *fmt, va_list args)
{
vfprintf(stderr, fmt, args);
vfprintf_sync(stderr, fmt, args);
osmo_generate_backtrace();
assert(0);
}