mirror of
https://gitea.osmocom.org/sim-card/simtrace2.git
synced 2026-03-18 22:38:32 +03:00
cardem: use USART timeout for waiting time
the reset/ATR handling has been heavily updated/fixed. 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 the clock signal is not connected to the timer counter. thus this change adds card emulation support for SIMtrace boards. Fi and Di have been properly rename to F and D since the "i" stands only for an "indicated" value, not the actual value. this does not change the USB protocol (the variable have just been renamed). additional variables store more information about the card capabilities NOTE: it has only be tested for the SIMtrace board Change-Id: Ibcb2c8cace9137695adf5fb3de43566f7cfb93b5
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
#include "simtrace.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "card_emu.h"
|
||||
#include "iso7816_fidi.h"
|
||||
#include "iso7816_3.h"
|
||||
#include "utils.h"
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
@@ -54,11 +54,15 @@ struct cardem_inst {
|
||||
struct card_handle *ch;
|
||||
struct llist_head usb_out_queue;
|
||||
struct ringbuf rb;
|
||||
uint32_t wt; /*!< receiver waiting time to trigger timeout (0 to deactivate it) */
|
||||
uint32_t wt_remaining; /*!< remaining waiting time */
|
||||
bool wt_halfed; /*!< if at least half of the waiting time passed */
|
||||
struct Usart_info usart_info;
|
||||
int usb_pending_old;
|
||||
uint8_t ep_out;
|
||||
uint8_t ep_in;
|
||||
uint8_t ep_int;
|
||||
const Pin pin_io;
|
||||
const Pin pin_insert;
|
||||
uint32_t vcc_uv;
|
||||
uint32_t vcc_uv_last;
|
||||
@@ -75,6 +79,7 @@ struct cardem_inst cardem_inst[] = {
|
||||
.ep_out = SIMTRACE_CARDEM_USB_EP_USIM1_DATAOUT,
|
||||
.ep_in = SIMTRACE_CARDEM_USB_EP_USIM1_DATAIN,
|
||||
.ep_int = SIMTRACE_CARDEM_USB_EP_USIM1_INT,
|
||||
.pin_io = PIN_USIM1_IO,
|
||||
#ifdef PIN_SET_USIM1_PRES
|
||||
.pin_insert = PIN_SET_USIM1_PRES,
|
||||
#endif
|
||||
@@ -90,6 +95,7 @@ struct cardem_inst cardem_inst[] = {
|
||||
.ep_out = SIMTRACE_CARDEM_USB_EP_USIM2_DATAOUT,
|
||||
.ep_in = SIMTRACE_CARDEM_USB_EP_USIM2_DATAIN,
|
||||
.ep_int = SIMTRACE_CARDEM_USB_EP_USIM2_INT,
|
||||
.pin_io = PIN_USIM2_IO,
|
||||
#ifdef PIN_SET_USIM2_PRES
|
||||
.pin_insert = PIN_SET_USIM2_PRES,
|
||||
#endif
|
||||
@@ -140,7 +146,7 @@ void card_emu_uart_enable(uint8_t uart_chan, uint8_t rxtx)
|
||||
* 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_RX:
|
||||
@@ -150,7 +156,7 @@ void card_emu_uart_enable(uint8_t uart_chan, uint8_t rxtx)
|
||||
USART_SetTransmitterEnabled(usart, 1);
|
||||
wait_tx_idle(usart);
|
||||
usart->US_CR = US_CR_RSTSTA | US_CR_RSTIT | US_CR_RSTNACK;
|
||||
USART_EnableIt(usart, US_IER_RXRDY);
|
||||
USART_EnableIt(usart, US_IER_RXRDY | US_IER_TIMEOUT);
|
||||
USART_SetReceiverEnabled(usart, 1);
|
||||
break;
|
||||
case 0:
|
||||
@@ -191,7 +197,10 @@ int card_emu_uart_tx(uint8_t uart_chan, uint8_t byte)
|
||||
/* FIXME: integrate this with actual irq handler */
|
||||
static void usart_irq_rx(uint8_t inst_num)
|
||||
{
|
||||
OSMO_ASSERT(inst_num < ARRAY_SIZE(cardem_inst));
|
||||
if (inst_num >= ARRAY_SIZE(cardem_inst)) {
|
||||
TRACE_ERROR("%u: UART channel out of bounds\r\n", inst_num);
|
||||
return;
|
||||
}
|
||||
Usart *usart = get_usart_by_chan(inst_num);
|
||||
struct cardem_inst *ci = &cardem_inst[inst_num];
|
||||
uint32_t csr;
|
||||
@@ -210,10 +219,32 @@ static void usart_irq_rx(uint8_t inst_num)
|
||||
USART_DisableIt(usart, US_IER_TXRDY); // stop the TX ready signal if not byte has been transmitted
|
||||
}
|
||||
|
||||
if (csr & (US_CSR_OVRE|US_CSR_FRAME|US_CSR_PARE|US_CSR_TIMEOUT|US_CSR_NACK|(1<<10))) { // error flag set
|
||||
if (csr & (US_CSR_OVRE | US_CSR_FRAME | US_CSR_PARE | US_CSR_NACK | (1 << 10))) { // error flag set
|
||||
usart->US_CR = US_CR_RSTSTA | US_CR_RSTIT | US_CR_RSTNACK; // reset UART state to clear flag
|
||||
TRACE_ERROR("%u USART error on 0x%x status: 0x%lx\n", ci->num, byte, csr); // warn user about error
|
||||
}
|
||||
|
||||
// handle timeout
|
||||
if (csr & US_CSR_TIMEOUT) { // RX has been inactive for some time
|
||||
if (ci->wt_remaining <= (usart->US_RTOR & 0xffff)) { // waiting time has passed
|
||||
ci->wt_remaining = 0; // timeout reached (will stop the timer)
|
||||
} else {
|
||||
ci->wt_remaining -= (usart->US_RTOR & 0xffff); // be sure to subtract the actual timeout since the new might not have been set and reloaded yet
|
||||
}
|
||||
if (0 == ci->wt_remaining) {
|
||||
card_emu_wt_expired(ci->ch); // let the state know WT has expired
|
||||
} else if (ci->wt_remaining <= ci->wt / 2 && !ci->wt_halfed) {
|
||||
ci->wt_halfed = true;
|
||||
card_emu_wt_halfed(ci->ch); // let the state know WT has half expired
|
||||
}
|
||||
if (ci->wt_remaining > 0xffff) { // value exceeds the USART TO range
|
||||
usart->US_RTOR = 0xffff; // use the MAX
|
||||
} else {
|
||||
usart->US_RTOR = ci->wt_remaining;
|
||||
}
|
||||
usart->US_CR |= US_CR_STTTO; // clear timeout flag (and stop timeout until next character is received)
|
||||
usart->US_CR |= US_CR_RETTO; // restart the counter (it wt is 0, the timeout is not started)
|
||||
}
|
||||
}
|
||||
|
||||
/*! ISR called for USART0 */
|
||||
@@ -230,16 +261,90 @@ void mode_cardemu_usart1_irq(void)
|
||||
usart_irq_rx(0);
|
||||
}
|
||||
|
||||
/* call-back from card_emu.c to change UART baud rate */
|
||||
int card_emu_uart_update_fidi(uint8_t uart_chan, unsigned int fidi)
|
||||
{
|
||||
int rc;
|
||||
Usart *usart = get_usart_by_chan(uart_chan);
|
||||
// call-back from card_emu.c to change UART baud rate
|
||||
|
||||
usart->US_CR |= US_CR_RXDIS | US_CR_RSTRX;
|
||||
usart->US_FIDI = fidi & 0x3ff;
|
||||
usart->US_CR |= US_CR_RXEN | US_CR_STTTO;
|
||||
return 0;
|
||||
void card_emu_uart_update_fd(uint8_t uart_chan, uint16_t f, uint8_t d)
|
||||
{
|
||||
Usart *usart = get_usart_by_chan(uart_chan); // get the USART based on the card handle
|
||||
if (NULL == usart) {
|
||||
TRACE_ERROR("%u: USART not found by chan\r\n", uart_chan);
|
||||
return;
|
||||
}
|
||||
if (!iso7816_3_valid_f(f)) {
|
||||
TRACE_ERROR("%u: invalid F: %u\r\n", uart_chan, f);
|
||||
return;
|
||||
}
|
||||
if (!iso7816_3_valid_d(d)) {
|
||||
TRACE_ERROR("%u: invalid D: %u\r\n", uart_chan, d);
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t ratio = f / d;
|
||||
if (ratio > 0 && ratio < 2048) {
|
||||
/* make sure USART uses new F/D ratio */
|
||||
usart->US_CR |= US_CR_RXDIS | US_CR_RSTRX; // disable USART before changing baud rate
|
||||
usart->US_FIDI = (ratio & 0x7ff); // change baud rate (ratio)
|
||||
usart->US_CR |= US_CR_RXEN | US_CR_STTTO; // re-enable USART (and stop timeout)
|
||||
TRACE_INFO("%u: USART F/D set to %u/%u\r\n", uart_chan, f, d);
|
||||
} else {
|
||||
TRACE_ERROR("%u: USART could not set F/D to %u/%u\r\n", uart_chan, f, d);
|
||||
// TODO become unresponsive
|
||||
}
|
||||
}
|
||||
|
||||
void card_emu_uart_update_wt(uint8_t uart_chan, uint32_t wt)
|
||||
{
|
||||
if (uart_chan >= ARRAY_SIZE(cardem_inst)) {
|
||||
TRACE_ERROR("%u: UART channel out of bounds\r\n", uart_chan);
|
||||
return;
|
||||
}
|
||||
struct cardem_inst *ci = &cardem_inst[uart_chan];
|
||||
Usart *usart = get_usart_by_chan(uart_chan); // get the USART based on the card handle
|
||||
if (NULL == usart) {
|
||||
TRACE_ERROR("%u: USART not found by chan\r\n", uart_chan);
|
||||
return;
|
||||
}
|
||||
|
||||
ci->wt = wt; // save value
|
||||
card_emu_uart_reset_wt(uart_chan); // reset and start timer
|
||||
TRACE_INFO("%u: USART WT set to %lu ETU\r\n", uart_chan, wt);
|
||||
}
|
||||
|
||||
void card_emu_uart_reset_wt(uint8_t uart_chan)
|
||||
{
|
||||
if (uart_chan >= ARRAY_SIZE(cardem_inst)) {
|
||||
TRACE_ERROR("%u: UART channel out of bounds\r\n", uart_chan);
|
||||
return;
|
||||
}
|
||||
struct cardem_inst *ci = &cardem_inst[uart_chan];
|
||||
Usart *usart = get_usart_by_chan(uart_chan); // get the USART based on the card handle
|
||||
if (NULL == usart) {
|
||||
TRACE_ERROR("%u: USART not found by chan\r\n", uart_chan);
|
||||
return;
|
||||
}
|
||||
|
||||
ci->wt_remaining = ci->wt; // reload WT value
|
||||
ci->wt_halfed = false; // reset half expired
|
||||
if (ci->wt_remaining > 0xffff) { // value exceeds the USART TO range
|
||||
usart->US_RTOR = 0xffff; // use the MAX
|
||||
} else {
|
||||
usart->US_RTOR = ci->wt_remaining;
|
||||
}
|
||||
usart->US_CR |= US_CR_RETTO; // restart the counter (if wt is 0, the timeout is not started)
|
||||
}
|
||||
|
||||
void card_emu_uart_io_set(uint8_t uart_chan, bool set)
|
||||
{
|
||||
if (uart_chan >= ARRAY_SIZE(cardem_inst)) {
|
||||
TRACE_ERROR("%u: UART channel out of bounds\r\n", uart_chan);
|
||||
return;
|
||||
}
|
||||
struct cardem_inst *ci = &cardem_inst[uart_chan];
|
||||
if (set) {
|
||||
PIO_Set(&ci->pin_io);
|
||||
} else {
|
||||
PIO_Clear(&ci->pin_io);
|
||||
}
|
||||
}
|
||||
|
||||
/* call-back from card_emu.c to force a USART interrupt */
|
||||
@@ -425,6 +530,10 @@ void mode_cardemu_init(void)
|
||||
#endif /* DETECT_VCC_BY_ADC */
|
||||
cardem_inst[0].ch = card_emu_init(0, 2, 0, SIMTRACE_CARDEM_USB_EP_USIM1_DATAIN, SIMTRACE_CARDEM_USB_EP_USIM1_INT);
|
||||
sim_switch_use_physical(0, 1);
|
||||
#ifndef DETECT_VCC_BY_ADC
|
||||
usim1_vcc_irqhandler(NULL); // check VCC/CLK state
|
||||
#endif
|
||||
usim1_rst_irqhandler(NULL); // force RST state
|
||||
|
||||
#ifdef CARDEMU_SECOND_UART
|
||||
INIT_LLIST_HEAD(&cardem_inst[1].usb_out_queue);
|
||||
|
||||
Reference in New Issue
Block a user