From 514b0f72f50b50b75ef855f008c888f29989d68e Mon Sep 17 00:00:00 2001
From: laforge <laforge@6dc7ffe9-61d6-0310-9af1-9938baff3ed1>
Date: Wed, 20 Sep 2006 11:44:10 +0000
Subject: - Add OpenPICC register definition (and USB command handling) - Add
 automatic generation of include/compile.h with svn revision and compiletime -
 Add openpcd_compile_version structure to obtain version via USB - Move LED
 commands into new CMD_CLS_GENERIC family - Update TODO - Add support for
 large (2048 byte) request contexts in addition to 64byte - Shrink req_ctx
 size by collapsing rx and tx buffer into one - move definition of
 DFU_API_LOCATION to header file - Implement large req_ctx aware USB transmit
 / refill routines - Implement TX refilling for IRQ Endpoint - Print version
 information at startup time - move some generic req_ctx processing into
 usb_handler.c - Some further work on DFU (still not finished) - Only use
 '-Os' for DFU, use '-O2' for application code

git-svn-id: https://svn.openpcd.org:2342/trunk@208 6dc7ffe9-61d6-0310-9af1-9938baff3ed1
---
 firmware/Makefile                 |  8 ++--
 firmware/TODO                     |  9 ++--
 firmware/include/openpcd.h        | 22 ++++++---
 firmware/include/openpicc_regs.h  | 46 +++++++++++++++++++
 firmware/src/dfu/dfu.c            | 38 +++++++++++-----
 firmware/src/os/dbgu.c            |  8 +++-
 firmware/src/os/led.c             | 21 ---------
 firmware/src/os/main.c            | 12 +++++
 firmware/src/os/main.h            |  1 +
 firmware/src/os/pcd_enumerate.c   | 93 +++++++++++++++++++++++++++------------
 firmware/src/os/pcd_enumerate.h   |  7 ++-
 firmware/src/os/pio_irq.c         | 11 +++--
 firmware/src/os/pwm.c             |  7 ++-
 firmware/src/os/req_ctx.c         | 37 +++++++++++++++-
 firmware/src/os/req_ctx.h         | 16 ++-----
 firmware/src/os/usb_benchmark.c   | 11 +++--
 firmware/src/os/usb_handler.c     | 42 +++++++++++-------
 firmware/src/os/usb_handler.h     | 10 +++++
 firmware/src/os/usbcmd_generic.c  | 49 +++++++++++++++++++++
 firmware/src/os/usbcmd_generic.h  |  4 ++
 firmware/src/pcd/rc632.c          | 81 ++++++++++++++--------------------
 firmware/src/picc/adc.c           | 10 ++---
 firmware/src/picc/decoder.c       |  2 -
 firmware/src/picc/main_openpicc.c |  5 +++
 firmware/src/picc/openpicc.c      | 82 ++++++++++++++++++++++++++++++++++
 firmware/src/picc/opicc_reg.h     | 18 ++++++++
 firmware/src/picc/pll.c           |  1 +
 firmware/src/picc/ssc_picc.c      | 21 +++++----
 28 files changed, 486 insertions(+), 186 deletions(-)
 create mode 100644 firmware/include/openpicc_regs.h
 create mode 100644 firmware/src/os/usbcmd_generic.c
 create mode 100644 firmware/src/os/usbcmd_generic.h
 create mode 100644 firmware/src/picc/openpicc.c
 create mode 100644 firmware/src/picc/opicc_reg.h

diff --git a/firmware/Makefile b/firmware/Makefile
index b5044e4..31e7cc5 100644
--- a/firmware/Makefile
+++ b/firmware/Makefile
@@ -87,7 +87,7 @@ SRCARM += src/os/pcd_enumerate.c src/os/fifo.c src/os/dbgu.c \
 	  src/os/led.c src/os/req_ctx.c src/os/trigger.c \
 	  src/os/main.c src/os/syscalls.c src/os/usb_handler.c \
 	  src/os/usb_benchmark.c src/os/tc_cdiv.c src/os/pit.c \
-	  src/os/pwm.c src/os/pio_irq.c
+	  src/os/pwm.c src/os/pio_irq.c src/os/usbcmd_generic.c
 
 ifeq ($(BOARD), PCD)
 # PCD support code
@@ -102,7 +102,8 @@ ifeq ($(BOARD), PICC)
 SRCARM += src/picc/tc_fdt.c src/picc/ssc_picc.c src/picc/adc.c \
 	  src/picc/decoder.c src/picc/decoder_miller.c \
 	  src/picc/load_modulation.c \
-	  src/picc/decoder_nrzl.c src/picc/poti.c src/picc/pll.c
+	  src/picc/decoder_nrzl.c src/picc/poti.c src/picc/pll.c \
+	  src/picc/openpicc.c
 # finally, the actual main application 
 SRCARM += src/picc/$(TARGET).c 
 endif
@@ -144,7 +145,7 @@ FORMAT = binary
 # Optimization level, can be [0, 1, 2, 3, s]. 
 # 0 = turn off optimization. s = optimize for size.
 # (Note: 3 is not always the best optimization level. See avr-libc FAQ.)
-OPT = s
+OPT = 2
 #OPT = 0
 
 # Debugging format.
@@ -562,6 +563,7 @@ clean_list :
 	$(REMOVE) $(CPPSRCARM:.cpp=.s) 
 	$(REMOVE) $(CPPSRCARM:.cpp=.d)
 	$(REMOVE) .dep/*
+	$(REMOVE) include/compile.h
 
 .PHONY: include/compile.h
 include/compile.h: 
diff --git a/firmware/TODO b/firmware/TODO
index c63fb89..1d19223 100644
--- a/firmware/TODO
+++ b/firmware/TODO
@@ -1,19 +1,18 @@
 SPI:
 - use PDC DMA for SPI transfers
-- use real SPI clock divisor (4.8MHz) rather than current 320kHz clock
 
 RC632:
 - Fix locking between 'atomic' ops like set/clear bit and RC632 IRQ
 - Implement VFIFO handling
 
 USB:
-- don't busy-wait for EP2/EP3 transfers but rather use TX completion IRQ
 - Implement VFIFO handling
-- Add DFU descriptor to host
 
 DFU:
-- implement DFU protocol for bootloader flash partition
-- add capability to copy and execute DFU from RAM
+- fix DFU download (usb control write / zlp at end)
+- add capability to flash DFU itself
+- add a header to the application program, checksum and size
+- checksum application image.  If wrong, enter DFU recovery mode
 - Get rid of lots of constant values and replace them by proper defines
 
 Generic:
diff --git a/firmware/include/openpcd.h b/firmware/include/openpcd.h
index e6ccebe..9e2d45e 100644
--- a/firmware/include/openpcd.h
+++ b/firmware/include/openpcd.h
@@ -6,19 +6,29 @@
 #include <sys/types.h>
 
 struct openpcd_hdr {
-	u_int8_t cmd;		/* command. high nibble: class, low nibble: cmd */
+	u_int8_t cmd;		/* command. high nibble: class,
+				 *	     low nibble: cmd */
 	u_int8_t flags;
 	u_int8_t reg;		/* register */
 	u_int8_t val;		/* value (in case of write *) */
 	u_int8_t data[0];
 } __attribute__ ((packed));
 
