Index: u-boot/drivers/usb/usbdcore_ep0.c
===================================================================
--- u-boot.orig/drivers/usb/usbdcore_ep0.c
+++ u-boot/drivers/usb/usbdcore_ep0.c
@@ -51,10 +51,15 @@
  */
 
 #include <common.h>
+DECLARE_GLOBAL_DATA_PTR;
 
 #if defined(CONFIG_USB_DEVICE)
 #include "usbdcore.h"
 
+#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)
 #else
@@ -273,7 +278,24 @@
 	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) {
+				urb->buffer = &device->dfu_cfg_desc->func_dfu;
+				urb->actual_length = sizeof(struct usb_dfu_func_descriptor);
+			} else
+				return -1;
+		}
+#else /* CONFIG_USBD_DFU */
 		{
 			serial_printf("USB_DESCRIPTOR_TYPE_HID - error not implemented\n");
 			return -1;	/* unsupported at this time */
@@ -301,6 +323,7 @@
 				     max);
 #endif
 		}
+#endif /* CONFIG_USBD_DFU */
 		break;
 	case USB_DESCRIPTOR_TYPE_REPORT:
 		{
@@ -403,6 +426,24 @@
 		 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_USB_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){
@@ -590,7 +631,8 @@
 			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:
Index: u-boot/drivers/usb/usbdfu.c
===================================================================
--- /dev/null
+++ u-boot/drivers/usb/usbdfu.c
@@ -0,0 +1,1050 @@
+/*
+ * (C) 2007 by OpenMoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ *
+ * based on existing SAM7DFU code from OpenPCD:
+ * (C) Copyright 2006 by Harald Welte <hwelte@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 usb_device_instance'
+ */
+
+#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/errno.h>
+#include <usbdcore.h>
+#include <usb_dfu.h>
+#include <usb_dfu_descriptors.h>
+#include <usb_dfu_trailer.h>
+
+#include <nand.h>
+#include <jffs2/load_kernel.h>
+int mtdparts_init(void);
+extern struct list_head devices;
+
+#include "usbdcore_s3c2410.h"
+#include "../serial/usbtty.h"			/* for STR_* defs */
+
+#define RET_NOTHING	0
+#define RET_ZLP		1
+#define RET_STALL	2
+
+volatile enum dfu_state *system_dfu_state; /* for 3rd parties */
+
+
+struct dnload_state {
+	nand_info_t *nand;
+	struct part_info *part;
+	unsigned int part_net_size;	/* net sizee (excl. bad blocks) of part */
+
+	nand_erase_options_t erase_opts;
+	nand_write_options_t write_opts;
+	nand_read_options_t read_opts;
+
+	unsigned char *ptr;	/* pointer to next empty byte in buffer */
+	unsigned int off;	/* offset of current erase page in flash chip */
+	unsigned char *buf;	/* pointer to allocated erase page buffer */
+
+	/* unless doing an atomic transfer, we use the static buffer below.
+	 * This saves us from having to clean up dynamic allications in the
+	 * various error paths of the code.  Also, it will always work, no
+	 * matter what the memory situation is. */
+	unsigned char _buf[0x20000];	/* FIXME: depends flash page size */
+};
+
+static struct dnload_state _dnstate;
+
+static int dfu_trailer_matching(const struct uboot_dfu_trailer *trailer)
+{
+	if (trailer->magic != UBOOT_DFU_TRAILER_MAGIC ||
+	    trailer->version != UBOOT_DFU_TRAILER_V1 ||
+	    trailer->vendor != CONFIG_USBD_VENDORID ||
+	    (trailer->product != CONFIG_USBD_PRODUCTID_CDCACM &&
+	     trailer->product != CONFIG_USBD_PRODUCTID_GSERIAL))
+		return 0;
+#ifdef CONFIG_REVISION_TAG
+	if (trailer->revision != get_board_rev())
+		return 0;
+#endif
+
+	return 1;
+}
+
+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;
+}
+
+#define LOAD_ADDR ((unsigned char *)0x32000000)
+
+static int initialize_ds_nand(struct usb_device_instance *dev, struct dnload_state *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->read_opts, 0, sizeof(ds->read_opts));
+
+	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"))
+		ds->erase_opts.jffs2 = 1;
+
+	memset(&ds->write_opts, 0, sizeof(ds->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 dnload_state *ds,
+				   unsigned long erasesize, unsigned long size)
+{
+	struct usb_device_instance *dev = urb->device;
+	int rc;
+
+	debug("erase_flash_verify_nand(urb=%p, ds=%p, erase=0x%x 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",
+		ds->erase_opts.length, 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;
+	}
+
+	ds->write_opts.buffer = ds->buf;
+	ds->write_opts.length = size;
+	ds->write_opts.offset = ds->off;
+	debug("Writing 0x%x bytes @ offset 0x%x\n", size, ds->off);
+	rc = nand_write_opts(ds->nand, &ds->write_opts);
+	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 dnload_state *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",
+		ds->erase_opts.length, 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 blcok from NAND into buffer */
+static int read_next_nand(struct urb *urb, struct dnload_state *ds)
+{
+	struct usb_device_instance *dev = urb->device;
+	int rc;
+
+	ds->read_opts.buffer = ds->buf;
+	ds->read_opts.length = ds->nand->erasesize;
+	ds->read_opts.offset = ds->off;
+	ds->read_opts.quiet = 1;
+
+	debug("Reading 0x%x@0x%x to 0x%08p\n", ds->nand->erasesize,
+		ds->off, ds->buf);
+	rc = nand_read_opts(ds->nand, &ds->read_opts);
+	if (rc) {
+		debug("Error reading\n");
+  		dev->dfu_state = DFU_STATE_dfuERROR;
+		dev->dfu_status = DFU_STATUS_errWRITE;
+		return RET_STALL;
+	}
+	ds->off += ds->nand->erasesize;
+	ds->ptr = ds->buf;
+
+	return RET_NOTHING;
+}
+
+
+static int handle_dnload(struct urb *urb, u_int16_t val, u_int16_t len, int first)
+{
+	struct usb_device_instance *dev = urb->device;
+	struct dnload_state *ds = &_dnstate;
+	unsigned int actual_len = len;
+	unsigned int remain_len;
+	unsigned long size;
+	int rc;
+
+	debug("download(len=%u, first=%u) ", len, first);
+
+	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) {
+		/* Make sure that we have a valid mtd partition table */
+		char *mtdp = getenv("mtdparts");
+		if (!mtdp)
+			run_command("dynpart", 0);
+	}
+
+	if (len == 0) {
+		debug("zero-size write -> MANIFEST_SYNC ");
+		dev->dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
+
+		/* cleanup */
+		switch (dev->alternate) {
+			char buf[12];
+		case 0:
+			sprintf(buf, "%lx", ds->ptr - ds->buf);
+			setenv("filesize", buf);
+			ds->ptr = ds->buf;
+			break;
+		case 1:
+			if (ds->ptr >
+			    ds->buf + sizeof(struct uboot_dfu_trailer)) {
+				struct uboot_dfu_trailer trailer;
+				dfu_trailer_mirror(&trailer, ds->ptr);
+				if (!dfu_trailer_matching(&trailer)) {
+					printf("DFU TRAILER NOT OK\n");
+					dev->dfu_state = DFU_STATE_dfuERROR;
+					dev->dfu_status = DFU_STATUS_errTARGET;
+					return RET_STALL;
+				}
+
+				rc = erase_flash_verify_nand(urb, ds,
+							ds->part->size,
+							ds->part_net_size);
+				/* re-write dynenv marker in OOB */
+				run_command("dynenv set u-boot_env", 0);
+			}
+			ds->nand = NULL;
+			free(ds->buf);
+			ds->ptr = ds->buf = ds->_buf;
+			break;
+		default:
+			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"))
+				rc = erase_tail_clean_nand(urb, ds);
+
+			ds->nand = NULL;
+			break;
+		}
+
+		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;
+	}
+
+	if (first && ds->buf && ds->buf != ds->_buf && ds->buf != LOAD_ADDR) {
+		free(ds->buf);
+		ds->buf = ds->_buf;
+	}
+
+	switch (dev->alternate) {
+	case 0:
+		if (first) {
+			printf("Starting DFU DOWNLOAD to RAM (0x%08p)\n",
+				LOAD_ADDR);
+			ds->buf = LOAD_ADDR;
+			ds->ptr = ds->buf;
+		}
+
+		memcpy(ds->ptr, urb->buffer, len);
+		ds->ptr += len;
+		break;
+	case 1:
+		if (first) {
+			rc = initialize_ds_nand(dev, ds);
+			if (rc)
+				return rc;
+			ds->buf = malloc(ds->part_net_size);
+			if (!ds->buf) {
+				printf("No memory for atomic buffer!!\n");
+				dev->dfu_state = DFU_STATE_dfuERROR;
+				dev->dfu_status = DFU_STATUS_errUNKNOWN;
+				return RET_STALL;
+			}
+			ds->ptr = ds->buf;
+			printf("Starting Atomic DFU DOWNLOAD to partition '%s'\n",
+				ds->part->name);
+		}
+
+		remain_len = (ds->buf + ds->part_net_size) - ds->ptr;
+		if (remain_len < len) {
+			len = remain_len;
+			printf("End of write exceeds partition end\n");
+			dev->dfu_state = DFU_STATE_dfuERROR;
+			dev->dfu_status = DFU_STATUS_errADDRESS;
+			return RET_STALL;
+		}
+		memcpy(ds->ptr, urb->buffer, len);
+		ds->ptr += len;
+		break;
+	default:
+		if (first) {
+			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);
+		}
+		break;
+	}
+
+	return RET_ZLP;
+}
+
+static int handle_upload(struct urb *urb, u_int16_t val, u_int16_t len, int first)
+{
+	struct usb_device_instance *dev = urb->device;
+	struct dnload_state *ds = &_dnstate;
+	unsigned int remain;
+	int rc;
+
+	debug("upload(val=0x%02x, len=%u, first=%u) ", val, len, first);
+
+	if (len > CONFIG_USBD_DFU_XFER_SIZE) {
+		/* Too big */
+		dev->dfu_state = DFU_STATE_dfuERROR;
+		dev->dfu_status = DFU_STATUS_errADDRESS;
+		//udc_ep0_send_stall();
+		debug("Error: Transfer size > CONFIG_USBD_DFU_XFER_SIZE ");
+		return -EINVAL;
+	}
+
+	switch (dev->alternate) {
+	case 0:
+		if (first) {
+			printf("Starting DFU Upload of RAM (0x%08p)\n",
+				LOAD_ADDR);
+			ds->ptr = ds->buf;
+		}
+
+		/* FIXME: end at some more dynamic point */
+		if (ds->ptr + len > LOAD_ADDR + 0x200000)
+			len = (LOAD_ADDR + 0x200000) - ds->ptr;
+
+		urb->buffer = ds->ptr;
+		urb->actual_length = len;
+		ds->ptr += len;
+		break;
+	default:
+		if (first) {
+			rc = initialize_ds_nand(dev, ds);
+			if (rc)
+				return -EINVAL;
+			printf("Starting DFU Upload of partition '%s'\n",
+				ds->part->name);
+			rc = read_next_nand(urb, ds);
+			if (rc)
+				return -EINVAL;
+		}
+
+		if (len > ds->nand->erasesize) {
+			printf("We don't support transfers bigger than %u\n",
+				ds->nand->erasesize);
+			len = ds->nand->erasesize;
+		}
+
+		remain = ds->nand->erasesize - (ds->ptr - ds->buf);
+		if (len < remain)
+			remain = len;
+
+		debug("copying %u bytes ", remain);
+		urb->buffer = ds->ptr;
+		ds->ptr += remain;
+		urb->actual_length = remain;
+
+		if (ds->ptr >= ds->buf + ds->nand->erasesize &&
+		    ds->off < ds->part->offset + ds->part->size) {
+			rc = read_next_nand(urb, ds);
+			if (rc)
+				return -EINVAL;
+			if (len > remain) {
+				debug("copying another %u bytes ", len - remain);
+				memcpy(urb->buffer + remain, ds->ptr, len - remain);
+				ds->ptr += (len - remain);
+				urb->actual_length += (len - remain);
+			}
+		}
+		break;
+	}
+
+	debug("returning len=%u\n", len);
+	return len;
+}
+
+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;
+
+	debug("getstatus ");
+
+	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:
+#if 0
+		if (fsr & AT91C_MC_PROGE) {
+			debug("errPROG ");
+			dev->dfu_status = DFU_STATUS_errPROG;
+			dev->dfu_state = DFU_STATE_dfuERROR;
+		} else if (fsr & AT91C_MC_LOCKE) {
+			debug("errWRITE ");
+			dev->dfu_status = DFU_STATUS_errWRITE;
+			dev->dfu_state = DFU_STATE_dfuERROR;
+		} else if (fsr & AT91C_MC_FRDY) {
+#endif
+			debug("DNLOAD_IDLE ");
+			dev->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
+#if 0
+		} else {
+			debug("DNBUSY ");
+			dev->dfu_state = DFU_STATE_dfuDNBUSY;
+		}
+#endif
+		break;
+	case DFU_STATE_dfuMANIFEST_SYNC:
+		break;
+	default:
+		//return;
+		break;
+	}
+
+	/* send status response */
+	dstat->bStatus = dev->dfu_status;
+	dstat->bState = dev->dfu_state;
+	dstat->iString = 0;
+	/* FIXME: set dstat->bwPollTimeout */
+	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, int max)
+{
+	debug("getstate ");
+
+	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);
+}
+
+#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 val = urb->device_request.wValue;
+	u_int16_t len = urb->device_request.wLength;
+	struct usb_device_instance *dev = urb->device;
+
+	debug("dfu_ep0(req=0x%x, val=0x%x, len=%u) old_state = %u ",
+		req, val, 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, len);
+			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, len);
+			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, val, len, 1);
+			break;
+		case USB_REQ_DFU_UPLOAD:
+			dev->dfu_state = DFU_STATE_dfuUPLOAD_IDLE;
+			handle_upload(urb, val, 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, len);
+			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, len);
+			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, val, 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, len);
+			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, len);
+			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, val, 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, len);
+			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, len);
+			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:
+		//udc_ep0_send_zlp();
+		urb->actual_length = 0;
+		return DFU_EP0_ZLP;
+		break;
+	case RET_STALL:
+		//udc_ep0_send_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;
+	}
+}
+
+
+#ifdef CONFIG_NAND_DYNPART
+
+void dfu_update_strings(void)
+{
+	int i;
+
+	if (!system_dfu_state) {
+		printf("NASTY SURPRISE: system_dfu_state not set\n");
+		return;
+	}
+
+	for (i = 1; i != DFU_NUM_ALTERNATES; i++) {
+		struct part_info *part = get_partition_nand(i-1);
+		struct usb_string_descriptor *strdesc, **slot;
+
+		if (part)
+			strdesc = create_usbstring(part->name);
+		else
+			strdesc = create_usbstring("undefined partition");
+		if (!strdesc)
+			continue;
+		slot = usb_strings+STR_COUNT+i+1;
+		if (*slot)
+			free(*slot);
+		*slot = strdesc;
+	}
+}
+
+#endif /* CONFIG_NAND_DYNPART */
+
+
+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;
+		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;
+
+	if (system_dfu_state)
+		printf("SURPRISE: system_dfu_state is already set\n");
+	system_dfu_state = &dev->dfu_state;
+
+	dfu_init_strings(dev);
+
+	return 0;
+}
+
+static int stdout_switched;
+
+/* event handler for usb device state events */
+void dfu_event(struct usb_device_instance *device,
+	       usb_device_event_t event, int data)
+{
+	char *out;
+
+	switch (event) {
+	case DEVICE_RESET:
+		switch (device->dfu_state) {
+		case DFU_STATE_appDETACH:
+			device->dfu_state = DFU_STATE_dfuIDLE;
+			out = getenv("stdout");
+			if (out && !strcmp(out, "usbtty")) {
+				setenv("stdout", "vga");
+				setenv("stderr", "vga");
+				stdout_switched = 1;
+			}
+			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");
+			if (stdout_switched) {
+				setenv("stdout", "usbtty");
+				setenv("stderr", "usbtty");
+				stdout_switched = 0;
+			}
+			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 */
Index: u-boot/drivers/usb/Makefile
===================================================================
--- u-boot.orig/drivers/usb/Makefile
+++ u-boot/drivers/usb/Makefile
@@ -28,6 +28,7 @@
 COBJS-y += isp116x-hcd.o
 COBJS-y += sl811_usb.o
 COBJS-y += usb_ohci.o
+COBJS-y += usbdfu.o
 COBJS-y += usbdcore.o
 COBJS-y += usbdcore_ep0.o
 COBJS-y += usbdcore_mpc8xx.o
Index: u-boot/drivers/usb/usbdcore.c
===================================================================
--- u-boot.orig/drivers/usb/usbdcore.c
+++ u-boot/drivers/usb/usbdcore.c
@@ -31,6 +31,7 @@
 
 #include <malloc.h>
 #include "usbdcore.h"
+#include <usb_dfu.h>
 
 #define MAX_INTERFACES 2
 
@@ -212,6 +213,10 @@
  */
 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);
 }
 
@@ -232,6 +237,10 @@
 	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);
 }
 
