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

USB DFU driver cleaning phase1

USB DFU driver cleaning phase2

USB DFU driver cleaning phase3

USB DFU driver cleaning phase4

Signed-off-by: Marcel <korg...@home.nl>
---
 common/update_dfu.c           |   89 +++
 doc/README.dfu                |  129 ++++
 drivers/usb/gadget/Makefile   |    9 +
 drivers/usb/gadget/usbdfu.c   | 1336 +++++++++++++++++++++++++++++++++++++++++
 include/usb_dfu.h             |  128 ++++
 include/usb_dfu_descriptors.h |  100 +++
 6 files changed, 1791 insertions(+), 0 deletions(-)
 create mode 100644 common/update_dfu.c
 create mode 100644 doc/README.dfu
 create mode 100644 drivers/usb/gadget/usbdfu.c
 create mode 100644 include/usb_dfu.h
 create mode 100644 include/usb_dfu_descriptors.h

diff --git a/common/update_dfu.c b/common/update_dfu.c
new file mode 100644
index 0000000..f1ceccf
--- /dev/null
+++ b/common/update_dfu.c
@@ -0,0 +1,89 @@
+/*
+ * (C) Copyright 2011 Marcel Janssen, Admesy B.V.
+ *
+ * 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 <usb_dfu.h>
+
+#include <command.h>
+#ifdef CONFIG_USB_GADGET_ATMEL_USBA
+#include <usb/atmel_usba_udc.h>
+#endif
+#ifdef CONFIG_USB_GADGET_AT91
+#include <usb/at91_udc.h>
+#endif
+#include <asm/arch/gpio.h>
+
+#ifdef CONFIG_USB_GADGET_ATMEL_USBA
+struct platform_data dfubrd = {
+       .board = {
+               .vbus_pin   = AT91_PIN_PC0,
+               .pullup_pin = 1,
+       },
+       .udc_clk = AT91SAM9G45_ID_UDPHS,
+};
+#endif
+
+int dfu_loop(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+       int rcv;
+
+       dfu_finished = 0;
+
+       /* initialize the USBD controller */
+#ifdef CONFIG_USB_GADGET_ATMEL_USBA
+       usba_udc_probe(&dfubrd);
+       udelay(100000);
+#endif
+#ifdef CONFIG_USB_GADGET_AT91
+       at91udc_probe(&dfubrd);
+       udelay(100000);
+#endif
+       if (usb_dfu_init() == 0) {
+               while (1) {
+                       /* 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,    CONFIG_SYS_MAXARGS,     1,      dfu_loop,
+       "Start DFU function",
+       "No params, see README.dfu"
+);
diff --git a/doc/README.dfu b/doc/README.dfu
new file mode 100644
index 0000000..363c7a2
--- /dev/null
+++ b/doc/README.dfu
@@ -0,0 +1,129 @@
+USBD DFU mode
+
+Initially written by Marcel Janssen, Admesy B.V.
+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" (common/update_dfu.c).
+This command should start the USB device controller and the DFU driver.
+A typical implementation would be that a script is executed, that will check
+whether DFU should be started. If so, it can execute '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 icnova_sam9g45 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
+========================================
+
+
+========================================
+DFU CONFIG options
+----------------------------------------
+
+#define CONFIG_USBD_DFU                 1
+#ifdef  CONFIG_USBD_DFU
+#define CONFIG_USBD_VENDORID           0x23CF     /* Admesy */
+#define CONFIG_USBD_PRODUCTID_DFU       0x0100     /* donated number */
+#define CONFIG_USBD_MANUFACTURER       "Admesy"
+#define CONFIG_USBD_PRODUCT_NAME       "Admesy DFU 001"
+#define CONFIG_USBD_DFU_XFER_SIZE      4096       /* Buffer size */
+#define CONFIG_USBD_DFU_INTERFACE       0
+#define DFU_NUM_ALTERNATES             3          /* 3 partitions */
+#define LOAD_ADDR ((unsigned char *)0x70400000)    /* RAM address to use */
+#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..d1ba030 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -30,6 +30,14 @@ ifdef CONFIG_USB_ETHER
 COBJS-y += ether.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
+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
@@ -42,6 +50,7 @@ COBJS-$(CONFIG_PXA27X) += pxa27x_udc.o
 COBJS-$(CONFIG_SPEARUDC) += spr_udc.o
 endif
 endif
+endif
 
 COBJS  := $(COBJS-y)
 SRCS   := $(COBJS:.o=.c)
diff --git a/drivers/usb/gadget/usbdfu.c b/drivers/usb/gadget/usbdfu.c
new file mode 100644
index 0000000..65f334a
--- /dev/null
+++ b/drivers/usb/gadget/usbdfu.c
@@ -0,0 +1,1336 @@
+/*
+ * (C) 2011 Marcel Janssen, Admesy B.V.
+ * (C) 2007 by OpenMoko, Inc.
+ * Author: Harald Welte <lafo...@openmoko.org>
+ *
+ * based on existing SAM7DFU code from OpenPCD:
+ * (C) Copyright 2006 by Harald Welte <hwe...@hmw-consulting.de>
+ *
+ * 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
+ *
+ * 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 <linux/ctype.h>
+#include <linux/porting-compat.h>
+
+#include <nand.h>
+#include <jffs2/load_kernel.h>
+
+extern struct list_head devices;
+
+#define RET_NOTHING    0
+#define RET_ZLP                1
+#define RET_STALL      2
+
+#ifndef CONFIG_USBD_DFU_XFER_SIZE
+#define CONFIG_USBD_DFU_XFER_SIZE      4096
+#endif
+
+/* this should be done differently */
+#define EP0_MAX_PACKET_SIZE    64 /* MUSB_EP0_FIFOSIZE */
+
+unsigned char ledcount;
+
+#define GFP_ATOMIC ((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)
+enum dfu_state *system_dfu_state; /* for 3rd parties */
+
+char rtm_dfu;
+
+/*
+ * 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"
+/* usually only one device should be in DFU mode, this may be fixed */
+#define FIXED_SERIAL           "1234567"
+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;
+
+#ifndef CONFIG_USBD_PRODUCTID_DFU
+#define #error YOU need to define a USBD product ID for DFU
+#endif
+
+#define LOAD_ADDR ((unsigned char *)0x70400000)
+
+static struct dfu_dev l_dfudev;
+static struct usb_gadget_driver dfu_driver;
+static u8 control_req[CONFIG_USBD_DFU_XFER_SIZE];
+
+
+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,
+               .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 size (excl. bad blocks) */
+       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 on 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;
+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, CONFIG_USBD_DFU_XFER_SIZE,  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\n");
+               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 */
+               char *mtdp = getenv("mtdparts");
+               if (mtdp)
+                       printf("Valid MTD partitions found\n");
+               /*this used to be in the Openmoko driver */
+               /*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
+                       */
+               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; /* do not allow this */
+                       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;
+
+               }
+               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) {
+                       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;
+
+       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;
+               /*FIXME : maybe we need this */
+               /* 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;
+       struct usb_request      *req = dev->req;
+       struct dfu_status *dstat     = (struct dfu_status *) req->buf;
+
+       switch (dev->dfu_state) {
+       case DFU_STATE_dfuDNLOAD_SYNC:
+       case DFU_STATE_dfuDNBUSY:
+                       debug("DNLOAD_IDLE ");
+                       dev->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
+               break;
+       case DFU_STATE_dfuMANIFEST_SYNC:
+               break;
+       default:
+               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;
+       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;
+
+       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) {
+               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;
+       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)\n",
+               req, request, wValue, wLength);
+
+       /* Handle vendor specific code
+        * this is an example how to reset u-boot by sending a
+        * control message. This can be useful in case
+        * the USB reset fails for whatever reason.
+       */
+       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");
+                       rtm_dfu = 0;
+                       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:
+               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;
+
+               if (wIndex != 1)
+                       *(u8 *)req->buf = 0;
+               else
+                       *(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;
+                       }
+                       debug("idle DNL : length %x, wValue %x\n",
+                               wLength, wValue);
+                       req->length = wLength;
+                       complete_status = 1;
+                       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");
+                       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);
+               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;
+}
+
+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;
+       int                     gcnum;
+       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);
+
+       /* 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 */
+                       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;
+}
+
+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,
+       .suspend        = dfu_suspend,
+       .resume         = dfu_resume,
+};
+
+
+#endif /* CONFIG_USBD_DFU */
diff --git a/include/usb_dfu.h b/include/usb_dfu.h
new file mode 100644
index 0000000..e232a7e
--- /dev/null
+++ b/include/usb_dfu.h
@@ -0,0 +1,128 @@
+#ifndef _DFU_H
+#define _DFU_H
+
+/* USB Device Firmware Update Implementation for u-boot
+ * (C) 2011 Marcel Janssen , Admesy B.V.
+ * (C) 2007 by OpenMoko, Inc.
+ * Author: Harald Welte <lafo...@openmoko.org>
+ *
+ * based on: USB Device Firmware Update Implementation for OpenPCD
+ * (C) 2006 by Harald Welte <hwe...@hmw-consulting.de>
+ *
+ * This ought to be compliant to the USB DFU Spec 1.0 as available from
+ * http://www.usb.org/developers/devclass_docs/usbdfu10.pdf
+ *
+ *  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 <asm/types.h>
+#include <linux/usb/ch9.h>
+#include <usb_dfu_descriptors.h>
+#include <config.h>
+
+#define USB_DFU_SUBCLASS        0x01
+
+/* USB DFU functional descriptor */
+#define DFU_FUNC_DESC  {                                               \
+       .bLength                = USB_DT_DFU_SIZE,                      \
+       .bDescriptorType        = USB_DT_DFU,                           \
+       .bmAttributes           = USB_DFU_CAN_UPLOAD                    \
+                       | USB_DFU_CAN_DOWNLOAD | USB_DFU_MANIFEST_TOL,  \
+       .wDetachTimeOut         = 0xff00,                               \
+       .wTransferSize          = CONFIG_USBD_DFU_XFER_SIZE,            \
+       .bcdDFUVersion          = 0x0100,                               \
+}
+
+/* USB Interface descriptor in Runtime mode */
+#define DFU_RT_IF_DESC {                                               \
+       .bLength                = USB_DT_INTERFACE_SIZE,                \
+       .bDescriptorType        = USB_DT_INTERFACE,                     \
+       .bInterfaceNumber       = CONFIG_USBD_DFU_INTERFACE,            \
+       .bAlternateSetting      = 0x00,                                 \
+       .bNumEndpoints          = 0x00,                                 \
+       .bInterfaceClass        = 0xfe,                                 \
+       .bInterfaceSubClass     = 0x01,                                 \
+       .bInterfaceProtocol     = 0x00,                                 \
+       .iInterface             = DFU_STR_CONFIG,                       \
+}
+
+#define ARRAY_SIZE(x)           (sizeof(x) / sizeof((x)[0]))
+
+#ifndef DFU_NUM_ALTERNATES
+#define DFU_NUM_ALTERNATES     3
+#endif
+
+#define STR_MANUFACTURER       0x01
+#define STR_PRODUCT            0x02
+#define STR_SERIAL             0x03
+#define STR_COUNT              0x04
+#define DFU_STR_MANUFACTURER   STR_MANUFACTURER
+#define DFU_STR_PRODUCT                STR_PRODUCT
+#define DFU_STR_SERIAL         STR_SERIAL
+#define DFU_STR_CONFIG         (STR_COUNT)
+#define DFU_STR_ALT(n)         (STR_COUNT+(n)+1)
+#define DFU_STR_COUNT          DFU_STR_ALT(DFU_NUM_ALTERNATES)
+/* needed for string list, it works with constants !! */
+#define DFU_STR_ALT1            (STR_COUNT+1)
+#define DFU_STR_ALT2            (STR_COUNT+2)
+#define DFU_STR_ALT3            (STR_COUNT+3)
+#define DFU_STR_ALT4            (STR_COUNT+4)
+#define DFU_STR_ALT5            (STR_COUNT+5)
+#define DFU_STR_ALT6            (STR_COUNT+6)
+
+#define CONFIG_DFU_CFG_STR     "USB Device Firmware Upgrade"
+#define CONFIG_DFU_ALT0_STR    "RAM 0x32000000"
+
+struct _dfu_desc {
+       struct usb_config_descriptor ucfg;
+       struct usb_interface_descriptor uif[DFU_NUM_ALTERNATES];
+       struct usb_dfu_func_descriptor func_dfu;
+};
+
+struct dfu_dev {
+  u8                                   alternate;
+  u8                                   interface;
+  enum         dfu_state               dfu_state;
+  u8                                   dfu_status;
+  struct       usb_request             *req;
+  const struct usb_ctrlrequest         *ctrl; /* last control request */
+
+  struct usb_gadget    *gadget;
+
+  unsigned             zlp:1;
+  u8                   config;
+  struct usb_request   *tx_req, *rx_req;
+
+  unsigned int         tx_qlen;
+  unsigned             dfu_started:1;
+
+  unsigned long                todo;
+
+};
+
+#define DFU_EP0_NONE           0
+#define DFU_EP0_UNHANDLED      1
+#define DFU_EP0_STALL          2
+#define DFU_EP0_ZLP            3
+#define DFU_EP0_DATA           4
+
+extern enum dfu_state *system_dfu_state; /* for 3rd parties */
+
+int dfu_ep0_handler(struct usb_gadget *gadget,
+                   const struct usb_ctrlrequest *ctrl);
+int usb_dfu_init(void);
+
+
+#endif /* _DFU_H */
diff --git a/include/usb_dfu_descriptors.h b/include/usb_dfu_descriptors.h
new file mode 100644
index 0000000..ddcafc1
--- /dev/null
+++ b/include/usb_dfu_descriptors.h
@@ -0,0 +1,100 @@
+#ifndef _USB_DFU_H
+#define _USB_DFU_H
+/* USB Device Firmware Update Implementation for OpenPCD
+ * (C) 2006 by Harald Welte <hwe...@hmw-consulting.de>
+ *
+ * Protocol definitions for USB DFU
+ *
+ * This ought to be compliant to the USB DFU Spec 1.0 as available from
+ * http://www.usb.org/developers/devclass_docs/usbdfu10.pdf
+ *
+ *  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 <linux/types.h>
+
+#define USB_DT_DFU                     0x21
+
+struct usb_dfu_func_descriptor {
+       u_int8_t                bLength;
+       u_int8_t                bDescriptorType;
+       u_int8_t                bmAttributes;
+/* the following attributes correspond to table 4.1 of the DFU spec */
+#define USB_DFU_CAN_DOWNLOAD   (1 << 0)
+#define USB_DFU_CAN_UPLOAD     (1 << 1)
+/* u-boot doesn't need the DFU driver to write to NAND
+ * the default driver sets this to 0, the original OPENMOKO driver
+ * had this defined. The dfu-utils don't check for it, which is wrong
+ * and should be changed
+*/
+#define USB_DFU_MANIFEST_TOL   (0 << 2)
+#define USB_DFU_WILL_DETACH    (1 << 3)
+       u_int16_t               wDetachTimeOut;
+       u_int16_t               wTransferSize;
+       u_int16_t               bcdDFUVersion;
+} __attribute__ ((packed));
+
+#define USB_DT_DFU_SIZE                        9
+
+#define USB_TYPE_DFU           (USB_TYPE_CLASS|USB_RECIP_INTERFACE)
+
+/* DFU class-specific requests (Section 3, DFU Rev 1.1) */
+#define USB_REQ_DFU_DETACH     0x00
+#define USB_REQ_DFU_DNLOAD     0x01
+#define USB_REQ_DFU_UPLOAD     0x02
+#define USB_REQ_DFU_GETSTATUS  0x03
+#define USB_REQ_DFU_CLRSTATUS  0x04
+#define USB_REQ_DFU_GETSTATE   0x05
+#define USB_REQ_DFU_ABORT      0x06
+
+struct dfu_status {
+       u_int8_t bStatus;
+       u_int8_t bwPollTimeout[3];
+       u_int8_t bState;
+       u_int8_t iString;
+} __attribute__((packed));
+
+#define DFU_STATUS_OK                  0x00
+#define DFU_STATUS_errTARGET           0x01
+#define DFU_STATUS_errFILE             0x02
+#define DFU_STATUS_errWRITE            0x03
+#define DFU_STATUS_errERASE            0x04
+#define DFU_STATUS_errCHECK_ERASED     0x05
+#define DFU_STATUS_errPROG             0x06
+#define DFU_STATUS_errVERIFY           0x07
+#define DFU_STATUS_errADDRESS          0x08
+#define DFU_STATUS_errNOTDONE          0x09
+#define DFU_STATUS_errFIRMWARE         0x0a
+#define DFU_STATUS_errVENDOR           0x0b
+#define DFU_STATUS_errUSBR             0x0c
+#define DFU_STATUS_errPOR              0x0d
+#define DFU_STATUS_errUNKNOWN          0x0e
+#define DFU_STATUS_errSTALLEDPKT       0x0f
+
+enum dfu_state {
+       DFU_STATE_appIDLE               = 0,
+       DFU_STATE_appDETACH             = 1,
+       DFU_STATE_dfuIDLE               = 2,
+       DFU_STATE_dfuDNLOAD_SYNC        = 3,
+       DFU_STATE_dfuDNBUSY             = 4,
+       DFU_STATE_dfuDNLOAD_IDLE        = 5,
+       DFU_STATE_dfuMANIFEST_SYNC      = 6,
+       DFU_STATE_dfuMANIFEST           = 7,
+       DFU_STATE_dfuMANIFEST_WAIT_RST  = 8,
+       DFU_STATE_dfuUPLOAD_IDLE        = 9,
+       DFU_STATE_dfuERROR              = 10,
+};
+
+#endif /* _USB_DFU_H */
-- 
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