-#define OPENPC_FLAG_RESPOND	0x01	/* Response requested */
+#define OPCD_REV_LEN	16
+struct openpcd_compile_version {
+	char svnrev[OPCD_REV_LEN];
+	char by[OPCD_REV_LEN];
+	char date[OPCD_REV_LEN];
+} __attribute__ ((packed));
+
+#define OPENPCD_FLAG_RESPOND	0x01	/* Response requested */
+#define OPENPCD_FLAG_ERROR	0x80	/* An error occurred */
 
 enum openpcd_cmd_class {
+	OPENPCD_CMD_CLS_GENERIC		= 0x0,
 	/* PCD (reader) side */
 	OPENPCD_CMD_CLS_RC632		= 0x1,
-	OPENPCD_CMD_CLS_LED		= 0x2,
+	//OPENPCD_CMD_CLS_LED		= 0x2,
 	OPENPCD_CMD_CLS_SSC		= 0x3,
 	OPENPCD_CMD_CLS_PWM		= 0x4,
 	OPENPCD_CMD_CLS_ADC		= 0x5,
@@ -35,6 +45,9 @@ enum openpcd_cmd_class {
 
 #define OPENPCD_CLS2CMD(x)	(x << 4)
 
+#define OPENPCD_CMD_GET_VERSION		(0x1|OPENPCD_CLS2CMD(OPENPCD_CMD_CLS_GENERIC))
+#define OPENPCD_CMD_SET_LED		(0x2|OPENPCD_CLS2CMD(OPENPCD_CMD_CLS_GENERIC))
+
 /* CMD_CLS_RC632 */
 #define OPENPCD_CMD_WRITE_REG		(0x1|OPENPCD_CLS2CMD(OPENPCD_CMD_CLS_RC632))
 #define OPENPCD_CMD_WRITE_FIFO		(0x2|OPENPCD_CLS2CMD(OPENPCD_CMD_CLS_RC632))
@@ -47,9 +60,6 @@ enum openpcd_cmd_class {
 #define OPENPCD_CMD_DUMP_REGS		(0x9|OPENPCD_CLS2CMD(OPENPCD_CMD_CLS_RC632))
 #define OPENPCD_CMD_IRQ			(0xa|OPENPCD_CLS2CMD(OPENPCD_CMD_CLS_RC632))
 
-/* CMD_CLS_LED */
-#define OPENPCD_CMD_SET_LED		(0x1|OPENPCD_CLS2CMD(OPENPCD_CMD_CLS_LED))
-
 /* CMD_CLS_SSC */
 #define OPENPCD_CMD_SSC_READ		(0x1|OPENPCD_CLS2CMD(OPENPCD_CMD_CLS_SSC))
 #define OPENPCD_CMD_SSC_WRITE		(0x2|OPENPCD_CLS2CMD(OPENPCD_CMD_CLS_SSC))
diff --git a/firmware/include/openpicc_regs.h b/firmware/include/openpicc_regs.h
new file mode 100644
index 0000000..2df0618
--- /dev/null
+++ b/firmware/include/openpicc_regs.h
@@ -0,0 +1,46 @@
+#ifndef _OPENPICC_STATE
+#define _OPENPICC_STATE
+
+/* according to ISO 14443-3(2000) 6.2 */
+enum opicc_14443a_state {
+	ISO14443A_ST_POWEROFF,
+	ISO14443A_ST_IDLE,
+	ISO14443A_ST_READY,
+	ISO14443A_ST_ACTIVE,
+	ISO14443A_ST_HALT,
+	ISO14443A_ST_READY2,
+	ISO14443A_ST_ACTIVE2,
+};
+
+enum opicc_reg_tx_control {
+	OPICC_REG_TX_BPSK	= 0x01,
+	OPICC_REG_TX_MANCHESTER	= 0x02,
+	OPICC_REG_TX_INTENSITY0	= 0x00,
+	OPICC_REG_TX_INTENSITY1	= 0x10,
+	OPICC_REG_TX_INTENSITY2	= 0x20,
+	OPICC_REG_TX_INTENSITY3	= 0x30,
+};
+
+enum opicc_reg {
+	OPICC_REG_14443A_UIDLEN,/* Length of UID in bytes */
+
+	OPICC_REG_14443A_FDT0,	/* Frame delay time if last bit 0 */
+	OPICC_REG_14443A_FDT1,	/* Frame delay time if last bit 1 */
+	OPICC_REG_14443A_STATE,	/* see 'enum opicc_14443a_state' */
+
+	OPICC_REG_RX_CLK_DIV,	/* Clock divider for Rx sample clock */
+	OPICC_REG_RX_CLK_PHASE,	/* Phase shift of Rx sample clock */
+	OPICC_REG_RX_COMP_LEVEL,/* Comparator level of Demodulator */
+	OPICC_REG_RX_CONTROL,
+
+	OPICC_REG_TX_CLK_DIV,	/* Clock divider for Tx sample clock */
+	OPICC_REG_TX_CONTROL,	/* see 'enum opicc_reg_tx_Control */
+	_OPICC_NUM_REGS,
+};
+
+enum openpicc_14443a_sregs {
+	/* string 'registers' */
+	OPICC_REG_14443A_UID,	/* The UID (4...10 bytes) */
+};
+
+#endif
diff --git a/firmware/src/dfu/dfu.c b/firmware/src/dfu/dfu.c
index 09c8784..c5f706e 100644
--- a/firmware/src/dfu/dfu.c
+++ b/firmware/src/dfu/dfu.c
@@ -31,6 +31,8 @@
 #include <os/pcd_enumerate.h>
 #include "../openpcd.h"
 
+#define SAM7DFU_SIZE	0x1000
+
 /* If debug is enabled, we need to access debug functions from flash
  * and therefore have to omit flashing */
 #define DEBUG_DFU
@@ -56,8 +58,8 @@ static void __dfufunc udp_init(void)
 	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 */
+	/* 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);
 }
 
@@ -150,8 +152,8 @@ static void __dfufunc udp_ep0_send_stall(void)
 
 
 static u_int8_t status;
-static u_int8_t *ptr = (u_int8_t *)0x00101000;
-static __dfudata u_int32_t dfu_state;
+static u_int8_t *ptr = AT91C_IFLASH + SAM7DFU_SIZE;
+static __dfudata u_int32_t dfu_state = DFU_STATE_appIDLE;
 static u_int8_t pagebuf[AT91C_IFLASH_PAGE_SIZE];
 
 static u_int16_t page_from_ramaddr(const void *addr)
@@ -179,7 +181,8 @@ static int __dfufunc handle_dnload(u_int16_t val, u_int16_t len)
 	DEBUGE("download ");
 
 	if (len > AT91C_IFLASH_PAGE_SIZE) {
-		/* Too big */
+		/* Too big. Not that we'd really care, but it's a
+		 * DFU protocol violation */
 	    	dfu_state = DFU_STATE_dfuERROR;
 		status = DFU_STATUS_errADDRESS;
 		udp_ep0_send_stall();
@@ -196,6 +199,12 @@ static int __dfufunc handle_dnload(u_int16_t val, u_int16_t len)
 		dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
 		return 0;
 	}
+	if (ptr + len > AT91C_IFLASH + AT91C_IFLASH_SIZE) {
+		dfu_state = DFU_STATE_dfuERROR;
+		status = DFU_STATUS_errADDRESS;
+		udp_ep0_send_stall();
+		return -EINVAL;
+	}
 
 	udp_ep0_recv_data(pagebuf, sizeof(pagebuf));
 
@@ -234,8 +243,8 @@ static __dfufunc int handle_upload(u_int16_t val, u_int16_t len)
 		return -EINVAL;
 	}
 
-	if (ptr + len > AT91C_IFLASH_SIZE) 
-		len = AT91C_IFLASH_SIZE - (u_int32_t) ptr;
+	if (ptr + len > AT91C_IFLASH + AT91C_IFLASH_SIZE) 
+		len = AT91C_IFLASH + AT91C_IFLASH_SIZE - (u_int32_t) ptr;
 		
 	udp_ep0_send_data((char *)ptr, len);
 	ptr+= len;
@@ -273,6 +282,7 @@ static __dfufunc void handle_getstatus(void)
 	dstat.bStatus = status;
 	dstat.bState = dfu_state;
 	dstat.iString = 0;
+	/* FIXME: set dstat.bwPollTimeout */
 
 	udp_ep0_send_data((char *)&dstat, sizeof(dstat));
 }
