/* Driver for AT91SAM7 USART0 in ISO7816-3 mode for passive sniffing * (C) 2010 by Harald Welte * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include "../simtrace.h" #include "../openpcd.h" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) static const AT91PS_USART usart = AT91C_BASE_US0; enum iso7816_3_state { ISO7816_S_RESET, /* in Reset */ ISO7816_S_WAIT_ATR, /* waiting for ATR to start */ ISO7816_S_IN_ATR, /* while we are receiving the ATR */ ISO7816_S_WAIT_APDU, /* waiting for start of new APDU */ ISO7816_S_IN_APDU, /* inside a single APDU */ ISO7816_S_IN_PTS, /* while we are inside the PTS / PSS */ }; /* detailed sub-states of ISO7816_S_IN_ATR */ enum atr_state { ATR_S_WAIT_TS, ATR_S_WAIT_T0, ATR_S_WAIT_TA, ATR_S_WAIT_TB, ATR_S_WAIT_TC, ATR_S_WAIT_TD, ATR_S_WAIT_HIST, ATR_S_WAIT_TCK, ATR_S_DONE, }; /* detailed sub-states of ISO7816_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, }; #define _PTSS 0 #define _PTS0 1 #define _PTS1 2 #define _PTS2 3 #define _PTS3 4 #define _PCK 5 struct iso7816_3_handle { enum iso7816_3_state state; uint8_t fi; uint8_t di; uint8_t wi; uint32_t waiting_time; enum atr_state atr_state; uint8_t atr_idx; uint8_t atr_hist_len; uint8_t atr_last_td; uint8_t atr[64]; uint16_t prot_t_supported; enum pts_state pts_state; uint8_t pts_req[6]; uint8_t pts_resp[6]; struct simtrace_hdr sh; int rctx_must_be_sent; struct req_ctx *rctx; struct simtrace_stats stats; }; struct iso7816_3_handle isoh; /* Table 6 from ISO 7816-3 */ static const uint16_t fi_table[] = { 372, 372, 558, 744, 1116, 1488, 1860, 0, 0, 512, 768, 1024, 1536, 2048, 0, 0 }; /* Table 7 from ISO 7816-3 */ static const uint8_t di_table[] = { 0, 1, 2, 4, 8, 16, 32, 64, 12, 20, 2, 4, 8, 16, 32, 64, }; void iso_uart_report_errors(void) { static unsigned lastOverrun = 0, lastParity = 0, lastFrame = 0; if (isoh.stats.overrun != lastOverrun) { DEBUGPCR("UART overrun: %u", lastOverrun = isoh.stats.overrun); } if (isoh.stats.frame_err != lastFrame) { DEBUGPCR("UART frame error: %u", lastFrame = isoh.stats.frame_err); } if (isoh.stats.parity_err != lastParity) { DEBUGPCR("UART parity error: %u", lastParity = isoh.stats.parity_err); } } void iso_uart_stats_dump(void) { DEBUGPCRF("no_rctx: %u, rctx_sent: %u, rst: %u, pps: %u, bytes: %u, " "parity_err: %u, frame_err: %u, overrun:%u", isoh.stats.no_rctx, isoh.stats.rctx_sent, isoh.stats.rst, isoh.stats.pps, isoh.stats.bytes, isoh.stats.parity_err, isoh.stats.frame_err, isoh.stats.overrun); } struct simtrace_stats *iso_uart_stats_get(void) { return &isoh.stats; } /* compute the F/D ratio based on Fi and Di values */ static int compute_fidi_ratio(uint8_t fi, uint8_t di) { uint16_t f, d; int ret; if (fi >= ARRAY_SIZE(fi_table) || di >= ARRAY_SIZE(di_table)) return -EINVAL; f = fi_table[fi]; if (f == 0) return -EINVAL; d = di_table[di]; if (d == 0) return -EINVAL; /* See table 7 of ISO 7816-3: From 1000 on we divide by 1/d, * which equals a multiplication by d */ if (di < 8) ret = f / d; else ret = f * d; return ret; } static void refill_rctx(struct iso7816_3_handle *ih) { struct req_ctx *rctx; rctx = req_ctx_find_get(0, RCTX_STATE_FREE, RCTX_STATE_LIBRFID_BUSY); if (!rctx) { ih->rctx = NULL; return; } ih->sh.cmd = SIMTRACE_MSGT_DATA; /* reserve spece at start of rctx */ rctx->tot_len = sizeof(struct simtrace_hdr); ih->rctx = rctx; } static void send_rctx(struct iso7816_3_handle *ih) { struct req_ctx *rctx = ih->rctx; if (!rctx) return; /* Put Fi and Di into res[2] array */ ih->sh.res[0] = ih->fi; ih->sh.res[1] = ih->di; /* copy the simtrace header */ memcpy(rctx->data, &ih->sh, sizeof(ih->sh)); req_ctx_set_state(rctx, RCTX_STATE_UDP_EP2_PENDING); memset(&ih->sh, 0, sizeof(ih->sh)); ih->rctx = NULL; ih->stats.rctx_sent++; } /* Update the ATR sub-state */ static void set_atr_state(struct iso7816_3_handle *ih, enum atr_state new_atrs) { if (new_atrs == ATR_S_WAIT_TS) { ih->atr_idx = 0; ih->atr_hist_len = 0; ih->atr_last_td = 0; ih->prot_t_supported = (1 << 0); memset(ih->atr, 0, sizeof(ih->atr)); } else if (ih->atr_state == new_atrs) return; //DEBUGPCR("ATR state %u -> %u", ih->atr_state, new_atrs); ih->atr_state = new_atrs; } #define ISO7816_3_INIT_WTIME 9600 #define ISO7816_3_DEFAULT_WI 10 static void update_fidi(struct iso7816_3_handle *ih) { int rc; rc = compute_fidi_ratio(ih->fi, ih->di); if (rc > 0 && rc < 0x400) { DEBUGPCR("computed Fi(%u) Di(%u) ratio: %d", ih->fi, ih->di, rc); /* make sure UART uses new F/D ratio */ usart->US_CR |= AT91C_US_RXDIS | AT91C_US_RSTRX; usart->US_FIDI = rc & 0x3ff; usart->US_CR |= AT91C_US_RXEN | AT91C_US_STTTO; /* notify ETU timer about this */ tc_etu_set_etu(rc); } else DEBUGPCRF("computed FiDi ratio %d unsupported", rc); } /* Update the ISO 7816-3 APDU receiver state */ static void set_state(struct iso7816_3_handle *ih, enum iso7816_3_state new_state) { if (new_state == ISO7816_S_RESET) { usart->US_CR |= AT91C_US_RXDIS | AT91C_US_RSTRX; } else if (new_state == ISO7816_S_WAIT_ATR) { /* Reset to initial Fi / Di ratio */ ih->fi = 1; ih->di = 1; update_fidi(ih); /* initialize todefault WI, this will be overwritten if we * receive TC2, and it will be programmed into hardware after * ATR is finished */ ih->wi = ISO7816_3_DEFAULT_WI; /* update waiting time to initial waiting time */ ih->waiting_time = ISO7816_3_INIT_WTIME; tc_etu_set_wtime(ih->waiting_time); /* Set ATR sub-state to initial state */ set_atr_state(ih, ATR_S_WAIT_TS); /* Notice that we are just coming out of reset */ ih->sh.flags |= SIMTRACE_FLAG_ATR; } if (ih->state == new_state) return; //DEBUGPCR("7816 state %u -> %u", ih->state, new_state); ih->state = new_state; } static void atr_done_wait_apdu(struct iso7816_3_handle *ih) { set_atr_state(ih, ATR_S_DONE); /* send off the USB context */ ih->rctx_must_be_sent = 1; /* update the waiting time */ ih->waiting_time = 960 * di_table[ih->di] * ih->wi; tc_etu_set_wtime(ih->waiting_time); } static enum iso7816_3_state transition_to_tck(struct iso7816_3_handle *ih) { if (ih->prot_t_supported == 0x01) { /* If only T=0 supported, there is no TCK but we * immediately transition to APDUs */ atr_done_wait_apdu(ih); return ISO7816_S_WAIT_APDU; } else { set_atr_state(ih, ATR_S_WAIT_TCK); return ISO7816_S_IN_ATR; } } /* determine the next ATR state based on received interface byte */ static enum atr_state next_intb_state(struct iso7816_3_handle *ih, uint8_t ch) { switch (ih->atr_state) { case ATR_S_WAIT_TD: ih->prot_t_supported |= (1 << (ch & 0xf)); case ATR_S_WAIT_T0: ih->atr_last_td = ch; goto from_td; case ATR_S_WAIT_TC: if ((ih->atr_last_td & 0x0f) == 0x02) { /* TC2 contains WI */ ih->wi = ch; } goto from_tc; case ATR_S_WAIT_TB: goto from_tb; case ATR_S_WAIT_TA: goto from_ta; default: DEBUGPCR("something wrong, old_state != TA"); return ATR_S_WAIT_TCK; } from_td: if (ih->atr_last_td & 0x10) return ATR_S_WAIT_TA; from_ta: if (ih->atr_last_td & 0x20) return ATR_S_WAIT_TB; from_tb: if (ih->atr_last_td & 0x40) return ATR_S_WAIT_TC; from_tc: if (ih->atr_last_td & 0x80) return ATR_S_WAIT_TD; /* Historical bytes are common, but optional! */ if (ih->atr_hist_len) return ATR_S_WAIT_HIST; else return transition_to_tck(ih); } /* process an incomng ATR byte */ static enum iso7816_3_state process_byte_atr(struct iso7816_3_handle *ih, uint8_t byte) { /* add byte to ATR buffer */ ih->atr[ih->atr_idx] = byte; ih->atr_idx++; switch (ih->atr_state) { case ATR_S_WAIT_TS: /* FIXME: if we don't have the RST line we might get this */ if (byte == 0) { ih->atr_idx--; break; } /* FIXME: check inverted logic */ set_atr_state(ih, ATR_S_WAIT_T0); break; case ATR_S_WAIT_T0: /* obtain the number of historical bytes */ ih->atr_hist_len = byte & 0xf; /* Mask out the hist-byte-length to indiicate T=0 */ set_atr_state(ih, next_intb_state(ih, byte & 0xf0)); break; case ATR_S_WAIT_TA: case ATR_S_WAIT_TB: case ATR_S_WAIT_TC: case ATR_S_WAIT_TD: set_atr_state(ih, next_intb_state(ih, byte)); break; case ATR_S_WAIT_HIST: ih->atr_hist_len--; /* after all historical bytes are recieved, go to TCK */ if (ih->atr_hist_len == 0) return transition_to_tck(ih); break; case ATR_S_WAIT_TCK: /* FIXME: process and verify the TCK */ atr_done_wait_apdu(ih); return ISO7816_S_WAIT_APDU; } return ISO7816_S_IN_ATR; } /* Update the ATR sub-state */ static void set_pts_state(struct iso7816_3_handle *ih, enum pts_state new_ptss) { //DEBUGPCR("PTS state %u -> %u", ih->pts_state, new_ptss); ih->pts_state = new_ptss; } /* Determine the next PTS state */ static enum pts_state next_pts_state(struct iso7816_3_handle *ih) { uint8_t is_resp = ih->pts_state & 0x10; uint8_t sstate = ih->pts_state & 0x0f; uint8_t *pts_ptr; if (!is_resp) pts_ptr = ih->pts_req; else pts_ptr = ih->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 (ih->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 enum iso7816_3_state process_byte_pts(struct iso7816_3_handle *ih, uint8_t byte) { switch (ih->pts_state) { case PTS_S_WAIT_REQ_PTSS: ih->pts_req[_PTSS] = byte; break; case PTS_S_WAIT_REQ_PTS0: ih->pts_req[_PTS0] = byte; break; case PTS_S_WAIT_REQ_PTS1: ih->pts_req[_PTS1] = byte; break; case PTS_S_WAIT_REQ_PTS2: ih->pts_req[_PTS2] = byte; break; case PTS_S_WAIT_REQ_PTS3: ih->pts_req[_PTS3] = byte; break; case PTS_S_WAIT_REQ_PCK: /* FIXME: check PCK */ ih->pts_req[_PCK] = byte; break; case PTS_S_WAIT_RESP_PTSS: ih->pts_resp[_PTSS] = byte; break; case PTS_S_WAIT_RESP_PTS0: ih->pts_resp[_PTS0] = byte; break; case PTS_S_WAIT_RESP_PTS1: /* This must be TA1 */ ih->fi = byte >> 4; ih->di = byte & 0xf; DEBUGPCR("found Fi=%u Di=%u", ih->fi, ih->di); ih->sh.flags |= SIMTRACE_FLAG_PPS_FIDI; ih->pts_resp[_PTS1] = byte; break; case PTS_S_WAIT_RESP_PTS2: ih->pts_resp[_PTS2] = byte; break; case PTS_S_WAIT_RESP_PTS3: ih->pts_resp[_PTS3] = byte; break; case PTS_S_WAIT_RESP_PCK: ih->pts_resp[_PCK] = byte; /* FIXME: check PCK */ set_pts_state(ih, PTS_S_WAIT_REQ_PTSS); /* update baud rate generator with Fi/Di */ update_fidi(ih); /* Wait for the next APDU */ return ISO7816_S_WAIT_APDU; } /* calculate the next state and set it */ set_pts_state(ih, next_pts_state(ih)); return ISO7816_S_IN_PTS; } static void process_byte(struct iso7816_3_handle *ih, uint8_t byte) { int new_state = -1; struct req_ctx *rctx; ih->stats.bytes++; if (!ih->rctx) refill_rctx(ih); switch (ih->state) { case ISO7816_S_RESET: break; case ISO7816_S_WAIT_ATR: case ISO7816_S_IN_ATR: new_state = process_byte_atr(ih, byte); break; case ISO7816_S_WAIT_APDU: if (byte == 0xff) { new_state = process_byte_pts(ih, byte); ih->stats.pps++; goto out_silent; } case ISO7816_S_IN_APDU: new_state = ISO7816_S_IN_APDU; break; case ISO7816_S_IN_PTS: new_state = process_byte_pts(ih, byte); goto out_silent; } /* The USB buffer could be gone in case the timer expired or code above * this line explicitly sent it off */ if (!ih->rctx) refill_rctx(ih); rctx = ih->rctx; if (!rctx) { //DEBUGPCR("==> Lost byte, missing rctx"); ih->stats.no_rctx++; return; } /* store the byte in the USB request context */ rctx->data[rctx->tot_len] = byte; rctx->tot_len++; if (rctx->tot_len >= rctx->size || ih->rctx_must_be_sent) { ih->rctx_must_be_sent = 0; send_rctx(ih); } out_silent: if (new_state != -1) set_state(ih, new_state); } /* timeout of work waiting time during receive */ void iso7816_wtime_expired(void) { /* Always flush the URB at Rx timeout as this indicates end of APDU */ if (isoh.rctx) { isoh.sh.flags |= SIMTRACE_FLAG_WTIME_EXP; send_rctx(&isoh); } if (isoh.state == ISO7816_S_IN_PTS) { /* Timout during PTS: Card does not support PTS */ } set_state(&isoh, ISO7816_S_WAIT_APDU); } void iso_uart_flush(void) { send_rctx(&isoh); } void iso_uart_idleflush(void) { static struct req_ctx *last_req = NULL; static uint16_t last_len = 0; if (last_req == isoh.rctx && last_len == isoh.rctx->tot_len && req_ctx_count(RCTX_STATE_UDP_EP2_PENDING) == 0 && (isoh.sh.flags & SIMTRACE_FLAG_ATR) == 0) { send_rctx(&isoh); } last_req = isoh.rctx; last_len = isoh.rctx->tot_len; } static __ramfunc void usart_irq(void) { uint32_t csr = usart->US_CSR; uint8_t octet; //DEBUGP("USART IRQ, CSR=0x%08x\n", csr); if (csr & AT91C_US_RXRDY) { /* at least one character received */ octet = usart->US_RHR & 0xff; //DEBUGP("%02x ", octet); process_byte(&isoh, octet); } if (csr & AT91C_US_TXRDY) { /* nothing to transmit anymore */ } if (csr & (AT91C_US_PARE|AT91C_US_FRAME|AT91C_US_OVRE)) { uint8_t nb_err = usart->US_NER; /* FIXME: some error has occurrerd */ //DEBUGP("NER=%02x ", nb_err); /* clear the status */ usart->US_CR |= AT91C_US_RSTSTA; if (csr & AT91C_US_PARE) isoh.stats.parity_err++; if (csr & AT91C_US_FRAME) isoh.stats.frame_err++; if (csr & AT91C_US_OVRE) isoh.stats.overrun++; } if (csr & AT91C_US_INACK) { /* we would have sent a NACK if INACK was not set */ usart->US_CR |= AT91C_US_RSTNACK; } } /* handler for the RST input pin state change */ static void reset_pin_irq(uint32_t pio) { if (!AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, pio)) { /* make sure to flush pending req_ctx */ iso_uart_flush(); DEBUGPCR("nRST"); set_state(&isoh, ISO7816_S_RESET); } else { /* make sure to flush pending req_ctx */ iso_uart_flush(); DEBUGPCR("RST"); set_state(&isoh, ISO7816_S_WAIT_ATR); isoh.stats.rst++; } } void iso_uart_dump(void) { uint32_t csr = usart->US_CSR; DEBUGPCR("USART CSR=0x%08x", csr); } void iso_uart_rst(unsigned int state) { DEBUGPCR("USART set nRST set state=%u", state); switch (state) { case 0: AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, SIMTRACE_PIO_nRST); AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, SIMTRACE_PIO_nRST); break; case 1: AT91F_PIO_SetOutput(AT91C_BASE_PIOA, SIMTRACE_PIO_nRST); AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, SIMTRACE_PIO_nRST); break; default: AT91F_PIO_CfgInput(AT91C_BASE_PIOA, SIMTRACE_PIO_nRST); break; } } void iso_uart_rx_mode(void) { DEBUGPCR("USART Entering Rx Mode"); /* Enable receive interrupts */ usart->US_IER = AT91C_US_RXRDY | AT91C_US_OVRE | AT91C_US_FRAME | AT91C_US_PARE | AT91C_US_NACK | AT91C_US_ITERATION; /* call interrupt handler once to set initial state RESET / ATR */ reset_pin_irq(SIMTRACE_PIO_nRST); } void iso_uart_clk_master(unsigned int master) { DEBUGPCR("USART Clock Master %u", master); if (master) { usart->US_MR = AT91C_US_USMODE_ISO7816_0 | AT91C_US_CLKS_CLOCK | AT91C_US_CHRL_8_BITS | AT91C_US_NBSTOP_1_BIT | AT91C_US_CKLO | AT91C_US_INACK; usart->US_BRGR = (0x0000 << 16) | 16; } else { usart->US_MR = AT91C_US_USMODE_ISO7816_0 | AT91C_US_CLKS_EXT | AT91C_US_CHRL_8_BITS | AT91C_US_NBSTOP_1_BIT | AT91C_US_CKLO | AT91C_US_INACK; usart->US_BRGR = (0x0000 << 16) | 0x0001; } } void iso_uart_init(void) { DEBUGPCR("USART Initializing"); memset(&isoh, 0, sizeof(isoh)); refill_rctx(&isoh); /* make sure we get clock from the power management controller */ AT91F_US0_CfgPMC(); /* configure all 3 signals as input */ AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, SIMTRACE_PIO_IO, SIMTRACE_PIO_CLK); AT91F_PIO_CfgInput(AT91C_BASE_PIOA, SIMTRACE_PIO_nRST); /* Configure Interrupt */ AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_US0, OPENPCD_IRQ_PRIO_USART, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, &usart_irq); AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_US0); usart->US_CR = AT91C_US_RXDIS | AT91C_US_TXDIS | (AT91C_US_RSTRX | AT91C_US_RSTTX); /* FIXME: wait for some time */ usart->US_CR = AT91C_US_RXDIS | AT91C_US_TXDIS; /* ISO7816 T=0 mode with external clock input */ usart->US_MR = AT91C_US_USMODE_ISO7816_0 | AT91C_US_CLKS_EXT | AT91C_US_CHRL_8_BITS | AT91C_US_NBSTOP_1_BIT | AT91C_US_CKLO | AT91C_US_INACK; /* Disable all interrupts */ usart->US_IDR = 0xff; /* Clock Divider = 1, i.e. no division of SCLK */ usart->US_BRGR = (0x0000 << 16) | 0x0001; /* Disable Receiver Time-out */ usart->US_RTOR = 0; /* Disable Transmitter Timeguard */ usart->US_TTGR = 0; pio_irq_register(SIMTRACE_PIO_nRST, &reset_pin_irq); AT91F_PIO_CfgInputFilter(AT91C_BASE_PIOA, SIMTRACE_PIO_nRST); pio_irq_enable(SIMTRACE_PIO_nRST); }