A vendor independent standard from the USB forum to upgrade firmware on
devices over USB. U-Boot is seen as the device to be upgraded here and
exposes the functionality over the USB gadget framework.

Signed-off-by: Stefan Schmidt <ste...@datenfreihafen.org>
---
 drivers/serial/usbtty.c        |   22 +-
 drivers/serial/usbtty.h        |    6 +
 drivers/usb/dfu/nand.c         |  386 +++++++++++++++++++++++++++++
 drivers/usb/gadget/Makefile    |    4 +-
 drivers/usb/gadget/core.c      |   19 ++
 drivers/usb/gadget/dfu.c       |   57 ++++-
 drivers/usb/gadget/dfu_old.c   |  521 ++++++++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/ep0.c       |   50 +++-
 include/configs/omap3_beagle.h |    3 +
 include/dfu_backend.h          |   16 ++
 include/usb_dfu.h              |   97 ++++++++
 include/usb_dfu_descriptors.h  |   94 ++++++++
 include/usbdevice.h            |   13 +
 13 files changed, 1274 insertions(+), 14 deletions(-)
 create mode 100644 drivers/usb/dfu/nand.c
 create mode 100644 drivers/usb/gadget/dfu_old.c
 create mode 100644 include/usb_dfu.h
 create mode 100644 include/usb_dfu_descriptors.h

diff --git a/drivers/serial/usbtty.c b/drivers/serial/usbtty.c
index cffd5a2..b13b497 100644
--- a/drivers/serial/usbtty.c
+++ b/drivers/serial/usbtty.c
@@ -22,6 +22,8 @@
  */
 
 #include <common.h>
+#include <usb_dfu_descriptors.h>
+#include <usb_dfu.h>
 #include <config.h>
 #include <circbuf.h>
 #include <stdio_dev.h>
@@ -101,7 +103,7 @@ extern struct usb_string_descriptor **usb_strings;
 static unsigned short rx_endpoint = 0;
 static unsigned short tx_endpoint = 0;
 static unsigned short interface_count = 0;
-static struct usb_string_descriptor *usbtty_string_table[STR_COUNT];
+static struct usb_string_descriptor *usbtty_string_table[NUM_STRINGS];
 
 /* USB Descriptor Strings */
 static u8 wstrLang[4] = {4,USB_DT_STRING,0x9,0x4};
@@ -151,6 +153,10 @@ struct acm_config_desc {
        /* Slave Interface */
        struct usb_interface_descriptor data_class_interface;
        struct usb_endpoint_descriptor data_endpoints[NUM_ENDPOINTS-1];
+#ifdef CONFIG_USBD_DFU
+       struct usb_interface_descriptor uif_dfu;
+       struct usb_dfu_func_descriptor func_dfu;
+#endif
 } __attribute__((packed));
 
 static struct acm_config_desc acm_configuration_descriptors[NUM_CONFIGS] = {
@@ -161,7 +167,11 @@ static struct acm_config_desc 
acm_configuration_descriptors[NUM_CONFIGS] = {
                        .bDescriptorType = USB_DT_CONFIG,
                        .wTotalLength =
                                cpu_to_le16(sizeof(struct acm_config_desc)),
+#ifdef CONFIG_USBD_DFU
+                       .bNumInterfaces = NUM_ACM_INTERFACES + 1,
+#else
                        .bNumInterfaces = NUM_ACM_INTERFACES,
+#endif
                        .bConfigurationValue = 1,
                        .iConfiguration = STR_CONFIG,
                        .bmAttributes =
@@ -260,6 +270,11 @@ static struct acm_config_desc 
acm_configuration_descriptors[NUM_CONFIGS] = {
                                .bInterval              = 0xFF,
                        },
                },
+#ifdef CONFIG_USBD_DFU
+               /* Interface 3 */
+               .uif_dfu = DFU_RT_IF_DESC,
+               .func_dfu = DFU_FUNC_DESC,
+#endif
        },
 };
 
@@ -370,7 +385,7 @@ static int fill_buffer (circbuf_t * buf);
 void usbtty_poll (void);
 
 /* utility function for converting char* to wide string used by USB */