@@ -322,11 +332,12 @@ int __dfufunc dfu_ep0_handler(u_int8_t req_type, u_int8_t req,
 				dfu_state = DFU_STATE_dfuERROR;
 				goto send_stall;
 			}
+			ptr = AT91C_IFLASH + SAM7DFU_SIZE;
 			handle_dnload(val, len);
 			dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
 			break;
 		case USB_REQ_DFU_UPLOAD:
-			ptr = 0x00101000; /* Flash base address for app */
+			ptr = AT91C_IFLASH + SAM7DFU_SIZE;
 			dfu_state = DFU_STATE_dfuUPLOAD_IDLE;
 			handle_upload(val, len);
 			break;
@@ -697,13 +708,16 @@ static __dfufunc void dfu_udp_ep0_handler(void)
 		udp_ep0_send_stall();
 		break;
 	default:
-		DEBUGE("DEFAULT(req=0x%02x, type=0x%02x) ", bRequest, bmRequestType);
+		DEBUGE("DEFAULT(req=0x%02x, type=0x%02x) ",
+			bRequest, bmRequestType);
 		if ((bmRequestType & 0x3f) == USB_TYPE_DFU) {
-			dfu_ep0_handler(bmRequestType, bRequest, wValue, wLength);
+			dfu_ep0_handler(bmRequestType, bRequest,
+					wValue, wLength);
 		} else
 			udp_ep0_send_stall();
 		break;
 	}
+	DEBUGE("\r\n");
 }
 
 /* minimal USB IRQ handler in DFU mode */
@@ -747,9 +761,12 @@ static void dfu_switch(void)
 void __dfufunc dfu_main(void)
 {
 	AT91F_DBGU_Init();
+	DEBUGE("sam7dfu startup\r\n");
 
 	udp_init();
 
+	dfu_state = DFU_STATE_dfuIDLE;
+
 	/* This implements 
 	AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_UDP,
 			      OPENPCD_IRQ_PRIO_UDP,
@@ -771,6 +788,7 @@ void __dfufunc dfu_main(void)
 
 	flash_init();
 
+	DEBUGE("sam7dfu entering main loop\r\n");
 	/* do nothing, since all of DFU is interrupt driven */
 	while (1) ;
 }
diff --git a/firmware/src/os/dbgu.c b/firmware/src/os/dbgu.c
index f7c62e3..eed5546 100644
--- a/firmware/src/os/dbgu.c
+++ b/firmware/src/os/dbgu.c
@@ -139,7 +139,13 @@ void AT91F_DBGU_Init(void)
 			      DBGU_irq_handler);
 	AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_SYS);
 
-	AT91F_DBGU_Printk
+	AT91F_DBGU_Printk("\n\r");
+	AT91F_DBGU_Printk(opcd_version.svnrev);
+	AT91F_DBGU_Printk(" ");
+	AT91F_DBGU_Printk(opcd_version.date);
+	AT91F_DBGU_Printk(" ");
+	AT91F_DBGU_Printk(opcd_version.by);
+	AT91F_DBGU_Printk("\n\r");
 	    ("\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");
diff --git a/firmware/src/os/led.c b/firmware/src/os/led.c
index 30353ac..96ffecc 100644
--- a/firmware/src/os/led.c
+++ b/firmware/src/os/led.c
@@ -73,31 +73,10 @@ int led_toggle(int led)
 	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/main.c b/firmware/src/os/main.c
index 3e05a7d..ce611bc 100644
--- a/firmware/src/os/main.c
+++ b/firmware/src/os/main.c
@@ -24,9 +24,18 @@
 #include <os/led.h>
 #include <os/main.h>
 #include <os/power.h>
+#include <os/usbcmd_generic.h>
 #include <os/pcd_enumerate.h>
 #include "../openpcd.h"
 
+#include <compile.h>
+
+const struct openpcd_compile_version opcd_version = {
+	.svnrev = COMPILE_SVNREV,
+	.date = COMPILE_DATE,
+	.by = COMPILE_BY,
+};
+
 int main(void)
 {
 	/* initialize LED and debug unit */
@@ -34,10 +43,13 @@ int main(void)
 	AT91F_DBGU_Init();
 
 	AT91F_PIOA_CfgPMC();
+
 	/* call application specific init function */
 	_init_func();
 
 	/* initialize USB */
+	req_ctx_init();
+	usbcmd_gen_init();
 	udp_open();
 
 	// Enable User Reset and set its minimal assertion to 960 us
diff --git a/firmware/src/os/main.h b/firmware/src/os/main.h
index 1adc8f6..bd42341 100644
--- a/firmware/src/os/main.h
+++ b/firmware/src/os/main.h
@@ -5,4 +5,5 @@ extern void _init_func(void);
 extern int _main_dbgu(char key);
 extern void _main_func(void);
 
+extern const struct openpcd_compile_version opcd_version;
 #endif
diff --git a/firmware/src/os/pcd_enumerate.c b/firmware/src/os/pcd_enumerate.c
index 1f35b06..e3443a8 100644
--- a/firmware/src/os/pcd_enumerate.c
+++ b/firmware/src/os/pcd_enumerate.c
@@ -52,7 +52,6 @@
 #define AT91C_EP_INT  3
 
 #ifdef CONFIG_DFU
-#define DFU_API_LOCATION	((const struct dfuapi *) 0x00100fd0)
 static const struct dfuapi *dfu = DFU_API_LOCATION;
 #define udp_init		dfu->udp_init
 #define udp_ep0_send_data	dfu->ep0_send_data
@@ -167,32 +166,64 @@ void udp_unthrottle(void)
 	pUDP->UDP_IER = AT91C_UDP_EPINT1;
 }
 
