diff --git a/firmware/apps/cardem/Makefile b/firmware/apps/cardem/Makefile index 75c43e8e..c0d903fc 100644 --- a/firmware/apps/cardem/Makefile +++ b/firmware/apps/cardem/Makefile @@ -1,3 +1,3 @@ C_FILES += $(C_LIBUSB_RT) -C_FILES += card_emu.c cciddriver.c iso7816_4.c iso7816_fidi.c mitm.c mode_cardemu.c mode_ccid.c simtrace_iso7816.c sniffer.c tc_etu.c usb.c +C_FILES += card_emu.c cciddriver.c iso7816_4.c iso7816_fidi.c mitm.c mode_cardemu.c mode_ccid.c simtrace_iso7816.c sniffer.c usb.c diff --git a/firmware/apps/trace/Makefile b/firmware/apps/trace/Makefile index 75c43e8e..c0d903fc 100644 --- a/firmware/apps/trace/Makefile +++ b/firmware/apps/trace/Makefile @@ -1,3 +1,3 @@ C_FILES += $(C_LIBUSB_RT) -C_FILES += card_emu.c cciddriver.c iso7816_4.c iso7816_fidi.c mitm.c mode_cardemu.c mode_ccid.c simtrace_iso7816.c sniffer.c tc_etu.c usb.c +C_FILES += card_emu.c cciddriver.c iso7816_4.c iso7816_fidi.c mitm.c mode_cardemu.c mode_ccid.c simtrace_iso7816.c sniffer.c usb.c diff --git a/firmware/apps/triple_play/Makefile b/firmware/apps/triple_play/Makefile index df89448d..be06c118 100644 --- a/firmware/apps/triple_play/Makefile +++ b/firmware/apps/triple_play/Makefile @@ -1,3 +1,3 @@ C_FILES += $(C_LIBUSB_RT) -C_FILES += card_emu.c iso7816_4.c iso7816_fidi.c mitm.c mode_cardemu.c mode_ccid.c simtrace_iso7816.c sniffer.c tc_etu.c usb.c +C_FILES += card_emu.c iso7816_4.c iso7816_fidi.c mitm.c mode_cardemu.c mode_ccid.c simtrace_iso7816.c sniffer.c usb.c diff --git a/firmware/libcommon/include/card_emu.h b/firmware/libcommon/include/card_emu.h index 8a73d5ca..58e24cd3 100644 --- a/firmware/libcommon/include/card_emu.h +++ b/firmware/libcommon/include/card_emu.h @@ -31,7 +31,6 @@ enum card_io { /** initialise card slot * @param[in] slot_num slot number (arbitrary number) - * @param[in] tc_chan timer counter channel (to measure the ETU) * @param[in] uart_chan UART peripheral channel * @param[in] in_ep USB IN end point number * @param[in] irq_ep USB INTerrupt end point number @@ -40,7 +39,7 @@ enum card_io { * @param[in] clocked initial CLK signat state (true = active) * @return main card handle reference */ -struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uart_chan, uint8_t in_ep, uint8_t irq_ep, bool vcc_active, bool in_reset, bool clocked); +struct card_handle *card_emu_init(uint8_t slot_num, uint8_t uart_chan, uint8_t in_ep, uint8_t irq_ep, bool vcc_active, bool in_reset, bool clocked); /* process a single byte received from the reader */ void card_emu_process_rx_byte(struct card_handle *ch, uint8_t byte); @@ -58,10 +57,17 @@ struct llist_head *card_emu_get_uart_tx_queue(struct card_handle *ch); void card_emu_have_new_uart_tx(struct card_handle *ch); void card_emu_report_status(struct card_handle *ch, bool report_on_irq); -#define ENABLE_TX 0x01 -#define ENABLE_RX 0x02 +void card_emu_wtime_half_expired(void *ch); +void card_emu_wtime_expired(void *ch); + + +#define ENABLE_TX 0x01 +#define ENABLE_RX 0x02 +#define ENABLE_TX_TIMER_ONLY 0x03 int card_emu_uart_update_fidi(uint8_t uart_chan, unsigned int fidi); +void card_emu_uart_update_wt(uint8_t uart_chan, uint32_t wt); +void card_emu_uart_reset_wt(uint8_t uart_chan); int card_emu_uart_tx(uint8_t uart_chan, uint8_t byte); void card_emu_uart_enable(uint8_t uart_chan, uint8_t rxtx); void card_emu_uart_wait_tx_idle(uint8_t uart_chan); diff --git a/firmware/libcommon/source/card_emu.c b/firmware/libcommon/source/card_emu.c index 0ba6eb04..cefc9572 100644 --- a/firmware/libcommon/source/card_emu.c +++ b/firmware/libcommon/source/card_emu.c @@ -27,7 +27,6 @@ #include "utils.h" #include "trace.h" #include "iso7816_fidi.h" -#include "tc_etu.h" #include "card_emu.h" #include "simtrace_prot.h" #include "usb_buf.h" @@ -177,7 +176,6 @@ struct card_handle { * \note this depends on Fi, Di, and WI if T=0 is used */ uint32_t waiting_time; /* in etu */ - uint8_t tc_chan; /* TC channel number */ uint8_t uart_chan; /* UART channel */ uint8_t in_ep; /* USB IN EP */ @@ -222,7 +220,7 @@ static void card_handle_reset(struct card_handle *ch) { struct msgb *msg; - tc_etu_disable(ch->tc_chan); + card_emu_uart_update_wt(ch->uart_chan, 0); /* release any buffers we may still own */ if (ch->uart_tx_msg) { @@ -383,8 +381,6 @@ static void emu_update_fidi(struct card_handle *ch) ch->F_index, ch->D_index, rc); /* make sure UART uses new F/D ratio */ card_emu_uart_update_fidi(ch->uart_chan, rc); - /* notify ETU timer about this */ - tc_etu_set_etu(ch->tc_chan, rc); } else TRACE_INFO("%u: computed F/D ratio %d unsupported\r\n", ch->num, rc); @@ -408,6 +404,8 @@ static void card_set_state(struct card_handle *ch, case ISO_S_WAIT_RST: /* disable Rx and Tx of UART */ card_emu_uart_enable(ch->uart_chan, 0); + /* disable timeout */ + card_emu_uart_update_wt(ch->uart_chan, 0); break; case ISO_S_WAIT_ATR: /* Reset to initial Fi / Di ratio */ @@ -416,13 +414,13 @@ static void card_set_state(struct card_handle *ch, ch->wi = ISO7816_3_DEFAULT_WI; ch->waiting_time = ISO7816_3_INIT_WTIME; emu_update_fidi(ch); + /* enable TX to be able to use the timeout */ + card_emu_uart_enable(ch->uart_chan, ENABLE_TX_TIMER_ONLY); /* the ATR should only be sent 400 to 40k clock cycles after the RESET. - * we use the tc_etu mechanism to wait this time. + * we use the UART timeout mechanism to wait this time. * since the initial ETU is Fd=372/Dd=1 clock cycles long, we have to wait 2-107 ETU. */ - tc_etu_set_wtime(ch->tc_chan, 2); - /* enable the TC/ETU counter once reset has been released */ - tc_etu_enable(ch->tc_chan); + card_emu_uart_update_wt(ch->uart_chan, 2); break; case ISO_S_IN_ATR: /* initialize to default WI, this will be overwritten if we @@ -432,7 +430,7 @@ static void card_set_state(struct card_handle *ch, /* update waiting time to initial waiting time */ ch->waiting_time = ISO7816_3_INIT_WTIME; /* set initial waiting time */ - tc_etu_set_wtime(ch->tc_chan, ch->waiting_time); + card_emu_uart_update_wt(ch->uart_chan, ch->waiting_time); /* Set ATR sub-state to initial state */ ch->atr.idx = 0; /* enable USART transmission to reader */ @@ -509,7 +507,6 @@ static int tx_byte_atr(struct card_handle *ch) } /* update waiting time (see ISO 7816-3 10.2) */ ch->waiting_time = ch->wi * 960 * iso7816_3_fi_table[ch->F_index]; - tc_etu_set_wtime(ch->tc_chan, ch->waiting_time); /* go to next state */ card_set_state(ch, ISO_S_WAIT_TPDU); return 0; @@ -677,6 +674,7 @@ static int tx_byte_pts(struct card_handle *ch) 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 */ @@ -752,14 +750,28 @@ static void set_tpdu_state(struct card_handle *ch, enum tpdu_state new_ts) switch (new_ts) { case TPDU_S_WAIT_CLA: - case TPDU_S_WAIT_RX: + /* switch back to receiving mode */ card_emu_uart_enable(ch->uart_chan, ENABLE_RX); + /* disable waiting time since we don't expect any data */ + card_emu_uart_update_wt(ch->uart_chan, 0); + break; + case TPDU_S_WAIT_INS: + /* start waiting for the rest of the header/body */ + card_emu_uart_update_wt(ch->uart_chan, ch->waiting_time); + break; + case TPDU_S_WAIT_RX: + /* switch to receive mode to receive the body */ + card_emu_uart_enable(ch->uart_chan, ENABLE_RX); + /* start waiting for the body */ + card_emu_uart_update_wt(ch->uart_chan, ch->waiting_time); break; case TPDU_S_WAIT_PB: /* we just completed the TPDU header from reader to card * and now need to disable the receiver, enable the * transmitter and transmit the procedure byte */ card_emu_uart_enable(ch->uart_chan, ENABLE_TX); + /* prepare to extend the waiting time once half of it is reached */ + card_emu_uart_update_wt(ch->uart_chan, ch->waiting_time); break; default: break; @@ -1103,8 +1115,6 @@ void card_emu_io_statechg(struct card_handle *ch, enum card_io io, int active) 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) { - /* enable the TC/ETU counter once reset has been released */ - tc_etu_enable(ch->tc_chan); /* prepare to send the ATR */ card_set_state(ch, ISO_S_WAIT_ATR); } @@ -1163,7 +1173,7 @@ int card_emu_set_atr(struct card_handle *ch, const uint8_t *atr, uint8_t len) } /* hardware driver informs us that one (more) ETU has expired */ -void tc_etu_wtime_half_expired(void *handle) +void card_emu_wtime_half_expired(void *handle) { struct card_handle *ch = handle; /* transmit NULL procedure byte well before waiting time expires */ @@ -1173,7 +1183,10 @@ void tc_etu_wtime_half_expired(void *handle) case TPDU_S_WAIT_PB: case TPDU_S_WAIT_TX: putchar('N'); + /* we are waiting for data from the user. Send a procedure byte to ask the + * reader to wait more time */ card_emu_uart_tx(ch->uart_chan, ISO7816_3_PB_NULL); + card_emu_uart_reset_wt(ch->uart_chan); break; default: break; @@ -1185,7 +1198,7 @@ void tc_etu_wtime_half_expired(void *handle) } /* hardware driver informs us that one (more) ETU has expired */ -void tc_etu_wtime_expired(void *handle) +void card_emu_wtime_expired(void *handle) { struct card_handle *ch = handle; switch (ch->state) { @@ -1231,7 +1244,7 @@ int card_emu_set_config(struct card_handle *ch, const struct cardemu_usb_msg_con return 0; } -struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uart_chan, uint8_t in_ep, uint8_t irq_ep, bool vcc_active, bool in_reset, bool clocked) +struct card_handle *card_emu_init(uint8_t slot_num, uint8_t uart_chan, uint8_t in_ep, uint8_t irq_ep, bool vcc_active, bool in_reset, bool clocked) { struct card_handle *ch; @@ -1256,7 +1269,6 @@ struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uar ch->Di_index = ch->D_index = 1; ch->wi = ISO7816_3_DEFAULT_WI; - ch->tc_chan = tc_chan; ch->uart_chan = uart_chan; ch->waiting_time = ISO7816_3_INIT_WTIME; @@ -1269,7 +1281,5 @@ struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uar card_handle_reset(ch); - tc_etu_init(ch->tc_chan, ch); - return ch; } diff --git a/firmware/libcommon/source/mode_cardemu.c b/firmware/libcommon/source/mode_cardemu.c index 4886983d..8531eb57 100644 --- a/firmware/libcommon/source/mode_cardemu.c +++ b/firmware/libcommon/source/mode_cardemu.c @@ -55,6 +55,14 @@ struct cardem_inst { struct llist_head usb_out_queue; struct ringbuf rb; struct Usart_info usart_info; + struct { + /*! receiver waiting time to trigger timeout (0 to deactivate it) */ + uint32_t total; + /*! remaining waiting time (we may need multiple timer runs to reach total */ + uint32_t remaining; + /*! did we already notify about half the time having expired? */ + bool half_time_notified; + } wt; int usb_pending_old; uint8_t ep_out; uint8_t ep_in; @@ -140,12 +148,23 @@ void card_emu_uart_enable(uint8_t uart_chan, uint8_t rxtx) Usart *usart = get_usart_by_chan(uart_chan); switch (rxtx) { case ENABLE_TX: - USART_DisableIt(usart, ~US_IER_TXRDY); + USART_DisableIt(usart, ~(US_IER_TXRDY | US_IER_TIMEOUT)); /* as irritating as it is, we actually want to keep the * receiver enabled during transmit */ USART_SetReceiverEnabled(usart, 1); usart->US_CR = US_CR_RSTSTA | US_CR_RSTIT | US_CR_RSTNACK; - USART_EnableIt(usart, US_IER_TXRDY); + USART_EnableIt(usart, US_IER_TXRDY | US_IER_TIMEOUT); + USART_SetTransmitterEnabled(usart, 1); + break; + case ENABLE_TX_TIMER_ONLY: + /* enable the transmitter without generating TXRDY interrupts + * just so that the timer can run */ + USART_DisableIt(usart, ~US_IER_TIMEOUT); + /* as irritating as it is, we actually want to keep the + * receiver enabled during transmit */ + USART_SetReceiverEnabled(usart, 1); + usart->US_CR = US_CR_RSTSTA | US_CR_RSTIT | US_CR_RSTNACK; + USART_EnableIt(usart, US_IER_TIMEOUT); USART_SetTransmitterEnabled(usart, 1); break; case ENABLE_RX: @@ -225,11 +244,40 @@ static void usart_irq_rx(uint8_t inst_num) } /* check if any error flags are set */ - if (csr & (US_CSR_OVRE|US_CSR_FRAME|US_CSR_PARE|US_CSR_TIMEOUT|US_CSR_NACK|(1<<10))) { + if (csr & (US_CSR_OVRE|US_CSR_FRAME|US_CSR_PARE|US_CSR_NACK|(1<<10))) { /* clear any error flags */ usart->US_CR = US_CR_RSTSTA | US_CR_RSTIT | US_CR_RSTNACK; TRACE_ERROR("%u USART error on 0x%x status: 0x%lx\n", ci->num, byte, csr); } + + /* check if the timeout has expired. We "abuse" the receive timer for tracking + * how many etu have expired since we last sent a byte. See section + * 33.7.3.11 "Receiver Time-out" of the SAM3S8 Data Sheet */ + if (csr & US_CSR_TIMEOUT) { + /* RX has been inactive for some time */ + if (ci->wt.remaining <= (usart->US_RTOR & 0xffff)) { + /* waiting time is over; will stop the timer */ + ci->wt.remaining = 0; + } else { + /* subtract the actual timeout since the new might not have been set and + * reloaded yet */ + ci->wt.remaining -= (usart->US_RTOR & 0xffff); + } + if (ci->wt.remaining == 0) { + /* let the FSM know that WT has expired */ + card_emu_wtime_expired(ci->ch); + } else if (ci->wt.remaining <= ci->wt.total / 2 && !ci->wt.half_time_notified) { + /* let the FS know that half of the WT has expired */ + card_emu_wtime_half_expired(ci->ch); + ci->wt.half_time_notified = true; + } + /* if value exceeds the USART TO range, use the maximum for now */ + usart->US_RTOR = OSMO_MIN(ci->wt.remaining, 0xffff); + /* clear timeout flag (and stop timeout until next character is received) */ + usart->US_CR |= US_CR_STTTO; + /* restart the counter (it wt is 0, the timeout is not started) */ + usart->US_CR |= US_CR_RETTO; + } } /*! ISR called for USART0 */ @@ -258,6 +306,42 @@ int card_emu_uart_update_fidi(uint8_t uart_chan, unsigned int fidi) return 0; } +/*! Update WT on USART peripheral. Will automatically re-start timer with new value. + * \param[in] usart USART peripheral to configure + * \param[in] wt inactivity Waiting Time before card_emu_wtime_expired is called (0 to disable) */ +void card_emu_uart_update_wt(uint8_t uart_chan, uint32_t wt) +{ + OSMO_ASSERT(uart_chan < ARRAY_SIZE(cardem_inst)); + struct cardem_inst *ci = &cardem_inst[uart_chan]; + Usart *usart = get_usart_by_chan(uart_chan); + + if (ci->wt.total != wt) { + TRACE_DEBUG("%u: USART WT changed from %lu to %lu ETU\r\n", uart_chan, + ci->wt.total, wt); + } + + ci->wt.total = wt; + /* reset and start the timer */ + card_emu_uart_reset_wt(uart_chan); +} + +/*! Reset and re-start waiting timeout count down on USART peripheral. + * \param[in] usart USART peripheral to configure */ +void card_emu_uart_reset_wt(uint8_t uart_chan) +{ + OSMO_ASSERT(uart_chan < ARRAY_SIZE(cardem_inst)); + struct cardem_inst *ci = &cardem_inst[uart_chan]; + Usart *usart = get_usart_by_chan(uart_chan); + + /* FIXME: guard against race with interrupt handler */ + ci->wt.remaining = ci->wt.total; + ci->wt.half_time_notified = false; + /* if value exceeds the USART TO range, use the maximum for now */ + usart->US_RTOR = OSMO_MIN(ci->wt.remaining, 0xffff); + /* restart the counter (if wt is 0, the timeout is not started) */ + usart->US_CR |= US_CR_RETTO; +} + /* call-back from card_emu.c to force a USART interrupt */ void card_emu_uart_interrupt(uint8_t uart_chan) { @@ -450,7 +534,7 @@ void mode_cardemu_init(void) do {} while (!adc_triggered); /* wait for first ADC reading */ #endif /* DETECT_VCC_BY_ADC */ - cardem_inst[0].ch = card_emu_init(0, 2, 0, SIMTRACE_CARDEM_USB_EP_USIM1_DATAIN, + cardem_inst[0].ch = card_emu_init(0, 0, SIMTRACE_CARDEM_USB_EP_USIM1_DATAIN, SIMTRACE_CARDEM_USB_EP_USIM1_INT, cardem_inst[0].vcc_active, cardem_inst[0].rst_active, cardem_inst[0].vcc_active); sim_switch_use_physical(0, 1); @@ -473,7 +557,7 @@ void mode_cardemu_init(void) do {} while (!adc_triggered); /* wait for first ADC reading */ #endif /* DETECT_VCC_BY_ADC */ - cardem_inst[1].ch = card_emu_init(1, 0, 1, SIMTRACE_CARDEM_USB_EP_USIM2_DATAIN, + cardem_inst[1].ch = card_emu_init(1, 1, SIMTRACE_CARDEM_USB_EP_USIM2_DATAIN, SIMTRACE_CARDEM_USB_EP_USIM2_INT, cardem_inst[1].vcc_active, cardem_inst[1].rst_active, cardem_inst[1].vcc_active); sim_switch_use_physical(1, 1); diff --git a/firmware/test/card_emu_tests.c b/firmware/test/card_emu_tests.c index 2a1d6827..69159a83 100644 --- a/firmware/test/card_emu_tests.c +++ b/firmware/test/card_emu_tests.c @@ -50,6 +50,9 @@ void card_emu_uart_enable(uint8_t uart_chan, uint8_t rxtx) case ENABLE_TX: rts = "TX"; break; + case ENABLE_TX_TIMER_ONLY: + rts = "TX-TIMER-ONLY"; + break; case ENABLE_RX: rts = "RX"; break; @@ -66,29 +69,14 @@ void card_emu_uart_interrupt(uint8_t uart_chan) printf("uart_interrupt(uart_chan=%u)\n", uart_chan); } -void tc_etu_set_wtime(uint8_t tc_chan, uint16_t wtime) +void card_emu_uart_update_wt(uint8_t uart_chan, uint32_t wt) { - printf("tc_etu_set_wtime(tc_chan=%u, wtime=%u)\n", tc_chan, wtime); + printf("%s(uart_chan=%u, wtime=%u)\n", __func__, uart_chan, wt); } -void tc_etu_set_etu(uint8_t tc_chan, uint16_t etu) +void card_emu_uart_reset_wt(uint8_t uart_chan) { - printf("tc_etu_set_etu(tc_chan=%u, etu=%u)\n", tc_chan, etu); -} - -void tc_etu_init(uint8_t chan_nr, void *handle) -{ - printf("tc_etu_init(tc_chan=%u)\n", chan_nr); -} - -void tc_etu_enable(uint8_t chan_nr) -{ - printf("tc_etu_enable(tc_chan=%u)\n", chan_nr); -} - -void tc_etu_disable(uint8_t chan_nr) -{ - printf("tc_etu_disable(tc_chan=%u)\n", chan_nr); + printf("%s(uart_chan=%u\n", __func__, uart_chan); } @@ -136,7 +124,7 @@ static void io_start_card(struct card_handle *ch) /* release from reset and verify th ATR */ card_emu_io_statechg(ch, CARD_IO_RST, 0); /* simulate waiting time before ATR expired */ - tc_etu_wtime_expired(ch); + card_emu_wtime_expired(ch); verify_atr(ch); } @@ -408,7 +396,7 @@ int main(int argc, char **argv) struct card_handle *ch; unsigned int i; - ch = card_emu_init(0, 23, 42, PHONE_DATAIN, PHONE_INT, false, true, false); + ch = card_emu_init(0, 42, PHONE_DATAIN, PHONE_INT, false, true, false); assert(ch); usb_buf_init();