@@ -253,6 +262,13 @@
 	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;
 	}
@@ -681,4 +697,7 @@
 		/* usbdbg("calling device->event"); */
 		device->event(device, event, data);
 	}
+#ifdef CONFIG_USBD_DFU
+	dfu_event(device, event, data);
+#endif
 }
Index: u-boot/drivers/serial/usbtty.c
===================================================================
--- u-boot.orig/drivers/serial/usbtty.c
+++ u-boot/drivers/serial/usbtty.c
@@ -31,6 +31,8 @@
 #include "usbtty.h"
 #include "usb_cdc_acm.h"
 #include "usbdescriptors.h"
+#include <usb_dfu_descriptors.h>
+#include <usb_dfu.h>
 #include <config.h>		/* If defined, override Linux identifiers with
 			   	 * vendor specific ones */
 
@@ -106,7 +108,7 @@
 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};
@@ -157,6 +159,10 @@
 	struct usb_interface_descriptor data_class_interface;
 	struct usb_endpoint_descriptor
 		data_endpoints[NUM_ENDPOINTS-1] __attribute__((packed));
+#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] = {
@@ -167,7 +173,11 @@
     			.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 =
@@ -266,6 +276,11 @@
 				.bInterval		= 0xFF,
 			},
 		},
+#ifdef CONFIG_USBD_DFU
+		/* Interface 3 */
+		.uif_dfu = DFU_RT_IF_DESC,
+		.func_dfu = DFU_FUNC_DESC,
+#endif
 	},
 };
 