-static void str2wide (char *str, u16 * wide)
+void str2wide (char *str, u16 * wide)
 {
        int i;
        for (i = 0; i < strlen (str) && str[i]; i++){
@@ -639,6 +654,9 @@ static void usbtty_init_instances (void)
        device_instance->bus = bus_instance;
        device_instance->configurations = NUM_CONFIGS;
        device_instance->configuration_instance_array = config_instance;
+#ifdef CONFIG_USBD_DFU
+       dfu_init_instance(device_instance);
+#endif
 
        /* initialize bus instance */
        memset (bus_instance, 0, sizeof (struct usb_bus_instance));
diff --git a/drivers/serial/usbtty.h b/drivers/serial/usbtty.h
index 14961c1..16ae86e 100644
--- a/drivers/serial/usbtty.h
+++ b/drivers/serial/usbtty.h
@@ -84,4 +84,10 @@
 #define STR_CTRL_INTERFACE     0x06
 #define STR_COUNT              0x07
 
+#ifdef CONFIG_USBD_DFU
+#define NUM_STRINGS            DFU_STR_COUNT
+#else
+#define NUM_STRINGS            STR_COUNT
+#endif
+
 #endif
diff --git a/drivers/usb/dfu/nand.c b/drivers/usb/dfu/nand.c
new file mode 100644
index 0000000..619ba0f
--- /dev/null
+++ b/drivers/usb/dfu/nand.c
@@ -0,0 +1,386 @@
+#include <common.h>
+#include <nand.h>
+#include <jffs2/load_kernel.h>
+#include <dfu_backend.h>
+#include <flash_entity.h>
+int mtdparts_init(void);
+extern struct list_head devices;
+
+};
+
+static struct flash_entity_ctx ctx;
+
+/* Return the 'net size' of the partition (i.e. excluding any bad blocks) */
+unsigned int nand_net_part_size(struct part_info *part)
+{
+       struct mtd_info *mtd;
+       unsigned int offs;
+       unsigned int bb_delta = 0;
+
+       if (!part || !part->dev || !part->dev->id ||
+               part->dev->id->num >= CONFIG_SYS_MAX_NAND_DEVICE)
+               return 0;
+
+       mtd = &nand_info[part->dev->id->num];
+
+       for (offs = part->offset; offs < part->offset + part->size;
+               offs += mtd->erasesize) {
+               if (nand_isbad_bbt(mtd, offs, 0))
+                       bb_delta += mtd->erasesize;
+       }
+
+       return part->size - bb_delta;
+}
+
+static struct part_info *get_partition_nand(int idx)
+{
+       struct mtd_device *dev;
+       struct part_info *part;
+       struct list_head *pentry;
+       int i;
+
+       if (mtdparts_init())
+               return NULL;
+       if (list_empty(&devices))
+               return NULL;
+
+       dev = list_entry(devices.next, struct mtd_device, link);
+       i = 0;
+       list_for_each(pentry, &dev->parts) {
+               if (i == idx)  {
+                       part = list_entry(pentry, struct part_info, link);
+                       return part;
+               }
+               i++;
+       }
+
+       return NULL;
+}
+
+static int initialize_ds_nand(struct usb_device_instance *dev,
+                             struct flash_entity_ctx *ds)
+{
+       ds->part = get_partition_nand(dev->alternate - 1);
+       if (!ds->part) {
+               printf("DFU: unable to find partition %u\b", dev->alternate-1);
+                       dev->dfu_state = DFU_STATE_dfuERROR;
+               dev->dfu_status = DFU_STATUS_errADDRESS;
+               return RET_STALL;
+       }
+       ds->nand = &nand_info[ds->part->dev->id->num];
+       ds->off = ds->part->offset;
+       ds->part_net_size = nand_net_part_size(ds->part);
+
+       if (ds->nand->erasesize > sizeof(ds->_buf)) {
+               printf("Warning - NAND ERASESIZE bigger than static buffer\n");
+               ds->buf = malloc(ds->nand->erasesize);
+               if (!ds->buf) {
+                       printf("DFU: can't allocate %u bytes\n",
+                               ds->nand->erasesize);
+                       dev->dfu_state = DFU_STATE_dfuERROR;
+                       dev->dfu_status = DFU_STATUS_errADDRESS;
+                       return RET_STALL;
+               }
+       } else
+               ds->buf = ds->_buf;
+
+       ds->ptr = ds->buf;
+
+       memset(&ds->erase_opts, 0, sizeof(ds->erase_opts));
+       ds->erase_opts.quiet = 1;
+       /* FIXME: do this more dynamic */
+       if ((!strcmp(ds->part->name, "rootfs")) ||
+           (!strcmp(ds->part->name, "fs")))
+               ds->erase_opts.jffs2 = 1;
+
+       /* FIXME: How to set these options without write_opts?
+        * ds->write_opts.pad = 1;
+        * ds->write_opts.blockalign = 1;
+        * ds->write_opts.quiet = 1;
+       */
+
+       debug("initialize_ds_nand(dev=%p, ds=%p): ", dev, ds);
+       debug("nand=%p, ptr=%p, buf=%p, off=0x%x\n", ds->nand, ds->ptr,
+                                                    ds->buf, ds->off);
+
+       return RET_NOTHING;
+}
+
+static int erase_flash_verify_nand(struct urb *urb, struct flash_entity_ctx 
*ds,
+                                  unsigned long erasesize, size_t size)
+{
+       struct usb_device_instance *dev = urb->device;
+       int rc;
+
+       debug("erase_flash_verify_nand(urb=%p, ds=%p, erase=0x%lx size=0x%x)\n",
+               urb, ds, erasesize, size);
+
+       if (erasesize == ds->nand->erasesize) {
+               /* we're only writing a single block and need to
+                * do bad block skipping / offset adjustments our own */
+               while (ds->nand->block_isbad(ds->nand, ds->off)) {
+                       debug("SKIP_ONE_BLOCK(0x%08x)!!\n", ds->off);
+                       ds->off += ds->nand->erasesize;
+               }
+       }
+
+       /* we have finished one eraseblock, flash it */
+       ds->erase_opts.offset = ds->off;
+       ds->erase_opts.length = erasesize;
+       debug("Erasing 0x%x bytes @ offset 0x%x (jffs=%u)\n",
+               (unsigned int)ds->erase_opts.length,
+               (unsigned int)ds->erase_opts.offset,
+               ds->erase_opts.jffs2);
+       rc = nand_erase_opts(ds->nand, &ds->erase_opts);
+       if (rc) {
+               debug("Error erasing\n");
+               dev->dfu_state = DFU_STATE_dfuERROR;
+               dev->dfu_status = DFU_STATUS_errERASE;
+               return RET_STALL;
+       }
+
+       debug("Writing 0x%x bytes @ offset 0x%x\n", size, ds->off);
+       /* FIXME handle oob */
+       rc = nand_write_skip_bad(ds->nand, ds->off, &size, ds->buf, 0);
+       if (rc) {
+               debug("Error writing\n");
+               dev->dfu_state = DFU_STATE_dfuERROR;
+               dev->dfu_status = DFU_STATUS_errWRITE;
+               return RET_STALL;
+       }
+
+       ds->off += size;
+       ds->ptr = ds->buf;
+
+       /* FIXME: implement verify! */
+       return RET_NOTHING;
+}
+
+static int erase_tail_clean_nand(struct urb *urb, struct flash_entity_ctx *ds)
+{
+       struct usb_device_instance *dev = urb->device;
+       int rc;
+
+       ds->erase_opts.offset = ds->off;
+       ds->erase_opts.length = ds->part->size - (ds->off - ds->part->offset);
+       debug("Erasing tail of 0x%x bytes @ offset 0x%x (jffs=%u)\n",
+               (unsigned int)ds->erase_opts.length,
+               (unsigned int)ds->erase_opts.offset,
+               ds->erase_opts.jffs2);
+       rc = nand_erase_opts(ds->nand, &ds->erase_opts);
+       if (rc) {
+               printf("Error erasing tail\n");
+               dev->dfu_state = DFU_STATE_dfuERROR;
+               dev->dfu_status = DFU_STATUS_errERASE;
+               return RET_STALL;
+       }
+
+       ds->off += ds->erase_opts.length; /* for consistency */
+
+       return RET_NOTHING;
+}
+
+/* Read the next erase block from NAND into buffer */
+static int read_next_nand(struct urb *urb, struct flash_entity_ctx *ds, size_t 
len)
+{
+       struct usb_device_instance *dev = urb->device;
+       int rc;
+
+       debug("Reading 0x%x@0x%x to 0x%p\n", len, ds->off, ds->buf);
+       rc = nand_read_skip_bad(ds->nand, ds->off, &len, ds->buf);
+       if (rc) {
+               debug("Error reading\n");
+               dev->dfu_state = DFU_STATE_dfuERROR;
+               dev->dfu_status = DFU_STATUS_errWRITE;
+               return RET_STALL;
+       }
+       ds->off += len;
+       ds->ptr = ds->buf;
+
+       return RET_NOTHING;
+}
+
+static int get_dfu_loadaddr(uint8_t **loadaddr)
+{
+       const char *s;
+       s = getenv("loadaddr");
+       if (s != NULL) {
+               *loadaddr = (uint8_t *)simple_strtoul(s, NULL, 16);
+               return 1;
+       } else
+               return 0;
+}
+
+static int handle_nand_dnload(struct urb *urb, u_int16_t len, int first)
+{
+       struct usb_device_instance *dev = urb->device;
+       struct flash_entity_ctx *ds = &ctx;
+       unsigned int actual_len = len;
+       unsigned int remain_len;
+       unsigned long size;
+       uint8_t *loadaddr;
+       int rc;
+
+       debug("download(len=%u, first=%u) ", len, first);
+
+       if (!get_dfu_loadaddr(&loadaddr)) {
+               printf("Error: DFU Download requires loadaddr to be set.\n");
+               dev->dfu_state = DFU_STATE_dfuERROR;
+               dev->dfu_status = DFU_STATUS_errADDRESS;
+               return RET_STALL;
+       }
+
+       if (len > CONFIG_USBD_DFU_XFER_SIZE) {
+               /* Too big. Not that we'd really care, but it's a
+                * DFU protocol violation */
+               debug("length exceeds flash page size ");
+               dev->dfu_state = DFU_STATE_dfuERROR;
+               dev->dfu_status = DFU_STATUS_errADDRESS;
+               return RET_STALL;
+       }
+
+       if (first && dev->alternate != 0) {
+               /* Make sure that we have a valid mtd partition table */
+               char *mtdp = getenv("mtdparts");
+               if (!mtdp)
+                       run_command("dynpart", 0);
+       }
+
+       if (len == 0) {
+               /* cleanup */
+               rc = 0;
+               if (ds->ptr > ds->buf)
+                       rc = erase_flash_verify_nand(urb, ds,
+                                               ds->nand->erasesize,
+                                               ds->nand->erasesize);
+               /* rootfs partition */
+               if (!rc && ((!strcmp(ds->part->name, "rootfs"))
+                       || (!strcmp(ds->part->name, "fs"))))
+                       rc = erase_tail_clean_nand(urb, ds);
+
+               ds->nand = NULL;
+
+               return RET_ZLP;
+       }
+
+       if (urb->actual_length != len) {
+               debug("urb->actual_length(%u) != len(%u) ?!? ",
+                       urb->actual_length, len);
+               dev->dfu_state = DFU_STATE_dfuERROR;
+               dev->dfu_status = DFU_STATUS_errADDRESS;
+               return RET_STALL;
+       }
+
+
+       // HOWTO handle this with preapre instead first?
+       if (first && ds->buf && ds->buf != ds->_buf && ds->buf != loadaddr) {
+               free(ds->buf);
+               ds->buf = ds->_buf;
+       }
+
+       if (first) { // Will be called from prepare()
+               rc = initialize_ds_nand(dev, ds);
+               if (rc)
+                       return rc;
+               printf("Starting DFU DOWNLOAD to partition '%s'\n",
+                       ds->part->name);
+       }
+
+       size = ds->nand->erasesize;
+       remain_len = ds->buf + size - ds->ptr;
+       if (remain_len < len)
+               actual_len = remain_len;
+
+       memcpy(ds->ptr, urb->buffer, actual_len);
+       ds->ptr += actual_len;
+
+       /* check partition end */
+       if (ds->off + (ds->ptr - ds->buf) > ds->part->offset +
+           ds->part->size) {
+               printf("End of write exceeds partition end\n");
+               dev->dfu_state = DFU_STATE_dfuERROR;
+               dev->dfu_status = DFU_STATUS_errADDRESS;
+               return RET_STALL;
+       }
+
+       if (ds->ptr >= ds->buf + size) {
+               rc = erase_flash_verify_nand(urb, ds,
+                                            ds->nand->erasesize,
+                                            ds->nand->erasesize);
+               if (rc)
+                       return rc;
+               /* copy remainder of data into buffer */
+               memcpy(ds->ptr, urb->buffer + actual_len, len -
+                      actual_len);
+               ds->ptr += (len - actual_len);
+       }
+
+       return RET_ZLP;
+}
+
+static int handle_nand_upload(struct urb *urb, u_int16_t len, int first)
+{
+       struct usb_device_instance *dev = urb->device;
+       struct flash_entity_ctx *ds = &ctx;
+       unsigned int remain;
+       int rc;
+
+       if (len > CONFIG_USBD_DFU_XFER_SIZE) {
+               /* Too big */
+               dev->dfu_state = DFU_STATE_dfuERROR;
+               dev->dfu_status = DFU_STATUS_errADDRESS;
+               debug("Error: Transfer size > CONFIG_USBD_DFU_XFER_SIZE ");
+               return -EINVAL;
+       }
+
+       switch (dev->alternate) {
+#if 0
+       case 0:
+               if (first) {
+                       printf("Starting DFU Upload of RAM (0x%p)\n", loadaddr);
+                       ds->buf = loadaddr;
+                       ds->ptr = ds->buf;
+               }
+
+               if (ds->ptr + len > loadaddr + filesize)
+                       len = (loadaddr + filesize) - ds->ptr;
+
+               memcpy(urb->buffer, ds->ptr, len);
+               urb->actual_length = len;
+               ds->ptr += len;
+               break;
+#endif
+       default:
+               if (first) {
+                       rc = initialize_ds_nand(dev, ds);
+                       if (rc)
+                               return -EINVAL;
+                       printf("Starting DFU Upload of partition '%s'\n",
+                               ds->part->name);
+               }
+
+               if (len > ds->nand->erasesize) {
+                       printf("We don't support transfers bigger than %u\n",
+                               ds->nand->erasesize);
+                       len = ds->nand->erasesize;
+               }
+
+               /* limit length to whatever number of bytes is left in
+                * this partition */
+               remain = (ds->part->offset + ds->part->size) - ds->off;
+               if (len > remain)
+                       len = remain;
+
+               rc = read_next_nand(urb, ds, len);
+               if (rc)
+                       return -EINVAL;
+
+               debug("uploading %u bytes ", len);
+               urb->buffer = ds->buf;
+               urb->actual_length = len;
+               break;
+       }
+
+       debug("returning len=%u\n", len);
+       return len;
+}
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 38af8c4..a97d953 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -28,13 +28,15 @@ LIB := $(obj)libusb_gadget.o
 # new USB gadget layer dependencies
 ifdef CONFIG_USB_ETHER
 COBJS-y += ether.o epautoconf.o config.o usbstring.o
+COBJS-$(CONFIG_DFU_GADGET) += dfu.o
 COBJS-$(CONFIG_USB_ETH_RNDIS) += rndis.o
 else
-COBJS-$(CONFIG_DFU_GADGET) += dfu.o
 # Devices not related to the new gadget layer depend on CONFIG_USB_DEVICE
 ifdef CONFIG_USB_DEVICE
 COBJS-y += core.o
 COBJS-y += ep0.o
+COBJS-y += dfu_old.o
+COBJS-$(CONFIG_DFU_GADGET) += dfu.o
 COBJS-$(CONFIG_OMAP1510) += omap1510_udc.o
 COBJS-$(CONFIG_OMAP1610) += omap1510_udc.o
 COBJS-$(CONFIG_MPC885_FAMILY) += mpc8xx_udc.o
diff --git a/drivers/usb/gadget/core.c b/drivers/usb/gadget/core.c
index 67b6681..e5ee9f6 100644
--- a/drivers/usb/gadget/core.c
+++ b/drivers/usb/gadget/core.c
@@ -31,6 +31,7 @@
 
 #include <malloc.h>
 #include <usbdevice.h>
+#include <usb_dfu.h>
 
 #define MAX_INTERFACES 2
 
@@ -209,6 +210,10 @@ struct usb_alternate_instance 
*usbd_device_alternate_instance (struct usb_device
  */
 struct usb_device_descriptor *usbd_device_device_descriptor (struct 
usb_device_instance *device, int port)
 {
+#ifdef CONFIG_USBD_DFU
+       if (device->dfu_state != DFU_STATE_appIDLE)
+               return device->dfu_dev_desc;
+#endif
        return (device->device_descriptor);
 }
 
@@ -229,6 +234,10 @@ struct usb_configuration_descriptor 
*usbd_device_configuration_descriptor (struc
        if (!(configuration_instance = usbd_device_configuration_instance 
(device, port, configuration))) {
                return NULL;
        }
+#ifdef CONFIG_USBD_DFU
+       if (device->dfu_state != DFU_STATE_appIDLE)
+               return (&device->dfu_cfg_desc->ucfg);
+#endif
        return (configuration_instance->configuration_descriptor);
 }
 
@@ -250,6 +259,13 @@ struct usb_interface_descriptor 
*usbd_device_interface_descriptor (struct usb_de
        if (!(interface_instance = usbd_device_interface_instance (device, 
port, configuration, interface))) {
                return NULL;
        }
+#ifdef CONFIG_USBD_DFU
+       if (device->dfu_state != DFU_STATE_appIDLE) {
+               if (alternate < 0 || alternate >= DFU_NUM_ALTERNATES)
+                       return NULL;
+               return &device->dfu_cfg_desc->uif[alternate];
+       }
+#endif
        if ((alternate < 0) || (alternate >= interface_instance->alternates)) {
                return NULL;
        }
@@ -680,4 +696,7 @@ void usbd_device_event_irq (struct usb_device_instance 
*device, usb_device_event
                /* usbdbg("calling device->event"); */
                device->event(device, event, data);
        }
+#ifdef CONFIG_USBD_DFU
+       dfu_event(device, event, data);
+#endif
 }
diff --git a/drivers/usb/gadget/dfu.c b/drivers/usb/gadget/dfu.c
index 7aed344..86f5dd8 100644
--- a/drivers/usb/gadget/dfu.c
+++ b/drivers/usb/gadget/dfu.c
@@ -4,10 +4,18 @@
  * Copyright (C) 2011 Samsung Electronics
  * author: Andrzej Pietrasiewicz <andrze...@samsung.com>
  *
+ * Copyright (C) 2011-2012 Stefan Schmidt <ste...@datenfreihafen.org>
+ *
  * Based on gadget zero:
  * Copyright (C) 2003-2007 David Brownell
  * All rights reserved.
  *
+ * (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>
+ *
  * 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
@@ -87,7 +95,7 @@ static struct usb_otg_descriptor otg_descriptor = {
 static const struct dfu_function_descriptor dfu_func = {
        .bLength =              sizeof dfu_func,
        .bDescriptorType =      DFU_DT_FUNC,
-       .bmAttributes =         DFU_BIT_WILL_DETACH |
+       .bmAttributes =         DFU_BIT_WILL_DETACH | /*FIXME */
                                DFU_BIT_MANIFESTATION_TOLERANT |
                                DFU_BIT_CAN_UPLOAD |
                                DFU_BIT_CAN_DNLOAD,
@@ -126,7 +134,7 @@ static const char longname[] = "DFU Gadget";
 /* default serial number takes at least two packets */
 static const char serial[] = "0123456789.0123456789.012345678";
 static const char dfu_name[] = "Device Firmware Upgrade";
-static const char shortname[] = "dfu";
+static const char shortname[] = "DFU";
 
 /* static strings, in UTF-8 */
 static struct usb_string strings_runtime[] = {
@@ -176,9 +184,10 @@ static int config_buf(struct usb_gadget *gadget,
        /* for now, don't advertise srp-only devices */
        if (!gadget_is_otg(gadget))
                function++;
-
+#ifdef CONFIG_USB_ETHER
        len = usb_gadget_config_buf(&dfu_config,
                        buf, USB_BUFSIZ, function);
+#endif
        if (len < 0)
                return len;
        ((struct usb_config_descriptor *) buf)->bDescriptorType = type;
@@ -282,6 +291,8 @@ static void handle_getstatus(struct usb_request *req)
        dstat->bwPollTimeout[0] = POLL_TIMEOUT_MILLISECONDS & 0xff;
        dstat->bwPollTimeout[1] = (POLL_TIMEOUT_MILLISECONDS >> 8) & 0xff;
        dstat->bwPollTimeout[2] = (POLL_TIMEOUT_MILLISECONDS >> 16) & 0xff;
+       /* Without the following line getstatus breaks with dfu-util. Maybe this
+        * value gets set correctly by the new gadget layer nowadays, check */
        //req->actual = MIN(sizeof(*dstat), max);
 }
 
@@ -336,18 +347,22 @@ dfu_handle(struct usb_gadget *gadget, const struct 
usb_ctrlrequest *ctrl)
        u16 len = le16_to_cpu(ctrl->wLength);
        int rc = 0;
 
+// u_int16_t len = urb->device_request.wLength;
+// handle_getstatus(urb, len);
+
        switch (dev->dfu_state) {
        case DFU_STATE_appIDLE:
                switch (ctrl->bRequest) {
                case USB_REQ_DFU_GETSTATUS:
                        handle_getstatus(req);
+                       //break;
                        return RET_STAT_LEN;
                case USB_REQ_DFU_GETSTATE:
                        handle_getstate(req);
                        break;
                case USB_REQ_DFU_DETACH:
                        dev->dfu_state = DFU_STATE_appDETACH;
-                       dev->dfu_state = DFU_STATE_dfuIDLE;
+                       dev->dfu_state = DFU_STATE_dfuIDLE; // Why?
                        return RET_ZLP;
                default:
                        return RET_STALL;
@@ -357,6 +372,7 @@ dfu_handle(struct usb_gadget *gadget, const struct 
usb_ctrlrequest *ctrl)
                switch (ctrl->bRequest) {
                case USB_REQ_DFU_GETSTATUS:
                        handle_getstatus(req);
+                       // break;
                        return RET_STAT_LEN;
                case USB_REQ_DFU_GETSTATE:
                        handle_getstate(req);
@@ -375,16 +391,18 @@ dfu_handle(struct usb_gadget *gadget, const struct 
usb_ctrlrequest *ctrl)
                                return RET_STALL;
                        }
                        dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+                       // ret = handle_dnload(urb, val, len, 1);
                        return handle_dnload(gadget, len);
                case USB_REQ_DFU_UPLOAD:
                        dev->dfu_state = DFU_STATE_dfuUPLOAD_IDLE;
+                       // handle_upload(urb, val, len, 1);
                        return handle_upload(req, len);
                case USB_REQ_DFU_ABORT:
                        /* no zlp? */
                        return RET_ZLP;
                case USB_REQ_DFU_GETSTATUS:
                        handle_getstatus(req);
-                       return RET_STAT_LEN;
+                       return RET_STAT_LEN; // Why?
                case USB_REQ_DFU_GETSTATE:
                        handle_getstate(req);
                        break;
@@ -397,7 +415,7 @@ dfu_handle(struct usb_gadget *gadget, const struct 
usb_ctrlrequest *ctrl)
                         * resets in a row :(
                         */
                        dev->dfu_state = DFU_STATE_dfuMANIFEST_WAIT_RST;
-                       dev->dfu_state = DFU_STATE_appIDLE;
+                       dev->dfu_state = DFU_STATE_appIDLE; // Why?
                        break;
                default:
                        dev->dfu_state = DFU_STATE_dfuERROR;
@@ -408,7 +426,7 @@ dfu_handle(struct usb_gadget *gadget, const struct 
usb_ctrlrequest *ctrl)
                switch (ctrl->bRequest) {
                case USB_REQ_DFU_GETSTATUS:
                        handle_getstatus(req);
-                       return RET_STAT_LEN;
+                       return RET_STAT_LEN; // Why?
                        /* FIXME: state transition depending
                         * on block completeness */
                case USB_REQ_DFU_GETSTATE:
@@ -425,7 +443,7 @@ dfu_handle(struct usb_gadget *gadget, const struct 
usb_ctrlrequest *ctrl)
                        /* FIXME: only accept getstatus if bwPollTimeout
                         * has elapsed */
                        handle_getstatus(req);
-                       return RET_STAT_LEN;
+                       return RET_STAT_LEN; // Why?
                default:
                        dev->dfu_state = DFU_STATE_dfuERROR;
                        return RET_STALL;
@@ -435,6 +453,7 @@ dfu_handle(struct usb_gadget *gadget, const struct 
usb_ctrlrequest *ctrl)
                switch (ctrl->bRequest) {
                case USB_REQ_DFU_DNLOAD:
                        dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+                       //ret = handle_dnload(urb, len, 0);
                        return handle_dnload(gadget, len);
                case USB_REQ_DFU_ABORT:
                        dev->dfu_state = DFU_STATE_dfuIDLE;
@@ -476,6 +495,7 @@ dfu_handle(struct usb_gadget *gadget, const struct 
usb_ctrlrequest *ctrl)
                switch (ctrl->bRequest) {
                case USB_REQ_DFU_UPLOAD:
                        /* state transition if less data then requested */
+                       //rc = handle_upload(urb, len, 0);
                        rc = handle_upload(req, len);
                        if (rc >= 0 && rc < len)
                                dev->dfu_state = DFU_STATE_dfuIDLE;
@@ -518,6 +538,24 @@ dfu_handle(struct usb_gadget *gadget, const struct 
usb_ctrlrequest *ctrl)
        }
 
        return 0;
+#if 0
+out:
+       debug("new_state = %u, ret = %u\n", dev->dfu_state, ret);
+
+       switch (ret) {
+       case RET_ZLP:
+               urb->actual_length = 0;
+               return DFU_EP0_ZLP;
+               break;
+       case RET_STALL:
+               return DFU_EP0_STALL;
+               break;
+       case RET_NOTHING:
+               break;
+       }
+
+       return DFU_EP0_DATA;
+#endif
 }
 
 static int
@@ -915,6 +953,7 @@ void register_flash_entities(struct flash_entity *flents, 
int n)
        num_flash_ents = n;
 }
 
+#ifdef CONFIG_USB_ETHER
 int __init dfu_init(void)
 {
        return usb_gadget_register_driver(&dfu_driver);
@@ -926,4 +965,4 @@ void __exit dfu_cleanup(void)
        usb_gadget_unregister_driver(&dfu_driver);
 }
 module_exit(cleanup);
-
+#endif
diff --git a/drivers/usb/gadget/dfu_old.c b/drivers/usb/gadget/dfu_old.c
new file mode 100644
index 0000000..52686a8
--- /dev/null
+++ b/drivers/usb/gadget/dfu_old.c
@@ -0,0 +1,521 @@
+/*
+ * (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
+ */
+
+#include <config.h>
+#if defined(CONFIG_USBD_DFU)
+
+/* FIXME disbale debug */
+#define DEBUG
+
+#include <common.h>
+DECLARE_GLOBAL_DATA_PTR; /* FIXME needed? */
+
+#include <malloc.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <asm/errno.h>
+#include <usbdevice.h>
+#include <usb_dfu.h>
+#include <usb_dfu_descriptors.h>
+
+#include <flash_entity.h>
+
+#include "../../serial/usbtty.h"                       /* for STR_* defs */
+
+#define POLL_TIMEOUT_MILLISECONDS 5
+
+static struct flash_entity *flash_ents;
+static int num_flash_ents;
+
+/* HACK to include nand backend code for now */
+#include "../dfu/nand.c"
+
+void register_flash_entities2(struct flash_entity *flents, int n)
+{
+       flash_ents = flents;
+       num_flash_ents = n;
+}
+
+static void handle_getstatus(struct urb *urb, int max)
+{
+       struct usb_device_instance *dev = urb->device;
+       struct dfu_status *dstat = (struct dfu_status *) urb->buffer;
+
+       if (!urb->buffer || urb->buffer_length < sizeof(*dstat)) {
+               debug("invalid urb! ");
+               return;
+       }
+
+       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:
+               /* return; */
+               break;
+       }
+
+       /* send status response */
+       dstat->bStatus = dev->dfu_status;
+       dstat->bState = dev->dfu_state;
+       dstat->iString = 0;
+       /* FIXME: Use real values from flash subsystem here instead a hardcoded
+        * value */
+       dstat->bwPollTimeout[0] = POLL_TIMEOUT_MILLISECONDS & 0xff;
+       dstat->bwPollTimeout[1] = (POLL_TIMEOUT_MILLISECONDS >> 8) & 0xff;
+       dstat->bwPollTimeout[2] = (POLL_TIMEOUT_MILLISECONDS >> 16) & 0xff;
+       urb->actual_length = MIN(sizeof(*dstat), max);
+
+       /* we don't need to explicitly send data here, will
+        * be done by the original caller! */
+}
+
+static void handle_getstate(struct urb *urb)
+{
+       if (!urb->buffer || urb->buffer_length < sizeof(u_int8_t)) {
+               debug("invalid urb! ");
+               return;
+       }
+
+       urb->buffer[0] = urb->device->dfu_state & 0xff;
+       urb->actual_length = sizeof(u_int8_t);
+}
+
+static int handle_dnload(struct urb *urb, u_int16_t len, int first)
+{
+       struct usb_device_instance *dev = urb->device;
+
+       if (len == 0)
+               dev->dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
+
+       return handle_nand_dnload(urb, len, first);
+}
+
+static int handle_upload(struct urb *urb, u_int16_t len, int first)
+{
+       return handle_nand_upload(urb, len, first);
+}
+
+#ifndef CONFIG_USBD_PRODUCTID_DFU
+#define CONFIG_USBD_PRODUCTID_DFU CONFIG_USBD_PRODUCTID_CDCACM
+#endif
+
+static const struct usb_device_descriptor dfu_dev_descriptor = {
+       .bLength                = USB_DT_DEVICE_SIZE,
+       .bDescriptorType        = USB_DT_DEVICE,
+       .bcdUSB                 = 0x0100,
+       .bDeviceClass           = 0x00,
+       .bDeviceSubClass        = 0x00,
+       .bDeviceProtocol        = 0x00,
+       .bMaxPacketSize0        = EP0_MAX_PACKET_SIZE,
+       .idVendor               = CONFIG_USBD_VENDORID,
+       .idProduct              = CONFIG_USBD_PRODUCTID_DFU,
+       .bcdDevice              = 0x0000,
+       .iManufacturer          = DFU_STR_MANUFACTURER,
+       .iProduct               = DFU_STR_PRODUCT,
+       .iSerialNumber          = DFU_STR_SERIAL,
+       .bNumConfigurations     = 0x01,
+};
+
+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           = BMATTRIBUTE_RESERVED,
+               .bMaxPower              = 50,
+       },
+       .func_dfu = DFU_FUNC_DESC,
+};
+
+int dfu_ep0_handler(struct urb *urb)
+{
+       int rc, ret = RET_NOTHING;
+       u_int8_t req = urb->device_request.bRequest;
+       u_int16_t len = urb->device_request.wLength;
+       struct usb_device_instance *dev = urb->device;
+
+       debug("dfu_ep0(req=0x%x, len=%u) old_state = %u ",
+               req, len, dev->dfu_state);
+
+       switch (dev->dfu_state) {
+       case DFU_STATE_appIDLE:
+               switch (req) {
+               case USB_REQ_DFU_GETSTATUS:
+                       handle_getstatus(urb, len);
+                       break;
+               case USB_REQ_DFU_GETSTATE:
+                       handle_getstate(urb);
+                       break;
+               case USB_REQ_DFU_DETACH:
+                       dev->dfu_state = DFU_STATE_appDETACH;
+                       ret = RET_ZLP;
+                       goto out;
+                       break;
+               default:
+                       ret = RET_STALL;
+               }
+               break;
+       case DFU_STATE_appDETACH:
+               switch (req) {
+               case USB_REQ_DFU_GETSTATUS:
+                       handle_getstatus(urb, len);
+                       break;
+               case USB_REQ_DFU_GETSTATE:
+                       handle_getstate(urb);
+                       break;
+               default:
+                       dev->dfu_state = DFU_STATE_appIDLE;
+                       ret = RET_STALL;
+                       goto out;
+                       break;
+               }
+               /* FIXME: implement timer to return to appIDLE */
+               break;
+       case DFU_STATE_dfuIDLE:
+               switch (req) {
+               case USB_REQ_DFU_DNLOAD:
+                       if (len == 0) {
+                               dev->dfu_state = DFU_STATE_dfuERROR;
+                               ret = RET_STALL;
+                               goto out;
+                       }
+                       dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+                       ret = handle_dnload(urb, len, 1);
+                       break;
+               case USB_REQ_DFU_UPLOAD:
+                       dev->dfu_state = DFU_STATE_dfuUPLOAD_IDLE;
+                       handle_upload(urb, len, 1);
+                       break;
+               case USB_REQ_DFU_ABORT:
+                       /* no zlp? */
+                       ret = RET_ZLP;
+                       break;
+               case USB_REQ_DFU_GETSTATUS:
+                       handle_getstatus(urb, len);
+                       break;
+               case USB_REQ_DFU_GETSTATE:
+                       handle_getstate(urb);
+                       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;
+                       break;
+               default:
+                       dev->dfu_state = DFU_STATE_dfuERROR;
+                       ret = RET_STALL;
+                       goto out;
+                       break;
+               }
+               break;
+       case DFU_STATE_dfuDNLOAD_SYNC:
+               switch (req) {
+               case USB_REQ_DFU_GETSTATUS:
+                       handle_getstatus(urb, len);
+                       /* FIXME: state transition depending on block
+                        * completeness */
+                       break;
+               case USB_REQ_DFU_GETSTATE:
+                       handle_getstate(urb);
+                       break;
+               default:
+                       dev->dfu_state = DFU_STATE_dfuERROR;
+                       ret = RET_STALL;
+                       goto out;
+               }
+               break;
+       case DFU_STATE_dfuDNBUSY:
+               switch (req) {
+               case USB_REQ_DFU_GETSTATUS:
+                       /* FIXME: only accept getstatus if bwPollTimeout
+                        * has elapsed */
+                       handle_getstatus(urb, len);
+                       break;
+               default:
+                       dev->dfu_state = DFU_STATE_dfuERROR;
+                       ret = RET_STALL;
+                       goto out;
+               }
+               break;
+       case DFU_STATE_dfuDNLOAD_IDLE:
+               switch (req) {
+               case USB_REQ_DFU_DNLOAD:
+                       dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+                       ret = handle_dnload(urb, len, 0);
+                       break;
+               case USB_REQ_DFU_ABORT:
+                       dev->dfu_state = DFU_STATE_dfuIDLE;
+                       ret = RET_ZLP;
+                       break;
+               case USB_REQ_DFU_GETSTATUS:
+                       handle_getstatus(urb, len);
+                       break;
+               case USB_REQ_DFU_GETSTATE:
+                       handle_getstate(urb);
+                       break;
+               default:
+                       dev->dfu_state = DFU_STATE_dfuERROR;
+                       ret = RET_STALL;
+                       break;
+               }
+               break;
+       case DFU_STATE_dfuMANIFEST_SYNC:
+               switch (req) {
+               case USB_REQ_DFU_GETSTATUS:
+                       /* We're MainfestationTolerant */
+                       dev->dfu_state = DFU_STATE_dfuIDLE;
+                       handle_getstatus(urb, len);
+                       break;
+               case USB_REQ_DFU_GETSTATE:
+                       handle_getstate(urb);
+                       break;
+               default:
+                       dev->dfu_state = DFU_STATE_dfuERROR;
+                       ret = RET_STALL;
+                       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 (req) {
+               case USB_REQ_DFU_UPLOAD:
+                       /* state transition if less data then requested */
+                       rc = handle_upload(urb, len, 0);
+                       if (rc >= 0 && rc < len)
+                               dev->dfu_state = DFU_STATE_dfuIDLE;
+                       break;
+               case USB_REQ_DFU_ABORT:
+                       dev->dfu_state = DFU_STATE_dfuIDLE;
+                       /* no zlp? */
+                       ret = RET_ZLP;
+                       break;
+               case USB_REQ_DFU_GETSTATUS:
+                       handle_getstatus(urb, len);
+                       break;
+               case USB_REQ_DFU_GETSTATE:
+                       handle_getstate(urb);
+                       break;
+               default:
+                       dev->dfu_state = DFU_STATE_dfuERROR;
+                       ret = RET_STALL;
+                       break;
+               }
+               break;
+       case DFU_STATE_dfuERROR:
+               switch (req) {
+               case USB_REQ_DFU_GETSTATUS:
+                       handle_getstatus(urb, len);
+                       break;
+               case USB_REQ_DFU_GETSTATE:
+                       handle_getstate(urb);
+                       break;
+               case USB_REQ_DFU_CLRSTATUS:
+                       dev->dfu_state = DFU_STATE_dfuIDLE;
+                       dev->dfu_status = DFU_STATUS_OK;
+                       /* no zlp? */
+                       ret = RET_ZLP;
+                       break;
+               default:
+                       dev->dfu_state = DFU_STATE_dfuERROR;
+                       ret = RET_STALL;
+                       break;
+               }
+               break;
+       default:
+               return DFU_EP0_UNHANDLED;
+               break;
+       }
+
+out:
+       debug("new_state = %u, ret = %u\n", dev->dfu_state, ret);
+
+       switch (ret) {
+       case RET_ZLP:
+               urb->actual_length = 0;
+               return DFU_EP0_ZLP;
+               break;
+       case RET_STALL:
+               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;
+}
+
+
+static void dfu_init_strings(struct usb_device_instance *dev)
+{
+       int i;
+       struct usb_string_descriptor *strdesc;
+
+       strdesc = create_usbstring(CONFIG_DFU_CFG_STR);
+       usb_strings[DFU_STR_CONFIG] = strdesc;
+
+       for (i = 0; i < DFU_NUM_ALTERNATES; i++) {
+               if (i == 0) {
+                       strdesc = create_usbstring(CONFIG_DFU_ALT0_STR);
+               } else {
+                       struct part_info *part = get_partition_nand(i-1);
+
+                       if (part)
+                               strdesc = create_usbstring(part->name);
+                       else
+                               strdesc =
+                                   create_usbstring("undefined partition");
+               }
+               if (!strdesc)
+                       continue;
+               usb_strings[STR_COUNT+i+1] = strdesc;
+       }
+}
+
+int dfu_init_instance(struct usb_device_instance *dev)
+{
+       int i;
+
+       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; /* FIXME: Corect number? */
+               uif->iInterface         = DFU_STR_ALT(i);
+       }
+
+       dev->dfu_dev_desc = &dfu_dev_descriptor;
+       dev->dfu_cfg_desc = &dfu_cfg_descriptor;
+       dev->dfu_state = DFU_STATE_appIDLE;
+       dev->dfu_status = DFU_STATUS_OK;
+
+       dfu_init_strings(dev);
+
+       return 0;
+}
+
+/* event handler for usb device state events */
+void dfu_event(struct usb_device_instance *device,
+              usb_device_event_t event, int data)
+{
+       switch (event) {
+       case DEVICE_RESET:
+               switch (device->dfu_state) {
+               case DFU_STATE_appDETACH:
+                       device->dfu_state = DFU_STATE_dfuIDLE;
+                       printf("DFU: Switching to DFU Mode\n");
+                       break;
+               case DFU_STATE_dfuMANIFEST_WAIT_RST:
+                       device->dfu_state = DFU_STATE_appIDLE;
+                       printf("DFU: Switching back to Runtime mode\n");
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case DEVICE_CONFIGURED:
+       case DEVICE_DE_CONFIGURED:
+               debug("SET_CONFIGURATION(%u) ", device->configuration);
+               /* fallthrough */
+       case DEVICE_SET_INTERFACE:
+               debug("SET_INTERFACE(%u,%u) old_state = %u ",
+                       device->interface, device->alternate,
+                       device->dfu_state);
+               switch (device->dfu_state) {
+               case DFU_STATE_appIDLE:
+               case DFU_STATE_appDETACH:
+               case DFU_STATE_dfuIDLE:
+               case DFU_STATE_dfuMANIFEST_WAIT_RST:
+                       /* do nothing, we're fine */
+                       break;
+               case DFU_STATE_dfuDNLOAD_SYNC:
+               case DFU_STATE_dfuDNBUSY:
+               case DFU_STATE_dfuDNLOAD_IDLE:
+               case DFU_STATE_dfuMANIFEST:
+                       device->dfu_state = DFU_STATE_dfuERROR;
+                       device->dfu_status = DFU_STATUS_errNOTDONE;
+                       /* FIXME: free malloc()ed buffer! */
+                       break;
+               case DFU_STATE_dfuMANIFEST_SYNC:
+               case DFU_STATE_dfuUPLOAD_IDLE:
+               case DFU_STATE_dfuERROR:
+                       device->dfu_state = DFU_STATE_dfuERROR;
+                       device->dfu_status = DFU_STATUS_errUNKNOWN;
+                       break;
+               }
+               debug("new_state = %u\n", device->dfu_state);
+               break;
+       default:
+               break;
+       }
+}
+#endif /* CONFIG_USBD_DFU */
diff --git a/drivers/usb/gadget/ep0.c b/drivers/usb/gadget/ep0.c
index 2b4ec44..ddeed93 100644
--- a/drivers/usb/gadget/ep0.c
+++ b/drivers/usb/gadget/ep0.c
@@ -52,6 +52,11 @@
 
 #include <common.h>
 #include <usbdevice.h>
+DECLARE_GLOBAL_DATA_PTR;
+
+#ifdef CONFIG_USBD_DFU
+#include <usb_dfu.h>
+#endif
 
 #if 0
 #define dbg_ep0(lvl,fmt,args...) serial_printf("[%s] %s:%d: 
"fmt"\n",__FILE__,__FUNCTION__,__LINE__,##args)
@@ -198,7 +203,7 @@ static int ep0_get_descriptor (struct usb_device_instance 
*device,
        urb->actual_length = 0;
        cp = (char*)urb->buffer;
 
-       dbg_ep0 (2, "%s", USBD_DEVICE_DESCRIPTORS (descriptor_type));
+       dbg_ep0 (2, "%s (%d)", USBD_DEVICE_DESCRIPTORS (descriptor_type), 
descriptor_type);
 
        switch (descriptor_type) {
        case USB_DESCRIPTOR_TYPE_DEVICE:
@@ -276,7 +281,28 @@ static int ep0_get_descriptor (struct usb_device_instance 
*device,
        case USB_DESCRIPTOR_TYPE_ENDPOINT:
                serial_printf("USB_DESCRIPTOR_TYPE_ENDPOINT - error not 
implemented\n");
                return -1;
+       /* This really means "Class Specific Descriptor #1 == USB_DT_DFU */
        case USB_DESCRIPTOR_TYPE_HID:
+#ifdef CONFIG_USBD_DFU
+               {
+                       int bNumInterface =
+                               le16_to_cpu(urb->device_request.wIndex);
+
+                       /* In runtime mode, we only respond to the DFU 
INTERFACE,
+                        * whereas in DFU mode, we respond for all intrfaces */
+                       if (device->dfu_state != DFU_STATE_appIDLE &&
+                           device->dfu_state != DFU_STATE_appDETACH ||
+                           bNumInterface == CONFIG_USBD_DFU_INTERFACE) {
+
+                               /* copy descriptor for this device */
+                               copy_config (urb, 
&device->dfu_cfg_desc->func_dfu,
+                                    sizeof (struct usb_dfu_func_descriptor),
+                                    max);
+                       } else {
+                               return -1;
+            }
+               }
+#else /* CONFIG_USBD_DFU */
                {
                        serial_printf("USB_DESCRIPTOR_TYPE_HID - error not 
implemented\n");
                        return -1;      /* unsupported at this time */
@@ -304,6 +330,7 @@ static int ep0_get_descriptor (struct usb_device_instance 
*device,
                                     max);
 #endif
                }
+#endif /* CONFIG_USBD_DFU */
                break;
        case USB_DESCRIPTOR_TYPE_REPORT:
                {
@@ -405,6 +432,24 @@ int ep0_recv_setup (struct urb *urb)
                 le16_to_cpu (request->wLength),
                 USBD_DEVICE_REQUESTS (request->bRequest));
 
+#ifdef CONFIG_USBD_DFU
+       if ((request->bmRequestType & 0x3f) == USB_TYPE_DFU &&
+            (device->dfu_state != DFU_STATE_appIDLE ||
+             le16_to_cpu(request->wIndex) == CONFIG_USBD_DFU_INTERFACE)) {
+               int rc = dfu_ep0_handler(urb);
+               switch (rc) {
+               case DFU_EP0_NONE:
+               case DFU_EP0_UNHANDLED:
+                       break;
+               case DFU_EP0_ZLP:
+               case DFU_EP0_DATA:
+                       return 0;
+               case DFU_EP0_STALL:
+                       return -1;
+               }
+       }
+#endif /* CONFIG_USBD_DFU */
+
        /* handle USB Standard Request (c.f. USB Spec table 9-2) */
        if ((request->bmRequestType & USB_REQ_TYPE_MASK) != 0) {
                if(device->device_state <= STATE_CONFIGURED){
@@ -582,7 +627,8 @@ int ep0_recv_setup (struct urb *urb)
                        device->interface = le16_to_cpu (request->wIndex);
                        device->alternate = le16_to_cpu (request->wValue);
                        /*dbg_ep0(2, "set interface: %d alternate: %d", 
device->interface, device->alternate); */
-                       serial_printf ("DEVICE_SET_INTERFACE.. event?\n");
+                       usbd_device_event_irq(device, DEVICE_SET_INTERFACE,
+                                               (request->wIndex << 16 | 
request->wValue));
                        return 0;
 
                case USB_REQ_GET_STATUS:
diff --git a/include/configs/omap3_beagle.h b/include/configs/omap3_beagle.h
index 15e40c5..6be4d10 100644
--- a/include/configs/omap3_beagle.h
+++ b/include/configs/omap3_beagle.h
@@ -138,6 +138,9 @@
 #define CONFIG_USB_ETHER_SMSC95XX
 #define CONFIG_USB_ETHER_ASIX
 
+#define CONFIG_DFU_GADGET
+//#define CONFIG_USB_ETHER
+
 
 /* commands to include */
 #include <config_cmd_default.h>
diff --git a/include/dfu_backend.h b/include/dfu_backend.h
index 8c608d1..a93f181 100644
--- a/include/dfu_backend.h
+++ b/include/dfu_backend.h
@@ -21,6 +21,7 @@
 
 #ifndef __DFU_BACKEND_H__
 #define __DFU_BACKEND_H__
+#include <nand.h>
 
 struct mbr_part_data {
        unsigned long           offset; /* #sectors from mmc begin */
@@ -43,6 +44,21 @@ struct flash_entity_ctx {
        erase_op                erase; /* erase op for this medium or NULL */
        struct flash_entity     *this_entity; /* the containing entity */
        void                    *associated; /* related entity, if any */
+
+
+       nand_info_t *nand;
+       struct part_info *part;
+       unsigned int part_net_size;     /* net size (no bad blocks) of part */
+       nand_erase_options_t erase_opts;
+
+       unsigned char *ptr;     /* pointer to next empty byte in buffer */
+       unsigned int off;       /* offset of current erase page in flash chip */
+
+       /* 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 */
 };
 
 extern void board_dfu_init(void);
diff --git a/include/usb_dfu.h b/include/usb_dfu.h
new file mode 100644
index 0000000..588dcfb
--- /dev/null
+++ b/include/usb_dfu.h
@@ -0,0 +1,97 @@
+#ifndef _DFU_H
+#define _DFU_H
+
+/* USB Device Firmware Update Implementation for u-boot
+ * (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 <usbdevice.h>
+#include <usbdescriptors.h>
+#include <usb_dfu_descriptors.h>
+#include <config.h>
+
+/* USB DFU functional descriptor */
+#define DFU_FUNC_DESC  {                                               \
+       .bLength                = USB_DT_DFU_SIZE,                      \
+       .bDescriptorType        = USB_DT_DFU,                           \
+       .bmAttributes           = USB_DFU_CAN_UPLOAD | USB_DFU_CAN_DOWNLOAD | 
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     = 0x01,                                 \
+       .iInterface             = DFU_STR_CONFIG,                       \
+}
+
+#define ARRAY_SIZE(x)           (sizeof(x) / sizeof((x)[0]))
+
+#ifndef DFU_NUM_ALTERNATES
+#define DFU_NUM_ALTERNATES     6
+#endif
+
+#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)
+
+#define CONFIG_DFU_CFG_STR     "USB Device Firmware Upgrade (DIRTY)"
+#define CONFIG_DFU_ALT0_STR    "RAM"
+
+struct _dfu_desc {
+       struct usb_configuration_descriptor ucfg;
+       struct usb_interface_descriptor uif[DFU_NUM_ALTERNATES];
+       struct usb_dfu_func_descriptor func_dfu;
+};
+
+int dfu_init_instance(struct usb_device_instance *dev);
+
+#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
+
+#define RET_NOTHING     0
+#define RET_ZLP         1
+#define RET_STALL       2
+
+int dfu_ep0_handler(struct urb *urb);
+
+void dfu_event(struct usb_device_instance *device,
+              usb_device_event_t event, int data);
+
+#endif /* _DFU_H */
diff --git a/include/usb_dfu_descriptors.h b/include/usb_dfu_descriptors.h
new file mode 100644
index 0000000..d4d6a4d
--- /dev/null
+++ b/include/usb_dfu_descriptors.h
@@ -0,0 +1,94 @@
+#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;
+#define USB_DFU_CAN_DOWNLOAD   (1 << 0)
+#define USB_DFU_CAN_UPLOAD     (1 << 1)
+#define USB_DFU_MANIFEST_TOL   (1 << 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 */
diff --git a/include/usbdevice.h b/include/usbdevice.h
index 4171636..6c2fc27 100644
--- a/include/usbdevice.h
+++ b/include/usbdevice.h
@@ -33,6 +33,7 @@
 
 #include <common.h>
 #include "usbdescriptors.h"
+#include <usb_dfu_descriptors.h>
 
 
 #define MAX_URBS_QUEUED 5
@@ -467,7 +468,11 @@ typedef struct urb_link {
  * function driver to inform it that data has arrived.
  */
 
+#ifdef CONFIG_USBD_DFU
+#define URB_BUF_SIZE (128+CONFIG_USBD_DFU_XFER_SIZE)
+#else
 #define URB_BUF_SIZE 128 /* in linux we'd malloc this, but in u-boot we prefer 
static data */
+#endif
 struct urb {
 
        struct usb_endpoint_instance *endpoint;
@@ -595,6 +600,12 @@ struct usb_device_instance {
        unsigned long usbd_rxtx_timestamp;
        unsigned long usbd_last_rxtx_timestamp;
 
+#ifdef CONFIG_USBD_DFU
+       const struct usb_device_descriptor *dfu_dev_desc;
+       const struct _dfu_desc *dfu_cfg_desc;
+       enum dfu_state dfu_state;
+       u_int8_t dfu_status;
+#endif
 };
 
 /* Bus Interface configuration structure
@@ -624,6 +635,8 @@ extern char *usbd_device_status[];
 extern char *usbd_device_requests[];
 extern char *usbd_device_descriptors[];
 
+extern struct usb_string_descriptor **usb_strings;
+
 void urb_link_init (urb_link * ul);
 void urb_detach (struct urb *urb);
 urb_link *first_urb_link (urb_link * hd);
-- 
1.7.9.5

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

Reply via email to