summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2010-11-14 23:04:16 +0100
committerHarald Welte <laforge@gnumonks.org>2010-11-14 23:04:16 +0100
commit50b1aff6f6f2a3a127f6121c5dedd8c2fa5a1c4d (patch)
tree22aff7eab132784ab11736c63ccf1c6156ed809c /firmware
parent506b033dd2ca8e4625f50f1dbafd2ce171fe03b8 (diff)
Initial version of a SAM7 USART based ISO7816-3 T=0 sniffer
Diffstat (limited to 'firmware')
-rw-r--r--firmware/include/openpcd.h3
-rw-r--r--firmware/src/openpcd.h7
-rw-r--r--firmware/src/simtrace.h18
-rw-r--r--firmware/src/simtrace/iso7816_uart.c399
-rw-r--r--firmware/src/simtrace/main_simtrace.c98
-rw-r--r--firmware/src/simtrace/usb_strings_app.txt6
6 files changed, 529 insertions, 2 deletions
diff --git a/firmware/include/openpcd.h b/firmware/include/openpcd.h
index e5a0ac7..ea32764 100644
--- a/firmware/include/openpcd.h
+++ b/firmware/include/openpcd.h
@@ -34,6 +34,8 @@ enum openpcd_cmd_class {
OPENPCD_CMD_CLS_ADC = 0x5,
OPENPCD_CMD_CLS_LIBRFID = 0x6,
OPENPCD_CMD_CLS_PRESENCE = 0x7,
+ /* SIM SCAN */
+ OPENPCD_CMD_CLS_SIM = 0x8,
/* PICC (transponder) side */
OPENPCD_CMD_CLS_PICC = 0xe,
@@ -102,6 +104,7 @@ enum openpcd_cmd_class {
#define OPENPCD_VENDOR_ID 0x16c0
+#define SIMTRACE_PRODUCT_ID 0x0762
#define OPENPCD_PRODUCT_ID 0x076b
#define OPENPICC_PRODUCT_ID 0x076c
#define OPENPCD_OUT_EP 0x01
diff --git a/firmware/src/openpcd.h b/firmware/src/openpcd.h
index a65cd0e..3bc890e 100644
--- a/firmware/src/openpcd.h
+++ b/firmware/src/openpcd.h
@@ -10,11 +10,13 @@
#define USB_VENDOR_ID OPENPCD_VENDOR_ID
-#ifdef OLIMEX
+#if defined(OLIMEX) || defined(SIMTRACE)
#define OPENPCD_PIO_LED2 AT91C_PIO_PA17
#define OPENPCD_PIO_LED1 AT91C_PIO_PA18
#define OPENPCD_PIO_UDP_CNX AT91C_PIO_PA24
-#define OPENPCD_PIO_UDP_PUP AT91C_PIO_PA16
+#define OPENPCD_PIO_UDP_PUPv4 AT91C_PIO_PA16
+#define PIO_BOOTLDR AT91C_PIO_PA19 /* B1 used as bootloader switch */
+#define USB_PRODUCT_ID SIMTRACE_PRODUCT_ID
#else
#if defined(PCD)
#define OPENPCD_PIO_UDP_CNX AT91C_PIO_PA15
@@ -166,6 +168,7 @@
#define OPENPCD_IRQ_PRIO_SPI AT91C_AIC_PRIOR_HIGHEST
#define OPENPCD_IRQ_PRIO_SSC (AT91C_AIC_PRIOR_HIGHEST-1)
#define OPENPCD_IRQ_PRIO_SYS (AT91C_AIC_PRIOR_HIGHEST-2)
+#define OPENPCD_IRQ_PRIO_USART (AT91C_AIC_PRIOR_HIGHEST-3)
#define OPENPCD_IRQ_PRIO_TC_FDT (AT91C_AIC_PRIOR_LOWEST+3)
#define OPENPCD_IRQ_PRIO_UDP (AT91C_AIC_PRIOR_LOWEST+2)
#define OPENPCD_IRQ_PRIO_PIT (AT91C_AIC_PRIOR_LOWEST+1)
diff --git a/firmware/src/simtrace.h b/firmware/src/simtrace.h
new file mode 100644
index 0000000..5a27c31
--- /dev/null
+++ b/firmware/src/simtrace.h
@@ -0,0 +1,18 @@
+
+#define USB_VENDOR_ID OPENPCD_VENDOR_ID
+
+#ifdef SIMTRACE
+#define OPENPCD_PIO_LED2 AT91C_PIO_PA17
+#define OPENPCD_PIO_LED1 AT91C_PIO_PA18
+#define OPENPCD_PIO_UDP_CNX AT91C_PIO_PA24
+#define OPENPCD_PIO_UDP_PUP AT91C_PIO_PA16
+#define USB_PRODUCT_ID SIMTRACE_PRODUCT_ID
+#else
+#error "unknown PCB"
+#endif
+
+#define SIMTRACE_PIO_CLK AT91C_PA2_SCK0
+#define SIMTRACE_PIO_IO AT91C_PA6_TXD0
+#define SIMTRACE_PIO_nRST AT91C_PIO_PA7
+
+
diff --git a/firmware/src/simtrace/iso7816_uart.c b/firmware/src/simtrace/iso7816_uart.c
new file mode 100644
index 0000000..9022830
--- /dev/null
+++ b/firmware/src/simtrace/iso7816_uart.c
@@ -0,0 +1,399 @@
+/* Driver for AT91SAM7 USART0 in ISO7816-3 mode
+ * (C) 2010 by Harald Welte <hwelte@hmw-consulting.de>
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <AT91SAM7.h>
+#include <lib_AT91SAM7.h>
+#include <openpcd.h>
+
+#include <os/usb_handler.h>
+#include <os/dbgu.h>
+
+#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,
+ ISO7816_S_WAIT_READER, /* waiting for data from reader */
+ ISO7816_S_WAIT_CARD, /* waiting for data from card */
+};
+
+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,
+};
+
+struct iso7816_3_handle {
+ enum iso7816_3_state state;
+
+ u_int8_t fi;
+ u_int8_t di;
+
+ u_int8_t atr_idx;
+ u_int8_t atr_hist_len;
+ u_int8_t atr_last_td;
+ enum atr_state atr_state;
+ u_int8_t atr[64];
+
+ u_int16_t apdu_len;
+ u_int16_t apdu_idx;
+};
+
+struct iso7816_3_handle isoh;
+
+
+/* Table 6 from ISO 7816-3 */
+static const u_int16_t fi_table[] = {
+ 0, 372, 558, 744, 1116, 1488, 1860, 0,
+ 0, 512, 768, 1024, 1536, 2048, 0, 0
+};
+
+/* Table 7 from ISO 7816-3 */
+static const u_int8_t di_table[] = {
+ 0, 1, 2, 4, 8, 16, 0, 0,
+ 0, 0, 2, 4, 8, 16, 32, 64,
+};
+
+static int compute_fidi_ratio(u_int8_t fi, u_int8_t di)
+{
+ u_int16_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;
+
+ if (di < 8)
+ ret = f / d;
+ else
+ ret = f * d;
+
+ return ret;
+}
+
+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;
+ 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;
+}
+
+static void set_state(struct iso7816_3_handle *ih, enum iso7816_3_state new_state)
+{
+ if (new_state == ISO7816_S_WAIT_ATR) {
+ int rc;
+ /* 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_FIDI = rc & 0x3ff;
+ 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;
+ }
+
+ if (ih->state == new_state)
+ return;
+
+ DEBUGPCR("7816 state %u -> %u", ih->state, new_state);
+ ih->state = new_state;
+}
+
+/* determine the next ATR state based on received interface byte */
+static enum atr_state next_intb_state(struct iso7816_3_handle *ih, u_int8_t ch)
+{
+ switch (ih->atr_state) {
+ case ATR_S_WAIT_TD:
+ case ATR_S_WAIT_T0:
+ ih->atr_last_td = ch;
+ goto from_td;
+ case ATR_S_WAIT_TC:
+ goto from_tc;
+ case ATR_S_WAIT_TB:
+ goto from_tb;
+ case ATR_S_WAIT_TA:
+ if ((ih->atr_last_td & 0x0f) == 0) {
+ /* This must be TA1 */
+ ih->fi = ch >> 4;
+ ih->di = ch & 0xf;
+ DEBUGPCR("found Fi=%u Di=%u", ih->fi, ih->di);
+ }
+ goto from_ta;
+ }
+
+ 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;
+
+ return ATR_S_WAIT_HIST;
+}
+
+/* process an incomng ATR byte */
+static enum iso7816_3_state
+process_byte_atr(struct iso7816_3_handle *ih, u_int8_t byte)
+{
+ int rc;
+
+ /* 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:
+ ih->atr_hist_len = byte & 0xf;
+ 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--;
+ if (ih->atr_hist_len == 0)
+ set_atr_state(ih, ATR_S_WAIT_TCK);
+ break;
+ case ATR_S_WAIT_TCK:
+ /* FIXME: process 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;
+ }
+
+ 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)
+{
+ int new_state = -1;
+
+ 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_READER:
+ new_state = process_byte_reader(ih, byte);
+ break;
+ case ISO7816_S_WAIT_CARD:
+ //new_state = process_byte_card(ih, byte);
+ break;
+ }
+
+ if (new_state != -1)
+ set_state(ih, new_state);
+}
+
+static int __ramfunc usart_irq(void)
+{
+ u_int32_t csr = usart->US_CSR;
+ u_int8_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)) {
+ /* some error has occurrerd */
+ }
+}
+
+void iso_uart_dump(void)
+{
+ u_int32_t csr = usart->US_CSR;
+
+ DEBUGPCR("USART CSR=0x%08x", csr);
+}
+
+void iso_uart_rst(unsigned int state)
+{
+ DEBUGPCR("USART 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;
+
+ /* Enable the receiver */
+ usart->US_CR = AT91C_US_RXEN;
+
+ set_state(&isoh, ISO7816_S_WAIT_ATR);
+}
+
+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;
+ 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;
+ usart->US_BRGR = (0x0000 << 16) | 0x0001;
+ }
+}
+
+void iso_uart_init(void)
+{
+ DEBUGPCR("USART Initializing");
+
+ 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);
+
+ 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;
+
+ 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;
+
+ /* 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;
+}
diff --git a/firmware/src/simtrace/main_simtrace.c b/firmware/src/simtrace/main_simtrace.c
new file mode 100644
index 0000000..d5a22dd
--- /dev/null
+++ b/firmware/src/simtrace/main_simtrace.c
@@ -0,0 +1,98 @@
+/* OpenPICC Main Program
+ * (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 <errno.h>
+#include <include/lib_AT91SAM7.h>
+#include <include/openpcd.h>
+#include <os/dbgu.h>
+#include <os/led.h>
+#include <os/pcd_enumerate.h>
+#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>
+
+void _init_func(void)
+{
+ /* low-level hardware initialization */
+ pio_irq_init();
+ iso_uart_init();
+
+ /* high-level protocol */
+ //opicc_usbapi_init();
+ led_switch(1, 0);
+ led_switch(2, 1);
+}
+
+static void help(void)
+{
+ DEBUGPCR("r: iso uart Rx mode\r\n"
+ "c: toggle clock master/slave\r\n"
+ "l: set nRST to low (active)\r\n"
+ "h: set nRST to high (inactive)\r\n"
+ "o: set nRST to input\r\n");
+}
+
+int _main_dbgu(char key)
+{
+ static int i = 0;
+ DEBUGPCRF("main_dbgu");
+
+ switch (key) {
+ case 'r':
+ iso_uart_rx_mode();
+ break;
+ case 'c':
+ iso_uart_clk_master(i++ & 1);
+ break;
+ case 'l':
+ iso_uart_rst(0);
+ break;
+ case 'h':
+ iso_uart_rst(1);
+ break;
+ case 'o':
+ iso_uart_rst(2);
+ break;
+ case 'd':
+ iso_uart_dump();
+ break;
+ case '?':
+ help();
+ break;
+ }
+
+ return -EINVAL;
+}
+
+void _main_func(void)
+{
+ /* first we try to get rid of pending to-be-sent stuff */
+ usb_out_process();
+
+ /* next we deal with incoming reqyests from USB EP1 (OUT) */
+ usb_in_process();
+
+ udp_unthrottle();
+}
diff --git a/firmware/src/simtrace/usb_strings_app.txt b/firmware/src/simtrace/usb_strings_app.txt
new file mode 100644
index 0000000..c1d3c95
--- /dev/null
+++ b/firmware/src/simtrace/usb_strings_app.txt
@@ -0,0 +1,6 @@
+SimTrace DFU Interface - Application Partition
+SimTrace DFU Interface - Bootloader Partition
+hmw-consulting.de
+SimTrace SIM Sniffer - Runtime Mode
+SimTrace Runtime Configuration
+SimTrace Runtime Interface
personal git repositories of Harald Welte. Your mileage may vary