@@ -378,7 +393,7 @@
 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++){
@@ -654,6 +669,9 @@
 	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));
Index: u-boot/include/configs/neo1973_gta01.h
===================================================================
--- u-boot.orig/include/configs/neo1973_gta01.h
+++ u-boot/include/configs/neo1973_gta01.h
@@ -159,7 +159,7 @@
  */
 #define CONFIG_STACKSIZE	(128*1024)	/* regular stack */
 #ifdef CONFIG_USE_IRQ
-#define CONFIG_STACKSIZE_IRQ	(4*1024)	/* IRQ stack */
+#define CONFIG_STACKSIZE_IRQ	(8*1024)	/* IRQ stack */
 #define CONFIG_STACKSIZE_FIQ	(4*1024)	/* FIQ stack */
 #endif
 
@@ -176,6 +176,10 @@
 #define CONFIG_USBD_MANUFACTURER	"OpenMoko, Inc"
 #define CONFIG_USBD_PRODUCT_NAME	"Neo1973 Bootloader " U_BOOT_VERSION
 #define CONFIG_EXTRA_ENV_SETTINGS 	"usbtty=cdc_acm\0"
+#define CONFIG_USBD_DFU			1
+#define CONFIG_USBD_DFU_XFER_SIZE 	4096	/* 0x4000 */
+#define CONFIG_USBD_DFU_INTERFACE	2
+
 
 /*-----------------------------------------------------------------------
  * Physical Memory Map
Index: u-boot/include/usb_dfu.h
===================================================================
--- /dev/null
+++ u-boot/include/usb_dfu.h
@@ -0,0 +1,94 @@
+#ifndef _DFU_H
+#define _DFU_H
+
+/* USB Device Firmware Update Implementation for u-boot
+ * (C) 2007 by OpenMoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ *
+ * based on: USB Device Firmware Update Implementation for OpenPCD
+ * (C) 2006 by Harald Welte <hwelte@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 <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"
+#define CONFIG_DFU_ALT0_STR	"RAM 0x32000000"
+
+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
+
+extern volatile enum dfu_state *system_dfu_state; /* for 3rd parties */
+
+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 */
Index: u-boot/include/usb_dfu_descriptors.h
===================================================================
--- /dev/null
+++ u-boot/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 <hwelte@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 */
Index: u-boot/include/usbdcore.h
===================================================================
--- u-boot.orig/include/usbdcore.h
+++ u-boot/include/usbdcore.h
@@ -33,6 +33,7 @@
 
 #include <common.h>
 #include "usbdescriptors.h"
