diff mbox

[U-Boot,v2,2/4] USB DFU driver added

Message ID 1297633945-15658-1-git-send-email-korgull@home.nl
State Changes Requested
Delegated to: Remy Bohmer
Headers show

Commit Message

Marcel Janssen Feb. 13, 2011, 9:52 p.m. UTC
From: Marcel <korgull@home.nl>

USB DFU driver cleaning phase1

USB DFU driver cleaning phase2

USB DFU driver cleaning phase3

USB DFU driver cleaning phase4

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

Comments

Remy Bohmer Feb. 15, 2011, 7:34 p.m. UTC | #1
Hi,

2011/2/13 Marcel Janssen <korgull@home.nl>:
> From: Marcel <korgull@home.nl>
>
> USB DFU driver cleaning phase1
>
> USB DFU driver cleaning phase2
>
> USB DFU driver cleaning phase3
>
> USB DFU driver cleaning phase4

Not a very descriptive patch header. Please fix this.

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

> diff --git a/doc/README.dfu b/doc/README.dfu
> new file mode 100644
> index 0000000..363c7a2
> --- /dev/null
> +++ b/doc/README.dfu
> @@ -0,0 +1,129 @@
> +USBD DFU mode
> +
> +Initially written by Marcel Janssen, Admesy B.V.
> +Based on parts from OpenMoko, ether.c and update.c
> +
> +

No useless double empty lines (globally)

> +========================================
> +
> +This describes the DFU implementation in u-boot.
> +
> +The implementation works with dfu-utils to upgrade NAND partitions defined by
> +mtdparts.
> +The board configuration file needs serveral CONFIG options to be set.
> +DFU is implemented to be executed as a command "dfu" (common/update_dfu.c).
> +This command should start the USB device controller and the DFU driver.
> +A typical implementation would be that a script is executed, that will check
> +whether DFU should be started. If so, it can execute 'dfu" and the device will
> +announce itself to the host as a DFU capable device.
> +dfu-util can than be used to upgrade the partitions defined by mtdparts.
> +
> +Description of flow :
> +dfu-utils sets the alternate interface which corresponds to the selected
> +partition.
> +The file (uImage, rootfs.arm.jffs2) is loaded fully to RAM first.
> +U-boot nand routines are used to write from RAM to NAND.
> +
> +LED usage :
> +Status LED's can be defined to show DFU action.
> +Define the RED and GREEN leds to make this happen.

LEDs are board specific, please do not use that in generic driver code.

> +
> +Initial testing example :
> +This was done on the in-circuit icnova_sam9g45 board.
> +This board uses atmel_usbd_udc.c
> +
> +========================================
> +
> +
> +

useles empty lines

> +To make DFU work you need a working USB controller, for example at91_udc or
> +atmel_usba_udc. Make sure to set it in the board config file.
> +
> +========================================
> +USBD CONFIG options
> +----------------------------------------
> +
> +#define CONFIG_USB_GADGET
> +#define CONFIG_USB_GADGET_ATMEL_USBA   (or  #define CONFIG_USB_GADGET_AT91_UDC )
> +#define CONFIG_USB_GADGET_DUALSPEED
> +
> +----------------------------------------
> +USBD CONFIG options end
> +========================================
> +
> +

empty lines

> +========================================
> +DFU CONFIG options
> +----------------------------------------
> +
> +#define CONFIG_USBD_DFU                 1
> +#ifdef  CONFIG_USBD_DFU
> +#define CONFIG_USBD_VENDORID           0x23CF     /* Admesy */
> +#define CONFIG_USBD_PRODUCTID_DFU       0x0100     /* donated number */
> +#define CONFIG_USBD_MANUFACTURER       "Admesy"
> +#define CONFIG_USBD_PRODUCT_NAME       "Admesy DFU 001"
> +#define CONFIG_USBD_DFU_XFER_SIZE      4096       /* Buffer size */
> +#define CONFIG_USBD_DFU_INTERFACE       0
> +#define DFU_NUM_ALTERNATES             3          /* 3 partitions */
> +#define LOAD_ADDR ((unsigned char *)0x70400000)    /* RAM address to use */
> +#endif
> +
> +----------------------------------------
> +DFU CONFIG options end
> +========================================
> +
> +
> +

empty lines