-int udp_refill_ep(int ep, struct req_ctx *rctx)
+int udp_refill_ep(int ep)
 {
 	u_int16_t i;
 	AT91PS_UDP pUDP = upcd.pUdp;
+	struct req_ctx *rctx;
+	unsigned int start, end;
 
+	/* If we're not configured by the host yet, there is no point
+	 * in trying to send data to it... */
 	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 there are already two packets in transit, the DPR of
+	 * the SAM7 UDC doesn't have space for more data */
 	if (atomic_read(&upcd.ep[ep].pkts_in_transit) == 2)
 		return -EBUSY;
-		
+
+	/* If we have an incompletely-transmitted req_ctx (>EP size),
+	 * we need to transmit the rest and finish the transaction */
+	if (upcd.ep[ep].incomplete.rctx) {
+		rctx = upcd.ep[ep].incomplete.rctx;
+		start = upcd.ep[ep].incomplete.bytes_sent;
+	} else {
+		unsigned int state, state_busy;
+		if (ep == 2) {
+			state = RCTX_STATE_UDP_EP2_PENDING;
+			state_busy = RCTX_STATE_UDP_EP2_BUSY;
+		} else {
+			state = RCTX_STATE_UDP_EP3_PENDING;
+			state_busy = RCTX_STATE_UDP_EP3_BUSY;
+		}
+
+		/* get pending rctx and start transmitting from zero */
+		rctx = req_ctx_find_get(0, state, state_busy);
+		if (!rctx)
+			return 0;
+		start = 0;
+	}
+
+	if (rctx->tot_len - start <= AT91C_EP_IN_SIZE)
+		end = rctx->tot_len;
+	else
+		end = AT91C_EP_IN_SIZE;
+
 	/* fill FIFO/DPR */
-	for (i = 0; i < rctx->tx.tot_len; i++) 
-		pUDP->UDP_FDR[ep] = rctx->tx.data[i];
+	for (i = start; i < end; i++) 
+		pUDP->UDP_FDR[ep] = rctx->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;
 	}
 
+	/* Increment the number of bytes sent in the current large
+	 * context, if any */
+	if (start != 0)
+		upcd.ep[ep].incomplete.bytes_sent += end-start;
+
 	/* return rctx to pool of free contexts */
 	req_ctx_put(rctx);
 
@@ -257,19 +288,21 @@ static void udp_irq(void)
 		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);
