diff mbox

[U-Boot,2/3] add USB DFU driver

Message ID 1297527603-21566-1-git-send-email-korgull@home.nl
State Changes Requested
Headers show

Commit Message

Marcel Janssen Feb. 12, 2011, 4:20 p.m. UTC
From: Marcel <korgull@home.nl>

Signed-off-by: Marcel <korgull@home.nl>
---
 common/update_dfu.c         |   90 +++
 doc/README.dfu              |  129 ++++
 drivers/usb/gadget/Makefile |   10 +-
 drivers/usb/gadget/usbdfu.c | 1470 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1697 insertions(+), 2 deletions(-)
 create mode 100644 common/update_dfu.c
 create mode 100644 doc/README.dfu
 create mode 100644 drivers/usb/gadget/usbdfu.c
diff mbox

Patch

diff --git a/common/update_dfu.c b/common/update_dfu.c
new file mode 100644
index 0000000..5f8da42
--- /dev/null
+++ b/common/update_dfu.c
@@ -0,0 +1,90 @@ 
+/*
+ * (C) Copyright 2008 Semihalf
+ *
+ * Written by: Rafal Czubak <rcz@semihalf.com>
+ *             Bartlomiej Sieka <tur@semihalf.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+#include <common.h>
+DECLARE_GLOBAL_DATA_PTR;
+
+#include <malloc.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <asm-generic/errno.h>
+
+#include <linux/usb/ch9.h>
+
+#include <linux/usb/gadget.h>
+#include <command.h>
+#include <usb/atmel_usba_udc.h>
+#include <asm/arch/gpio.h>
+
+int dfu_finished;
+int usb_dfu_init(void);
+
+#ifdef CONFIG_USB_GADGET_ATMEL_USBA
+struct platform_data dfubrd = {
+	.board = {
+		//.vbus_pin   = AT91_PIN_PC9,// AT91_PIN_PC0, },
+		.vbus_pin   = AT91_PIN_PC0,
+		.pullup_pin = 1,
+	},
+	.udc_clk = AT91SAM9G45_ID_UDPHS,
+};
+#endif
+
+int dfu_loop(void)
+{
+	int rcv;
+	
+	dfu_finished = 0;
+#ifdef CONFIG_USB_GADGET_ATMEL_USBA	
+	usba_udc_probe(&dfubrd);
+#endif	
+#ifdef 	CONFIG_USB_GADGET_AT91
+	at91udc_probe(&dfubrd);
+#endif
+	udelay(100000);
+
+	if(usb_dfu_init() == 0)
+	{
+	
+	  while (!dfu_finished ) {
+		/* Handle control-c and timeouts */
+		rcv = usb_gadget_handle_interrupts();
+		if(rcv) 
+		    if (ctrlc()) goto dfu_end;
+	  }
+	  return 0;
+	}
+	return -1;
+dfu_end:
+	return -1;
+}
+
+U_BOOT_CMD(
+	dfu,	1,	0,	dfu_loop,
+	"Start DFU function",
+	"No params, see README.dfu"
+);
+ 
\ No newline at end of file
diff --git a/doc/README.dfu b/doc/README.dfu
new file mode 100644
index 0000000..04b7b76
--- /dev/null
+++ b/doc/README.dfu
@@ -0,0 +1,129 @@ 
+USBD DFU mode 
+
+Initially written by Marcel Janssen (marcel.janssen@admesy.nl).
+Based on parts from OpenMoko, ether.c and update.c
+
+
+========================================
+
+This describes the DFU implementation in u-boot.
+
+The implementation works with dfu-utils to upgrade NAND partitions defined by mtdparts.
+The board configuration file needs serveral CONFIG options to be set.
+DFU is implemented to be executed as a command "dfu". This command should start the USB device
+controller and the DFU driver. This is done in common/update_dfu.c
+
+A typical implementation would be that a script is executed, that will check whether DFU should 
+be started. If so, it executes 'dfu" and the device will announce itself to the host as a DFU
+capable device. dfu-util can than be used to upgrade the partitions defined by mtdparts.
+
+Description of flow :
+dfu-utils sets the alternate interface which corresponds to the selected partition.
+The file (uImage, rootfs.arm.jffs2) is loaded fully to RAM first.
+U-boot nand routines are used to write from RAM to NAND.
+
+LED usage :
+Status LED's can be defined to show DFU action.
+Define the RED and GREEN leds to make this happen.
+
+Initial testing example :
+This was done on the in-circuit sam9g45_oem board. This board uses atmel_usbd_udc.c
+
+========================================
+
+
+
+To make DFU work you need a working USB controller, for example at91_udc or atmel_usba_udc.
+Make sure to set it in the board config file 
+
+========================================
+USBD CONFIG options 
+----------------------------------------
+
+#define CONFIG_USB_GADGET		
+#define CONFIG_USB_GADGET_ATMEL_USBA   (or  #define CONFIG_USB_GADGET_AT91_UDC )
+#define CONFIG_USB_GADGET_DUALSPEED   	 
+
+----------------------------------------
+USBD CONFIG options end
+========================================
+
+
+
+The DFU driver has a few options. 
+Make sure that CONFIG_USBD_DFU_XFER_SIZE does not exceed your USB_BUFSIZE.
+
+========================================
+DFU CONFIG options 
+----------------------------------------
+
+#define CONFIG_USBD_DFU                 1
+#ifdef  CONFIG_USBD_DFU
+#define CONFIG_USBD_VENDORID		0x23CF     /* Admesy - Use this for testing purposes only */ 
+#define CONFIG_USBD_PRODUCTID_DFU       0xBEEF     /* donated number */
+#define CONFIG_USBD_MANUFACTURER	"Admesy"
+#define CONFIG_USBD_PRODUCT_NAME	"Admesy DFU 001"
+#define CONFIG_USBD_DFU_XFER_SIZE	4096       /* 4096 is maximum or increase USB_BUFSIZ*/
+#define CONFIG_USBD_DFU_INTERFACE       0
+#define DFU_NUM_ALTERNATES		3          /* 3 partitions */
+#define LOAD_ADDR ((unsigned char *)0x70400000)    /* RAM address to use to write files to */
+#endif
+
+----------------------------------------
+DFU CONFIG options end
+========================================
+
+
+
+In order to make DFU work with dfu-utils, mtdparts need to be defined. 
+See the example futher below on how to do this.
+
+========================================
+mtdparts example with dfu-utils
+----------------------------------------
+
+mtdparts add nand0 0x2000000 kernel 
+mtdparts add nand0 0x1000000@0x00200000 root 
+mtdparts add nand0 0xEE00000@0x01200000 data 
+saveenv
+
+Your mtdparts than should look like this :
+
+board> mtdparts
+device nand0 <nand.0>, # parts = 3
+ #: name		size		offset		mask_flags
+ 0: kernel              0x00200000	0x00000000	0
+ 1: root                0x01000000	0x00200000	0
+ 2: data                0x0ee00000	0x01200000	0
+
+active partition: nand0,0 - (kernel) 0x00200000 @ 0x00000000
+
+
+After the mtdparts have been defined, dfu-utils can be used to upgrade the kernel and root partition.
+Make sure you have read/write access to the DFU device !
+
+cd dfu-utils/src
+./dfu-util -a0 -D uImage
+./dfu-util -a1 -D rootfs.arm.jffs2 -R
+
+----------------------------------------
+mtdparts example end 
+========================================
+
+
+
+Possible changes for the near future :
+1) integrate DFU and Ethernet and/or tty by making it a composite driver.
+2) allow partitions larger than RAM by implementing per-buffer NAND writing.
+   Openmoko does this, but I didn't need it and liked to write to RAM first.
+3) Allow DFU to flash NOR (could be added to handle_dnload )
+
+
+
+
+
+
+
+
+
+
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 024844d..91246da 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -26,10 +26,15 @@  include $(TOPDIR)/config.mk
 LIB	:= $(obj)libusb_gadget.o
 
 # new USB gadget layer dependencies