+#include <usb_dfu_descriptors.h>
 
 
 #define MAX_URBS_QUEUED 5
@@ -475,7 +476,11 @@
  * 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;
@@ -603,6 +608,12 @@
 	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
@@ -632,6 +643,8 @@
 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);
Index: u-boot/drivers/serial/usbtty.h
===================================================================
--- u-boot.orig/drivers/serial/usbtty.h
+++ u-boot/drivers/serial/usbtty.h
@@ -70,4 +70,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
Index: u-boot/include/configs/qt2410.h
===================================================================
--- u-boot.orig/include/configs/qt2410.h
+++ u-boot/include/configs/qt2410.h
@@ -191,7 +191,8 @@
 #define CONFIG_USBD_PRODUCT_NAME	"QT2410 Bootloader " U_BOOT_VERSION
 #define CONFIG_EXTRA_ENV_SETTINGS	"usbtty=cdc_acm\0"
 #define CONFIG_USBD_DFU			1
-#define CONFIG_USBD_DFU_XFER_SIZE	0x4000
+#define CONFIG_USBD_DFU_XFER_SIZE	4096
+#define CONFIG_USBD_DFU_INTERFACE	2
 
 /*-----------------------------------------------------------------------
  * Physical Memory Map
Index: u-boot/tools/Makefile
===================================================================
--- u-boot.orig/tools/Makefile
+++ u-boot/tools/Makefile
@@ -21,10 +21,10 @@
 # MA 02111-1307 USA
 #
 
-BIN_FILES	= img2srec$(SFX) mkimage$(SFX) envcrc$(SFX) ubsha1$(SFX) gen_eth_addr$(SFX) bmp_logo$(SFX)
+BIN_FILES	= img2srec$(SFX) mkimage$(SFX) envcrc$(SFX) ubsha1$(SFX) gen_eth_addr$(SFX) bmp_logo$(SFX) mkudfu$(SFX)
 
 OBJ_LINKS	= environment.o crc32.o sha1.o
-OBJ_FILES	= img2srec.o mkimage.o envcrc.o ubsha1.o gen_eth_addr.o bmp_logo.o
+OBJ_FILES	= img2srec.o mkimage.o envcrc.o ubsha1.o gen_eth_addr.o bmp_logo.o mkudfu.o
 
 ifeq ($(ARCH),mips)
 BIN_FILES	+= inca-swap-bytes$(SFX)
@@ -141,6 +141,10 @@
 		$(CC) $(CFLAGS) $(HOST_LDFLAGS) -o $@ $^
 		$(STRIP) $@
 
+$(obj)mkudfu$(SFX):	$(obj)mkudfu.o
+		$(CC) $(CFLAGS) $(HOST_LDFLAGS) -o $@ $^
+		$(STRIP) $@
+
 $(obj)ncb$(SFX):	$(obj)ncb.o
 		$(CC) $(CFLAGS) $(HOST_LDFLAGS) -o $@ $^
 		$(STRIP) $@
Index: u-boot/tools/mkudfu.c
===================================================================
--- /dev/null
+++ u-boot/tools/mkudfu.c
@@ -0,0 +1,314 @@
+/*
+ * USB DFU file trailer tool
+ * (C) Copyright by OpenMoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ *
+ * based on mkimage.c, copyright information as follows:
+ *
+ * (C) Copyright 2000-2004
+ * DENX Software Engineering
+ * Wolfgang Denk, wd@denx.de
+ * All rights reserved.
+ *
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef __WIN32__
+#include <netinet/in.h>		/* for host / network byte order conversions	*/
+#endif
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#if defined(__BEOS__) || defined(__NetBSD__) || defined(__APPLE__)
+#include <inttypes.h>
+#endif
+
+#ifdef __WIN32__
+typedef unsigned int __u32;
+
+#define SWAP_LONG(x) \
+	((__u32)( \
+		(((__u32)(x) & (__u32)0x000000ffUL) << 24) | \
+		(((__u32)(x) & (__u32)0x0000ff00UL) <<  8) | \
+		(((__u32)(x) & (__u32)0x00ff0000UL) >>  8) | \
+		(((__u32)(x) & (__u32)0xff000000UL) >> 24) ))
+typedef		unsigned char	uint8_t;
+typedef		unsigned short	uint16_t;
+typedef		unsigned int	uint32_t;
+
+#define     ntohl(a)	SWAP_LONG(a)
+#define     htonl(a)	SWAP_LONG(a)
+#endif	/* __WIN32__ */
+
+#ifndef	O_BINARY		/* should be define'd on __WIN32__ */
+#define O_BINARY	0
+#endif
+
+#include <usb_dfu_trailer.h>
+
+extern int errno;
+
+#ifndef MAP_FAILED
+#define MAP_FAILED (-1)
+#endif
+
+static char *cmdname;
+
+static char *datafile;
+static char *imagefile;
+
+
+static void usage()
+{
+	fprintf (stderr, "%s - create / display u-boot DFU trailer\n", cmdname);
+	fprintf (stderr, "Usage: %s -l image\n"
+			 "          -l ==> list image header information\n"
+			 "       %s -v VID -p PID -r REV -d data_file image\n",
+		cmdname, cmdname);
+	fprintf (stderr, "          -v ==> set vendor ID to 'VID'\n"
+			 "          -p ==> set product ID system to 'PID'\n"
+			 "          -r ==> set hardware revision to 'REV'\n"
+			 "          -d ==> use 'data_file' as input file\n"
+		);
+	exit (EXIT_FAILURE);
+}
+
+static void print_trailer(struct uboot_dfu_trailer *trailer)
+{
+	printf("===> DFU Trailer information:\n");
+	printf("Trailer Vers.:	%d\n", trailer->version);
+	printf("Trailer Length:	%d\n", trailer->length);
+	printf("VendorID:	0x%04x\n", trailer->vendor);
+	printf("ProductID:	0x%04x\n", trailer->product);
+	printf("HW Revision:	0x%04x\n", trailer->revision);
+}
+
+static void copy_file (int ifd, const char *datafile, int pad)
+{
+	int dfd;
+	struct stat sbuf;
+	unsigned char *ptr;
+	int tail;
+	int zero = 0;
+	int offset = 0;
+	int size;
+
+	if ((dfd = open(datafile, O_RDONLY|O_BINARY)) < 0) {
+		fprintf (stderr, "%s: Can't open %s: %s\n",
+			cmdname, datafile, strerror(errno));
+		exit (EXIT_FAILURE);
+	}
+
+	if (fstat(dfd, &sbuf) < 0) {
+		fprintf (stderr, "%s: Can't stat %s: %s\n",
+			cmdname, datafile, strerror(errno));
+		exit (EXIT_FAILURE);
+	}
+
+	ptr = (unsigned char *)mmap(0, sbuf.st_size,
+				    PROT_READ, MAP_SHARED, dfd, 0);
+	if (ptr == (unsigned char *)MAP_FAILED) {
+		fprintf (stderr, "%s: Can't read %s: %s\n",
+			cmdname, datafile, strerror(errno));
+		exit (EXIT_FAILURE);
+	}
+
+	size = sbuf.st_size - offset;
+	if (write(ifd, ptr + offset, size) != size) {
+		fprintf (stderr, "%s: Write error on %s: %s\n",
+			cmdname, imagefile, strerror(errno));
+		exit (EXIT_FAILURE);
+	}
+
+	if (pad && ((tail = size % 4) != 0)) {
+
+		if (write(ifd, (char *)&zero, 4-tail) != 4-tail) {
+			fprintf (stderr, "%s: Write error on %s: %s\n",
+				cmdname, imagefile, strerror(errno));
+			exit (EXIT_FAILURE);
+		}
+	}
+
+	(void) munmap((void *)ptr, sbuf.st_size);
+	(void) close (dfd);
+}
+
+
+int main(int argc, char **argv)
+{
+	int ifd;
+	int lflag = 0;
+	struct stat sbuf;
+	u_int16_t opt_vendor, opt_product, opt_revision;
+	struct uboot_dfu_trailer _hdr, _mirror, *hdr = &_hdr;
+
+	opt_vendor = opt_product = opt_revision = 0;
+
+	cmdname = *argv;
+
+	while (--argc > 0 && **++argv == '-') {
+		while (*++*argv) {
+			switch (**argv) {
+			case 'l':
+				lflag = 1;
+				break;
+			case 'v':
+				if (--argc <= 0)
+					usage ();
+				opt_vendor = strtoul(*++argv, NULL, 16);
+				goto NXTARG;
+			case 'p':
+				if (--argc <= 0)
+					usage ();
+				opt_product = strtoul(*++argv, NULL, 16);
+				goto NXTARG;
+			case 'r':
+				if (--argc <= 0)
+					usage ();
+				opt_revision = strtoul(*++argv, NULL, 16);
+				goto NXTARG;
+			case 'd':
+				if (--argc <= 0)
+					usage ();
+				datafile = *++argv;
+				goto NXTARG;
+			case 'h':
+				usage();
+				break;
+			default:
+				usage();
+			}
+		}
+NXTARG:		;
+	}
+
+	if (argc != 1)
+		usage();
+
+	imagefile = *argv;
+
+	if (lflag)
+		ifd = open(imagefile, O_RDONLY|O_BINARY);
+	else
+		ifd = open(imagefile, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0666);
+
+	if (ifd < 0) {
+		fprintf (stderr, "%s: Can't open %s: %s\n",
+			cmdname, imagefile, strerror(errno));
+		exit (EXIT_FAILURE);
+	}
+
+	if (lflag) {
+		unsigned char *ptr;
+		/* list header information of existing image */
+		if (fstat(ifd, &sbuf) < 0) {
+			fprintf (stderr, "%s: Can't stat %s: %s\n",
+				cmdname, imagefile, strerror(errno));
+			exit (EXIT_FAILURE);
+		}
+
+		if ((unsigned)sbuf.st_size < sizeof(struct uboot_dfu_trailer)) {
+			fprintf (stderr,
+				"%s: Bad size: \"%s\" is no valid image\n",
+				cmdname, imagefile);
+			exit (EXIT_FAILURE);
+		}
+
+		ptr = (unsigned char *)mmap(0, sbuf.st_size,
+					    PROT_READ, MAP_SHARED, ifd, 0);
+		if ((caddr_t)ptr == (caddr_t)-1) {
+			fprintf (stderr, "%s: Can't read %s: %s\n",
+				cmdname, imagefile, strerror(errno));
+			exit (EXIT_FAILURE);
+		}
+
+		dfu_trailer_mirror(hdr, ptr+sbuf.st_size);
+
+		if (hdr->magic != UBOOT_DFU_TRAILER_MAGIC) {
+			fprintf (stderr,
+				"%s: Bad Magic Number: \"%s\" is no valid image\n",
+				cmdname, imagefile);
+			exit (EXIT_FAILURE);
+		}
+
+		/* for multi-file images we need the data part, too */
+		print_trailer(hdr);
+
+		(void) munmap((void *)ptr, sbuf.st_size);
+		(void) close (ifd);
+
+		exit (EXIT_SUCCESS);
+	}
+
+	/* if we're not listing: */
+
+	copy_file (ifd, datafile, 0);
+
+	memset (hdr, 0, sizeof(struct uboot_dfu_trailer));
+
+	/* Build new header */
+	hdr->version	= UBOOT_DFU_TRAILER_V1;
+	hdr->magic	= UBOOT_DFU_TRAILER_MAGIC;
+	hdr->length	= sizeof(struct uboot_dfu_trailer);
+	hdr->vendor	= opt_vendor;
+	hdr->product	= opt_product;
+	hdr->revision	= opt_revision;
+
+	print_trailer(hdr);
+	dfu_trailer_mirror(&_mirror, (unsigned char *)hdr+sizeof(*hdr));
+
+	if (write(ifd, &_mirror, sizeof(struct uboot_dfu_trailer))
+					!= sizeof(struct uboot_dfu_trailer)) {
+		fprintf (stderr, "%s: Write error on %s: %s\n",
+			cmdname, imagefile, strerror(errno));
+		exit (EXIT_FAILURE);
+	}
+
+	/* We're a bit of paranoid */
+#if defined(_POSIX_SYNCHRONIZED_IO) && !defined(__sun__) && !defined(__FreeBSD__)
+	(void) fdatasync (ifd);
+#else
+	(void) fsync (ifd);
+#endif
+
+	if (fstat(ifd, &sbuf) < 0) {
+		fprintf (stderr, "%s: Can't stat %s: %s\n",
+			cmdname, imagefile, strerror(errno));
+		exit (EXIT_FAILURE);
+	}
+
+	/* We're a bit of paranoid */
+#if defined(_POSIX_SYNCHRONIZED_IO) && !defined(__sun__) && !defined(__FreeBSD__)
+	(void) fdatasync (ifd);
+#else
+	(void) fsync (ifd);
+#endif
+
+	if (close(ifd)) {
+		fprintf (stderr, "%s: Write error on %s: %s\n",
+			cmdname, imagefile, strerror(errno));
+		exit (EXIT_FAILURE);
+	}
+
+	exit (EXIT_SUCCESS);
+}
Index: u-boot/include/usb_dfu_trailer.h
===================================================================
--- /dev/null
+++ u-boot/include/usb_dfu_trailer.h
@@ -0,0 +1,31 @@
+#ifndef _USB_DFU_TRAILER_H
+#define _USB_DFU_TRAILER_H
+
+/* trailer handling for DFU files */
+
+#define UBOOT_DFU_TRAILER_V1	1
+#define UBOOT_DFU_TRAILER_MAGIC	0x19731978
+struct uboot_dfu_trailer {
+	u_int32_t	magic;
+	u_int16_t	version;
+	u_int16_t	length;
+	u_int16_t	vendor;
+	u_int16_t	product;
+	u_int32_t	revision;
+} __attribute__((packed));
+
+/* we mirror the trailer because we want it to be longer in later versions
+ * while keeping backwards compatibility */
+static inline void dfu_trailer_mirror(struct uboot_dfu_trailer *trailer,
+				      unsigned char *eof)
+{
+	int i;
+	int len = sizeof(struct uboot_dfu_trailer);
+	unsigned char *src = eof - len;
+	unsigned char *dst = (unsigned char *) trailer;
+
+	for (i = 0; i < len; i++)
+		dst[len-1-i] = src[i];
+}
+
+#endif /* _USB_DFU_TRAILER_H */
Index: u-boot/Makefile
===================================================================
--- u-boot.orig/Makefile
+++ u-boot/Makefile
@@ -298,6 +298,12 @@
 $(obj)u-boot.bin:	$(obj)u-boot
 		$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
 
