summaryrefslogtreecommitdiff
path: root/firmware/src
diff options
context:
space:
mode:
authorlaforge <laforge@6dc7ffe9-61d6-0310-9af1-9938baff3ed1>2006-09-12 17:35:30 +0000
committerlaforge <laforge@6dc7ffe9-61d6-0310-9af1-9938baff3ed1>2006-09-12 17:35:30 +0000
commitd256545b2fd62d78910efcc6273c3b70abd3aa13 (patch)
treea05e17ec752cfbcc0b79fdbfba81fb949545a112 /firmware/src
parent04e0441914eeb25e042189679b55c9577fc96d2a (diff)
move to new directory
git-svn-id: https://svn.openpcd.org:2342/trunk@191 6dc7ffe9-61d6-0310-9af1-9938baff3ed1
Diffstat (limited to 'firmware/src')
-rw-r--r--firmware/src/main_usb.c48
-rw-r--r--firmware/src/openpcd.h165
-rw-r--r--firmware/src/os/dbgu.c303
-rw-r--r--firmware/src/os/dbgu.h44
-rw-r--r--firmware/src/os/dfu.c681
-rw-r--r--firmware/src/os/dfu.h80
-rw-r--r--firmware/src/os/fifo.c108
-rw-r--r--firmware/src/os/fifo.h28
-rw-r--r--firmware/src/os/flash.c45
-rw-r--r--firmware/src/os/led.c85
-rw-r--r--firmware/src/os/led.h9
-rw-r--r--firmware/src/os/main.c47
-rw-r--r--firmware/src/os/main.h8
-rw-r--r--firmware/src/os/pcd_enumerate.c605
-rw-r--r--firmware/src/os/pcd_enumerate.h56
-rw-r--r--firmware/src/os/pio_irq.c122
-rw-r--r--firmware/src/os/pio_irq.h13
-rw-r--r--firmware/src/os/pit.c38
-rw-r--r--firmware/src/os/pit.h7
-rw-r--r--firmware/src/os/power.h8
-rw-r--r--firmware/src/os/pwm.c165
-rw-r--r--firmware/src/os/pwm.h11
-rw-r--r--firmware/src/os/req_ctx.c50
-rw-r--r--firmware/src/os/req_ctx.h49
-rw-r--r--firmware/src/os/syscalls.c169
-rw-r--r--firmware/src/os/tc_cdiv.c92
-rw-r--r--firmware/src/os/tc_cdiv.h26
-rw-r--r--firmware/src/os/trigger.c17
-rw-r--r--firmware/src/os/trigger.h7
-rw-r--r--firmware/src/os/usb_benchmark.c57
-rw-r--r--firmware/src/os/usb_handler.c88
-rw-r--r--firmware/src/os/usb_handler.h17
-rw-r--r--firmware/src/os/wdt.c23
-rw-r--r--firmware/src/pcd.h35
-rw-r--r--firmware/src/pcd/main_analog.c85
-rw-r--r--firmware/src/pcd/main_dumbreader.c63
-rw-r--r--firmware/src/pcd/main_pwm.c257
-rw-r--r--firmware/src/pcd/main_reqa.c265
-rw-r--r--firmware/src/pcd/rc632.c624
-rw-r--r--firmware/src/pcd/rc632.h31
-rw-r--r--firmware/src/pcd/rc632_highlevel.c1473
-rw-r--r--firmware/src/pcd/rfid_layer2_iso14443a.c319
-rw-r--r--firmware/src/picc/adc.c145
-rw-r--r--firmware/src/picc/decoder.c65
-rw-r--r--firmware/src/picc/decoder.h32
-rw-r--r--firmware/src/picc/decoder_miller.c112
-rw-r--r--firmware/src/picc/decoder_nrzl.c91
-rw-r--r--firmware/src/picc/iso14443a_manchester.c106
-rw-r--r--firmware/src/picc/load_modulation.c29
-rw-r--r--firmware/src/picc/load_modulation.h7
-rw-r--r--firmware/src/picc/main_openpicc.c215
-rw-r--r--firmware/src/picc/piccsim.h23
-rw-r--r--firmware/src/picc/pll.c40
-rw-r--r--firmware/src/picc/pll.h8
-rw-r--r--firmware/src/picc/poti.c58
-rw-r--r--firmware/src/picc/poti.h7
-rw-r--r--firmware/src/picc/ssc_picc.c384
-rw-r--r--firmware/src/picc/ssc_picc.h15
-rw-r--r--firmware/src/picc/tc_fdt.c50
-rw-r--r--firmware/src/picc/tc_fdt.h9
-rw-r--r--firmware/src/start/Cstartup.S386
-rw-r--r--firmware/src/start/Cstartup_SAM7.c89
62 files changed, 8294 insertions, 0 deletions
diff --git a/firmware/src/main_usb.c b/firmware/src/main_usb.c
new file mode 100644
index 0000000..abb6948
--- /dev/null
+++ b/firmware/src/main_usb.c
@@ -0,0 +1,48 @@
+/* main_usb - OpenPCD test firmware for benchmarking USB performance
+ * (C) 2006 by Harald Welte <laforge@gnumonks.org>
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <lib_AT91SAM7.h>
+#include "openpcd.h"
+#include "rc632.h"
+#include "dbgu.h"
+#include "led.h"
+#include "pwm.h"
+#include "tc.h"
+#include "ssc.h"
+#include "pcd_enumerate.h"
+#include "usb_handler.h"
+
+static void help(void)
+{
+}
+
+int _main_dbgu(char key)
+{
+ switch (key) {
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void _init_func(void)
+{
+ usbtest_init();
+}
+
+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();
+
+ /* try unthrottling sources since we now are [more] likely to
+ * have empty request contexts */
+ udp_unthrottle();
+}
diff --git a/firmware/src/openpcd.h b/firmware/src/openpcd.h
new file mode 100644
index 0000000..3099373
--- /dev/null
+++ b/firmware/src/openpcd.h
@@ -0,0 +1,165 @@
+#ifndef _OPENPCD_H
+#define _OPENPCD_H
+
+#include <openpcd.h>
+
+#define Hz
+#define kHz *1000 Hz
+#define MHz *1000 kHz
+#define MCLK (48 MHz)
+
+
+#ifdef OLIMEX
+#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
+#else
+#if defined(PCD)
+#define OPENPCD_PIO_UDP_CNX AT91C_PIO_PA15
+#define OPENPCD_PIO_UDP_PUP AT91C_PIO_PA22
+#define OPENPCD_PIO_LED1 AT91C_PIO_PA25
+#define OPENPCD_PIO_LED2 AT91C_PIO_PA26
+#define PIO_BOOTLDR AT91C_PIO_PA27
+#elif defined(PICC)
+#define OPENPCD_PIO_UDP_CNX NO_UDP_CNX
+#define OPENPCD_PIO_UDP_PUP AT91C_PIO_PA22
+#define OPENPCD_PIO_LED1 AT91C_PIO_PA25
+#define OPENPCD_PIO_LED2 AT91C_PIO_PA12
+#define PIO_BOOTLDR AT91C_PIO_PA6
+#else
+#error "unknown PCB"
+#endif
+#endif
+
+#define OPENPCD_IRQ_RC632 AT91C_ID_IRQ1
+
+/* PIO A mapping for OpenPCD v0.2
+ *
+ * PA0 TIOA0 B O CARRIER_DIV_HELP
+ * PA1 TIOB1 B O CARRIER_DIV
+ * PA2 PA2 P I N/C
+ * PA3 TWD A I/O I2C
+ *
+ * PA4 TWCK A O I2C
+ * PA5 PA5 P O RFID_RESET
+ * PA6 PA6 P I N/C
+ * PA7 PA7 P I N/C
+ *
+ * PA8 PA8 P I N/C
+ * PA9 DRXD A I Debug
+ * PA10 DTXD A O Debug
+ * PA11 NPCS0 A O SPI Slave Select
+ *
+ * PA12 MISO A I SPi Master In
+ * PA13 MOSI A O SPI Maste Out
+ * PA14 SPCK A O SPI Clock
+ * PA15 P I N/C
+ *
+ * PA16 PA16 P O UDP_PUP (disabled)
+ * PA17 TD A O MFIN *
+ * PA18 RD A I MFOUT
+ * PA19 RK A I CARRIER_DIV
+ *
+ * PA20 PA20 P I AB_DETECT
+ * PA21 PA21 P I N/C
+ * PA22 PA22 P I UDP_PUP
+ * PA23 PWM0 B O MFIN *
+ *
+ * PA24 P I N/C
+ * PA25 PA25 P O LED1
+ * PA26 PA26 P O LED2
+ * PA27 PA27 P I BOOTLDR_SW
+ *
+ * PA28 TCLK1 B I PIO_CARRIER
+ * PA29 TCLK2 B I CARRIER_DIV_HELP
+ * PA30 IRQ1 A I RC632_IRQ
+ * PA31 PA31 P O TRIGGER
+ *
+ * => PIO_PSR = 0x8f7181e4 (= PIO_PER)
+ * => PIO_OSR = 0x86010020 (= PIO_OER)
+ * => PIO_ASR = 0x400e7e18
+ * => PIO_BSR = 0x30800003
+ *
+ * * MFIN connected to both SSC and PWM output !!!
+ */
+
+#define OPENPCD_PIO_CDIV_HELP_OUT AT91C_PA0_TIOA0
+#define OPENPCD_PIO_CDIV_HELP_IN AT91C_PA29_TCLK2
+#define OPENPCD_PIO_MFIN_PWM AT91C_PA23_PWM0
+#define OPENPCD_PIO_CARRIER_DIV_OUT AT91C_PA1_TIOB0
+#define OPENPCD_PIO_MFIN_SSC_TX AT91C_PA17_TD
+#define OPENPCD_PIO_MFOUT_SSC_RX AT91C_PA18_RD
+#define OPENPCD_PIO_SSP_CKIN AT91C_PA19_RK
+#define OPENPCD_PIO_RC632_RESET AT91C_PIO_PA5
+#define OPENPCD_PIO_TRIGGER AT91C_PIO_PA31
+#define OPENPCD_PIO_CARRIER_IN AT91C_PA28_TCLK1
+
+
+/* PIO Definition PICCSIM v0.3 (modified)
+ *
+ * PA0 TIOA0 B O CARRIER_DIV_HELP
+ * PA1 TIOB1 B O SSC_CLOCK
+ * PA2 PA2 P O LOAD1
+ * PA3 PA3 P O LOAD2
+ *
+ * PA4 PA4 P I PLL_LOCK
+ * PA5 PA5 P O nSLAVE_RESET
+ * PA6 PA6 P I BOOTLOADER_SW
+ * PA7 PA7 P I N/C
+ *
+ * PA8 PA8 P O SPI_SS2 (Comparator)
+ * PA9 DRXD A I Debug
+ * PA10 DTXD A O Debug
+ * PA11 NPCS0 A O SPI_SS1 (Gain)
+ *
+ * PA12 PA12 P O LED2 red
+ * PA13 MOSI A O SPI Master Out
+ * PA14 SPCK A O SPI Clock
+ * PA15 TF A I SSC Tx Frame
+ *
+ * PA16 PA16 P O UDP_PUP (old)
+ * PA17 TD A O SSC Tx Data (MOD) *
+ * PA18 RD A I SSC Rx Data (SSC_DATA)
+ * PA19 RK A I SSC Rx Clock (SSC_CLOCK)
+ *
+ * PA20 PA20 P I AB_DETECT
+ * PA21 PA21 P I N/C
+ * PA22 PA22 P I N/C
+ * PA23 PWM0 B O PWM Output (MOD) *
+ *
+ * PA24 PA24 P O PLL_INHIBIT
+ * PA25 PA25 P O LED1 green
+ * PA26 TIOA2 B O TC FDT output (-> SPI Tx Frame)
+ * PA27 TIOB2 B I DATA
+ *
+ * PA28 TCLK1 B I CARRIER
+ * PA29 TCLK2 B I CARRIER_DIV_HELP
+ * PA30 PA30 P I N/C
+ * PA31 PA31 P I N/C
+ *
+ */
+
+#define OPENPICC_PIO_LOAD1 AT91C_PIO_PA2
+#define OPENPICC_PIO_LOAD2 AT91C_PIO_PA3
+#define OPENPICC_PIO_nSLAVE_RESET AT91C_PIO_PA5
+#define OPENPICC_PIO_BOOTLDR AT91C_PIO_PA6
+#define OPENPICC_PIO_SS2_DT_THRESH AT91C_PIO_PA8
+#define OPENPICC_PIO_SS1_GAIN AT91C_PIO_PA11
+#define OPENPICC_PIO_PLL_LOCK AT91C_PIO_PA4
+
+#define OPENPICC_PIO_AB_DETECT AT91C_PIO_PA20
+#define OPENPICC_PIO_PLL_INHIBIT AT91C_PIO_PA24
+
+#define OPENPICC_ADC_FIELD_STRENGTH AT91C_ADC_CH4
+#define OPENPICC_ADC_PLL_DEM AT91C_ADC_CH5
+#define OPENPICC_ADC_AN1 AT91C_ADC_CH6
+#define OPENPICC_ADC_AN2 AT91C_ADC_CH7
+
+#define OPENPCD_IRQ_PRIO_SPI AT91C_AIC_PRIOR_HIGHEST
+#define OPENPCD_IRQ_PRIO_SSC (AT91C_AIC_PRIOR_HIGHEST-1)
+#define OPENPCD_IRQ_PRIO_UDP (AT91C_AIC_PRIOR_LOWEST+2)
+#define OPENPCD_IRQ_PRIO_PIT (AT91C_AIC_PRIOR_LOWEST+1)
+#define OPENPCD_IRQ_PRIO_RC632 AT91C_AIC_PRIOR_LOWEST
+
+#endif /* _OPENPCD_H */
diff --git a/firmware/src/os/dbgu.c b/firmware/src/os/dbgu.c
new file mode 100644
index 0000000..dc1a040
--- /dev/null
+++ b/firmware/src/os/dbgu.c
@@ -0,0 +1,303 @@
+/*----------------------------------------------------------------------------
+ * ATMEL Microcontroller Software Support - ROUSSET -
+ *----------------------------------------------------------------------------
+ * The software is delivered "AS IS" without warranty or condition of any
+ * kind, either express, implied or statutory. This includes without
+ * limitation any warranty or condition with respect to merchantability or
+ * fitness for any particular purpose, or against the infringements of
+ * intellectual property rights of others.
+ *----------------------------------------------------------------------------
+ * File Name : Debug.c
+ * Object : Debug menu
+ * Creation : JPP 14/Sep/2004
+ * 1.1 29/Aug/05 JPP : Update AIC definion
+ *----------------------------------------------------------------------------*/
+
+// Include Standard files
+#include <board.h>
+#include <os/dbgu.h>
+#include "../openpcd.h"
+#include <os/led.h>
+#include <os/main.h>
+#include <asm/system.h>
+
+#define USART_SYS_LEVEL 4
+/*---------------------------- Global Variable ------------------------------*/
+//*--------------------------1--------------------------------------------------
+//* \fn AT91F_DBGU_Printk
+//* \brief This function is used to send a string through the DBGU channel
+//*----------------------------------------------------------------------------
+void AT91F_DBGU_Ready(void)
+{
+ while (!(AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXEMPTY)) ;
+}
+
+//*----------------------------------------------------------------------------
+//* Function Name : Send_reset
+//* Object : Acknoledeg AIC and send reset
+//*----------------------------------------------------------------------------
+static void Send_reset(void)
+{
+ void (*pfct) (void) = (void (*)(void))0x00000000;
+
+ // Acknoledge the interrupt
+ // Mark the End of Interrupt on the AIC
+ AT91C_BASE_AIC->AIC_EOICR = 0;
+ AT91F_DBGU_Ready();
+ // Jump in reset
+ pfct();
+}
+
+//*----------------------------------------------------------------------------
+//* Function Name : DBGU_irq_handler
+//* Object : C handler interrupt function called by the interrupts
+//* assembling routine
+//*----------------------------------------------------------------------------
+static void DBGU_irq_handler(void)
+{
+ static char value;
+
+ AT91F_DBGU_Get(&value);
+ switch (value) {
+ case '0': //* info
+ AT91F_DBGU_Frame("Clear Pull up\n\r");
+ // Set
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPCD_PIO_UDP_PUP);
+ break;
+ case '1': //* info
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPCD_PIO_UDP_PUP);
+ AT91F_DBGU_Printk("Set Pull up\n\r");
+ // Reset Application
+ Send_reset();
+ break;
+ case '2':
+ AT91F_DBGU_Printk("Toggling LED 1\n\r");
+ led_toggle(1);
+ break;
+ case '3':
+ AT91F_DBGU_Printk("Toggling LED 2\n\r");
+ led_toggle(2);
+ break;
+ default:
+ if (_main_dbgu(value) < 0)
+ AT91F_DBGU_Printk("\n\r");
+ break;
+ } // end switch
+}
+
+void dbgu_rb_init(void);
+//*----------------------------------------------------------------------------
+//* \fn AT91F_DBGU_Init
+//* \brief This function is used to send a string through the DBGU channel (Very low level debugging)
+//*----------------------------------------------------------------------------
+void AT91F_DBGU_Init(void)
+{
+ dbgu_rb_init();
+
+ //* Open PIO for DBGU
+ AT91F_DBGU_CfgPIO();
+ //* Enable Transmitter & receivier
+ ((AT91PS_USART) AT91C_BASE_DBGU)->US_CR =
+ AT91C_US_RSTTX | AT91C_US_RSTRX;
+
+ //* Configure DBGU
+ AT91F_US_Configure((AT91PS_USART) AT91C_BASE_DBGU, // DBGU base address
+ MCK, AT91C_US_ASYNC_MODE, // Mode Register to be programmed
+ AT91C_DBGU_BAUD, // Baudrate to be programmed
+ 0); // Timeguard to be programmed
+
+ //* Enable Transmitter & receivier
+ ((AT91PS_USART) AT91C_BASE_DBGU)->US_CR = AT91C_US_RXEN | AT91C_US_TXEN;
+
+ //* Enable USART IT error and AT91C_US_ENDRX
+ AT91F_US_EnableIt((AT91PS_USART) AT91C_BASE_DBGU, AT91C_US_RXRDY);
+
+ //* open interrupt
+ AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_SYS, USART_SYS_LEVEL,
+ AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL,
+ DBGU_irq_handler);
+ AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_SYS);
+
+ AT91F_DBGU_Printk
+ ("\n\r-I- OpenPCD test mode\n\r 0) Set Pull-up 1) Clear Pull-up "
+ "2) Toggle LED1 3) Toggle LED2 4) Test RC632\n\r"
+ "5) Read RxWait 6) Write RxWait 7) Dump RC632 Regs\n\r");
+}
+
+//*----------------------------------------------------------------------------
+//* \fn AT91F_DBGU_Printk
+//* \brief This function is used to send a string through the DBGU channel (Very low level debugging)
+//*----------------------------------------------------------------------------
+void AT91F_DBGU_Printk(char *buffer)
+{
+ while (*buffer != '\0') {
+ while (!AT91F_US_TxReady((AT91PS_USART) AT91C_BASE_DBGU)) ;
+ AT91F_US_PutChar((AT91PS_USART) AT91C_BASE_DBGU, *buffer++);
+ }
+}
+
+//*----------------------------------------------------------------------------
+//* \fn AT91F_DBGU_Frame
+//* \brief This function is used to send a string through the DBGU channel
+//*----------------------------------------------------------------------------
+void AT91F_DBGU_Frame(char *buffer)
+{
+ unsigned char len;
+
+ for (len = 0; buffer[len] != '\0'; len++) {
+ }
+
+ AT91F_US_SendFrame((AT91PS_USART) AT91C_BASE_DBGU,
+ (unsigned char *)buffer, len, 0, 0);
+
+}
+
+//*----------------------------------------------------------------------------
+//* \fn AT91F_US_Get
+//* \brief Get a Char to USART
+//*----------------------------------------------------------------------------
+int AT91F_DBGU_Get(char *val)
+{
+ if ((AT91F_US_RxReady((AT91PS_USART) AT91C_BASE_DBGU)) == 0)
+ return (0);
+ else {
+ *val = AT91F_US_GetChar((AT91PS_USART) AT91C_BASE_DBGU);
+ return (-1);
+ }
+}
+
+// mthomas: function not used in this application. avoid
+// linking huge newlib code for sscanf.
+
+#ifdef DEBUG
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+const char *
+hexdump(const void *data, unsigned int len)
+{
+ static char string[256];
+ unsigned char *d = (unsigned char *) data;
+ unsigned int i, left;
+
+ string[0] = '\0';
+ left = sizeof(string);
+ for (i = 0; len--; i += 3) {
+ if (i >= sizeof(string) -4)
+ break;
+ snprintf(string+i, 4, " %02x", *d++);
+ }
+ return string;
+}
+
+struct dbgu {
+ char buf[4096];
+ char *next_inbyte;
+ char *next_outbyte;
+};
+static struct dbgu dbgu;
+
+void dbgu_rb_init(void)
+{
+ memset(dbgu.buf, 0, sizeof(dbgu.buf));
+ dbgu.next_inbyte = &dbgu.buf[0];
+ dbgu.next_outbyte = &dbgu.buf[0];
+}
+
+/* pull one char out of debug ring buffer */
+static int dbgu_rb_pull(char *ret)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ if (dbgu.next_outbyte == dbgu.next_inbyte) {
+ local_irq_restore(flags);
+ return -1;
+ }
+
+ *ret = *dbgu.next_outbyte;
+
+ dbgu.next_outbyte++;
+ if (dbgu.next_outbyte == &dbgu.buf[0]+sizeof(dbgu.buf)) {
+ //AT91F_DBGU_Printk("WRAP DURING PULL\r\n");
+ dbgu.next_outbyte = &dbgu.buf[0];
+ } else if (dbgu.next_outbyte > &dbgu.buf[0]+sizeof(dbgu.buf)) {
+ //AT91F_DBGU_Printk("OUTBYTE > END_OF_BUF!!\r\n");
+ dbgu.next_outbyte -= sizeof(dbgu.buf);
+ }
+
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static void __rb_flush(void)
+{
+ char ch;
+ while (dbgu_rb_pull(&ch) >= 0) {
+ while (!AT91F_US_TxReady((AT91PS_USART) AT91C_BASE_DBGU)) ;
+ AT91F_US_PutChar((AT91PS_USART) AT91C_BASE_DBGU, ch);
+ }
+}
+
+/* flush pending data from debug ring buffer to serial port */
+void dbgu_rb_flush(void)
+{
+ __rb_flush();
+}
+
+static void __dbgu_rb_append(char *data, int len)
+{
+ char *pos = dbgu.next_inbyte;
+
+ dbgu.next_inbyte += len;
+ if (dbgu.next_inbyte >= &dbgu.buf[0]+sizeof(dbgu.buf)) {
+ AT91F_DBGU_Printk("WRAP DURING APPEND\r\n");
+ dbgu.next_inbyte -= sizeof(dbgu.buf);
+ }
+
+ memcpy(pos, data, len);
+}
+
+void dbgu_rb_append(char *data, int len)
+{
+ unsigned long flags;
+ int bytes_left;
+ char *data_cur;
+
+ local_irq_save(flags);
+
+ bytes_left = &dbgu.buf[0]+sizeof(dbgu.buf)-dbgu.next_inbyte;
+ data_cur = data;
+
+ if (len > bytes_left) {
+ AT91F_DBGU_Printk("LEN > BYTES_LEFT\r\n");
+ __rb_flush();
+ __dbgu_rb_append(data_cur, bytes_left);
+ len -= bytes_left;
+ data_cur += bytes_left;
+ }
+ __dbgu_rb_append(data_cur, len);
+
+ local_irq_restore(flags);
+}
+
+static char dbg_buf[256];
+void debugp(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vsnprintf(dbg_buf, sizeof(dbg_buf)-1, format, ap);
+ va_end(ap);
+
+ dbg_buf[sizeof(dbg_buf)-1] = '\0';
+ //AT91F_DBGU_Frame(dbg_buf);
+ //AT91F_DBGU_Printk(dbg_buf);
+ dbgu_rb_append(dbg_buf, strlen(dbg_buf));
+}
+#else
+void dbgu_rb_flush(void) {}
+void dbgu_rb_init(void) {}
+#endif
diff --git a/firmware/src/os/dbgu.h b/firmware/src/os/dbgu.h
new file mode 100644
index 0000000..e29e351
--- /dev/null
+++ b/firmware/src/os/dbgu.h
@@ -0,0 +1,44 @@
+//*----------------------------------------------------------------------------
+//* ATMEL Microcontroller Software Support - ROUSSET -
+//*----------------------------------------------------------------------------
+//* The software is delivered "AS IS" without warranty or condition of any
+//* kind, either express, implied or statutory. This includes without
+//* limitation any warranty or condition with respect to merchantability or
+//* fitness for any particular purpose, or against the infringements of
+//* intellectual property rights of others.
+//*----------------------------------------------------------------------------
+//* File Name : Debug.h
+//* Object : Debug menu
+//* Creation : JPP 02/Sep/2004
+//*----------------------------------------------------------------------------
+
+#ifndef dbgu_h
+#define dbgu_h
+
+#define AT91C_DBGU_BAUD 115200
+
+//#define DEBUGP(x) AT91F_DBGU_Printk(x)
+
+//* ----------------------- External Function Prototype -----------------------
+
+extern const char *hexdump(const void *data, unsigned int len);
+void AT91F_DBGU_Init(void);
+void AT91F_DBGU_Printk( char *buffer);
+void AT91F_DBGU_Frame( char *buffer);
+int AT91F_DBGU_Get( char *val);
+void dbgu_rb_flush(void);
+#ifndef __WinARM__
+void AT91F_DBGU_scanf(char * type,unsigned int * val);
+#endif
+
+#ifdef DEBUG
+extern void debugp(const char *format, ...);
+#define DEBUGP(x, args ...) debugp(x, ## args)
+#else
+#define DEBUGP(x, args ...) do {} while(0)
+#endif
+
+#define DEBUGPCR(x, args ...) DEBUGP(x "\r\n", ## args)
+#define DEBUGPCRF(x, args ...) DEBUGPCR("%s(%d): " x, __FUNCTION__, __LINE__, ## args)
+
+#endif /* dbgu_h */
diff --git a/firmware/src/os/dfu.c b/firmware/src/os/dfu.c
new file mode 100644
index 0000000..f2bc0ca
--- /dev/null
+++ b/firmware/src/os/dfu.c
@@ -0,0 +1,681 @@
+/* USB Device Firmware Update Implementation for OpenPCD
+ * (C) 2006 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
+ *
+ */
+
+#include <errno.h>
+#include <usb_ch9.h>
+#include <usb_dfu.h>
+#include <lib_AT91SAM7.h>
+
+#include <os/dfu.h>
+#include <os/pcd_enumerate.h>
+#include <os/req_ctx.h>
+#include "../openpcd.h"
+
+/* If debug is enabled, we need to access debug functions from flash
+ * and therefore have to omit flashing */
+//#define DEBUG_DFU
+
+#ifdef DEBUG_DFU
+#define DEBUGE DEBUGP
+#define DEBUGI DEBUGP
+#else
+#define DEBUGE(x, args ...) do { } while (0)
+#define DEBUGI(x, args ...) do { } while (0)
+#endif
+
+
+void __dfufunc udp_init(void)
+{
+ /* Set the PLL USB Divider */
+ AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1;
+
+ /* Enables the 48MHz USB clock UDPCK and System Peripheral USB Clock */
+ AT91C_BASE_PMC->PMC_SCER = AT91C_PMC_UDP;
+ AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_UDP);
+
+ /* Enable UDP PullUp (USB_DP_PUP) : enable & Clear of the corresponding PIO
+ * Set in PIO mode and Configure in Output */
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPCD_PIO_UDP_PUP);
+}
+
+/* Send Data through the control endpoint */
+static void __dfufunc udp_ep0_send_data(const char *pData, u_int32_t length)
+{
+ AT91PS_UDP pUdp = AT91C_BASE_UDP;
+ u_int32_t cpt = 0;
+ AT91_REG csr;
+
+ DEBUGE("send_data: %u bytes ", length);
+
+ do {
+ cpt = MIN(length, 8);
+ length -= cpt;
+
+ while (cpt--)
+ pUdp->UDP_FDR[0] = *pData++;
+
+ if (pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP) {
+ pUdp->UDP_CSR[0] &= ~(AT91C_UDP_TXCOMP);
+ while (pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP) ;
+ }
+
+ pUdp->UDP_CSR[0] |= AT91C_UDP_TXPKTRDY;
+ do {
+ csr = pUdp->UDP_CSR[0];
+
+ /* Data IN stage has been stopped by a status OUT */
+ if (csr & AT91C_UDP_RX_DATA_BK0) {
+ pUdp->UDP_CSR[0] &= ~(AT91C_UDP_RX_DATA_BK0);
+ DEBUGE("stopped by status out ");
+ return;
+ }
+ } while (!(csr & AT91C_UDP_TXCOMP));
+
+ } while (length);
+
+ if (pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP) {
+ pUdp->UDP_CSR[0] &= ~(AT91C_UDP_TXCOMP);
+ while (pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP) ;
+ }
+}
+
+/* Send zero length packet through the control endpoint */
+static void __dfufunc udp_ep0_send_zlp(void)
+{
+ AT91PS_UDP pUdp = AT91C_BASE_UDP;
+ pUdp->UDP_CSR[0] |= AT91C_UDP_TXPKTRDY;
+ while (!(pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP)) ;
+ pUdp->UDP_CSR[0] &= ~(AT91C_UDP_TXCOMP);
+ while (pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP) ;
+}
+
+/* Stall the control endpoint */
+static void __dfufunc udp_ep0_send_stall(void)
+{
+ AT91PS_UDP pUdp = AT91C_BASE_UDP;
+ pUdp->UDP_CSR[0] |= AT91C_UDP_FORCESTALL;
+ while (!(pUdp->UDP_CSR[0] & AT91C_UDP_ISOERROR)) ;
+ pUdp->UDP_CSR[0] &= ~(AT91C_UDP_FORCESTALL | AT91C_UDP_ISOERROR);
+ while (pUdp->UDP_CSR[0] & (AT91C_UDP_FORCESTALL | AT91C_UDP_ISOERROR)) ;
+}
+
+
+static u_int8_t status;
+static u_int8_t *ptr;
+static u_int8_t dfu_state;
+
+static int __dfufunc handle_dnload(u_int16_t val, u_int16_t len)
+{
+ volatile u_int32_t *p = (volatile u_int32_t *)ptr;
+
+ DEBUGE("download ");
+
+ if (len > AT91C_IFLASH_PAGE_SIZE) {
+ /* Too big */
+ dfu_state = DFU_STATE_dfuERROR;
+ status = DFU_STATUS_errADDRESS;
+ udp_ep0_send_stall();
+ return -EINVAL;
+ }
+ if (len & 0x3) {
+ dfu_state = DFU_STATE_dfuERROR;
+ status = DFU_STATUS_errADDRESS;
+ udp_ep0_send_stall();
+ return -EINVAL;
+ }
+ if (len == 0) {
+ dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
+ return 0;
+ }
+#if 0
+ for (i = 0; i < len/4; i++)
+ p[i] =
+#endif
+
+ /* FIXME: get data packet from FIFO, (erase+)write flash */
+#ifndef DEBUG_DFU
+
+#endif
+
+ return 0;
+}
+
+static __dfufunc int handle_upload(u_int16_t val, u_int16_t len)
+{
+ DEBUGE("upload ");
+ if (len > AT91C_IFLASH_PAGE_SIZE
+ || ptr > AT91C_IFLASH_SIZE) {
+ /* Too big */
+ dfu_state = DFU_STATE_dfuERROR;
+ status = DFU_STATUS_errADDRESS;
+ udp_ep0_send_stall();
+ return -EINVAL;
+ }
+
+ if (ptr + len > AT91C_IFLASH_SIZE)
+ len = AT91C_IFLASH_SIZE - (u_int32_t) ptr;
+
+ udp_ep0_send_data((char *)ptr, len);
+ ptr+= len;
+
+ return len;
+}
+
+static __dfufunc void handle_getstatus(void)
+{
+ struct dfu_status dstat;
+
+ DEBUGE("getstatus ");
+
+ /* send status response */
+ dstat.bStatus = status;
+ dstat.bState = dfu_state;
+ dstat.iString = 0;
+ udp_ep0_send_data((char *)&dstat, sizeof(dstat));
+}
+
+static void __dfufunc handle_getstate(void)
+{
+ u_int8_t u8 = dfu_state;
+ DEBUGE("getstate ");
+ udp_ep0_send_data((char *)&u8, sizeof(u8));
+}
+
+/* callback function for DFU requests */
+int __dfufunc dfu_ep0_handler(u_int8_t req_type, u_int8_t req,
+ u_int16_t val, u_int16_t len)
+{
+ int rc;
+
+ DEBUGE("old_state = %u ", dfu_state);
+
+ switch (dfu_state) {
+ case DFU_STATE_appIDLE:
+ if (req != USB_REQ_DFU_DETACH)
+ goto send_stall;
+ dfu_state = DFU_STATE_appDETACH;
+ goto send_zlp;
+ break;
+ case DFU_STATE_appDETACH:
+ switch (req) {
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus();
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate();
+ break;
+ default:
+ dfu_state = DFU_STATE_appIDLE;
+ goto send_stall;
+ break;
+ }
+ /* FIXME: implement timer to return to appIDLE */
+ break;
+ case DFU_STATE_dfuIDLE:
+ switch (req) {
+ case USB_REQ_DFU_DNLOAD:
+ if (len == 0) {
+ dfu_state = DFU_STATE_dfuERROR;
+ goto send_stall;
+ }
+ handle_dnload(val, len);
+ break;
+ case USB_REQ_DFU_UPLOAD:
+ ptr = 0;
+ dfu_state = DFU_STATE_dfuUPLOAD_IDLE;
+ handle_upload(val, len);
+ break;
+ case USB_REQ_DFU_ABORT:
+ /* no zlp? */
+ goto send_zlp;
+ break;
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus();
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate();
+ break;
+ default:
+ dfu_state = DFU_STATE_dfuERROR;
+ goto send_stall;
+ break;
+ }
+ break;
+ case DFU_STATE_dfuDNLOAD_SYNC:
+ switch (req) {
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus();
+ /* FIXME: state transition depending on block completeness */
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate();
+ break;
+ default:
+ dfu_state = DFU_STATE_dfuERROR;
+ goto send_stall;
+ }
+ break;
+ case DFU_STATE_dfuDNBUSY:
+ dfu_state = DFU_STATE_dfuERROR;
+ goto send_stall;
+ break;
+ case DFU_STATE_dfuDNLOAD_IDLE:
+ switch (req) {
+ case USB_REQ_DFU_DNLOAD:
+ if (handle_dnload(val, len))
+ /* FIXME: state transition */
+ break;
+ case USB_REQ_DFU_ABORT:
+ dfu_state = DFU_STATE_dfuIDLE;
+ goto send_zlp;
+ break;
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus();
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate();
+ break;
+ default:
+ dfu_state = DFU_STATE_dfuERROR;
+ goto send_stall;
+ break;
+ }
+ break;
+ case DFU_STATE_dfuMANIFEST_SYNC:
+ switch (req) {
+ case USB_REQ_DFU_GETSTATUS:
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate();
+ break;
+ default:
+ dfu_state = DFU_STATE_dfuERROR;
+ goto send_stall;
+ break;
+ }
+ break;
+ case DFU_STATE_dfuMANIFEST:
+ dfu_state = DFU_STATE_dfuERROR;
+ goto send_stall;
+ break;
+ case DFU_STATE_dfuMANIFEST_WAIT_RST:
+ /* we should never go here */
+ break;
+ case DFU_STATE_dfuUPLOAD_IDLE:
+ switch (req) {
+ case USB_REQ_DFU_UPLOAD:
+ /* state transition if less data then requested */
+ rc = handle_upload(val, len);
+ if (rc >= 0 && rc < len)
+ dfu_state = DFU_STATE_dfuIDLE;
+ break;
+ case USB_REQ_DFU_ABORT:
+ dfu_state = DFU_STATE_dfuIDLE;
+ /* no zlp? */
+ goto send_zlp;
+ break;
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus();
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate();
+ break;
+ default:
+ dfu_state = DFU_STATE_dfuERROR;
+ goto send_stall;
+ break;
+ }
+ break;
+ case DFU_STATE_dfuERROR:
+ switch (req) {
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus();
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate();
+ break;
+ case USB_REQ_DFU_CLRSTATUS:
+ dfu_state = DFU_STATE_dfuIDLE;
+ /* no zlp? */
+ goto send_zlp;
+ break;
+ default:
+ dfu_state = DFU_STATE_dfuERROR;
+ goto send_stall;
+ break;
+ }
+ break;
+ }
+
+ DEBUGE("OK new_state = %u\r\n", dfu_state);
+ return 0;
+
+send_stall:
+ udp_ep0_send_stall();
+ DEBUGE("STALL new_state = %u\r\n", dfu_state);
+ return -EINVAL;
+
+send_zlp:
+ udp_ep0_send_zlp();
+ DEBUGE("ZLP new_state = %u\r\n", dfu_state);
+ return 0;
+}
+static u_int8_t cur_config;
+
+/* USB DFU Device descriptor in DFU mode */
+__dfustruct const struct usb_device_descriptor dfu_dev_descriptor = {
+ .bLength = USB_DT_DEVICE_SIZE,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = 0x0100,
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize0 = 8,
+ .idVendor = OPENPCD_VENDOR_ID,
+ .idProduct = OPENPCD_PRODUCT_ID,
+ .bcdDevice = 0x0000,
+ .iManufacturer = 0x00,
+ .iProduct = 0x00,
+ .iSerialNumber = 0x00,
+ .bNumConfigurations = 0x01,
+};
+
+/* USB DFU Config descriptor in DFU mode */
+__dfustruct const struct _dfu_desc dfu_cfg_descriptor = {
+ .ucfg = {
+ .bLength = USB_DT_CONFIG_SIZE,
+ .bDescriptorType = USB_DT_CONFIG,
+ .wTotalLength = USB_DT_CONFIG_SIZE +
+ 2* USB_DT_INTERFACE_SIZE +
+ USB_DT_DFU_SIZE,
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = USB_CONFIG_ATT_ONE,
+ .bMaxPower = 100,
+ },
+ .uif[0] = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0x00,
+ .bAlternateSetting = 0x00,
+ .bNumEndpoints = 0x00,
+ .bInterfaceClass = 0xfe,
+ .bInterfaceSubClass = 0x01,
+ .bInterfaceProtocol = 0x02,
+ .iInterface = 0,
+ },
+ .uif[1] = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0x00,
+ .bAlternateSetting = 0x01,
+ .bNumEndpoints = 0x00,
+ .bInterfaceClass = 0xfe,
+ .bInterfaceSubClass = 0x01,
+ .bInterfaceProtocol = 0x02,
+ .iInterface = 0,
+ },
+
+ .func_dfu = DFU_FUNC_DESC,
+};
+
+
+/* minimal USB EP0 handler in DFU mode */
+static __dfufunc void dfu_udp_ep0_handler(void)
+{
+ AT91PS_UDP pUDP = AT91C_BASE_UDP;
+ u_int8_t bmRequestType, bRequest;
+ u_int16_t wValue, wIndex, wLength, wStatus;
+ u_int32_t csr = pUDP->UDP_CSR[0];
+
+ DEBUGE("CSR=0x%04x ", csr);
+
+ if (csr & AT91C_UDP_STALLSENT) {
+ DEBUGE("ACK_STALLSENT ");
+ pUDP->UDP_CSR[0] = ~AT91C_UDP_STALLSENT;
+ }
+
+ if (csr & AT91C_UDP_RX_DATA_BK0) {
+ DEBUGE("ACK_BANK0 ");
+ pUDP->UDP_CSR[0] &= ~AT91C_UDP_RX_DATA_BK0;
+ }
+
+ if (!(csr & AT91C_UDP_RXSETUP)) {
+ DEBUGE("no setup packet ");
+ return;
+ }
+
+ DEBUGE("len=%d ", csr >> 16);
+ if (csr >> 16 == 0) {
+ DEBUGE("empty packet ");
+ return;
+ }
+
+ bmRequestType = pUDP->UDP_FDR[0];
+ bRequest = pUDP->UDP_FDR[0];
+ wValue = (pUDP->UDP_FDR[0] & 0xFF);
+ wValue |= (pUDP->UDP_FDR[0] << 8);
+ wIndex = (pUDP->UDP_FDR[0] & 0xFF);
+ wIndex |= (pUDP->UDP_FDR[0] << 8);
+ wLength = (pUDP->UDP_FDR[0] & 0xFF);
+ wLength |= (pUDP->UDP_FDR[0] << 8);
+
+ DEBUGE("bmRequestType=0x%2x ", bmRequestType);
+
+ if (bmRequestType & 0x80) {
+ DEBUGE("DATA_IN=1 ");
+ pUDP->UDP_CSR[0] |= AT91C_UDP_DIR;
+ while (!(pUDP->UDP_CSR[0] & AT91C_UDP_DIR)) ;
+ }
+ pUDP->UDP_CSR[0] &= ~AT91C_UDP_RXSETUP;
+ while ((pUDP->UDP_CSR[0] & AT91C_UDP_RXSETUP)) ;
+
+ /* Handle supported standard device request Cf Table 9-3 in USB
+ * speciication Rev 1.1 */
+ switch ((bRequest << 8) | bmRequestType) {
+ case STD_GET_DESCRIPTOR:
+ DEBUGE("GET_DESCRIPTOR ");
+ if (wValue == 0x100) {
+ /* Return Device Descriptor */
+ udp_ep0_send_data((const char *)
+ &dfu_dev_descriptor,
+ MIN(sizeof(dfu_dev_descriptor),
+ wLength));
+ } else if (wValue == 0x200) {
+ /* Return Configuration Descriptor */
+ udp_ep0_send_data((const char *)
+ &dfu_cfg_descriptor,
+ MIN(sizeof(dfu_cfg_descriptor),
+ wLength));
+#if 0
+ } else if (wValue == 0x400) {
+ /* Return Interface descriptor */
+ if (wIndex != 0x01)
+ udp_ep0_send_stall();
+ udp_ep0_send_data((const char *)
+ &dfu_if_descriptor,
+ MIN(sizeof(dfu_if_descriptor),
+ wLength));
+#endif
+ } else
+ udp_ep0_send_stall();
+ break;
+ case STD_SET_ADDRESS:
+ DEBUGE("SET_ADDRESS ");
+ udp_ep0_send_zlp();
+ pUDP->UDP_FADDR = (AT91C_UDP_FEN | wValue);
+ pUDP->UDP_GLBSTATE = (wValue) ? AT91C_UDP_FADDEN : 0;
+ break;
+ case STD_SET_CONFIGURATION:
+ DEBUGE("SET_CONFIG ");
+ if (wValue)
+ DEBUGE("VALUE!=0 ");
+ cur_config = wValue;
+ udp_ep0_send_zlp();
+ pUDP->UDP_GLBSTATE =
+ (wValue) ? AT91C_UDP_CONFG : AT91C_UDP_FADDEN;
+ pUDP->UDP_CSR[1] =
+ (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT) :
+ 0;
+ pUDP->UDP_CSR[2] =
+ (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN) : 0;
+ pUDP->UDP_CSR[3] =
+ (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN) : 0;
+ pUDP->UDP_IER = (AT91C_UDP_EPINT0|AT91C_UDP_EPINT1|
+ AT91C_UDP_EPINT2|AT91C_UDP_EPINT3);
+ break;
+ case STD_GET_CONFIGURATION:
+ DEBUGE("GET_CONFIG ");
+ udp_ep0_send_data((char *)&(cur_config),
+ sizeof(cur_config));
+ break;
+ case STD_GET_STATUS_ZERO:
+ DEBUGE("GET_STATUS_ZERO ");
+ wStatus = 0;
+ udp_ep0_send_data((char *)&wStatus, sizeof(wStatus));
+ break;
+ case STD_GET_STATUS_INTERFACE:
+ DEBUGE("GET_STATUS_INTERFACE ");
+ wStatus = 0;
+ udp_ep0_send_data((char *)&wStatus, sizeof(wStatus));
+ break;
+ case STD_GET_STATUS_ENDPOINT:
+ DEBUGE("GET_STATUS_ENDPOINT(EPidx=%u) ", wIndex&0x0f);
+ wStatus = 0;
+ wIndex &= 0x0F;
+ if ((pUDP->UDP_GLBSTATE & AT91C_UDP_CONFG) && (wIndex == 0)) {
+ wStatus =
+ (pUDP->UDP_CSR[wIndex] & AT91C_UDP_EPEDS) ? 0 : 1;
+ udp_ep0_send_data((char *)&wStatus,
+ sizeof(wStatus));
+ } else if ((pUDP->UDP_GLBSTATE & AT91C_UDP_FADDEN)
+ && (wIndex == 0)) {
+ wStatus =
+ (pUDP->UDP_CSR[wIndex] & AT91C_UDP_EPEDS) ? 0 : 1;
+ udp_ep0_send_data((char *)&wStatus,
+ sizeof(wStatus));
+ } else
+ udp_ep0_send_stall();
+ break;
+ case STD_SET_FEATURE_ZERO:
+ DEBUGE("SET_FEATURE_ZERO ");
+ udp_ep0_send_stall();
+ break;
+ case STD_SET_FEATURE_INTERFACE:
+ DEBUGE("SET_FEATURE_INTERFACE ");
+ udp_ep0_send_zlp();
+ break;
+ case STD_SET_FEATURE_ENDPOINT:
+ DEBUGE("SET_FEATURE_ENDPOINT ");
+ udp_ep0_send_stall();
+ break;
+ case STD_CLEAR_FEATURE_ZERO:
+ DEBUGE("CLEAR_FEATURE_ZERO ");
+ udp_ep0_send_stall();
+ break;
+ case STD_CLEAR_FEATURE_INTERFACE:
+ DEBUGE("CLEAR_FEATURE_INTERFACE ");
+ udp_ep0_send_zlp();
+ break;
+ case STD_CLEAR_FEATURE_ENDPOINT:
+ DEBUGE("CLEAR_FEATURE_ENDPOINT(EPidx=%u) ", wIndex & 0x0f);
+ udp_ep0_send_stall();
+ break;
+ case STD_SET_INTERFACE:
+ DEBUGE("SET INTERFACE ");
+ udp_ep0_send_stall();
+ break;
+ default:
+ DEBUGE("DEFAULT(req=0x%02x, type=0x%02x) ", bRequest, bmRequestType);
+ if ((bmRequestType & 0x3f) == USB_TYPE_DFU) {
+ dfu_ep0_handler(bmRequestType, bRequest, wValue, wLength);
+ } else
+ udp_ep0_send_stall();
+ break;
+ }
+}
+
+/* minimal USB IRQ handler in DFU mode */
+static __dfufunc void dfu_udp_irq(void)
+{
+ AT91PS_UDP pUDP = AT91C_BASE_UDP;
+ AT91_REG isr = pUDP->UDP_ISR;
+
+ if (isr & AT91C_UDP_ENDBUSRES) {
+ pUDP->UDP_IER = AT91C_UDP_EPINT0;
+ /* reset all endpoints */
+ pUDP->UDP_RSTEP = (unsigned int)-1;
+ pUDP->UDP_RSTEP = 0;
+ /* Enable the function */
+ pUDP->UDP_FADDR = AT91C_UDP_FEN;
+ /* Configure endpoint 0 */
+ pUDP->UDP_CSR[0] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_CTRL);
+ cur_config = 0;
+ }
+
+ if (isr & AT91C_UDP_EPINT0)
+ dfu_udp_ep0_handler();
+
+ /* clear all interrupts */
+ pUDP->UDP_ICR = isr;
+
+ AT91F_AIC_ClearIt(AT91C_BASE_AIC, AT91C_ID_UDP);
+}
+
+/* this is only called once before DFU mode, no __dfufunc required */
+static void dfu_switch(void)
+{
+ AT91PS_AIC pAic = AT91C_BASE_AIC;
+
+ DEBUGE("Switching to DFU mode ");
+
+ pAic->AIC_SVR[AT91C_ID_UDP] = (unsigned int) &dfu_udp_irq;
+ dfu_state = DFU_STATE_dfuIDLE;
+}
+
+void __dfufunc dfu_main(void)
+{
+ udp_init();
+
+ /* This implements
+ AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_UDP,
+ OPENPCD_IRQ_PRIO_UDP,
+ AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, dfu_udp_irq);
+ */
+ AT91PS_AIC pAic = AT91C_BASE_AIC;
+ pAic->AIC_IDCR = 1 << AT91C_ID_UDP;
+ pAic->AIC_SVR[AT91C_ID_UDP] = (unsigned int) &dfu_udp_irq;
+ pAic->AIC_SMR[AT91C_ID_UDP] = AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL |
+ OPENPCD_IRQ_PRIO_UDP;
+ pAic->AIC_ICCR = 1 << AT91C_ID_UDP;
+
+ AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_UDP);
+
+ /* End-of-Bus-Reset is always enabled */
+
+ /* Clear for set the Pull up resistor */
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPCD_PIO_UDP_PUP);
+
+ /* do nothing, since all of DFU is interrupt driven */
+ while (1) ;
+}
+
+const struct dfuapi __dfufunctab dfu_api = {
+ .ep0_send_data = &udp_ep0_send_data,
+ .ep0_send_zlp = &udp_ep0_send_zlp,
+ .ep0_send_stall = &udp_ep0_send_stall,
+ .dfu_ep0_handler = &dfu_ep0_handler,
+ .dfu_switch = &dfu_switch,
+ .dfu_state = &dfu_state,
+ .dfu_dev_descriptor = &dfu_dev_descriptor,
+ .dfu_cfg_descriptor = &dfu_cfg_descriptor,
+};
+
+/* just for testing */
+int foo = 12345;
diff --git a/firmware/src/os/dfu.h b/firmware/src/os/dfu.h
new file mode 100644
index 0000000..8786044
--- /dev/null
+++ b/firmware/src/os/dfu.h
@@ -0,0 +1,80 @@
+#ifndef _DFU_H
+#define _DFU_H
+
+/* USB Device Firmware Update Implementation for OpenPCD
+ * (C) 2006 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
+ *
+ */
+
+#include <sys/types.h>
+#include <usb_ch9.h>
+#include <usb_dfu.h>
+
+#include "dbgu.h"
+
+/* USB DFU functional descriptor */
+#define DFU_FUNC_DESC { \
+ .bLength = USB_DT_DFU_SIZE, \
+ .bDescriptorType = USB_DT_DFU, \
+ .bmAttributes = USB_DFU_CAN_UPLOAD | USB_DFU_CAN_DOWNLOAD, \
+ .wDetachTimeOut = 0xff00, \
+ .wTransferSize = AT91C_IFLASH_PAGE_SIZE, \
+ .bcdDFUVersion = 0x0100, \
+}
+
+/* USB Interface descriptor in Runtime mode */
+#define DFU_RT_IF_DESC { \
+ .bLength = USB_DT_INTERFACE_SIZE, \
+ .bDescriptorType = USB_DT_INTERFACE, \
+ .bInterfaceNumber = 0x01, \
+ .bAlternateSetting = 0x00, \
+ .bNumEndpoints = 0x00, \
+ .bInterfaceClass = 0xfe, \
+ .bInterfaceSubClass = 0x01, \
+ .bInterfaceProtocol = 0x01, \
+ .iInterface = 1, \
+}
+
+#define __dfufunc __attribute__ ((long_call, section (".dfu.func")))
+#define __dfustruct __attribute__ ((section (".dfu.struct"))) const
+#define __dfufunctab __attribute__ ((section (".dfu.functab")))
+
+#if 0
+extern void __dfufunc udp_ep0_send_data(const char *data, u_int32_t length);
+extern void __dfufunc udp_ep0_send_zlp(void);
+extern void __dfufunc udp_ep0_send_stall(void);
+extern __dfustruct struct usb_device_descriptor dfu_dev_descriptor;
+extern __dfustruct struct _dfu_desc dfu_cfg_descriptor;
+extern void dfu_switch(void);
+extern int __dfufunc dfu_ep0_handler(u_int8_t req_type, u_int8_t req,
+ u_int16_t val, u_int16_t len);
+extern static u_int8_t dfu_state;
+struct udp_pcd;
+#endif
+
+
+extern void __dfufunc udp_init(void);
+
+struct _dfu_desc {
+ struct usb_config_descriptor ucfg;
+ struct usb_interface_descriptor uif[2];
+ struct usb_dfu_func_descriptor func_dfu;
+};
+
+struct dfuapi {
+ void (*ep0_send_data)(const char *data, u_int32_t len);
+ void (*ep0_send_zlp)(void);
+ void (*ep0_send_stall)(void);
+ int (*dfu_ep0_handler)(u_int8_t req_type, u_int8_t req,
+ u_int16_t val, u_int16_t len);
+ void (*dfu_switch)(void);
+ u_int8_t *dfu_state;
+ const struct usb_device_descriptor *dfu_dev_descriptor;
+ const struct _dfu_desc *dfu_cfg_descriptor;
+};
+
+
+#endif /* _DFU_H */
diff --git a/firmware/src/os/fifo.c b/firmware/src/os/fifo.c
new file mode 100644
index 0000000..b0ec152
--- /dev/null
+++ b/firmware/src/os/fifo.c
@@ -0,0 +1,108 @@
+/* Implementation of a virtual FIFO */
+
+#include "fifo.h"
+
+#include <errno.h>
+#include <string.h>
+
+#define FIFO_IRQ_LO 0x01
+#define FIFO_IRQ_HI 0x02
+#define FIFO_IRQ_OFLOW 0x04
+
+/* returns number of data bytes present in the fifo */
+int fifo_available(struct fifo *fifo)
+{
+ if (fifo->producer > fifo->consumer)
+ return fifo->producer - fifo->consumer;
+ else
+ return (fifo->size - fifo->consumer) + fifo->producer;
+}
+
+void fifo_check_water(struct fifo *fifo)
+{
+ int avail = fifo_available(fifo);
+
+ if (avail <= fifo->watermark)
+ fifo->irq |= FIFO_IRQ_LO;
+ else
+ fifo->irq &= FIFO_IRQ_LO;
+
+ if (fifo->size - avail >= fifo->watermark)
+ fifo->irq |= FIFO_IRQ_HI;
+ else
+ fifo->irq &= FIFO_IRQ_HI;
+}
+
+void fifo_check_raise_int(struct fifo *fifo)
+{
+ if (fifo->irq & fifo->irq_en)
+ fifo->callback(fifo, fifo->irq, fifo->cb_data);
+}
+
+
+u_int16_t fifo_data_put(struct fifo *fifo, u_int16_t len, u_int8_t *data)
+{
+ if (len > fifo_available(fifo)) {
+ len = fifo_available(fifo);
+ fifo->irq |= FIFO_IRQ_OFLOW;
+ }
+
+ if (len + fifo->producer <= fifo->size) {
+ /* easy case */
+ memcpy(&fifo->data[fifo->producer], data, len);
+ fifo->producer += len;
+ } else {
+ /* difficult: wrap around */
+ u_int16_t chunk_len;
+
+ chunk_len = fifo->size - fifo->producer;
+ memcpy(&fifo->data[fifo->producer], data, chunk_len);
+
+ memcpy(&fifo->data[0], data + chunk_len, len - chunk_len);
+ fifo->producer = len - chunk_len;
+ }
+
+ fifo_check_water(fifo);
+
+ return len;
+}
+
+
+u_int16_t fifo_data_get(struct fifo *fifo, u_int16_t len, u_int8_t *data)
+{
+ u_int16_t avail = fifo_available(fifo);
+
+ if (avail < len)
+ len = avail;
+
+ if (fifo->producer > fifo->consumer) {
+ /* easy case */
+ memcpy(data, &fifo->data[fifo->consumer], len);
+ } else {
+ /* difficult case: wrap */
+ u_int16_t chunk_len = fifo->size - fifo->consumer;
+ memcpy(data, &fifo->data[fifo->consumer], chunk_len);
+ memcpy(data+chunk_len, &fifo->data[0], len - chunk_len);
+ }
+
+ fifo_check_water(fifo);
+
+ return len;
+}
+
+int fifo_init(struct fifo *fifo, u_int16_t size,
+ void (*cb)(struct fifo *fifo, u_int8_t event, void *data), void *cb_data)
+{
+ if (size > sizeof(fifo->data))
+ return -EINVAL;
+
+ memset(fifo->data, 0, sizeof(fifo->data));
+ fifo->size = size;
+ fifo->producer = fifo->consumer = 0;
+ fifo->watermark = 0;
+ fifo->callback = cb;
+ fifo->cb_data = cb_data;
+
+ return 0;
+}
+
diff --git a/firmware/src/os/fifo.h b/firmware/src/os/fifo.h
new file mode 100644
index 0000000..d91c6c2
--- /dev/null
+++ b/firmware/src/os/fifo.h
@@ -0,0 +1,28 @@
+#ifndef _FIFO_H
+#define _FIFO_H
+
+#include <sys/types.h>
+
+#define FIFO_SIZE 1024
+
+struct fifo {
+ u_int16_t size; /* actual FIFO size, can be smaller than 'data' */
+ u_int16_t producer; /* index of producer */
+ u_int16_t consumer; /* index of consumer */
+ u_int16_t watermark;
+ u_int8_t irq;
+ u_int8_t irq_en;
+ u_int8_t status;
+ void (*callback)(struct fifo *fifo, u_int8_t event, void *data);
+ void *cb_data;
+ u_int8_t data[FIFO_SIZE];
+};
+
+
+extern int fifo_init(struct fifo *fifo, u_int16_t size,
+ void (*callback)(struct fifo *fifo, u_int8_t event, void *data), void *cb_data);
+extern u_int16_t fifo_data_get(struct fifo *fifo, u_int16_t len, u_int8_t *data);
+extern u_int16_t fifo_data_put(struct fifo *fifo, u_int16_t len, u_int8_t *data);
+extern int fifo_available(struct fifo *fifo);
+
+#endif
diff --git a/firmware/src/os/flash.c b/firmware/src/os/flash.c
new file mode 100644
index 0000000..2aaf760
--- /dev/null
+++ b/firmware/src/os/flash.c
@@ -0,0 +1,45 @@
+
+
+#define EFCS_CMD_WRITE_PAGE 0x01
+#define EFCS_CMD_SET_LOCK_BIT 0x02
+#define EFCS_CMD_WRITE_PAGE_LOCK 0x03
+#define EFCS_CMD_CLEAR_LOCK 0x04
+#define EFCS_CMD_ERASE_ALL 0x08
+#define EFCS_CMD_SET_NVM_BIT 0x0b
+#define EFCS_CMD_CLEAR_NVM_BIT 0x0d
+#define EFCS_CMD_SET_SECURITY_BIT 0x0f
+
+
+int unlock_page(u_int16_t page)
+{
+ AT91C_MC_FCMD_UNLOCK | AT91C_MC_CORRECT_KEY |
+
+}
+
+int flash_sector(unsigned int sector, const u_int8_t *data, unsigned int len)
+{
+ volatile u_int32_t *p = (volatile u_int32_t *)0;
+ u_int32_t *src32 = (u_int32_t *)data;
+ int i;
+
+ /* hand-code memcpy because we need to make sure only 32bit accesses
+ * are used */
+ for (i = 0; i < len/4; i++)
+ p[i] = src32[i];
+
+ AT91F_MC_EFC_PerformCmd(pmc , AT91C_MC_FCMD_START_PROG|
+ AT91C_MC_CORRECT_KEY | );
+}
+
+
+void flash_init(void)
+{
+ unsigned int fmcn = AT91F_MC_EFC_ComputerFMCN(48000000);
+
+ AT91F_MC_EFC_CfgModeReg(ff, fmcn << 16 | AT91C_MC_FWS_3FWS |
+ AT91C_MC_FRDY | AT91C_MC_LOCKE |
+ AT91C_MC_PROGE);
+
+ AT91F_AIC_EnableIt();
+
+}
diff --git a/firmware/src/os/led.c b/firmware/src/os/led.c
new file mode 100644
index 0000000..8c34c6c
--- /dev/null
+++ b/firmware/src/os/led.c
@@ -0,0 +1,85 @@
+
+#include <sys/types.h>
+#include <errno.h>
+#include <lib_AT91SAM7.h>
+#include <openpcd.h>
+#include "../openpcd.h"
+#include <os/usb_handler.h>
+#include <os/req_ctx.h>
+#include <os/dbgu.h>
+
+static int led2port(int led)
+{
+ if (led == 1)
+ return OPENPCD_PIO_LED1;
+ else if (led == 2)
+ return OPENPCD_PIO_LED2;
+ else
+ return 0;
+}
+
+void led_switch(int led, int on)
+{
+ int port = led2port(led);
+
+ if (port == -1)
+ return;
+
+ if (on)
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, port);
+ else
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, port);
+}
+
+int led_get(int led)
+{
+ int port = led2port(led);
+
+ if (port == -1)
+ return -1;
+
+ return !(AT91F_PIO_GetOutputDataStatus(AT91C_BASE_PIOA) & port);
+}
+
+int led_toggle(int led)
+{
+ int on = led_get(led);
+ if (on == -1)
+ return -1;
+
+ if (on)
+ led_switch(led, 0);
+ else
+ led_switch(led, 1);
+
+ return !on;
+}
+
+static int led_usb_rx(struct req_ctx *rctx)
+{
+ struct openpcd_hdr *poh = (struct openpcd_hdr *) &rctx->rx.data[0];
+ int ret = 1;
+
+ switch (poh->cmd) {
+ case OPENPCD_CMD_SET_LED:
+ DEBUGP("SET LED(%u,%u) ", poh->reg, poh->val);
+ led_switch(poh->reg, poh->val);
+ break;
+ default:
+ DEBUGP("UNKNOWN ");
+ ret = -EINVAL;
+ break;
+ }
+ req_ctx_put(rctx);
+ return 1;
+}
+
+void led_init(void)
+{
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPCD_PIO_LED1);
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPCD_PIO_LED2);
+ led_switch(1, 0);
+ led_switch(2, 0);
+
+ usb_hdlr_register(&led_usb_rx, OPENPCD_CMD_CLS_LED);
+}
diff --git a/firmware/src/os/led.h b/firmware/src/os/led.h
new file mode 100644
index 0000000..394107b
--- /dev/null
+++ b/firmware/src/os/led.h
@@ -0,0 +1,9 @@
+#ifndef _LED_H
+#define _LED_H
+
+extern void led_init(void);
+extern void led_switch(int led, int on);
+extern int led_get(int led);
+extern int led_toggle(int led);
+
+#endif
diff --git a/firmware/src/os/main.c b/firmware/src/os/main.c
new file mode 100644
index 0000000..4b7a7a2
--- /dev/null
+++ b/firmware/src/os/main.c
@@ -0,0 +1,47 @@
+#include <errno.h>
+#include <string.h>
+#include <include/lib_AT91SAM7.h>
+#include <os/dbgu.h>
+#include <os/led.h>
+#include <os/dfu.h>
+#include <os/main.h>
+#include <os/power.h>
+#include <os/pcd_enumerate.h>
+#include "../openpcd.h"
+
+int main(void)
+{
+ /* initialize LED and debug unit */
+ led_init();
+ AT91F_DBGU_Init();
+
+ AT91F_PIOA_CfgPMC();
+ /* call application specific init function */
+ _init_func();
+
+ /* initialize USB */
+ udp_init();
+ udp_open();
+
+ // Enable User Reset and set its minimal assertion to 960 us
+ AT91C_BASE_RSTC->RSTC_RMR =
+ AT91C_RSTC_URSTEN | (0x4 << 8) | (unsigned int)(0xA5 << 24);
+
+#ifdef DEBUG_CLOCK_PA6
+ AT91F_PMC_EnablePCK(AT91C_BASE_PMC, 0, AT91C_PMC_CSS_PLL_CLK);
+ AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, 0, AT91C_PA6_PCK0);
+#endif
+
+ /* switch on first led */
+ led_switch(2, 1);
+
+ DEBUGPCRF("entering main (idle) loop");
+ while (1) {
+ /* Call application specific main idle function */
+ _main_func();
+ dbgu_rb_flush();
+#ifdef CONFIG_IDLE
+ //cpu_idle();
+#endif
+ }
+}
diff --git a/firmware/src/os/main.h b/firmware/src/os/main.h
new file mode 100644
index 0000000..1adc8f6
--- /dev/null
+++ b/firmware/src/os/main.h
@@ -0,0 +1,8 @@
+#ifndef _MAIN_H
+#define _MAIN_H
+
+extern void _init_func(void);
+extern int _main_dbgu(char key);
+extern void _main_func(void);
+
+#endif
diff --git a/firmware/src/os/pcd_enumerate.c b/firmware/src/os/pcd_enumerate.c
new file mode 100644
index 0000000..cded8c8
--- /dev/null
+++ b/firmware/src/os/pcd_enumerate.c
@@ -0,0 +1,605 @@
+/* AT91SAM7 USB interface code for OpenPCD
+ *
+ * (C) 2006 by Harald Welte <laforge@gnumonks.org>
+ *
+ * based on existing AT91SAM7 UDP CDC ACM example code, licensed as followed:
+ *----------------------------------------------------------------------------
+ * ATMEL Microcontroller Software Support - ROUSSET -
+ *----------------------------------------------------------------------------
+ * The software is delivered "AS IS" without warranty or condition of any
+ * kind, either express, implied or statutory. This includes without
+ * limitation any warranty or condition with respect to merchantability or
+ * fitness for any particular purpose, or against the infringements of
+ * intellectual property rights of others.
+ *----------------------------------------------------------------------------
+ */
+
+#include <errno.h>
+#include <usb_ch9.h>
+#include <sys/types.h>
+#include <asm/atomic.h>
+#include <lib_AT91SAM7.h>
+#include <openpcd.h>
+
+#include <os/pcd_enumerate.h>
+#include <os/req_ctx.h>
+#include <os/dfu.h>
+#include "../openpcd.h"
+#include <os/dbgu.h>
+
+#define DEBUG_UDP_IRQ
+#define DEBUG_UDP_EP0
+
+#define CONFIG_DFU
+
+#define AT91C_EP_OUT 1
+#define AT91C_EP_OUT_SIZE 0x40
+#define AT91C_EP_IN 2
+#define AT91C_EP_IN_SIZE 0x40
+#define AT91C_EP_INT 3
+
+#ifdef CONFIG_DFU
+#define DFU_API_LOCATION ((const struct dfuapi *) 0x00102100)
+static const struct dfuapi *dfu = DFU_API_LOCATION;
+#define udp_ep0_send_data dfu->ep0_send_data
+#define udp_ep0_send_zlp dfu->ep0_send_zlp
+#define udp_ep0_send_stall dfu->ep0_send_stall
+#else
+#error non-DFU builds currently not supported (yet) again
+#endif
+
+static struct udp_pcd upcd;
+
+const struct usb_device_descriptor dev_descriptor = {
+ .bLength = USB_DT_DEVICE_SIZE,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = 0x0200,
+ .bDeviceClass = USB_CLASS_VENDOR_SPEC,
+ .bDeviceSubClass = 0xff,
+ .bDeviceProtocol = 0xff,
+ .bMaxPacketSize0 = 0x08,
+ .idVendor = OPENPCD_VENDOR_ID,
+ .idProduct = OPENPCD_PRODUCT_ID,
+ .bcdDevice = 0x0000,
+ .iManufacturer = 0x00,
+ .iProduct = 0x00,
+ .iSerialNumber = 0x00,
+ .bNumConfigurations = 0x01,
+};
+
+struct _desc {
+ struct usb_config_descriptor ucfg;
+ struct usb_interface_descriptor uif;
+ struct usb_endpoint_descriptor ep[3];
+#ifdef CONFIG_DFU
+ struct usb_interface_descriptor uif_dfu;
+ struct usb_dfu_func_descriptor func_dfu;
+#endif
+};
+
+const struct _desc cfg_descriptor = {
+ .ucfg = {
+ .bLength = USB_DT_CONFIG_SIZE,
+ .bDescriptorType = USB_DT_CONFIG,
+ .wTotalLength = USB_DT_CONFIG_SIZE +
+#ifdef CONFIG_DFU
+ 2 * USB_DT_INTERFACE_SIZE +
+ 3 * USB_DT_ENDPOINT_SIZE +
+ USB_DT_DFU_SIZE,
+ .bNumInterfaces = 2,
+#else
+ 1 * USB_DT_INTERFACE_SIZE +
+ 3 * USB_DT_ENDPOINT_SIZE,
+ .bNumInterfaces = 1,
+#endif
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = USB_CONFIG_ATT_ONE,
+ .bMaxPower = 100, /* 200mA */
+ },
+ .uif = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 3,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0xff,
+ .iInterface = 0,
+ },
+ .ep= {
+ {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = OPENPCD_OUT_EP,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = AT91C_EP_OUT_SIZE,
+ .bInterval = 0x00,
+ }, {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = OPENPCD_IN_EP,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = AT91C_EP_IN_SIZE,
+ .bInterval = 0x00,
+ }, {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = OPENPCD_IRQ_EP,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = AT91C_EP_IN_SIZE,
+ .bInterval = 0xff, /* FIXME */
+ },
+ },
+#ifdef CONFIG_DFU
+ .uif_dfu = DFU_RT_IF_DESC,
+ .func_dfu = DFU_FUNC_DESC,
+#endif
+};
+
+static const struct usb_string_descriptor string0 = {
+ .bLength = sizeof(string0),
+ .bDescriptorType = USB_DT_STRING,
+ .wData[0] = 0x0409, /* English */
+};
+
+
+static void udp_ep0_handler(void);
+
+void udp_unthrottle(void)
+{
+ AT91PS_UDP pUDP = upcd.pUdp;
+ pUDP->UDP_IER = AT91C_UDP_EPINT1;
+}
+
+int udp_refill_ep(int ep, struct req_ctx *rctx)
+{
+ u_int16_t i;
+ AT91PS_UDP pUDP = upcd.pUdp;
+
+ if (!upcd.cur_config)
+ return -ENXIO;
+
+ if (rctx->tx.tot_len > AT91C_EP_IN_SIZE) {
+ DEBUGPCRF("TOO LARGE!!!!!!!!!!!!!!!!!!!!!!!!!!! (%d > %d)",
+ rctx->tx.tot_len, AT91C_EP_IN_SIZE);
+ return -EINVAL;
+ }
+
+ if (atomic_read(&upcd.ep[ep].pkts_in_transit) == 2)
+ return -EBUSY;
+
+ /* fill FIFO/DPR */
+ for (i = 0; i < rctx->tx.tot_len; i++)
+ pUDP->UDP_FDR[ep] = rctx->tx.data[i];
+
+ if (atomic_inc_return(&upcd.ep[ep].pkts_in_transit) == 1) {
+ /* not been transmitting before, start transmit */
+ pUDP->UDP_CSR[ep] |= AT91C_UDP_TXPKTRDY;
+ }
+
+ /* return rctx to pool of free contexts */
+ req_ctx_put(rctx);
+
+ return 0;
+}
+
+#ifdef DEBUG_UDP_IRQ
+#define DEBUGI(x, args ...) DEBUGP(x, ## args)
+#else
+#define DEBUGI(x, args ...) do { } while (0)
+#endif
+
+static void udp_irq(void)
+{
+ u_int32_t csr;
+ AT91PS_UDP pUDP = upcd.pUdp;
+ AT91_REG isr = pUDP->UDP_ISR;
+
+ DEBUGI("udp_irq(imr=0x%04x, isr=0x%04x): ", pUDP->UDP_IMR, isr);
+
+ if (isr & AT91C_UDP_ENDBUSRES) {
+ DEBUGI("ENDBUSRES ");
+ pUDP->UDP_ICR = AT91C_UDP_ENDBUSRES;
+ pUDP->UDP_IER = AT91C_UDP_EPINT0;
+ /* reset all endpoints */
+ pUDP->UDP_RSTEP = (unsigned int)-1;
+ pUDP->UDP_RSTEP = 0;
+ /* Enable the function */
+ pUDP->UDP_FADDR = AT91C_UDP_FEN;
+ /* Configure endpoint 0 */
+ pUDP->UDP_CSR[0] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_CTRL);
+ upcd.cur_config = 0;
+
+#ifdef CONFIG_DFU
+ if (*dfu->dfu_state == DFU_STATE_appDETACH) {
+ /* now we need to switch to DFU mode */
+ dfu->dfu_switch();
+ }
+#endif
+ }
+
+ if (isr & AT91C_UDP_EPINT0) {
+ DEBUGI("EP0INT(Control) ");
+ udp_ep0_handler();
+ }
+ if (isr & AT91C_UDP_EPINT1) {
+ u_int32_t cur_rcv_bank = upcd.cur_rcv_bank;
+ csr = pUDP->UDP_CSR[1];
+ DEBUGI("EP1INT(Out, CSR=0x%08x) ", csr);
+ if (cur_rcv_bank == AT91C_UDP_RX_DATA_BK1)
+ DEBUGI("cur_bank=1 ");
+ else if (cur_rcv_bank == AT91C_UDP_RX_DATA_BK0)
+ DEBUGI("cur_bank=0 ");
+ else
+ DEBUGI("cur_bank INVALID ");
+
+ if (csr & AT91C_UDP_RX_DATA_BK1)
+ DEBUGI("BANK1 ");
+ if (csr & AT91C_UDP_RX_DATA_BK0)
+ DEBUGI("BANK0 ");
+
+ if (csr & cur_rcv_bank) {
+ u_int16_t pkt_recv = 0;
+ u_int16_t pkt_size = csr >> 16;
+ struct req_ctx *rctx = req_ctx_find_get(RCTX_STATE_FREE,
+ RCTX_STATE_UDP_RCV_BUSY);
+
+ if (rctx) {
+ rctx->rx.tot_len = pkt_size;
+ while (pkt_size--)
+ rctx->rx.data[pkt_recv++] = pUDP->UDP_FDR[1];
+ pUDP->UDP_CSR[1] &= ~cur_rcv_bank;
+ if (cur_rcv_bank == AT91C_UDP_RX_DATA_BK0)
+ cur_rcv_bank = AT91C_UDP_RX_DATA_BK1;
+ else
+ cur_rcv_bank = AT91C_UDP_RX_DATA_BK0;
+ upcd.cur_rcv_bank = cur_rcv_bank;
+ req_ctx_set_state(rctx, RCTX_STATE_UDP_RCV_DONE);
+ DEBUGI("RCTX=%u ", req_ctx_num(rctx));
+ } else {
+ /* disable interrupts for now */
+ pUDP->UDP_IDR = AT91C_UDP_EPINT1;
+ DEBUGP("NO_RCTX_AVAIL! ");
+ }
+ }
+ }
+ if (isr & AT91C_UDP_EPINT2) {
+ csr = pUDP->UDP_CSR[2];
+ DEBUGI("EP2INT(In, CSR=0x%08x) ", csr);
+ if (csr & AT91C_UDP_TXCOMP) {
+ struct req_ctx *rctx;
+
+ DEBUGI("ACK_TX_COMP ");
+ /* acknowledge TX completion */
+ pUDP->UDP_CSR[2] &= ~AT91C_UDP_TXCOMP;
+ while (pUDP->UDP_CSR[2] & AT91C_UDP_TXCOMP) ;
+
+ /* if we already have another packet in DPR, send it */
+ if (atomic_dec_return(&upcd.ep[2].pkts_in_transit) == 1)
+ pUDP->UDP_CSR[2] |= AT91C_UDP_TXPKTRDY;
+
+ /* try to re-fill from pending rcts for EP2 */
+ rctx = req_ctx_find_get(RCTX_STATE_UDP_EP2_PENDING,
+ RCTX_STATE_UDP_EP2_BUSY);
+ if (rctx)
+ udp_refill_ep(2, rctx);
+ else
+ DEBUGI("NO_RCTX_pending ");
+ }
+ }
+ if (isr & AT91C_UDP_EPINT3) {
+ csr = pUDP->UDP_CSR[3];
+ DEBUGI("EP3INT(Interrupt, CSR=0x%08x) ", csr);
+ /* Transmit has completed, re-fill from pending rcts for EP3 */
+ }
+
+ if (isr & AT91C_UDP_RXSUSP) {
+ pUDP->UDP_ICR = AT91C_UDP_RXSUSP;
+ DEBUGI("RXSUSP ");
+ /* FIXME: implement suspend/resume */
+ }
+ if (isr & AT91C_UDP_RXRSM) {
+ pUDP->UDP_ICR = AT91C_UDP_RXRSM;
+ DEBUGI("RXRSM ");
+ /* FIXME: implement suspend/resume */
+ }
+ if (isr & AT91C_UDP_EXTRSM) {
+ pUDP->UDP_ICR = AT91C_UDP_EXTRSM;
+ DEBUGI("EXTRSM ");
+ /* FIXME: implement suspend/resume */
+ }
+ if (isr & AT91C_UDP_SOFINT) {
+ pUDP->UDP_ICR = AT91C_UDP_SOFINT;
+ DEBUGI("SOFINT ");
+ }
+ if (isr & AT91C_UDP_WAKEUP) {
+ pUDP->UDP_ICR = AT91C_UDP_WAKEUP;
+ DEBUGI("WAKEUP ");
+ /* FIXME: implement suspend/resume */
+ }
+
+ DEBUGI("END\r\n");
+ AT91F_AIC_ClearIt(AT91C_BASE_AIC, AT91C_ID_UDP);
+}
+
+/* Open USB Device Port */
+void udp_open(void)
+{
+ DEBUGPCRF("entering");
+ upcd.pUdp = AT91C_BASE_UDP;
+ upcd.cur_config = 0;
+ upcd.cur_rcv_bank = AT91C_UDP_RX_DATA_BK0;
+
+ AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_UDP,
+ OPENPCD_IRQ_PRIO_UDP,
+ AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, udp_irq);
+ AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_UDP);
+
+ /* End-of-Bus-Reset is always enabled */
+
+ /* Set the Pull up resistor */
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPCD_PIO_UDP_PUP);
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPCD_PIO_UDP_PUP);
+}
+
+void udp_reset(void)
+{
+ volatile int i;
+
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPCD_PIO_UDP_PUP);
+ for (i = 0; i < 0xffff; i++)
+ ;
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPCD_PIO_UDP_PUP);
+}
+
+#ifdef DEBUG_UDP_EP0
+#define DEBUGE(x, args ...) DEBUGP(x, ## args)
+#else
+#define DEBUGE(x, args ...) do { } while (0)
+#endif
+
+/* Handle requests on the USB Control Endpoint */
+static void udp_ep0_handler(void)
+{
+ AT91PS_UDP pUDP = upcd.pUdp;
+ u_int8_t bmRequestType, bRequest;
+ u_int16_t wValue, wIndex, wLength, wStatus;
+ u_int32_t csr = pUDP->UDP_CSR[0];
+
+ DEBUGE("CSR=0x%04x ", csr);
+
+ if (csr & AT91C_UDP_STALLSENT) {
+ DEBUGE("ACK_STALLSENT ");
+ pUDP->UDP_CSR[0] = ~AT91C_UDP_STALLSENT;
+ }
+
+ if (csr & AT91C_UDP_RX_DATA_BK0) {
+ DEBUGE("ACK_BANK0 ");
+ pUDP->UDP_CSR[0] &= ~AT91C_UDP_RX_DATA_BK0;
+ }
+
+ if (!(csr & AT91C_UDP_RXSETUP)) {
+ DEBUGE("no setup packet ");
+ return;
+ }
+
+ DEBUGE("len=%d ", csr >> 16);
+ if (csr >> 16 == 0) {
+ DEBUGE("empty packet ");
+ return;
+ }
+
+ bmRequestType = pUDP->UDP_FDR[0];
+ bRequest = pUDP->UDP_FDR[0];
+ wValue = (pUDP->UDP_FDR[0] & 0xFF);
+ wValue |= (pUDP->UDP_FDR[0] << 8);
+ wIndex = (pUDP->UDP_FDR[0] & 0xFF);
+ wIndex |= (pUDP->UDP_FDR[0] << 8);
+ wLength = (pUDP->UDP_FDR[0] & 0xFF);
+ wLength |= (pUDP->UDP_FDR[0] << 8);
+
+ DEBUGE("bmRequestType=0x%2x ", bmRequestType);
+
+ if (bmRequestType & 0x80) {
+ DEBUGE("DATA_IN=1 ");
+ pUDP->UDP_CSR[0] |= AT91C_UDP_DIR;
+ while (!(pUDP->UDP_CSR[0] & AT91C_UDP_DIR)) ;
+ }
+ pUDP->UDP_CSR[0] &= ~AT91C_UDP_RXSETUP;
+ while ((pUDP->UDP_CSR[0] & AT91C_UDP_RXSETUP)) ;
+
+ /* Handle supported standard device request Cf Table 9-3 in USB
+ * speciication Rev 1.1 */
+ switch ((bRequest << 8) | bmRequestType) {
+ case STD_GET_DESCRIPTOR:
+ DEBUGE("GET_DESCRIPTOR ");
+ if (wValue == 0x100) {
+ /* Return Device Descriptor */
+#ifdef CONFIG_DFU
+ if (*dfu->dfu_state != DFU_STATE_appIDLE)
+ udp_ep0_send_data((const char *)
+ dfu->dfu_dev_descriptor,
+ MIN(dfu->dfu_dev_descriptor->bLength,
+ wLength));
+ else
+#endif
+ udp_ep0_send_data((const char *) &dev_descriptor,
+ MIN(sizeof(dev_descriptor), wLength));
+ } else if (wValue == 0x200) {
+ /* Return Configuration Descriptor */
+#ifdef CONFIG_DFU
+ if (*dfu->dfu_state != DFU_STATE_appIDLE)
+ udp_ep0_send_data((const char *)
+ dfu->dfu_cfg_descriptor,
+ MIN(dfu->dfu_cfg_descriptor->ucfg.wTotalLength,
+ wLength));
+ else
+#endif
+ udp_ep0_send_data((const char *) &cfg_descriptor,
+ MIN(sizeof(cfg_descriptor), wLength));
+ } else if (wValue == 0x300) {
+ /* Return String descriptor */
+ switch (wIndex) {
+ case 0:
+ udp_ep0_send_data((const char *) &string0,
+ MIN(sizeof(string0), wLength));
+ break;
+ default:
+ /* FIXME: implement this */
+ udp_ep0_send_stall();
+ break;
+ }
+#if 0
+ } else if (wValue == 0x400) {
+ /* Return Interface descriptor */
+ if (wIndex != 0x01)
+ udp_ep0_send_stall();
+ udp_ep0_send_data((const char *)
+ &dfu_if_descriptor,
+ MIN(sizeof(dfu_if_descriptor),
+ wLength));
+#endif
+ } else
+ udp_ep0_send_stall();
+ break;
+ case STD_SET_ADDRESS:
+ DEBUGE("SET_ADDRESS ");
+ udp_ep0_send_zlp();
+ pUDP->UDP_FADDR = (AT91C_UDP_FEN | wValue);
+ pUDP->UDP_GLBSTATE = (wValue) ? AT91C_UDP_FADDEN : 0;
+ break;
+ case STD_SET_CONFIGURATION:
+ DEBUGE("SET_CONFIG ");
+ if (wValue)
+ DEBUGE("VALUE!=0 ");
+ upcd.cur_config = wValue;
+ udp_ep0_send_zlp();
+ pUDP->UDP_GLBSTATE =
+ (wValue) ? AT91C_UDP_CONFG : AT91C_UDP_FADDEN;
+ pUDP->UDP_CSR[1] =
+ (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT) :
+ 0;
+ pUDP->UDP_CSR[2] =
+ (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN) : 0;
+ pUDP->UDP_CSR[3] =
+ (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN) : 0;
+ pUDP->UDP_IER = (AT91C_UDP_EPINT0|AT91C_UDP_EPINT1|
+ AT91C_UDP_EPINT2|AT91C_UDP_EPINT3);
+ break;
+ case STD_GET_CONFIGURATION:
+ DEBUGE("GET_CONFIG ");
+ udp_ep0_send_data((char *)&(upcd.cur_config),
+ sizeof(upcd.cur_config));
+ break;
+ case STD_GET_STATUS_ZERO:
+ DEBUGE("GET_STATUS_ZERO ");
+ wStatus = 0;
+ udp_ep0_send_data((char *)&wStatus, sizeof(wStatus));
+ break;
+ case STD_GET_STATUS_INTERFACE:
+ DEBUGE("GET_STATUS_INTERFACE ");
+ wStatus = 0;
+ udp_ep0_send_data((char *)&wStatus, sizeof(wStatus));
+ break;
+ case STD_GET_STATUS_ENDPOINT:
+ DEBUGE("GET_STATUS_ENDPOINT(EPidx=%u) ", wIndex&0x0f);
+ wStatus = 0;
+ wIndex &= 0x0F;
+ if ((pUDP->UDP_GLBSTATE & AT91C_UDP_CONFG) && (wIndex <= 3)) {
+ wStatus =
+ (pUDP->UDP_CSR[wIndex] & AT91C_UDP_EPEDS) ? 0 : 1;
+ udp_ep0_send_data((char *)&wStatus,
+ sizeof(wStatus));
+ } else if ((pUDP->UDP_GLBSTATE & AT91C_UDP_FADDEN)
+ && (wIndex == 0)) {
+ wStatus =
+ (pUDP->UDP_CSR[wIndex] & AT91C_UDP_EPEDS) ? 0 : 1;
+ udp_ep0_send_data((char *)&wStatus,
+ sizeof(wStatus));
+ } else
+ udp_ep0_send_stall();
+ break;
+ case STD_SET_FEATURE_ZERO:
+ DEBUGE("SET_FEATURE_ZERO ");
+ udp_ep0_send_stall();
+ break;
+ case STD_SET_FEATURE_INTERFACE:
+ DEBUGE("SET_FEATURE_INTERFACE ");
+ udp_ep0_send_zlp();
+ break;
+ case STD_SET_FEATURE_ENDPOINT:
+ DEBUGE("SET_FEATURE_ENDPOINT ");
+ udp_ep0_send_zlp();
+ wIndex &= 0x0F;
+ if ((wValue == 0) && wIndex && (wIndex <= 3)) {
+ pUDP->UDP_CSR[wIndex] = 0;
+ udp_ep0_send_zlp();
+ } else
+ udp_ep0_send_stall();
+ break;
+ case STD_CLEAR_FEATURE_ZERO:
+ DEBUGE("CLEAR_FEATURE_ZERO ");
+ udp_ep0_send_stall();
+ break;
+ case STD_CLEAR_FEATURE_INTERFACE:
+ DEBUGP("CLEAR_FEATURE_INTERFACE ");
+ udp_ep0_send_zlp();
+ break;
+ case STD_CLEAR_FEATURE_ENDPOINT:
+ DEBUGE("CLEAR_FEATURE_ENDPOINT(EPidx=%u) ", wIndex & 0x0f);
+ wIndex &= 0x0F;
+ if ((wValue == 0) && wIndex && (wIndex <= 3)) {
+ struct req_ctx *rctx;
+ if (wIndex == 1) {
+ pUDP->UDP_CSR[1] =
+ (AT91C_UDP_EPEDS |
+ AT91C_UDP_EPTYPE_BULK_OUT);
+ pUDP->UDP_RSTEP |= AT91C_UDP_EP1;
+ pUDP->UDP_RSTEP &= ~AT91C_UDP_EP1;
+ }
+ else if (wIndex == 2) {
+ pUDP->UDP_CSR[2] =
+ (AT91C_UDP_EPEDS |
+ AT91C_UDP_EPTYPE_BULK_IN);
+ pUDP->UDP_RSTEP |= AT91C_UDP_EP2;
+ pUDP->UDP_RSTEP &= ~AT91C_UDP_EP2;
+
+ /* free all currently transmitting contexts */
+ while (rctx = req_ctx_find_get(RCTX_STATE_UDP_EP2_BUSY,
+ RCTX_STATE_FREE)) {}
+ atomic_set(&upcd.ep[wIndex].pkts_in_transit, 0);
+ }
+ else if (wIndex == 3) {
+ pUDP->UDP_CSR[3] =
+ (AT91C_UDP_EPEDS |
+ AT91C_UDP_EPTYPE_INT_IN);
+ pUDP->UDP_RSTEP |= AT91C_UDP_EP3;
+ pUDP->UDP_RSTEP &= ~AT91C_UDP_EP3;
+
+ /* free all currently transmitting contexts */
+ while (rctx = req_ctx_find_get(RCTX_STATE_UDP_EP3_BUSY,
+ RCTX_STATE_FREE)) {}
+ atomic_set(&upcd.ep[wIndex].pkts_in_transit, 0);
+ }
+ udp_ep0_send_zlp();
+ } else
+ udp_ep0_send_stall();
+ break;
+ case STD_SET_INTERFACE:
+ DEBUGE("SET INTERFACE ");
+ udp_ep0_send_stall();
+ break;
+ default:
+ DEBUGE("DEFAULT(req=0x%02x, type=0x%02x) ", bRequest, bmRequestType);
+#ifdef CONFIG_DFU
+ if ((bmRequestType & 0x3f) == USB_TYPE_DFU) {
+ dfu->dfu_ep0_handler(bmRequestType, bRequest, wValue, wLength);
+ } else
+#endif
+ udp_ep0_send_stall();
+ break;
+ }
+}
+
diff --git a/firmware/src/os/pcd_enumerate.h b/firmware/src/os/pcd_enumerate.h
new file mode 100644
index 0000000..57ff88c
--- /dev/null
+++ b/firmware/src/os/pcd_enumerate.h
@@ -0,0 +1,56 @@
+#ifndef _OPCD_USB_H
+#define _OPCD_USB_H
+
+#include <lib_AT91SAM7.h>
+#include <sys/types.h>
+#include <asm/atomic.h>
+#include "openpcd.h"
+#include "dfu.h"
+
+struct req_ctx;
+
+extern void udp_open(void);
+extern int udp_refill_ep(int ep, struct req_ctx *rctx);
+extern void udp_unthrottle(void);
+extern void udp_reset(void);
+
+struct ep_ctx {
+ atomic_t pkts_in_transit;
+ void *ctx;
+};
+
+struct udp_pcd {
+ AT91PS_UDP pUdp;
+ unsigned char cur_config;
+ unsigned int cur_rcv_bank;
+ struct ep_ctx ep[4];
+};
+
+/* USB standard request code */
+
+#define STD_GET_STATUS_ZERO 0x0080
+#define STD_GET_STATUS_INTERFACE 0x0081
+#define STD_GET_STATUS_ENDPOINT 0x0082
+
+#define STD_CLEAR_FEATURE_ZERO 0x0100
+#define STD_CLEAR_FEATURE_INTERFACE 0x0101
+#define STD_CLEAR_FEATURE_ENDPOINT 0x0102
+
+#define STD_SET_FEATURE_ZERO 0x0300
+#define STD_SET_FEATURE_INTERFACE 0x0301
+#define STD_SET_FEATURE_ENDPOINT 0x0302
+
+#define STD_SET_ADDRESS 0x0500
+#define STD_GET_DESCRIPTOR 0x0680
+#define STD_SET_DESCRIPTOR 0x0700
+#define STD_GET_CONFIGURATION 0x0880
+#define STD_SET_CONFIGURATION 0x0900
+#define STD_GET_INTERFACE 0x0A81
+#define STD_SET_INTERFACE 0x0B01
+#define STD_SYNCH_FRAME 0x0C82
+
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+
+#endif
+
diff --git a/firmware/src/os/pio_irq.c b/firmware/src/os/pio_irq.c
new file mode 100644
index 0000000..8dae806
--- /dev/null
+++ b/firmware/src/os/pio_irq.c
@@ -0,0 +1,122 @@
+#include <errno.h>
+#include <sys/types.h>
+#include <lib_AT91SAM7.h>
+#include <os/pio_irq.h>
+#include <os/dbgu.h>
+#include <os/req_ctx.h>
+#include <openpcd.h>
+
+struct pioirq_state {
+ irq_handler_t *handlers[NR_PIO];
+ u_int32_t usbmask;
+ u_int32_t usb_throttled; /* atomic? */
+};
+
+static struct pioirq_state pirqs;
+
+static void pio_irq_demux(void)
+{
+ u_int32_t pio = AT91F_PIO_GetInterruptStatus(AT91C_BASE_PIOA);
+ u_int8_t send_usb = 0;
+ int i;
+
+ DEBUGPCRF("PIO_ISR_STATUS = 0x%08x", pio);
+
+ for (i = 0; i < NR_PIO; i++) {
+ if (pio & (1 << i) && pirqs.handlers[i])
+ pirqs.handlers[i](i);
+ if (pirqs.usbmask & (1 << i))
+ send_usb = 1;
+ }
+
+ if (send_usb && !pirqs.usb_throttled) {
+ struct req_ctx *irq_rctx;
+ irq_rctx = req_ctx_find_get(RCTX_STATE_FREE,
+ RCTX_STATE_PIOIRQ_BUSY);
+ if (!irq_rctx) {
+ /* we cannot disable the interrupt, since we have
+ * non-usb listeners */
+ pirqs.usb_throttled = 1;
+ } else {
+ struct openpcd_hdr *opcdh;
+ u_int32_t *regmask;
+ opcdh = (struct openpcd_hdr *) &irq_rctx->tx.data[0];
+ regmask = (u_int32_t *) (&irq_rctx->tx.data[0] + sizeof(*opcdh));
+ opcdh->cmd = OPENPCD_CMD_PIO_IRQ;
+ opcdh->reg = 0x00;
+ opcdh->flags = 0x00;
+ opcdh->val = 0x00;
+
+ irq_rctx->tx.tot_len = sizeof(*opcdh) + sizeof(u_int32_t);
+ req_ctx_set_state(irq_rctx, RCTX_STATE_UDP_EP3_PENDING);
+ }
+ }
+
+ AT91F_AIC_ClearIt(AT91C_BASE_AIC, AT91C_ID_PIOA);
+}
+
+void pio_irq_enable(u_int32_t pio)
+{
+ AT91F_PIO_InterruptEnable(AT91C_BASE_PIOA, pio);
+}
+
+void pio_irq_disable(u_int32_t pio)
+{
+ AT91F_PIO_InterruptDisable(AT91C_BASE_PIOA, pio);
+}
+
+int pio_irq_register(u_int32_t pio, irq_handler_t *handler)
+{
+ u_int8_t num = ffs(pio);
+
+ if (num == 0)
+ return -EINVAL;
+ num--;
+
+ if (pirqs.handlers[num])
+ return -EBUSY;
+
+ pio_irq_disable(pio);
+ AT91F_PIO_CfgInput(AT91C_BASE_PIOA, pio);
+ pirqs.handlers[num] = handler;
+
+ return 0;
+}
+
+void pio_irq_unregister(u_int32_t pio)
+{
+ u_int8_t num = ffs(pio);
+
+ if (num == 0)
+ return;
+ num--;
+
+ pio_irq_disable(pio);
+ pirqs.handlers[num] = NULL;
+}
+
+static int pio_irq_usb_in(struct req_ctx *rctx)
+{
+ struct openpcd_hdr *poh = (struct openpcd_hdr *) &rctx->rx.data[0];
+ struct openpcd_hdr *pih = (struct openpcd_hdr *) &rctx->tx.data[0];
+
+ switch (poh->cmd) {
+ case OPENPCD_CMD_PIO_IRQ:
+ pirqs.usbmask = poh->val;
+ break;
+ default:
+ DEBUGP("UNKNOWN ");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void pio_irq_init(void)
+{
+ AT91F_PIOA_CfgPMC();
+ AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_PIOA,
+ AT91C_AIC_PRIOR_LOWEST,
+ AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, &pio_irq_demux);
+ AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_PIOA);
+}
diff --git a/firmware/src/os/pio_irq.h b/firmware/src/os/pio_irq.h
new file mode 100644
index 0000000..33f4656
--- /dev/null
+++ b/firmware/src/os/pio_irq.h
@@ -0,0 +1,13 @@
+#ifndef _PIO_IRQ_H
+#define _PIO_IRQ_H
+
+#define NR_PIO 32
+typedef void irq_handler_t(u_int32_t pio);
+
+extern void pio_irq_enable(u_int32_t pio);
+extern void pio_irq_disable(u_int32_t pio);
+extern int pio_irq_register(u_int32_t pio, irq_handler_t *func);
+extern void pio_irq_unregister(u_int32_t pio);
+extern void pio_irq_init(void);
+
+#endif
diff --git a/firmware/src/os/pit.c b/firmware/src/os/pit.c
new file mode 100644
index 0000000..409faef
--- /dev/null
+++ b/firmware/src/os/pit.c
@@ -0,0 +1,38 @@
+
+
+#include <errno.h>
+#include <sys/types.h>
+#include <lib_AT91SAM7.h>
+#include <AT91SAM7.h>
+#include "../openpcd.h"
+
+/* PIT runs at MCK/16 (= 3MHz) */
+#define PIV_MS(x) (x * 3000)
+
+static void pit_irq(void)
+{
+ /* FIXME: do something */
+}
+
+void pit_mdelay(u_int32_t ms)
+{
+ u_int32_t end;
+
+ end = (AT91F_PITGetPIIR(AT91C_BASE_PITC) + ms) % 20;
+
+ while (end < AT91F_PITGetPIIR(AT91C_BASE_PITC)) { }
+}
+
+void pit_init(void)
+{
+ AT91F_PITC_CfgPMC();
+
+ AT91F_PITInit(AT91C_BASE_PITC, 1000 /* uS */, 48 /* MHz */);
+
+ AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_SYS,
+ OPENPCD_IRQ_PRIO_PIT,
+ AT91C_AIC_SRCTYPE_INT_POSITIVE_EDGE,
+ &pit_irq);
+
+ //AT91F_PITEnableInt(AT91C_BASE_PITC);
+}
diff --git a/firmware/src/os/pit.h b/firmware/src/os/pit.h
new file mode 100644
index 0000000..92426e9
--- /dev/null
+++ b/firmware/src/os/pit.h
@@ -0,0 +1,7 @@
+#ifndef _PIT_H
+#define _PIT_H
+
+extern void pit_init(void);
+extern void pit_mdelay(u_int32_t ms);
+
+#endif
diff --git a/firmware/src/os/power.h b/firmware/src/os/power.h
new file mode 100644
index 0000000..bfc6989
--- /dev/null
+++ b/firmware/src/os/power.h
@@ -0,0 +1,8 @@
+#ifndef _POWER_H
+
+static inline void cpu_idle(void)
+{
+ AT91F_PMC_DisablePCK(AT91C_BASE_PMC, AT91C_PMC_PCK);
+}
+
+#endif
diff --git a/firmware/src/os/pwm.c b/firmware/src/os/pwm.c
new file mode 100644
index 0000000..e05699b
--- /dev/null
+++ b/firmware/src/os/pwm.c
@@ -0,0 +1,165 @@
+/* AT91SAM7 PWM routines for OpenPCD / OpenPICC
+ *
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ *
+ */
+
+#include <lib_AT91SAM7.h>
+#include <AT91SAM7.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <openpcd.h>
+#include <os/usb_handler.h>
+#include <os/pcd_enumerate.h>
+#include <os/req_ctx.h>
+#include <os/dbgu.h>
+#include "../openpcd.h"
+
+#define Hz
+#define kHz *1000 Hz
+#define MHz *1000 kHz
+#define MCLK (48 MHz)
+
+#if 1
+#define DEBUGPWM DEBUGPCRF
+#else
+#define DEBUGPWM(x, args...)
+#endif
+
+static AT91PS_PWMC pwm = AT91C_BASE_PWMC;
+
+/* find highest bit set. returns bit (32..1) or 0 in case no bit set */
+static int fhs(u_int32_t val)
+{
+ int i;
+
+ for (i = 32; i > 0; i--) {
+ if (val & (1 << (i-1)))
+ return i;
+ }
+
+ return 0;
+}
+
+/* set frequency of PWM signal to freq */
+int pwm_freq_set(int channel, u_int32_t freq)
+{
+ /* in order to get maximum resolution, the pre-scaler must be set to
+ * something like freq << 16. However, the mimimum pre-scaled frequency
+ * we can get is MCLK (48MHz), the minimum is MCLK/(1024*255) =
+ * 48MHz/261120 = 183Hz */
+ u_int32_t overall_div;
+ u_int32_t presc_total;
+ u_int8_t cpre = 0;
+ u_int16_t cprd;
+
+ if (freq > MCLK)
+ return -ERANGE;
+
+ overall_div = MCLK / freq;
+ DEBUGPCRF("mclk=%u, freq=%u, overall_div=%u", MCLK, freq, overall_div);
+
+ if (overall_div > 0x7fff) {
+ /* divisor is larger than half the maximum CPRD register, we
+ * have to configure prescalers */
+ presc_total = overall_div >> 15;
+
+ /* find highest 2^n fitting in prescaler (highest bit set) */
+ cpre = fhs(presc_total);
+ if (cpre > 0) {
+ /* subtract one, because of fhs semantics */
+ cpre--;
+ }
+ cprd = overall_div / (1 << cpre);
+ } else
+ cprd = overall_div;
+
+ DEBUGPCRF("cpre=%u, cprd=%u", cpre, cprd);
+ AT91F_PWMC_CfgChannel(AT91C_BASE_PWMC, channel,
+ cpre|AT91C_PWMC_CPOL, cprd, 1);
+
+ return 0;
+}
+
+void pwm_start(int channel)
+{
+ AT91F_PWMC_StartChannel(AT91C_BASE_PWMC, (1 << channel));
+}
+
+void pwm_stop(int channel)
+{
+ AT91F_PWMC_StopChannel(AT91C_BASE_PWMC, (1 << channel));
+}
+
+void pwm_duty_set_percent(int channel, u_int16_t duty)
+{
+ u_int32_t tmp = pwm->PWMC_CH[channel].PWMC_CPRDR & 0xffff;
+
+ tmp = tmp << 16; /* extend value by 2^16 */
+ tmp = tmp / 100; /* tmp = 1 % of extended cprd */
+ tmp = duty * tmp; /* tmp = 'duty' % of extended cprd */
+ tmp = tmp >> 16; /* un-extend tmp (divide by 2^16) */
+
+ DEBUGPWM("Writing %u to Update register\n", tmp);
+ AT91F_PWMC_UpdateChannel(AT91C_BASE_PWMC, channel, tmp);
+}
+
+static int pwm_usb_in(struct req_ctx *rctx)
+{
+ struct openpcd_hdr *poh = (struct openpcd_hdr *) &rctx->rx.data[0];
+ /* struct openpcd_hdr *pih = (struct openpcd_hdr *) &rctx->tx.data[0]; */
+ u_int32_t *freq;
+
+ switch (poh->cmd) {
+ case OPENPCD_CMD_PWM_ENABLE:
+ if (poh->val)
+ pwm_start(0);
+ else
+ pwm_stop(0);
+ break;
+ case OPENPCD_CMD_PWM_DUTY_SET:
+ pwm_duty_set_percent(0, poh->val);
+ break;
+ case OPENPCD_CMD_PWM_DUTY_GET:
+ goto respond;
+ break;
+ case OPENPCD_CMD_PWM_FREQ_SET:
+ if (rctx->rx.tot_len < sizeof(*poh)+4)
+ break;
+ freq = (unsigned char *) poh + sizeof(*poh);
+ pwm_freq_set(0, *freq);
+ break;
+ case OPENPCD_CMD_PWM_FREQ_GET:
+ goto respond;
+ break;
+ default:
+ break;
+ }
+
+ req_ctx_put(rctx);
+ return 0;
+respond:
+ req_ctx_set_state(rctx, RCTX_STATE_UDP_EP2_PENDING);
+ udp_refill_ep(2, rctx);
+ return 1;
+}
+
+void pwm_init(void)
+{
+ /* IMPORTANT: Disable PA17 (SSC TD) output */
+ AT91F_PIO_CfgInput(AT91C_BASE_PIOA, AT91C_PIO_PA17);
+
+ /* Set PA23 to Peripheral A (PWM0) */
+ AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, 0, OPENPCD_PIO_MFIN_PWM);
+
+ /* Enable Clock for PWM controller */
+ AT91F_PWMC_CfgPMC();
+
+ usb_hdlr_register(&pwm_usb_in, OPENPCD_CMD_CLS_PWM);
+}
+
+void pwm_fini(void)
+{
+ usb_hdlr_unregister(OPENPCD_CMD_CLS_PWM);
+ AT91F_PMC_DisablePeriphClock(AT91C_BASE_PMC, (1 << AT91C_ID_PWMC));
+}
diff --git a/firmware/src/os/pwm.h b/firmware/src/os/pwm.h
new file mode 100644
index 0000000..8836c32
--- /dev/null
+++ b/firmware/src/os/pwm.h
@@ -0,0 +1,11 @@
+#ifndef _PWM_H
+#define _PWM_H
+
+extern void pwm_freq_set(int channel, u_int32_t freq);
+extern void pwm_start(int channel);
+extern void pwm_stop(int channel);
+extern void pwm_duty_set_percent(int channel, u_int16_t duty);
+extern void pwm_init(void);
+extern void pwm_fini(void);
+
+#endif
diff --git a/firmware/src/os/req_ctx.c b/firmware/src/os/req_ctx.c
new file mode 100644
index 0000000..99d248b
--- /dev/null
+++ b/firmware/src/os/req_ctx.c
@@ -0,0 +1,50 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <asm/bitops.h>
+#include <os/dbgu.h>
+#include <os/req_ctx.h>
+
+#include "../openpcd.h"
+
+/* FIXME: locking, FIFO order processing */
+
+static struct req_ctx req_ctx[NUM_REQ_CTX];
+
+struct req_ctx *req_ctx_find_get(unsigned long old_state, unsigned long new_state)
+{
+ unsigned long flags;
+ u_int8_t i;
+
+ for (i = 0; i < NUM_REQ_CTX; i++) {
+ local_irq_save(flags);
+ if (req_ctx[i].state == old_state) {
+ req_ctx[i].state = new_state;
+ local_irq_restore(flags);
+ return &req_ctx[i];
+ }
+ local_irq_restore(flags);
+ }
+
+ return NULL;
+}
+
+u_int8_t req_ctx_num(struct req_ctx *ctx)
+{
+ return ((char *)ctx - (char *)&req_ctx[0])/sizeof(*ctx);
+}
+
+void req_ctx_set_state(struct req_ctx *ctx, unsigned long new_state)
+{
+ unsigned long flags;
+
+ /* FIXME: do we need this kind of locking, we're UP! */
+ local_irq_save(flags);
+ ctx->state = new_state;
+ local_irq_restore(flags);
+}
+
+void req_ctx_put(struct req_ctx *ctx)
+{
+ req_ctx_set_state(ctx, RCTX_STATE_FREE);
+}
diff --git a/firmware/src/os/req_ctx.h b/firmware/src/os/req_ctx.h
new file mode 100644
index 0000000..82a133f
--- /dev/null
+++ b/firmware/src/os/req_ctx.h
@@ -0,0 +1,49 @@
+#ifndef _REQ_CTX_H
+#define _REQ_CTX_H
+
+#define MAX_HDRSIZE sizeof(struct openpcd_hdr)
+#define MAX_REQSIZE (64-MAX_HDRSIZE)
+
+#define req_buf_payload(x) (x->data[x->hdr_len])
+#define req_buf_hdr(x) (x->data[0])
+
+#include <sys/types.h>
+
+struct req_buf {
+ u_int16_t hdr_len;
+ u_int16_t tot_len;
+ u_int8_t data[64];
+};
+
+struct req_ctx {
+ u_int16_t seq; /* request sequence number */
+ u_int16_t flags;
+ volatile u_int32_t state;
+ struct req_buf rx;
+ struct req_buf tx;
+};
+
+#define RCTX_STATE_FREE 0x00
+#define RCTX_STATE_UDP_RCV_BUSY 0x01
+#define RCTX_STATE_UDP_RCV_DONE 0x02
+#define RCTX_STATE_MAIN_PROCESSING 0x03
+#define RCTX_STATE_RC632IRQ_BUSY 0x04
+
+#define RCTX_STATE_UDP_EP2_PENDING 0x10
+#define RCTX_STATE_UDP_EP2_BUSY 0x11
+
+#define RCTX_STATE_UDP_EP3_PENDING 0x12
+#define RCTX_STATE_UDP_EP3_BUSY 0x13
+
+#define RCTX_STATE_SSC_RX_BUSY 0x20
+
+#define RCTX_STATE_PIOIRQ_BUSY 0x80
+
+#define NUM_REQ_CTX 8
+extern struct req_ctx *req_ctx_find_get(unsigned long old_state, unsigned long new_state);
+extern struct req_ctx *req_ctx_find_busy(void);
+extern void req_ctx_set_state(struct req_ctx *ctx, unsigned long new_state);
+extern void req_ctx_put(struct req_ctx *ctx);
+extern u_int8_t req_ctx_num(struct req_ctx *ctx);
+
+#endif /* _REQ_CTX_H */
diff --git a/firmware/src/os/syscalls.c b/firmware/src/os/syscalls.c
new file mode 100644
index 0000000..ed989f1
--- /dev/null
+++ b/firmware/src/os/syscalls.c
@@ -0,0 +1,169 @@
+/***********************************************************************/
+/* */
+/* SYSCALLS.C: System Calls */
+/* most of this is from newlib-lpc and a Keil-demo */
+/* */
+/* These are "reentrant functions" as needed by */
+/* the WinARM-newlib-config, see newlib-manual. */
+/* Collected and modified by Martin Thomas */
+/* */
+/***********************************************************************/
+
+
+#include <stdlib.h>
+#include <reent.h>
+#include <sys/stat.h>
+
+#include <board.h>
+
+static void my_putc(char c)
+{
+ while (!AT91F_US_TxReady((AT91PS_USART)AT91C_BASE_DBGU));
+ AT91F_US_PutChar((AT91PS_USART)AT91C_BASE_DBGU, c);
+}
+
+static int my_kbhit( void )
+{
+ if ((AT91F_US_RxReady((AT91PS_USART)AT91C_BASE_DBGU)) == 0) return 0;
+ else return 1;
+}
+
+static char my_getc( void )
+{
+ return AT91F_US_GetChar((AT91PS_USART)AT91C_BASE_DBGU);
+}
+
+_ssize_t _read_r(
+ struct _reent *r,
+ int file,
+ void *ptr,
+ size_t len)
+{
+ char c;
+ int i;
+ unsigned char *p;
+
+ p = (unsigned char*)ptr;
+
+ for (i = 0; i < len; i++) {
+ // c = uart0Getch();
+ // c = uart0GetchW();
+ while ( !my_kbhit() ) ;
+ c = (char) my_getc();
+ if (c == 0x0D) {
+ *p='\0';
+ break;
+ }
+ *p++ = c;
+ ////// uart0_putc(c);
+ }
+ return len - i;
+}
+
+
+_ssize_t _write_r (
+ struct _reent *r,
+ int file,
+ const void *ptr,
+ size_t len)
+{
+ int i;
+ const unsigned char *p;
+
+ p = (const unsigned char*) ptr;
+
+ for (i = 0; i < len; i++) {
+ if (*p == '\n' ) my_putc('\r');
+ my_putc(*p++);
+ }
+
+ return len;
+}
+
+
+int _close_r(
+ struct _reent *r,
+ int file)
+{
+ return 0;
+}
+
+
+_off_t _lseek_r(
+ struct _reent *r,
+ int file,
+ _off_t ptr,
+ int dir)
+{
+ return (_off_t)0; /* Always indicate we are at file beginning. */
+}
+
+
+int _fstat_r(
+ struct _reent *r,
+ int file,
+ struct stat *st)
+{
+ /* Always set as character device. */
+ st->st_mode = S_IFCHR;
+ /* assigned to strong type with implicit */
+ /* signed/unsigned conversion. Required by */
+ /* newlib. */
+
+ return 0;
+}
+
+
+int isatty(int file); /* avoid warning */
+
+int isatty(int file)
+{
+ return 1;
+}
+
+
+#if 0
+static void _exit (int n) {
+label: goto label; /* endless loop */
+}
+#endif
+
+
+/* "malloc clue function" from newlib-lpc/Keil-Demo/"generic" */
+
+/**** Locally used variables. ****/
+// mt: "cleaner": extern char* end;
+extern char end[]; /* end is set in the linker command */
+ /* file and is the end of statically */
+ /* allocated data (thus start of heap). */
+
+static char *heap_ptr; /* Points to current end of the heap. */
+
+/************************** _sbrk_r *************************************
+ * Support function. Adjusts end of heap to provide more memory to
+ * memory allocator. Simple and dumb with no sanity checks.
+
+ * struct _reent *r -- re-entrancy structure, used by newlib to
+ * support multiple threads of operation.
+ * ptrdiff_t nbytes -- number of bytes to add.
+ * Returns pointer to start of new heap area.
+ *
+ * Note: This implementation is not thread safe (despite taking a
+ * _reent structure as a parameter).
+ * Since _s_r is not used in the current implementation,
+ * the following messages must be suppressed.
+ */
+void * _sbrk_r(
+ struct _reent *_s_r,
+ ptrdiff_t nbytes)
+{
+ char *base; /* errno should be set to ENOMEM on error */
+
+ if (!heap_ptr) { /* Initialize if first time through. */
+ heap_ptr = end;
+ }
+ base = heap_ptr; /* Point to end of heap. */
+ heap_ptr += nbytes; /* Increase heap. */
+
+ return base; /* Return pointer to start of new heap area. */
+}
diff --git a/firmware/src/os/tc_cdiv.c b/firmware/src/os/tc_cdiv.c
new file mode 100644
index 0000000..6c4024c
--- /dev/null
+++ b/firmware/src/os/tc_cdiv.c
@@ -0,0 +1,92 @@
+/* OpenPC TC (Timer / Clock) support code
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ *
+ * This idea of this code is to feed the 13.56MHz carrier clock of RC632
+ * into TCLK1, which is routed to XC1. Then configure TC0 to divide this
+ * clock by a configurable divider.
+ *
+ */
+
+#include <lib_AT91SAM7.h>
+#include <AT91SAM7.h>
+#include <os/dbgu.h>
+
+#include "../openpcd.h"
+#include <os/tc_cdiv.h>
+
+static AT91PS_TCB tcb = AT91C_BASE_TCB;
+
+/* set carrier divider to a specific */
+void tc_cdiv_set_divider(u_int16_t div)
+{
+ tcb->TCB_TC0.TC_RC = div;
+
+ /* set to 50% duty cycle */
+ tcb->TCB_TC0.TC_RA = 1;
+ tcb->TCB_TC0.TC_RB = 1 + (div >> 1);
+}
+
+void tc_cdiv_phase_add(int16_t inc)
+{
+ tcb->TCB_TC0.TC_RA = (tcb->TCB_TC0.TC_RA + inc) % tcb->TCB_TC0.TC_RC;
+ tcb->TCB_TC0.TC_RB = (tcb->TCB_TC0.TC_RB + inc) % tcb->TCB_TC0.TC_RC;
+
+ /* FIXME: can this be done more elegantly? */
+ if (tcb->TCB_TC0.TC_RA == 0) {
+ tcb->TCB_TC0.TC_RA += 1;
+ tcb->TCB_TC0.TC_RB += 1;
+ }
+}
+
+void tc_cdiv_init(void)
+{
+ /* Cfg PA28(TCLK1), PA0(TIOA0), PA1(TIOB0), PA20(TCLK2) as Periph B */
+ AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, 0,
+ OPENPCD_PIO_CARRIER_IN |
+ OPENPCD_PIO_CARRIER_DIV_OUT |
+ OPENPCD_PIO_CDIV_HELP_OUT |
+ OPENPCD_PIO_CDIV_HELP_IN);
+
+ AT91F_PMC_EnablePeriphClock(AT91C_BASE_PMC,
+ ((unsigned int) 1 << AT91C_ID_TC0));
+
+ /* Enable Clock for TC0 */
+ tcb->TCB_TC0.TC_CCR = AT91C_TC_CLKEN;
+
+ /* Connect TCLK1 to XC1, TCLK2 to XC2 */
+ tcb->TCB_BMR &= ~(AT91C_TCB_TC1XC1S | AT91C_TCB_TC2XC2S);
+ tcb->TCB_BMR |= (AT91C_TCB_TC1XC1S_TCLK1 | AT91C_TCB_TC2XC2S_TCLK2);
+
+ /* Clock XC1, Wave mode, Reset on RC comp
+ * TIOA0 on RA comp = set, * TIOA0 on RC comp = clear,
+ * TIOB0 on EEVT = set, TIOB0 on RB comp = clear,
+ * EEVT = XC2 (TIOA0) */
+ tcb->TCB_TC0.TC_CMR = AT91C_TC_CLKS_XC1 | AT91C_TC_WAVE |
+ AT91C_TC_WAVESEL_UP_AUTO |
+ AT91C_TC_ACPA_SET | AT91C_TC_ACPC_CLEAR |
+ AT91C_TC_BEEVT_SET | AT91C_TC_BCPB_CLEAR |
+ AT91C_TC_EEVT_XC2 | AT91C_TC_ETRGEDG_RISING;
+
+ tc_cdiv_set_divider(128);
+
+ /* Reset to start timers */
+ tcb->TCB_BCR = 1;
+}
+
+void tc_cdiv_print(void)
+{
+ DEBUGP("TCB_BMR=0x%08x ", tcb->TCB_BMR);
+ DEBUGP("TC0_CV=0x%08x ", tcb->TCB_TC0.TC_CV);
+ DEBUGP("TC0_CMR=0x%08x ", tcb->TCB_TC0.TC_CMR);
+ DEBUGPCR("TC0_SR=0x%08x", tcb->TCB_TC0.TC_SR);
+
+ DEBUGPCR("TC0_RA=0x%04x, TC0_RB=0x%04x, TC0_RC=0x%04x",
+ tcb->TCB_TC0.TC_RA, tcb->TCB_TC0.TC_RB, tcb->TCB_TC0.TC_RC);
+}
+
+void tc_cdiv_fini(void)
+{
+ tcb->TCB_TC0.TC_CCR = AT91C_TC_CLKDIS;
+ AT91F_PMC_DisablePeriphClock(AT91C_BASE_PMC,
+ ((unsigned int) 1 << AT91C_ID_TC0));
+}
diff --git a/firmware/src/os/tc_cdiv.h b/firmware/src/os/tc_cdiv.h
new file mode 100644
index 0000000..4f2bc02
--- /dev/null
+++ b/firmware/src/os/tc_cdiv.h
@@ -0,0 +1,26 @@
+#ifndef _TC_CDIV_H
+#define _TC_CDIV_H
+
+#include <sys/types.h>
+#include <lib_AT91SAM7.h>
+
+static AT91PS_TCB tcb;
+
+extern void tc_cdiv_phase_add(int16_t inc);
+extern void tc_cdiv_set_divider(u_int16_t div);
+
+static inline void tc_cdiv_phase_inc(void)
+{
+ tc_cdiv_phase_add(1);
+}
+
+static inline void tc_cdiv_phase_dec(void)
+{
+ tc_cdiv_phase_add(-1);
+}
+
+extern void tc_cdiv_print(void);
+extern void tc_cdiv_init(void);
+extern void tc_cdiv_fini(void);
+
+#endif
diff --git a/firmware/src/os/trigger.c b/firmware/src/os/trigger.c
new file mode 100644
index 0000000..b8cedf3
--- /dev/null
+++ b/firmware/src/os/trigger.c
@@ -0,0 +1,17 @@
+#include <lib_AT91SAM7.h>
+#include <os/trigger.h>
+#include "../openpcd.h"
+
+void trigger_init(void)
+{
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPCD_PIO_TRIGGER);
+}
+
+void trigger_pulse(void)
+{
+ volatile int i;
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPCD_PIO_TRIGGER);
+ for (i=0; i < 0xff; i++)
+ { }
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPCD_PIO_TRIGGER);
+}
diff --git a/firmware/src/os/trigger.h b/firmware/src/os/trigger.h
new file mode 100644
index 0000000..597704b
--- /dev/null
+++ b/firmware/src/os/trigger.h
@@ -0,0 +1,7 @@
+#ifndef _TRIGGER_H
+#define _TRIGGER_H
+
+extern void trigger_init(void);
+extern void trigger_pulse(void);
+
+#endif
diff --git a/firmware/src/os/usb_benchmark.c b/firmware/src/os/usb_benchmark.c
new file mode 100644
index 0000000..3637bc3
--- /dev/null
+++ b/firmware/src/os/usb_benchmark.c
@@ -0,0 +1,57 @@
+#include <errno.h>
+#include <string.h>
+#include <lib_AT91SAM7.h>
+#include <os/led.h>
+#include <os/pcd_enumerate.h>
+#include <os/usb_handler.h>
+#include <os/req_ctx.h>
+#include "../openpcd.h"
+
+static struct req_ctx dummy_rctx;
+static struct req_ctx empty_rctx;
+
+static void usbtest_tx_transfer(unsigned int num_pkts)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_pkts; i++) {
+ /* send 16 packets of 64byte */
+ while (udp_refill_ep(2, &dummy_rctx) < 0)
+ ;
+ }
+ /* send one packet of 0 byte */
+ while (udp_refill_ep(2, &empty_rctx) < 0)
+ ;
+}
+
+static int usbtest_rx(struct req_ctx *rctx)
+{
+ struct openpcd_hdr *poh = (struct openpcd_hdr *) &rctx->rx.data[0];
+ int i;
+
+ switch (poh->cmd) {
+ case OPENPCD_CMD_USBTEST_IN:
+ /* test bulk in pipe */
+ for (i = 0; i < poh->reg; i++) {
+ usbtest_tx_transfer(poh->val);
+ led_toggle(2);
+ }
+ break;
+ case OPENPCD_CMD_USBTEST_OUT:
+ /* test bulk out pipe */
+ break;
+ }
+
+ req_ctx_put(rctx);
+ return 1;
+}
+
+void usbtest_init(void)
+{
+ dummy_rctx.tx.tot_len = 64;
+ memset(dummy_rctx.tx.data, 0x23, 64);
+
+ empty_rctx.tx.tot_len = 0;
+
+ usb_hdlr_register(&usbtest_rx, OPENPCD_CMD_CLS_USBTEST);
+}
diff --git a/firmware/src/os/usb_handler.c b/firmware/src/os/usb_handler.c
new file mode 100644
index 0000000..274353c
--- /dev/null
+++ b/firmware/src/os/usb_handler.c
@@ -0,0 +1,88 @@
+/* OpenPCD USB handler - handle incoming USB requests on OUT pipe
+ * (C) 2006 by Harald Welte
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <string.h>
+
+#include <openpcd.h>
+
+#include <os/pcd_enumerate.h>
+#include <os/usb_handler.h>
+#include <os/req_ctx.h>
+#include <os/led.h>
+#include <os/dbgu.h>
+
+#include "../openpcd.h"
+
+static usb_cmd_fn *cmd_hdlrs[16];
+
+int usb_hdlr_register(usb_cmd_fn *hdlr, u_int8_t class)
+{
+ cmd_hdlrs[class] = hdlr;
+ return 0;
+}
+
+void usb_hdlr_unregister(u_int8_t class)
+{
+ cmd_hdlrs[class] = NULL;
+}
+
+static int usb_in(struct req_ctx *rctx)
+{
+ struct openpcd_hdr *poh = (struct openpcd_hdr *) &rctx->rx.data[0];
+ struct openpcd_hdr *pih = (struct openpcd_hdr *) &rctx->tx.data[0];
+ usb_cmd_fn *hdlr;
+
+ DEBUGP("usb_in(cls=%d) ", OPENPCD_CMD_CLS(poh->cmd));
+
+ if (rctx->rx.tot_len < sizeof(*poh))
+ return -EINVAL;
+
+ memcpy(pih, poh, sizeof(*poh));
+ rctx->tx.tot_len = sizeof(*poh);
+
+ hdlr = cmd_hdlrs[OPENPCD_CMD_CLS(poh->cmd)];
+ if (hdlr)
+ return (hdlr)(rctx);
+ else
+ DEBUGPCR("no handler for this class\n");
+}
+
+/* Process all pending request contexts that want to Tx on either
+ * IN or INTERRUPT endpoint */
+void usb_out_process(void)
+{
+ struct req_ctx *rctx;
+
+ while (rctx = req_ctx_find_get(RCTX_STATE_UDP_EP3_PENDING,
+ RCTX_STATE_UDP_EP3_BUSY)) {
+ DEBUGPCRF("EP3_BUSY for ctx %u", req_ctx_num(rctx));
+ if (udp_refill_ep(3, rctx) < 0)
+ req_ctx_set_state(rctx, RCTX_STATE_UDP_EP3_PENDING);
+ }
+
+ while (rctx = req_ctx_find_get(RCTX_STATE_UDP_EP2_PENDING,
+ RCTX_STATE_UDP_EP2_BUSY)) {
+ DEBUGPCRF("EP2_BUSY for ctx %u", req_ctx_num(rctx));
+ if (udp_refill_ep(2, rctx) < 0)
+ req_ctx_set_state(rctx, RCTX_STATE_UDP_EP2_PENDING);
+ }
+}
+
+/* process incoming USB packets (OUT pipe) that have already been
+ * put into request contexts by the UDP IRQ handler */
+void usb_in_process(void)
+{
+ struct req_ctx *rctx;
+
+ while (rctx = req_ctx_find_get(RCTX_STATE_UDP_RCV_DONE,
+ RCTX_STATE_MAIN_PROCESSING)) {
+ DEBUGPCRF("found used ctx %u: len=%u",
+ req_ctx_num(rctx), rctx->rx.tot_len);
+ usb_in(rctx);
+ }
+ udp_unthrottle();
+}
+
diff --git a/firmware/src/os/usb_handler.h b/firmware/src/os/usb_handler.h
new file mode 100644
index 0000000..3efcc1f
--- /dev/null
+++ b/firmware/src/os/usb_handler.h
@@ -0,0 +1,17 @@
+#ifndef _USB_HANDLER_H
+#define _USB_HANDLER_H
+
+#include "openpcd.h"
+#include <os/req_ctx.h>
+
+#define MAX_PAYLOAD_LEN (64 - sizeof(struct openpcd_hdr))
+
+typedef int usb_cmd_fn(struct req_ctx *rctx);
+
+extern int usb_hdlr_register(usb_cmd_fn *hdlr, u_int8_t class);
+extern void usb_hdlr_unregister(u_int8_t class);
+
+extern void usb_in_process(void);
+extern void usb_out_process(void);
+
+#endif
diff --git a/firmware/src/os/wdt.c b/firmware/src/os/wdt.c
new file mode 100644
index 0000000..d8a2145
--- /dev/null
+++ b/firmware/src/os/wdt.c
@@ -0,0 +1,23 @@
+/* AT91SAM7 Watch Dog Timer code for OpenPCD / OpenPICC
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ */
+
+#define WDT_DEBUG
+
+void wdt_irq(void)
+{
+ DEBUGPCRF("================> WATCHDOG EXPIRED !!!!!");
+}
+
+void wdt_init(void)
+{
+#ifdef WDT_DEBUG
+ AT91F_WDTSetMode(AT91C_BASE_WDT, (0xfff << 16) |
+ AT91C_WDTC_WDDBGHLT | AT91C_WDTC_WDIDLEHLT |
+ AT91C_WDTC_WDFIEN);
+#else
+ AT91F_WDTSetMode(AT91C_BASE_WDT, (0xfff << 16) |
+ AT91C_WDTC_WDDBGHLT | AT91C_WDTC_WDIDLEHLT |
+ AT91C_WDTC_WDRSTEN);
+#endif
+}
diff --git a/firmware/src/pcd.h b/firmware/src/pcd.h
new file mode 100644
index 0000000..77c99b2
--- /dev/null
+++ b/firmware/src/pcd.h
@@ -0,0 +1,35 @@
+#ifndef _OPENPCD_H
+#define _OPENPCD_H
+/* pcd.h - OpenPCD USB protocol definitions
+ * (C) 2006 Harald Welte <laforge@gnumonks.org>
+ */
+
+#include <sys/types.h>
+
+struct opcd_cmd_hdr {
+ u_int8_t cmd;
+ u_int8_t arg1;
+ u_int16_t arg2;
+} __attribute__ ((packed));
+
+enum opcd_cmd {
+ OPCD_CMD_REG_READ = 0x01, /* Transparent Read of RC632 REG */
+ OPCD_CMD_REG_WRITE = 0x02, /* Transparent Write to RC632 REG */
+
+ OPCD_CMD_FIFO_READ = 0x03, /* Transparent Read fron RC632 FIFO */
+ OPCD_CMD_FIFO_WRITE = 0x04, /* Transparent Write to RC632 FIFO */
+
+ OPCD_CMD_VFIFO_READ = 0x05, /* Read bytes from virtual FIFO */
+ OPCD_CMD_VFIFO_WRITE = 0x06, /* Write bytes to virtual FIFO */
+ OPCD_CMD_VFIFO_MODE = 0x07, /* Set Virtual FIFO mode */
+
+ OPCD_CMD_REG_SETBIT = 0x08, /* Set a bit in RC632 Register */
+ OPCD_CMD_REG_CLRBIT = 0x09, /* Clear a bit in RC632 Register */
+};
+
+struct opcd_status_hdr {
+ u_int8_t cause, /* interrupt cause register RC632 */
+ u_int8_t prim_status, /* primary status register RC632 */
+} __attribute__ ((packed));
+
+#endif /* _OPENPCD_H */
diff --git a/firmware/src/pcd/main_analog.c b/firmware/src/pcd/main_analog.c
new file mode 100644
index 0000000..8ffa1d4
--- /dev/null
+++ b/firmware/src/pcd/main_analog.c
@@ -0,0 +1,85 @@
+/* main_reqa - OpenPCD firmware for generating an endless loop of
+ * ISO 14443-A REQA packets.
+ *
+ * If a response is received from the PICC, LED1 (Red) will be switched
+ * on. If no valid response has been received within the timeout of the
+ * receiver, LED1 (Red) will be switched off.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <librfid/rfid_layer2_iso14443a.h>
+#include "rc632.h"
+#include <os/dbgu.h>
+#include <os/led.h>
+#include <os/trigger.h>
+#include <os/pcd_enumerate.h>
+#include <os/main.h>
+
+void _init_func(void)
+{
+ trigger_init();
+ rc632_init();
+ DEBUGPCRF("turning on RF");
+ rc632_turn_on_rf(RAH);
+ /* FIXME: do we need this? */
+ DEBUGPCRF("initializing 14443A operation");
+ rc632_iso14443a_init(RAH);
+ /* Switch to 848kBps (1subcp / bit) */
+ //rc632_clear_bits(RAH, RC632_REG_RX_CONTROL1, RC632_RXCTRL1_SUBCP_MASK);
+}
+
+int _main_dbgu(char key)
+{
+ static char ana_out_sel;
+ int ret = -EINVAL;
+
+ switch (key) {
+ case 'q':
+ ana_out_sel--;
+ ret = 1;
+ break;
+ case 'w':
+ ana_out_sel++;
+ ret = 1;
+ break;
+ case 'c':
+ rc632_turn_on_rf(RAH);
+ break;
+ case 'o':
+ rc632_turn_off_rf(RAH);
+ break;
+ }
+
+ if (ana_out_sel >= 0xd)
+ ana_out_sel = 0;
+
+ if (ret == 1) {
+ ana_out_sel &= 0x0f;
+ DEBUGPCR("switching to analog output mode 0x%x\n", ana_out_sel);
+ rc632_reg_write(RAH, RC632_REG_TEST_ANA_SELECT, ana_out_sel);
+ }
+
+ return ret;
+}
+
+void _main_func(void)
+{
+#if 1
+ struct iso14443a_atqa atqa;
+
+ memset(&atqa, 0, sizeof(atqa));
+
+ trigger_pulse();
+
+ if (rc632_iso14443a_transceive_sf(RAH, ISO14443A_SF_CMD_WUPA, &atqa) < 0) {
+ DEBUGPCRF("error during transceive_sf");
+ led_switch(1, 0);
+ } else {
+ DEBUGPCRF("received ATQA: %s\n", hexdump((char *)&atqa, sizeof(atqa)));
+ led_switch(1, 1);
+ }
+#endif
+ led_toggle(2);
+}
diff --git a/firmware/src/pcd/main_dumbreader.c b/firmware/src/pcd/main_dumbreader.c
new file mode 100644
index 0000000..1535e27
--- /dev/null
+++ b/firmware/src/pcd/main_dumbreader.c
@@ -0,0 +1,63 @@
+#include <errno.h>
+#include <include/lib_AT91SAM7.h>
+#include <include/openpcd.h>
+#include <os/dbgu.h>
+#include "rc632.h"
+#include <os/led.h>
+#include <os/pcd_enumerate.h>
+#include <os/usb_handler.h>
+#include "../openpcd.h"
+#include <os/main.h>
+
+void _init_func(void)
+{
+ rc632_init();
+ rc632_test(RAH);
+}
+
+int _main_dbgu(char key)
+{
+ unsigned char value;
+
+ switch (key) {
+ case '4':
+ AT91F_DBGU_Printk("Testing RC632 : ");
+ if (rc632_test(RAH) == 0)
+ AT91F_DBGU_Printk("SUCCESS!\n\r");
+ else
+ AT91F_DBGU_Printk("ERROR!\n\r");
+
+ break;
+ case '5':
+ rc632_reg_read(RAH, RC632_REG_RX_WAIT, &value);
+ DEBUGPCR("Reading RC632 Reg RxWait: 0x%02xr", value);
+
+ break;
+ case '6':
+ DEBUGPCR("Writing RC632 Reg RxWait: 0x55");
+ rc632_reg_write(RAH, RC632_REG_RX_WAIT, 0x55);
+ break;
+ case '7':
+ rc632_dump();
+ break;
+ case 'P':
+ rc632_power(1);
+ break;
+ case 'p':
+ rc632_power(0);
+ 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();
+
+ rc632_unthrottle();
+}
diff --git a/firmware/src/pcd/main_pwm.c b/firmware/src/pcd/main_pwm.c
new file mode 100644
index 0000000..2f92d28
--- /dev/null
+++ b/firmware/src/pcd/main_pwm.c
@@ -0,0 +1,257 @@
+/* main_pwm - OpenPCD firmware for generating a PWM-modulated 13.56MHz
+ * carrier
+ *
+ * To use this, you need to connect PIOA P0 with MFIN of the reader.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <lib_AT91SAM7.h>
+#include "../openpcd.h"
+#include <pcd/rc632.h>
+#include <os/dbgu.h>
+#include <os/led.h>
+#include <os/pwm.h>
+#include <os/tc_cdiv.h>
+#include <os/pcd_enumerate.h>
+#include <os/usb_handler.h>
+
+#ifdef SSC
+#include <pcd/ssc.h>
+#endif
+
+static u_int8_t force_100ask = 1;
+static u_int8_t mod_conductance = 0x3f;
+static u_int8_t cw_conductance = 0x3f;
+static u_int16_t duty_percent = 22;
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+static u_int32_t pwm_freq[] = { 105937, 211875, 423750, 847500 };
+static u_int8_t pwm_freq_idx = 0;
+
+static void rc632_modulate_mfin()
+{
+ rc632_reg_write(RAH, RC632_REG_TX_CONTROL,
+ RC632_TXCTRL_MOD_SRC_MFIN|RC632_TXCTRL_TX2_INV|
+ RC632_TXCTRL_TX1_RF_EN|RC632_TXCTRL_TX2_RF_EN);
+}
+
+/* (77/40) ^ EXPcsCfgCW */
+/* 1, 1.925, 3.705625, 7.13332812 */
+
+#define COND_MANT(x) (x & 0x0f)
+#define COND_EXP(x) ((x & 0x30) >> 4)
+
+static const u_int16_t rsrel_expfact[] = { 1000, 1925, 3706, 7133 };
+
+static u_int32_t calc_conduct_rel(u_int8_t inp)
+{
+ u_int32_t cond_rel;
+
+ cond_rel = COND_MANT(inp) * rsrel_expfact[COND_EXP(inp)];
+ cond_rel = cond_rel / 1000;
+
+ return cond_rel;
+}
+
+static const u_int8_t rsrel_table[] = {
+ 0, 16, 32, 48, 1, 17, 2, 3, 33, 18, 4, 5, 19, 6, 7, 49, 34, 20,
+ 8, 9, 21, 10, 11, 35, 22, 12, 13, 23, 14, 50, 36, 15, 24, 25,
+ 37, 26, 27, 51, 38, 28, 29, 39, 30, 52, 31, 40, 41, 53, 42, 43,
+ 54, 44, 45, 55, 46, 47, 56, 57, 58, 59, 60, 61, 62, 63 };
+
+
+static const u_int16_t cdivs[] = { 128, 64, 32, 16 };
+static int cdiv_idx = 0;
+
+static void help(void)
+{
+ DEBUGPCR("o: decrease duty p: increase duty\r\n"
+ "k: stop pwm l: start pwn\r\n"
+ "n: decrease freq m: incresae freq\r\n"
+ "v: decrease mod_cond b: increase mod_cond\r\n"
+ "g: decrease cw_cond h: increase cw_cond");
+ DEBUGPCR("u: PA23 const 1 y: PA23 const 0\r\n"
+ "t: PA23 PWM0 f: toggle Force100ASK\r\n"
+ "{: decrease cdiv_idx }: increse cdiv idx");
+}
+
+void _init_func(void)
+{
+ DEBUGPCR("\r\n===> main_pwm <===\r\n");
+ help();
+
+ rc632_init();
+ DEBUGPCRF("turning on RF");
+ rc632_turn_on_rf(RAH);
+
+ /* switch PA17 (connected to MFIN on board) to input */
+ AT91F_PIO_CfgInput(AT91C_BASE_PIOA, AT91C_PIO_PA17);
+
+ DEBUGPCRF("Initializing carrier divider");
+ tc_cdiv_init();
+
+ DEBUGPCRF("Initializing PWM");
+ pwm_init();
+ pwm_freq_set(0, 105937);
+ pwm_start(0);
+ pwm_duty_set_percent(0, 22); /* 22% of 9.43uS = 2.07uS */
+ rc632_modulate_mfin();
+
+#ifdef SSC
+ DEBUGPCRF("Initializing SSC RX");
+ ssc_rx_init();
+#endif
+}
+
+int _main_dbgu(char key)
+{
+ switch (key) {
+ case 'o':
+ if (duty_percent >= 1)
+ duty_percent--;
+ pwm_duty_set_percent(0, duty_percent);
+ break;
+ case 'p':
+ if (duty_percent <= 99)
+ duty_percent++;
+ pwm_duty_set_percent(0, duty_percent);
+ break;
+ case 'k':
+ pwm_stop(0);
+ break;
+ case 'l':
+ pwm_start(0);
+ break;
+ case 'n':
+ if (pwm_freq_idx > 0) {
+ pwm_freq_idx--;
+ pwm_stop(0);
+ pwm_freq_set(0, pwm_freq[pwm_freq_idx]);
+ pwm_start(0);
+ pwm_duty_set_percent(0, 22); /* 22% of 9.43uS = 2.07uS */
+ }
+ break;
+ case 'm':
+ if (pwm_freq_idx < ARRAY_SIZE(pwm_freq)-1) {
+ pwm_freq_idx++;
+ pwm_stop(0);
+ pwm_freq_set(0, pwm_freq[pwm_freq_idx]);
+ pwm_start(0);
+ pwm_duty_set_percent(0, 22); /* 22% of 9.43uS = 2.07uS */
+ }
+ break;
+ case 'u':
+ DEBUGPCRF("PA23 output high");
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, AT91C_PIO_PA23);
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, AT91C_PIO_PA23);
+ break;
+ case 'y':
+ DEBUGPCRF("PA23 output low");
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, AT91C_PIO_PA23);
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, AT91C_PIO_PA23);
+ return 0;
+ break;
+ case 't':
+ DEBUGPCRF("PA23 PeriphA (PWM)");
+ AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, 0, AT91C_PA23_PWM0);
+ return 0;
+ break;
+ case 'f':
+ DEBUGPCRF("%sabling Force100ASK", force_100ask ? "Dis":"En");
+ if (force_100ask) {
+ force_100ask = 0;
+ rc632_clear_bits(RAH, RC632_REG_TX_CONTROL,
+ RC632_TXCTRL_FORCE_100_ASK);
+ } else {
+ force_100ask = 1;
+ rc632_set_bits(RAH, RC632_REG_TX_CONTROL,
+ RC632_TXCTRL_FORCE_100_ASK);
+ }
+ return 0;
+ break;
+ case 'v':
+ if (mod_conductance > 0) {
+ mod_conductance--;
+ rc632_reg_write(RAH, RC632_REG_MOD_CONDUCTANCE,
+ rsrel_table[mod_conductance]);
+ }
+ break;
+ case 'b':
+ if (mod_conductance < 0x3f) {
+ mod_conductance++;
+ rc632_reg_write(RAH, RC632_REG_MOD_CONDUCTANCE,
+ rsrel_table[mod_conductance]);
+ }
+ break;
+ case 'g':
+ if (cw_conductance > 0) {
+ cw_conductance--;
+ rc632_reg_write(RAH, RC632_REG_CW_CONDUCTANCE,
+ rsrel_table[cw_conductance]);
+ }
+ break;
+ case 'h':
+ if (cw_conductance < 0x3f) {
+ cw_conductance++;
+ rc632_reg_write(RAH, RC632_REG_CW_CONDUCTANCE,
+ rsrel_table[cw_conductance]);
+ }
+ break;
+ case '?':
+ help();
+ return 0;
+ break;
+ case '<':
+ tc_cdiv_phase_inc();
+ break;
+ case '>':
+ tc_cdiv_phase_dec();
+ break;
+ case '{':
+ if (cdiv_idx > 0)
+ cdiv_idx--;
+ tc_cdiv_set_divider(cdivs[cdiv_idx]);
+ break;
+ case '}':
+ if (cdiv_idx < ARRAY_SIZE(cdivs)-1)
+ cdiv_idx++;
+ tc_cdiv_set_divider(cdivs[cdiv_idx]);
+ break;
+#ifdef SSC
+ case 's':
+ ssc_rx_start();
+ break;
+ case 'S':
+ ssc_rx_stop();
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ DEBUGPCR("pwm_freq=%u, duty_percent=%u, mod_cond=%u, cw_cond=%u tc_cdiv=%u",
+ pwm_freq[pwm_freq_idx], duty_percent, mod_conductance, cw_conductance,
+ cdivs[cdiv_idx]);
+
+ return 0;
+}
+
+
+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();
+
+ /* try unthrottling sources since we now are [more] likely to
+ * have empty request contexts */
+ rc632_unthrottle();
+#ifdef SSC
+ ssc_rx_unthrottle();
+#endif
+
+ led_toggle(2);
+}
diff --git a/firmware/src/pcd/main_reqa.c b/firmware/src/pcd/main_reqa.c
new file mode 100644
index 0000000..e28fdff
--- /dev/null
+++ b/firmware/src/pcd/main_reqa.c
@@ -0,0 +1,265 @@
+/* main_reqa - OpenPCD firmware for generating an endless loop of
+ * ISO 14443-A REQA packets. Alternatively we can send WUPA, or
+ * perform a full ISO14443A anti-collision loop.
+ *
+ * If a response is received from the PICC, LED1 (Red) will be switched
+ * on. If no valid response has been received within the timeout of the
+ * receiver, LED1 (Red) will be switched off.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <lib_AT91SAM7.h>
+#include <librfid/rfid_layer2_iso14443a.h>
+#include "rc632.h"
+#include <os/dbgu.h>
+#include <os/led.h>
+#include <os/pcd_enumerate.h>
+#include <os/trigger.h>
+
+#include "../openpcd.h"
+
+#ifdef WITH_TC
+#include "tc.h"
+#endif
+
+void _init_func(void)
+{
+ trigger_init();
+ DEBUGPCRF("enabling RC632");
+ rc632_init();
+#ifdef WITH_TC
+ DEBUGPCRF("enabling TC");
+ tc_cdiv_init();
+#endif
+ DEBUGPCRF("turning on RF");
+ rc632_turn_on_rf(RAH);
+ DEBUGPCRF("initializing 14443A operation");
+ rc632_iso14443a_init(RAH);
+}
+
+#define MODE_REQA 0x01
+#define MODE_WUPA 0x02
+#define MODE_ANTICOL 0x03
+#define MODE_14443A 0x04
+
+static volatile int mode = MODE_REQA;
+
+static const char frame_14443a[] = { 0x00, 0xff, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+
+static void reg_inc(u_int8_t reg)
+{
+ u_int8_t val;
+ rc632_reg_read(RAH, reg, &val);
+ rc632_reg_write(RAH, reg, val++);
+ DEBUGPCRF("reg 0x%02x = 0x%02x", reg, val);
+}
+
+static void reg_dec(u_int8_t reg)
+{
+ u_int8_t val;
+ rc632_reg_read(RAH, reg, &val);
+ rc632_reg_write(RAH, reg, val--);
+ DEBUGPCRF("reg 0x%02x = 0x%02x", reg, val);
+}
+
+static u_int8_t ana_out_sel;
+static u_int8_t mfout_sel;
+static u_int8_t speed_idx;
+
+static void help(void)
+{
+ DEBUGPCR("r: REQA w: WUPA a: ANTICOL\r\n"
+ "A: 14443A +: inc speed -: dec speed\r\n"
+ "y: inc cw cond x: dec cond c: inc mod cond");
+ DEBUGPCR("v: dec mod cond o: dec ana_out p: dec ana_out\r\n"
+ "h: trigger high l: trigger low u: dec MFOUT mode");
+ DEBUGPCR("i: inc MFOUT md <: dec cdiv ph >: inc cdiv phase\r\n"
+ "{: dev cdiv }: inc cdiv");
+}
+
+static u_int16_t cdivs[] = { 128, 64, 32, 16 };
+
+int _main_dbgu(char key)
+{
+ int ret = 0;
+ static int cdiv_idx = 0;
+
+ switch (key) {
+ case '?':
+ help();
+ break;
+ case 'r':
+ mode = MODE_REQA;
+ break;
+ case 'w':
+ mode = MODE_WUPA;
+ break;
+ case 'A':
+ mode = MODE_14443A;
+ break;
+ case 'a':
+ mode = MODE_ANTICOL;
+ break;
+ /* Those below don't work as long as
+ * iso14443a_init() is called before
+ * every cycle */
+ case 'y':
+ reg_inc(RC632_REG_CW_CONDUCTANCE);
+ break;
+ case 'x':
+ reg_dec(RC632_REG_CW_CONDUCTANCE);
+ break;
+ case 'c':
+ reg_inc(RC632_REG_MOD_CONDUCTANCE);
+ break;
+ case 'v':
+ reg_dec(RC632_REG_MOD_CONDUCTANCE);
+ break;
+ case 'o':
+ if (ana_out_sel > 0) {
+ ana_out_sel--;
+ DEBUGPCR("switching to analog output mode 0x%x\n", ana_out_sel);
+ rc632_reg_write(RAH, RC632_REG_TEST_ANA_SELECT, ana_out_sel);
+ }
+ ret = 1;
+ break;
+ case 'p':
+ if (ana_out_sel < 0xc) {
+ ana_out_sel++;
+ DEBUGPCR("switching to analog output mode 0x%x\n", ana_out_sel);
+ rc632_reg_write(RAH, RC632_REG_TEST_ANA_SELECT, ana_out_sel);
+ }
+ ret = 1;
+ break;
+ case 'u':
+ if (mfout_sel > 0) {
+ mfout_sel--;
+ DEBUGPCR("switching to MFOUT mode 0x%x\n", mfout_sel);
+ rc632_reg_write(RAH, RC632_REG_MFOUT_SELECT, mfout_sel);
+ }
+ ret = 1;
+ break;
+ case 'i':
+ if (mfout_sel < 5) {
+ mfout_sel++;
+ DEBUGPCR("switching to MFOUT mode 0x%x\n", mfout_sel);
+ rc632_reg_write(RAH, RC632_REG_MFOUT_SELECT, mfout_sel);
+ }
+ ret = 1;
+ break;
+ case 'h':
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPCD_PIO_TRIGGER);
+ break;
+ case 'l':
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPCD_PIO_TRIGGER);
+ break;
+#ifdef WITH_TC
+ case '<':
+ tc_cdiv_phase_inc();
+ break;
+ case '>':
+ tc_cdiv_phase_dec();
+ break;
+ case '{':
+ if (cdiv_idx > 0)
+ cdiv_idx--;
+ tc_cdiv_set_divider(cdivs[cdiv_idx]);
+ break;
+ case '}':
+ if (cdiv_idx < ARRAY_SIZE(cdivs)-1)
+ cdiv_idx++;
+ tc_cdiv_set_divider(cdivs[cdiv_idx]);
+ break;
+#endif
+ case '-':
+ if (speed_idx > 0)
+ speed_idx--;
+ break;
+ case '+':
+ if (speed_idx < 3)
+ speed_idx++;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+void _main_func(void)
+{
+ int status;
+ struct iso14443a_atqa atqa;
+ struct rfid_layer2_handle l2h;
+ volatile int i;
+
+ memset(&atqa, 0, sizeof(atqa));
+
+ /* fake layer2 handle initialization */
+ memset(&l2h, 0, sizeof(l2h));
+ l2h.l2 = &rfid_layer2_iso14443a;
+ l2h.priv.iso14443a.state = ISO14443A_STATE_NONE;
+ l2h.priv.iso14443a.level = ISO14443A_LEVEL_NONE;
+
+ /* FIXME: why does this only work every second attempt without reset or
+ * power-cycle? */
+ rc632_turn_off_rf();
+ //rc632_reset();
+ rc632_turn_on_rf();
+
+ rc632_iso14443a_init(RAH);
+ rc632_reg_write(RAH, RC632_REG_TEST_ANA_SELECT, ana_out_sel);
+ rc632_reg_write(RAH, RC632_REG_MFOUT_SELECT, mfout_sel);
+ for (i = 0; i < 0x3ffff; i++) {}
+ //rc632_dump();
+#ifdef WITH_TC
+ tc_cdiv_print();
+#endif
+
+ switch (mode) {
+ case MODE_REQA:
+ status = rc632_iso14443a_transceive_sf(RAH, ISO14443A_SF_CMD_REQA, &atqa);
+ if (status < 0)
+ DEBUGPCRF("error during transceive_sf REQA");
+ else
+ DEBUGPCRF("received ATQA: %s", hexdump((char *)&atqa, sizeof(atqa)));
+ break;
+ case MODE_WUPA:
+ status = rc632_iso14443a_transceive_sf(RAH, ISO14443A_SF_CMD_WUPA, &atqa);
+ if (status < 0)
+ DEBUGPCRF("error during transceive_sf WUPA");
+ else
+ DEBUGPCRF("received WUPA: %s", hexdump((char *)&atqa, sizeof(atqa)));
+ break;
+ case MODE_ANTICOL:
+ status = rfid_layer2_iso14443a.fn.open(&l2h);
+ if (status < 0)
+ DEBUGPCR("error during anticol");
+ else
+ DEBUGPCR("Anticol OK");
+ break;
+ case MODE_14443A:
+ {
+ char rx_buf[4];
+ int rx_len = sizeof(rx_buf);
+ rfid_layer2_iso14443a.fn.setopt(&l2h, RFID_OPT_14443A_SPEED_RX,
+ &speed_idx, sizeof(speed_idx));
+ rfid_layer2_iso14443a.fn.setopt(&l2h, RFID_OPT_14443A_SPEED_TX,
+ &speed_idx, sizeof(speed_idx));
+ rfid_layer2_iso14443a.fn.transceive(&l2h, RFID_14443A_FRAME_REGULAR,
+ &frame_14443a, sizeof(frame_14443a),
+ &rx_buf, &rx_len, 1, 0);
+ }
+ break;
+ }
+
+ if (status < 0)
+ led_switch(1, 0);
+ else
+ led_switch(1, 1);
+
+ led_toggle(2);
+
+}
diff --git a/firmware/src/pcd/rc632.c b/firmware/src/pcd/rc632.c
new file mode 100644
index 0000000..5d45955
--- /dev/null
+++ b/firmware/src/pcd/rc632.c
@@ -0,0 +1,624 @@
+/* Philips CL RC632 driver (via SPI) for OpenPCD firmware
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ *
+ * This is heavily based on the librfid RC632 driver. All primitive access
+ * functions such as rc632_{reg,fifo}_{read,write}() are API compatible to
+ * librfid in order to be able to leverage higher-level code from librfid
+ * to this OpenPCD firmware.
+ *
+ * */
+
+#include <string.h>
+#include <errno.h>
+#include <lib_AT91SAM7.h>
+#include <cl_rc632.h>
+#include <openpcd.h>
+#include "../openpcd.h"
+#include <os/fifo.h>
+#include <os/dbgu.h>
+#include <os/pcd_enumerate.h>
+#include <os/usb_handler.h>
+#include <os/req_ctx.h>
+#include "rc632.h"
+
+#define ALWAYS_RESPOND
+
+#define NOTHING do {} while(0)
+
+#if 0
+#define DEBUGPSPI DEBUGP
+#define DEBUGPSPIIRQ DEBUGP
+#else
+#define DEBUGPSPI(x, args ...) NOTHING
+#define DEBUGPSPIIRQ(x, args...) NOTHING
+#endif
+
+#if 0
+#define DEBUG632 DEBUGPCRF
+#else
+#define DEBUG632(x, args ...) NOTHING
+#endif
+
+
+/* SPI driver */
+
+#ifdef OLIMEX
+#define SPI_DEBUG_LOOPBACK
+#endif
+
+#define SPI_USES_DMA
+
+#define SPI_MAX_XFER_LEN 65
+
+static const AT91PS_SPI pSPI = AT91C_BASE_SPI;
+
+/* SPI irq handler */
+static void spi_irq(void)
+{
+ u_int32_t status = pSPI->SPI_SR;
+
+ DEBUGPSPIIRQ("spi_irq: 0x%08x ", status);
+
+ if (status & AT91C_SPI_OVRES)
+ DEBUGPSPIIRQ("Overrun ");
+ if (status & AT91C_SPI_MODF)
+ DEBUGPSPIIRQ("ModeFault ");
+ if (status & AT91C_SPI_ENDRX) {
+ pSPI->SPI_IDR = AT91C_SPI_ENDRX;
+ DEBUGPSPIIRQ("ENDRX ");
+ }
+ if (status & AT91C_SPI_ENDTX) {
+ pSPI->SPI_IDR = AT91C_SPI_ENDTX;
+ DEBUGPSPIIRQ("ENDTX ");
+ }
+
+ DEBUGPSPIIRQ("\r\n");
+
+ AT91F_AIC_ClearIt(AT91C_BASE_AIC, AT91C_ID_SPI);
+}
+
+#ifdef SPI_USES_DMA
+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)
+{
+ //tx_len = *rx_len = 65;
+ DEBUGPSPI("DMA Xfer tx=%s\r\n", hexdump(tx_data, tx_len));
+ if (*rx_len < tx_len) {
+ DEBUGPCRF("rx_len=%u smaller tx_len=%u\n", *rx_len, tx_len);
+ return -1;
+ }
+ //AT91F_SPI_Disable(pSPI);
+
+ AT91F_SPI_ReceiveFrame(pSPI, rx_data, tx_len, NULL, 0);
+ AT91F_SPI_SendFrame(pSPI, tx_data, tx_len, NULL, 0);
+
+ AT91F_PDC_EnableRx(AT91C_BASE_PDC_SPI);
+ AT91F_PDC_EnableTx(AT91C_BASE_PDC_SPI);
+
+ pSPI->SPI_IER = AT91C_SPI_ENDTX|AT91C_SPI_ENDRX;
+ //pSPI->SPI_IDR = AT91C_SPI_ENDTX|AT91C_SPI_ENDRX;
+
+
+ while (! (pSPI->SPI_SR & AT91C_SPI_ENDRX)) ;
+
+ DEBUGPSPI("DMA Xfer finished rx=%s\r\n", hexdump(rx_data, tx_len));
+
+ *rx_len = tx_len;
+
+ return 0;
+}
+#else
+/* stupid polling transceiver routine */
+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;
+
+ /* disable RC632 interrupt because it wants to do SPI transactions */
+ AT91F_AIC_DisableIt(AT91C_BASE_AIC, OPENPCD_IRQ_RC632);
+
+ 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");
+
+ /* Re-enable RC632 interrupts */
+ AT91F_AIC_EnableIt(AT91C_BASE_AIC, OPENPCD_IRQ_RC632);
+
+ return 0;
+}
+#endif
+
+/* RC632 driver */
+
+/* static buffers used by RC632 access primitives below.
+ * Since we only have one */
+
+static u_int8_t spi_outbuf[SPI_MAX_XFER_LEN];
+static u_int8_t spi_inbuf[SPI_MAX_XFER_LEN];
+
+#define FIFO_ADDR (RC632_REG_FIFO_DATA << 1)
+
+struct rc632 {
+ u_int16_t flags;
+ struct fifo fifo;
+};
+#define RC632_F_FIFO_TX 0x0001
+static struct rc632 rc632;
+
+/* RC632 access primitives */
+
+int rc632_reg_write(struct rfid_asic_handle *hdl,
+ u_int8_t addr, u_int8_t data)
+{
+ u_int16_t rx_len = 2;
+
+ DEBUG632("[0x%02x] <= 0x%02x", addr, data);
+
+ addr = (addr << 1) & 0x7e;
+
+ spi_outbuf[0] = addr;
+ spi_outbuf[1] = data;
+
+ //spi_transceive(spi_outbuf, 2, NULL, NULL);
+ return spi_transceive(spi_outbuf, 2, spi_inbuf, &rx_len);
+}
+
+int rc632_fifo_write(struct rfid_asic_handle *hdl,
+ u_int8_t len, u_int8_t *data, u_int8_t flags)
+{
+ if (len > sizeof(spi_outbuf)-1)
+ len = sizeof(spi_outbuf)-1;
+
+ spi_outbuf[0] = FIFO_ADDR;
+ memcpy(&spi_outbuf[1], data, len);
+
+ DEBUG632("[FIFO] <= %s", hexdump(data, len));
+
+ return spi_transceive(spi_outbuf, len+1, NULL, NULL);
+}
+
+int rc632_reg_read(struct rfid_asic_handle *hdl,
+ u_int8_t addr, u_int8_t *val)
+{
+ u_int16_t rx_len = 2;
+
+ addr = (addr << 1) & 0x7e;
+
+ spi_outbuf[0] = addr | 0x80;
+ spi_outbuf[1] = 0x00;
+
+ spi_transceive(spi_outbuf, 2, spi_inbuf, &rx_len);
+ *val = spi_inbuf[1];
+
+ DEBUG632("[0x%02x] => 0x%02x", addr>>1, *val);
+
+ return 0;
+}
+
+int rc632_fifo_read(struct rfid_asic_handle *hdl,
+ u_int8_t max_len, u_int8_t *data)
+{
+ int ret;
+ u_int8_t fifo_length;
+ u_int8_t i;
+ u_int16_t rx_len;
+
+ ret = rc632_reg_read(hdl, RC632_REG_FIFO_LENGTH, &fifo_length);
+ if (ret < 0)
+ return ret;
+
+ rx_len = fifo_length+1;
+
+ if (max_len < fifo_length)
+ fifo_length = max_len;
+
+ for (i = 0; i < fifo_length; i++)
+ spi_outbuf[i] = FIFO_ADDR;
+
+ spi_outbuf[0] |= 0x80;
+ spi_outbuf[fifo_length] = 0x00;
+
+ spi_transceive(spi_outbuf, fifo_length+1, spi_inbuf, &rx_len);
+ memcpy(data, spi_inbuf+1, rx_len-1);
+
+ DEBUG632("[FIFO] => %s", hexdump(data, rx_len-1));
+
+ return 0;
+}
+
+int rc632_set_bits(struct rfid_asic_handle *hdl,
+ u_int8_t reg, u_int8_t bits)
+{
+ u_int8_t val;
+ int ret;
+
+ ret = rc632_reg_read(hdl, reg, &val);
+ if (ret < 0)
+ return ret;
+
+ val |= bits;
+
+ return rc632_reg_write(hdl, reg, val);
+}
+
+int rc632_clear_bits(struct rfid_asic_handle *hdl,
+ u_int8_t reg, u_int8_t bits)
+{
+ u_int8_t val;
+ int ret;
+
+ ret = rc632_reg_read(hdl, reg, &val);
+ if (ret < 0)
+ return ret;
+
+ val &= ~bits;
+
+ return rc632_reg_write(hdl, reg, val);
+}
+
+/* RC632 interrupt handling */
+
+static void rc632_irq(void)
+{
+ struct req_ctx *irq_rctx;
+ struct openpcd_hdr *irq_opcdh;
+ u_int8_t cause;
+
+ /* CL RC632 has interrupted us */
+ rc632_reg_read(RAH, RC632_REG_INTERRUPT_RQ, &cause);
+
+ /* ACK all interrupts */
+ //rc632_reg_write(RAH, RC632_REG_INTERRUPT_RQ, cause);
+ rc632_reg_write(RAH, RC632_REG_INTERRUPT_RQ, RC632_INT_TIMER);
+ DEBUGP("rc632_irq: ");
+
+ if (cause & RC632_INT_LOALERT) {
+ /* FIFO is getting low, refill from virtual FIFO */
+ DEBUGP("FIFO_low ");
+ #if 0
+ if (!fifo_available(&rc632.fifo))
+ return;
+ #endif
+ /* FIXME */
+ }
+ if (cause & RC632_INT_HIALERT) {
+ /* FIFO is getting full, empty into virtual FIFO */
+ DEBUGP("FIFO_high ");
+ /* FIXME */
+ }
+ /* All interrupts below can be reported directly to the host */
+ if (cause & RC632_INT_TIMER)
+ DEBUGP("Timer ");
+ if (cause & RC632_INT_IDLE)
+ DEBUGP("Idle ");
+ if (cause & RC632_INT_RX)
+ DEBUGP("RxComplete ");
+ if (cause & RC632_INT_TX)
+ DEBUGP("TxComplete ");
+
+
+ irq_rctx = req_ctx_find_get(RCTX_STATE_FREE,
+ RCTX_STATE_RC632IRQ_BUSY);
+ if (!irq_rctx) {
+ DEBUGPCRF("NO RCTX!\n");
+ /* disable rc632 interrupt until RCTX is free */
+ AT91F_AIC_DisableIt(AT91C_BASE_AIC, OPENPCD_IRQ_RC632);
+ return;
+ }
+
+ irq_opcdh = (struct openpcd_hdr *) &irq_rctx->tx.data[0];
+
+ /* initialize static part of openpcd_hdr for USB IRQ reporting */
+ irq_opcdh->cmd = OPENPCD_CMD_IRQ;
+ irq_opcdh->flags = 0x00;
+ irq_opcdh->reg = 0x07;
+ irq_opcdh->val = cause;
+
+ req_ctx_set_state(irq_rctx, RCTX_STATE_UDP_EP3_PENDING);
+ DEBUGPCR("");
+}
+
+void rc632_unthrottle(void)
+{
+ AT91F_AIC_EnableIt(AT91C_BASE_AIC, OPENPCD_IRQ_RC632);
+}
+
+void rc632_power(u_int8_t up)
+{
+ DEBUGPCRF("powering %s RC632", up ? "up" : "down");
+ if (up)
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA,
+ OPENPCD_PIO_RC632_RESET);
+ else
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA,
+ OPENPCD_PIO_RC632_RESET);
+}
+
+void rc632_reset(void)
+{
+ volatile int i;
+
+ rc632_power(0);
+ for (i = 0; i < 0xffff; i++)
+ {}
+ rc632_power(1);
+
+ /* wait for startup phase to finish */
+ while (1) {
+ u_int8_t val;
+ rc632_reg_read(RAH, RC632_REG_COMMAND, &val);
+ if (val == 0x00)
+ break;
+ }
+
+ /* turn off register paging */
+ rc632_reg_write(RAH, RC632_REG_PAGE0, 0x00);
+}
+
+static int rc632_usb_in(struct req_ctx *rctx)
+{
+ struct openpcd_hdr *poh = (struct openpcd_hdr *) &rctx->rx.data[0];
+ struct openpcd_hdr *pih = (struct openpcd_hdr *) &rctx->tx.data[0];
+ u_int16_t len = rctx->rx.tot_len-sizeof(*poh);
+
+ switch (poh->cmd) {
+ case OPENPCD_CMD_READ_REG:
+ rc632_reg_read(RAH, poh->reg, &pih->val);
+ DEBUGP("READ REG(0x%02x)=0x%02x ", poh->reg, pih->val);
+ goto respond;
+ break;
+ case OPENPCD_CMD_READ_FIFO:
+ {
+ u_int16_t req_len = poh->val, remain_len = req_len, pih_len;
+ if (req_len > MAX_PAYLOAD_LEN) {
+ pih_len = MAX_PAYLOAD_LEN;
+ remain_len -= pih_len;
+ rc632_fifo_read(RAH, pih_len, pih->data);
+ rctx->tx.tot_len += pih_len;
+ DEBUGP("READ FIFO(len=%u)=%s ", req_len,
+ hexdump(pih->data, pih_len));
+ req_ctx_set_state(rctx, RCTX_STATE_UDP_EP2_PENDING);
+ udp_refill_ep(2, rctx);
+
+ /* get and initialize second rctx */
+ rctx = req_ctx_find_get(RCTX_STATE_FREE,
+ RCTX_STATE_MAIN_PROCESSING);
+ if (!rctx) {
+ DEBUGPCRF("FATAL_NO_RCTX!!!\n");
+ break;
+ }
+ poh = (struct openpcd_hdr *) &rctx->rx.data[0];
+ pih = (struct openpcd_hdr *) &rctx->tx.data[0];
+ memcpy(pih, poh, sizeof(*poh));
+ rctx->tx.tot_len = sizeof(*poh);
+
+ pih_len = remain_len;
+ rc632_fifo_read(RAH, pih->val, pih->data);
+ rctx->tx.tot_len += pih_len;
+ DEBUGP("READ FIFO(len=%u)=%s ", pih_len,
+ hexdump(pih->data, pih_len));
+ /* don't set state of second rctx, main function
+ * body will do this after switch statement */
+ } else {
+ pih->val = poh->val;
+ rc632_fifo_read(RAH, poh->val, pih->data);
+ rctx->tx.tot_len += pih_len;
+ DEBUGP("READ FIFO(len=%u)=%s ", poh->val,
+ hexdump(pih->data, poh->val));
+ }
+ goto respond;
+ break;
+ }
+ case OPENPCD_CMD_WRITE_REG:
+ DEBUGP("WRITE_REG(0x%02x, 0x%02x) ", poh->reg, poh->val);
+ rc632_reg_write(RAH, poh->reg, poh->val);
+ break;
+ case OPENPCD_CMD_WRITE_FIFO:
+ DEBUGP("WRITE FIFO(len=%u): %s ", len,
+ hexdump(poh->data, len));
+ rc632_fifo_write(RAH, len, poh->data, 0);
+ break;
+ case OPENPCD_CMD_READ_VFIFO:
+ DEBUGP("READ VFIFO ");
+ DEBUGP("NOT IMPLEMENTED YET ");
+ goto respond;
+ break;
+ case OPENPCD_CMD_WRITE_VFIFO:
+ DEBUGP("WRITE VFIFO ");
+ DEBUGP("NOT IMPLEMENTED YET ");
+ break;
+ case OPENPCD_CMD_REG_BITS_CLEAR:
+ DEBUGP("CLEAR BITS ");
+ pih->val = rc632_clear_bits(RAH, poh->reg, poh->val);
+ break;
+ case OPENPCD_CMD_REG_BITS_SET:
+ DEBUGP("SET BITS ");
+ pih->val = rc632_set_bits(RAH, poh->reg, poh->val);
+ break;
+ case OPENPCD_CMD_DUMP_REGS:
+ DEBUGP("DUMP REGS ");
+ DEBUGP("NOT IMPLEMENTED YET ");
+ goto respond;
+ break;
+ default:
+ DEBUGP("UNKNOWN ");
+ return -EINVAL;
+ }
+
+#ifdef ALWAYS_RESPOND
+ goto respond;
+#endif
+
+ req_ctx_put(rctx);
+ DEBUGPCR("");
+ return 0;
+
+respond:
+ req_ctx_set_state(rctx, RCTX_STATE_UDP_EP2_PENDING);
+ /* FIXME: we could try to send this immediately */
+ udp_refill_ep(2, rctx);
+ DEBUGPCR("");
+
+ return 1;
+}
+
+void rc632_init(void)
+{
+ //fifo_init(&rc632.fifo, 256, NULL, &rc632);
+
+ DEBUGPCRF("entering");
+
+ AT91F_SPI_CfgPMC();
+
+ AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA,
+ AT91C_PA11_NPCS0|AT91C_PA12_MISO|
+ AT91C_PA13_MOSI |AT91C_PA14_SPCK, 0);
+
+ 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);
+
+ AT91F_SPI_EnableIt(pSPI, AT91C_SPI_MODF|AT91C_SPI_OVRES);
+#ifdef SPI_USES_DMA
+ AT91F_SPI_EnableIt(pSPI, AT91C_SPI_ENDRX|AT91C_SPI_ENDTX);
+#endif
+
+#ifdef SPI_DEBUG_LOOPBACK
+ AT91F_SPI_CfgMode(pSPI, AT91C_SPI_MSTR|AT91C_SPI_PS_FIXED|
+ AT91C_SPI_MODFDIS|AT91C_SPI_LLB);
+#else
+ AT91F_SPI_CfgMode(pSPI, AT91C_SPI_MSTR|AT91C_SPI_PS_FIXED|
+ AT91C_SPI_MODFDIS);
+#endif
+ /* CPOL = 0, NCPHA = 1, CSAAT = 0, BITS = 0000, SCBR = 10 (4.8MHz),
+ * DLYBS = 0, DLYBCT = 0 */
+#ifdef SPI_USES_DMA
+ AT91F_SPI_CfgCs(pSPI, 0, AT91C_SPI_BITS_8|AT91C_SPI_NCPHA|(10<<8));
+#else
+ /* 320 kHz in case of I/O based SPI */
+ AT91F_SPI_CfgCs(pSPI, 0, AT91C_SPI_BITS_8|AT91C_SPI_NCPHA|(0x7f<<8));
+#endif
+ AT91F_SPI_Enable(pSPI);
+
+ /* Register rc632_irq */
+ AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, OPENPCD_IRQ_RC632,
+ OPENPCD_IRQ_PRIO_RC632,
+ AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, &rc632_irq);
+ AT91F_AIC_EnableIt(AT91C_BASE_AIC, OPENPCD_IRQ_RC632);
+
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPCD_PIO_RC632_RESET);
+
+ rc632_reset();
+
+ /* configure IRQ pin */
+ rc632_reg_write(RAH, RC632_REG_IRQ_PIN_CONFIG,
+ RC632_IRQCFG_CMOS|RC632_IRQCFG_INV);
+ /* enable interrupts */
+ rc632_reg_write(RAH, RC632_REG_INTERRUPT_EN, RC632_INT_TIMER);
+
+ /* configure AUX to test signal four */
+ rc632_reg_write(RAH, RC632_REG_TEST_ANA_SELECT, 0x04);
+
+ usb_hdlr_register(&rc632_usb_in, OPENPCD_CMD_CLS_RC632);
+};
+
+#if 0
+void rc632_exit(void)
+{
+ usb_hdlr_unregister(OPENPCD_CMD_CLS_RC632);
+ AT91F_AIC_DisableIt(AT91C_BASE_AIC, OPENPCD_IRQ_RC632);
+ AT91F_AIC_DisableIt(AT91C_BASE_AIC, AT91C_ID_SPI);
+ AT91F_SPI_Disable(pSPI);
+}
+#endif
+
+#ifdef DEBUG
+static int rc632_reg_write_verify(struct rfid_asic_handle *hdl,
+ u_int8_t reg, u_int8_t val)
+{
+ u_int8_t tmp;
+
+ rc632_reg_write(hdl, reg, val);
+ rc632_reg_read(hdl, reg, &tmp);
+
+ DEBUGPCRF("reg=0x%02x, write=0x%02x, read=0x%02x ", reg, val, tmp);
+
+ return (val == tmp);
+}
+
+int rc632_dump(void)
+{
+ u_int8_t i;
+ u_int16_t rx_len = sizeof(spi_inbuf);
+
+ for (i = 0; i <= 0x3f; i++) {
+ u_int8_t reg = i;
+ if (reg == RC632_REG_FIFO_DATA)
+ reg = 0x3e;
+
+ spi_outbuf[i] = reg << 1;
+ spi_inbuf[i] = 0x00;
+ }
+
+ /* MSB of first byte of read spi transfer is high */
+ spi_outbuf[0] |= 0x80;
+
+ /* last byte of read spi transfer is 0x00 */
+ spi_outbuf[0x40] = 0x00;
+ spi_inbuf[0x40] = 0x00;
+
+ spi_transceive(spi_outbuf, 0x41, spi_inbuf, &rx_len);
+
+ for (i = 0; i < 0x3f; i++) {
+ if (i == RC632_REG_FIFO_DATA)
+ DEBUGPCR("REG 0x02 = NOT READ");
+ else
+ DEBUGPCR("REG 0x%02x = 0x%02x", i, spi_inbuf[i+1]);
+ }
+
+ return 0;
+}
+
+int rc632_test(struct rfid_asic_handle *hdl)
+{
+ if (rc632_reg_write_verify(hdl, RC632_REG_RX_WAIT, 0x55) != 1)
+ return -1;
+
+ if (rc632_reg_write_verify(hdl, RC632_REG_RX_WAIT, 0xAA) != 1)
+ return -1;
+
+ return 0;
+}
+#else /* DEBUG */
+int rc632_test(struct rfid_asic_handle *hdl) {}
+int rc632_dump(void) {}
+#endif /* DEBUG */
diff --git a/firmware/src/pcd/rc632.h b/firmware/src/pcd/rc632.h
new file mode 100644
index 0000000..1a4dc22
--- /dev/null
+++ b/firmware/src/pcd/rc632.h
@@ -0,0 +1,31 @@
+#ifndef _RC623_API_H
+#define _RC632_API_H
+
+#include <sys/types.h>
+#include <cl_rc632.h>
+#include <librfid/rfid.h>
+
+extern int rc632_reg_write(struct rfid_asic_handle *hdl,
+ u_int8_t addr, u_int8_t data);
+extern int rc632_fifo_write(struct rfid_asic_handle *hdl,
+ u_int8_t len, u_int8_t *data, u_int8_t flags);
+extern int rc632_reg_read(struct rfid_asic_handle *hdl,
+ u_int8_t addr, u_int8_t *val);
+extern int rc632_fifo_read(struct rfid_asic_handle *hdl,
+ u_int8_t max_len, u_int8_t *data);
+extern int rc632_clear_bits(struct rfid_asic_handle *hdl,
+ u_int8_t reg, u_int8_t bits);
+extern int rc632_set_bits(struct rfid_asic_handle *hdl,
+ u_int8_t reg, u_int8_t bits);
+
+extern void rc632_init(void);
+extern void rc632_exit(void);
+
+extern void rc632_unthrottle(void);
+
+#ifdef DEBUG
+extern int rc632_test(struct rfid_asic_handle *hdl);
+extern int rc632_dump(void);
+#endif
+
+#endif
diff --git a/firmware/src/pcd/rc632_highlevel.c b/firmware/src/pcd/rc632_highlevel.c
new file mode 100644
index 0000000..b1186ab
--- /dev/null
+++ b/firmware/src/pcd/rc632_highlevel.c
@@ -0,0 +1,1473 @@
+/* Generic Philips CL RC632 Routines
+ *
+ * (C) Harald Welte <laforge@gnumonks.org>
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation
+ *
+ * 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 <sys/types.h>
+#include <string.h>
+#include <errno.h>
+#include <cl_rc632.h>
+#include "rc632.h"
+#include <os/dbgu.h>
+#include <librfid/rfid_layer2_iso14443a.h>
+#include <librfid/rfid_protocol_mifare_classic.h>
+
+/* initially we use the same values as cm5121 */
+#define OPENPCD_CW_CONDUCTANCE 0x3f
+#define OPENPCD_MOD_CONDUCTANCE 0x3f
+#define OPENPCD_14443A_BITPHASE 0xa9
+#define OPENPCD_14443A_THRESHOLD 0xff
+#define OPENPCD_14443B_BITPHASE 0xad
+#define OPENPCD_14443B_THRESHOLD 0xff
+
+#define RC632_TMO_AUTH1 14000
+
+#define RC632_TIMEOUT_FUZZ_FACTOR 10
+
+#define USE_IRQ
+
+#define ENTER() DEBUGPCRF("entering")
+struct rfid_asic rc632;
+
+static int
+rc632_set_bit_mask(struct rfid_asic_handle *handle,
+ u_int8_t reg, u_int8_t mask, u_int8_t val)
+{
+ int ret;
+ u_int8_t tmp;
+
+ ret = rc632_reg_read(handle, reg, &tmp);
+ if (ret < 0)
+ return ret;
+
+ /* if bits are already like we want them, abort */
+ if ((tmp & mask) == val)
+ return 0;
+
+ return rc632_reg_write(handle, reg, (tmp & ~mask)|(val & mask));
+}
+
+int
+rc632_turn_on_rf(struct rfid_asic_handle *handle)
+{
+ ENTER();
+ return rc632_set_bits(handle, RC632_REG_TX_CONTROL, 0x03);
+}
+
+int
+rc632_turn_off_rf(struct rfid_asic_handle *handle)
+{
+ ENTER();
+ return rc632_clear_bits(handle, RC632_REG_TX_CONTROL, 0x03);
+}
+
+static int
+rc632_power_up(struct rfid_asic_handle *handle)
+{
+ ENTER();
+ return rc632_clear_bits(handle, RC632_REG_CONTROL,
+ RC632_CONTROL_POWERDOWN);
+}
+
+static int
+rc632_power_down(struct rfid_asic_handle *handle)
+{
+ return rc632_set_bits(handle, RC632_REG_CONTROL,
+ RC632_CONTROL_POWERDOWN);
+}
+
+/* calculate best 8bit prescaler and divisor for given usec timeout */
+static int best_prescaler(u_int64_t timeout, u_int8_t *prescaler,
+ u_int8_t *divisor)
+{
+ u_int8_t best_presc, best_divisor, i;
+ int64_t smallest_diff;
+
+ smallest_diff = 0x7fffffffffffffff;
+ best_presc = 0;
+
+ for (i = 0; i < 21; i++) {
+ u_int64_t clk, tmp_div, res;
+ int64_t diff;
+ clk = 13560000 / (1 << i);
+ tmp_div = (clk * timeout) / 1000000;
+ tmp_div++;
+
+ if (tmp_div > 0xff)
+ continue;
+
+ res = 1000000 / (clk / tmp_div);
+ diff = res - timeout;
+
+ if (diff < 0)
+ continue;
+
+ if (diff < smallest_diff) {
+ best_presc = i;
+ best_divisor = tmp_div;
+ smallest_diff = diff;
+ }
+ }
+
+ *prescaler = best_presc;
+ *divisor = best_divisor;
+
+ DEBUGPCRF("timeout %u usec, prescaler = %u, divisor = %u", timeout, best_presc, best_divisor);
+
+ return 0;
+}
+
+static int
+rc632_timer_set(struct rfid_asic_handle *handle,
+ u_int64_t timeout)
+{
+ int ret;
+ u_int8_t prescaler, divisor;
+
+ ret = best_prescaler(timeout*RC632_TIMEOUT_FUZZ_FACTOR,
+ &prescaler, &divisor);
+
+ ret = rc632_reg_write(handle, RC632_REG_TIMER_CLOCK,
+ prescaler & 0x1f);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_TIMER_CONTROL,
+ RC632_TMR_START_TX_END|RC632_TMR_STOP_RX_BEGIN);
+
+ /* clear timer irq bit */
+ ret |= rc632_set_bits(handle, RC632_REG_INTERRUPT_RQ, RC632_INT_TIMER);
+
+#ifdef USE_IRQ_
+ /* enable interrupt for expired timer */
+ ret |= rc632_reg_write(handle, RC632_REG_INTERRUPT_EN,
+ RC632_INT_TIMER|RC632_INT_SET);
+#endif
+
+ ret |= rc632_reg_write(handle, RC632_REG_TIMER_RELOAD, divisor);
+
+ return ret;
+}
+
+/* Wait until RC632 is idle or TIMER IRQ has happened */
+static int rc632_wait_idle_timer(struct rfid_asic_handle *handle)
+{
+ int ret;
+ u_int8_t irq, cmd;
+
+ while (1) {
+ ret = rc632_reg_read(handle, RC632_REG_INTERRUPT_RQ, &irq);
+ if (ret < 0)
+ return ret;
+
+ /* FIXME: currently we're lazy: If we actually received
+ * something even after the timer expired, we accept it */
+ if (irq & RC632_INT_TIMER && !(irq & RC632_INT_RX)) {
+ u_int8_t foo;
+ rc632_reg_read(handle, RC632_REG_PRIMARY_STATUS, &foo);
+ DEBUGPCRF("TIMER && !INT_RX, PRIM_STATUS=0x%02x", foo);
+ if (foo & 0x04) {
+ rc632_reg_read(handle, RC632_REG_ERROR_FLAG, &foo);
+ DEBUGPCRF("ERROR_FLAG=0x%02x, returning timeout", foo);
+ }
+
+ return -ETIMEDOUT;
+ }
+
+ ret = rc632_reg_read(handle, RC632_REG_COMMAND, &cmd);
+ if (ret < 0)
+ return ret;
+
+ if (cmd == 0 || irq & RC632_INT_RX)
+ return 0;
+
+ /* poll every millisecond */
+ /* FIXME usleep(1000);*/
+ }
+}
+
+/* Stupid RC632 implementations don't evaluate interrupts but poll the
+ * command register for "status idle" */
+static int
+rc632_wait_idle(struct rfid_asic_handle *handle, u_int64_t timeout)
+{
+ u_int8_t cmd = 0xff;
+ int ret, cycles = 0;
+#define USLEEP_PER_CYCLE 128
+
+ while (cmd != 0) {
+ ret = rc632_reg_read(handle, RC632_REG_COMMAND, &cmd);
+ if (ret < 0)
+ return ret;
+
+ if (cmd == 0) {
+ /* FIXME: read second time ?? */
+ return 0;
+ }
+
+ {
+ u_int8_t foo;
+ rc632_reg_read(handle, RC632_REG_PRIMARY_STATUS, &foo);
+ if (foo & 0x04)
+ rc632_reg_read(handle, RC632_REG_ERROR_FLAG, &foo);
+ }
+
+ /* Abort after some timeout */
+ if (cycles > timeout*RC632_TIMEOUT_FUZZ_FACTOR/USLEEP_PER_CYCLE) {
+ return -ETIMEDOUT;
+ }
+
+ cycles++;
+ usleep(USLEEP_PER_CYCLE);
+ }
+
+ return 0;
+}
+
+static int
+rc632_transmit(struct rfid_asic_handle *handle,
+ const u_int8_t *buf,
+ u_int8_t len,
+ u_int64_t timeout)
+{
+ int ret, cur_len;
+ const u_int8_t *cur_buf = buf;
+
+ if (len > 64)
+ cur_len = 64;
+ else
+ cur_len = len;
+
+ do {
+ ret = rc632_fifo_write(handle, cur_len, cur_buf, 0x03);
+ if (ret < 0)
+ return ret;
+
+ if (cur_buf == buf) {
+ /* only start transmit first time */
+ ret = rc632_reg_write(handle, RC632_REG_COMMAND,
+ RC632_CMD_TRANSMIT);
+ if (ret < 0)
+ return ret;
+ }
+
+ cur_buf += cur_len;
+ if (cur_buf < buf + len) {
+ cur_len = buf - cur_buf;
+ if (cur_len > 64)
+ cur_len = 64;
+ } else
+ cur_len = 0;
+
+ } while (cur_len);
+
+ return rc632_wait_idle(handle, timeout);
+}
+
+static int
+tcl_toggle_pcb(struct rfid_asic_handle *handle)
+{
+ // FIXME: toggle something between 0x0a and 0x0b
+ return 0;
+}
+
+static int
+rc632_transceive(struct rfid_asic_handle *handle,
+ const u_int8_t *tx_buf,
+ u_int8_t tx_len,
+ u_int8_t *rx_buf,
+ u_int8_t *rx_len,
+ u_int64_t timer,
+ unsigned int toggle)
+{
+ int ret, cur_tx_len;
+ const u_int8_t *cur_tx_buf = tx_buf;
+
+ DEBUGPCRF("tx_len=%u, rx_len=%u, timer=%llu", tx_len, *rx_len, timer);
+
+ if (tx_len > 64)
+ cur_tx_len = 64;
+ else
+ cur_tx_len = tx_len;
+
+ ret = rc632_reg_write(handle, RC632_REG_COMMAND, 0x00);
+ /* clear all interrupts */
+ ret = rc632_reg_write(handle, RC632_REG_INTERRUPT_RQ, 0x7f);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_timer_set(handle, timer);
+ if (ret < 0)
+ return ret;
+
+ do {
+ ret = rc632_fifo_write(handle, cur_tx_len, cur_tx_buf, 0x03);
+ if (ret < 0)
+ return ret;
+
+ if (cur_tx_buf == tx_buf) {
+ ret = rc632_reg_write(handle, RC632_REG_COMMAND,
+ RC632_CMD_TRANSCEIVE);
+ if (ret < 0)
+ return ret;
+ }
+
+ cur_tx_buf += cur_tx_len;
+ if (cur_tx_buf < tx_buf + tx_len) {
+ u_int8_t fifo_fill;
+ ret = rc632_reg_read(handle, RC632_REG_FIFO_LENGTH,
+ &fifo_fill);
+ if (ret < 0)
+ return ret;
+
+ cur_tx_len = 64 - fifo_fill;
+ DEBUGPCRF("refilling tx fifo with %u bytes", cur_tx_len);
+ } else
+ cur_tx_len = 0;
+
+ } while (cur_tx_len);
+
+ if (toggle == 1)
+ tcl_toggle_pcb(handle);
+
+ ret = rc632_wait_idle_timer(handle);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_read(handle, RC632_REG_FIFO_LENGTH, rx_len);
+ if (ret < 0)
+ return ret;
+
+ DEBUGPCRF("rx_len = %u\n", *rx_len);
+
+ if (*rx_len == 0) {
+ u_int8_t tmp, tmp2;
+
+ rc632_reg_read(handle, RC632_REG_ERROR_FLAG, &tmp);
+ rc632_reg_read(handle, RC632_REG_CHANNEL_REDUNDANCY, &tmp2);
+
+ DEBUGPCRF("rx_len == 0, error_flag=0x%02x, channel_red=0x%02x",
+ tmp, tmp2);
+
+ return -1;
+ }
+
+ return rc632_fifo_read(handle, *rx_len, rx_buf);
+}
+
+int
+rc632_read_eeprom(struct rfid_asic_handle *handle, u_int16_t addr, u_int8_t len,
+ u_int8_t *recvbuf)
+{
+ u_int8_t sndbuf[3];
+ u_int8_t err;
+ int ret;
+
+ sndbuf[0] = (addr & 0xff);
+ sndbuf[1] = addr >> 8;
+ sndbuf[2] = len;
+
+ ret = rc632_fifo_write(handle, 3, sndbuf, 0x03);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_COMMAND, RC632_CMD_READ_E2);
+ if (ret < 0)
+ return ret;
+
+ /* usleep(20000); */
+
+ ret = rc632_reg_read(handle, RC632_REG_ERROR_FLAG, &err);
+ if (err & RC632_ERR_FLAG_ACCESS_ERR)
+ return -EPERM;
+
+ ret = rc632_reg_read(handle, RC632_REG_FIFO_DATA, &err);
+ if (err < len)
+ len = err;
+
+ ret = rc632_fifo_read(handle, len, recvbuf);
+ if (ret < 0)
+ return ret;
+
+ return len;
+}
+
+static int
+rc632_calc_crc16_from(struct rfid_asic_handle *handle)
+{
+ u_int8_t sndbuf[2] = { 0x01, 0x02 };
+ u_int8_t crc_lsb = 0x00 , crc_msb = 0x00;
+ int ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_CRC_PRESET_LSB, 0x12);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_CRC_PRESET_MSB, 0xe0);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_fifo_write(handle, sizeof(sndbuf), sndbuf, 3);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_COMMAND, RC632_CMD_CALC_CRC);
+ if (ret < 0)
+ return ret;
+
+ usleep(10000); // FIXME: no checking for cmd completion?
+
+ ret = rc632_reg_read(handle, RC632_REG_CRC_RESULT_LSB, &crc_lsb);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_read(handle, RC632_REG_CRC_RESULT_MSB, &crc_msb);
+ if (ret < 0)
+ return ret;
+
+ // FIXME: what to do with crc result?
+ return ret;
+}
+
+
+int
+rc632_register_dump(struct rfid_asic_handle *handle, u_int8_t *buf)
+{
+ int ret;
+ u_int8_t i;
+
+ for (i = 0; i <= 0x3f; i++) {
+ ret = rc632_reg_read(handle, i, &buf[i]);
+ // do we want error checks?
+ }
+ return 0;
+}
+
+
+#if 0
+static int
+rc632_init(struct rfid_asic_handle *ah)
+{
+ int ret;
+
+ /* switch off rf (make sure PICCs are reset at init time) */
+ ret = rc632_power_down(ah);
+ if (ret < 0)
+ return ret;
+
+ usleep(10000);
+
+ /* switch on rf */
+ ret = rc632_power_up(ah);
+ if (ret < 0)
+ return ret;
+
+ /* disable register paging */
+ ret = rc632_reg_write(ah, 0x00, 0x00);
+ if (ret < 0)
+ return ret;
+
+ /* set some sane default values */
+ ret = rc632_reg_write(ah, 0x11, 0x5b);
+ if (ret < 0)
+ return ret;
+
+ /* switch on rf */
+ ret = rc632_turn_on_rf(ah);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int
+rc632_fini(struct rfid_asic_handle *ah)
+{
+ int ret;
+
+ /* switch off rf */
+ ret = rc632_turn_off_rf(ah);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_power_down(ah);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+#endif
+
+
+/*
+ * Philips CL RC632 primitives for ISO 14443-A compliant PICC's
+ *
+ * (C) 2005 by Harald Welte <laforge@gnumonks.org>
+ *
+ */
+
+int
+rc632_iso14443a_init(struct rfid_asic_handle *handle)
+{
+ int ret;
+
+ // FIXME: some fifo work (drain fifo?)
+
+ /* flush fifo (our way) */
+ ret = rc632_reg_write(handle, RC632_REG_CONTROL, 0x01);
+
+ ret = rc632_reg_write(handle, RC632_REG_TX_CONTROL,
+ (RC632_TXCTRL_TX1_RF_EN |
+ RC632_TXCTRL_TX2_RF_EN |
+ RC632_TXCTRL_TX2_INV |
+ RC632_TXCTRL_FORCE_100_ASK |
+ RC632_TXCTRL_MOD_SRC_INT));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_CW_CONDUCTANCE,
+ OPENPCD_CW_CONDUCTANCE);
+ if (ret < 0)
+ return ret;
+
+ /* Since FORCE_100_ASK is set (cf mc073930.pdf), this line may be left out? */
+ ret = rc632_reg_write(handle, RC632_REG_MOD_CONDUCTANCE,
+ OPENPCD_MOD_CONDUCTANCE);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_CODER_CONTROL,
+ (RC632_CDRCTRL_TXCD_14443A |
+ RC632_CDRCTRL_RATE_106K));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_MOD_WIDTH, 0x13);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_MOD_WIDTH_SOF, 0x3f);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_TYPE_B_FRAMING, 0x00);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_RX_CONTROL1,
+ (RC632_RXCTRL1_GAIN_35DB |
+ RC632_RXCTRL1_ISO14443 |
+ RC632_RXCTRL1_SUBCP_8));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_DECODER_CONTROL,
+ (RC632_DECCTRL_MANCHESTER |
+ RC632_DECCTRL_RXFR_14443A));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_BIT_PHASE,
+ OPENPCD_14443A_BITPHASE);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_RX_THRESHOLD,
+ OPENPCD_14443A_THRESHOLD);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_BPSK_DEM_CONTROL, 0x00);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_RX_CONTROL2,
+ (RC632_RXCTRL2_DECSRC_INT |
+ RC632_RXCTRL2_CLK_Q));
+ if (ret < 0)
+ return ret;
+
+ /* Omnikey proprietary driver has 0x03, but 0x06 is the default reset value ?!? */
+ ret = rc632_reg_write(handle, RC632_REG_RX_WAIT, 0x06);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_CHANNEL_REDUNDANCY,
+ (RC632_CR_PARITY_ENABLE |
+ RC632_CR_PARITY_ODD));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_CRC_PRESET_LSB, 0x63);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_CRC_PRESET_MSB, 0x63);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int
+rc632_iso14443a_fini(struct iso14443a_handle *handle_14443)
+{
+
+#if 0
+ ret = rc632_turn_off_rf(handle);
+ if (ret < 0)
+ return ret;
+#endif
+
+
+ return 0;
+}
+
+
+/* issue a 14443-3 A PCD -> PICC command in a short frame, such as REQA, WUPA */
+int
+rc632_iso14443a_transceive_sf(struct rfid_asic_handle *handle,
+ u_int8_t cmd,
+ struct iso14443a_atqa *atqa)
+{
+ int ret;
+ u_int8_t tx_buf[1];
+ u_int8_t rx_len = 2;
+
+ DEBUGPCRF("");
+ memset(atqa, 0, sizeof(atqa));
+
+ tx_buf[0] = cmd;
+
+ /* transfer only 7 bits of last byte in frame */
+ ret = rc632_reg_write(handle, RC632_REG_BIT_FRAMING, 0x07);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_clear_bits(handle, RC632_REG_CONTROL,
+ RC632_CONTROL_CRYPTO1_ON);
+ if (ret < 0)
+ return ret;
+
+#if 0
+ ret = rc632_reg_write(handle, RC632_REG_CHANNEL_REDUNDANCY,
+ (RC632_CR_PARITY_ENABLE |
+ RC632_CR_PARITY_ODD));
+#else
+ ret = rc632_clear_bits(handle, RC632_REG_CHANNEL_REDUNDANCY,
+ RC632_CR_RX_CRC_ENABLE|RC632_CR_TX_CRC_ENABLE);
+
+#endif
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_transceive(handle, tx_buf, sizeof(tx_buf),
+ (u_int8_t *)atqa, &rx_len,
+ ISO14443A_FDT_ANTICOL_LAST1, 0);
+ if (ret < 0) {
+ DEBUGPCRF("error during rc632_transceive()");
+ return ret;
+ }
+
+ /* switch back to normal 8bit last byte */
+ ret = rc632_reg_write(handle, RC632_REG_BIT_FRAMING, 0x00);
+ if (ret < 0)
+ return ret;
+
+ if (rx_len != 2) {
+ DEBUGPCRF("rx_len(%d) != 2", rx_len);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* transceive regular frame */
+int
+rc632_iso14443ab_transceive(struct rfid_asic_handle *handle,
+ unsigned int frametype,
+ const u_int8_t *tx_buf, unsigned int tx_len,
+ u_int8_t *rx_buf, unsigned int *rx_len,
+ u_int64_t timeout, unsigned int flags)
+{
+ int ret;
+ u_int8_t rxl = *rx_len & 0xff;
+ u_int8_t channel_red;
+
+ DEBUGPCRF("tx_len=%u, rx_len=%u", tx_len, *rx_len);
+
+ memset(rx_buf, 0, *rx_len);
+
+ switch (frametype) {
+ case RFID_14443A_FRAME_REGULAR:
+ case RFID_MIFARE_FRAME:
+ channel_red = RC632_CR_RX_CRC_ENABLE|RC632_CR_TX_CRC_ENABLE
+ |RC632_CR_PARITY_ENABLE|RC632_CR_PARITY_ODD;
+ break;
+ case RFID_14443B_FRAME_REGULAR:
+ channel_red = RC632_CR_RX_CRC_ENABLE|RC632_CR_TX_CRC_ENABLE
+ |RC632_CR_CRC3309;
+ break;
+#if 0
+ case RFID_MIFARE_FRAME:
+ channel_red = RC632_CR_PARITY_ENABLE|RC632_CR_PARITY_ODD;
+ break;
+#endif
+ default:
+ return -EINVAL;
+ break;
+ }
+ ret = rc632_reg_write(handle, RC632_REG_CHANNEL_REDUNDANCY,
+ channel_red);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_transceive(handle, tx_buf, tx_len, rx_buf, &rxl, timeout, 0);
+ *rx_len = rxl;
+ if (ret < 0)
+ return ret;
+
+
+ return 0;
+}
+
+/* transceive anti collission bitframe */
+int
+rc632_iso14443a_transceive_acf(struct rfid_asic_handle *handle,
+ struct iso14443a_anticol_cmd *acf,
+ unsigned int *bit_of_col)
+{
+ int ret;
+ u_int8_t rx_buf[64];
+ u_int8_t rx_len = sizeof(rx_buf);
+ u_int8_t rx_align = 0, tx_last_bits, tx_bytes;
+ u_int8_t boc;
+ u_int8_t error_flag;
+ *bit_of_col = ISO14443A_BITOFCOL_NONE;
+ memset(rx_buf, 0, sizeof(rx_buf));
+
+ /* disable mifare cryto */
+ ret = rc632_clear_bits(handle, RC632_REG_CONTROL,
+ RC632_CONTROL_CRYPTO1_ON);
+ if (ret < 0)
+ return ret;
+
+ /* disable CRC summing */
+#if 0
+ ret = rc632_reg_write(handle, RC632_REG_CHANNEL_REDUNDANCY,
+ (RC632_CR_PARITY_ENABLE |
+ RC632_CR_PARITY_ODD));
+#else
+ ret = rc632_clear_bits(handle, RC632_REG_CHANNEL_REDUNDANCY,
+ RC632_CR_TX_CRC_ENABLE|RC632_CR_TX_CRC_ENABLE);
+#endif
+ if (ret < 0)
+ return ret;
+
+ tx_last_bits = acf->nvb & 0x0f; /* lower nibble indicates bits */
+ tx_bytes = acf->nvb >> 4;
+ if (tx_last_bits) {
+ tx_bytes++;
+ rx_align = (tx_last_bits+1) % 8;/* rx frame complements tx */
+ }
+
+ //rx_align = 8 - tx_last_bits;/* rx frame complements tx */
+
+ /* set RxAlign and TxLastBits*/
+ ret = rc632_reg_write(handle, RC632_REG_BIT_FRAMING,
+ (rx_align << 4) | (tx_last_bits));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_transceive(handle, (u_int8_t *)acf, tx_bytes,
+ rx_buf, &rx_len, 0x32, 0);
+ if (ret < 0)
+ return ret;
+
+ /* bitwise-OR the two halves of the split byte */
+ acf->uid_bits[tx_bytes-2] = (
+ (acf->uid_bits[tx_bytes-2] & (0xff >> (8-tx_last_bits)))
+ | rx_buf[0]);
+ /* copy the rest */
+ memcpy(&acf->uid_bits[tx_bytes+1-2], &rx_buf[1], rx_len-1);
+
+ /* determine whether there was a collission */
+ ret = rc632_reg_read(handle, RC632_REG_ERROR_FLAG, &error_flag);
+ if (ret < 0)
+ return ret;
+
+ if (error_flag & RC632_ERR_FLAG_COL_ERR) {
+ /* retrieve bit of collission */
+ ret = rc632_reg_read(handle, RC632_REG_COLL_POS, &boc);
+ if (ret < 0)
+ return ret;
+
+ /* bit of collission relative to start of part 1 of
+ * anticollision frame (!) */
+ *bit_of_col = 2*8 + boc;
+ }
+
+ return 0;
+}
+
+enum rc632_rate {
+ RC632_RATE_106 = 0x00,
+ RC632_RATE_212 = 0x01,
+ RC632_RATE_424 = 0x02,
+ RC632_RATE_848 = 0x03,
+};
+
+struct rx_config {
+ u_int8_t subc_pulses;
+ u_int8_t rx_coding;
+ u_int8_t rx_threshold;
+ u_int8_t bpsk_dem_ctrl;
+};
+
+struct tx_config {
+ u_int8_t rate;
+ u_int8_t mod_width;
+};
+
+static const struct rx_config rx_configs[] = {
+ {
+ .subc_pulses = RC632_RXCTRL1_SUBCP_8,
+ .rx_coding = RC632_DECCTRL_MANCHESTER,
+ .rx_threshold = 0x88,
+ .bpsk_dem_ctrl = 0x00,
+ },
+ {
+ .subc_pulses = RC632_RXCTRL1_SUBCP_4,
+ .rx_coding = RC632_DECCTRL_BPSK,
+ .rx_threshold = 0x50,
+ .bpsk_dem_ctrl = 0x0c,
+ },
+ {
+ .subc_pulses = RC632_RXCTRL1_SUBCP_2,
+ .rx_coding = RC632_DECCTRL_BPSK,
+ .rx_threshold = 0x50,
+ .bpsk_dem_ctrl = 0x0c,
+ },
+ {
+ .subc_pulses = RC632_RXCTRL1_SUBCP_1,
+ .rx_coding = RC632_DECCTRL_BPSK,
+ .rx_threshold = 0x50,
+ .bpsk_dem_ctrl = 0x0c,
+ },
+};
+
+static const struct tx_config tx_configs[] = {
+ {
+ .rate = RC632_CDRCTRL_RATE_106K,
+ .mod_width = 0x13,
+ },
+ {
+ .rate = RC632_CDRCTRL_RATE_212K,
+ .mod_width = 0x07,
+ },
+ {
+ .rate = RC632_CDRCTRL_RATE_424K,
+ .mod_width = 0x03,
+ },
+ {
+ .rate = RC632_CDRCTRL_RATE_848K,
+ .mod_width = 0x01,
+ },
+};
+
+int rc632_iso14443a_set_speed(struct rfid_asic_handle *handle,
+ unsigned int tx,
+ u_int8_t rate)
+{
+ int rc;
+ u_int8_t reg;
+
+
+ if (!tx) {
+ /* Rx */
+ if (rate > ARRAY_SIZE(rx_configs))
+ return -EINVAL;
+
+ rc = rc632_set_bit_mask(handle, RC632_REG_RX_CONTROL1,
+ RC632_RXCTRL1_SUBCP_MASK,
+ rx_configs[rate].subc_pulses);
+ if (rc < 0)
+ return rc;
+
+ rc = rc632_set_bit_mask(handle, RC632_REG_DECODER_CONTROL,
+ RC632_DECCTRL_BPSK,
+ rx_configs[rate].rx_coding);
+ if (rc < 0)
+ return rc;
+
+ rc = rc632_reg_write(handle, RC632_REG_RX_THRESHOLD,
+ rx_configs[rate].rx_threshold);
+ if (rc < 0)
+ return rc;
+
+ if (rx_configs[rate].rx_coding == RC632_DECCTRL_BPSK) {
+ rc = rc632_reg_write(handle,
+ RC632_REG_BPSK_DEM_CONTROL,
+ rx_configs[rate].bpsk_dem_ctrl);
+ if (rc < 0)
+ return rc;
+ }
+ } else {
+ /* Tx */
+ if (rate > ARRAY_SIZE(tx_configs))
+ return -EINVAL;
+
+ rc = rc632_set_bit_mask(handle, RC632_REG_CODER_CONTROL,
+ RC632_CDRCTRL_RATE_MASK,
+ tx_configs[rate].rate);
+ if (rc < 0)
+ return rc;
+
+ rc = rc632_reg_write(handle, RC632_REG_MOD_WIDTH,
+ tx_configs[rate].mod_width);
+ if (rc < 0)
+ return rc;
+ }
+
+ return 0;
+}
+
+static int rc632_iso14443b_init(struct rfid_asic_handle *handle)
+{
+ int ret;
+
+ // FIXME: some FIFO work
+
+ /* flush fifo (our way) */
+ ret = rc632_reg_write(handle, RC632_REG_CONTROL, 0x01);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_TX_CONTROL,
+ (RC632_TXCTRL_TX1_RF_EN |
+ RC632_TXCTRL_TX2_RF_EN |
+ RC632_TXCTRL_TX2_INV |
+ RC632_TXCTRL_MOD_SRC_INT));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_CW_CONDUCTANCE, 0x3f);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_MOD_CONDUCTANCE, 0x04);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_CODER_CONTROL,
+ (RC632_CDRCTRL_TXCD_NRZ |
+ RC632_CDRCTRL_RATE_14443B));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_MOD_WIDTH, 0x13);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_MOD_WIDTH_SOF, 0x3f);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_TYPE_B_FRAMING,
+ (RC632_TBFRAMING_SOF_11L_3H |
+ (6 << RC632_TBFRAMING_SPACE_SHIFT) |
+ RC632_TBFRAMING_EOF_11));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_RX_CONTROL1,
+ (RC632_RXCTRL1_GAIN_35DB |
+ RC632_RXCTRL1_ISO14443 |
+ RC632_RXCTRL1_SUBCP_8));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_DECODER_CONTROL,
+ (RC632_DECCTRL_BPSK |
+ RC632_DECCTRL_RXFR_14443B));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_BIT_PHASE,
+ OPENPCD_14443B_BITPHASE);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_RX_THRESHOLD,
+ OPENPCD_14443B_THRESHOLD);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_BPSK_DEM_CONTROL,
+ ((0x2 & RC632_BPSKD_TAUB_MASK)<<RC632_BPSKD_TAUB_SHIFT |
+ (0x3 & RC632_BPSKD_TAUD_MASK)<<RC632_BPSKD_TAUD_SHIFT |
+ RC632_BPSKD_FILTER_AMP_DETECT |
+ RC632_BPSKD_NO_RX_EOF |
+ RC632_BPSKD_NO_RX_EGT));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_RX_CONTROL2,
+ (RC632_RXCTRL2_AUTO_PD |
+ RC632_RXCTRL2_DECSRC_INT));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_RX_WAIT, 0x03);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_CHANNEL_REDUNDANCY,
+ (RC632_CR_TX_CRC_ENABLE |
+ RC632_CR_RX_CRC_ENABLE |
+ RC632_CR_CRC3309));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_CRC_PRESET_LSB, 0xff);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(handle, RC632_REG_CRC_PRESET_MSB, 0xff);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int
+rc632_iso15693_init(struct rfid_asic_handle *h)
+{
+ int ret;
+
+ ret = rc632_reg_write(h, RC632_REG_TX_CONTROL,
+ (RC632_TXCTRL_MOD_SRC_INT |
+ RC632_TXCTRL_TX2_INV |
+ RC632_TXCTRL_TX2_RF_EN |
+ RC632_TXCTRL_TX1_RF_EN));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_CW_CONDUCTANCE, 0x3f);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_MOD_CONDUCTANCE, 0x03);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_CODER_CONTROL,
+ (RC632_CDRCTRL_RATE_15693 |
+ 0x03)); /* FIXME */
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_MOD_WIDTH, 0x3f);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_MOD_WIDTH_SOF, 0x3f);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_TYPE_B_FRAMING, 0x00);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_RX_CONTROL1,
+ (RC632_RXCTRL1_SUBCP_16 |
+ RC632_RXCTRL1_ISO15693 |
+ RC632_RXCTRL1_GAIN_35DB));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_DECODER_CONTROL,
+ (RC632_DECCTRL_RXFR_15693 |
+ RC632_DECCTRL_RX_INVERT));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_BIT_PHASE, 0xe0);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_RX_THRESHOLD, 0xff);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_BPSK_DEM_CONTROL, 0x00);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_RX_CONTROL2,
+ (RC632_RXCTRL2_AUTO_PD |
+ RC632_RXCTRL2_DECSRC_INT));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_CHANNEL_REDUNDANCY,
+ (RC632_CR_CRC3309 |
+ RC632_CR_RX_CRC_ENABLE |
+ RC632_CR_TX_CRC_ENABLE));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_CRC_PRESET_LSB, 0xff);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_CRC_PRESET_MSB, 0xff);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int
+rc632_iso15693_icode_init(struct rfid_asic_handle *h)
+{
+ int ret;
+
+ ret = rc632_reg_write(h, RC632_REG_TX_CONTROL,
+ (RC632_TXCTRL_MOD_SRC_INT |
+ RC632_TXCTRL_TX2_INV |
+ RC632_TXCTRL_TX2_RF_EN |
+ RC632_TXCTRL_TX1_RF_EN));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_CW_CONDUCTANCE, 0x3f);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_MOD_CONDUCTANCE, 0x02);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_CODER_CONTROL, 0x2c);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_MOD_WIDTH, 0x3f);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_MOD_WIDTH_SOF, 0x3f);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_MOD_WIDTH_SOF, 0x3f);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_TYPE_B_FRAMING, 0x00);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_RX_CONTROL1, 0x8b); /* FIXME */
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_DECODER_CONTROL, 0x00);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_BIT_PHASE, 0x52);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_RX_THRESHOLD, 0x66);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_BPSK_DEM_CONTROL, 0x00);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_RX_CONTROL2,
+ RC632_RXCTRL2_DECSRC_INT);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_CHANNEL_REDUNDANCY,
+ (RC632_CR_RX_CRC_ENABLE |
+ RC632_CR_TX_CRC_ENABLE));
+ ret = rc632_reg_write(h, RC632_REG_CRC_PRESET_LSB, 0xfe);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_CRC_PRESET_MSB, 0xff);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int
+rc632_iso15693_icl_init(struct rfid_asic_handle *h)
+{
+ int ret;
+
+ /* ICL */
+
+ ret = rc632_reg_write(h, RC632_REG_TX_CONTROL,
+ (RC632_TXCTRL_MOD_SRC_INT |
+ RC632_TXCTRL_TX2_INV |
+ RC632_TXCTRL_TX2_RF_EN |
+ RC632_TXCTRL_TX1_RF_EN));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_CW_CONDUCTANCE, 0x3f);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_MOD_CONDUCTANCE, 0x11);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_CODER_CONTROL,
+ (RC632_CDRCTRL_RATE_15693 |
+ RC632_CDRCTRL_TXCD_ICODE_STD |
+ 0x03)); /* FIXME */
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_MOD_WIDTH, 0x3f);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_MOD_WIDTH_SOF, 0x3f);
+ if (ret < 0)
+ return ret;
+ ret = rc632_reg_write(h, RC632_REG_RX_CONTROL1,
+ (RC632_RXCTRL1_SUBCP_16|
+ RC632_RXCTRL1_ISO15693|
+ RC632_RXCTRL1_GAIN_35DB));
+ if (ret < 0)
+ return ret;
+ ret = rc632_reg_write(h, RC632_REG_DECODER_CONTROL,
+ (RC632_DECCTRL_RX_INVERT|
+ RC632_DECCTRL_RXFR_15693));
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_BIT_PHASE, 0xbd);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_RX_THRESHOLD, 0xff);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_BPSK_DEM_CONTROL, 0x00);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_RX_CONTROL2,
+ RC632_RXCTRL2_DECSRC_INT);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_CHANNEL_REDUNDANCY, 0x00);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_CRC_PRESET_LSB, 0x12);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_CRC_PRESET_MSB, 0xe0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+struct mifare_authcmd {
+ u_int8_t auth_cmd;
+ u_int8_t block_address;
+ u_int32_t serno; /* lsb 1 2 msb */
+} __attribute__ ((packed));
+
+
+#define RFID_MIFARE_KEY_LEN 6
+#define RFID_MIFARE_KEY_CODED_LEN 12
+
+/* Transform crypto1 key from generic 6byte into rc632 specific 12byte */
+static int
+rc632_mifare_transform_key(const u_int8_t *key6, u_int8_t *key12)
+{
+ int i;
+ u_int8_t ln;
+ u_int8_t hn;
+
+ for (i = 0; i < RFID_MIFARE_KEY_LEN; i++) {
+ ln = key6[i] & 0x0f;
+ hn = key6[i] >> 4;
+ key12[i * 2 + 1] = (~ln << 4) | ln;
+ key12[i * 2] = (~hn << 4) | hn;
+ }
+ return 0;
+}
+
+static int
+rc632_mifare_set_key(struct rfid_asic_handle *h, const u_int8_t *key)
+{
+ u_int8_t coded_key[RFID_MIFARE_KEY_CODED_LEN];
+ u_int8_t reg;
+ int ret;
+
+ ret = rc632_mifare_transform_key(key, coded_key);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_fifo_write(h, RFID_MIFARE_KEY_CODED_LEN, coded_key, 0x03);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_COMMAND, RC632_CMD_LOAD_KEY);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_wait_idle(h, RC632_TMO_AUTH1);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_read(h, RC632_REG_ERROR_FLAG, &reg);
+ if (ret < 0)
+ return ret;
+
+ if (reg & RC632_ERR_FLAG_KEY_ERR)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int
+rc632_mifare_auth(struct rfid_asic_handle *h, u_int8_t cmd, u_int32_t serno,
+ u_int8_t block)
+{
+ int ret;
+ struct mifare_authcmd acmd;
+ u_int8_t reg;
+
+ if (cmd != RFID_CMD_MIFARE_AUTH1A && cmd != RFID_CMD_MIFARE_AUTH1B)
+ return -EINVAL;
+
+ /* Initialize acmd */
+ acmd.block_address = block & 0xff;
+ acmd.auth_cmd = cmd;
+ //acmd.serno = htonl(serno);
+ acmd.serno = serno;
+
+ /* Clear Rx CRC */
+ ret = rc632_clear_bits(h, RC632_REG_CHANNEL_REDUNDANCY,
+ RC632_CR_RX_CRC_ENABLE);
+ if (ret < 0)
+ return ret;
+
+ /* Send Authent1 Command */
+ ret = rc632_fifo_write(h, sizeof(acmd), (unsigned char *)&acmd, 0x03);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_write(h, RC632_REG_COMMAND, RC632_CMD_AUTHENT1);
+ if (ret < 0)
+ return ret;
+
+ /* Wait until transmitter is idle */
+ ret = rc632_wait_idle(h, RC632_TMO_AUTH1);
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_reg_read(h, RC632_REG_SECONDARY_STATUS, &reg);
+ if (ret < 0)
+ return ret;
+ if (reg & 0x07)
+ return -EIO;
+
+ /* Clear Tx CRC */
+ ret = rc632_clear_bits(h, RC632_REG_CHANNEL_REDUNDANCY,
+ RC632_CR_TX_CRC_ENABLE);
+ if (ret < 0)
+ return ret;
+
+ /* Send Authent2 Command */
+ ret = rc632_reg_write(h, RC632_REG_COMMAND, RC632_CMD_AUTHENT2);
+ if (ret < 0)
+ return ret;
+
+ /* Wait until transmitter is idle */
+ ret = rc632_wait_idle(h, RC632_TMO_AUTH1);
+ if (ret < 0)
+ return ret;
+
+ /* Check whether authentication was successful */
+ ret = rc632_reg_read(h, RC632_REG_CONTROL, &reg);
+ if (ret < 0)
+ return ret;
+
+ if (!(reg & RC632_CONTROL_CRYPTO1_ON))
+ return -EACCES;
+
+ return 0;
+
+}
+
+/* transceive regular frame */
+static int
+rc632_mifare_transceive(struct rfid_asic_handle *handle,
+ const u_int8_t *tx_buf, unsigned int tx_len,
+ u_int8_t *rx_buf, unsigned int *rx_len,
+ u_int64_t timeout, unsigned int flags)
+{
+ int ret;
+ u_int8_t rxl = *rx_len & 0xff;
+
+ DEBUGP("entered\n");
+ memset(rx_buf, 0, *rx_len);
+
+#if 1
+ ret = rc632_reg_write(handle, RC632_REG_CHANNEL_REDUNDANCY,
+ (RC632_CR_PARITY_ENABLE |
+ RC632_CR_PARITY_ODD |
+ RC632_CR_TX_CRC_ENABLE |
+ RC632_CR_RX_CRC_ENABLE));
+#else
+ ret = rc632_clear_bits(handle, RC632_REG_CHANNEL_REDUNDANCY,
+ RC632_CR_RX_CRC_ENABLE|RC632_CR_TX_CRC_ENABLE);
+#endif
+ if (ret < 0)
+ return ret;
+
+ ret = rc632_transceive(handle, tx_buf, tx_len, rx_buf, &rxl, 0x32, 0);
+ *rx_len = rxl;
+ if (ret < 0)
+ return ret;
+
+
+ return 0;
+}
+
diff --git a/firmware/src/pcd/rfid_layer2_iso14443a.c b/firmware/src/pcd/rfid_layer2_iso14443a.c
new file mode 100644
index 0000000..80d9d5f
--- /dev/null
+++ b/firmware/src/pcd/rfid_layer2_iso14443a.c
@@ -0,0 +1,319 @@
+/* ISO 14443-3 A anticollision implementation
+ *
+ * (C) 2005 by Harald Welte <laforge@gnumonks.org>
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation
+ *
+ * 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 <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <librfid/rfid.h>
+#include <librfid/rfid_layer2.h>
+//#include <librfid/rfid_reader.h>
+#include <librfid/rfid_layer2_iso14443a.h>
+
+#define TIMEOUT 1236
+
+/* Transceive a 7-bit short frame */
+static int
+iso14443a_transceive_sf(struct rfid_layer2_handle *handle,
+ unsigned char cmd,
+ struct iso14443a_atqa *atqa)
+{
+ //struct rfid_reader *rdr = handle->rh->reader;
+ DEBUGPCRF("");
+
+ return rc632_iso14443a_transceive_sf(handle->rh, cmd, atqa);
+}
+
+/* Transmit an anticollission bit frame */
+static int
+iso14443a_transceive_acf(struct rfid_layer2_handle *handle,
+ struct iso14443a_anticol_cmd *acf,
+ unsigned int *bit_of_col)
+{
+ //struct rfid_reader *rdr = handle->rh->reader;
+ DEBUGPCRF("");
+
+ return rc632_iso14443a_transceive_acf(handle->rh, acf, bit_of_col);
+}
+
+/* Transmit a regular frame */
+static int
+iso14443a_transceive(struct rfid_layer2_handle *handle,
+ enum rfid_frametype frametype,
+ const unsigned char *tx_buf, unsigned int tx_len,
+ unsigned char *rx_buf, unsigned int *rx_len,
+ u_int64_t timeout, unsigned int flags)
+{
+ DEBUGPCRF("tx_len=%u, rx_len=%u", tx_len, *rx_len);
+ return rc632_iso14443ab_transceive(handle->rh, frametype, tx_buf,
+ tx_len, rx_buf, rx_len, timeout, flags);
+}
+
+static int
+iso14443a_code_nvb_bits(unsigned char *nvb, unsigned int bits)
+{
+ unsigned int byte_count = bits / 8;
+ unsigned int bit_count = bits % 8;
+
+ if (byte_count < 2 || byte_count > 7)
+ return -1;
+
+ *nvb = ((byte_count & 0xf) << 4) | bit_count;
+
+ return 0;
+}
+
+/* first bit is '1', second bit '2' */
+static void
+set_bit_in_field(unsigned char *bitfield, unsigned int bit)
+{
+ unsigned int byte_count = bit / 8;
+ unsigned int bit_count = bit % 8;
+
+ DEBUGP("bitfield=%p, byte_count=%u, bit_count=%u\n",
+ bitfield, byte_count, bit_count);
+ DEBUGP("%p = 0x%02x\n", (bitfield+byte_count), *(bitfield+byte_count));
+ *(bitfield+byte_count) |= 1 << (bit_count-1);
+ DEBUGP("%p = 0x%02x\n", (bitfield+byte_count), *(bitfield+byte_count));
+}
+
+static int
+iso14443a_anticol(struct rfid_layer2_handle *handle)
+{
+ int ret;
+ unsigned int uid_size;
+ struct iso14443a_handle *h = &handle->priv.iso14443a;
+ struct iso14443a_atqa atqa;
+ struct iso14443a_anticol_cmd acf;
+ unsigned int bit_of_col;
+ unsigned char sak[3];
+ unsigned int rx_len = sizeof(sak);
+ char *aqptr = (char *) &atqa;
+
+ memset(handle->uid, 0, sizeof(handle->uid));
+ memset(sak, 0, sizeof(sak));
+ memset(&atqa, 0, sizeof(atqa));
+ memset(&acf, 0, sizeof(acf));
+
+ ret = iso14443a_transceive_sf(handle, ISO14443A_SF_CMD_REQA, &atqa);
+ if (ret < 0) {
+ h->state = ISO14443A_STATE_REQA_SENT;
+ DEBUGP("error during transceive_sf: %d\n", ret);
+ return ret;
+ }
+ h->state = ISO14443A_STATE_ATQA_RCVD;
+
+ DEBUGP("ATQA: 0x%02x 0x%02x\n", *aqptr, *(aqptr+1));
+
+ if (!atqa.bf_anticol) {
+ h->state = ISO14443A_STATE_NO_BITFRAME_ANTICOL;
+ DEBUGP("no bitframe anticollission bits set, aborting\n");
+ return -1;
+ }
+
+ if (atqa.uid_size == 2 || atqa.uid_size == 3)
+ uid_size = 3;
+ else if (atqa.uid_size == 1)
+ uid_size = 2;
+ else
+ uid_size = 1;
+
+ acf.sel_code = ISO14443A_AC_SEL_CODE_CL1;
+
+ h->state = ISO14443A_STATE_ANTICOL_RUNNING;
+ h->level = ISO14443A_LEVEL_CL1;
+
+cascade:
+ iso14443a_code_nvb_bits(&acf.nvb, 16);
+
+ ret = iso14443a_transceive_acf(handle, &acf, &bit_of_col);
+ if (ret < 0)
+ return ret;
+ DEBUGP("bit_of_col = %d\n", bit_of_col);
+
+ while (bit_of_col != ISO14443A_BITOFCOL_NONE) {
+ set_bit_in_field(&acf.uid_bits[0], bit_of_col-16);
+ iso14443a_code_nvb_bits(&acf.nvb, bit_of_col);
+ ret = iso14443a_transceive_acf(handle, &acf, &bit_of_col);
+ DEBUGP("bit_of_col = %d\n", bit_of_col);
+ if (ret < 0)
+ return ret;
+ }
+
+ iso14443a_code_nvb_bits(&acf.nvb, 7*8);
+ ret = iso14443a_transceive(handle, RFID_14443A_FRAME_REGULAR,
+ (unsigned char *)&acf, 7,
+ (unsigned char *) &sak, &rx_len,
+ TIMEOUT, 0);
+ if (ret < 0)
+ return ret;
+
+ if (sak[0] & 0x04) {
+ /* Cascade bit set, UID not complete */
+ switch (acf.sel_code) {
+ case ISO14443A_AC_SEL_CODE_CL1:
+ /* cascading from CL1 to CL2 */
+ if (acf.uid_bits[0] != 0x88) {
+ DEBUGP("Cascade bit set, but UID0 != 0x88\n");
+ return -1;
+ }
+ memcpy(&handle->uid[0], &acf.uid_bits[1], 3);
+ acf.sel_code = ISO14443A_AC_SEL_CODE_CL2;
+ h->level = ISO14443A_LEVEL_CL2;
+ break;
+ case ISO14443A_AC_SEL_CODE_CL2:
+ /* cascading from CL2 to CL3 */
+ memcpy(&handle->uid[3], &acf.uid_bits[1], 3);
+ acf.sel_code = ISO14443A_AC_SEL_CODE_CL3;
+ h->level = ISO14443A_LEVEL_CL3;
+ break;
+ default:
+ DEBUGP("cannot cascade any further than CL3\n");
+ h->state = ISO14443A_STATE_ERROR;
+ return -1;
+ break;
+ }
+ goto cascade;
+
+ } else {
+ switch (acf.sel_code) {
+ case ISO14443A_AC_SEL_CODE_CL1:
+ /* single size UID (4 bytes) */
+ memcpy(&handle->uid[0], &acf.uid_bits[0], 4);
+ break;
+ case ISO14443A_AC_SEL_CODE_CL2:
+ /* double size UID (7 bytes) */
+ memcpy(&handle->uid[3], &acf.uid_bits[0], 4);
+ break;
+ case ISO14443A_AC_SEL_CODE_CL3:
+ /* triple size UID (10 bytes) */
+ memcpy(&handle->uid[6], &acf.uid_bits[0], 4);
+ break;
+ }
+ }
+
+ h->level = ISO14443A_LEVEL_NONE;
+ h->state = ISO14443A_STATE_SELECTED;
+
+ {
+ if (uid_size == 1)
+ handle->uid_len = 4;
+ else if (uid_size == 2)
+ handle->uid_len = 7;
+ else
+ handle->uid_len = 10;
+
+ DEBUGP("UID %s\n", rfid_hexdump(handle->uid, handle->uid_len));
+ }
+
+ if (sak[0] & 0x20) {
+ DEBUGP("we have a T=CL compliant PICC\n");
+ h->tcl_capable = 1;
+ } else {
+ DEBUGP("we have a T!=CL PICC\n");
+ h->tcl_capable = 0;
+ }
+
+ return 0;
+}
+
+static int
+iso14443a_hlta(struct rfid_layer2_handle *handle)
+{
+ int ret;
+ unsigned char tx_buf[2] = { 0x50, 0x00 };
+ unsigned char rx_buf[10];
+ unsigned int rx_len = sizeof(rx_buf);
+
+ ret = iso14443a_transceive(handle, RFID_14443A_FRAME_REGULAR,
+ tx_buf, sizeof(tx_buf),
+ rx_buf, &rx_len, 1000 /* 1ms */, 0);
+ if (ret < 0) {
+ /* "error" case: we don't get somethng back from the card */
+ return 0;
+ }
+ return -1;
+}
+
+static int
+iso14443a_setopt(struct rfid_layer2_handle *handle, int optname,
+ const void *optval, unsigned int optlen)
+{
+ int ret = -EINVAL;
+ unsigned int speed;
+
+ switch (optname) {
+ case RFID_OPT_14443A_SPEED_RX:
+ speed = *(unsigned int *)optval;
+ ret = rc632_iso14443a_set_speed(handle->rh, 0, speed);
+ break;
+ case RFID_OPT_14443A_SPEED_TX:
+ speed = *(unsigned int *)optval;
+ ret = rc632_iso14443a_set_speed(handle->rh, 1, speed);
+ break;
+ };
+
+ return ret;
+}
+
+
+static struct rfid_layer2_handle *
+iso14443a_init(struct rfid_reader_handle *rh)
+{
+ int ret;
+ struct rfid_layer2_handle *h = malloc(sizeof(*h));
+ if (!h)
+ return NULL;
+
+ h->l2 = &rfid_layer2_iso14443a;
+ h->rh = rh;
+ h->priv.iso14443a.state = ISO14443A_STATE_NONE;
+ h->priv.iso14443a.level = ISO14443A_LEVEL_NONE;
+
+ ret = rc632_iso14443a_init(h->rh);
+ if (ret < 0) {
+ free(h);
+ return NULL;
+ }
+
+ return h;
+}
+
+static int
+iso14443a_fini(struct rfid_layer2_handle *handle)
+{
+ free(handle);
+ return 0;
+}
+
+struct rfid_layer2 rfid_layer2_iso14443a = {
+ .id = RFID_LAYER2_ISO14443A,
+ .name = "ISO 14443-3 A",
+ .fn = {
+ .init = &iso14443a_init,
+ .open = &iso14443a_anticol,
+ .transceive = &iso14443a_transceive,
+ .close = &iso14443a_hlta,
+ .fini = &iso14443a_fini,
+ .setopt = &iso14443a_setopt,
+ },
+};
diff --git a/firmware/src/picc/adc.c b/firmware/src/picc/adc.c
new file mode 100644
index 0000000..31b3469
--- /dev/null
+++ b/firmware/src/picc/adc.c
@@ -0,0 +1,145 @@
+/* AT91SAM7 ADC controller routines for OpenPCD / OpenPICC
+ * (C) 2006 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 "../openpcd.h"
+#include <os/dbgu.h>
+
+#define OPENPICC_ADC_CH_FIELDSTR AT91C_ADC_CH4
+#define OPENPICC_ADC_CH_PLL_DEM AT91C_ADC_CH5
+
+#define DEBUG_ADC
+
+#ifdef DEBUG_ADC
+#define DEBUGADC DEBUGP
+#else
+#define DEBUGADC do { } while (0)
+#endif
+
+static const AT91PS_ADC adc = AT91C_BASE_ADC;
+
+enum adc_states {
+ ADC_NONE,
+ ADC_READ_CONTINUOUS,
+ ADC_READ_CONTINUOUS_USB,
+ ADC_READ_SINGLE,
+};
+
+struct adc_state {
+ enum adc_states state;
+ struct req_ctx *rctx;
+};
+
+static struct adc_state adc_state;
+
+static void adc_irq(void)
+{
+ u_int32_t sr = adc->ADC_SR;
+ struct req_ctx *rctx = adc_state.rctx;
+
+ DEBUGADC("adc_irq(SR=0x%08x, IMR=0x%08x, state=%u): ",
+ sr, adc->ADC_IMR, adc_state.state);
+
+ switch (adc_state.state) {
+ case ADC_NONE:
+ //break;
+ case ADC_READ_CONTINUOUS_USB:
+ if (sr & AT91C_ADC_EOC4)
+ DEBUGADC("CDR4=0x%4x ", adc->ADC_CDR4);
+ if (sr & AT91C_ADC_EOC5)
+ DEBUGADC("CDR5=0x%4x ", adc->ADC_CDR5);
+ if (sr & AT91C_ADC_ENDRX) {
+ /* rctx full, get rid of it */
+ DEBUGADC("sending rctx (val=%s) ",
+ hexdump(rctx->tx.data[4], 2));
+
+ req_ctx_set_state(rctx, RCTX_STATE_UDP_EP2_PENDING);
+ adc_state.state = ADC_NONE;
+ adc_state.rctx = NULL;
+
+ //AT91F_PDC_SetRx(AT91C_BASE_PDC_ADC, NULL, 0);
+
+ /* Disable EOC interrupts since we don't want to
+ * re-start conversion any further*/
+ AT91F_ADC_DisableIt(AT91C_BASE_ADC, AT91C_ADC_ENDRX);
+ //AT91C_ADC_EOC4|AT91C_ADC_EOC5|AT91C_ADC_ENDRX);
+ AT91F_PDC_DisableRx(AT91C_BASE_PDC_ADC);
+ DEBUGADC("disabled IT/RX ");
+ } else {
+ if (sr & (AT91C_ADC_EOC4|AT91C_ADC_EOC5)) {
+ /* re-start conversion, since we need more values */
+ AT91F_ADC_StartConversion(adc);
+ }
+ }
+ break;
+ }
+
+ AT91F_AIC_ClearIt(AT91C_BASE_AIC, AT91C_ID_ADC);
+ DEBUGADC("cleeared ADC IRQ in AIC\r\n");
+}
+
+#if 0
+u_int16_t adc_read_fieldstr(void)
+{
+ return adc->ADC_CDR4;
+}
+
+u_int16_T adc_read_pll_dem(void)
+{
+ return adc
+}
+#endif
+
+static int adc_usb_in(struct req_ctx *rctx)
+{
+ struct openpcd_hdr *poh = (struct openpcd_hdr *) &rctx->rx.data[0];
+ struct openpcd_hdr *pih = (struct openpcd_hdr *) &rctx->tx.data[0];
+
+ switch (poh->cmd) {
+ case OPENPCD_CMD_ADC_READ:
+ DEBUGADC("ADC_READ(chan=%u, len=%u) ", poh->reg, poh->val);
+ //channel = poh->reg;
+ if (adc_state.rctx) {
+ /* FIXME: do something */
+ req_ctx_put(rctx);
+ }
+
+ adc_state.state = ADC_READ_CONTINUOUS_USB;
+ adc_state.rctx = rctx;
+ memcpy(pih, poh, sizeof(*pih));
+ rctx->tx.tot_len = sizeof(*pih) + poh->val * 2;
+ AT91F_PDC_SetRx(AT91C_BASE_PDC_ADC, rctx->rx.data, poh->val);
+ AT91F_PDC_EnableRx(AT91C_BASE_PDC_ADC);
+ AT91F_ADC_EnableChannel(AT91C_BASE_ADC, OPENPICC_ADC_CH_FIELDSTR);
+ AT91F_ADC_EnableIt(AT91C_BASE_ADC, AT91C_ADC_ENDRX |
+ OPENPICC_ADC_CH_FIELDSTR);
+ AT91F_ADC_StartConversion(adc);
+ break;
+ }
+}
+
+int adc_init(void)
+{
+ AT91F_ADC_CfgPMC();
+ AT91F_ADC_CfgTimings(AT91C_BASE_ADC, 48 /*MHz*/, 5 /*MHz*/,
+ 20/*uSec*/, 700/*nSec*/);
+#if 0
+ AT91F_ADC_EnableChannel(AT91C_BASE_ADC, OPENPICC_ADC_CH_FIELDSTR |
+ OPENPICC_ADC_CH_PLL_DEM);
+#endif
+ AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_ADC,
+ AT91C_AIC_PRIOR_LOWEST,
+ AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, &adc_irq);
+ AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_ADC);
+
+ usb_hdlr_register(&adc_usb_in, OPENPCD_CMD_CLS_ADC);
+}
diff --git a/firmware/src/picc/decoder.c b/firmware/src/picc/decoder.c
new file mode 100644
index 0000000..9f9191a
--- /dev/null
+++ b/firmware/src/picc/decoder.c
@@ -0,0 +1,65 @@
+/* Decoder Core for OpenPCD / OpenPICC
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <picc/decoder.h>
+
+#include <os/dbgu.h>
+
+static struct decoder_algo *decoder_algo[DECODER_NUM_ALGOS];
+
+static int get_next_data(struct decoder_state *st, u_int8_t *data)
+{
+ u_int8_t parity_sample;
+ u_int32_t bytesample;
+
+ bytesample = st->algo->get_next_bytesample(st, &parity_sample);
+
+ return st->algo->decode_sample(bytesample, data);
+}
+
+/* iterate over sample buffer (size N bytes) and decode data */
+int decoder_decode(u_int8_t algo, const char *sample_buf,
+ int sample_buf_size, char *data_buf)
+{
+ int i, ret;
+ struct decoder_state st;
+
+ if (algo >= DECODER_NUM_ALGOS)
+ return -EINVAL;
+
+ st.buf = sample_buf;
+ st.buf32 = (u_int32_t *) st.buf;
+ st.bit_ofs = 0;
+ st.algo = decoder_algo[algo];
+
+ for (i = 0; i < (sample_buf_size*8)/st.algo->bits_per_sampled_char;
+ i++) {
+ ret = get_next_data(&st, &data_buf[i]);
+ if (ret < 0) {
+ DEBUGPCR("decoder error %d at data byte %u",
+ ret, i);
+ return ret;
+ }
+ }
+
+ return i+1;
+}
+
+int decoder_register(int algnum, struct decoder_algo *algo)
+{
+ if (algnum >= DECODER_NUM_ALGOS)
+ return -EINVAL;
+
+ decoder_algo[algnum] = algo;
+
+ return 0;
+}
+
+void decoder_init(void)
+{
+ decoder_register(DECODER_MILLER, &miller_decoder);
+ decoder_register(DECODER_NRZL, &nrzl_decoder);
+}
diff --git a/firmware/src/picc/decoder.h b/firmware/src/picc/decoder.h
new file mode 100644
index 0000000..aef0e20
--- /dev/null
+++ b/firmware/src/picc/decoder.h
@@ -0,0 +1,32 @@
+#ifndef _DECODER_H
+#define _DECODER_H
+
+struct decoder_state;
+
+struct decoder_algo {
+ u_int8_t oversampling_rate;
+ u_int8_t bits_per_sampled_char;
+ u_int32_t bytesample_mask;
+ int (*decode_sample)(const u_int32_t sample, u_int8_t data);
+ u_int32_t (*get_next_bytesample)(struct decoder_state *st, u_int8_t *parity_sample);
+};
+
+struct decoder_state {
+ struct decoder_algo *algo;
+ u_int8_t bit_ofs;
+ const char *buf;
+ const u_int32_t *buf32;
+};
+
+extern int decoder_register(int algnum, struct decoder_algo *algo);
+extern int decoder_decode(u_int8_t algo, const char *sample_buf,
+ int sample_buf_size, char *data_buf);
+
+#define DECODER_MILLER 0
+#define DECODER_NRZL 1
+#define DECODER_NUM_ALGOS 2
+
+static struct decoder_algo nrzl_decoder;
+static struct decoder_algo miller_decoder;
+
+#endif
diff --git a/firmware/src/picc/decoder_miller.c b/firmware/src/picc/decoder_miller.c
new file mode 100644
index 0000000..1394a17
--- /dev/null
+++ b/firmware/src/picc/decoder_miller.c
@@ -0,0 +1,112 @@
+/*
+ * ISO14443A modified Miller decoder
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ *
+ * LSB First LSB hex
+ * Sequence X 0010 0100 0x4
+ * Sequence Y 0000 0000 0x0
+ * Sequence Z 1000 0001 0x1
+ *
+ * Logic 1 Sequence X
+ * Logic 0 Sequence Y with two exceptions:
+ * - if there are more contiguous 0, Z used from second one
+ * - if the first bit after SOF is 0, sequence Z used for all contig 0's
+ * SOF Sequence Z
+ * EOF Logic 0 followed by Sequence Y
+ *
+ * cmd hex bits symbols hex (quad-sampled)
+ *
+ * REQA 0x26 S 0110010 E Z ZXXYZXY ZY 0x10410441
+ * WUPA 0x52 S 0100101 E Z ZXYZXYX YY 0x04041041
+ *
+ * SOF is 'eaten' by SSC start condition (Compare 0). Remaining bits are
+ * mirrored, e.g. samples for LSB of first byte are & 0xf
+ *
+ */
+
+#include <sys/types.h>
+
+#include <os/dbgu.h>
+#include <picc/decoder.h>
+
+
+#define OVERSAMPLING_RATE 4
+
+/* definitions for four-times oversampling */
+#define SEQ_X 0x4
+#define SEQ_Y 0x0
+#define SEQ_Z 0x1
+
+/* decode a single sampled bit */
+static u_int8_t miller_decode_sampled_bit(u_int32_t sampled_bit)
+{
+ switch (sampled_bit) {
+ case SEQ_X:
+ return 1;
+ break;
+ case SEQ_Z:
+ case SEQ_Y:
+ return 0;
+ break;
+ default:
+ DEBUGP("unknown sequence sample `%x' ", sampled_bit);
+ return 2;
+ break;
+ }
+}
+
+/* decode a single 32bit data sample of an 8bit miller encoded word */
+static int miller_decode_sample(u_int32_t sample, u_int8_t *data)
+{
+ u_int8_t ret = 0;
+ unsigned int i;
+
+ for (i = 0; i < sizeof(sample)/OVERSAMPLING_RATE; i++) {
+ u_int8_t bit = miller_decode_sampled_bit(sample & 0xf);
+
+ if (bit == 1)
+ ret |= 1;
+ /* else do nothing since ret was initialized with 0 */
+
+ /* skip shifting in case of last data bit */
+ if (i == sizeof(sample)/OVERSAMPLING_RATE)
+ break;
+
+ sample = sample >> OVERSAMPLING_RATE;
+ ret = ret << 1;
+ }
+
+ *data = ret;
+
+ return ret;
+}
+
+static u_int32_t get_next_bytesample(struct decoder_state *ms,
+ u_int8_t *parity_sample)
+{
+ u_int32_t ret = 0;
+
+ /* get remaining bits from the current word */
+ ret = *(ms->buf32) >> ms->bit_ofs;
+ /* move to next word */
+ ms->buf32++;
+
+ /* if required, get remaining bits from next word */
+ if (ms->bit_ofs)
+ ret |= *(ms->buf32) << (32 - ms->bit_ofs);
+
+ *parity_sample = (*(ms->buf32) >> ms->bit_ofs & 0xf);
+
+ /* increment bit offset (modulo 32) */
+ ms->bit_ofs = (ms->bit_ofs + OVERSAMPLING_RATE) % 32;
+
+ return ret;
+}
+
+static struct decoder_algo miller_decoder = {
+ .oversampling_rate = OVERSAMPLING_RATE,
+ .bits_per_sampled_char = 9 * OVERSAMPLING_RATE,
+ .bytesample_mask = 0xffffffff,
+ .decode_sample = &miller_decode_sample,
+ .get_next_bytesample = &get_next_bytesample,
+};
diff --git a/firmware/src/picc/decoder_nrzl.c b/firmware/src/picc/decoder_nrzl.c
new file mode 100644
index 0000000..a1b79aa
--- /dev/null
+++ b/firmware/src/picc/decoder_nrzl.c
@@ -0,0 +1,91 @@
+/* ISO 14443 B Rx (PCD->PICC) implementation
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ *
+ * speed(kbps) 106 212 424 848
+ * etu 128/fc 64/fc 32/fc 16/fc
+ * etu(usec) 9.4 4.7 2.35 1.18
+ *
+ * NRZ-L coding with logic level
+ *
+ * logic 1: carrier high field amplitude (no modulation)
+ * logic 0: carrier low field amplitude
+ *
+ * Character transmission format:
+ * start bit: logic 0
+ * data: eight bits, lsb first
+ * stop bit logic 1
+ *
+ * Frame Format:
+ *
+ * SOF char [EGT char, ...] EOF
+ *
+ * SOF: falling edge, 10..11 etu '0', rising edge in 1etu, 2..3etu '1'
+ * EGT: between 0 and 57uS
+ * EOF: falling edge, 10..11 etu '0', rising edge in 1etu
+ *
+ *
+ * Sampling
+ * - sample once per bit clock, exactly in the middle of it
+ * - synchronize CARRIER_DIV TC0 to first falling edge
+ * - Configure CARRIER_DIV RA compare (rising edge) to be at
+ * etu/2 carrier clocks.
+ * - problem: SOF 12..14etu length, therefore we cannot specify
+ * SOF as full start condition and then sample with 10bit
+ * frames :(
+ *
+ */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <os/dbgu.h>
+#include <picc/decoder.h>
+
+/* currently this code will only work with oversampling_rate == 1 */
+#define OVERSAMPLING_RATE 1
+
+static u_int32_t get_next_bytesample(struct decoder_state *st,
+ u_int8_t *parity_sample)
+{
+ u_int32_t ret = 0;
+ u_int8_t bits_per_sampled_char = st->algo->bits_per_sampled_char;
+ u_int8_t bytesample_mask = st->algo->bytesample_mask;
+
+ /* FIXME: shift start and stop bit into parity_sample and just
+ * return plain 8-bit data word */
+
+ /* first part of 10-databit bytesample */
+ ret = (*(st->buf32) >> st->bit_ofs) & bytesample_mask;
+
+ if (st->bit_ofs > 32 - bits_per_sampled_char) {
+ /* second half of 10-databit bytesample */
+ st->buf32++;
+ ret |= (*(st->buf32) << (32 - st->bit_ofs));
+ }
+ st->bit_ofs = (st->bit_ofs + bits_per_sampled_char) % 32;
+
+ return ret & bytesample_mask;
+}
+
+static int nrzl_decode_sample(const u_int32_t sample, u_int8_t *data)
+{
+ *data = (sample >> 1) & 0xff;
+
+ if (!(sample & 0x01)) {
+ DEBUGPCRF("invalid start bit 0!");
+ return -EIO;
+ }
+ if (sample & 0x20) {
+ DEBUGPCRF("invalid stop bit 1!");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static struct decoder_algo nrzl_decoder = {
+ .oversampling_rate = OVERSAMPLING_RATE,
+ .bits_per_sampled_char = 10 * OVERSAMPLING_RATE,
+ .bytesample_mask = 0x3ff,
+ .decode_sample = &nrzl_decode_sample,
+ .get_next_bytesample = &get_next_bytesample,
+};
diff --git a/firmware/src/picc/iso14443a_manchester.c b/firmware/src/picc/iso14443a_manchester.c
new file mode 100644
index 0000000..aca832c
--- /dev/null
+++ b/firmware/src/picc/iso14443a_manchester.c
@@ -0,0 +1,106 @@
+/*
+ * ISO14443A Manchester encoder
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ *
+ * Definitions for 106kBps, at sampling clock 1695kHz
+ *
+ * bit sample pattern for one bit cycle
+ * MSB first LSB first hex LSB first
+ * Sequence D 1010101000000000 0000000001010101 0x0055
+ * Sequence E 0000000010101010 0101010100000000 0x5500
+ * Sequence F 1010101010101010 0101010101010101 0x5555
+ *
+ * Logic 1 Sequence D
+ * Logic 0 Sequence E
+ * SOF Sequence D
+ * EOF Sequence F
+ *
+ * 212/424/848kBps: BPSK.
+ *
+ * SOF: 32 subcarrier clocks + bit '0'
+ *
+ * SOF: hex LSB first: 0x55555555 55555555 + bit '0'
+ *
+ * EOF: even parity of last byte (!)
+ *
+ */
+
+#define MANCHESTER_SEQ_D 0x0055
+#define MANCHESTER_SEQ_E 0x5500
+#define MANCHESTER_SEQ_F 0x5555
+
+static u_int32_t manchester_sample_size(u_int8_t frame_bytelen)
+{
+ /* 16 bits (2 bytes) per bit => 16 bytes samples per data byte,
+ * plus 16bit (2 bytes) parity per data byte
+ * plus 16bit (2 bytes) SOF plus 16bit (2 bytes) EOF */
+ return (frame_bytelen*18) + 2 + 2;
+
+ /* this results in a maximum samples-per-frame size of 4612 bytes
+ * for a 256byte frame */
+}
+
+struct manch_enc_state {
+ const char *data;
+ char *samples;
+ u_int16_t *samples16;
+};
+
+static void manchester_enc_byte(struct manch_enc_state *mencs, u_int8_t data)
+{
+ int i;
+ u_int8_t sum_1 = 0;
+
+ /* append 8 sample blobs, one for each bit */
+ for (i = 0; i < 8; i++) {
+ if (data & (1 << i)) {
+ *(mencs->samples16) = MANCHESTER_SEQ_D;
+ sum_1++;
+ } else {
+ *(mencs->samples16) = MANCHESTER_SEQ_E;
+ }
+ mencs->samples16++
+ }
+ /* append odd parity */
+ if (sum_1 & 0x01)
+ *(mencs->samples16) = MANCHESTER_SEQ_E;
+ else
+ *(mencs->samples16) = MANCHESTER_SEQ_D;
+ mencs->samples16++
+}
+
+int manchester_encode(char *sample_buf, u_int16_t sample_buf_len,
+ const char *data, u_int8_t data_len)
+{
+ int i, enc_size;
+ struct manch_enc_state mencs
+
+ enc_size = manchester_sample_size(data_len);
+
+ if (sample_buf_len < enc_size)
+ return -EINVAL;
+
+ /* SOF */
+ *(mencs.samples16++) = MANCHESTER_SEQ_D;
+
+ for (i = 0; i < data_len; i++)
+ manchester_enc_byte(mencs, data[i]);
+
+ /* EOF */
+ *(mencs.samples16++) = MANCHESTER_SEQ_F;
+
+ return enc_size;
+}
+
+#define BPSK_SPEED_212
+
+
+static u_int32_t bpsk_sample_size(u_int8_t frame_bytelen)
+
+int bpsk_encode(char *sample_buf, u_int16_t sample_buf_len,
+ const char *data, u_int8_t data_len)
+{
+ /* burst of 32 sub carrier cycles */
+ memset(sample_buf, 0x55, 8);
+
+}
diff --git a/firmware/src/picc/load_modulation.c b/firmware/src/picc/load_modulation.c
new file mode 100644
index 0000000..65c59d1
--- /dev/null
+++ b/firmware/src/picc/load_modulation.c
@@ -0,0 +1,29 @@
+#include <sys/types.h>
+#include <lib_AT91SAM7.h>
+
+#include "../openpcd.h"
+
+void load_mod_level(u_int8_t level)
+{
+ if (level > 3)
+ level = 3;
+
+ if (level & 0x1)
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC_PIO_LOAD1);
+ else
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC_PIO_LOAD1);
+
+ if (level & 0x2)
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC_PIO_LOAD2);
+ else
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC_PIO_LOAD2);
+}
+
+void load_mod_init(void)
+{
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC_PIO_LOAD1);
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC_PIO_LOAD2);
+
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC_PIO_LOAD1);
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC_PIO_LOAD2);
+}
diff --git a/firmware/src/picc/load_modulation.h b/firmware/src/picc/load_modulation.h
new file mode 100644
index 0000000..71f9d6f
--- /dev/null
+++ b/firmware/src/picc/load_modulation.h
@@ -0,0 +1,7 @@
+#ifndef _LOAD_MODULATION_H
+#define _LOAD_MODULATION_H
+
+extern void load_mod_level(u_int8_t level);
+extern void load_mod_init(void);
+
+#endif
diff --git a/firmware/src/picc/main_openpicc.c b/firmware/src/picc/main_openpicc.c
new file mode 100644
index 0000000..312194b
--- /dev/null
+++ b/firmware/src/picc/main_openpicc.c
@@ -0,0 +1,215 @@
+#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 <picc/poti.h>
+#include <picc/pll.h>
+#include <picc/ssc_picc.h>
+#include <picc/load_modulation.h>
+
+static const u_int16_t cdivs[] = { 128, 64, 32, 16 };
+static u_int8_t cdiv_idx = 0;
+
+static u_int16_t duty_percent = 22;
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+static u_int32_t pwm_freq[] = { 105937, 211875, 423750, 847500 };
+static u_int8_t pwm_freq_idx = 0;
+
+static u_int8_t load_mod = 0;
+
+void _init_func(void)
+{
+ pio_irq_init();
+ pll_init();
+ poti_init();
+ load_mod_init();
+ tc_cdiv_init();
+ //tc_fdt_init();
+ pwm_init();
+ adc_init();
+ ssc_rx_init();
+ // ssc_tx_init();
+
+ AT91F_PIO_CfgInput(AT91C_BASE_PIOA, OPENPICC_PIO_BOOTLDR);
+}
+
+static void help(void)
+{
+ DEBUGPCR("q: poti decrease q: poti increase\r\n"
+ "e: poti retransmit P: PLL inhibit toggle");
+ DEBUGPCR("o: decrease duty p: increase duty\r\n"
+ "k: stop pwm l: start pwn\r\n"
+ "n: decrease freq m: incresae freq");
+ DEBUGPCR("u: PA23 const 1 y: PA23 const 0\r\n"
+ "t: PA23 PWM0 L: display PLL LOCK\r\n"
+ "{: decrease cdiv_idx }: increse cdiv idx\r\n"
+ "<: decrease cdiv_phase >: increase cdiv_phase");
+ DEBUGPCR("v: decrease load_mod b: increase load_mod\r\n"
+ "B: read button S: toggle nSLAVE_RESET\r\n"
+ "a: SSC stop s: SSC start\r\n"
+ "d: SSC mode select");
+}
+
+int _main_dbgu(char key)
+{
+ static u_int8_t poti = 64;
+ static u_int8_t pll_inh = 1;
+ static u_int8_t ssc_mode = 1;
+
+ DEBUGPCRF("main_dbgu");
+
+ switch (key) {
+ case 'q':
+ if (poti > 0)
+ poti--;
+ poti_comp_carr(poti);
+ DEBUGPCRF("Poti: %u", poti);
+ break;
+ case 'w':
+ if (poti < 127)
+ poti++;
+ poti_comp_carr(poti);
+ DEBUGPCRF("Poti: %u", poti);
+ break;
+ case 'e':
+ poti_comp_carr(poti);
+ DEBUGPCRF("Poti: %u", poti);
+ break;
+ case 'P':
+ pll_inh++;
+ pll_inh &= 0x01;
+ pll_inhibit(pll_inh);
+ DEBUGPCRF("PLL Inhibit: %u", pll_inh);
+ break;
+ case 'L':
+ DEBUGPCRF("PLL Lock: %u", pll_is_locked());
+ break;
+ case 'o':
+ if (duty_percent >= 1)
+ duty_percent--;
+ pwm_duty_set_percent(0, duty_percent);
+ break;
+ case 'p':
+ if (duty_percent <= 99)
+ duty_percent++;
+ pwm_duty_set_percent(0, duty_percent);
+ break;
+ case 'k':
+ pwm_stop(0);
+ break;
+ case 'l':
+ pwm_start(0);
+ break;
+ case 'n':
+ if (pwm_freq_idx > 0) {
+ pwm_freq_idx--;
+ pwm_stop(0);
+ pwm_freq_set(0, pwm_freq[pwm_freq_idx]);
+ pwm_start(0);
+ pwm_duty_set_percent(0, 22); /* 22% of 9.43uS = 2.07uS */
+ }
+ break;
+ case 'm':
+ if (pwm_freq_idx < ARRAY_SIZE(pwm_freq)-1) {
+ pwm_freq_idx++;
+ pwm_stop(0);
+ pwm_freq_set(0, pwm_freq[pwm_freq_idx]);
+ pwm_start(0);
+ pwm_duty_set_percent(0, 22); /* 22% of 9.43uS = 2.07uS */
+ }
+ break;
+ case 'u':
+ DEBUGPCRF("PA23 output high");
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, AT91C_PIO_PA23);
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, AT91C_PIO_PA23);
+ break;
+ case 'y':
+ DEBUGPCRF("PA23 output low");
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, AT91C_PIO_PA23);
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, AT91C_PIO_PA23);
+ break;
+ case 't':
+ DEBUGPCRF("PA23 PeriphA (PWM)");
+ AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, 0, AT91C_PA23_PWM0);
+ break;
+ case '?':
+ help();
+ break;
+ case '<':
+ tc_cdiv_phase_inc();
+ break;
+ case '>':
+ tc_cdiv_phase_dec();
+ break;
+ case '{':
+ if (cdiv_idx > 0)
+ cdiv_idx--;
+ tc_cdiv_set_divider(cdivs[cdiv_idx]);
+ break;
+ case '}':
+ if (cdiv_idx < ARRAY_SIZE(cdivs)-1)
+ cdiv_idx++;
+ tc_cdiv_set_divider(cdivs[cdiv_idx]);
+ break;
+ case 'v':
+ if (load_mod > 0)
+ load_mod--;
+ load_mod_level(load_mod);
+ DEBUGPCR("load_mod: %u\n", load_mod);
+ break;
+ case 'b':
+ if (load_mod < 3)
+ load_mod++;
+ load_mod_level(load_mod);
+ DEBUGPCR("load_mod: %u\n", load_mod);
+ break;
+ case 'B':
+ DEBUGPCRF("Button status: %u\n",
+ AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, AT91F_PIO_IsInputSet));
+ break;
+ case 'S':
+ if (AT91F_PIO_IsOutputSet(AT91C_BASE_PIOA, OPENPICC_PIO_nSLAVE_RESET)) {
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC_PIO_nSLAVE_RESET);
+ DEBUGPCRF("nSLAVE_RESET == LOW");
+ } else {
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC_PIO_nSLAVE_RESET);
+ DEBUGPCRF("nSLAVE_RESET == HIGH");
+ }
+ break;
+ case 'a':
+ DEBUGPCRF("SSC RX STOP");
+ ssc_rx_stop();
+ break;
+ case 's':
+ DEBUGPCRF("SSC RX START");
+ ssc_rx_start();
+ break;
+ case 'd':
+ ssc_rx_mode_set(++ssc_mode);
+ DEBUGPCRF("SSC MODE %u", ssc_mode);
+ break;
+ }
+
+ tc_cdiv_print();
+
+ 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();
+
+ ssc_rx_unthrottle();
+}
diff --git a/firmware/src/picc/piccsim.h b/firmware/src/picc/piccsim.h
new file mode 100644
index 0000000..3cf6514
--- /dev/null
+++ b/firmware/src/picc/piccsim.h
@@ -0,0 +1,23 @@
+
+#include <librfid/rfid_layer2_iso14443a.h>
+
+struct piccsim_state {
+ enum rfid_layer2_id l2prot;
+ unsigned char uid[10];
+ u_int8_t uid_len;
+ union {
+ struct {
+ enum iso14443a_state state;
+ enum iso14443a_level level;
+ u_int32_t flags;
+ } iso14443a;
+ struct {
+ } iso14443b;
+ } l2;
+
+ union {
+ u_int32_t flags;
+ } proto;
+}
+
+#define PICCSIM_PROT_F_AUTO_WTX 0x01
diff --git a/firmware/src/picc/pll.c b/firmware/src/picc/pll.c
new file mode 100644
index 0000000..bed08ef
--- /dev/null
+++ b/firmware/src/picc/pll.c
@@ -0,0 +1,40 @@
+
+#include <sys/types.h>
+#include <lib_AT91SAM7.h>
+#include <os/pio_irq.h>
+#include <os/dbgu.h>
+#include "../openpcd.h"
+
+void pll_inhibit(int inhibit)
+{
+ if (inhibit)
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC_PIO_PLL_INHIBIT);
+ else
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC_PIO_PLL_INHIBIT);
+}
+
+int pll_is_locked(void)
+{
+ return AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, OPENPICC_PIO_PLL_LOCK);
+}
+
+static void pll_lock_change_cb(u_int32_t pio)
+{
+ DEBUGPCRF("PLL LOCK: %d", pll_is_locked());
+#if 1
+ if (pll_is_locked())
+ led_switch(1, 1);
+ else
+ led_switch(1, 0);
+#endif
+}
+
+void pll_init(void)
+{
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC_PIO_PLL_INHIBIT);
+ AT91F_PIO_CfgInput(AT91C_BASE_PIOA, OPENPICC_PIO_PLL_LOCK);
+ pll_inhibit(0);
+
+ pio_irq_register(OPENPICC_PIO_PLL_LOCK, &pll_lock_change_cb);
+ pio_irq_enable(OPENPICC_PIO_PLL_LOCK);
+}
diff --git a/firmware/src/picc/pll.h b/firmware/src/picc/pll.h
new file mode 100644
index 0000000..fc00105
--- /dev/null
+++ b/firmware/src/picc/pll.h
@@ -0,0 +1,8 @@
+#ifndef _PLL_H
+#define _PLL_H
+
+extern int pll_is_locked(void);
+extern void pll_inhibit(int inhibit);
+extern void pll_init(void);
+
+#endif
diff --git a/firmware/src/picc/poti.c b/firmware/src/picc/poti.c
new file mode 100644
index 0000000..2cc8e5c
--- /dev/null
+++ b/firmware/src/picc/poti.c
@@ -0,0 +1,58 @@
+/* SPI Potentiometer AD 7367 Driver for OpenPICC
+ * (C) by Harald Welte <hwelte@hmw-consulting.de>
+ */
+
+#include <sys/types.h>
+#include <lib_AT91SAM7.h>
+#include "../openpcd.h"
+
+static const AT91PS_SPI spi = AT91C_BASE_SPI;
+
+void poti_comp_carr(u_int8_t position)
+{
+ volatile int i;
+
+ while (!(spi->SPI_SR & AT91C_SPI_TDRE)) { }
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC_PIO_SS2_DT_THRESH);
+ //for (i = 0; i < 0xff; i++) { }
+ /* shift one left, since it is a seven-bit value written as 8 bit xfer */
+ spi->SPI_TDR = position & 0x7f;
+ while (!(spi->SPI_SR & AT91C_SPI_TDRE)) { }
+ for (i = 0; i < 0xff; i++) { }
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC_PIO_SS2_DT_THRESH);
+}
+
+void poti_reset(void)
+{
+ volatile int i;
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC_PIO_nSLAVE_RESET);
+ for (i = 0; i < 0xff; i++) { }
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC_PIO_nSLAVE_RESET);
+}
+
+void poti_init(void)
+{
+ AT91F_SPI_CfgPMC();
+ AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA,
+ AT91C_PA13_MOSI | AT91C_PA14_SPCK, 0);
+
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC_PIO_SS2_DT_THRESH);
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC_PIO_SS2_DT_THRESH);
+
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC_PIO_nSLAVE_RESET);
+ poti_reset();
+
+#if 0
+ AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_SPI,
+ OPENPCD_IRQ_PRIO_SPI,
+ AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, &spi_irq);
+ AT91G_AIC_EnableIt(AT9C_BASE_AIC, AT91C_ID_SPI);
+#endif
+ AT91F_SPI_CfgMode(spi, AT91C_SPI_MSTR |
+ AT91C_SPI_PS_FIXED | AT91C_SPI_MODFDIS);
+ /* CPOL = 0, NCPHA = 1, CSAAT = 0, BITS = 0000, SCBR = 13 (3.69MHz),
+ * DLYBS = 6 (125nS), DLYBCT = 0 */
+ AT91F_SPI_CfgCs(spi, 0, AT91C_SPI_BITS_8 | AT91C_SPI_NCPHA |
+ (13 << 8) | (6 << 16));
+ AT91F_SPI_Enable(spi);
+}
diff --git a/firmware/src/picc/poti.h b/firmware/src/picc/poti.h
new file mode 100644
index 0000000..92ec00d
--- /dev/null
+++ b/firmware/src/picc/poti.h
@@ -0,0 +1,7 @@
+#ifndef _POTI_H
+#define _POTI_H
+
+extern void poti_comp_carr(u_int8_t position);
+extern void poti_init(void);
+
+#endif
diff --git a/firmware/src/picc/ssc_picc.c b/firmware/src/picc/ssc_picc.c
new file mode 100644
index 0000000..fac2c62
--- /dev/null
+++ b/firmware/src/picc/ssc_picc.c
@@ -0,0 +1,384 @@
+/* AT91SAM7 SSC controller routines for OpenPCD
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ *
+ * We use SSC for both TX and RX side.
+ *
+ * RX side is interconnected with demodulated carrier
+ *
+ * TX side is interconnected with load modulation circuitry
+ */
+
+#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 "../openpcd.h"
+
+/* definitions for four-times oversampling */
+#define REQA 0x10410441
+#define WUPA 0x04041041
+
+static const AT91PS_SSC ssc = AT91C_BASE_SSC;
+static AT91PS_PDC rx_pdc;
+
+enum ssc_mode {
+ SSC_MODE_NONE,
+ SSC_MODE_14443A_SHORT,
+ SSC_MODE_14443A_STANDARD,
+ SSC_MODE_14443B,
+ SSC_MODE_EDGE_ONE_SHOT,
+};
+
+struct ssc_state {
+ struct req_ctx *rx_ctx[2];
+ enum ssc_mode mode;
+};
+static struct ssc_state ssc_state;
+
+/* This is for four-times oversampling */
+#define ISO14443A_SOF_SAMPLE 0x08
+#define ISO14443A_SOF_LEN 4
+
+void ssc_rx_mode_set(enum ssc_mode ssc_mode)
+{
+ u_int8_t data_len, num_data, sync_len;
+ u_int32_t start_cond;
+
+ /* disable Rx */
+ ssc->SSC_CR = AT91C_SSC_RXDIS;
+
+ /* disable all Rx related interrupt sources */
+ AT91F_SSC_DisableIt(ssc, AT91C_SSC_RXRDY |
+ AT91C_SSC_OVRUN | AT91C_SSC_ENDRX |
+ AT91C_SSC_RXBUFF | AT91C_SSC_RXSYN |
+ AT91C_SSC_CP0 | AT91C_SSC_CP1);
+
+ switch (ssc_mode) {
+ case SSC_MODE_14443A_SHORT:
+ start_cond = AT91C_SSC_START_0;
+ sync_len = ISO14443A_SOF_LEN;
+ ssc->SSC_RC0R = ISO14443A_SOF_SAMPLE;
+ data_len = 32;
+ num_data = 1;
+ break;
+ case SSC_MODE_14443A_STANDARD:
+ start_cond = AT91C_SSC_START_0;
+ sync_len = ISO14443A_SOF_LEN;
+ ssc->SSC_RC0R = ISO14443A_SOF_SAMPLE;
+ data_len = 1;
+ num_data = 1; /* FIXME */
+ break;
+ case SSC_MODE_14443B:
+ /* start sampling at first falling data edge */
+ //start_cond =
+ break;
+ case SSC_MODE_EDGE_ONE_SHOT:
+ //start_cond = AT91C_SSC_START_EDGE_RF;
+ start_cond = AT91C_SSC_START_CONTINOUS;
+ sync_len = 0;
+ data_len = 8;
+ num_data = 50;
+ break;
+ default:
+ return;
+ }
+ ssc->SSC_RFMR = (data_len-1) & 0x1f |
+ (((num_data-1) & 0x0f) << 8) |
+ (((sync_len-1) & 0x0f) << 16);
+ ssc->SSC_RCMR = AT91C_SSC_CKS_RK | AT91C_SSC_CKO_NONE | start_cond;
+
+ AT91F_PDC_EnableRx(rx_pdc);
+ /* Enable RX interrupts */
+ AT91F_SSC_EnableIt(ssc, AT91C_SSC_OVRUN |
+ AT91C_SSC_ENDRX | AT91C_SSC_RXBUFF);
+
+ ssc_state.mode = ssc_mode;
+
+}
+
+static void ssc_tx_mode_set(enum ssc_mode ssc_mode)
+{
+ u_int8_t data_len, num_data, sync_len;
+ u_int32_t start_cond;
+
+ /* disable Tx */
+ ssc->SSC_CR = AT91C_SSC_TXDIS;
+
+ /* disable all Tx related interrupt sources */
+ ssc->SSC_IDR = AT91C_SSC_TXRDY | AT91C_SSC_TXEMPTY | AT91C_SSC_ENDTX |
+ AT91C_SSC_TXBUFE | AT91C_SSC_TXSYN;
+
+ switch (ssc_mode) {
+ case SSC_MODE_14443A_SHORT:
+ start_cond = AT91C_SSC_START_RISE_RF;
+ sync_len = ISO14443A_SOF_LEN;
+ data_len = 32;
+ num_data = 1;
+ break;
+ case SSC_MODE_14443A_STANDARD:
+ start_cond = AT91C_SSC_START_0;
+ sync_len = ISO14443A_SOF_LEN;
+ ssc->SSC_RC0R = ISO14443A_SOF_SAMPLE;
+ data_len = 1;
+ num_data = 1; /* FIXME */
+ break;
+ }
+ ssc->SSC_TFMR = (data_len-1) & 0x1f |
+ (((num_data-1) & 0x0f) << 8) |
+ (((sync_len-1) & 0x0f) << 16);
+ ssc->SSC_TCMR = AT91C_SSC_CKS_RK | AT91C_SSC_CKO_NONE | start_cond;
+
+#if 0
+ /* Enable RX interrupts */
+ AT91F_SSC_EnableIt(ssc, AT91C_SSC_OVRUN |
+ AT91C_SSC_ENDRX | AT91C_SSC_RXBUFF);
+ AT91F_PDC_EnableRx(rx_pdc);
+
+ ssc_state.mode = ssc_mode;
+#endif
+}
+
+
+
+
+static struct openpcd_hdr opcd_ssc_hdr = {
+ .cmd = OPENPCD_CMD_SSC_READ,
+};
+
+static inline void init_opcdhdr(struct req_ctx *rctx)
+{
+ memcpy(&rctx->tx.data[0], &opcd_ssc_hdr, sizeof(opcd_ssc_hdr));
+ rctx->tx.tot_len = MAX_HDRSIZE + MAX_REQSIZE -1;
+}
+
+#ifdef DEBUG_SSC_REFILL
+#define DEBUGR(x, args ...) DEBUGPCRF(x, ## args)
+#else
+#define DEBUGR(x, args ...)
+#endif
+
+static char dmabuf1[512];
+static char dmabuf2[512];
+
+/* Try to refill RX dma descriptors. Return values:
+ * 0) no dma descriptors empty
+ * 1) filled next/secondary descriptor
+ * 2) filled both primary and secondary descriptor
+ * -1) no free request contexts to use
+ * -2) only one free request context, but two free descriptors
+ */
+static int8_t ssc_rx_refill(void)
+{
+#if 1
+ struct req_ctx *rctx;
+
+ rctx = req_ctx_find_get(RCTX_STATE_FREE, RCTX_STATE_SSC_RX_BUSY);
+ if (!rctx) {
+ DEBUGPCRF("no rctx for refill!");
+ return -1;
+ }
+ init_opcdhdr(rctx);
+
+ if (AT91F_PDC_IsRxEmpty(rx_pdc)) {
+ DEBUGR("filling primary SSC RX dma ctx");
+ AT91F_PDC_SetRx(rx_pdc, &rctx->rx.data[MAX_HDRSIZE],
+ (sizeof(rctx->rx.data)-MAX_HDRSIZE)>>2);
+ ssc_state.rx_ctx[0] = rctx;
+
+ /* If primary is empty, secondary must be empty, too */
+ rctx = req_ctx_find_get(RCTX_STATE_FREE,
+ RCTX_STATE_SSC_RX_BUSY);
+ if (!rctx) {
+ DEBUGPCRF("no rctx for secondary refill!");
+ return -2;
+ }
+ init_opcdhdr(rctx);
+ }
+
+ if (AT91F_PDC_IsNextRxEmpty(rx_pdc)) {
+ DEBUGR("filling secondary SSC RX dma ctx");
+ AT91F_PDC_SetNextRx(rx_pdc, &rctx->rx.data[MAX_HDRSIZE],
+ (sizeof(rctx->rx.data)-MAX_HDRSIZE)>2);
+ ssc_state.rx_ctx[1] = rctx;
+ return 2;
+ } else {
+ /* we were unable to fill*/
+ DEBUGPCRF("prim/secnd DMA busy, can't refill");
+ req_ctx_put(rctx);
+ return 0;
+ }
+#else
+ if (AT91F_PDC_IsRxEmpty(rx_pdc))
+ AT91F_PDC_SetRx(rx_pdc, dmabuf1, sizeof(dmabuf1)>>2);
+
+ if (AT91F_PDC_IsNextRxEmpty(rx_pdc))
+ AT91F_PDC_SetNextRx(rx_pdc, dmabuf2, sizeof(dmabuf2)>>2);
+ else
+ DEBUGPCRF("prim/secnd DMA busy, can't refill");
+#endif
+}
+
+#define ISO14443A_FDT_SHORT_1 1236
+#define ISO14443A_FDT_SHORT_0 1172
+
+static void ssc_irq(void)
+{
+ u_int32_t ssc_sr = ssc->SSC_SR;
+ DEBUGP("ssc_sr=0x%08x, mode=%u: ", ssc_sr, ssc_state.mode);
+
+ if (ssc_sr & AT91C_SSC_OVRUN)
+ DEBUGP("RX OVERRUN ");
+
+ switch (ssc_state.mode) {
+ case SSC_MODE_14443A_SHORT:
+ if (ssc_sr & AT91C_SSC_RXSYN)
+ DEBUGP("RXSYN ");
+ if (ssc_sr & AT91C_SSC_RXRDY) {
+ u_int32_t sample = ssc->SSC_RHR;
+ DEBUGP("RXRDY=0x%08x ", sample);
+ /* Try to set FDT compare register ASAP */
+ if (sample == REQA) {
+ tc_fdt_set(ISO14443A_FDT_SHORT_0);
+ /* FIXME: prepare and configure ATQA response */
+ } else if (sample == WUPA) {
+ tc_fdt_set(ISO14443A_FDT_SHORT_1);
+ /* FIXME: prepare and configure ATQA response */
+ } else
+ DEBUGP("<== unknown ");
+ }
+ break;
+
+ case SSC_MODE_14443A_STANDARD:
+ case SSC_MODE_EDGE_ONE_SHOT:
+
+ if (ssc_sr & (AT91C_SSC_ENDRX | AT91C_SSC_RXBUFF)) {
+#if 1
+ /* Mark primary RCTX as ready to send for usb */
+ req_ctx_set_state(ssc_state.rx_ctx[0],
+ RCTX_STATE_UDP_EP2_PENDING);
+ /* second buffer gets propagated to primary */
+ ssc_state.rx_ctx[0] = ssc_state.rx_ctx[1];
+ ssc_state.rx_ctx[1] = NULL;
+#endif
+
+ if (ssc_sr & AT91C_SSC_RXBUFF) {
+ DEBUGP("RXBUFF, shouldn't happen! ");
+#if 0
+ req_ctx_set_state(ssc_state.rx_ctx[0],
+ RCTX_STATE_UDP_EP2_PENDING);
+#endif
+ }
+ if (ssc_rx_refill() == -1)
+ AT91F_AIC_DisableIt(ssc, AT91C_SSC_ENDRX |
+ AT91C_SSC_RXBUFF |
+ AT91C_SSC_OVRUN);
+ }
+ break;
+ }
+ DEBUGPCR("");
+ AT91F_AIC_ClearIt(AT91C_BASE_AIC, AT91C_ID_SSC);
+}
+
+
+void ssc_rx_unthrottle(void)
+{
+ AT91F_SSC_EnableIt(ssc, AT91C_SSC_ENDRX |
+ AT91C_SSC_RXBUFF | AT91C_SSC_OVRUN);
+}
+
+void ssc_rx_start(void)
+{
+ DEBUGPCRF("starting SSC RX\n");
+
+ /* Enable Reception */
+ AT91F_SSC_EnableRx(ssc);
+}
+
+void ssc_rx_stop(void)
+{
+ /* Disable reception */
+ AT91F_SSC_DisableRx(ssc);
+}
+
+void ssc_tx_init(void)
+{
+ /* IMPORTANT: Disable PA23 (PWM0) output, since it is connected to
+ * PA17 !! */
+ AT91F_PIO_CfgInput(AT91C_BASE_PIOA, OPENPCD_PIO_MFIN_PWM);
+ AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, OPENPCD_PIO_MFIN_SSC_TX |
+ OPENPCD_PIO_MFOUT_SSC_RX | OPENPCD_PIO_SSP_CKIN,
+ 0);
+}
+
+static int ssc_usb_in(struct req_ctx *rctx)
+{
+ struct openpcd_hdr *poh = (struct openpcd_hdr *) &rctx->rx.data[0];
+ struct openpcd_hdr *pih = (struct openpcd_hdr *) &rctx->tx.data[0];
+
+ switch (poh->cmd) {
+ case OPENPCD_CMD_SSC_READ:
+ /* FIXME: allow host to specify mode */
+ ssc_rx_mode_set(SSC_MODE_EDGE_ONE_SHOT);
+ ssc_rx_start();
+ break;
+ case OPENPCD_CMD_SSC_WRITE:
+ /* FIXME: implement this */
+ //ssc_tx_start()
+ break;
+ }
+
+ req_ctx_put(rctx);
+ return -EINVAL;
+}
+
+void ssc_rx_init(void)
+{
+ rx_pdc = (AT91PS_PDC) &(ssc->SSC_RPR);
+
+ AT91F_SSC_CfgPMC();
+
+ AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA,
+ OPENPCD_PIO_MFOUT_SSC_RX | OPENPCD_PIO_SSP_CKIN,
+ 0);
+
+ AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_SSC,
+ OPENPCD_IRQ_PRIO_SSC,
+ AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, &ssc_irq);
+ /* Reset */
+ //ssc->SSC_CR = AT91C_SSC_SWRST;
+
+ /* don't divide clock */
+ ssc->SSC_CMR = 0;
+
+ ssc->SSC_RCMR = AT91C_SSC_CKS_RK | AT91C_SSC_CKO_NONE |
+ AT91C_SSC_START_CONTINOUS;
+ /* Data bits per Data N = 32-1, Data words per Frame = 15-1 (=60 byte)*/
+ ssc->SSC_RFMR = 31 | AT91C_SSC_MSBF | (14 << 8);
+
+ ssc_rx_mode_set(SSC_MODE_EDGE_ONE_SHOT);
+
+ AT91F_PDC_EnableRx(rx_pdc);
+
+ /* Enable RX interrupts */
+ AT91F_SSC_EnableIt(ssc, AT91C_SSC_OVRUN |
+ AT91C_SSC_ENDRX | AT91C_SSC_RXBUFF);
+ AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_SSC);
+
+ usb_hdlr_register(&ssc_usb_in, OPENPCD_CMD_CLS_SSC);
+}
+
+void ssc_fini(void)
+{
+ usb_hdlr_unregister(OPENPCD_CMD_CLS_SSC);
+ AT91F_PDC_DisableRx(rx_pdc);
+ AT91F_SSC_DisableTx(ssc);
+ AT91F_SSC_DisableRx(ssc);
+ AT91F_SSC_DisableIt(ssc, 0xfff);
+ AT91F_PMC_DisablePeriphClock(AT91C_BASE_PMC,
+ ((unsigned int) 1 << AT91C_ID_SSC));
+}
diff --git a/firmware/src/picc/ssc_picc.h b/firmware/src/picc/ssc_picc.h
new file mode 100644
index 0000000..710ee45
--- /dev/null
+++ b/firmware/src/picc/ssc_picc.h
@@ -0,0 +1,15 @@
+#ifndef _SSC_H
+#define _SSC_H
+
+extern void ssc_rx_start(void);
+extern void ssc_rx_stop(void);
+
+/* Rx/Tx initialization separate, since Tx disables PWM output ! */
+extern void ssc_tx_init(void);
+extern void ssc_rx_init(void);
+
+extern void ssc_fini(void);
+
+extern void ssc_rx_unthrottle(void);
+
+#endif
diff --git a/firmware/src/picc/tc_fdt.c b/firmware/src/picc/tc_fdt.c
new file mode 100644
index 0000000..041001d
--- /dev/null
+++ b/firmware/src/picc/tc_fdt.c
@@ -0,0 +1,50 @@
+/* OpenPC TC (Timer / Clock) support code
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ *
+ * PICC Simulator Side:
+ * In order to support responding to synchronous frames (REQA/WUPA/ANTICOL),
+ * we need a second Timer/Counter (TC2). This unit is reset by an external
+ * event (rising edge of modulation pause PCD->PICC) connected to TIOB2, and
+ * counts up to a configurable number of carrier clock cycles (RA). Once the
+ * RA value is reached, TIOA2 will see a rising edge. This rising edge will
+ * be interconnected to TF (Tx Frame) of the SSC to start transmitting our
+ * synchronous response.
+ *
+ */
+
+#include <lib_AT91SAM7.h>
+#include <AT91SAM7.h>
+#include <os/dbgu.h>
+
+#include "../openpcd.h"
+#include <os/tc_cdiv.h>
+#include <picc/tc_fdt.h>
+
+void tc_fdt_set(u_int16_t count)
+{
+ tcb->TCB_TC2.TC_RA = count;
+}
+
+void tc_fdt_init(void)
+{
+ AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, AT91C_PA15_TF,
+ AT91C_PA26_TIOA2 | AT91C_PA27_TIOB2);
+ AT91F_PMC_EnablePeriphClock(AT91C_BASE_PMC,
+ ((unsigned int) 1 << AT91C_ID_TC2));
+ /* Enable Clock for TC2 */
+ tcb->TCB_TC2.TC_CCR = AT91C_TC_CLKEN;
+
+ /* Clock XC1, Wave Mode, No automatic reset on RC comp
+ * TIOA2 in RA comp = set, TIOA2 on RC comp = clear,
+ * TIOB2 as input, EEVT = TIOB2, Reset/Trigger on EEVT */
+ tcb->TCB_TC2.TC_CMR = AT91C_TC_CLKS_XC1 | AT91C_TC_WAVE |
+ AT91C_TC_WAVESEL_UP |
+ AT91C_TC_ACPA_SET | AT91C_TC_ACPC_CLEAR |
+ AT91C_TC_BEEVT_NONE | AT91C_TC_BCPB_NONE |
+ AT91C_TC_EEVT_TIOB | AT91C_TC_ETRGEDG_RISING |
+ AT91C_TC_ENETRG ;
+
+ /* Reset to start timers */
+ tcb->TCB_BCR = 1;
+}
+
diff --git a/firmware/src/picc/tc_fdt.h b/firmware/src/picc/tc_fdt.h
new file mode 100644
index 0000000..b39b935
--- /dev/null
+++ b/firmware/src/picc/tc_fdt.h
@@ -0,0 +1,9 @@
+#ifndef _TC_FDT_H
+#define _TC_FDT_H
+
+#include <sys/types.h>
+
+extern void tc_fdt_init(void);
+extern void tc_fdt_set(u_int16_t count);
+
+#endif
diff --git a/firmware/src/start/Cstartup.S b/firmware/src/start/Cstartup.S
new file mode 100644
index 0000000..e91fd86
--- /dev/null
+++ b/firmware/src/start/Cstartup.S
@@ -0,0 +1,386 @@
+/*------------------------------------------------------------------------------
+//*- ATMEL Microcontroller Software Support - ROUSSET -
+//*------------------------------------------------------------------------------
+//* The software is delivered "AS IS" without warranty or condition of any
+//* kind, either express, implied or statutory. This includes without
+//* limitation any warranty or condition with respect to merchantability or
+//* fitness for any particular purpose, or against the infringements of
+//* intellectual property rights of others.
+//*-----------------------------------------------------------------------------
+//*- File source : Cstartup.s
+//*- Object : Generic CStartup for KEIL and GCC No Use REMAP
+//*- Compilation flag : None
+//*-
+//*- 1.0 18/Oct/04 JPP : Creation
+//*- 1.1 21/Feb/05 JPP : Set Interrupt
+//*- 1.1 01/Apr/05 JPP : save SPSR
+//*-----------------------------------------------------------------------------*/
+
+#define CONFIG_DFU_SWITCH
+
+//#define DEBUG_LL
+
+#ifdef DEBUG_LL
+/* Debugging macros for switching on/off LED1 (green) */
+#define PIOA_PER 0xFFFFF400
+#define PIOA_OER 0xFFFFF410
+#define PIOA_SODR 0xFFFFF430
+#define PIOA_CODR 0xFFFFF434
+#define LED1 25 /* this only works on OpenPICC, not Olimex */
+ .macro led1on
+ ldr r2, =PIOA_CODR
+ mov r1, #(1 << LED1)
+ str r1, [r2]
+ .endm
+ .macro led1off
+ ldr r2, =PIOA_SODR
+ mov r1, #(1 << LED1)
+ str r1, [r2]
+ .endm
+ .macro ledinit
+ ldr r2, =PIOA_PER
+ mov r1, #(1 << LED1)
+ str r1, [r2]
+ ldr r2, =PIOA_OER
+ str r1, [r2]
+ led1off
+ .endm
+#else
+ .macro ledinit
+ .endm
+ .macro led1on
+ .endm
+ .macro led1off
+ .endm
+#endif
+
+ .equ IRQ_Stack_Size, 0x00000400
+
+ .equ AIC_IVR, (256)
+ .equ AIC_FVR, (260)
+ .equ AIC_EOICR, (304)
+ .equ AIC_MCR_RCR, (0xf00)
+ .equ AT91C_BASE_AIC, (0xFFFFF000)
+ .equ AT91C_PMC_PCER, (0xFFFFFC10)
+ .equ AT91C_BASE_PIOA, (0xFFFFF400)
+ .equ AT91C_ID_PIOA, (2)
+ .equ PIOA_PDSR, (0x3c)
+ .equ PIO_BOOTLDR, (1 << 27)
+ #.equ PIO_BOOTLDR, (1 << 6)
+
+
+/* #include "AT91SAM7S64_inc.h" */
+
+/* Exception Vectors in RAM */
+
+ .text
+ .arm
+ .section .vectram, "ax"
+resetvecR: B resetvecR
+undefvecR: B undefvecR
+swivecR: B swivecR
+pabtvecR: B pabtvecR
+dabtvecR: B dabtvecR
+rsvdvecR: B rsvdvecR
+irqvecR: B IRQ_Handler_EntryR
+fiqvecR:
+
+FIQ_Handler_EntryR:
+
+/*- Switch in SVC/User Mode to allow User Stack access for C code */
+/* because the FIQ is not yet acknowledged*/
+
+/*- Save and r0 in FIQ_Register */
+ mov r9,r0
+ ldr r0 , [r8, #AIC_FVR]
+ msr CPSR_c,#I_BIT | F_BIT | ARM_MODE_SVC
+
+/*- Save scratch/used registers and LR in User Stack */
+ stmfd sp!, { r1-r3, r12, lr}
+
+/*- Branch to the routine pointed by the AIC_FVR */
+ mov r14, pc
+ bx r0
+
+/*- Restore scratch/used registers and LR from User Stack */
+ ldmia sp!, { r1-r3, r12, lr}
+
+/*- Leave Interrupts disabled and switch back in FIQ mode */
+ msr CPSR_c, #I_BIT | F_BIT | ARM_MODE_FIQ
+
+/*- Restore the R0 ARM_MODE_SVC register */
+ mov r0,r9
+
+/*- Restore the Program Counter using the LR_fiq directly in the PC */
+ subs pc,lr,#4
+
+ .global IRQ_Handler_EntryR
+ .func IRQ_Handler_EntryR
+
+IRQ_Handler_EntryR:
+
+/*- Manage Exception Entry */
+/*- Adjust and save LR_irq in IRQ stack */
+ sub lr, lr, #4
+ stmfd sp!, {lr}
+
+/*- Save SPSR need to be saved for nested interrupt */
+ mrs r14, SPSR
+ stmfd sp!, {r14}
+
+/*- Save and r0 in IRQ stack */
+ stmfd sp!, {r0}
+
+/*- Write in the IVR to support Protect Mode */
+/*- No effect in Normal Mode */
+/*- De-assert the NIRQ and clear the source in Protect Mode */
+ ldr r14, =AT91C_BASE_AIC
+ ldr r0 , [r14, #AIC_IVR]
+ str r14, [r14, #AIC_IVR]
+
+/*- Enable Interrupt and Switch in Supervisor Mode */
+ msr CPSR_c, #ARM_MODE_SVC
+
+/*- Save scratch/used registers and LR in User Stack */
+ stmfd sp!, { r1-r3, r12, r14}
+
+/*- Branch to the routine pointed by the AIC_IVR */
+ mov r14, pc
+ bx r0
+/*- Restore scratch/used registers and LR from User Stack*/
+ ldmia sp!, { r1-r3, r12, r14}
+
+/*- Disable Interrupt and switch back in IRQ mode */
+ msr CPSR_c, #I_BIT | ARM_MODE_IRQ
+
+/*- Mark the End of Interrupt on the AIC */
+ ldr r14, =AT91C_BASE_AIC
+ str r14, [r14, #AIC_EOICR]
+
+/*- Restore SPSR_irq and r0 from IRQ stack */
+ ldmia sp!, {r0}
+
+/*- Restore SPSR_irq and r0 from IRQ stack */
+ ldmia sp!, {r14}
+ msr SPSR_cxsf, r14
+
+/*- Restore adjusted LR_irq from IRQ stack directly in the PC */
+ ldmia sp!, {pc}^
+
+ .size IRQ_Handler_EntryR, . - IRQ_Handler_EntryR
+ .endfunc
+
+ .global remap
+ .func remap
+_remap:
+# led1on
+# Remap RAM to 0x00000000 for DFU
+ ldr r1, =AT91C_BASE_AIC
+ mov r2, #0x01
+ str r2, [r1, #AIC_MCR_RCR]
+
+ /* prepare c function call to main */
+ mov r0, #0 /* argc = 0 */
+ ldr lr,=exit
+ ldr r10,=main
+
+#ifdef CONFIG_DFU_SWITCH
+ /* check whether bootloader button is pressed */
+ ldr r1, =AT91C_PMC_PCER
+ mov r2, #(1 << AT91C_ID_PIOA)
+ str r2, [r1]
+
+ ldr r1, =AT91C_BASE_PIOA
+ ldr r2, [r1, #PIOA_PDSR]
+ tst r2, #PIO_BOOTLDR
+ ldrne r10,=dfu_main
+#endif
+
+ bx r10
+ .size remap, . - remap
+ .endfunc
+
+/* "exit" dummy added by mthomas to avoid sbrk write read etc. needed
+ by the newlib default "exit" */
+ .global exit
+ .func exit
+exit:
+ b .
+ .size exit, . - exit
+ .endfunc
+
+/*---------------------------------------------------------------
+//* ?EXEPTION_VECTOR
+//* This module is only linked if needed for closing files.
+//*---------------------------------------------------------------*/
+ .global AT91F_Default_FIQ_handler
+ .func AT91F_Default_FIQ_handler
+AT91F_Default_FIQ_handler:
+ b AT91F_Default_FIQ_handler
+ .size AT91F_Default_FIQ_handler, . - AT91F_Default_FIQ_handler
+ .endfunc
+
+ .global AT91F_Default_IRQ_handler
+ .func AT91F_Default_IRQ_handler
+AT91F_Default_IRQ_handler:
+ b AT91F_Default_IRQ_handler
+ .size AT91F_Default_IRQ_handler, . - AT91F_Default_IRQ_handler
+ .endfunc
+
+ .global AT91F_Spurious_handler
+ .func AT91F_Spurious_handler
+AT91F_Spurious_handler:
+ b AT91F_Spurious_handler
+ .size AT91F_Spurious_handler, . - AT91F_Spurious_handler
+ .endfunc
+
+
+
+#;------------------------------------------------------------------------------
+#;- Section Definition
+#;-----------------
+#;- Section
+#;- .internal_ram_top Top_Stack: used by the cstartup for vector initalisation
+#;- management defined by ld and affect from ldscript
+#;------------------------------------------------------------------------------
+ .section .internal_ram_top
+ .code 32
+ .align 0
+ .global Top_Stack
+Top_Stack:
+
+/*------------------------------------------------------------------------------
+*- Area Definition
+*------------------------------------------------------------------------------
+* .text is used instead of .section .text so it works with arm-aout too. */
+ .section .reset
+ .text
+ .global _startup
+ .func _startup
+_startup:
+reset:
+/*------------------------------------------------------------------------------
+//*- Exception vectors
+//*--------------------
+//*- These vectors can be read at address 0 or at RAM address
+//*- They ABSOLUTELY requires to be in relative addresssing mode in order to
+//*- guarantee a valid jump. For the moment, all are just looping.
+//*- If an exception occurs before remap, this would result in an infinite loop.
+//*- To ensure if a exeption occurs before start application to infinite loop.
+//*------------------------------------------------------------------------------*/
+
+ B InitReset /* 0x00 Reset handler */
+undefvec:
+ B undefvec /* 0x04 Undefined Instruction */
+swivec:
+ B swivec /* 0x08 Software Interrupt */
+pabtvec:
+ B pabtvec /* 0x0C Prefetch Abort */
+dabtvec:
+ B dabtvec /* 0x10 Data Abort */
+rsvdvec:
+ B rsvdvec /* 0x14 reserved */
+irqvec:
+ B irqvec /* 0x18 IRQ */
+fiqvec:
+ B fiqvec /* 0x1c FIQ */
+ .align 0
+.RAM_TOP:
+ .word Top_Stack
+
+InitReset:
+/*------------------------------------------------------------------------------
+/*- Low level Init (PMC, AIC, ? ....) by C function AT91F_LowLevelInit
+/*------------------------------------------------------------------------------*/
+ .extern AT91F_LowLevelInit
+/*- minumum C initialization */
+/*- call AT91F_LowLevelInit( void) */
+
+ ldr r13,.RAM_TOP /* temporary stack in internal RAM */
+/*--Call Low level init function in ABSOLUTE through the Interworking */
+ ldr r0,=AT91F_LowLevelInit
+ mov lr, pc
+ bx r0
+ ledinit
+/*------------------------------------------------------------------------------
+//*- Stack Sizes Definition
+//*------------------------
+//*- Interrupt Stack requires 2 words x 8 priority level x 4 bytes when using
+//*- the vectoring. This assume that the IRQ management.
+//*- The Interrupt Stack must be adjusted depending on the interrupt handlers.
+//*- Fast Interrupt not requires stack If in your application it required you must
+//*- be definehere.
+//*- The System stack size is not defined and is limited by the free internal
+//*- SRAM.
+//*------------------------------------------------------------------------------*/
+
+/*------------------------------------------------------------------------------
+//*- Top of Stack Definition
+//*-------------------------
+//*- Interrupt and Supervisor Stack are located at the top of internal memory in
+//*- order to speed the exception handling context saving and restoring.
+//*- ARM_MODE_SVC (Application, C) Stack is located at the top of the external memory.
+//*------------------------------------------------------------------------------*/
+
+ .EQU IRQ_STACK_SIZE, (3*8*4)
+ .EQU ARM_MODE_FIQ, 0x11
+ .EQU ARM_MODE_IRQ, 0x12
+ .EQU ARM_MODE_SVC, 0x13
+
+ .EQU I_BIT, 0x80
+ .EQU F_BIT, 0x40
+
+/*------------------------------------------------------------------------------
+//*- Setup the stack for each mode
+//*-------------------------------*/
+ mov r0,r13
+
+/*- Set up Fast Interrupt Mode and set FIQ Mode Stack*/
+ msr CPSR_c, #ARM_MODE_FIQ | I_BIT | F_BIT
+/*- Init the FIQ register*/
+ ldr r8, =AT91C_BASE_AIC
+
+/*- Set up Interrupt Mode and set IRQ Mode Stack*/
+ msr CPSR_c, #ARM_MODE_IRQ | I_BIT | F_BIT
+ mov r13, r0 /* Init stack IRQ */
+ sub r0, r0, #IRQ_Stack_Size
+/*- Set up Supervisor Mode and set Supervisor Mode Stack*/
+ msr CPSR_c, #ARM_MODE_SVC
+ mov r13, r0 /* Init stack Sup */
+
+/*- Relocate DFU .data section (Copy from ROM to RAM)*/
+ ldr r1, =_etext_dfu
+ ldr r2, =_data_dfu
+ ldr r3, =_edata_dfu
+LoopRelDFU: cmp r2, r3
+ ldrlo r0, [r1], #4
+ strlo r0, [r2], #4
+ blo LoopRelDFU
+
+# Relocate .data section (Copy from ROM to RAM)
+ LDR R1, =_etext
+ LDR R2, =_data
+ LDR R3, =_edata
+LoopRel: CMP R2, R3
+ LDRLO R0, [R1], #4
+ STRLO R0, [R2], #4
+ BLO LoopRel
+
+# Clear .bss section (Zero init)
+ MOV R0, #0
+ LDR R1, =__bss_start__
+ LDR R2, =__bss_end__
+LoopZI: CMP R1, R2
+ STRLO R0, [R1], #4
+ BLO LoopZI
+
+ led1on
+
+ ldr r0, =_remap
+ bx r0
+
+ .size _startup, . - _startup
+ .endfunc
+
+ .end
+
diff --git a/firmware/src/start/Cstartup_SAM7.c b/firmware/src/start/Cstartup_SAM7.c
new file mode 100644
index 0000000..66dbe20
--- /dev/null
+++ b/firmware/src/start/Cstartup_SAM7.c
@@ -0,0 +1,89 @@
+//*----------------------------------------------------------------------------
+//* ATMEL Microcontroller Software Support - ROUSSET -
+//*----------------------------------------------------------------------------
+//* The software is delivered "AS IS" without warranty or condition of any
+//* kind, either express, implied or statutory. This includes without
+//* limitation any warranty or condition with respect to merchantability or
+//* fitness for any particular purpose, or against the infringements of
+//* intellectual property rights of others.
+//*----------------------------------------------------------------------------
+//* File Name : Cstartup_SAM7.c
+//* Object : Low level initializations written in C for GCC Tools
+//* Creation : 12/Jun/04
+//* 1.2 28/Feb/05 JPP : LIB change AT91C_WDTC_WDDIS & PLL
+//* 1.3 21/Mar/05 JPP : Change PLL Wait time
+//*----------------------------------------------------------------------------
+
+// Include the board file description
+#include <board.h>
+
+// The following functions must be write in ARM mode this function called directly
+// by exception vector
+extern void AT91F_Spurious_handler (void);
+extern void AT91F_Default_IRQ_handler (void);
+extern void AT91F_Default_FIQ_handler (void);
+
+//*----------------------------------------------------------------------------
+//* \fn AT91F_LowLevelInit
+//* \brief This function performs very low level HW initialization
+//* this function can be use a Stack, depending the compilation
+//* optimization mode
+//*----------------------------------------------------------------------------
+void
+AT91F_LowLevelInit (void)
+{
+ int i;
+ AT91PS_PMC pPMC = AT91C_BASE_PMC;
+ //* Set Flash Waite sate
+ // Single Cycle Access at Up to 30 MHz, or 40
+ // if MCK = 47923200 I have 50 Cycle for 1 usecond ( flied MC_FMR->FMCN
+ AT91C_BASE_MC->MC_FMR = ((AT91C_MC_FMCN) & (48 << 16)) | AT91C_MC_FWS_1FWS;
+
+ //* Watchdog Disable
+ AT91C_BASE_WDTC->WDTC_WDMR = AT91C_WDTC_WDDIS;
+
+ //* Set MCK at 47 923 200
+ // 1 Enabling the Main Oscillator:
+ // SCK = 1/32768 = 30.51 uSecond
+ // Start up time = 8 * 6 / SCK = 56 * 30.51 = 1,46484375 ms
+ pPMC->PMC_MOR = ((AT91C_CKGR_OSCOUNT) & (0x06 << 8)) | AT91C_CKGR_MOSCEN;
+ // Wait the startup time
+ while (!(pPMC->PMC_SR & AT91C_PMC_MOSCS));
+ // 2 Checking the Main Oscillator Frequency (Optional)
+ // 3 Setting PLL and divider:
+ // - div by 24 Fin = 0,7680 =(18,432 / 24)
+ // - Mul 125: Fout = 96,0000 =(0,7680 *125)
+ // for 96 MHz the erroe is 0.16%
+ // Field out NOT USED = 0
+ // PLLCOUNT pll startup time estimate at : 0.844 ms
+ // PLLCOUNT 28 = 0.000844 /(1/32768)
+#if 0
+ pPMC->PMC_PLLR = ((AT91C_CKGR_DIV & 0x05) |
+ (AT91C_CKGR_PLLCOUNT & (28 << 8)) |
+ (AT91C_CKGR_MUL & (25 << 16)));
+#else
+ pPMC->PMC_PLLR = ((AT91C_CKGR_DIV & 24) |
+ (AT91C_CKGR_PLLCOUNT & (28 << 8)) |
+ (AT91C_CKGR_MUL & (125 << 16)));
+#endif
+
+ // Wait the startup time
+ while (!(pPMC->PMC_SR & AT91C_PMC_LOCK));
+ while (!(pPMC->PMC_SR & AT91C_PMC_MCKRDY));
+ // 4. Selection of Master Clock and Processor Clock
+ // select the PLL clock divided by 2
+ pPMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2;
+ while (!(pPMC->PMC_SR & AT91C_PMC_MCKRDY));
+
+ pPMC->PMC_MCKR |= AT91C_PMC_CSS_PLL_CLK;
+ while (!(pPMC->PMC_SR & AT91C_PMC_MCKRDY));
+
+ // Set up the default interrupts handler vectors
+ AT91C_BASE_AIC->AIC_SVR[0] = (int) AT91F_Default_FIQ_handler;
+ for (i = 1; i < 31; i++)
+ {
+ AT91C_BASE_AIC->AIC_SVR[i] = (int) AT91F_Default_IRQ_handler;
+ }
+ AT91C_BASE_AIC->AIC_SPU = (int) AT91F_Spurious_handler;
+
+}
personal git repositories of Harald Welte. Your mileage may vary