-ifdef CONFIG_USB_ETHER
-COBJS-y += ether.o epautoconf.o config.o usbstring.o
+#ifdef CONFIG_USB_ETHER
+#COBJS-y += ether.o epautoconf.o config.o usbstring.o
+
+ifdef CONFIG_USBD_DFU
+COBJS-y += usbdfu.o epautoconf.o config.o usbstring.o
+
 COBJS-$(CONFIG_USB_GADGET_AT91) += at91_udc.o
 COBJS-$(CONFIG_USB_GADGET_ATMEL_USBA) += atmel_usba_udc.o
+
 else
 # Devices not related to the new gadget layer depend on CONFIG_USB_DEVICE
 ifdef CONFIG_USB_DEVICE
@@ -43,6 +48,7 @@  COBJS-$(CONFIG_SPEARUDC) += spr_udc.o
 endif
 endif
 
+
 COBJS	:= $(COBJS-y)
 SRCS	:= $(COBJS:.o=.c)
 OBJS	:= $(addprefix $(obj),$(COBJS))
diff --git a/drivers/usb/gadget/usbdfu.c b/drivers/usb/gadget/usbdfu.c
new file mode 100644
index 0000000..8c6199e
--- /dev/null
+++ b/drivers/usb/gadget/usbdfu.c
@@ -0,0 +1,1470 @@ 
+/* (C) 2011 Admesy B.V (Marcel Janssen)
+ * (C) 2007 by OpenMoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * TODO:
+ * - make NAND support reasonably self-contained and put in apropriate
+ *   ifdefs
+ * - add some means of synchronization, i.e. block commandline access
+ *   while DFU transfer is in progress, and return to commandline once
+ *   we're finished
+ * - add VERIFY support after writing to flash
+ * - sanely free() resources allocated during first uppload/download
+ *   request when aborting
+ * - sanely free resources when another alternate interface is selected
+ *
+ * Maybe:
+ * - add something like uImage or some other header that provides CRC
+ *   checking?
+ * - make 'dnstate' attached to 'struct dfu_dev'
+ */
+
+#include <config.h>
+#if defined(CONFIG_USBD_DFU)
+
+#include <common.h>
+DECLARE_GLOBAL_DATA_PTR;
+
+#include <malloc.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <asm-generic/errno.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include "gadget_chips.h"
+
+#include <usb_dfu.h>
+#include <usb_dfu_descriptors.h>
+#include <usb_dfu_trailer.h>
+#include <linux/ctype.h>
+#include <linux/porting-compat.h>
+
+#include <nand.h>
+#include <jffs2/load_kernel.h>
+#if defined(CONFIG_CMD_MTDPARTS)
+
+/* partition handling routines */
+int mtdparts_init(void);
+
+int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num);
+int find_dev_and_part(const char *id, struct mtd_device **dev,
+		      u8 *part_num, struct part_info **part);
+#endif
+
+extern struct list_head devices;
+extern int dfu_finished;
+//extern struct usb_string_descriptor usb_strings[DFU_STR_COUNT];
+
+//#include "usbdcore_s3c2410.h"
+//#include <usb/atmel_usba_udc.h>
+//#include "../drivers/serial/usbtty.h"			/* for STR_* defs */
+
+#define RET_NOTHING	0
+#define RET_ZLP		1
+#define RET_STALL	2
+
+/* this should be done differently */
+#define EP0_MAX_PACKET_SIZE	64 /* MUSB_EP0_FIFOSIZE */
+
+unsigned char ledcount;
+
+#define GFP_ATOMIC ((gfp_t) 0)
+//#define GFP_KERNEL ((gfp_t) 0)
+
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+#define	DEVSPEED	USB_SPEED_HIGH
+#else
+#define DEVSPEED	USB_SPEED_FULL
+#endif
+
+#define USB_CONNECT_TIMEOUT (30 * CONFIG_SYS_HZ)
+volatile enum dfu_state *system_dfu_state; /* for 3rd parties */
+
+char rtm_dfu = 0;
+
+/*
+ * Some systems will want different product identifers published in the
+ * device descriptor, either numbers or strings or both.  These string
+ * parameters are in UTF-8 (superset of ASCII's 7 bit characters).
+ */
+#define DRIVER_DESC		"USBD-DFU device"
+#define FIXED_SERIAL		"1234567"   /* usually only one device should be in DFU mode, this may be fixed */
+static ushort bcdDevice;
+#if defined(CONFIG_USBD_MANUFACTURER)
+static char *iManufacturer = CONFIG_USBD_MANUFACTURER;
+#else
+static char *iManufacturer = "U-boot-dfu";
+#endif
+static char *iProduct;
+static char *iSerialNumber;
+
+/* holds our biggest descriptor */
+#define USB_BUFSIZ	4096
+
+#ifndef CONFIG_USBD_PRODUCTID_DFU
+#define #error YOU need to define a USBD product ID for DFU
+#endif
+#ifndef LOAD_ADDR
+#define LOAD_ADDR ((unsigned char *)0x70400000)
+#endif
+
+static struct dfu_dev l_dfudev;
+//static struct dfu_device l_dfudevice;
+static struct usb_gadget_driver dfu_driver;
+static u8 control_req[USB_BUFSIZ];
+
+
+struct usb_device_descriptor 
+dfu_dev_descriptor = {
+	.bLength		= USB_DT_DEVICE_SIZE,
+	.bDescriptorType	= USB_DT_DEVICE,
+	.bcdUSB			= __constant_cpu_to_le16(0x0100),
+	.bDeviceClass		= 0x00,
+	.bDeviceSubClass	= 0x00,
+	.bDeviceProtocol	= 0x00,
+	.bMaxPacketSize0	= EP0_MAX_PACKET_SIZE,
+	.idVendor		= __constant_cpu_to_le16(CONFIG_USBD_VENDORID),
+	.idProduct		= __constant_cpu_to_le16(CONFIG_USBD_PRODUCTID_DFU),
+	.bcdDevice		= 0x0000,
+	.iManufacturer		= DFU_STR_MANUFACTURER,
+	.iProduct		= DFU_STR_PRODUCT,
+	.iSerialNumber		= DFU_STR_SERIAL,
+	.bNumConfigurations	= 0x01,
+};
+
+/**/static struct usb_config_descriptor
+dfu_config = {
+	.bLength =		USB_DT_CONFIG_SIZE,
+	.bDescriptorType =	USB_DT_CONFIG,
+
+	//compute wTotalLength on the fly 
+	.wTotalLength		= USB_DT_CONFIG_SIZE +
+			          USB_DT_INTERFACE_SIZE +  USB_DT_DFU_SIZE,
+	.bNumInterfaces =	1,
+	.bConfigurationValue =	1,
+	.iConfiguration =	DFU_STR_CONFIG,
+	.bmAttributes =		USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+	.bMaxPower =		0x60,
+};
+
+static struct usb_interface_descriptor
+control_intf = {
+	.bLength =		sizeof control_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bInterfaceNumber =	CONFIG_USBD_DFU_INTERFACE,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	USB_CLASS_APP_SPEC,
+	.bInterfaceSubClass =	USB_DFU_SUBCLASS,
+	.bInterfaceProtocol =	1,
+	.iInterface =		DFU_STR_CONFIG,
+};
+
+static struct usb_dfu_func_descriptor 
+dfu_func = {						
+	.bLength		= USB_DT_DFU_SIZE,			
+	.bDescriptorType	= USB_DT_DFU,				
+	.bmAttributes		= USB_DFU_CAN_UPLOAD | USB_DFU_CAN_DOWNLOAD , 
+	.wDetachTimeOut		= 0xff00,				
+	.wTransferSize		= CONFIG_USBD_DFU_XFER_SIZE,		
+	.bcdDFUVersion		= 0x0100,				
+};
+
+static struct _dfu_desc dfu_cfg_descriptor = {
+	.ucfg = {
+		.bLength		= USB_DT_CONFIG_SIZE,
+		.bDescriptorType	= USB_DT_CONFIG,
+		.wTotalLength		= USB_DT_CONFIG_SIZE +
+					  DFU_NUM_ALTERNATES * USB_DT_INTERFACE_SIZE +
+					  USB_DT_DFU_SIZE,
+		.bNumInterfaces		= 1,
+		.bConfigurationValue	= 1,
+		.iConfiguration		= DFU_STR_CONFIG,
+		//FIXME BMATTRIBUTE_RESERVED 
+		.bmAttributes		= USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+		.bMaxPower		= 200,
+	},
+	.func_dfu = DFU_FUNC_DESC,
+};
+
+static struct usb_qualifier_descriptor
+dev_qualifier = {
+	.bLength =		sizeof dev_qualifier,
+	.bDescriptorType =	USB_DT_DEVICE_QUALIFIER,
+
+	.bcdUSB =		__constant_cpu_to_le16(0x0200),
+	.bDeviceClass =		USB_CLASS_APP_SPEC,
+
+	.bNumConfigurations =	1,
+};
+
+
+static const struct usb_descriptor_header *hs_rtm_function[3] = {
+        (struct usb_descriptor_header *) &control_intf,
+        (struct usb_descriptor_header *) &dfu_func,
+	NULL,
+};
+
+static const struct usb_descriptor_header *hs_dfu_function[5] = {
+        (struct usb_descriptor_header *) &dfu_cfg_descriptor.uif[0],
+        (struct usb_descriptor_header *) &dfu_cfg_descriptor.uif[1],
+        (struct usb_descriptor_header *) &dfu_cfg_descriptor.uif[2],
+        (struct usb_descriptor_header *) &dfu_cfg_descriptor.func_dfu,
+	NULL,
+};
+
+struct dnload_state {
+	nand_info_t *nand;
+	struct part_info *part;
+	unsigned int part_net_size;	/* net sizee (excl. bad blocks) of part */
+
+	nand_erase_options_t erase_opts;
+	nand_write_options_t write_opts;
+	nand_read_options_t read_opts;
+
+	unsigned char *ptr;	/* pointer to next empty byte in buffer */
+	unsigned int off;	/* offset of current erase page in flash chip */
+	unsigned char *buf;	/* pointer to allocated erase page buffer */
+
+	/* unless doing an atomic transfer, we use the static buffer below.
+	 * This saves us from having to clean up dynamic allications in the
+	 * various error paths of the code.  Also, it will always work, no
+	 * matter what the memory situation is. */
+	unsigned char _buf[0x20000];	/* FIXME: depends flash page size */
+};
+
+static struct dnload_state _dnstate;
+
+static struct part_info *get_partition_nand(int idx)
+{
+	struct mtd_device *dev;
+	struct part_info *part;
+	struct list_head *dentry;
+	struct list_head *pentry;
+	int i;
+
+	if (mtdparts_init())
+		return NULL;
+
+	list_for_each(dentry, &devices) {
+		dev = list_entry(dentry, struct mtd_device, link);
+		if (dev->id->type == MTD_DEV_TYPE_NAND) {
+			i = 0;
+			list_for_each(pentry, &dev->parts) {
+				if (i == idx)  {
+					part = list_entry(pentry,
+					    struct part_info, link);
+					return part;
+				}
+				i++;
+			}
+			return NULL;
+		}
+	}
+	return NULL;
+}
+
+/* descriptors that are built on-demand */
+static char manufacturer[50];
+static char product_desc[40] = CONFIG_USBD_PRODUCT_NAME;//DRIVER_DESC;
+static char serial_number[20] = FIXED_SERIAL;
+
+/* static strings, in UTF-8 */
+static struct usb_string		strings[] = {
+	{ DFU_STR_MANUFACTURER,	manufacturer, }, 
+	{ DFU_STR_PRODUCT,	product_desc, },
+	{ DFU_STR_SERIAL,	serial_number, },
+	{ DFU_STR_CONFIG,	"USB DFU Config", },
+	{ DFU_STR_ALT1,		"DFU part1", },
+	{ DFU_STR_ALT2,		"DFU part2", },
+	{ DFU_STR_ALT3,		"DFU part3", },
+	{ DFU_STR_ALT4,		"DFU part4", },
+	{ DFU_STR_ALT5,		"DFU part5", },
+	{ DFU_STR_ALT6,		"DFU part6", },
+	{  }		// end of list 
+};
+
+struct usb_gadget_strings  stringtab = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings,
+};
+
+/*============================================================================*/
+
+/**
+ * strlcpy - Copy a %NUL terminated string into a sized buffer
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ * @size: size of destination buffer
+ *
+ * Compatible with *BSD: the result is always a valid
+ * NUL-terminated string that fits in the buffer (unless,
+ * of course, the buffer size is zero). It does not pad
+ * out the result like strncpy() does.
+ */
+size_t strlcpy(char *dest, const char *src, size_t size)
+{
+	size_t ret = strlen(src);
+
+	if (size) {
+		size_t len = (ret >= size) ? size - 1 : ret;
+		memcpy(dest, src, len);
+		dest[len] = '\0';
+	}
+	return ret;
+}
+
+/*
+ * one config, two interfaces:  control, data.
+ * complications: class descriptors, and an altsetting.
+ */
+static int
+config_buf(struct usb_gadget *g, u8 *buf, u8 type, unsigned index)
+{
+	int					len;
+	const struct usb_config_descriptor	*config;
+	const struct usb_descriptor_header	**function;
+	int					hs = 0;
+
+	debug("Set configuration - type %d - index %d\n", type, index);
+	
+	if (gadget_is_dualspeed(g)) {
+		hs = (g->speed == USB_SPEED_HIGH);
+		if (type == USB_DT_OTHER_SPEED_CONFIG)
+			hs = !hs;
+	}
+
+	if (index >= dfu_dev_descriptor.bNumConfigurations)
+		return -EINVAL;
+
+	switch (type)
+	{
+	  case 2: 
+	  {	
+	    config = &dfu_config;  
+	    if(rtm_dfu == 0) function = hs_rtm_function;
+	    else function = hs_dfu_function;
+	    
+	  }
+	  break;
+	}
+	len = usb_gadget_config_buf(config, buf, USB_BUFSIZ,  function);
+	if (len < 0)
+		return len;
+        ((struct usb_config_descriptor *) buf)->bDescriptorType = type;
+	return len;
+}
+
+static int
+set_dfu_config(struct dfu_dev *dev, gfp_t gfp_flags)
+{
+        const struct usb_config_descriptor	*config;
+	
+	printf("Set DFU config\n");
+
+	config = &dfu_cfg_descriptor.ucfg;
+
+	return 0;
+}
+
+static int handle_dnload(struct usb_gadget *gadget, u_int16_t val, u_int16_t len, int first)
+{
+	struct dfu_dev *dev = &l_dfudev;
+	struct usb_request	*req = dev->req;
+	struct dnload_state *ds = &_dnstate;
+	int rc;
+	ulong addr;
+	size_t rwsize;
+
+	debug("download(len=%u, first=%u) \n", len, first);
+	
+	if (len > CONFIG_USBD_DFU_XFER_SIZE) {
+		/* Too big. Not that we'd really care, but it's a
+		 * DFU protocol violation */
+		printf("length exceeds flash page size ");
+	    	dev->dfu_state = DFU_STATE_dfuERROR;
+		dev->dfu_status = DFU_STATUS_errADDRESS;
+		return RET_STALL;
+	}
+
+	if (first) {
+		/* Make sure that we have a valid mtd partition table */
+		/* the following code crashes I guess because of env_buf too small [32] */
+		char *mtdp = getenv("mtdparts");
+		if(mtdp) printf("Valid MTD partitions found\n");
+		//if (!mtdp)
+			//run_command("dynpart", 0);
+		else 
+		{
+		  dev->dfu_state = DFU_STATE_dfuERROR;
+		  dev->dfu_status = DFU_STATUS_errADDRESS;
+		  return RET_STALL;
+		}
+	}
+
+	if (len == 0) {
+		debug("zero-size write -> MANIFEST_SYNC ");
+		dev->dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
+
+		/* cleanup */
+		switch (dev->alternate) {
+			char buf[12];	
+			/* we don't actually handle partitions differently */
+			/* the original Openmoko driver did use the alternate setting to do different things */
+			/* we have no need for that */
+			/* a big difference is that our driver first reads the full firmware to RAM */
+			/* and than writes it to flash after */
+			/* we could also not write to flash but let a script handle it via "nand write" */
+		default:
+		        sprintf(buf, "0x%08x", ds->ptr - ds->buf);
+			addr = (ulong)LOAD_ADDR;
+			rwsize = ds->ptr - ds->buf;
+			printf("Filesize = %s\n",buf);
+			setenv("filesize", buf);
+			rc = 0;
+			rc = mtdparts_init();
+			if(rc) 
+			{
+			  printf("Error initilizing mtdparts\n");
+			  break;
+			}
+			ds->part =  get_partition_nand(dev->alternate);
+			if( ds->part == NULL)
+			{
+			  printf("Error reading partition info\n");
+			  break;
+			}
+			
+			ds->erase_opts.offset = ds->part->offset;
+			ds->erase_opts.length = ds->part->size;
+			if (!rc && !strcmp(ds->part->name, "root")) ds->erase_opts.jffs2  = 1;
+			else ds->erase_opts.jffs2  = 0;
+			ds->erase_opts.quiet  = 0;
+			ds->erase_opts.spread = 1;
+			ds->erase_opts.scrub = 0; /* make sure it was not set previously */
+			ds->nand = &nand_info[ds->part->dev->id->num];
+			
+			printf("Using partition : %s ,", ds->part->name);
+			if(ds->erase_opts.jffs2) printf(" with jffs2 option\n");
+			else printf(" without jffs2 option\n");
+			printf("Partition size=%lx offset=%lx\n", (unsigned long)ds->part->size , (unsigned long)ds->part->offset );
+			
+#ifdef CONFIG_GREEN_LED
+	green_LED_off();
+#endif			
+#ifdef CONFIG_RED_LED
+	red_LED_off();
+#endif	
+			rc = nand_erase_opts(ds->nand, &ds->erase_opts);
+			if( rc )
+			{
+			  printf("NAND erase failed\n");
+			  break;
+			}
+			else printf("NAND erased succesfully\n");
+#ifdef CONFIG_GREEN_LED
+	green_LED_on();
+#endif			
+#ifdef CONFIG_RED_LED
+	red_LED_off();
+#endif				
+			rc = nand_write_skip_bad(ds->nand, ds->part->offset, &rwsize, (u_char *)addr);
+			if( rc )
+			{
+			  printf("NAND write failed\n");
+			  break;
+			} 
+			else printf("NAND Written succesfully\n");
+#ifdef CONFIG_GREEN_LED
+	green_LED_on();
+#endif			
+#ifdef CONFIG_RED_LED
+	red_LED_on();
+#endif				
+			break;
+
+		}
+		//dfu_finished = 1;
+		return RET_ZLP;
+	}
+
+	if (req->length != len) {
+		printf("req->length(%u) != len(%u) ?!? ",
+			req->length, len);
+	    	dev->dfu_state = DFU_STATE_dfuERROR;
+		dev->dfu_status = DFU_STATUS_errADDRESS;
+		return RET_STALL;
+	}
+
+	if (first && ds->buf && ds->buf != ds->_buf && ds->buf != LOAD_ADDR) {
+		printf("free buffer \n");
+		free(ds->buf);
+		ds->buf = ds->_buf;
+	}
+
+	debug("alternate %d , first = %d\n",dev->alternate, first);
+	switch (dev->alternate) {
+	default:
+		if (first) {
+			//printf("Starting DFU DOWNLOAD to RAM %s\n",
+			//	LOAD_ADDR);
+			ds->buf = LOAD_ADDR;
+			ds->ptr = ds->buf;
+			printf("Starting DFU DOWNLOAD to RAM %p\n",ds->ptr);
+		}
+		memcpy(ds->ptr, req->buf, len);
+		ds->ptr += len;
+		debug("data in buf %x, next address = %p\n", *(char *)req->buf, ds->ptr);
+		break;
+	}
+	debug("Copy finished at %p\n", ds->ptr);
+	return RET_ZLP;
+}
+
+static int handle_upload(struct usb_gadget *gadget, u_int16_t val, u_int16_t len, int first)
+{
+	struct dfu_dev *dev = &l_dfudev;//get_gadget_data(gadget);
+	//struct usb_request	*req = dev->req;
+	//struct dnload_state *ds = &_dnstate;
+	//unsigned int remain;
+	//int rc;
+
+	printf("upload(val=0x%02x, len=%u, first=%u) ", val, len, first);
+
+	if (len > CONFIG_USBD_DFU_XFER_SIZE) {
+		/* Too big */
+		dev->dfu_state = DFU_STATE_dfuERROR;
+		dev->dfu_status = DFU_STATUS_errADDRESS;
+		//udc_ep0_send_stall();
+		debug("Error: Transfer size > CONFIG_USBD_DFU_XFER_SIZE ");
+		return -EINVAL;
+	}
+
+	switch (dev->alternate) {
+	default:
+		/* this needs to be implemented */
+		break;
+	}
+
+	printf("returning len=%u\n", len);
+	return len;
+}
+
+static void handle_getstatus(struct usb_gadget *gadget, int max)
+{
+	struct dfu_dev *dev          = &l_dfudev;//get_gadget_data(gadget);
+	struct usb_request	*req = dev->req;
+	struct dfu_status *dstat     = (struct dfu_status *) req->buf;
+
+	//if (!req->buf || req->length < sizeof(*dstat)) {
+	//	debug("invalid ctrl! ");
+	//	return;
+	//}
+
+	switch (dev->dfu_state) {
+	case DFU_STATE_dfuDNLOAD_SYNC:
+		//printf("DNLOAD_IDLE ");
+		//dev->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
+		//break;
+	case DFU_STATE_dfuDNBUSY:
+#if 0
+		if (fsr & AT91C_MC_PROGE) {
+			debug("errPROG ");
+			dev->dfu_status = DFU_STATUS_errPROG;
+			dev->dfu_state = DFU_STATE_dfuERROR;
+		} else if (fsr & AT91C_MC_LOCKE) {
+			debug("errWRITE ");
+			dev->dfu_status = DFU_STATUS_errWRITE;
+			dev->dfu_state = DFU_STATE_dfuERROR;
+		} else if (fsr & AT91C_MC_FRDY) {
+#endif
+			debug("DNLOAD_IDLE ");
+			dev->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
+#if 0
+		} else {
+			debug("DNBUSY ");
+			dev->dfu_state = DFU_STATE_dfuDNBUSY;
+		}
+#endif
+		break;
+	case DFU_STATE_dfuMANIFEST_SYNC:
+		break;
+	default:
+		//return;
+		break;
+	}
+
+	/* send status response */
+	dstat->bStatus = dev->dfu_status;
+	dstat->bState = dev->dfu_state;
+	dstat->iString = 0;
+	/* FIXME: set dstat->bwPollTimeout */
+	req->length = MIN(sizeof(*dstat), max);
+
+	debug("status : bStatus=0x%x bState=0x%x \n",dstat->bStatus, dstat->bState);
+
+	/* we don't need to explicitly send data here, will
+	 * be done by the original caller! */
+}
+
+static void handle_getstate(struct usb_gadget *gadget, int max)
+{
+        struct dfu_dev *dev = &l_dfudev;//get_gadget_data(gadget);
+	struct usb_request	*req = dev->req;
+
+        printf("getstate \n");
+	
+	if (!req->buf || req->length < sizeof(u_int8_t)) {
+		debug("invalid ctrl! ");
+		return;
+	}
+	
+	*(char *)(req->buf) = (char)dev->dfu_state & 0xff;
+	req->length = sizeof(u_int8_t);
+}
+
+/*
+ * change our operational config.  must agree with the code
+ * that returns config descriptors, and altsetting code.
+ */
+static int dfu_set_config(struct dfu_dev *dev, unsigned number,
+				gfp_t gfp_flags)
+{
+	int			result = 0;
+	struct usb_gadget	*gadget = dev->gadget;
+	//struct usb_request	*req = dev->req;
+	
+	if (gadget_is_sa1100(gadget)
+			&& dev->config
+			&& dev->tx_qlen != 0) {
+		/* tx fifo is full, but we can't clear it...*/
+		error("can't change configurations");
+		return -ESPIPE;
+	}
+	
+	if (rtm_dfu == 0)
+	{
+	  printf("DFU set config : We should switch to DFU set !\n");
+	  //dfu_reset_config(dev);
+
+	  if (number > 0)
+	  {
+	    if (number <= DFU_NUM_ALTERNATES)
+	    {
+	        printf("DFU set config : %d\n", number);
+		result = set_dfu_config(dev, gfp_flags);
+	    }
+	    else result = -EINVAL;
+	  }
+	}
+	char *speed;
+	unsigned power;
+
+	power = 2 * dfu_cfg_descriptor.ucfg.bMaxPower;
+	usb_gadget_vbus_draw(gadget, power);
+
+	switch (gadget->speed) {
+		case USB_SPEED_FULL:
+			speed = "full"; break;
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+		case USB_SPEED_HIGH:
+			speed = "high"; break;
+#endif
+		default:
+			speed = "?"; break;
+		}
+
+	dev->config = number;
+	printf("%s speed config #%d: %d mA\n",
+		speed, number, power);
+
+	return result;
+}
+
+static void dfu_setup_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	debug("dfu_setup_complete\n");
+
+        if (req->status || req->actual != req->length)
+		printf("setup complete --> %d, %d/%d\n",
+				req->status, req->actual, req->length);
+}
+
+static void dfu_status_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct dfu_dev *dev 	= &l_dfudev;
+	struct usb_gadget	*gadget = dev->gadget;
+	const struct usb_ctrlrequest  *ctrl = dev->ctrl;
+	u16 	wValue 	= le16_to_cpu(ctrl->wValue);
+	u16 	wLength = le16_to_cpu(ctrl->wLength);
+	char ret = RET_NOTHING;
+	int	value = -EOPNOTSUPP;
+	
+	debug("dfu_status_complete , status = %d\n",dev->dfu_state);
+#ifdef CONFIG_GREEN_LED
+	if(ledcount <  127) green_LED_on();
+	else green_LED_off();
+	ledcount -= 5;
+#endif
+	switch (dev->dfu_state) {
+		case DFU_STATE_dfuIDLE:
+			debug("handle first download\n");
+			dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+			ret = handle_dnload(gadget, wValue, wLength, 1);
+			value = req->length;
+			break;
+		case DFU_STATE_dfuDNLOAD_IDLE: 
+		        debug("handle DFU_STATE_dfuDNLOAD_IDLE\n");
+			dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+			ret = handle_dnload(gadget, wValue, wLength, 0);
+			value = req->length;
+			break;	
+		case DFU_STATE_appIDLE: 		break;
+		case DFU_STATE_dfuDNLOAD_SYNC: 
+			debug("handle sequential download\n");
+			dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+			ret = handle_dnload(gadget, wValue, wLength, 0);
+			value = req->length;
+			break;	
+		case DFU_STATE_dfuDNBUSY: 		break;
+		
+		case DFU_STATE_dfuMANIFEST_SYNC: 	break;
+		case DFU_STATE_dfuMANIFEST: 		break;
+		case DFU_STATE_dfuMANIFEST_WAIT_RST: 	break;
+		case DFU_STATE_dfuUPLOAD_IDLE: 		break;
+		case DFU_STATE_appDETACH: 		break;
+		case DFU_STATE_dfuERROR:		break;	
+	}
+}
+
+int dfu_ep0_handler(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+	struct dfu_dev *dev = &l_dfudev;//get_gadget_data(gadget);
+	struct usb_request	*req = dev->req;
+	int rc, complete_status;
+	char ret = RET_NOTHING;
+	int	value = -EOPNOTSUPP;
+	u8 	requesttype = le16_to_cpu(ctrl->bRequestType);
+	u8 	request = le16_to_cpu(ctrl->bRequest);
+	u16 	wValue 	= le16_to_cpu(ctrl->wValue);
+	u16 	wLength = le16_to_cpu(ctrl->wLength);
+	u16 	wIndex  = le16_to_cpu(ctrl->wIndex);
+	
+	debug("dfu_ep0 (req=0x%x, bRequest=0x%x wVal=0x%x, wLen=%u) old_state = %u \n",
+		req, request, wValue, wLength, dev->dfu_state);
+	
+	/* Handle reset of the device */
+	if ( (requesttype == 0x41) && (request == 0x08) )
+	{
+	  printf("Device reset called \n");
+	  do_reset (NULL, 0, 0, NULL);
+	}
+	
+	dev->ctrl = ctrl;
+	complete_status = 0;
+	req->complete = dfu_setup_complete;
+	switch (request) 
+	{
+
+	case USB_REQ_GET_DESCRIPTOR:
+		if (ctrl->bRequestType != USB_DIR_IN)
+			break;
+		
+		switch (wValue >> 8) {
+
+		case USB_DT_DEVICE:
+		        debug("USB_DT_DEVICE\n");
+			value = min(wLength, (u16) sizeof dfu_dev_descriptor);
+			memcpy(req->buf, &dfu_dev_descriptor, value);
+			break;
+		case USB_DT_DEVICE_QUALIFIER:
+			if (!gadget_is_dualspeed(gadget))
+				break;
+			debug("USB_DT_DEVICE_QUALIFIER not supported\n");
+			value = min(wLength, (u16) sizeof dev_qualifier);
+			memcpy(req->buf, &dev_qualifier, value);
+			break;
+
+		case USB_DT_OTHER_SPEED_CONFIG:
+		        debug("USB_DT_OTHER_SPEED_CONFIG\n");
+			if (!gadget_is_dualspeed(gadget))
+				break;
+			/* FALLTHROUGH */
+		case USB_DT_CONFIG:
+		        debug("USB_DT_CONFIG\n");
+			rtm_dfu = 1;
+		        value = config_buf(gadget, req->buf,
+					wValue >> 8,
+					wValue & 0xff);
+			if (value >= 0)
+				value = min(wLength, (u16) value);
+			break;
+
+		case USB_DT_STRING:
+		        debug("USB_DT_STRING\n");
+			value = usb_gadget_get_string(&stringtab,
+					wValue & 0xff, req->buf);
+			
+			if (value >= 0)
+				value = min(wLength, (u16) value);
+			break;
+		
+		case USB_DT_INTERFACE:
+		        debug("USB_DT_INTERFACE is not needed \n");
+		        break;
+		}
+		break;
+
+	case USB_REQ_SET_CONFIGURATION:
+	  debug("USB_REQ_SET_CONFIGURATION %d\n", wValue);
+		if( wValue >= 1)
+			value = dfu_set_config(dev, wValue, GFP_ATOMIC);
+		if (req->status != -ECONNRESET ) dev->dfu_started = 1;
+		dev->dfu_state = DFU_STATE_dfuIDLE;
+		break;
+	case USB_REQ_GET_CONFIGURATION:
+	  debug("USB_REQ_GET_CONFIGURATION %d\n", wValue);
+		if (ctrl->bRequestType != USB_DIR_IN)
+			break;
+		*(u8 *)req->buf = dev->config;
+		value = min(wLength, (u16) 1);
+		break;
+
+	case USB_REQ_SET_INTERFACE: 
+		/*printf("USB_REQ_SET_INTERFACE %d , %d\n", wValue, wIndex);
+		if (ctrl->bRequestType != USB_RECIP_INTERFACE
+				|| !dev->config
+				|| wIndex > 1)
+			break;*/
+		
+		dev->interface = le16_to_cpu (wIndex);
+		dev->alternate = le16_to_cpu (wValue);
+		
+		debug("SET_INTERFACE(%u,%u) old_state = %u \n",
+			dev->interface, dev->alternate,
+			dev->dfu_state);
+		req->complete = dfu_setup_complete;
+		value = 0;
+		break;
+		
+	case USB_REQ_GET_INTERFACE:
+	        debug("USB_REQ_GET_INTERFACE\n");
+		if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)
+				|| !dev->config
+				|| wIndex > 1)
+			break;
+		if (wIndex != 0)
+			break;
+
+		/* for CDC, iff carrier is on, data interface is active. */
+		if (wIndex != 1)
+			*(u8 *)req->buf = 0;
+		else {
+			/* *(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0; */
+			/* carrier always ok ...*/
+			*(u8 *)req->buf = 1 ;
+		}
+		value = min(wLength, (u16) 1);
+		break;
+
+	default:
+	       debug("DFU State = %d\n", dev->dfu_state);
+	        
+	switch (dev->dfu_state) {
+	
+	case DFU_STATE_appIDLE:
+		switch (request) {
+		case USB_REQ_DFU_GETSTATUS:
+			handle_getstatus(gadget, wLength);
+			value = req->length;
+			break;
+		case USB_REQ_DFU_GETSTATE:
+			handle_getstate(gadget, wLength);
+			value = req->length;
+			break;
+		case USB_REQ_DFU_DETACH:
+			dev->dfu_state = DFU_STATE_appDETACH;
+			ret = RET_ZLP;
+			goto out;
+			break;
+		default:
+			ret = RET_STALL;
+			goto out;
+		}
+		break;
+	case DFU_STATE_appDETACH:
+		switch (request) {
+		case USB_REQ_DFU_GETSTATUS:
+			handle_getstatus(gadget, wLength);
+			value = req->length;
+			break;
+		case USB_REQ_DFU_GETSTATE:
+			handle_getstate(gadget, wLength);
+			value = req->length;
+			break;
+		default:
+			dev->dfu_state = DFU_STATE_appIDLE;
+			ret = RET_STALL;
+			goto out;
+			break;
+		}
+		/* FIXME: implement timer to return to appIDLE */
+		value = req->length;
+		break;
+	case DFU_STATE_dfuIDLE:
+		switch (request) {
+		case USB_REQ_DFU_DNLOAD:
+			if (wLength == 0) {
+			        printf("first packet can not be zero length\n");
+				dev->dfu_state = DFU_STATE_dfuERROR;
+				ret = RET_STALL;
+				goto out;
+			}
+			//dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+			debug("idle DNL : length %x, wValue %x\n", wLength,wValue );
+			req->length = wLength;
+			complete_status =1;
+			//ret = handle_dnload(gadget, wValue, wLength, 1);
+			//value = req->length;
+			break;
+		case USB_REQ_DFU_UPLOAD:
+			dev->dfu_state = DFU_STATE_dfuUPLOAD_IDLE;
+			handle_upload(gadget, wValue, wLength, 1);
+			value = req->length;
+			break;
+		case USB_REQ_DFU_ABORT:
+			/* no zlp? */
+			ret = RET_ZLP;
+			goto out;
+			break;
+		case USB_REQ_DFU_GETSTATUS:
+			handle_getstatus(gadget, wLength);
+			value = req->length;
+			break;
+		case USB_REQ_DFU_GETSTATE:
+			handle_getstate(gadget, wLength);
+			value = req->length;
+			break;
+		case USB_REQ_DFU_DETACH:
+			/* Proprietary extension: 'detach' from idle mode and
+			 * get back to runtime mode in case of USB Reset.  As
+			 * much as I dislike this, we just can't use every USB
+			 * bus reset to switch back to runtime mode, since at
+			 * least the Linux USB stack likes to send a number of resets
+			 * in a row :( */
+			dev->dfu_state = DFU_STATE_dfuMANIFEST_WAIT_RST;
+			value = 0;
+			break;
+		default:
+			dev->dfu_state = DFU_STATE_dfuERROR;
+			ret = RET_STALL;
+			goto out;
+			break;
+		}
+		debug("DFU packet length = %d bRequest=0x%x\n",req->length, ctrl->bRequest);
+		value = req->length;
+		break;
+	case DFU_STATE_dfuDNLOAD_SYNC:
+		switch (request) {
+		case USB_REQ_DFU_GETSTATUS:
+			handle_getstatus(gadget, wLength);
+			/* FIXME: state transition depending on block completeness */
+			value = req->length;
+			break;
+		case USB_REQ_DFU_GETSTATE:
+			handle_getstate(gadget, wLength);
+			value = req->length;
+			break;
+		default:
+			dev->dfu_state = DFU_STATE_dfuERROR;
+			ret = RET_STALL;
+			goto out;
+		}
+		break;
+	case DFU_STATE_dfuDNBUSY:
+		switch (request) {
+		case USB_REQ_DFU_GETSTATUS:
+			/* FIXME: only accept getstatus if bwPollTimeout
+			 * has elapsed */
+			handle_getstatus(gadget, wLength);
+			value = req->length;
+			break;
+		default:
+			dev->dfu_state = DFU_STATE_dfuERROR;
+			ret = RET_STALL;
+			goto out;
+		}
+		break;
+	case DFU_STATE_dfuDNLOAD_IDLE:
+		switch (request) {
+		case USB_REQ_DFU_DNLOAD:
+			if (wLength == 0) {
+				debug("We finished the download\n");
+			}
+			//dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+			debug("idle DNL 2: length %x, wValue %x , state = %x\n", wLength,wValue, dev->dfu_state );
+			req->length = wLength;
+			complete_status = 1;
+			break;
+		case USB_REQ_DFU_ABORT:
+			dev->dfu_state = DFU_STATE_dfuIDLE;
+			ret = RET_ZLP;
+			value = 0;
+			break;
+		case USB_REQ_DFU_GETSTATUS:
+			handle_getstatus(gadget, wLength);
+			value = req->length;
+			break;
+		case USB_REQ_DFU_GETSTATE:
+			handle_getstate(gadget, wLength);
+			value = req->length;
+			break;
+		default:
+			dev->dfu_state = DFU_STATE_dfuERROR;
+			ret = RET_STALL;
+			value = 0;
+			break;
+		}
+		debug("DFU packet length = %d bRequest=0x%x\n",req->length, ctrl->bRequest);
+		value = req->length;
+		break;
+	case DFU_STATE_dfuMANIFEST_SYNC:
+		switch (request) {
+		case USB_REQ_DFU_GETSTATUS:
+			/* We're MainfestationTolerant */
+			dev->dfu_state = DFU_STATE_dfuIDLE;
+			handle_getstatus(gadget, wLength);
+			value = req->length;
+			break;
+		case USB_REQ_DFU_GETSTATE:
+			handle_getstate(gadget, wLength);
+			break;
+		default:
+			dev->dfu_state = DFU_STATE_dfuERROR;
+			ret = RET_STALL;
+			value = 0;
+			break;
+		}
+		break;
+	case DFU_STATE_dfuMANIFEST:
+		/* we should never go here */
+		dev->dfu_state = DFU_STATE_dfuERROR;
+		ret = RET_STALL;
+		break;
+	case DFU_STATE_dfuMANIFEST_WAIT_RST:
+		/* we should never go here */
+		break;
+	case DFU_STATE_dfuUPLOAD_IDLE:
+		switch (request) {
+		case USB_REQ_DFU_UPLOAD:
+			/* state transition if less data then requested */
+			rc = handle_upload(gadget, wValue, wLength, 0);
+			if (rc >= 0 && rc < wLength)
+				dev->dfu_state = DFU_STATE_dfuIDLE;
+			value = req->length;
+			break;
+		case USB_REQ_DFU_ABORT:
+			dev->dfu_state = DFU_STATE_dfuIDLE;
+			/* no zlp? */
+			ret = RET_ZLP;
+			value = 0;
+			break;
+		case USB_REQ_DFU_GETSTATUS:
+			handle_getstatus(gadget, wLength);
+			value = req->length;
+			break;
+		case USB_REQ_DFU_GETSTATE:
+			handle_getstate(gadget, wLength);
+			value = req->length;
+			break;
+		default:
+			dev->dfu_state = DFU_STATE_dfuERROR;
+			ret = RET_STALL;
+			value = 0;
+			break;
+		}
+		break;
+	case DFU_STATE_dfuERROR:
+		switch (request) {
+		case USB_REQ_DFU_GETSTATUS:
+			handle_getstatus(gadget, wLength);
+			value = req->length;
+			break;
+		case USB_REQ_DFU_GETSTATE:
+			handle_getstate(gadget, wLength);
+			value = req->length;
+			break;
+		case USB_REQ_DFU_CLRSTATUS:
+			debug("Clear DFU status\n");
+			dev->dfu_state = DFU_STATE_dfuIDLE;
+			dev->dfu_status = DFU_STATUS_OK;
+			/* no zlp? */
+			ret = RET_ZLP;
+			value = 0;
+			break;
+		default:
+			dev->dfu_state = DFU_STATE_dfuERROR;
+			ret = RET_STALL;
+			value = 0;
+			break;
+		}
+		break;
+	default:
+		printf("unknown control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			wValue, wIndex, wLength);
+	        return DFU_EP0_UNHANDLED;
+		break;
+	}
+
+		
+	}
+	/* respond with data transfer before status phase? */
+	if (value >= 0) {
+		debug("respond with data transfer before status phase\n");
+		req->length = value;
+		req->zero = value < wLength
+				&& (value % gadget->ep0->maxpacket) == 0;
+		value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+		debug("ep_queue %d req%02x.%02x v%04x i%04x l%d\n",
+			req->length, ctrl->bRequestType, ctrl->bRequest,
+			wValue, wIndex, wLength);
+		if (value < 0) {
+			debug("ep_queue --> %d\n", value);
+			req->status = 0;
+			dfu_setup_complete(gadget->ep0, req);
+		}
+	}
+	
+	if(complete_status) req->complete = dfu_status_complete;
+	
+        return DFU_EP0_DATA;
+
+out:
+	debug("new_state = %u, ret = %u\n", dev->dfu_state, ret);
+
+	/*FIXME the following code needs a review */
+	switch (ret) {
+	case RET_ZLP:
+	        debug("ZERO LENGTH packet\n");
+		req->length = 0;
+		req->zero = value < wLength
+				&& (value % gadget->ep0->maxpacket) == 0;
+		value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+		//usb_gadget_handle_interrupts();
+		req->complete = dfu_status_complete;
+		return DFU_EP0_ZLP;
+		break;
+	case RET_STALL:
+		 req->complete = dfu_status_complete;
+		return DFU_EP0_STALL;
+		break;
+	case RET_NOTHING:
+		break;
+	}
+	return DFU_EP0_DATA;
+}
+
+/*
+void str2wide (char *str, u16 * wide);
+static struct usb_string_descriptor *create_usbstring(char *string)
+{
+	struct usb_string_descriptor *strdesc;
+	int size = sizeof(*strdesc) + strlen(string)*2;
+
+	if (size > 255)
+		return NULL;
+
+	strdesc = malloc(size);
+	if (!strdesc)
+		return NULL;
+
+	strdesc->bLength = size;
+	strdesc->bDescriptorType = USB_DT_STRING;
+	str2wide(string, strdesc->wData);
+
+	return strdesc;
+}
+*/
+#ifdef CONFIG_NAND_DYNPART
+
+void dfu_update_strings(void)
+{
+	int i;
+
+	if (!system_dfu_state) {
+		printf("NASTY SURPRISE: system_dfu_state not set\n");
+		return;
+	}
+
+	for (i = 1; i != DFU_NUM_ALTERNATES; i++) {
+		struct part_info *part = get_partition_nand(i-1);
+		struct usb_string_descriptor *strdesc, **slot;
+
+		if (part)
+			strdesc = create_usbstring(part->name);
+		else
+			strdesc = create_usbstring("undefined partition");
+		if (!strdesc)
+			continue;
+		slot = usb_strings+STR_COUNT+i+1;
+		if (*slot)
+			free(*slot);
+		*slot = strdesc;
+	}
+}
+
+#endif /* CONFIG_NAND_DYNPART */
+
+
+static void dfu_unbind(struct usb_gadget *gadget)
+{
+	struct dfu_dev *dev = get_gadget_data(gadget);
+
+	debug("%s...\n", __func__);
+
+	/* we've already been disconnected ... no i/o is active */
+	if (dev->req) {
+		usb_ep_free_request(gadget->ep0, dev->req);
+		dev->req = NULL;
+	}
+
+	dev->gadget = NULL;
+	set_gadget_data(gadget, NULL);
+}
+
+static int dfu_bind(struct usb_gadget *gadget)
+{
+	struct dfu_dev		*dev = &l_dfudev;
+	u8			zlp = 1; //cdc = 1, ;
+	int			gcnum;
+	//u8			tmp[7];
+	debug("DFU bind\n");
+	/*
+	 * Because most host side USB stacks handle CDC Ethernet, that
+	 * standard protocol is _strongly_ preferred for interop purposes.
+	 * (By everyone except Microsoft.)
+	 */
+	if (gadget_is_musbhdrc(gadget)) {
+		/* reduce tx dma overhead by avoiding special cases */
+		zlp = 0;
+	} else if (gadget_is_sa1100(gadget)) {
+		/* hardware can't write zlps */
+		zlp = 0;
+	}
+
+        gcnum = usb_gadget_controller_number(gadget);
+	if (gcnum >= 0)
+		dfu_dev_descriptor.bcdDevice = cpu_to_le16(0x0300 + gcnum);
+	else {
+		/*
+		 * can't assume CDC works.  don't want to default to
+		 * anything less functional on CDC-capable hardware,
+		 * so we fail in this case.
+		 */
+		printf("ERROR : controller not recognised\n");
+		error("controller '%s' not recognized",
+			gadget->name);
+		return -ENODEV;
+	}
+
+#if defined(CONFIG_USBD_VENDORID) && defined(CONFIG_USBD_PRODUCTID_DFU)
+	dfu_dev_descriptor.idVendor  = cpu_to_le16(CONFIG_USBD_VENDORID);
+	dfu_dev_descriptor.idProduct = cpu_to_le16(CONFIG_USBD_PRODUCTID_DFU);
+#endif	
+
+	if (bcdDevice)
+		dfu_dev_descriptor.bcdDevice = cpu_to_le16(bcdDevice);
+	if (iManufacturer)
+		strlcpy(manufacturer, iManufacturer, sizeof manufacturer);
+	if (iProduct)
+		strlcpy(product_desc, iProduct, sizeof product_desc);
+	if (iSerialNumber) {
+		dfu_dev_descriptor.iSerialNumber = DFU_STR_SERIAL,
+		strlcpy(serial_number, iSerialNumber, sizeof serial_number);
+	}
+
+	/* all we really need is Ep0 */
+	
+	usb_ep_autoconfig_reset(gadget);
+
+	dfu_dev_descriptor.bMaxPacketSize0 = gadget->ep0->maxpacket;
+	usb_gadget_set_selfpowered(gadget);
+
+	if (gadget_is_dualspeed(gadget)) {
+
+	        //dev_qualifier.bDeviceClass = USB_CLASS_VENDOR_SPEC;
+
+		/* assumes ep0 uses the same value for both speeds ... */
+		//dev_qualifier.bMaxPacketSize0 = dfu_dev_descriptor.bMaxPacketSize0;
+	}
+
+	/* preallocate control message data and buffer */
+	dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+	if (!dev->req)
+		goto fail;
+	dev->req->buf = control_req;
+	dev->req->complete = dfu_setup_complete;
+
+	/* finish hookup to lower layer ... */
+	dev->gadget = gadget;
+	set_gadget_data(gadget, dev);
+	gadget->ep0->driver_data = dev;
+
+	return 0;
+
+fail:
+	error("%s failed", __func__);
+	dfu_unbind(gadget);
+	return -ENOMEM;
+}
+
+int usb_dfu_init(void)
+{
+	struct dfu_dev *dev = &l_dfudev;
+	struct usb_gadget *gadget;
+	unsigned long ts;
+	unsigned long timeout = USB_CONNECT_TIMEOUT;
+	int ret;
+	int i;
+	
+	/* set up interface descriptors fro each partition */
+	/* this must be done better */
+	for (i = 0; i < DFU_NUM_ALTERNATES; i++) {
+		struct usb_interface_descriptor *uif =
+		    dfu_cfg_descriptor.uif+i;
+
+		uif->bLength		= USB_DT_INTERFACE_SIZE;
+		uif->bDescriptorType	= USB_DT_INTERFACE;
+		uif->bAlternateSetting	= i;
+		uif->bInterfaceClass	= 0xfe;
+		uif->bInterfaceSubClass	= 1;
+		uif->bInterfaceProtocol	= 2;
+		uif->iInterface		= DFU_STR_ALT(i);
+	}
+	
+	dev->dfu_started = 0;
+	dev->dfu_state = DFU_STATE_appIDLE;
+	dev->dfu_status = DFU_STATUS_OK;
+	
+	if (system_dfu_state)
+		printf("SURPRISE: system_dfu_state is already set\n");
+	system_dfu_state = &dev->dfu_state;
+	
+	ret = usb_gadget_register_driver(&dfu_driver);
+	if (ret < 0)
+	{
+		if (ret == -EBUSY); /*driver already registered */
+		else
+		{
+		  error("USB gadget driver failed to register\n");
+		  goto fail;
+		}
+	}
+	
+	gadget = dev->gadget;
+	usb_gadget_connect(gadget);
+
+	if (getenv("dfu_connect_timeout"))
+		timeout = simple_strtoul(getenv("dfu_connect_timeout"),
+						NULL, 10) * CONFIG_SYS_HZ;
+	ts = get_timer(0);
+	debug("trying to connect\n");
+	while (!dev->dfu_started) {
+		/* Handle control-c and timeouts */
+		if (ctrlc() || (get_timer(ts) > timeout)) {
+			error("The remote end did not respond in time.");
+			goto fail;
+		}
+		usb_gadget_handle_interrupts();
+	}
+#ifdef CONFIG_GREEN_LED
+	green_LED_on();
+	ledcount = 100;
+#endif
+#ifdef CONFIG_RED_LED
+	red_LED_on();
+#endif
+
+	return 0;
+fail:
+        debug("usb_dfu_init failed\n");
+	system_dfu_state = NULL;
+#ifdef CONFIG_GREEN_LED
+	green_LED_off();
+	ledcount = 100;
+#endif
+#ifdef CONFIG_RED_LED
+	red_LED_off();
+#endif
+	return -1;
+}
+
+/*void usb_dfu_halt(struct dfu_device *dfudev)
+{
+	struct dfu_dev *dev = &l_dfudev;
+
+	debug("usb_dfu_halt\n");
+	
+	if (!dfudev) {
+		error("received NULL ptr");
+		return;
+	}
+
+	// If the gadget not registered, simple return 
+	if (!dev->gadget)
+		return;
+
+	usb_gadget_disconnect(dev->gadget);
+	usb_gadget_unregister_driver(&dfu_driver);
+}
+*/
+static void dfu_suspend(struct usb_gadget *gadget)
+{
+	/* Not used */
+}
+
+static void dfu_resume(struct usb_gadget *gadget)
+{
+	/* Not used */
+}
+
+/* we need to bind/unbind and have ep0 */
+static struct usb_gadget_driver dfu_driver = {
+	.speed		= DEVSPEED,
+
+	.bind		= dfu_bind,
+	.unbind		= dfu_unbind,
+
+	.setup		= dfu_ep0_handler,
+	//.disconnect	= dfu_disconnect,
+
+	.suspend	= dfu_suspend,
+	.resume	        = dfu_resume,
+};
+
+U_BOOT_CMD(
+	dfuinit,	1,	0,	usb_dfu_init,
+	"DFU initialization",
+	"No params, see README.dfu"
+);
+
+
+#endif /* CONFIG_USBD_DFU */