+$(obj)u-boot.udfu:	$(obj)u-boot.bin
+		./tools/mkudfu -v $(CONFIG_USB_DFU_VENDOR) \
+			       -p $(CONFIG_USB_DFU_PRODUCT) \
+			       -r $(CONFIG_USB_DFU_REVISION) \
+			       -d $< $@
+
 $(obj)u-boot.img:	$(obj)u-boot.bin
 		./tools/mkimage -A $(ARCH) -T firmware -C none \
 		-a $(TEXT_BASE) -e 0 \
Index: u-boot/board/neo1973/gta01/split_by_variant.sh
===================================================================
--- u-boot.orig/board/neo1973/gta01/split_by_variant.sh
+++ u-boot/board/neo1973/gta01/split_by_variant.sh
@@ -15,37 +15,44 @@
 	echo "$0:: No parameters - using GTA01Bv3 config"
 	echo "#define CONFIG_ARCH_GTA01B_v3" > $CFGINC
 	echo "GTA01_BIG_RAM=y" > $CFGTMP
+	echo "CONFIG_USB_DFU_REVISION=0x0230" > $CFGTMP
 else
 	case "$1" in
 	gta01v4_config)
 	echo "#define CONFIG_ARCH_GTA01_v4" > $CFGINC
 	echo "GTA01_BIG_RAM=n" > $CFGTMP