> +In order to make DFU work with dfu-utils, mtdparts need to be defined.
> +See the example futher below on how to do this.
> +
> +========================================
> +mtdparts example with dfu-utils
> +----------------------------------------
> +
> +mtdparts add nand0 0x2000000 kernel
> +mtdparts add nand0 0x1000000@0x00200000 root
> +mtdparts add nand0 0xEE00000@0x01200000 data
> +saveenv
> +
> +Your mtdparts than should look like this :
> +
> +board> mtdparts
> +device nand0 <nand.0>, # parts = 3
> + #: name               size            offset          mask_flags
> + 0: kernel              0x00200000     0x00000000      0
> + 1: root                0x01000000     0x00200000      0
> + 2: data                0x0ee00000     0x01200000      0
> +
> +active partition: nand0,0 - (kernel) 0x00200000 @ 0x00000000
> +
> +
> +After the mtdparts have been defined, dfu-utils can be used to upgrade the
> +kernel and root partition.
> +Make sure you have read/write access to the DFU device !
> +
> +cd dfu-utils/src
> +./dfu-util -a0 -D uImage
> +./dfu-util -a1 -D rootfs.arm.jffs2 -R
> +
> +----------------------------------------
> +mtdparts example end
> +========================================
> +
> +
> +

empty lines

> +Possible changes for the near future :
> +1) integrate DFU and Ethernet and/or tty by making it a composite driver.
> +2) allow partitions larger than RAM by implementing per-buffer NAND writing.
> +   Openmoko does this, but I didn't need it and liked to write to RAM first.
> +3) Allow DFU to flash NOR (could be added to handle_dnload )
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +

enough said....

> diff --git a/drivers/usb/gadget/usbdfu.c b/drivers/usb/gadget/usbdfu.c
> new file mode 100644
> index 0000000..65f334a
> --- /dev/null
> +++ b/drivers/usb/gadget/usbdfu.c
> +#include <linux/ctype.h>
> +#include <linux/porting-compat.h>
> +
> +#include <nand.h>
> +#include <jffs2/load_kernel.h>
> +
> +extern struct list_head devices;

No extern's please

> +
> +#define RET_NOTHING    0
> +#define RET_ZLP                1
> +#define RET_STALL      2
> +
> +#ifndef CONFIG_USBD_DFU_XFER_SIZE
> +#define CONFIG_USBD_DFU_XFER_SIZE      4096
> +#endif
> +
> +/* this should be done differently */
> +#define EP0_MAX_PACKET_SIZE    64 /* MUSB_EP0_FIFOSIZE */
> +
> +unsigned char ledcount;

Drop this led support

> +#define USB_CONNECT_TIMEOUT (30 * CONFIG_SYS_HZ)
> +enum dfu_state *system_dfu_state; /* for 3rd parties */
> +
> +char rtm_dfu;

If used only locally n this file, please make it 'static'

> +#define LOAD_ADDR ((unsigned char *)0x70400000)

My board does not have RAM on this location...
And even if there is RAM, How do you know it is not occupied by something else?