+			struct req_ctx *rctx = 
+				req_ctx_find_get(0, RCTX_STATE_FREE,
+						 RCTX_STATE_UDP_RCV_BUSY);
 
 			if (rctx) {
-				rctx->rx.tot_len = pkt_size;
+				rctx->tot_len = pkt_size;
 				while (pkt_size--)
-					rctx->rx.data[pkt_recv++] = pUDP->UDP_FDR[1];
+					rctx->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;
+				DEBUGI("rctxdump(%s) ", hexdump(rctx->data, rctx->tot_len));
 				req_ctx_set_state(rctx, RCTX_STATE_UDP_RCV_DONE);
 				DEBUGI("RCTX=%u ", req_ctx_num(rctx));
 			} else {
@@ -283,8 +316,6 @@ static void udp_irq(void)
 		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;
@@ -294,21 +325,24 @@ static void udp_irq(void)
 			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 ");
+			udp_refill_ep(2);
 		}
 	}
 	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 (csr & AT91C_UDP_TXCOMP) {
+			pUDP->UDP_CSR[3] &= ~AT91C_UDP_TXCOMP;
+			while (pUDP->UDP_CSR[3] & AT91C_UDP_TXCOMP) ;
 
+			/* if we already have another packet in DPR, send it */
+			if (atomic_dec_return(&upcd.ep[3].pkts_in_transit) == 1)
+				pUDP->UDP_CSR[3] |= AT91C_UDP_TXPKTRDY;
+
+			udp_refill_ep(3);
+		}
+	}
 	if (isr & AT91C_UDP_RXSUSP) {
 		pUDP->UDP_ICR = AT91C_UDP_RXSUSP;
 		DEBUGI("RXSUSP ");
@@ -424,6 +458,7 @@ static void udp_ep0_handler(void)
 	pUDP->UDP_CSR[0] &= ~AT91C_UDP_RXSETUP;
 	while ((pUDP->UDP_CSR[0] & AT91C_UDP_RXSETUP)) ;
 
+	DEBUGE("dfu_state = %u ", *dfu->dfu_state);
 	/* Handle supported standard device request Cf Table 9-3 in USB
 	 * speciication Rev 1.1 */
 	switch ((bRequest << 8) | bmRequestType) {
@@ -465,6 +500,10 @@ static void udp_ep0_handler(void)
 				udp_ep0_send_stall();
 				break;
 			}
+		} else if (wValue == 0x2100) {
+			/* Return Function descriptor */
+			udp_ep0_send_data((const char *) &dfu->dfu_cfg_descriptor->func_dfu,
+					  MIN(sizeof(dfu->dfu_cfg_descriptor->func_dfu), wLength));
 #if 0
 		} else if (wValue == 0x400) {
 			/* Return Interface descriptor */
@@ -581,7 +620,7 @@ static void udp_ep0_handler(void)
 				pUDP->UDP_RSTEP &= ~AT91C_UDP_EP2;
 
 				/* free all currently transmitting contexts */
-				while (rctx = req_ctx_find_get(RCTX_STATE_UDP_EP2_BUSY,
+				while (rctx = req_ctx_find_get(0, RCTX_STATE_UDP_EP2_BUSY,
 							       RCTX_STATE_FREE)) {}
 				atomic_set(&upcd.ep[wIndex].pkts_in_transit, 0);
 			}
@@ -593,7 +632,7 @@ static void udp_ep0_handler(void)
 				pUDP->UDP_RSTEP &= ~AT91C_UDP_EP3;
 
 				/* free all currently transmitting contexts */
-				while (rctx = req_ctx_find_get(RCTX_STATE_UDP_EP3_BUSY,
+				while (rctx = req_ctx_find_get(0, RCTX_STATE_UDP_EP3_BUSY,
 							       RCTX_STATE_FREE)) {}
 				atomic_set(&upcd.ep[wIndex].pkts_in_transit, 0);
 			}
diff --git a/firmware/src/os/pcd_enumerate.h b/firmware/src/os/pcd_enumerate.h
index 813243e..3b95de7 100644
--- a/firmware/src/os/pcd_enumerate.h
+++ b/firmware/src/os/pcd_enumerate.h
@@ -10,13 +10,16 @@
 struct req_ctx;
 
 extern void udp_open(void);
-extern int udp_refill_ep(int ep, struct req_ctx *rctx);
+extern int udp_refill_ep(int ep);
 extern void udp_unthrottle(void);
 extern void udp_reset(void);
 
 struct ep_ctx {
 	atomic_t pkts_in_transit;
-	void *ctx;
+	struct {
+		struct req_ctx *rctx;
+		unsigned int bytes_sent;
+	} incomplete;
 };
 
 struct udp_pcd {
diff --git a/firmware/src/os/pio_irq.c b/firmware/src/os/pio_irq.c
index dfe818c..14f51c2 100644
--- a/firmware/src/os/pio_irq.c
+++ b/firmware/src/os/pio_irq.c
@@ -50,7 +50,7 @@ static void pio_irq_demux(void)
 
 	if (send_usb && !pirqs.usb_throttled) {
 		struct req_ctx *irq_rctx;
-		irq_rctx = req_ctx_find_get(RCTX_STATE_FREE,
+		irq_rctx = req_ctx_find_get(0, RCTX_STATE_FREE,
 					    RCTX_STATE_PIOIRQ_BUSY);
 		if (!irq_rctx) {
 			/* we cannot disable the interrupt, since we have
@@ -59,14 +59,14 @@ static void pio_irq_demux(void)
 		} 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 = (struct openpcd_hdr *) irq_rctx->data;
+			regmask = (u_int32_t *) (irq_rctx->data + 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);
+			irq_rctx->tot_len = sizeof(*opcdh) + sizeof(u_int32_t);
 			req_ctx_set_state(irq_rctx, RCTX_STATE_UDP_EP3_PENDING);
 		}
 	}
@@ -116,8 +116,7 @@ void pio_irq_unregister(u_int32_t pio)
 
 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];
+	struct openpcd_hdr *poh = (struct openpcd_hdr *) rctx->data;
 
 	switch (poh->cmd) {
 	case OPENPCD_CMD_PIO_IRQ:
diff --git a/firmware/src/os/pwm.c b/firmware/src/os/pwm.c
index 5c52ee6..285f4e7 100644
--- a/firmware/src/os/pwm.c
+++ b/firmware/src/os/pwm.c
@@ -119,8 +119,7 @@ void pwm_duty_set_percent(int channel, u_int16_t duty)
 
 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]; */
+	struct openpcd_hdr *poh = (struct openpcd_hdr *) rctx->data;
 	u_int32_t *freq;
 
 	switch (poh->cmd) {
@@ -137,7 +136,7 @@ static int pwm_usb_in(struct req_ctx *rctx)
 		goto respond;
 		break;
 	case OPENPCD_CMD_PWM_FREQ_SET:
-		if (rctx->rx.tot_len < sizeof(*poh)+4)
+		if (rctx->tot_len < sizeof(*poh)+4)
 			break;
 		freq = (unsigned char *) poh + sizeof(*poh);
 		pwm_freq_set(0, *freq);
@@ -153,7 +152,7 @@ static int pwm_usb_in(struct req_ctx *rctx)
 	return 0;
 respond:
 	req_ctx_set_state(rctx, RCTX_STATE_UDP_EP2_PENDING);
-	udp_refill_ep(2, rctx);
+	udp_refill_ep(2);
 	return 1;
 }
 
diff --git a/firmware/src/os/req_ctx.c b/firmware/src/os/req_ctx.c
index d118aca..fef7258 100644
--- a/firmware/src/os/req_ctx.c
+++ b/firmware/src/os/req_ctx.c
@@ -28,14 +28,32 @@
 
 /* FIXME: locking, FIFO order processing */
 
+#define RCTX_SIZE_LARGE	2048
+#define RCTX_SIZE_SMALL	64
+
+#define NUM_RCTX_SMALL 8
+#define NUM_RCTX_LARGE 3
+
+#define NUM_REQ_CTX	(NUM_RCTX_SMALL+NUM_RCTX_LARGE)
+
+static u_int8_t rctx_data[NUM_RCTX_SMALL][RCTX_SIZE_SMALL];
+static u_int8_t rctx_data_large[NUM_RCTX_LARGE][RCTX_SIZE_LARGE];
+
 static struct req_ctx req_ctx[NUM_REQ_CTX];
 
-struct req_ctx *req_ctx_find_get(unsigned long old_state, unsigned long new_state)
+struct req_ctx *req_ctx_find_get(int large,
+				 unsigned long old_state, 
+				 unsigned long new_state)
 {
 	unsigned long flags;
 	u_int8_t i;
+	
+	if (large)
+		i = NUM_RCTX_SMALL;
+	else
+		i = 0;
 
-	for (i = 0; i < NUM_REQ_CTX; i++) {
+	for (1; i < NUM_REQ_CTX; i++) {
 		local_irq_save(flags);
 		if (req_ctx[i].state == old_state) {
 			req_ctx[i].state = new_state;
@@ -67,3 +85,18 @@ void req_ctx_put(struct req_ctx *ctx)
 {
 	req_ctx_set_state(ctx, RCTX_STATE_FREE);
 }
+
+void req_ctx_init(void)
+{
+	int i;
+
+	for (i = 0; i < NUM_RCTX_SMALL; i++) {
+		req_ctx[i].size = RCTX_SIZE_SMALL;
+		req_ctx[i].data = rctx_data[i];
+	}
+
+	for (i = 0; i < NUM_RCTX_LARGE; i++) {
+		req_ctx[i].size = RCTX_SIZE_LARGE;
+		req_ctx[NUM_RCTX_SMALL+i].data = rctx_data_large[i];
+	}
+}
diff --git a/firmware/src/os/req_ctx.h b/firmware/src/os/req_ctx.h
index 82a133f..b68fb66 100644
--- a/firmware/src/os/req_ctx.h
+++ b/firmware/src/os/req_ctx.h
@@ -9,18 +9,11 @@
 
 #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;
+	u_int16_t size;
+	u_int16_t tot_len;
+	u_int8_t *data;
 };
 
 #define RCTX_STATE_FREE			0x00
@@ -39,8 +32,7 @@ struct req_ctx {
 
 #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_get(int large, 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);
diff --git a/firmware/src/os/usb_benchmark.c b/firmware/src/os/usb_benchmark.c
index 1f890bf..bfc6b37 100644
--- a/firmware/src/os/usb_benchmark.c
+++ b/firmware/src/os/usb_benchmark.c
@@ -33,6 +33,8 @@ static void usbtest_tx_transfer(unsigned int num_pkts)
 {
 	unsigned int i;
 
+#if 0
+#warning please reimplement refill userspecified rctx 
 	for (i = 0; i < num_pkts; i++) {
 		/* send 16 packets of 64byte */
 		while (udp_refill_ep(2, &dummy_rctx) < 0) 
@@ -41,11 +43,12 @@ static void usbtest_tx_transfer(unsigned int num_pkts)
 	/* send one packet of 0 byte */
 	while (udp_refill_ep(2, &empty_rctx) < 0) 
 		;
+#endif
 }
 
 static int usbtest_rx(struct req_ctx *rctx)
 {
-	struct openpcd_hdr *poh = (struct openpcd_hdr *) &rctx->rx.data[0];
+	struct openpcd_hdr *poh = (struct openpcd_hdr *) rctx->data;
 	int i;
 
 	switch (poh->cmd) {
@@ -67,10 +70,10 @@ static int usbtest_rx(struct req_ctx *rctx)
 
 void usbtest_init(void)
 {
-	dummy_rctx.tx.tot_len = 64;
-	memset(dummy_rctx.tx.data, 0x23, 64);
+	dummy_rctx.tot_len = 64;
+	memset(dummy_rctx.data, 0x23, 64);
 
-	empty_rctx.tx.tot_len = 0;
+	empty_rctx.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
index 6c45e18..c3d4cfa 100644
--- a/firmware/src/os/usb_handler.c
+++ b/firmware/src/os/usb_handler.c
@@ -46,23 +46,33 @@ void usb_hdlr_unregister(u_int8_t class)
 
 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];
+	struct openpcd_hdr *poh = (struct openpcd_hdr *) rctx->data;
 	usb_cmd_fn *hdlr;
+	int ret;
 
 	DEBUGP("usb_in(cls=%d) ", OPENPCD_CMD_CLS(poh->cmd));
 
-	if (rctx->rx.tot_len < sizeof(*poh))
+	if (rctx->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");
+	if (!hdlr) {
+		DEBUGPCR("no handler for this class ");
+		ret = USB_ERR(USB_ERR_CMD_UNKNOWN);
+	} else
+		ret = (hdlr)(rctx);
+	
+	if (ret & USB_RET_ERR) {
+		poh->val = ret & 0xff;
+		poh->flags = OPENPCD_FLAG_ERROR;
+	}
+	if (ret & USB_RET_RESPOND) { 
+		req_ctx_set_state(rctx, RCTX_STATE_UDP_EP2_PENDING);
+		udp_refill_ep(2);
+	}
+
+	DEBUGPCR("");
+	return (ret & USB_RET_ERR) ? 1 : 0;
 }
 
 /* Process all pending request contexts that want to Tx on either
@@ -71,17 +81,17 @@ void usb_out_process(void)
 {
 	struct req_ctx *rctx;
 
-	while (rctx = req_ctx_find_get(RCTX_STATE_UDP_EP3_PENDING,
+	while (rctx = req_ctx_find_get(0, 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)
+		if (udp_refill_ep(3) < 0)
 			req_ctx_set_state(rctx, RCTX_STATE_UDP_EP3_PENDING);
 	}
 
-	while (rctx = req_ctx_find_get(RCTX_STATE_UDP_EP2_PENDING,
+	while (rctx = req_ctx_find_get(0, 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)
+		if (udp_refill_ep(2) < 0)
 			req_ctx_set_state(rctx, RCTX_STATE_UDP_EP2_PENDING);
 	}
 }
@@ -92,10 +102,10 @@ void usb_in_process(void)
 {
 	struct req_ctx *rctx;
 
-	while (rctx = req_ctx_find_get(RCTX_STATE_UDP_RCV_DONE,
+	while (rctx = req_ctx_find_get(0, RCTX_STATE_UDP_RCV_DONE,
 				       RCTX_STATE_MAIN_PROCESSING)) {
 	     	DEBUGPCRF("found used ctx %u: len=%u", 
-			req_ctx_num(rctx), rctx->rx.tot_len);
+			req_ctx_num(rctx), rctx->tot_len);
 		usb_in(rctx);
 	}
 	udp_unthrottle();
diff --git a/firmware/src/os/usb_handler.h b/firmware/src/os/usb_handler.h
index 3efcc1f..9d5ad48 100644
--- a/firmware/src/os/usb_handler.h
+++ b/firmware/src/os/usb_handler.h
@@ -6,6 +6,16 @@
 
 #define MAX_PAYLOAD_LEN	(64 - sizeof(struct openpcd_hdr))
 
+#define USB_RET_RESPOND		(1 << 8)
+#define USB_RET_ERR		(2 << 8)
+#define USB_ERR(x)	(USB_RET_RESPOND|USB_RET_ERR|(x & 0xff))
+
+enum usbapi_err {
+	USB_ERR_NONE,
+	USB_ERR_CMD_UNKNOWN,
+	USB_ERR_CMD_NOT_IMPL,
+};
+
 typedef int usb_cmd_fn(struct req_ctx *rctx);
 
 extern int usb_hdlr_register(usb_cmd_fn *hdlr, u_int8_t class);
diff --git a/firmware/src/os/usbcmd_generic.c b/firmware/src/os/usbcmd_generic.c
new file mode 100644
index 0000000..9dec14e
--- /dev/null
+++ b/firmware/src/os/usbcmd_generic.c
@@ -0,0 +1,49 @@
+/* Some generel USB API commands, common between OpenPCD and OpenPICC
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <openpcd.h>
+#include <os/req_ctx.h>
+#include <os/usb_handler.h>
+#include <os/led.h>
+#include <os/dbgu.h>
+#include <os/main.h>
+
+static int gen_usb_rx(struct req_ctx *rctx)
+{
+	struct openpcd_hdr *poh = (struct openpcd_hdr *) rctx->data;
+	struct openpcd_compile_version *ver = 
+			(struct openpcd_compile_version *) poh->data;
+	int ret = 1;
+
+	rctx->tot_len = sizeof(*poh);
+
+	switch (poh->cmd) {
+	case OPENPCD_CMD_GET_VERSION:
+		DEBUGP("GET_VERSION ");
+		memcpy(ver, &opcd_version, sizeof(*ver));
+		rctx->tot_len += sizeof(*ver);
+		poh->flags |= OPENPCD_FLAG_RESPOND;
+		break;
+	case OPENPCD_CMD_SET_LED:
+		DEBUGP("SET LED(%u,%u) ", poh->reg, poh->val);
+		led_switch(poh->reg, poh->val);
+		break;
+	default:
+		DEBUGP("UNKNOWN ");
+		return USB_ERR(USB_ERR_CMD_UNKNOWN);
+		break;
+	}
+
+	if (poh->flags & OPENPCD_FLAG_RESPOND)
+		return USB_RET_RESPOND;
+	return 0;
+}
+
+void usbcmd_gen_init(void)
+{
+	usb_hdlr_register(&gen_usb_rx, OPENPCD_CMD_CLS_GENERIC);
+}
+
diff --git a/firmware/src/os/usbcmd_generic.h b/firmware/src/os/usbcmd_generic.h
new file mode 100644
index 0000000..0a7b8b7
--- /dev/null
+++ b/firmware/src/os/usbcmd_generic.h
@@ -0,0 +1,4 @@
+#ifndef _USBAPI_GENERIC_H
+#define _USBAPI_GENERIC_H
+extern void usbcmd_gen_init(void);
+#endif
diff --git a/firmware/src/pcd/rc632.c b/firmware/src/pcd/rc632.c
index f0375c6..3713d94 100644
--- a/firmware/src/pcd/rc632.c
+++ b/firmware/src/pcd/rc632.c
@@ -340,7 +340,7 @@ static void rc632_irq(void)
 		DEBUGP("TxComplete ");
 	
 
-	irq_rctx = req_ctx_find_get(RCTX_STATE_FREE,
+	irq_rctx = req_ctx_find_get(0, RCTX_STATE_FREE,
 				    RCTX_STATE_RC632IRQ_BUSY);
 	if (!irq_rctx) {
 		DEBUGPCRF("NO RCTX!\n");
@@ -349,7 +349,7 @@ static void rc632_irq(void)
 		return;
 	}
 
-	irq_opcdh = (struct openpcd_hdr *) &irq_rctx->tx.data[0];
+	irq_opcdh = (struct openpcd_hdr *) irq_rctx->data;
 
 	/* initialize static part of openpcd_hdr for USB IRQ reporting */
 	irq_opcdh->cmd = OPENPCD_CMD_IRQ;
@@ -400,58 +400,56 @@ void rc632_reset(void)
 
 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);
+	struct openpcd_hdr *poh = (struct openpcd_hdr *) rctx->data;
+	u_int16_t len = rctx->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;
+		rc632_reg_read(RAH, poh->reg, &poh->val);
+		DEBUGP("READ REG(0x%02x)=0x%02x ", poh->reg, poh->val);
+		/* register read always has to provoke a response */
+		poh->flags &= OPENPCD_FLAG_RESPOND;
 		break;
 	case OPENPCD_CMD_READ_FIFO:
+		/* FIFO read always has to provoke a response */
+		poh->flags &= OPENPCD_FLAG_RESPOND;
 		{
 		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;
+			rc632_fifo_read(RAH, pih_len, poh->data);
+			rctx->tot_len += pih_len;
 			DEBUGP("READ FIFO(len=%u)=%s ", req_len,
-				hexdump(pih->data, pih_len));
+				hexdump(poh->data, pih_len));
 			req_ctx_set_state(rctx, RCTX_STATE_UDP_EP2_PENDING);
-			udp_refill_ep(2, rctx);
+			udp_refill_ep(2);
 
 			/* get and initialize second rctx */
-			rctx = req_ctx_find_get(RCTX_STATE_FREE,
+			rctx = req_ctx_find_get(0, 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);
+			poh = (struct openpcd_hdr *) rctx->data;
+			rctx->tot_len = sizeof(*poh);
 
 			pih_len = remain_len;
-			rc632_fifo_read(RAH, pih->val, pih->data);
-			rctx->tx.tot_len += pih_len;
+			rc632_fifo_read(RAH, pih_len, poh->data);
+			rctx->tot_len += pih_len;
 			DEBUGP("READ FIFO(len=%u)=%s ", pih_len,
-				hexdump(pih->data, pih_len));
+				hexdump(poh->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;
+			rc632_fifo_read(RAH, req_len, poh->data);
+			rctx->tot_len += pih_len;
 			DEBUGP("READ FIFO(len=%u)=%s ", poh->val,
-				hexdump(pih->data, poh->val));
+				hexdump(poh->data, poh->val));
 		}
-		goto respond;
-		break;
 		}
+		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);
@@ -463,46 +461,33 @@ static int rc632_usb_in(struct req_ctx *rctx)
 		break;
 	case OPENPCD_CMD_READ_VFIFO:
 		DEBUGP("READ VFIFO ");
-		DEBUGP("NOT IMPLEMENTED YET ");
-		goto respond;
+		goto not_impl;
 		break;
 	case OPENPCD_CMD_WRITE_VFIFO:
 		DEBUGP("WRITE VFIFO ");
-		DEBUGP("NOT IMPLEMENTED YET ");
+		goto not_impl;
 		break;
 	case OPENPCD_CMD_REG_BITS_CLEAR:
 		DEBUGP("CLEAR BITS ");
-		pih->val = rc632_clear_bits(RAH, poh->reg, poh->val);
+		poh->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);
+		poh->val = rc632_set_bits(RAH, poh->reg, poh->val);
 		break;
 	case OPENPCD_CMD_DUMP_REGS:
 		DEBUGP("DUMP REGS ");
-		DEBUGP("NOT IMPLEMENTED YET ");
-		goto respond;
+		goto not_impl;
 		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;
+	return (poh->flags & OPENPCD_FLAG_RESPOND) ? USB_RET_RESPOND : 0;
+not_impl:
+	DEBUGP("NOT IMPLEMENTED YET ");
+	return USB_ERR(USB_ERR_CMD_NOT_IMPL);
 }
 
 void rc632_init(void)
diff --git a/firmware/src/picc/adc.c b/firmware/src/picc/adc.c
index c78f89d..f350a49 100644
--- a/firmware/src/picc/adc.c
+++ b/firmware/src/picc/adc.c
@@ -74,7 +74,7 @@ static void adc_irq(void)
 		if (sr & AT91C_ADC_ENDRX) {
 			/* rctx full, get rid of it */
 			DEBUGADC("sending rctx (val=%s) ",
-				 hexdump(rctx->tx.data[4], 2));
+				 hexdump(rctx->data[4], 2));
 				 
 			req_ctx_set_state(rctx, RCTX_STATE_UDP_EP2_PENDING);
 			adc_state.state = ADC_NONE;
@@ -115,8 +115,7 @@ u_int16_T adc_read_pll_dem(void)
 
 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];
+	struct openpcd_hdr *poh = (struct openpcd_hdr *) &rctx->data[0];
 
 	switch (poh->cmd) {
 	case OPENPCD_CMD_ADC_READ:
@@ -129,9 +128,8 @@ static int adc_usb_in(struct req_ctx *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);
+		rctx->tot_len = sizeof(*poh) + poh->val * 2;
+		AT91F_PDC_SetRx(AT91C_BASE_PDC_ADC, rctx->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 |
diff --git a/firmware/src/picc/decoder.c b/firmware/src/picc/decoder.c
index ab77250..bdd910e 100644
--- a/firmware/src/picc/decoder.c
+++ b/firmware/src/picc/decoder.c
@@ -17,8 +17,6 @@
  *
  */
 
-
-
 #include <errno.h>
 #include <sys/types.h>
 #include <picc/decoder.h>
diff --git a/firmware/src/picc/main_openpicc.c b/firmware/src/picc/main_openpicc.c
index c8d2fd0..df754de 100644
--- a/firmware/src/picc/main_openpicc.c
+++ b/firmware/src/picc/main_openpicc.c
@@ -47,6 +47,7 @@ static u_int8_t load_mod = 0;
 
 void _init_func(void)
 {
+	/* low-level hardware initialization */
 	pio_irq_init();
 	pll_init();
 	poti_init();
@@ -57,6 +58,10 @@ void _init_func(void)
 	adc_init();
 	ssc_rx_init();
 	// ssc_tx_init();
+	
+	/* high-level protocol */
+	decoder_init();
+	opicc_usbapi_init();
 
 	AT91F_PIO_CfgInput(AT91C_BASE_PIOA, OPENPICC_PIO_BOOTLDR);
 }
diff --git a/firmware/src/picc/openpicc.c b/firmware/src/picc/openpicc.c
new file mode 100644
index 0000000..7b2b5f2
--- /dev/null
+++ b/firmware/src/picc/openpicc.c
@@ -0,0 +1,82 @@
+/* Main state machine and register implementation for OpenPICC
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de> */
+
+#include <sys/types.h>
+#include <openpcd.h>
+#include <openpicc_regs.h>
+#include <openpicc.h>
+#include <os/req_ctx.h>
+#include <os/usb_handler.h>
+
+#include "opicc_reg.h"
+
+/********************************************************************
+ * OpenPICC Register set
+ ********************************************************************/
+
+/* Our registers, including their power-up default values */
+static u_int16_t opicc_regs[_OPICC_NUM_REGS] = {
+	[OPICC_REG_14443A_UIDLEN]	= 4,
+	[OPICC_REG_14443A_FDT0]		= 1236,
+	[OPICC_REG_14443A_FDT1]		= 1172,
+	[OPICC_REG_14443A_STATE]	= ISO14443A_ST_POWEROFF,
+	[OPICC_REG_RX_CLK_DIV]		= 32,
+	[OPICC_REG_RX_CLK_PHASE]	= 0,
+	[OPICC_REG_RX_CONTROL]		= 0,
+	[OPICC_REG_TX_CLK_DIV]		= 16,
+	[OPICC_REG_TX_CONTROL]		= 0,
+	[OPICC_REG_RX_COMP_LEVEL]	= 0,
+};
+
+u_int16_t opicc_reg_read(enum opicc_reg reg)
+{
+	if (reg < _OPICC_NUM_REGS)
+		return opicc_regs[reg];
+	return 0;
+}
+
+void opicc_reg_write(enum opicc_reg reg, u_int16_t val)
+{
+	if (reg < _OPICC_NUM_REGS)
+		opicc_regs[reg] = val;
+	return;
+}
+
+/********************************************************************
+ * OpenPICC USB Commandset (access to register set, ...)
+ ********************************************************************/
+
+static int opicc_reg_usb_in(struct req_ctx *rctx)
+{
+	struct openpcd_hdr *poh = (struct openpcd_hdr *) &rctx->data[0];
+	u_int16_t *val16 = (u_int16_t *) poh->data;
+	
+	poh->val = 0;
+	rctx->tot_len = sizeof(*poh);
+
+	switch (poh->cmd) {
+	case OPENPCD_CMD_PICC_REG_READ:
+		*val16 = opicc_reg_read(poh->reg);
+		rctx->tot_len += sizeof(u_int16_t);
+		poh->flags |= OPENPCD_FLAG_RESPOND;
+		break;
+	case OPENPCD_CMD_PICC_REG_WRITE:
+		if (rctx->tot_len < sizeof(*poh) + sizeof(u_int16_t)) {
+			poh->flags = OPENPCD_FLAG_ERROR;
+		}
+		opicc_reg_write(poh->reg, *val16);
+		break;
+	default:
+		return USB_ERR(USB_ERR_CMD_UNKNOWN);
+	}
+	
+	if (poh->flags & OPENPCD_FLAG_RESPOND)
+		return USB_RET_RESPOND;
+
+	return 0;
+}
+
+void opicc_usbapi_init(void)
+{
+	usb_hdlr_register(&opicc_reg_usb_in, OPENPCD_CMD_CLS_PICC);
+}
diff --git a/firmware/src/picc/opicc_reg.h b/firmware/src/picc/opicc_reg.h
new file mode 100644
index 0000000..e77d157
--- /dev/null
+++ b/firmware/src/picc/opicc_reg.h
@@ -0,0 +1,18 @@
+#ifndef _OPCD_REG_H
+#define _OPCD_REG_H
+
+#include <openpicc.h>
+#include <sys/types.h>
+
+#ifdef DEBUG
+u_int16_t opicc_reg_read(enum opicc_reg reg);
+void opicc_reg_write(enum opicc_reg reg, u_int16_t val);
+#else
+u_int16_t opicc_regs[_OPICC_NUM_REGS];
+#define opicc_reg_read(x)	(opicc_regs[x])
+#define opicc_reg_Write(x, y)	(opicc_regs[x] = y)
+#endif
+
+void opicc_usbapi_init(void);
+
+#endif
diff --git a/firmware/src/picc/pll.c b/firmware/src/picc/pll.c
index f98d171..7eb49a4 100644
--- a/firmware/src/picc/pll.c
+++ b/firmware/src/picc/pll.c
@@ -22,6 +22,7 @@
 #include <lib_AT91SAM7.h>
 #include <os/pio_irq.h>
 #include <os/dbgu.h>
+#include <os/led.h>
 #include "../openpcd.h"
 
 void pll_inhibit(int inhibit)
diff --git a/firmware/src/picc/ssc_picc.c b/firmware/src/picc/ssc_picc.c
index ea48de5..d4db71a 100644
--- a/firmware/src/picc/ssc_picc.c
+++ b/firmware/src/picc/ssc_picc.c
@@ -166,8 +166,8 @@ static struct openpcd_hdr opcd_ssc_hdr = {
 
 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;
+	memcpy(rctx->data, &opcd_ssc_hdr, sizeof(opcd_ssc_hdr));
+	rctx->tot_len = MAX_HDRSIZE + MAX_REQSIZE -1;
 }
 
 #ifdef DEBUG_SSC_REFILL
@@ -191,7 +191,7 @@ 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);
+	rctx = req_ctx_find_get(1, RCTX_STATE_FREE, RCTX_STATE_SSC_RX_BUSY);
 	if (!rctx) {
 		DEBUGPCRF("no rctx for refill!");
 		return -1;
@@ -200,12 +200,12 @@ static int8_t ssc_rx_refill(void)
 
 	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);
+		AT91F_PDC_SetRx(rx_pdc, &rctx->data[MAX_HDRSIZE],
+				(rctx->size-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 = req_ctx_find_get(1, RCTX_STATE_FREE, 
 					RCTX_STATE_SSC_RX_BUSY);
 		if (!rctx) {
 			DEBUGPCRF("no rctx for secondary refill!");
@@ -216,8 +216,8 @@ static int8_t ssc_rx_refill(void)
 
 	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);
+		AT91F_PDC_SetNextRx(rx_pdc, &rctx->data[MAX_HDRSIZE],
+				    (rctx->size-MAX_HDRSIZE)>2);
 		ssc_state.rx_ctx[1] = rctx;
 		return 2;
 	} else {
@@ -331,8 +331,7 @@ void ssc_tx_init(void)
 
 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];
+	struct openpcd_hdr *poh = (struct openpcd_hdr *) rctx->data;
 
 	switch (poh->cmd) {
 	case OPENPCD_CMD_SSC_READ:
@@ -366,7 +365,7 @@ void ssc_rx_init(void)
 	/* Reset */
 	//ssc->SSC_CR = AT91C_SSC_SWRST;
 
-	/* don't divide clock */
+	/* don't divide clock inside SSC, we do that in tc_cdiv */
 	ssc->SSC_CMR = 0;
 
 	ssc->SSC_RCMR = AT91C_SSC_CKS_RK | AT91C_SSC_CKO_NONE |
-- 
cgit v1.2.3