diff options
Diffstat (limited to 'firmware/src')
25 files changed, 1528 insertions, 35 deletions
diff --git a/firmware/src/dfu/dfu.c b/firmware/src/dfu/dfu.c index 8425c28..0e22256 100644 --- a/firmware/src/dfu/dfu.c +++ b/firmware/src/dfu/dfu.c @@ -1,5 +1,5 @@ /* USB Device Firmware Update Implementation for OpenPCD - * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de> + * (C) 2006-2011 by Harald Welte <hwelte@hmw-consulting.de> * * This ought to be compliant to the USB DFU Spec 1.0 as available from * http://www.usb.org/developers/devclass_docs/usbdfu10.pdf @@ -37,6 +37,7 @@ #include <compile.h> #define SAM7DFU_SIZE 0x4000 +#define SAM7DFU_RAM_SIZE 0x2000 /* If debug is enabled, we need to access debug functions from flash * and therefore have to omit flashing */ @@ -69,6 +70,12 @@ #define led2on() AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPCD_PIO_LED2) #define led2off() AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPCD_PIO_LED2) +static int past_manifest = 0; +static int switch_to_ram = 0; /* IRQ handler requests main to jump to RAM */ +static u_int16_t usb_if_nr = 0; /* last SET_INTERFACE */ +static u_int16_t usb_if_alt_nr = 0; /* last SET_INTERFACE AltSetting */ +static u_int16_t usb_if_alt_nr_dnload = 0; /* AltSetting during last dnload */ + static void __dfufunc udp_init(void) { /* Set the PLL USB Divider */ @@ -210,14 +217,37 @@ static void __dfufunc udp_ep0_send_stall(void) } -static u_int8_t *ptr = (u_int8_t *) AT91C_IFLASH + SAM7DFU_SIZE; +static int first_download = 1; +static u_int8_t *ptr, *ptr_max; static __dfudata u_int8_t dfu_status; __dfudata u_int32_t dfu_state = DFU_STATE_appIDLE; static u_int32_t pagebuf32[AT91C_IFLASH_PAGE_SIZE/4]; -static int __dfufunc handle_dnload(u_int16_t val, u_int16_t len) +static void chk_first_dnload_set_ptr(void) +{ + if (!first_download) + return; + + switch (usb_if_alt_nr) { + case 0: + ptr = (u_int8_t *) AT91C_IFLASH + SAM7DFU_SIZE; + ptr_max = AT91C_IFLASH + AT91C_IFLASH_SIZE - ENVIRONMENT_SIZE; + break; + case 1: + ptr = (u_int8_t *) AT91C_IFLASH; + ptr_max = AT91C_IFLASH + SAM7DFU_SIZE; + break; + case 2: + ptr = (u_int8_t *) AT91C_ISRAM + SAM7DFU_RAM_SIZE; + ptr_max = AT91C_ISRAM + AT91C_ISRAM_SIZE; + break; + } + first_download = 0; +} + +static int __dfufunc handle_dnload_flash(u_int16_t val, u_int16_t len) { - volatile u_int32_t *p = (volatile u_int32_t *)ptr; + volatile u_int32_t *p; u_int8_t *pagebuf = (u_int8_t *) pagebuf32; int i; @@ -238,13 +268,20 @@ static int __dfufunc handle_dnload(u_int16_t val, u_int16_t len) dfu_status = DFU_STATUS_errADDRESS; return RET_STALL; } + chk_first_dnload_set_ptr(); + p = (volatile u_int32_t *)ptr; + if (len == 0) { DEBUGP("zero-size write -> MANIFEST_SYNC "); - flash_page(p); + if (((unsigned long)p % AT91C_IFLASH_PAGE_SIZE) != 0) + flash_page(p); dfu_state = DFU_STATE_dfuMANIFEST_SYNC; + first_download = 1; return RET_ZLP; } - if (ptr + len >= (u_int8_t *) AT91C_IFLASH + AT91C_IFLASH_SIZE - ENVIRONMENT_SIZE ) { + + /* check if we would exceed end of memory */ + if (ptr + len > ptr_max) { DEBUGP("end of write exceeds flash end "); dfu_state = DFU_STATE_dfuERROR; dfu_status = DFU_STATUS_errADDRESS; @@ -256,10 +293,10 @@ static int __dfufunc handle_dnload(u_int16_t val, u_int16_t len) DEBUGR(hexdump(pagebuf, len)); - /* we can only access the write buffer with correctly aligned - * 32bit writes ! */ #ifndef DEBUG_DFU_NOFLASH DEBUGP("copying "); + /* we can only access the write buffer with correctly aligned + * 32bit writes ! */ for (i = 0; i < len/4; i++) { *p++ = pagebuf32[i]; /* If we have filled a page buffer, flash it */ @@ -274,6 +311,57 @@ static int __dfufunc handle_dnload(u_int16_t val, u_int16_t len) return RET_ZLP; } +static int __dfufunc handle_dnload_ram(u_int16_t val, u_int16_t len) +{ + DEBUGE("download "); + + if (len > AT91C_IFLASH_PAGE_SIZE) { + /* Too big. Not that we'd really care, but it's a + * DFU protocol violation */ + DEBUGP("length exceeds flash page size "); + dfu_state = DFU_STATE_dfuERROR; + dfu_status = DFU_STATUS_errADDRESS; + return RET_STALL; + } + chk_first_dnload_set_ptr(); + + if (len == 0) { + DEBUGP("zero-size write -> MANIFEST_SYNC "); + dfu_state = DFU_STATE_dfuMANIFEST_SYNC; + first_download = 1; + return RET_ZLP; + } + + /* check if we would exceed end of memory */ + if (ptr + len >= ptr_max) { + DEBUGP("end of write exceeds RAM end "); + dfu_state = DFU_STATE_dfuERROR; + dfu_status = DFU_STATUS_errADDRESS; + return RET_STALL; + } + + /* drectly copy into RAM */ + DEBUGP("try_to_recv=%u ", len); + udp_ep0_recv_data(ptr, len); + + DEBUGR(hexdump(ptr, len)); + + ptr += len; + + return RET_ZLP; +} + +static int __dfufunc handle_dnload(u_int16_t val, u_int16_t len) +{ + usb_if_alt_nr_dnload = usb_if_alt_nr; + switch (usb_if_alt_nr) { + case 2: + return handle_dnload_ram(val, len); + default: + return handle_dnload_flash(val, len); + } +} + #define AT91C_IFLASH_END ((u_int8_t *)AT91C_IFLASH + AT91C_IFLASH_SIZE) static __dfufunc int handle_upload(u_int16_t val, u_int16_t len) { @@ -285,9 +373,12 @@ static __dfufunc int handle_upload(u_int16_t val, u_int16_t len) udp_ep0_send_stall(); return -EINVAL; } + chk_first_dnload_set_ptr(); - if (ptr + len > AT91C_IFLASH_END) + if (ptr + len > AT91C_IFLASH_END) { len = AT91C_IFLASH_END - (u_int8_t *)ptr; + first_download = 1; + } udp_ep0_send_data((char *)ptr, len); ptr+= len; @@ -486,7 +577,16 @@ int __dfufunc dfu_ep0_handler(u_int8_t req_type, u_int8_t req, case DFU_STATE_dfuMANIFEST: switch (req) { case USB_REQ_DFU_GETSTATUS: + /* we don't want to change to WAIT_RST, as it + * would mean that we can not support another + * DFU transaction before doing the actual + * reset. Instead, we switch to idle and note + * that we've already been through MANIFST in + * the global variable 'past_manifest'. + */ + //dfu_state = DFU_STATE_dfuMANIFEST_WAIT_RST; dfu_state = DFU_STATE_dfuIDLE; + past_manifest = 1; handle_getstatus(); break; case USB_REQ_DFU_GETSTATE: @@ -595,7 +695,7 @@ __dfustruct const struct _dfu_desc dfu_cfg_descriptor = { .bLength = USB_DT_CONFIG_SIZE, .bDescriptorType = USB_DT_CONFIG, .wTotalLength = USB_DT_CONFIG_SIZE + - 2* USB_DT_INTERFACE_SIZE + + 3* USB_DT_INTERFACE_SIZE + USB_DT_DFU_SIZE, .bNumInterfaces = 1, .bConfigurationValue = 1, @@ -637,6 +737,21 @@ __dfustruct const struct _dfu_desc dfu_cfg_descriptor = { .iInterface = 0, #endif }, + .uif[2] = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x02, + .bNumEndpoints = 0x00, + .bInterfaceClass = 0xfe, + .bInterfaceSubClass = 0x01, + .bInterfaceProtocol = 0x02, +#ifdef CONFIG_USB_STRING + .iInterface = 6, +#else + .iInterface = 0, +#endif + }, .func_dfu = DFU_FUNC_DESC, }; @@ -820,9 +935,11 @@ static __dfufunc void dfu_udp_ep0_handler(void) udp_ep0_send_stall(); break; case STD_SET_INTERFACE: - DEBUGE("SET INTERFACE "); - /* FIXME: store the interface number somewhere, once + DEBUGE("SET INTERFACE(if=%d, alt=%d) ", wIndex, wValue); + /* store the interface number somewhere, once * we need to support DFU flashing DFU */ + usb_if_alt_nr = wValue; + usb_if_nr = wIndex; udp_ep0_send_zlp(); break; default: @@ -858,12 +975,22 @@ static __dfufunc void dfu_udp_irq(void) cur_config = 0; if (dfu_state == DFU_STATE_dfuMANIFEST_WAIT_RST || - dfu_state == DFU_STATE_dfuMANIFEST) { - AT91F_RSTSoftReset(AT91C_BASE_RSTC, AT91C_RSTC_PROCRST| - AT91C_RSTC_PERRST| - AT91C_RSTC_EXTRST); + dfu_state == DFU_STATE_dfuMANIFEST || + past_manifest) { + AT91F_DBGU_Printk("sam7dfu: switching to APP mode\r\n"); + switch (usb_if_alt_nr_dnload) { + case 2: + switch_to_ram = 1; + break; + default: + /* reset back into the main application */ + AT91F_RSTSoftReset(AT91C_BASE_RSTC, + AT91C_RSTC_PROCRST| + AT91C_RSTC_PERRST| + AT91C_RSTC_EXTRST); + break; + } } - } if (isr & AT91C_UDP_EPINT0) @@ -902,7 +1029,7 @@ void __dfufunc dfu_main(void) AT91F_DBGU_Init(); AT91F_DBGU_Printk("\n\r\n\rsam7dfu - AT91SAM7 USB DFU bootloader\n\r" - "(C) 2006-2008 by Harald Welte <hwelte@hmw-consulting.de>\n\r" + "(C) 2006-2011 by Harald Welte <hwelte@hmw-consulting.de>\n\r" "This software is FREE SOFTWARE licensed under GNU GPL\n\r"); AT91F_DBGU_Printk("Version " COMPILE_SVNREV " compiled " COMPILE_DATE @@ -945,6 +1072,16 @@ void __dfufunc dfu_main(void) if( i== 0) { AT91F_WDTRestart(AT91C_BASE_WDTC); } + if (switch_to_ram) { + void (*ram_app_entry)(void); + int i; + for (i = 0; i < 32; i++) + AT91F_AIC_DisableIt(AT91C_BASE_AIC, i); + /* jump into RAM */ + AT91F_DBGU_Printk("JUMP TO RAM\r\n"); + ram_app_entry = AT91C_ISRAM + SAM7DFU_RAM_SIZE; + ram_app_entry(); + } } } diff --git a/firmware/src/dfu/dfu.h b/firmware/src/dfu/dfu.h index a4184b4..c898197 100644 --- a/firmware/src/dfu/dfu.h +++ b/firmware/src/dfu/dfu.h @@ -61,6 +61,16 @@ .bInterfaceSubClass = 0x01, \ .bInterfaceProtocol = 0x01, \ .iInterface = 2, \ + }, { \ + .bLength = USB_DT_INTERFACE_SIZE, \ + .bDescriptorType = USB_DT_INTERFACE, \ + .bInterfaceNumber = 0x03, \ + .bAlternateSetting = 0x00, \ + .bNumEndpoints = 0x00, \ + .bInterfaceClass = 0xfe, \ + .bInterfaceSubClass = 0x01, \ + .bInterfaceProtocol = 0x01, \ + .iInterface = 3, \ }, \ } #else @@ -78,8 +88,18 @@ }, { \ .bLength = USB_DT_INTERFACE_SIZE, \ .bDescriptorType = USB_DT_INTERFACE, \ - .bInterfaceNumber = 0x02, \ - .bAlternateSetting = 0x00, \ + .bInterfaceNumber = 0x01, \ + .bAlternateSetting = 0x01, \ + .bNumEndpoints = 0x00, \ + .bInterfaceClass = 0xfe, \ + .bInterfaceSubClass = 0x01, \ + .bInterfaceProtocol = 0x01, \ + .iInterface = 0, \ + }, { \ + .bLength = USB_DT_INTERFACE_SIZE, \ + .bDescriptorType = USB_DT_INTERFACE, \ + .bInterfaceNumber = 0x01, \ + .bAlternateSetting = 0x02, \ .bNumEndpoints = 0x00, \ .bInterfaceClass = 0xfe, \ .bInterfaceSubClass = 0x01, \ @@ -98,7 +118,7 @@ struct _dfu_desc { struct usb_config_descriptor ucfg; - struct usb_interface_descriptor uif[2]; + struct usb_interface_descriptor uif[3]; struct usb_dfu_func_descriptor func_dfu; }; 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/os/dbgu.c b/firmware/src/os/dbgu.c index 026b56b..28d86a4 100644 --- a/firmware/src/os/dbgu.c +++ b/firmware/src/os/dbgu.c @@ -147,7 +147,7 @@ void AT91F_DBGU_Init(void) sysirq_register(AT91SAM7_SYSIRQ_DBGU, &DBGU_irq_handler); AT91F_DBGU_Printk("\n\r"); - AT91F_DBGU_Printk("(C) 2006 by Harald Welte <hwelte@hmw-consulting.de>\n\r" + AT91F_DBGU_Printk("(C) 2006-2011 by Harald Welte <hwelte@hmw-consulting.de>\n\r" "This software is FREE SOFTWARE licensed under GNU GPL\n\r"); AT91F_DBGU_Printk("Version " COMPILE_SVNREV " compiled " COMPILE_DATE diff --git a/firmware/src/os/req_ctx.c b/firmware/src/os/req_ctx.c index 73b6035..cc8d57b 100644 --- a/firmware/src/os/req_ctx.c +++ b/firmware/src/os/req_ctx.c @@ -28,9 +28,9 @@ /* FIXME: locking, FIFO order processing */ -#ifdef __AT91SAM7S64__ -#define NUM_RCTX_SMALL 4 -#define NUM_RCTX_LARGE 2 +#if defined(__AT91SAM7S64__) || defined(RUN_FROM_RAM) +#define NUM_RCTX_SMALL 16 +#define NUM_RCTX_LARGE 1 #else #define NUM_RCTX_SMALL 8 #define NUM_RCTX_LARGE 4 diff --git a/firmware/src/os/req_ctx.h b/firmware/src/os/req_ctx.h index ac9ab3f..94b5c5a 100644 --- a/firmware/src/os/req_ctx.h +++ b/firmware/src/os/req_ctx.h @@ -2,7 +2,7 @@ #define _REQ_CTX_H #define RCTX_SIZE_LARGE 2048 -#define RCTX_SIZE_SMALL 64 +#define RCTX_SIZE_SMALL 128 #define MAX_HDRSIZE sizeof(struct openpcd_hdr) #define MAX_REQSIZE (64-MAX_HDRSIZE) diff --git a/firmware/src/os/usb_descriptors_openpcd.h b/firmware/src/os/usb_descriptors_openpcd.h index ceb7ff6..58c54ec 100644 --- a/firmware/src/os/usb_descriptors_openpcd.h +++ b/firmware/src/os/usb_descriptors_openpcd.h @@ -20,8 +20,8 @@ const struct usb_device_descriptor dev_descriptor = { .idProduct = USB_PRODUCT_ID, .bcdDevice = 0x0030, /* Version 0.3 */ #ifdef CONFIG_USB_STRING - .iManufacturer = 3, - .iProduct = 4, + .iManufacturer = 4, + .iProduct = 5, .iSerialNumber = 0, #else .iManufacturer = 0, @@ -36,7 +36,7 @@ struct _desc { struct usb_interface_descriptor uif; struct usb_endpoint_descriptor ep[3]; #ifdef CONFIG_DFU - struct usb_interface_descriptor uif_dfu[2]; + struct usb_interface_descriptor uif_dfu[3]; #endif }; @@ -46,9 +46,9 @@ const struct _desc cfg_descriptor = { .bDescriptorType = USB_DT_CONFIG, .wTotalLength = USB_DT_CONFIG_SIZE + #ifdef CONFIG_DFU - 3 * USB_DT_INTERFACE_SIZE + + 4 * USB_DT_INTERFACE_SIZE + 3 * USB_DT_ENDPOINT_SIZE, - .bNumInterfaces = 3, + .bNumInterfaces = 4, #else 1 * USB_DT_INTERFACE_SIZE + 3 * USB_DT_ENDPOINT_SIZE, @@ -56,7 +56,7 @@ const struct _desc cfg_descriptor = { #endif .bConfigurationValue = 1, #ifdef CONFIG_USB_STRING - .iConfiguration = 5, + .iConfiguration = 6, #else .iConfiguration = 0, #endif @@ -73,7 +73,7 @@ const struct _desc cfg_descriptor = { .bInterfaceSubClass = 0, .bInterfaceProtocol = 0xff, #ifdef CONFIG_USB_STRING - .iInterface = 6, + .iInterface = 7, #else .iInterface = 0, #endif diff --git a/firmware/src/pcd/usb_strings_app.txt b/firmware/src/pcd/usb_strings_app.txt index a4e9f57..ce295dc 100644 --- a/firmware/src/pcd/usb_strings_app.txt +++ b/firmware/src/pcd/usb_strings_app.txt @@ -1,5 +1,6 @@ OpenPCD DFU Interface - Application Partition OpenPCD DFU Interface - Bootloader Partition +OpenPCD DFU Interface - RAM bitmanufaktur.de IT Solutions and hmw-consulting.de OpenPCD RFID Reader - Runtime Mode OpenPCD Runtime Configuration diff --git a/firmware/src/pcd/usb_strings_dfu.txt b/firmware/src/pcd/usb_strings_dfu.txt index a7714b7..9b18e60 100644 --- a/firmware/src/pcd/usb_strings_dfu.txt +++ b/firmware/src/pcd/usb_strings_dfu.txt @@ -3,3 +3,4 @@ OpenPCD RFID Reader - DFU Mode OpenPCD DFU Configuration OpenPCD DFU Interface - Application Partition OpenPCD DFU Interface - Bootloader Partition +OpenPCD DFU Interface - RAM diff --git a/firmware/src/picc/usb_strings_app.txt b/firmware/src/picc/usb_strings_app.txt index d892c72..5e8ed56 100644 --- a/firmware/src/picc/usb_strings_app.txt +++ b/firmware/src/picc/usb_strings_app.txt @@ -1,5 +1,6 @@ OpenPICC DFU Interface - Application Partition OpenPICC DFU Interface - Bootloader Partition +OpenPICC DFU Interface - RAM bitmanufaktur.de IT Solutions and hmw-consulting.de OpenPICC RFID Simulator - Runtime Mode OpenPICC Runtime Configuration diff --git a/firmware/src/picc/usb_strings_dfu.txt b/firmware/src/picc/usb_strings_dfu.txt index 681c30a..008259d 100644 --- a/firmware/src/picc/usb_strings_dfu.txt +++ b/firmware/src/picc/usb_strings_dfu.txt @@ -3,3 +3,4 @@ OpenPICC RFID Simulator - DFU Mode OpenPIIC DFU Configuration OpenPICC DFU Interface - Application Partition OpenPICC DFU Interface - Bootloader Partition +OpenPICC DFU Interface - RAM diff --git a/firmware/src/simtrace.h b/firmware/src/simtrace.h new file mode 100644 index 0000000..40ed3ab --- /dev/null +++ b/firmware/src/simtrace.h @@ -0,0 +1,41 @@ + +#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_PA29 +#define OPENPCD_PIO_UDP_PUP AT91C_PIO_PA16 +#define USB_PRODUCT_ID SIMTRACE_PRODUCT_ID +#else +#error "unknown PCB" +#endif + +/* 7816 UART for SIM-card side */ +#define SIMTRACE_PIO_CLK AT91C_PA2_SCK0 +#define SIMTRACE_PIO_CLK_T AT91C_PA4_TCLK0 +#define SIMTRACE_PIO_IO AT91C_PA6_TXD0 +#define SIMTRACE_PIO_IO_T AT91C_PA1_TIOB0 +#define SIMTRACE_PIO_nRST AT91C_PIO_PA7 +#define SIMTRACE_PIO_SW_SIM AT91C_PIO_PA8 + +/* 7816 UART for phone side */ +#define SIMTRACE_PIO_nRST_PH AT91C_PIO_PA24 +#define SIMTRACE_PIO_CLK_PH AT91C_PA23_SCK1 +#define SIMTRACE_PIO_CLK_PH_T AT91C_PA28_TCLK1 +#define SIMTRACE_PIO_IO_PH_TX AT91C_PA22_TXD1 +#define SIMTRACE_PIO_IO_PH_RX AT91C_PA21_RXD1 + +/* bus switch for SIM card connection */ +#define SIMTRACE_PIO_SC_SW AT91C_PIO_PA20 +#define SIMTRACE_PIO_IO_SW AT91C_PIO_PA19 + +#define SIMTRACE_PIO_VCC_SIM AT91C_PIO_PA5 +#define SIMTRACE_PIO_VCC_PHONE AT91C_PIO_PA30 + +/* SPI flash */ +#define PIO_SPIF_nWP AT91C_PIO_PA15 +#define PIO_SPIF_SCK AT91C_PA14_SPCK +#define PIO_SPIF_MOSI AT91C_PA13_MOSI +#define PIO_SPIF_MISO AT91C_PA12_MISO +#define PIO_SPIF_nCS AT91C_PA11_NPCS0 diff --git a/firmware/src/simtrace/iso7816_uart.c b/firmware/src/simtrace/iso7816_uart.c new file mode 100644 index 0000000..cd1a2f0 --- /dev/null +++ b/firmware/src/simtrace/iso7816_uart.c @@ -0,0 +1,641 @@ +/* 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> +#include <string.h> +#include <sys/types.h> +#include <AT91SAM7.h> +#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> + +#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; + + u_int8_t fi; + u_int8_t di; + u_int8_t wi; + u_int32_t waiting_time; + + enum atr_state atr_state; + u_int8_t atr_idx; + u_int8_t atr_hist_len; + u_int8_t atr_last_td; + u_int8_t atr[64]; + + enum pts_state pts_state; + u_int8_t pts_req[6]; + u_int8_t pts_resp[6]; + + struct simtrace_hdr sh; + + int rctx_must_be_sent; + struct req_ctx *rctx; +}; + +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, +}; + +/* 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; + 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 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) { + 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; +} + +#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; +} + +/* 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: + 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; + + 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) +{ + /* 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) + set_atr_state(ih, ATR_S_WAIT_TCK); + break; + case ATR_S_WAIT_TCK: + /* FIXME: process and verify the TCK */ + 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); + 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) +{ + u_int8_t is_resp = ih->pts_state & 0x10; + u_int8_t sstate = ih->pts_state & 0x0f; + u_int8_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, u_int8_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->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, 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: + 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); + 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"); + 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); +} + +static __ramfunc void 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)) { + /* FIXME: some error has occurrerd */ + } +} + +/* handler for the RST input pin state change */ +static void reset_pin_irq(u_int32_t pio) +{ + if (!AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, pio)) { + DEBUGPCR("nRST"); + set_state(&isoh, ISO7816_S_RESET); + } else { + DEBUGPCR("RST"); + set_state(&isoh, ISO7816_S_WAIT_ATR); + } +} + +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 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; + 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"); + + 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); + + 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; + + /* 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); +} 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_factory.c b/firmware/src/simtrace/main_factory.c new file mode 100644 index 0000000..3585fa6 --- /dev/null +++ b/firmware/src/simtrace/main_factory.c @@ -0,0 +1,80 @@ +/* SIMtrace factory programming + * (C) 2011 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 "../simtrace.h" +#include <os/main.h> +#include <os/pio_irq.h> + +#include <simtrace/tc_etu.h> +#include <simtrace/iso7816_uart.h> +#include <simtrace/sim_switch.h> + +void _init_func(void) +{ + /* low-level hardware initialization */ + pio_irq_init(); + spiflash_init(); + + /* high-level protocol */ + //opicc_usbapi_init(); + led_switch(1, 0); + led_switch(2, 1); +} + +static void help(void) +{ + DEBUGPCR("f: read flash ID\r\n"); +} + +int _main_dbgu(char key) +{ + static int i = 0; + DEBUGPCRF("main_dbgu"); + + switch (key) { + case 'f': + spiflash_id(); + 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 requests from USB EP1 (OUT) */ + usb_in_process(); + + udp_unthrottle(); +} diff --git a/firmware/src/simtrace/main_simtrace.c b/firmware/src/simtrace/main_simtrace.c new file mode 100644 index 0000000..f919690 --- /dev/null +++ b/firmware/src/simtrace/main_simtrace.c @@ -0,0 +1,165 @@ +/* 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 "../simtrace.h" +#include <os/main.h> +#include <os/pio_irq.h> + +#include <simtrace/tc_etu.h> +#include <simtrace/iso7816_uart.h> +#include <simtrace/sim_switch.h> + +enum simtrace_md { + SIMTRACE_MD_OFF, + SIMTRACE_MD_SNIFFER, + SIMTRACE_MD_MITM, +}; + +#define UART1_PINS (SIMTRACE_PIO_nRST_PH | \ + SIMTRACE_PIO_CLK_PH | \ + SIMTRACE_PIO_CLK_PH_T | \ + SIMTRACE_PIO_IO_PH_RX | \ + SIMTRACE_PIO_IO_PH_TX) + +#define UART0_PINS (SIMTRACE_PIO_nRST | \ + SIMTRACE_PIO_CLK | \ + SIMTRACE_PIO_CLK_T | \ + SIMTRACE_PIO_IO | \ + SIMTRACE_PIO_IO_T) + +static void simtrace_set_mode(enum simtrace_md mode) +{ + switch (mode) { + case SIMTRACE_MD_SNIFFER: + DEBUGPCR("MODE: SNIFFER\n"); + + /* switch UART1 pins to input, no pull-up */ + AT91F_PIO_CfgInput(AT91C_BASE_PIOA, UART1_PINS); + AT91F_PIO_CfgPullupDis(AT91C_BASE_PIOA, UART1_PINS); + + /* switch VCC_SIM pin into output mode, as in the first + * generation prototype we use it directly to supply Vcc + * to the SIM */ + AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, SIMTRACE_PIO_VCC_SIM); + AT91F_PIO_SetOutput(AT91C_BASE_PIOA, SIMTRACE_PIO_VCC_SIM); + + /* switch UART0 pins to 'ISO7816 card mode' */ + AT91F_PIO_CfgInput(AT91C_BASE_PIOA, UART0_PINS); + AT91F_PIO_CfgPullupDis(AT91C_BASE_PIOA, UART0_PINS); + AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, SIMTRACE_PIO_IO, SIMTRACE_PIO_CLK); + sim_switch_mode(1, 1); + break; + case SIMTRACE_MD_MITM: + DEBUGPCR("MODE: MITM\n"); + /* switch UART1 pins to 'ISO7816 card mode' */ + /* switch UART0 pins to 'ISO7816 reader mode' */ + sim_switch_mode(0, 0); + break; + } +} + +void _init_func(void) +{ + /* low-level hardware initialization */ + pio_irq_init(); + iso_uart_init(); + tc_etu_init(); + sim_switch_init(); + + usbtest_init(); + + /* high-level protocol */ + //opicc_usbapi_init(); + led_switch(1, 0); + led_switch(2, 1); + + iso_uart_rx_mode(); + simtrace_set_mode(SIMTRACE_MD_SNIFFER); +} + + +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" + "r: set Rx mode for UART\r\n" + "s: disconnect SIM bus switch\r\n" + "S: connect SIM bus switch\r\n"); +} + +int _main_dbgu(char key) +{ + static int i = 0; + DEBUGPCRF("main_dbgu"); + + switch (key) { + case 's': + simtrace_set_mode(SIMTRACE_MD_MITM); + break; + case 'S': + simtrace_set_mode(SIMTRACE_MD_SNIFFER); + break; + 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 requests from USB EP1 (OUT) */ + usb_in_process(); + + udp_unthrottle(); +} diff --git a/firmware/src/simtrace/sim_switch.c b/firmware/src/simtrace/sim_switch.c new file mode 100644 index 0000000..f83290f --- /dev/null +++ b/firmware/src/simtrace/sim_switch.c @@ -0,0 +1,95 @@ +/* + * (C) 2011 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 <string.h> +#include <sys/types.h> +#include <AT91SAM7.h> +#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> + +#include "../simtrace.h" +#include "../openpcd.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +void sim_switch_mode(int connect_io, int connect_misc) +{ + if (connect_io) + AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, SIMTRACE_PIO_IO_SW); + else + AT91F_PIO_SetOutput(AT91C_BASE_PIOA, SIMTRACE_PIO_IO_SW); + + if (connect_misc) + AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, SIMTRACE_PIO_SC_SW); + else + AT91F_PIO_SetOutput(AT91C_BASE_PIOA, SIMTRACE_PIO_SC_SW); +} + +static void sw_sim_irq(u_int32_t pio) +{ + + if (!AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, pio)) + DEBUGPCR("SIM card inserted"); + else + DEBUGPCR("SIM card removed"); +} + +static void vcc_phone_irq(u_int32_t pio) +{ + if (!AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, pio)) + DEBUGPCR("VCC_PHONE off"); + else + DEBUGPCR("VCC_PHONE on"); +} + +void sim_switch_init(void) +{ + DEBUGPCR("ISO_SW Initializing"); + + /* make sure we get clock from the power management controller */ + AT91F_US0_CfgPMC(); + + /* configure both signals as output */ + AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, SIMTRACE_PIO_SC_SW | + SIMTRACE_PIO_IO_SW); + + /* configure sim card detect */ + AT91F_PIO_CfgInput(AT91C_BASE_PIOA, SIMTRACE_PIO_SW_SIM); + AT91F_PIO_CfgInputFilter(AT91C_BASE_PIOA, SIMTRACE_PIO_SW_SIM); + pio_irq_register(SIMTRACE_PIO_SW_SIM, &sw_sim_irq); + pio_irq_enable(SIMTRACE_PIO_SW_SIM); + /* configure VCC_PHONE detection */ + AT91F_PIO_CfgInput(AT91C_BASE_PIOA, SIMTRACE_PIO_VCC_PHONE); + AT91F_PIO_CfgPullupDis(AT91C_BASE_PIOA, SIMTRACE_PIO_VCC_PHONE); + AT91F_PIO_CfgInputFilter(AT91C_BASE_PIOA, SIMTRACE_PIO_VCC_PHONE); + pio_irq_register(SIMTRACE_PIO_VCC_PHONE, &vcc_phone_irq); + pio_irq_enable(SIMTRACE_PIO_VCC_PHONE); + +#if 0 + AT91F_ADC_CfgPMC(); + AT91F_ADC_EnableChannel(AT91C_BASE_ADC, AT91C_ADC_CH7); +#endif +} diff --git a/firmware/src/simtrace/sim_switch.h b/firmware/src/simtrace/sim_switch.h new file mode 100644 index 0000000..01a6a66 --- /dev/null +++ b/firmware/src/simtrace/sim_switch.h @@ -0,0 +1,7 @@ +#ifndef SIMTRACE_ISO_SW_H +#define SIMTRACE_ISO_SW_H + +void sim_switch_mode(int connect_io, int connect_misc); +void sim_switch_init(void); + +#endif diff --git a/firmware/src/simtrace/spi_flash.c b/firmware/src/simtrace/spi_flash.c new file mode 100644 index 0000000..98bc369 --- /dev/null +++ b/firmware/src/simtrace/spi_flash.c @@ -0,0 +1,144 @@ +/* Driver for a SST25VF040B spi flash attached to AT91SAM7 SPI + * (C) 2011 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 <string.h> +#include <sys/types.h> +#include <AT91SAM7.h> +#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> + +#include "../simtrace.h" +#include "../openpcd.h" + +#define DEBUGPSPI DEBUGP +//#define DEBUGPSPI(x, y ...) do { } while (0) + +static const AT91PS_SPI pSPI = AT91C_BASE_SPI; + +void spiflash_write_protect(int on) +{ + if (on) + AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, PIO_SPIF_nWP); + else + AT91F_PIO_SetOutput(AT91C_BASE_PIOA, PIO_SPIF_nWP); +} + +#define SPI_PERIPHA (PIO_SPIF_SCK|PIO_SPIF_MOSI|PIO_SPIF_MISO|PIO_SPIF_nCS) + +static __ramfunc void spi_irq(void) +{ + u_int32_t status = pSPI->SPI_SR; + + AT91F_AIC_ClearIt(AT91C_BASE_AIC, AT91C_ID_SPI); +} + +void spiflash_init(void) +{ + DEBUGP("spiflash_init\r\n"); + + /* activate and enable the write protection */ + AT91F_PIO_CfgPullupDis(AT91C_BASE_PIOA, PIO_SPIF_nWP); + AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, PIO_SPIF_nWP); + spiflash_write_protect(1); + + /* Configure PIOs for SCK, MOSI, MISO and nCS */ + AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, SPI_PERIPHA, 0); + + AT91F_SPI_CfgPMC(); + /* Spansion flash in v1.0p only supprts Mode 3 or Mode 0 */ + /* Mode 3: CPOL=1 nCPHA=0 CSAAT=0 BITS=0(8) MCK/2 */ + AT91F_SPI_CfgCs(AT91C_BASE_SPI, 0, AT91C_SPI_CPOL | + AT91C_SPI_BITS_8 | + (64 << 8)); + + /* SPI master mode, fixed CS, CS = 0 */ + AT91F_SPI_CfgMode(AT91C_BASE_SPI, AT91C_SPI_MSTR | + AT91C_SPI_PS_FIXED | + (0 << 16)); + + /* configure interrupt controller for SPI IRQ */ + AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_SPI, + OPENPCD_IRQ_PRIO_SPI, + AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, &spi_irq); + //AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_SPI); + + /* Enable the SPI Controller */ + AT91F_SPI_Enable(AT91C_BASE_SPI); + AT91F_SPI_EnableIt(AT91C_BASE_SPI, AT91C_SPI_MODF | + AT91C_SPI_OVRES | + AT91C_SPI_ENDRX | + AT91C_SPI_ENDTX); +} + +static int spi_transceive(const u_int8_t *tx_data, u_int16_t tx_len, + u_int8_t *rx_data, u_int16_t *rx_len) +{ + u_int16_t tx_cur = 0; + u_int16_t rx_len_max = 0; + u_int16_t rx_cnt = 0; + + DEBUGPSPI("spi_transceive: enter(tx_len=%u) ", tx_len); + + if (rx_len) { + rx_len_max = *rx_len; + *rx_len = 0; + } + + //AT91F_SPI_Enable(pSPI); + while (1) { + u_int32_t sr = pSPI->SPI_SR; + u_int8_t tmp; + if (sr & AT91C_SPI_RDRF) { + tmp = pSPI->SPI_RDR; + rx_cnt++; + if (rx_len && *rx_len < rx_len_max) + rx_data[(*rx_len)++] = tmp; + } + if (sr & AT91C_SPI_TDRE) { + if (tx_len > tx_cur) + pSPI->SPI_TDR = tx_data[tx_cur++]; + } + if (tx_cur >= tx_len && rx_cnt >= tx_len) + break; + } + //AT91F_SPI_Disable(pSPI); + if (rx_data) + DEBUGPSPI(" leave(%02x %02x)\r\n", rx_data[0], rx_data[1]); + else + DEBUGPSPI("leave()\r\n"); + + return 0; +} + +void spiflash_id(void) +{ + const u_int8_t tx_data[] = { 0x9f, 0, 0, 0 }; + u_int8_t rx_data[] = { 0,0,0,0 }; + u_int16_t rx_len = sizeof(rx_data); + + spi_transceive(tx_data, sizeof(tx_data), rx_data, &rx_len); + DEBUGP("SPI ID: %02x %02x %02x\n", rx_data[1], rx_data[2], rx_data[3]); +} diff --git a/firmware/src/simtrace/tc_etu.c b/firmware/src/simtrace/tc_etu.c new file mode 100644 index 0000000..fc52033 --- /dev/null +++ b/firmware/src/simtrace/tc_etu.c @@ -0,0 +1,128 @@ +/* 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 */ + /* Enabling the line below will cause race conditions. We + * thus re-trigger at all zero-bits in the byte and thus wait + * up to 12 etu longer than required */ + //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(); + //DEBUGPCR("wtime=%u, actually waiting %u", wtime, wait_events * 12); +} + +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); diff --git a/firmware/src/simtrace/usb_strings_app.txt b/firmware/src/simtrace/usb_strings_app.txt new file mode 100644 index 0000000..02cf326 --- /dev/null +++ b/firmware/src/simtrace/usb_strings_app.txt @@ -0,0 +1,7 @@ +SimTrace DFU Interface - Application Partition +SimTrace DFU Interface - Bootloader Partition +SimTrace DFU Interface - RAM +sysmocom - systems for mobile communications GmbH +SimTrace SIM Sniffer - Runtime Mode +SimTrace Runtime Configuration +SimTrace Runtime Interface diff --git a/firmware/src/simtrace/usb_strings_dfu.txt b/firmware/src/simtrace/usb_strings_dfu.txt new file mode 100644 index 0000000..2f65684 --- /dev/null +++ b/firmware/src/simtrace/usb_strings_dfu.txt @@ -0,0 +1,6 @@ +sysmocom - systems for mobile communications GmbH +SimTrace SIM Sniffer - DFU Mode +SimTrace DFU Configuration +SimTrace DFU Interface - Application Partition +SimTrace DFU Interface - Bootloader Partition +SimTrace DFU Interface - RAM diff --git a/firmware/src/start/Cstartup.S b/firmware/src/start/Cstartup.S index a28b400..e262b8f 100644 --- a/firmware/src/start/Cstartup.S +++ b/firmware/src/start/Cstartup.S @@ -98,6 +98,9 @@ /* Olimex SAM7-Pxxx boards have a button B1 on PA19 that is low-active */ .equ PIO_BOOTLDR, (1 << 19) #define CONFIG_DFU_SWITCH_INV +#elif defined(SIMTRACE) + .equ PIO_BOOTLDR, (1 << 31) +#define CONFIG_DFU_SWITCH_INV #else #error please define PIO_BOOTLDR for your board #endif diff --git a/firmware/src/start/Cstartup_app.S b/firmware/src/start/Cstartup_app.S index 448cc93..197be66 100644 --- a/firmware/src/start/Cstartup_app.S +++ b/firmware/src/start/Cstartup_app.S @@ -79,6 +79,7 @@ .global _startup .func _startup _startup: +#ifndef RUN_FROM_RAM /* Relocate .data section (copy from Flash to RAM) */ ldr r1, =_etext ldr r2, =_data @@ -87,6 +88,7 @@ loop_r: cmp r2, r3 ldrlo r0, [r1], #4 strlo r0, [r2], #4 blo loop_r +#endif /* Clear .bss section (Zero init) */ mov r0, #0 |