diff options
Diffstat (limited to 'firmware/src/simtrace')
| -rw-r--r-- | firmware/src/simtrace/iso7816_uart.c | 226 | ||||
| -rw-r--r-- | firmware/src/simtrace/iso7816_uart.h | 6 | ||||
| -rw-r--r-- | firmware/src/simtrace/main_simtrace.c | 10 | ||||
| -rw-r--r-- | firmware/src/simtrace/tc_etu.c | 124 | ||||
| -rw-r--r-- | firmware/src/simtrace/tc_etu.h | 4 | 
5 files changed, 292 insertions, 78 deletions
| diff --git a/firmware/src/simtrace/iso7816_uart.c b/firmware/src/simtrace/iso7816_uart.c index 6ed797c..18335f6 100644 --- a/firmware/src/simtrace/iso7816_uart.c +++ b/firmware/src/simtrace/iso7816_uart.c @@ -1,5 +1,20 @@ -/* Driver for AT91SAM7 USART0 in ISO7816-3 mode +/* Driver for AT91SAM7 USART0 in ISO7816-3 mode for passive sniffing   * (C) 2010 by Harald Welte <hwelte@hmw-consulting.de> + * + *  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 <errno.h> @@ -9,6 +24,8 @@  #include <lib_AT91SAM7.h>  #include <openpcd.h> +#include <simtrace_usb.h> +  #include <os/usb_handler.h>  #include <os/dbgu.h>  #include <os/pio_irq.h> @@ -23,11 +40,12 @@ 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, -	ISO7816_S_WAIT_READER,	/* waiting for data from reader */ -	ISO7816_S_WAIT_CARD,	/* waiting for data from card */ +	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 */  }; +/* detailed sub-states of ISO7816_S_IN_ATR */  enum atr_state {  	ATR_S_WAIT_TS,  	ATR_S_WAIT_T0, @@ -45,6 +63,8 @@ struct iso7816_3_handle {  	u_int8_t fi;  	u_int8_t di; +	u_int8_t wi; +	u_int32_t waiting_time;  	u_int8_t atr_idx;  	u_int8_t atr_hist_len; @@ -52,8 +72,9 @@ struct iso7816_3_handle {  	enum atr_state atr_state;  	u_int8_t atr[64]; -	u_int16_t apdu_len; -	u_int16_t apdu_idx; +	struct simtrace_hdr sh; + +	struct req_ctx *rctx;  };  struct iso7816_3_handle isoh; @@ -71,6 +92,7 @@ static const u_int8_t di_table[] = {  	0, 0, 2, 4, 8, 16, 32, 64,  }; +/* compute the F/D ratio based on Fi and Di values */  static int compute_fidi_ratio(u_int8_t fi, u_int8_t di)  {  	u_int16_t f, d; @@ -96,6 +118,43 @@ static int compute_fidi_ratio(u_int8_t fi, u_int8_t di)  	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; + +	/* 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; +} + + +/* 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) { @@ -110,30 +169,48 @@ static void set_atr_state(struct iso7816_3_handle *ih, enum 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) { -		int rc; -		/* Initial Fi / Di ratio */ +		/* Reset to initial Fi / Di ratio */  		ih->fi = 1;  		ih->di = 1; -		rc = compute_fidi_ratio(ih->fi, ih->di); -		DEBUGPCRF("computed Fi(%u) Di(%u) ratio: %d", ih->fi, ih->di, rc); -		usart->US_CR |= AT91C_US_RXDIS | AT91C_US_RSTRX; -		usart->US_FIDI = rc & 0x3ff; -		usart->US_CR |= AT91C_US_RXEN; +		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); -	} else if (new_state == ISO7816_S_WAIT_READER) { -		/* CLA INS P1 P2 LEN */ -		ih->apdu_len = 5; -		ih->apdu_idx = 0; -	} else if (new_state == ISO7816_S_WAIT_CARD) { -		/* 8.2.2 procedure bytes sent by the card */ -		/* FIXME: NULL byte and similar oddities */ -		ih->apdu_len += 2; -	}  +		/* Notice that we are just coming out of reset */ +		ih->sh.flags |= SIMTRACE_FLAG_ATR; +	}  	if (ih->state == new_state)  		return; @@ -151,6 +228,10 @@ static enum atr_state next_intb_state(struct iso7816_3_handle *ih, u_int8_t ch)  		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; @@ -204,7 +285,9 @@ process_byte_atr(struct iso7816_3_handle *ih, u_int8_t byte)  		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: @@ -215,62 +298,31 @@ process_byte_atr(struct iso7816_3_handle *ih, u_int8_t 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)  			set_atr_state(ih, ATR_S_WAIT_TCK);  		break;  	case ATR_S_WAIT_TCK: -		/* FIXME: process TCK */ +		/* FIXME: process and verify the TCK */  		set_atr_state(ih, ATR_S_DONE); -		/* FIXME: update Fi/Di */ -		rc = compute_fidi_ratio(ih->fi, ih->di); -		if (rc > 0 && rc < 0x400) { -			DEBUGPCR("computed FiDi ratio %d", rc); -			/* update baud rate generator in UART */ -			usart->US_CR |= AT91C_US_RXDIS| AT91C_US_RSTRX; -			usart->US_FIDI = rc & 0x3ff; -			usart->US_CR |= AT91C_US_RXEN; -		} else -			DEBUGPCRF("computed FiDi ratio %d unsupported", rc); -		return ISO7816_S_WAIT_READER; +		/* update baud rate generator with Fi/Di */ +		update_fidi(ih); +		/* update the waiting time */ +		ih->waiting_time = 960 * di_table[ih->di] * ih->wi; +		tc_etu_set_wtime(ih->waiting_time); +		return ISO7816_S_WAIT_APDU;  	}  	return ISO7816_S_IN_ATR;  } -/* process an incomng byte from the reader */ -static enum iso7816_3_state -process_byte_reader(struct iso7816_3_handle *ih, u_int8_t byte) -{ -	/* add response length to total number of expected bytes */ -	if (ih->apdu_idx == 4) -		ih->apdu_len += byte; - -	ih->apdu_idx++; -	 -	/* once we have received all bytes, transition to card response */ -	if (ih->apdu_idx == ih->apdu_len) -		return ISO7816_S_WAIT_CARD; - -	return ISO7816_S_WAIT_READER; -} - -/* process an incomng byte from the card */ -static enum iso7816_3_state -process_byte_card(struct iso7816_3_handle *ih, u_int8_t byte) -{ -	ih->apdu_idx++; -	 -	/* once we have received all bytes, apdu is finished */ -	if (ih->apdu_idx == ih->apdu_len) -		return ISO7816_S_WAIT_READER; - -	return ISO7816_S_WAIT_CARD; -} - - -void process_byte(struct iso7816_3_handle *ih, u_int8_t byte) +static void process_byte(struct iso7816_3_handle *ih, u_int8_t byte)  {  	int new_state = -1; +	struct req_ctx *rctx; + +	if (!ih->rctx) +		refill_rctx(ih);  	switch (ih->state) {  	case ISO7816_S_RESET: @@ -279,18 +331,40 @@ void process_byte(struct iso7816_3_handle *ih, u_int8_t byte)  	case ISO7816_S_IN_ATR:  		new_state = process_byte_atr(ih, byte);  		break; -	case ISO7816_S_WAIT_READER: -		new_state = process_byte_reader(ih, byte); -		break; -	case ISO7816_S_WAIT_CARD: -		new_state = process_byte_card(ih, byte); +	case ISO7816_S_WAIT_APDU: +	case ISO7816_S_IN_APDU: +		new_state = ISO7816_S_IN_APDU;  		break;  	} +	rctx = ih->rctx; +	if (!rctx) { +		DEBUGPCR("==> Lost byte, missing 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) +		send_rctx(ih); +  	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); +	} +	set_state(&isoh, ISO7816_S_WAIT_APDU); +} +  static __ramfunc void usart_irq(void)  {  	u_int32_t csr = usart->US_CSR; @@ -301,7 +375,7 @@ static __ramfunc void usart_irq(void)  	if (csr & AT91C_US_RXRDY) {  		/* at least one character received */  		octet = usart->US_RHR & 0xff; -		DEBUGP("%02x ", octet); +		//DEBUGP("%02x ", octet);  		process_byte(&isoh, octet);  	} @@ -310,7 +384,7 @@ static __ramfunc void usart_irq(void)  	}  	if (csr & (AT91C_US_PARE|AT91C_US_FRAME|AT91C_US_OVRE)) { -		/* some error has occurrerd */ +		/* FIXME: some error has occurrerd */  	}  } @@ -358,7 +432,6 @@ void iso_uart_rx_mode(void)  	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);  } @@ -383,6 +456,8 @@ void iso_uart_init(void)  {  	DEBUGPCR("USART Initializing"); +	refill_rctx(&isoh); +  	/* make sure we get clock from the power management controller */  	AT91F_US0_CfgPMC(); @@ -395,7 +470,8 @@ void iso_uart_init(void)  			      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); +	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; diff --git a/firmware/src/simtrace/iso7816_uart.h b/firmware/src/simtrace/iso7816_uart.h new file mode 100644 index 0000000..ed1c898 --- /dev/null +++ b/firmware/src/simtrace/iso7816_uart.h @@ -0,0 +1,6 @@ + +void iso_uart_dump(void); +void iso_uart_rst(unsigned int state); +void iso_uart_rx_mode(void); +void iso_uart_clk_master(unsigned int master); +void iso_uart_init(void); diff --git a/firmware/src/simtrace/main_simtrace.c b/firmware/src/simtrace/main_simtrace.c index d5a22dd..5e4302e 100644 --- a/firmware/src/simtrace/main_simtrace.c +++ b/firmware/src/simtrace/main_simtrace.c @@ -27,22 +27,26 @@  #include <os/usb_handler.h>  #include "../openpcd.h"  #include <os/main.h> -#include <os/pwm.h> -#include <os/tc_cdiv.h>  #include <os/pio_irq.h> -//#include <simtrace/iso7816_uart.h> +#include <simtrace/tc_etu.h> +#include <simtrace/iso7816_uart.h>  void _init_func(void)  {  	/* low-level hardware initialization */  	pio_irq_init();  	iso_uart_init(); +	tc_etu_init(); + +	usbtest_init();  	/* high-level protocol */  	//opicc_usbapi_init();  	led_switch(1, 0);  	led_switch(2, 1); + +	iso_uart_rx_mode();  }  static void help(void) diff --git a/firmware/src/simtrace/tc_etu.c b/firmware/src/simtrace/tc_etu.c new file mode 100644 index 0000000..93d2da0 --- /dev/null +++ b/firmware/src/simtrace/tc_etu.c @@ -0,0 +1,124 @@ +/* SimTrace TC (Timer / Clock) support code + * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de> + * + *  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 <lib_AT91SAM7.h> +#include <AT91SAM7.h> +#include <os/dbgu.h> + +#include "../openpcd.h" + +static AT91PS_TCB tcb; +static AT91PS_TC tcetu = AT91C_BASE_TC0; + +static u_int16_t waiting_time = 9600; +static u_int16_t clocks_per_etu = 372; +static u_int16_t wait_events; + +static __ramfunc void tc_etu_irq(void) +{ +	u_int32_t sr = tcetu->TC_SR; +	static u_int16_t nr_events; + +	if (sr & AT91C_TC_ETRGS) { +		/* external trigger, i.e. we have seen a bit on I/O */ +		//DEBUGPCR("tE"); +		nr_events = 0; +		/* Make sure we don't accept any additional external trigger */ +		tcetu->TC_CMR &= ~AT91C_TC_ENETRG; +	} + +	if (sr & AT91C_TC_CPCS) { +		/* Compare C event has occurred, i.e. 1 etu expired */ +		//DEBUGPCR("tC"); +		nr_events++; +		if (nr_events >= wait_events) { +			/* enable external triggers again to catch start bit */ +			tcetu->TC_CMR |= AT91C_TC_ENETRG; + +			/* disable and re-enable clock to make it stop */ +			tcetu->TC_CCR = AT91C_TC_CLKDIS; +			tcetu->TC_CCR = AT91C_TC_CLKEN; + +			//DEBUGPCR("%u", nr_events); + +			/* Indicate that the waiting time has expired */ +			iso7816_wtime_expired(); +		} +	} +} + +static void recalc_nr_events(void) +{ +	wait_events = waiting_time/12; +	/* clocks_per_etu * 12 equals 'sbit + 8 data bits + parity + 2 stop bits */ +	tcetu->TC_RC = clocks_per_etu * 12; +} + +void tc_etu_set_wtime(u_int16_t wtime) +{ +	waiting_time = wtime; +	recalc_nr_events(); +} + +void tc_etu_set_etu(u_int16_t etu) +{ +	clocks_per_etu = etu; +	recalc_nr_events(); +} + +void tc_etu_init(void) +{ +	/* Cfg PA4(TCLK0), PA0(TIOA0), PA1(TIOB0) */ +	AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, 0,  +			    AT91C_PA4_TCLK0 | AT91C_PA0_TIOA0 | AT91C_PA1_TIOB0); + +	AT91F_PMC_EnablePeriphClock(AT91C_BASE_PMC,  +				    ((unsigned int) 1 << AT91C_ID_TC0)); + +	/* Connect TCLK0 to XC0 */ +	tcb->TCB_BMR &= ~(AT91C_TCB_TC0XC0S); +	tcb->TCB_BMR |=  AT91C_TCB_TC0XC0S_TCLK0; + +	/* Register Interrupt handler */ +	AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_TC0, +			      OPENPCD_IRQ_PRIO_TC_FDT, +			      AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, &tc_etu_irq); +	AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_TC0); + +	/* enable interrupts for Compare-C and External Trigger */ +	tcetu->TC_IER = AT91C_TC_CPCS | AT91C_TC_ETRGS; + +	tcetu->TC_CMR = AT91C_TC_CLKS_XC0 |	/* XC0 (TCLK0) clock */ +		        AT91C_TC_WAVE |		/* Wave Mode */ +		        AT91C_TC_ETRGEDG_FALLING |/* Ext trig on falling edge */ +		        AT91C_TC_EEVT_TIOB |	/* Ext trigger is TIOB0 */ +		        AT91C_TC_ENETRG | 	/* Enable ext. trigger */ +		        AT91C_TC_WAVESEL_UP_AUTO |/* Wave mode UP */ +		        AT91C_TC_ACPA_SET |	/* Set TIOA0 on A compare */ +		        AT91C_TC_ACPC_CLEAR |	/* Clear TIOA0 on C compare */ +		        AT91C_TC_ASWTRG_CLEAR;	/* Clear TIOa0 on software trigger */ + +	tc_etu_set_etu(372); + +	/* Enable master clock for TC0 */ +	tcetu->TC_CCR = AT91C_TC_CLKEN; + +	/* Reset to start timers */ +	tcb->TCB_BCR = 1; +} diff --git a/firmware/src/simtrace/tc_etu.h b/firmware/src/simtrace/tc_etu.h new file mode 100644 index 0000000..59d9031 --- /dev/null +++ b/firmware/src/simtrace/tc_etu.h @@ -0,0 +1,4 @@ + +void tc_etu_set_wtime(u_int16_t wtime); +void tc_etu_set_etu(u_int16_t etu); +void tc_etu_init(void); | 
