From 51a303096af5721601015d6a928d0941096a5aba Mon Sep 17 00:00:00 2001 From: Eric Wild Date: Thu, 12 Nov 2020 02:37:21 +0100 Subject: asf4 uart: properly handle uart errors Uart errors will now lead to a (temporary) deactivation of the uart as well as the deactivatin of the card + a a proper ccid response to notify the host if the slot was busy. Change-Id: Ia0efef03829b68d2b4f25899bb933b14fb9e0bd1 --- ccid_common/ccid_slot_fsm.c | 27 +++++++--- ccid_common/cuart.h | 1 + ccid_common/iso7816_fsm.c | 8 ++- sysmoOCTSIM/cuart_driver_asf4_usart_async.c | 82 ++++++++++++++++++++++++----- 4 files changed, 97 insertions(+), 21 deletions(-) diff --git a/ccid_common/ccid_slot_fsm.c b/ccid_common/ccid_slot_fsm.c index 4cf75bc..8dccfa5 100644 --- a/ccid_common/ccid_slot_fsm.c +++ b/ccid_common/ccid_slot_fsm.c @@ -147,12 +147,18 @@ static void iso_fsm_slot_icc_power_on_async(struct ccid_slot *cs, struct msgb *m msgb_free(msg); /* continues in iso_fsm_clot_user_cb once ATR is received */ } + static void iso_fsm_clot_user_cb(struct osmo_fsm_inst *fi, int event, int cause, void *data) { struct iso_fsm_slot *ss = iso7816_fsm_get_user_priv(fi); struct ccid_slot *cs = ss->cs; switch (event) { + /* special case: not handled as a normal callback below, in case slot was busy the fsm will + * additionally emit a proper error event handled below to notify the host */ + case ISO7816_E_HW_ERR_IND: + card_uart_ctrl(ss->cuart, CUART_CTL_NO_RXTX, true); + break; case ISO7816_E_ATR_DONE_IND: case ISO7816_E_ATR_ERR_IND: case ISO7816_E_TPDU_DONE_IND: @@ -219,10 +225,14 @@ static int iso_handle_fsm_events(struct ccid_slot *cs, bool enable){ break; case ISO7816_E_ATR_ERR_IND: tpdu = data; - LOGPCS(cs, LOGL_DEBUG, "%s(event=%d, data=%s)\n", __func__, event, - msgb_hexdump(tpdu)); - resp = ccid_gen_data_block(cs, ss->seq, CCID_CMD_STATUS_FAILED, CCID_ERR_ICC_MUTE, - msgb_data(tpdu), msgb_length(tpdu)); + LOGPCS(cs, LOGL_DEBUG, "%s(event=%d, data=%s)\n", __func__, event, msgb_hexdump(tpdu)); + + /* perform deactivation */ + card_uart_ctrl(ss->cuart, CUART_CTL_RST, true); + card_uart_ctrl(ss->cuart, CUART_CTL_POWER_5V0, false); + cs->icc_powered = false; + + resp = ccid_gen_data_block(cs, ss->seq, CCID_CMD_STATUS_FAILED, CCID_ERR_ICC_MUTE, msgb_data(tpdu), msgb_length(tpdu)); ccid_slot_send_unbusy(cs, resp); cs->event = 0; break; @@ -237,8 +247,13 @@ static int iso_handle_fsm_events(struct ccid_slot *cs, bool enable){ break; case ISO7816_E_TPDU_FAILED_IND: tpdu = data; - LOGPCS(cs, LOGL_DEBUG, "%s(event=%d, data=%s)\n", __func__, event, - msgb_hexdump(tpdu)); + + /* perform deactivation */ + card_uart_ctrl(ss->cuart, CUART_CTL_RST, true); + card_uart_ctrl(ss->cuart, CUART_CTL_POWER_5V0, false); + cs->icc_powered = false; + + LOGPCS(cs, LOGL_DEBUG, "%s(event=%d, data=%s)\n", __func__, event, msgb_hexdump(tpdu)); /* FIXME: other error causes than card removal?*/ resp = ccid_gen_data_block(cs, ss->seq, CCID_CMD_STATUS_FAILED, CCID_ERR_ICC_MUTE, msgb_l2(tpdu), 0); ccid_slot_send_unbusy(cs, resp); diff --git a/ccid_common/cuart.h b/ccid_common/cuart.h index c0a3a52..c8573c5 100644 --- a/ccid_common/cuart.h +++ b/ccid_common/cuart.h @@ -36,6 +36,7 @@ enum card_uart_event { CUART_E_RX_TIMEOUT, /* an entire block of data was written, as instructed in prior card_uart_tx() call */ CUART_E_TX_COMPLETE, + CUART_E_HW_ERROR, /* might be uart parity or mystery error, might be something else */ }; extern const struct value_string card_uart_event_vals[]; diff --git a/ccid_common/iso7816_fsm.c b/ccid_common/iso7816_fsm.c index 8e113b0..7207245 100644 --- a/ccid_common/iso7816_fsm.c +++ b/ccid_common/iso7816_fsm.c @@ -276,6 +276,9 @@ static void tpdu_uart_notification(struct card_uart *cuart, enum card_uart_event case CUART_E_TX_COMPLETE: osmo_fsm_inst_dispatch(fi, ISO7816_E_TX_COMPL, data); break; + case CUART_E_HW_ERROR: + osmo_fsm_inst_dispatch(fi, ISO7816_E_HW_ERR_IND, data); + break; } } @@ -361,7 +364,7 @@ static void iso7816_3_wait_tpdu_onenter(struct osmo_fsm_inst *fi, uint32_t prev_ struct iso7816_3_priv *ip = get_iso7816_3_priv(fi); OSMO_ASSERT(fi->fsm == &iso7816_3_fsm); card_uart_ctrl(ip->uart, CUART_CTL_RX_TIMER_HINT, 0); - card_uart_ctrl(ip->uart, CUART_CTL_NO_RXTX, true); + /* reset the TPDU state machine */ osmo_fsm_inst_dispatch(ip->tpdu_fi, ISO7816_E_TPDU_CLEAR_REQ, NULL); } @@ -424,6 +427,9 @@ static void iso7816_3_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, switch (event) { case ISO7816_E_HW_ERR_IND: + /* deactivates uart, then continues with the callbacks below for proper error reporting */ + ip->user_cb(fi, ISO7816_E_HW_ERR_IND, 0, 0); + /* no break */ case ISO7816_E_CARD_REMOVAL: /* FIXME: power off? */ if(fi->state == ISO7816_S_WAIT_ATR || fi->state == ISO7816_S_IN_ATR) diff --git a/sysmoOCTSIM/cuart_driver_asf4_usart_async.c b/sysmoOCTSIM/cuart_driver_asf4_usart_async.c index 64ec9f2..42c2110 100644 --- a/sysmoOCTSIM/cuart_driver_asf4_usart_async.c +++ b/sysmoOCTSIM/cuart_driver_asf4_usart_async.c @@ -56,11 +56,10 @@ static void _SIM_tx_cb(const struct usart_async_descriptor *const io_descr, uint #include -static void _SIM_error_cb(const struct usart_async_descriptor *const descr){ - volatile uint32_t status = hri_sercomusart_read_STATUS_reg(descr->device.hw); - volatile uint32_t data = hri_sercomusart_read_DATA_reg(descr->device.hw); - volatile uint32_t errrs = hri_sercomusart_read_RXERRCNT_reg(descr->device.hw); - OSMO_ASSERT(0 == 1) +static void _SIM_error_cb(const struct usart_async_descriptor *const io_descr, uint8_t slot_nr) { + struct card_uart *cuart = cuart4slot_nr(slot_nr); + OSMO_ASSERT(cuart); + card_uart_notification(cuart, CUART_E_HW_ERROR, 0); } /* the below ugli-ness is required as the usart_async_descriptor doesn't have @@ -139,6 +138,44 @@ static usart_cb_t SIM_tx_cb[8] = { SIM4_tx_cb, SIM5_tx_cb, SIM6_tx_cb, SIM7_tx_cb, }; +static void SIM0_error_cb(const struct usart_async_descriptor *const io_descr) +{ + _SIM_error_cb(io_descr, 0); +} +static void SIM1_error_cb(const struct usart_async_descriptor *const io_descr) +{ + _SIM_error_cb(io_descr, 1); +} +static void SIM2_error_cb(const struct usart_async_descriptor *const io_descr) +{ + _SIM_error_cb(io_descr, 2); +} +static void SIM3_error_cb(const struct usart_async_descriptor *const io_descr) +{ + _SIM_error_cb(io_descr, 3); +} +static void SIM4_error_cb(const struct usart_async_descriptor *const io_descr) +{ + _SIM_error_cb(io_descr, 4); +} +static void SIM5_error_cb(const struct usart_async_descriptor *const io_descr) +{ + _SIM_error_cb(io_descr, 5); +} +static void SIM6_error_cb(const struct usart_async_descriptor *const io_descr) +{ + _SIM_error_cb(io_descr, 6); +} +static void SIM7_error_cb(const struct usart_async_descriptor *const io_descr) +{ + _SIM_error_cb(io_descr, 7); +} +static usart_cb_t SIM_error_cb[8] = { + SIM0_error_cb, SIM1_error_cb, SIM2_error_cb, SIM3_error_cb, + SIM4_error_cb, SIM5_error_cb, SIM6_error_cb, SIM7_error_cb, +}; + + #include #include "atmel_start.h" #include "atmel_start_pins.h" @@ -182,9 +219,14 @@ static void set_inverted_signalling(void* hw, bool on) { static bool slot_set_baudrate(struct card_uart *cuart, uint32_t baudrate) { uint8_t slotnr = cuart->u.asf4.slot_nr; - + struct usart_async_descriptor* slot = SIM_peripheral_descriptors[slotnr]; + Sercom *sercom = cuart->u.asf4.usa_pd->device.hw; ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors)); + if (NULL == slot) { + return false; + } + // calculate the error corresponding to the clock sources uint16_t bauds[ARRAY_SIZE(sercom_glck_freqs)]; double errors[ARRAY_SIZE(sercom_glck_freqs)]; @@ -218,11 +260,6 @@ static bool slot_set_baudrate(struct card_uart *cuart, uint32_t baudrate) return false; } - // set clock and baud rate - struct usart_async_descriptor* slot = SIM_peripheral_descriptors[slotnr]; // get slot - if (NULL == slot) { - return false; - } // update cached values cuart->u.asf4.current_baudrate = baudrate; @@ -231,7 +268,7 @@ static bool slot_set_baudrate(struct card_uart *cuart, uint32_t baudrate) printf("(%u) switching SERCOM clock to GCLK%u (freq = %lu kHz) and baud rate to %lu bps (baud = %u)\r\n", slotnr, (best + 1) * 2, (uint32_t)(round(sercom_glck_freqs[best] / 1000)), baudrate, bauds[best]); /* only wait if the uart is enabled.... */ - if (hri_sercomusart_get_CTRLA_reg(slot->device.hw, SERCOM_USART_CTRLA_ENABLE)) { + if (hri_sercomusart_get_CTRLA_reg(sercom, SERCOM_USART_CTRLA_ENABLE)) { while (!usart_async_is_tx_empty(slot)); // wait for transmission to complete (WARNING no timeout) usart_async_disable(slot); // disable SERCOM peripheral } @@ -241,6 +278,17 @@ static bool slot_set_baudrate(struct card_uart *cuart, uint32_t baudrate) // it does not seem we need to completely disable the peripheral using hri_mclk_clear_APBDMASK_SERCOMn_bit hri_gclk_write_PCHCTRL_reg(GCLK, SIM_peripheral_GCLK_ID[slotnr], sercom_glck_sources[best] | (1 << GCLK_PCHCTRL_CHEN_Pos)); // set peripheral core clock and re-enable it usart_async_set_baud_rate(slot, bauds[best]); // set the new baud rate + + /* clear pending errors that happened while + * - the interrupt was off (inverse ATR? -> parity error) + * -- OR -- + * - the uart was disabled due to a hw error + * and enable it*/ + hri_sercomusart_clear_interrupt_ERROR_bit(sercom); + hri_sercomusart_clear_STATUS_reg(sercom, 0xff); + volatile uint8_t dummy = hri_sercomusart_read_RXERRCNT_reg(sercom); + + usart_async_flush_rx_buffer(cuart->u.asf4.usa_pd); usart_async_enable(slot); // re-enable SERCOM peripheral return true; @@ -330,7 +378,7 @@ static int asf4_usart_open(struct card_uart *cuart, const char *device_name) usart_async_register_callback(usa_pd, USART_ASYNC_RXC_CB, SIM_rx_cb[slot_nr]); usart_async_register_callback(usa_pd, USART_ASYNC_TXC_CB, SIM_tx_cb[slot_nr]); - usart_async_register_callback(usa_pd, USART_ASYNC_ERROR_CB, _SIM_error_cb); + usart_async_register_callback(usa_pd, USART_ASYNC_ERROR_CB, SIM_error_cb[slot_nr]); usart_async_enable(usa_pd); // set USART baud rate to match the interface (f = 2.5 MHz) and card default settings (Fd = 372, Dd = 1) @@ -385,7 +433,13 @@ static int asf4_usart_ctrl(struct card_uart *cuart, enum card_uart_ctl ctl, int switch (ctl) { case CUART_CTL_NO_RXTX: - /* no op */ + + /* immediately disables the uart, useful for hw errors (parity, colllision, ...) + * uart is automatically re-enabled during slot_set_isorate which also clears the errors + * when resetting the slot which happens automatically during error callback handling or powerup + * so the uart usually does not stay disabled for a long time */ + if (arg) + _usart_async_disable(&cuart->u.asf4.usa_pd->device); break; case CUART_CTL_RX: if (arg){ -- cgit v1.2.3