mirror of
https://gitea.osmocom.org/sim-card/simtrace2.git
synced 2026-03-16 21:28:33 +03:00
Remove the paragraph about writing to the Free Software Foundation's mailing address. The FSF has changed addresses in the past, and may do so again. In 2021 this is not useful, let's rather have a bit less boilerplate at the start of source files. Change-Id: Ie0a3b2273383adbb3303faffd6ff96be7f4cae99
1301 lines
35 KiB
C
1301 lines
35 KiB
C
/* ISO7816-3 state machine for the card side
|
|
*
|
|
* (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
|
|
* 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.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "utils.h"
|
|
#include "trace.h"
|
|
#include "iso7816_fidi.h"
|
|
#include "card_emu.h"
|
|
#include "simtrace_prot.h"
|
|
#include "usb_buf.h"
|
|
#include <osmocom/core/linuxlist.h>
|
|
#include <osmocom/core/msgb.h>
|
|
|
|
#ifdef HAVE_SLOT_MUX
|
|
#include "mux.h"
|
|
#endif
|
|
|
|
#define NUM_SLOTS 2
|
|
|
|
/* bit-mask of supported CEMU_FEAT_F_ flags */
|
|
#define SUPPORTED_FEATURES (CEMU_FEAT_F_STATUS_IRQ)
|
|
|
|
#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 */
|
|
};
|
|
|
|
const struct value_string iso7816_3_card_state_names[] = {
|
|
{ ISO_S_WAIT_POWER, "WAIT_POWER" },
|
|
{ ISO_S_WAIT_CLK, "WAIT_CLK" },
|
|
{ ISO_S_WAIT_RST, "WAIT_RST" },
|
|
{ ISO_S_WAIT_ATR, "WAIT_ATR" },
|
|
{ ISO_S_IN_ATR, "IN_ATR" },
|
|
{ ISO_S_IN_PTS, "IN_PTS" },
|
|
{ ISO_S_WAIT_TPDU, "WAIT_TPDU" },
|
|
{ ISO_S_IN_TPDU, "IN_TPDU" },
|
|
{ 0, NULL }
|
|
};
|
|
|
|
|
|
/* 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,
|
|
};
|
|
|
|
const struct value_string pts_state_names[] = {
|
|
{ PTS_S_WAIT_REQ_PTSS, "WAIT_REQ_PTSS" },
|
|
{ PTS_S_WAIT_REQ_PTS0, "WAIT_REQ_PTS0" },
|
|
{ PTS_S_WAIT_REQ_PTS1, "WAIT_REQ_PTS1" },
|
|
{ PTS_S_WAIT_REQ_PTS2, "WAIT_REQ_PTS2" },
|
|
{ PTS_S_WAIT_REQ_PTS3, "WAIT_REQ_PTS3" },
|
|
{ PTS_S_WAIT_REQ_PCK, "WAIT_REQ_PCK" },
|
|
{ PTS_S_WAIT_RESP_PTSS, "WAIT_RESP_PTSS" },
|
|
{ PTS_S_WAIT_RESP_PTS0, "WAIT_RESP_PTS0" },
|
|
{ PTS_S_WAIT_RESP_PTS1, "WAIT_RESP_PTS1" },
|
|
{ PTS_S_WAIT_RESP_PTS2, "WAIT_RESP_PTS2" },
|
|
{ PTS_S_WAIT_RESP_PTS3, "WAIT_RESP_PTS3" },
|
|
{ PTS_S_WAIT_RESP_PCK, "WAIT_RESP_PCK" },
|
|
{ 0, NULL }
|
|
};
|
|
|
|
/* PTS field byte index */
|
|
#define _PTSS 0
|
|
#define _PTS0 1
|
|
#define _PTS1 2
|
|
#define _PTS2 3
|
|
#define _PTS3 4
|
|
#define _PCK 5
|
|
|
|
/* T-PDU state machine states */
|
|
enum tpdu_state {
|
|
TPDU_S_WAIT_CLA, /* waiting for CLA byte from reader */
|
|
TPDU_S_WAIT_INS, /* waiting for INS byte from reader */
|
|
TPDU_S_WAIT_P1, /* waiting for P1 byte from reader */
|
|
TPDU_S_WAIT_P2, /* waiting for P2 byte from reader */
|
|
TPDU_S_WAIT_P3, /* waiting for P3 byte from reader */
|
|
TPDU_S_WAIT_PB, /* waiting for Tx of procedure byte */
|
|
TPDU_S_WAIT_RX, /* waiting for more data from reader */
|
|
TPDU_S_WAIT_TX, /* waiting for more data to reader */
|
|
};
|
|
|
|
const struct value_string tpdu_state_names[] = {
|
|
{ TPDU_S_WAIT_CLA, "WAIT_CLA" },
|
|
{ TPDU_S_WAIT_INS, "WAIT_INS" },
|
|
{ TPDU_S_WAIT_P1, "WAIT_P1" },
|
|
{ TPDU_S_WAIT_P2, "WAIT_P2" },
|
|
{ TPDU_S_WAIT_P3, "WAIT_P3" },
|
|
{ TPDU_S_WAIT_PB, "WAIT_PB" },
|
|
{ TPDU_S_WAIT_RX, "WAIT_RX" },
|
|
{ TPDU_S_WAIT_TX, "WAIT_TX" },
|
|
{ 0, NULL }
|
|
};
|
|
|
|
/* TPDU field byte index */
|
|
#define _CLA 0
|
|
#define _INS 1
|
|
#define _P1 2
|
|
#define _P2 3
|
|
#define _P3 4
|
|
|
|
struct card_handle {
|
|
unsigned int num;
|
|
|
|
/* bit-mask of enabled optional features (CEMU_FEAT_F_*) */
|
|
uint32_t features;
|
|
|
|
enum iso7816_3_card_state state;
|
|
|
|
/* signal levels */
|
|
bool vcc_active; /*< if VCC is active (true = active/ON) */
|
|
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) */
|
|
|
|
/* 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;
|
|
|
|
/*! 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 */
|
|
|
|
/* 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]; /* request bytes */
|
|
uint8_t resp[6]; /* response bytes */
|
|
} pts;
|
|
|
|
/* TPDU */
|
|
struct {
|
|
enum tpdu_state state;
|
|
uint8_t hdr[5]; /* CLA INS P1 P2 P3 */
|
|
} tpdu;
|
|
|
|
struct msgb *uart_rx_msg; /* UART RX -> USB TX */
|
|
struct msgb *uart_tx_msg; /* USB RX -> UART TX */
|
|
|
|
struct llist_head uart_tx_queue;
|
|
|
|
struct {
|
|
uint32_t tx_bytes;
|
|
uint32_t rx_bytes;
|
|
uint32_t pps;
|
|
} stats;
|
|
};
|
|
|
|
/* reset all the 'dynamic' state of the card handle to the initial/default values */
|
|
static void card_handle_reset(struct card_handle *ch)
|
|
{
|
|
struct msgb *msg;
|
|
|
|
card_emu_uart_update_wt(ch->uart_chan, 0);
|
|
|
|
/* release any buffers we may still own */
|
|
if (ch->uart_tx_msg) {
|
|
usb_buf_free(ch->uart_tx_msg);
|
|
ch->uart_tx_msg = NULL;
|
|
}
|
|
if (ch->uart_rx_msg) {
|
|
usb_buf_free(ch->uart_rx_msg);
|
|
ch->uart_rx_msg = NULL;
|
|
}
|
|
while ((msg = msgb_dequeue(&ch->uart_tx_queue))) {
|
|
usb_buf_free(msg);
|
|
}
|
|
}
|
|
|
|
struct llist_head *card_emu_get_uart_tx_queue(struct card_handle *ch)
|
|
{
|
|
return &ch->uart_tx_queue;
|
|
}
|
|
|
|
static void set_tpdu_state(struct card_handle *ch, enum tpdu_state new_ts);
|
|
static void set_pts_state(struct card_handle *ch, enum pts_state new_ptss);
|
|
|
|
/* update simtrace header msg_len and submit USB buffer */
|
|
void usb_buf_upd_len_and_submit(struct msgb *msg)
|
|
{
|
|
struct simtrace_msg_hdr *sh = (struct simtrace_msg_hdr *) msg->l1h;
|
|
|
|
sh->msg_len = msgb_length(msg);
|
|
|
|
usb_buf_submit(msg);
|
|
}
|
|
|
|
/* Allocate USB buffer and push + initialize simtrace_msg_hdr */
|
|
struct msgb *usb_buf_alloc_st(uint8_t ep, uint8_t msg_class, uint8_t msg_type)
|
|
{
|
|
struct msgb *msg = NULL;
|
|
struct simtrace_msg_hdr *sh;
|
|
|
|
while (!msg) {
|
|
msg = usb_buf_alloc(ep); // try to allocate some memory
|
|
if (!msg) { // allocation failed, we might be out of memory
|
|
struct usb_buffered_ep *bep = usb_get_buf_ep(ep);
|
|
if (!bep) {
|
|
TRACE_ERROR("ep %u: %s queue does not exist\n\r",
|
|
ep, __func__);
|
|
return NULL;
|
|
}
|
|
if (llist_empty(&bep->queue)) {
|
|
TRACE_ERROR("ep %u: %s EOMEM (queue already empty)\n\r",
|
|
ep, __func__);
|
|
return NULL;
|
|
}
|
|
msg = msgb_dequeue_count(&bep->queue, &bep->queue_len);
|
|
if (!msg) {
|
|
TRACE_ERROR("ep %u: %s no msg in non-empty queue\n\r",
|
|
ep, __func__);
|
|
return NULL;
|
|
}
|
|
usb_buf_free(msg);
|
|
msg = NULL;
|
|
TRACE_DEBUG("ep %u: %s queue msg dropped\n\r",
|
|
ep, __func__);
|
|
}
|
|
}
|
|
|
|
msg->l1h = msgb_put(msg, sizeof(*sh));
|
|
sh = (struct simtrace_msg_hdr *) msg->l1h;
|
|
memset(sh, 0, sizeof(*sh));
|
|
sh->msg_class = msg_class;
|
|
sh->msg_type = msg_type;
|
|
msg->l2h = msg->l1h + sizeof(*sh);
|
|
|
|
return msg;
|
|
}
|
|
|
|
/* Update cardemu_usb_msg_rx_data length + submit buffer */
|
|
static void flush_rx_buffer(struct card_handle *ch)
|
|
{
|
|
struct msgb *msg;
|
|
struct cardemu_usb_msg_rx_data *rd;
|
|
uint32_t data_len;
|
|
|
|
msg = ch->uart_rx_msg;
|
|
if (!msg)
|
|
return;
|
|
|
|
ch->uart_rx_msg = NULL;
|
|
|
|
/* store length of data payload field in header */
|
|
rd = (struct cardemu_usb_msg_rx_data *) msg->l2h;
|
|
rd->data_len = msgb_l2len(msg) - sizeof(*rd);
|
|
|
|
TRACE_INFO("%u: %s (%u)\n\r",
|
|
ch->num, __func__, rd->data_len);
|
|
|
|
usb_buf_upd_len_and_submit(msg);
|
|
}
|
|
|
|
/* convert a non-contiguous PTS request/response into a contiguous
|
|
* buffer, returning the number of bytes used in the buffer */
|
|
static int serialize_pts(uint8_t *out, const uint8_t *in)
|
|
{
|
|
int i = 0;
|
|
|
|
out[i++] = in[_PTSS];
|
|
out[i++] = in[_PTS0];
|
|
if (in[_PTS0] & (1 << 4))
|
|
out[i++] = in[_PTS1];
|
|
if (in[_PTS0] & (1 << 5))
|
|
out[i++] = in[_PTS2];
|
|
if (in[_PTS0] & (1 << 6))
|
|
out[i++] = in[_PTS3];
|
|
out[i++] = in[_PCK];
|
|
|
|
return i;
|
|
}
|
|
|
|
static uint8_t csum_pts(const uint8_t *in)
|
|
{
|
|
uint8_t out[6];
|
|
int len = serialize_pts(out, in);
|
|
uint8_t csum = 0;
|
|
int i;
|
|
|
|
/* we don't include the PCK byte in the checksumming process */
|
|
len -= 1;
|
|
|
|
for (i = 0; i < len; i++)
|
|
csum = csum ^ out[i];
|
|
|
|
return csum;
|
|
}
|
|
|
|
static void flush_pts(struct card_handle *ch)
|
|
{
|
|
struct msgb *msg;
|
|
struct cardemu_usb_msg_pts_info *ptsi;
|
|
|
|
msg = usb_buf_alloc_st(ch->in_ep, SIMTRACE_MSGC_CARDEM, SIMTRACE_MSGT_DO_CEMU_PTS);
|
|
if (!msg)
|
|
return;
|
|
|
|
ptsi = (struct cardemu_usb_msg_pts_info *) msgb_put(msg, sizeof(*ptsi));
|
|
ptsi->pts_len = serialize_pts(ptsi->req, ch->pts.req);
|
|
serialize_pts(ptsi->resp, ch->pts.resp);
|
|
|
|
usb_buf_upd_len_and_submit(msg);
|
|
}
|
|
|
|
static void emu_update_fidi(struct card_handle *ch)
|
|
{
|
|
int rc;
|
|
|
|
rc = iso7816_3_compute_fd_ratio(ch->F_index, ch->D_index);
|
|
if (rc > 0 && rc < 0x400) {
|
|
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);
|
|
} else
|
|
TRACE_INFO("%u: computed F/D ratio %d unsupported\r\n",
|
|
ch->num, 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)
|
|
{
|
|
if (ch->state == new_state)
|
|
return;
|
|
|
|
TRACE_DEBUG("%u: 7816 card state %s -> %s\r\n", ch->num,
|
|
get_value_string(iso7816_3_card_state_names, ch->state),
|
|
get_value_string(iso7816_3_card_state_names, new_state));
|
|
ch->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);
|
|
/* 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_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 UART timeout mechanism to wait this time.
|
|
* since the initial ETU is Fd=372/Dd=1 clock cycles long, we have to wait 2-107 ETU.
|
|
*/
|
|
card_emu_uart_update_wt(ch->uart_chan, 2);
|
|
break;
|
|
case ISO_S_IN_ATR:
|
|
/* initialize to default WI, this will be overwritten if we
|
|
* send TC2, and it will be programmed into hardware after
|
|
* ATR is finished */
|
|
ch->wi = ISO7816_3_DEFAULT_WI;
|
|
/* update waiting time to initial waiting time */
|
|
ch->waiting_time = ISO7816_3_INIT_WTIME;
|
|
/* set initial 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 */
|
|
card_emu_uart_enable(ch->uart_chan, ENABLE_TX);
|
|
/* 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_PTS:
|
|
case ISO_S_IN_TPDU:
|
|
/* do nothing */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**********************************************************************
|
|
* 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);
|
|
return 1;
|
|
} else { /* The ATR has been completely transmitted */
|
|
/* search for TC2 to updated WI */
|
|
ch->wi = ISO7816_3_DEFAULT_WI;
|
|
if (ch->atr.len >= 2 && ch->atr.atr[1] & 0xf0) { /* Y1 has some data */
|
|
uint8_t atr_td1 = 2;
|
|
if (ch->atr.atr[1] & 0x10) { /* TA1 is present */
|
|
atr_td1++;
|
|
}
|
|
if (ch->atr.atr[1] & 0x20) { /* TB1 is present */
|
|
atr_td1++;
|
|
}
|
|
if (ch->atr.atr[1] & 0x40) { /* TC1 is present */
|
|
atr_td1++;
|
|
}
|
|
if (ch->atr.atr[1] & 0x80) { /* TD1 is present */
|
|
if (ch->atr.len > atr_td1 && ch->atr.atr[atr_td1] & 0xf0) { /* Y2 has some data */
|
|
uint8_t atr_tc2 = atr_td1+1;
|
|
if (ch->atr.atr[atr_td1] & 0x10) { /* TA2 is present */
|
|
atr_tc2++;
|
|
}
|
|
if (ch->atr.atr[atr_td1] & 0x20) { /* TB2 is present */
|
|
atr_tc2++;
|
|
}
|
|
if (ch->atr.atr[atr_td1] & 0x40) { /* TC2 is present */
|
|
if (ch->atr.len > atr_tc2 && ch->atr.atr[atr_tc2]) { /* TC2 encodes WI */
|
|
ch->wi = ch->atr.atr[atr_tc2]; /* set WI */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* 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;
|
|
}
|
|
|
|
/* return number of bytes transmitted */
|
|
return 1;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* PTS / PPS handling
|
|
**********************************************************************/
|
|
|
|
/* Update the PTS sub-state */
|
|
static void set_pts_state(struct card_handle *ch, enum pts_state new_ptss)
|
|
{
|
|
TRACE_DEBUG("%u: 7816 PTS state %s -> %s\r\n", ch->num,
|
|
get_value_string(pts_state_names, ch->pts.state),
|
|
get_value_string(pts_state_names, 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 int
|
|
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;
|
|
if (ch->pts.req[_PCK] != csum_pts(ch->pts.req)) {
|
|
TRACE_ERROR("%u: Error in PTS Checksum!\r\n",
|
|
ch->num);
|
|
/* Wait for the next TPDU */
|
|
set_pts_state(ch, PTS_S_WAIT_REQ_PTSS);
|
|
return ISO_S_WAIT_TPDU;
|
|
}
|
|
/* FIXME: check if proposal matches capabilities in ATR */
|
|
memcpy(ch->pts.resp, ch->pts.req, sizeof(ch->pts.resp));
|
|
break;
|
|
default:
|
|
TRACE_ERROR("%u: process_byte_pts() in invalid PTS state %s\r\n", ch->num,
|
|
get_value_string(pts_state_names, ch->pts.state));
|
|
break;
|
|
}
|
|
/* calculate the next state and set it */
|
|
set_pts_state(ch, next_pts_state(ch));
|
|
|
|
if (ch->pts.state == PTS_S_WAIT_RESP_PTSS) {
|
|
flush_pts(ch);
|
|
/* activate UART TX to transmit PTS response */
|
|
card_emu_uart_enable(ch->uart_chan, ENABLE_TX);
|
|
/* don't fall-through to the 'return ISO_S_IN_PTS'
|
|
* below, rather keep ISO7816 state as-is, it will be
|
|
* further updated by the tx-completion handler */
|
|
return -1;
|
|
}
|
|
|
|
return ISO_S_IN_PTS;
|
|
}
|
|
|
|
/* return a single byte to be transmitted to the reader */
|
|
static int tx_byte_pts(struct card_handle *ch)
|
|
{
|
|
uint8_t byte;
|
|
|
|
/* 1: Determine the next transmit byte */
|
|
switch (ch->pts.state) {
|
|
case PTS_S_WAIT_RESP_PTSS:
|
|
byte = ch->pts.resp[_PTSS];
|
|
break;
|
|
case PTS_S_WAIT_RESP_PTS0:
|
|
byte = ch->pts.resp[_PTS0];
|
|
break;
|
|
case PTS_S_WAIT_RESP_PTS1:
|
|
byte = ch->pts.resp[_PTS1];
|
|
/* This must be TA1 */
|
|
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];
|
|
break;
|
|
case PTS_S_WAIT_RESP_PTS3:
|
|
byte = ch->pts.resp[_PTS3];
|
|
break;
|
|
case PTS_S_WAIT_RESP_PCK:
|
|
byte = ch->pts.resp[_PCK];
|
|
break;
|
|
default:
|
|
TRACE_ERROR("%u: get_byte_pts() in invalid PTS state %s\r\n", ch->num,
|
|
get_value_string(pts_state_names, ch->pts.state));
|
|
return 0;
|
|
}
|
|
|
|
/* 2: Transmit the byte */
|
|
card_emu_uart_tx(ch->uart_chan, byte);
|
|
|
|
/* 3: Update the state */
|
|
|
|
switch (ch->pts.state) {
|
|
case PTS_S_WAIT_RESP_PCK:
|
|
card_emu_uart_wait_tx_idle(ch->uart_chan);
|
|
/* 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 */
|
|
set_pts_state(ch, next_pts_state(ch));
|
|
break;
|
|
}
|
|
|
|
/* return number of bytes transmitted */
|
|
return 1;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* TPDU handling
|
|
**********************************************************************/
|
|
|
|
|
|
/* compute number of data bytes according to Chapter 10.3.2 of 7816-3 */
|
|
static unsigned int t0_num_data_bytes(uint8_t p3, int reader_to_card)
|
|
{
|
|
if (reader_to_card) {
|
|
return p3;
|
|
} else {
|
|
if (p3 == 0)
|
|
return 256;
|
|
else
|
|
return p3;
|
|
}
|
|
}
|
|
|
|
/* add a just-received TPDU byte (from reader) to USB buffer */
|
|
static void add_tpdu_byte(struct card_handle *ch, uint8_t byte)
|
|
{
|
|
struct msgb *msg;
|
|
struct cardemu_usb_msg_rx_data *rd;
|
|
unsigned int num_data_bytes = t0_num_data_bytes(ch->tpdu.hdr[_P3], 0);
|
|
|
|
/* ensure we have a buffer */
|
|
if (!ch->uart_rx_msg) {
|
|
msg = ch->uart_rx_msg = usb_buf_alloc_st(ch->in_ep, SIMTRACE_MSGC_CARDEM,
|
|
SIMTRACE_MSGT_DO_CEMU_RX_DATA);
|
|
if (!ch->uart_rx_msg) {
|
|
TRACE_ERROR("%u: Received UART byte but ENOMEM\r\n",
|
|
ch->num);
|
|
return;
|
|
}
|
|
msgb_put(msg, sizeof(*rd));
|
|
} else
|
|
msg = ch->uart_rx_msg;
|
|
|
|
rd = (struct cardemu_usb_msg_rx_data *) msg->l2h;
|
|
msgb_put_u8(msg, byte);
|
|
|
|
/* check if the buffer is full. If so, send it */
|
|
if (msgb_l2len(msg) >= sizeof(*rd) + num_data_bytes) {
|
|
rd->flags |= CEMU_DATA_F_FINAL;
|
|
flush_rx_buffer(ch);
|
|
/* We need to transmit the SW now, */
|
|
set_tpdu_state(ch, TPDU_S_WAIT_TX);
|
|
} else if (msgb_tailroom(msg) <= 0)
|
|
flush_rx_buffer(ch);
|
|
}
|
|
|
|
static void set_tpdu_state(struct card_handle *ch, enum tpdu_state new_ts)
|
|
{
|
|
if (ch->tpdu.state == new_ts)
|
|
return;
|
|
|
|
TRACE_DEBUG("%u: 7816 TPDU state %s -> %s\r\n", ch->num,
|
|
get_value_string(tpdu_state_names, ch->tpdu.state),
|
|
get_value_string(tpdu_state_names, new_ts));
|
|
ch->tpdu.state = new_ts;
|
|
|
|
switch (new_ts) {
|
|
case TPDU_S_WAIT_CLA:
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
/* we should never reach here */
|
|
assert(0);
|
|
return -1;
|
|
}
|
|
|
|
static void send_tpdu_header(struct card_handle *ch)
|
|
{
|
|
struct msgb *msg;
|
|
struct cardemu_usb_msg_rx_data *rd;
|
|
uint8_t *cur;
|
|
|
|
TRACE_INFO("%u: %s: %02x %02x %02x %02x %02x\r\n",
|
|
ch->num, __func__,
|
|
ch->tpdu.hdr[0], ch->tpdu.hdr[1],
|
|
ch->tpdu.hdr[2], ch->tpdu.hdr[3],
|
|
ch->tpdu.hdr[4]);
|
|
|
|
/* if we already/still have a context, send it off */
|
|
if (ch->uart_rx_msg) {
|
|
TRACE_DEBUG("%u: have old buffer\r\n", ch->num);
|
|
if (msgb_l2len(ch->uart_rx_msg)) {
|
|
TRACE_DEBUG("%u: flushing old buffer\r\n", ch->num);
|
|
flush_rx_buffer(ch);
|
|
}
|
|
}
|
|
TRACE_DEBUG("%u: allocating new buffer\r\n", ch->num);
|
|
/* ensure we have a new buffer */
|
|
ch->uart_rx_msg = usb_buf_alloc_st(ch->in_ep, SIMTRACE_MSGC_CARDEM,
|
|
SIMTRACE_MSGT_DO_CEMU_RX_DATA);
|
|
if (!ch->uart_rx_msg) {
|
|
TRACE_ERROR("%u: %s: ENOMEM\r\n", ch->num, __func__);
|
|
return;
|
|
}
|
|
msg = ch->uart_rx_msg;
|
|
rd = (struct cardemu_usb_msg_rx_data *) msgb_put(msg, sizeof(*rd));
|
|
|
|
/* initialize header */
|
|
rd->flags = CEMU_DATA_F_TPDU_HDR;
|
|
|
|
/* copy TPDU header to data field */
|
|
cur = msgb_put(msg, sizeof(ch->tpdu.hdr));
|
|
memcpy(cur, ch->tpdu.hdr, sizeof(ch->tpdu.hdr));
|
|
/* rd->data_len is set in flush_rx_buffer() */
|
|
|
|
flush_rx_buffer(ch);
|
|
}
|
|
|
|
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;
|
|
set_tpdu_state(ch, next_tpdu_state(ch));
|
|
break;
|
|
case TPDU_S_WAIT_INS:
|
|
ch->tpdu.hdr[_INS] = byte;
|
|
set_tpdu_state(ch, next_tpdu_state(ch));
|
|
break;
|
|
case TPDU_S_WAIT_P1:
|
|
ch->tpdu.hdr[_P1] = byte;
|
|
set_tpdu_state(ch, next_tpdu_state(ch));
|
|
break;
|
|
case TPDU_S_WAIT_P2:
|
|
ch->tpdu.hdr[_P2] = byte;
|
|
set_tpdu_state(ch, next_tpdu_state(ch));
|
|
break;
|
|
case TPDU_S_WAIT_P3:
|
|
ch->tpdu.hdr[_P3] = byte;
|
|
set_tpdu_state(ch, next_tpdu_state(ch));
|
|
/* 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_ERROR("%u: process_byte_tpdu() in invalid TPDU state %s\r\n", ch->num,
|
|
get_value_string(tpdu_state_names, ch->tpdu.state));
|
|
}
|
|
|
|
/* ensure we stay in TPDU ISO state */
|
|
return ISO_S_IN_TPDU;
|
|
}
|
|
|
|
/* tx a single byte to be transmitted to the reader */
|
|
static int tx_byte_tpdu(struct card_handle *ch)
|
|
{
|
|
struct msgb *msg;
|
|
struct cardemu_usb_msg_tx_data *td;
|
|
uint8_t byte;
|
|
|
|
/* ensure we are aware of any data that might be pending for
|
|
* transmit */
|
|
if (!ch->uart_tx_msg) {
|
|
/* uart_tx_queue is filled from main loop, so no need
|
|
* for irq-safe operations */
|
|
if (llist_empty(&ch->uart_tx_queue))
|
|
return 0;
|
|
|
|
/* dequeue first at head */
|
|
ch->uart_tx_msg = msgb_dequeue(&ch->uart_tx_queue);
|
|
ch->uart_tx_msg->l1h = ch->uart_tx_msg->head;
|
|
ch->uart_tx_msg->l2h = ch->uart_tx_msg->l1h + sizeof(struct simtrace_msg_hdr);
|
|
msg = ch->uart_tx_msg;
|
|
/* remove the header */
|
|
msgb_pull(msg, sizeof(struct simtrace_msg_hdr) + sizeof(*td));
|
|
}
|
|
msg = ch->uart_tx_msg;
|
|
td = (struct cardemu_usb_msg_tx_data *) msg->l2h;
|
|
|
|
/* take the next pending byte out of the msgb */
|
|
byte = msgb_pull_u8(msg);
|
|
|
|
card_emu_uart_tx(ch->uart_chan, byte);
|
|
card_emu_uart_reset_wt(ch->uart_chan);
|
|
|
|
/* this must happen _after_ the byte has been transmitted */
|
|
switch (ch->tpdu.state) {
|
|
case TPDU_S_WAIT_PB:
|
|
/* if we just transmitted the procedure byte, we need to decide
|
|
* if we want to continue to receive or transmit */
|
|
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;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* check if the buffer has now been fully transmitted */
|
|
if (msgb_length(msg) == 0) {
|
|
if (td->flags & CEMU_DATA_F_PB_AND_RX) {
|
|
/* we have just sent the procedure byte and now
|
|
* need to continue receiving */
|
|
set_tpdu_state(ch, TPDU_S_WAIT_RX);
|
|
} else {
|
|
/* we have transmitted all bytes */
|
|
if (td->flags & CEMU_DATA_F_FINAL) {
|
|
/* this was the final part of the APDU, go
|
|
* back to state one */
|
|
card_set_state(ch, ISO_S_WAIT_TPDU);
|
|
}
|
|
}
|
|
usb_buf_free(msg);
|
|
ch->uart_tx_msg = NULL;
|
|
}
|
|
|
|
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_TPDU:
|
|
if (byte == 0xff) {
|
|
/* reset PTS to initial state */
|
|
set_pts_state(ch, PTS_S_WAIT_REQ_PTSS);
|
|
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;
|
|
default:
|
|
TRACE_ERROR("%u: Received UART char in invalid 7816 state %s\r\n", ch->num,
|
|
get_value_string(iso7816_3_card_state_names, ch->state));
|
|
break;
|
|
}
|
|
|
|
out_silent:
|
|
if (new_state != -1)
|
|
card_set_state(ch, new_state);
|
|
}
|
|
|
|
/* transmit a single byte to the reader */
|
|
int card_emu_tx_byte(struct card_handle *ch)
|
|
{
|
|
int rc = 0;
|
|
|
|
switch (ch->state) {
|
|
case ISO_S_IN_ATR:
|
|
rc = tx_byte_atr(ch);
|
|
break;
|
|
case ISO_S_IN_PTS:
|
|
rc = tx_byte_pts(ch);
|
|
break;
|
|
case ISO_S_IN_TPDU:
|
|
rc = tx_byte_tpdu(ch);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (rc)
|
|
ch->stats.tx_bytes++;
|
|
|
|
/* if we return 0 here, the UART needs to disable transmit-ready
|
|
* interrupts */
|
|
return rc;
|
|
}
|
|
|
|
void card_emu_have_new_uart_tx(struct card_handle *ch)
|
|
{
|
|
switch (ch->state) {
|
|
case ISO_S_IN_TPDU:
|
|
switch (ch->tpdu.state) {
|
|
case TPDU_S_WAIT_TX:
|
|
case TPDU_S_WAIT_PB:
|
|
card_emu_uart_enable(ch->uart_chan, ENABLE_TX);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void card_emu_report_status(struct card_handle *ch, bool report_on_irq)
|
|
{
|
|
struct msgb *msg;
|
|
struct cardemu_usb_msg_status *sts;
|
|
uint8_t ep = ch->in_ep;
|
|
|
|
if (report_on_irq)
|
|
ep = ch->irq_ep;
|
|
|
|
msg = usb_buf_alloc_st(ep, SIMTRACE_MSGC_CARDEM, SIMTRACE_MSGT_BD_CEMU_STATUS);
|
|
if (!msg)
|
|
return;
|
|
|
|
sts = (struct cardemu_usb_msg_status *) msgb_put(msg, sizeof(*sts));
|
|
sts->flags = 0;
|
|
if (ch->vcc_active)
|
|
sts->flags |= CEMU_STATUS_F_VCC_PRESENT;
|
|
if (ch->clocked)
|
|
sts->flags |= CEMU_STATUS_F_CLK_ACTIVE;
|
|
if (ch->in_reset)
|
|
sts->flags |= CEMU_STATUS_F_RESET_ACTIVE;
|
|
/* FIXME: voltage + card insert */
|
|
sts->F_index = ch->F_index;
|
|
sts->D_index = ch->D_index;
|
|
sts->wi = ch->wi;
|
|
sts->waiting_time = ch->waiting_time;
|
|
|
|
usb_buf_upd_len_and_submit(msg);
|
|
}
|
|
|
|
static void card_emu_report_config(struct card_handle *ch)
|
|
{
|
|
struct msgb *msg;
|
|
struct cardemu_usb_msg_config *cfg;
|
|
uint8_t ep = ch->in_ep;
|
|
|
|
msg = usb_buf_alloc_st(ch->in_ep, SIMTRACE_MSGC_CARDEM, SIMTRACE_MSGT_BD_CEMU_CONFIG);
|
|
if (!msg)
|
|
return;
|
|
|
|
cfg = (struct cardemu_usb_msg_config *) msgb_put(msg, sizeof(*cfg));
|
|
cfg->features = ch->features;
|
|
#ifdef HAVE_SLOT_MUX
|
|
cfg->slot_mux_nr = mux_get_slot();
|
|
#else
|
|
cfg->slot_mux_nr = 0;
|
|
#endif
|
|
|
|
|
|
usb_buf_upd_len_and_submit(msg);
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
uint32_t chg_mask = 0;
|
|
|
|
switch (io) {
|
|
case CARD_IO_VCC:
|
|
if (active == 0 && ch->vcc_active == 1) {
|
|
TRACE_INFO("%u: VCC deactivated\r\n", ch->num);
|
|
card_handle_reset(ch);
|
|
card_set_state(ch, ISO_S_WAIT_POWER);
|
|
chg_mask |= CEMU_STATUS_F_VCC_PRESENT;
|
|
} else if (active == 1 && ch->vcc_active == 0) {
|
|
TRACE_INFO("%u: VCC activated\r\n", ch->num);
|
|
card_set_state(ch, ISO_S_WAIT_CLK);
|
|
chg_mask |= CEMU_STATUS_F_VCC_PRESENT;
|
|
}
|
|
ch->vcc_active = active;
|
|
break;
|
|
case CARD_IO_CLK:
|
|
if (active == 1 && ch->clocked == 0) {
|
|
TRACE_INFO("%u: CLK activated\r\n", ch->num);
|
|
if (ch->state == ISO_S_WAIT_CLK)
|
|
card_set_state(ch, ISO_S_WAIT_RST);
|
|
chg_mask |= CEMU_STATUS_F_CLK_ACTIVE;
|
|
} else if (active == 0 && ch->clocked == 1) {
|
|
TRACE_INFO("%u: CLK deactivated\r\n", ch->num);
|
|
chg_mask |= CEMU_STATUS_F_CLK_ACTIVE;
|
|
}
|
|
ch->clocked = active;
|
|
break;
|
|
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 && ch->state == ISO_S_WAIT_RST) {
|
|
/* prepare to send the ATR */
|
|
card_set_state(ch, ISO_S_WAIT_ATR);
|
|
}
|
|
chg_mask |= CEMU_STATUS_F_RESET_ACTIVE;
|
|
} else if (active && !ch->in_reset) {
|
|
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;
|
|
}
|
|
|
|
switch (ch->state) {
|
|
case ISO_S_WAIT_POWER:
|
|
case ISO_S_WAIT_CLK:
|
|
case ISO_S_WAIT_RST:
|
|
/* check end activation state (even if the reader does
|
|
* not respect the activation sequence) */
|
|
if (ch->vcc_active && ch->clocked && !ch->in_reset) {
|
|
/* prepare to send the ATR */
|
|
card_set_state(ch, ISO_S_WAIT_ATR);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* notify the host about the state change */
|
|
if ((ch->features & CEMU_FEAT_F_STATUS_IRQ) && chg_mask)
|
|
card_emu_report_status(ch, true);
|
|
}
|
|
|
|
/* 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;
|
|
|
|
#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;
|
|
}
|
|
|
|
/* hardware driver informs us that one (more) ETU has expired */
|
|
void card_emu_wtime_half_expired(void *handle)
|
|
{
|
|
struct card_handle *ch = handle;
|
|
/* transmit NULL procedure byte well before waiting time expires */
|
|
switch (ch->state) {
|
|
case ISO_S_IN_TPDU:
|
|
switch (ch->tpdu.state) {
|
|
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;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* hardware driver informs us that one (more) ETU has expired */
|
|
void card_emu_wtime_expired(void *handle)
|
|
{
|
|
struct card_handle *ch = handle;
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* reasonable ATR offering all protocols and voltages
|
|
* smartphones might not care, but other readers do
|
|
*
|
|
* TS = 0x3B Direct Convention
|
|
* T0 = 0x80 Y(1): b1000, K: 0 (historical bytes)
|
|
* TD(1) = 0x80 Y(i+1) = b1000, Protocol T=0
|
|
* ----
|
|
* TD(2) = 0x81 Y(i+1) = b1000, Protocol T=1
|
|
* ----
|
|
* TD(3) = 0x1F Y(i+1) = b0001, Protocol T=15
|
|
* ----
|
|
* TA(4) = 0xC7 Clock stop: no preference - Class accepted by the card: (3G) A 5V B 3V C 1.8V
|
|
* ----
|
|
* Historical bytes
|
|
* TCK = 0x59 correct checksum
|
|
*/
|
|
static const uint8_t default_atr[] = { 0x3B, 0x80, 0x80, 0x81 , 0x1F, 0xC7, 0x59 };
|
|
|
|
static struct card_handle card_handles[NUM_SLOTS];
|
|
|
|
int card_emu_set_config(struct card_handle *ch, const struct cardemu_usb_msg_config *scfg,
|
|
unsigned int scfg_len)
|
|
{
|
|
if (scfg_len >= sizeof(uint32_t))
|
|
ch->features = (scfg->features & SUPPORTED_FEATURES);
|
|
|
|
#ifdef HAVE_SLOT_MUX
|
|
if (scfg_len >= sizeof(uint32_t)+sizeof(uint8_t)) {
|
|
mux_set_slot(scfg->slot_mux_nr);
|
|
}
|
|
#endif
|
|
|
|
/* send back a report of our current configuration */
|
|
card_emu_report_config(ch);
|
|
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
|
|
if (slot_num >= ARRAY_SIZE(card_handles))
|
|
return NULL;
|
|
|
|
ch = &card_handles[slot_num];
|
|
|
|
memset(ch, 0, sizeof(*ch));
|
|
|
|
INIT_LLIST_HEAD(&ch->uart_tx_queue);
|
|
|
|
ch->num = slot_num;
|
|
ch->irq_ep = irq_ep;
|
|
ch->in_ep = in_ep;
|
|
ch->state = ISO_S_WAIT_POWER;
|
|
ch->vcc_active = vcc_active;
|
|
ch->in_reset = in_reset;
|
|
ch->clocked = clocked;
|
|
|
|
ch->Fi_index = ch->F_index = 1;
|
|
ch->Di_index = ch->D_index = 1;
|
|
ch->wi = ISO7816_3_DEFAULT_WI;
|
|
|
|
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;
|
|
|
|
card_handle_reset(ch);
|
|
|
|
return ch;
|
|
}
|