diff options
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/Makefile | 3 | ||||
-rw-r--r-- | firmware/src_simtrace/card_emu.c | 694 | ||||
-rw-r--r-- | firmware/src_simtrace/card_emu.h | 33 | ||||
-rw-r--r-- | firmware/src_simtrace/cardemu_prot.h | 121 | ||||
-rw-r--r-- | firmware/src_simtrace/req_ctx.c | 210 | ||||
-rw-r--r-- | firmware/src_simtrace/req_ctx.h | 48 | ||||
-rw-r--r-- | firmware/src_simtrace/tc_etu.c | 194 | ||||
-rw-r--r-- | firmware/src_simtrace/tc_etu.h | 9 | ||||
-rw-r--r-- | firmware/src_simtrace/utils.h | 14 | ||||
-rw-r--r-- | firmware/test/Makefile | 13 | ||||
-rw-r--r-- | firmware/test/card_emu_tests.c | 160 | ||||
-rw-r--r-- | firmware/test/trace.h | 5 |
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) |