diff options
author | laforge <laforge@6dc7ffe9-61d6-0310-9af1-9938baff3ed1> | 2006-09-12 17:35:30 +0000 |
---|---|---|
committer | laforge <laforge@6dc7ffe9-61d6-0310-9af1-9938baff3ed1> | 2006-09-12 17:35:30 +0000 |
commit | d256545b2fd62d78910efcc6273c3b70abd3aa13 (patch) | |
tree | a05e17ec752cfbcc0b79fdbfba81fb949545a112 /firmware/src | |
parent | 04e0441914eeb25e042189679b55c9577fc96d2a (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')
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, ®); + 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, ®); + 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, ®); + 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; + +} |