> +struct dnload_state {
> +       nand_info_t *nand;
> +       struct part_info *part;
> +       unsigned int part_net_size;     /* net size (excl. bad blocks) */
> +       nand_erase_options_t erase_opts;
> +       nand_write_options_t write_opts;
> +       nand_read_options_t read_opts;
> +       unsigned char *ptr;     /* pointer to next empty byte in buffer */
> +       unsigned int off;       /* offset of current erase page in flash chip*/
> +       unsigned char *buf;     /* pointer to allocated erase page buffer */
> +       /* unless doing an atomic transfer, we use the static buffer below.
> +        * This saves us from having to clean up dynamic allications in the
> +        * various error paths of the code.  Also, it will always work, no
> +        * matter what the memory situation is. */
> +       unsigned char _buf[0x20000];    /*FIXME : depends on flash page size */

Then use malloc for this

> +       if (first) {
> +               /* Make sure that we have a valid mtd partition table */
> +               char *mtdp = getenv("mtdparts");

Why directly write to flash?
Just load the image first to RAM, from which the user can store it to
flash. (And if the user can script that it would work on more
configurations)
You seem to mix up driver code with some type of application.

> +               if (mtdp)
> +                       printf("Valid MTD partitions found\n");
> +               /*this used to be in the Openmoko driver */
> +               /*if (!mtdp)
> +                       /*run_command("dynpart", 0); */

No dead code please.

> +               else {
> +                       dev->dfu_state = DFU_STATE_dfuERROR;
> +                       dev->dfu_status = DFU_STATUS_errADDRESS;

Const defines in capitals please.

> +                       return RET_STALL;
> +               }
> +       }
> +
> +       if (len == 0) {
> +               debug("zero-size write -> MANIFEST_SYNC ");
> +               dev->dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
> +
> +               /* cleanup */
> +               switch (dev->alternate) {
> +                       char buf[12];
> +                       /* we don't actually handle partitions differently

Bad multiline comment

> +                        * the original Openmoko driver did use the alternate
> +                        * setting to do different things we have no need for
> +                        * that a big difference is that our driver first reads
> +                        * the full firmware to RAM and than writes it to flash
> +                       */
> +               default:
> +                       sprintf(buf, "0x%08x", ds->ptr - ds->buf);
> +                       addr = (ulong)LOAD_ADDR;
> +                       rwsize = ds->ptr - ds->buf;
> +                       printf("Filesize = %s\n", buf);
> +                       setenv("filesize", buf);
> +                       rc = 0;
> +                       rc = mtdparts_init();
> +                       if (rc) {
> +                               printf("Error initilizing mtdparts\n");
> +                               break;
> +                       }
> +                       ds->part =  get_partition_nand(dev->alternate);
> +                       if (ds->part == NULL) {
> +                               printf("Error reading partition info\n");
> +                               break;
> +                       }

What if I do not have NAND on my board...
Remove this mtd related code, use store in RAM, let the user decide
how to handle it from there.

> +
> +                       ds->erase_opts.offset = ds->part->offset;
> +                       ds->erase_opts.length = ds->part->size;
> +                       if (!rc && !strcmp(ds->part->name, "root"))
> +                               ds->erase_opts.jffs2  = 1;
> +                       else
> +                               ds->erase_opts.jffs2  = 0;
> +                       ds->erase_opts.quiet  = 0;
> +                       ds->erase_opts.spread = 1;
> +                       ds->erase_opts.scrub = 0; /* do not allow this */
> +                       ds->nand = &nand_info[ds->part->dev->id->num];
> +
> +                       printf("Using partition : %s ,", ds->part->name);

The partitioning of my board is probably different than you expect.

> +                       if (ds->erase_opts.jffs2)
> +                               printf(" with jffs2 option\n");
> +                       else
> +                               printf(" without jffs2 option\n");
> +                               printf("Partition size=%lx offset=%lx\n",
> +                               (unsigned long)ds->part->size,
> +                               (unsigned long)ds->part->offset);
> +#ifdef CONFIG_GREEN_LED
> +       green_LED_off();
> +#endif
> +#ifdef CONFIG_RED_LED
> +       red_LED_off();
> +#endif

No LED control please. My board could contain a single led which my be
used for different purposes.

> +                       rc = nand_erase_opts(ds->nand, &ds->erase_opts);
> +                       if (rc) {
> +                               printf("NAND erase failed\n");
> +                               break;
> +                       } else
> +                               printf("NAND erased succesfully\n");
> +#ifdef CONFIG_GREEN_LED
> +       green_LED_on();
> +#endif
> +#ifdef CONFIG_RED_LED
> +       red_LED_off();
> +#endif
> +                       rc = nand_write_skip_bad(ds->nand,
> +                                                ds->part->offset,
> +                                                &rwsize,
> +                                                (u_char *)addr);

Hmm, I might want it in a UBIFS or Yaffs partition... Get the picture?

> +       /* Handle vendor specific code

Bad multiline comment

> +        * this is an example how to reset u-boot by sending a

An example does not belong in generic driver code.

> +        * control message. This can be useful in case
> +        * the USB reset fails for whatever reason.
> +       */
> +       if ((requesttype == 0x41) && (request == 0x08)) {
> +               printf("Device reset called\n");
> +               do_reset(NULL, 0, 0, NULL);
> +       }


> +       switch (request) {
> +
> +       case USB_REQ_GET_DESCRIPTOR:

No empty line.

> +               if (ctrl->bRequestType != USB_DIR_IN)
> +                       break;
> +
> +               switch (wValue >> 8) {
> +
> +               case USB_DT_DEVICE:

Apparently globally: no empty lines

> +       default:
> +               printf("unknown control req%02x.%02x v%04x i%04x l%d\n",
> +                       ctrl->bRequestType, ctrl->bRequest,
> +                       wValue, wIndex, wLength);
> +               return DFU_EP0_UNHANDLED;
> +               break;
> +       }
> +
> +       }

Indentation garbage... (2 closing brackets on the same indentation)


> diff --git a/include/usb_dfu.h b/include/usb_dfu.h
> new file mode 100644
> index 0000000..e232a7e
> --- /dev/null
> +++ b/include/usb_dfu.h
> +extern enum dfu_state *system_dfu_state; /* for 3rd parties */

Why globally export data structures?

> +#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

Constant defines in all capitals please

> +
> +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,

Same here.

I am sorry, but I have too many review remarks here, among which some
really fundamental design flaws (Like assumption on NAND availability
and partitioning, memory addresses). This code makes a lot of
assumptions on a certain type of application, and it even contains
board knowledge inside a generic DFU-USB device driver. Therefore this
code is non portable across different architectures, and it is not
even portable between 2 different AT91 boards. So, I have to give it a
full NAK at this time.

Kind regards,

Remy
diff mbox

Patch

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