+	echo "CONFIG_USB_DFU_REVISION=0x0140" > $CFGTMP
 	;;
 
 	gta01v3_config)
 	echo "#define CONFIG_ARCH_GTA01_v3" > $CFGINC
 	echo "GTA01_BIG_RAM=n" > $CFGTMP
+	echo "CONFIG_USB_DFU_REVISION=0x0130" > $CFGTMP
 	;;
 
 	gta01bv2_config)
 	echo "#define CONFIG_ARCH_GTA01B_v2" > $CFGINC
 	echo "GTA01_BIG_RAM=y" > $CFGTMP
+	echo "CONFIG_USB_DFU_REVISION=0x0220" > $CFGTMP
 	;;
 
 	gta01bv3_config)
 	echo "#define CONFIG_ARCH_GTA01B_v3" > $CFGINC
 	echo "GTA01_BIG_RAM=y" > $CFGTMP
+	echo "CONFIG_USB_DFU_REVISION=0x0230" > $CFGTMP
 	;;
 
 	gta01bv4_config)
 	echo "#define CONFIG_ARCH_GTA01B_v4" > $CFGINC
 	echo "GTA01_BIG_RAM=y" > $CFGTMP
+	echo "CONFIG_USB_DFU_REVISION=0x0240" > $CFGTMP
 	;;
 
 	*)
 	echo "$0:: Unrecognised config - using GTA01Bv4 config"
 	echo "#define CONFIG_ARCH_GTA01B_v4" > $CFGINC
 	echo "GTA01_BIG_RAM=y" > $CFGTMP
+	echo "CONFIG_USB_DFU_REVISION=0x0240" > $CFGTMP
 	;;
 
 	esac
Index: u-boot/board/neo1973/gta01/config.mk
===================================================================
--- u-boot.orig/board/neo1973/gta01/config.mk
+++ u-boot/board/neo1973/gta01/config.mk
@@ -24,6 +24,9 @@
 #
 # download area is 3200'0000 or 3300'0000
 
+CONFIG_USB_DFU_VENDOR=0x1457
+CONFIG_USB_DFU_PRODUCT=0x5119
+
 sinclude $(OBJTREE)/board/$(BOARDDIR)/config.tmp
 
 ifeq ($(GTA01_BIG_RAM),y)
