From: Marcel <korg...@home.nl>

Signed-off-by: Marcel <korg...@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 --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 <r...@semihalf.com>
+ *             Bartlomiej Sieka <t...@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.jans...@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 <lafo...@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 */
-- 
1.7.3.4

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to