firmware/sniffer: Handle WT timeouts via ring-buffer

Before this patch, all UART characters went through a fifo/ringbuffer
of depth 512, while events like timeout were delivered directly via
a global flags variable from ISR to main code.  This means that one or
more correct/complete TPDUs could theoretically still be in the FIFO,
but the "Fast path" of the timeout handling is pre-empting that and
messing with the state machines.

All events from the UART should be delivered via the ring-buffer to make
sure they arrive in order at the main function.

The old "report timeout via change flags in separate USB message" code
is left in place.  On the USB protocol we should keep it for
compatibility.  Internally we should probably also migrate that over
to the new ring-buffer method in a second step.

Change-Id: I4434c6fcd59d1a425e9ded734bbc8b0411a0a0d8
This commit is contained in:
Harald Welte 2022-11-16 18:51:51 +01:00 committed by laforge
parent 61394cde29
commit 67a6d5724c
1 changed files with 73 additions and 66 deletions

View File

@ -1,6 +1,6 @@
/* SIMtrace 2 sniffer mode
*
* (C) 2016-2017 by Harald Welte <hwelte@hmw-consulting.de>
* (C) 2016-2022 by Harald Welte <hwelte@hmw-consulting.de>
* (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
@ -115,6 +115,8 @@ enum tpdu_sniff_state {
#define RBUF16_F_OVERRUN 0x0100
#define RBUF16_F_FRAMING 0x0200
#define RBUF16_F_PARITY 0x0400
#define RBUF16_F_TIMEOUT_WT 0x0800
#define RBUF16_F_DATA_BYTE 0x8000
/*------------------------------------------------------------------------------
* Internal variables
@ -825,13 +827,11 @@ void Sniffer_usart_isr(void)
uint32_t csr = sniff_usart.base->US_CSR;
uint16_t byte = 0;
bool byte_received = false;
/* Verify if character has been received */
if (csr & US_CSR_RXRDY) {
byte_received = true;
/* Read communication data byte between phone and SIM */
byte = sniff_usart.base->US_RHR;
byte = RBUF16_F_DATA_BYTE | (sniff_usart.base->US_RHR & 0xff);
/* Reset WT timer */
wt_remaining = g_wt;
}
@ -847,15 +847,11 @@ void Sniffer_usart_isr(void)
if (csr & (US_CSR_OVRE|US_CSR_FRAME|US_CSR_PARE))
sniff_usart.base->US_CR |= US_CR_RSTSTA;
/* Store sniffed data (or error flags, or both) into buffer */
if (byte_received || byte) {
if (rbuf16_write(&sniff_buffer, byte) != 0)
TRACE_ERROR("USART buffer full\n\r");
}
/* Verify it WT timeout occurred, to detect unresponsive card */
if (csr & US_CSR_TIMEOUT) {
if (wt_remaining <= (sniff_usart.base->US_RTOR & 0xffff)) {
/* ensure the timeout is enqueued in the ring-buffer */
byte |= RBUF16_F_TIMEOUT_WT;
/* Just set the flag and let the main loop handle it */
change_flags |= SNIFF_CHANGE_FLAG_TIMEOUT_WT;
/* Reset timeout value */
@ -875,6 +871,12 @@ void Sniffer_usart_isr(void)
sniff_usart.base->US_CR |= US_CR_RETTO;
}
}
/* Store sniffed data (or error flags, or both) into buffer */
if (byte) {
if (rbuf16_write(&sniff_buffer, byte) != 0)
TRACE_ERROR("USART buffer full\n\r");
}
}
/** PIO interrupt service routine to checks if the card reset line has changed
@ -1023,43 +1025,72 @@ void Sniffer_run(void)
/* Handle sniffed data */
if (!rbuf16_is_empty(&sniff_buffer)) { /* use if instead of while to let the main loop restart the watchdog */
uint16_t entry = rbuf16_read(&sniff_buffer);
uint8_t byte = entry & 0xff;
/* Convert convention if required */
if (convention_convert) {
byte = convention_convert_lut[byte];
}
//TRACE_ERROR_WP(">%02x", byte);
switch (iso_state) { /* Handle byte depending on state */
case ISO7816_S_RESET: /* During reset we shouldn't receive any data */
break;
case ISO7816_S_WAIT_ATR: /* After a reset we expect the ATR */
change_state(ISO7816_S_IN_ATR); /* go to next state */
case ISO7816_S_IN_ATR: /* More ATR data incoming */
process_byte_atr(byte);
break;
case ISO7816_S_WAIT_TPDU: /* After the ATR we expect TPDU or PPS data */
case ISO7816_S_WAIT_PPS_RSP:
if (0xff == byte) {
if (ISO7816_S_WAIT_PPS_RSP == iso_state) {
change_state(ISO7816_S_IN_PPS_RSP); /* Go to PPS state */
} else {
change_state(ISO7816_S_IN_PPS_REQ); /* Go to PPS state */
if (entry & RBUF16_F_DATA_BYTE) {
uint8_t byte = entry & 0xff;
/* Convert convention if required */
if (convention_convert) {
byte = convention_convert_lut[byte];
}
//TRACE_ERROR_WP(">%02x", byte);
switch (iso_state) { /* Handle byte depending on state */
case ISO7816_S_RESET: /* During reset we shouldn't receive any data */
break;
case ISO7816_S_WAIT_ATR: /* After a reset we expect the ATR */
change_state(ISO7816_S_IN_ATR); /* go to next state */
case ISO7816_S_IN_ATR: /* More ATR data incoming */
process_byte_atr(byte);
break;
case ISO7816_S_WAIT_TPDU: /* After the ATR we expect TPDU or PPS data */
case ISO7816_S_WAIT_PPS_RSP:
if (0xff == byte) {
if (ISO7816_S_WAIT_PPS_RSP == iso_state) {
change_state(ISO7816_S_IN_PPS_RSP); /* Go to PPS state */
} else {
change_state(ISO7816_S_IN_PPS_REQ); /* Go to PPS state */
}
process_byte_pps(byte);
break;
}
case ISO7816_S_IN_TPDU: /* More TPDU data incoming */
if (ISO7816_S_WAIT_TPDU == iso_state) {
change_state(ISO7816_S_IN_TPDU);
}
process_byte_tpdu(byte);
break;
case ISO7816_S_IN_PPS_REQ:
case ISO7816_S_IN_PPS_RSP:
process_byte_pps(byte);
break;
default:
TRACE_ERROR("Data received in unknown state %u\n\r", iso_state);
}
case ISO7816_S_IN_TPDU: /* More TPDU data incoming */
if (ISO7816_S_WAIT_TPDU == iso_state) {
change_state(ISO7816_S_IN_TPDU);
}
/* Use timeout to detect interrupted data transmission */
if (entry & RBUF16_F_TIMEOUT_WT) {
TRACE_ERROR("USART TIMEOUT Error\n\r");
switch (iso_state) {
case ISO7816_S_IN_ATR:
led_blink(LED_RED, BLINK_2F_O); /* indicate error to user */
usb_send_atr(SNIFF_DATA_FLAG_ERROR_INCOMPLETE); /* send incomplete ATR to host software using USB */
change_state(ISO7816_S_WAIT_ATR);
break;
case ISO7816_S_IN_TPDU:
led_blink(LED_RED, BLINK_2F_O); /* indicate error to user */
usb_send_tpdu(SNIFF_DATA_FLAG_ERROR_INCOMPLETE); /* send incomplete PPS to host software using USB */
change_state(ISO7816_S_WAIT_TPDU);
break;
case ISO7816_S_IN_PPS_REQ:
case ISO7816_S_IN_PPS_RSP:
led_blink(LED_RED, BLINK_2F_O); /* indicate error to user */
usb_send_pps(SNIFF_DATA_FLAG_ERROR_INCOMPLETE); /* send incomplete TPDU to host software using USB */
change_state(ISO7816_S_WAIT_TPDU);
break;
default:
break;
}
process_byte_tpdu(byte);
break;
case ISO7816_S_IN_PPS_REQ:
case ISO7816_S_IN_PPS_RSP:
process_byte_pps(byte);
break;
default:
TRACE_ERROR("Data received in unknown state %u\n\r", iso_state);
}
if (entry & RBUF16_F_PARITY)
@ -1101,30 +1132,6 @@ void Sniffer_run(void)
printf("reset de-asserted\n\r");
}
}
if (change_flags & SNIFF_CHANGE_FLAG_TIMEOUT_WT) {
/* Use timeout to detect interrupted data transmission */
switch (iso_state) {
case ISO7816_S_IN_ATR:
led_blink(LED_RED, BLINK_2F_O); /* indicate error to user */
usb_send_atr(SNIFF_DATA_FLAG_ERROR_INCOMPLETE); /* send incomplete ATR to host software using USB */
change_state(ISO7816_S_WAIT_ATR);
break;
case ISO7816_S_IN_TPDU:
led_blink(LED_RED, BLINK_2F_O); /* indicate error to user */
usb_send_tpdu(SNIFF_DATA_FLAG_ERROR_INCOMPLETE); /* send incomplete PPS to host software using USB */
change_state(ISO7816_S_WAIT_TPDU);
break;
case ISO7816_S_IN_PPS_REQ:
case ISO7816_S_IN_PPS_RSP:
led_blink(LED_RED, BLINK_2F_O); /* indicate error to user */
usb_send_pps(SNIFF_DATA_FLAG_ERROR_INCOMPLETE); /* send incomplete TPDU to host software using USB */
change_state(ISO7816_S_WAIT_TPDU);
break;
default:
change_flags &= ~SNIFF_CHANGE_FLAG_TIMEOUT_WT; /* We don't care about the timeout is all other cases */
break;
}
}
if (change_flags) {
usb_send_change(change_flags); /* send timeout to host software over USB */
change_flags = 0; /* Reset flags */