[U-Boot] [PATCH] dfu: initial implementation

Andrzej Pietrasiewicz andrzej.p at samsung.com
Wed Nov 2 11:00:07 CET 2011


Signed-off-by: Andrzej Pietrasiewicz <andrzej.p at samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park at samsung.com>
---

Dear All,

This is Device Firmware Upgrade (DFU) implementation which supports data
upload and download function to devices which are equipped with a UDC.

Device Firmware Upgrade (DFU) is an extension to the USB specification.
As of the time of this writing it is documented at

http://www.usb.org/developers/devclass_docs/DFU_1.1.pdf

The aim of DFU is to allow transferring data to/from selected areas of
interest within a compliant device. In the DFU nomenclature the host->device
direction is called download and the device->host direction is called
upload. The areas are defined by the compliant device. In the DFU
nomenclature the area of interest within the device is called an altsetting.
The device is connected to the host through USB. On the host side compliant
software must be used to initiate the data transfer. Example piece of such
software is dfu-util package from the OpenMoko project:

http://wiki.openmoko.org/wiki/Dfu-util.

Please refer to dfu-util documentation for details.
The below ASCII-art presents a connection between the host and the device.

      +-----------------+    <--- DFU --->    +-------------------------+
      |  e.g. dfu-util  |    <=== USB ===>    |          / altsetting 0 |
      |          host   +---------------------+   device - altsetting 1 |
      |   file /        |                     |          \     ...      |
      +-----------------+                     +-------------------------+

The DFU implementation on the device side remains in one of the two modes:
the Run-Time mode and the DFU mode. The USB descriptor sets exported by
the device depend on which mode is in force. While in DFU mode the device
exports the descriptors corresponding to each available altsetting. An
example dfu-util session when the device is in the DFU mode looks similar
to this:

# dfu-util -l
dfu-util - (C) 2007-2008 by OpenMoko Inc.
This program is Free Software and has ABSOLUTELY NO WARRANTY

Found DFU: [0x0525:0xffff] devnum=46, cfg=0, intf=0, alt=0, name="bootldr"
Found DFU: [0x0525:0xffff] devnum=46, cfg=0, intf=0, alt=1, name="kernel"

In the above example there are two altsettings, altsetting 0 with a
human-readable name "bootldr" and altsetting 1 with a human-readable name
"kernel"

To initiate data transfer the user at the host side must specify the
altsetting to/from which the data transfer is to be performed. In case of
the dfu-util it is specified with a "-a" option followed by either a number
or a human-readable name. The user also needs to specify the direction of
the data transfer and the file on the host from/to which data are to be
transfered. An example download command line looks similar to this:

# dfu-util -D vmlinux -a kernel

In the above command line the contents of the file vmlinux is to be
downloaded into the altsetting named kernel.

An example upload command line looks similar to this:

# dfu-util -U vmlinux -a kernel

In the above command line the contents of the altsetting named kernel is to
be uploaded into the file vmlinux.

This u-boot implementation introduces a parameter-less dfu command.
After the user finishes with dfu they can Ctrl-C to return to u-boot main
menu.

The implementation is split into two parts: the dfu gadget implementation
and a "flashing backend", the interface between the two being the
struct flash_entity. The "flashing backend"'s implementation is
board-specific and is implemented on the Goni target.

Patch summary:

Andrzej Pietrasiewicz (1):
  dfu: initial implementation

 board/samsung/goni/Makefile |    2 +
 board/samsung/goni/flash.c  |  634 +++++++++++++++++++++++++++++
 board/samsung/goni/flash.h  |   28 ++
 board/samsung/goni/goni.c   |   17 +
 common/Makefile             |    1 +
 common/cmd_dfu.c            |   50 +++
 drivers/usb/gadget/Makefile |    1 +
 drivers/usb/gadget/dfu.c    |  920 +++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/dfu.h    |  171 ++++++++
 include/configs/s5p_goni.h  |    6 +
 include/dfu.h               |   31 ++
 include/flash_entity.h      |   39 ++
 include/mbr.h               |   49 +++
 13 files changed, 1949 insertions(+), 0 deletions(-)
 create mode 100644 board/samsung/goni/flash.c
 create mode 100644 board/samsung/goni/flash.h
 create mode 100644 common/cmd_dfu.c
 create mode 100644 drivers/usb/gadget/dfu.c
 create mode 100644 drivers/usb/gadget/dfu.h
 create mode 100644 include/dfu.h
 create mode 100644 include/flash_entity.h
 create mode 100644 include/mbr.h

diff --git a/board/samsung/goni/Makefile b/board/samsung/goni/Makefile
index ecde7a7..c07b0a2 100644
--- a/board/samsung/goni/Makefile
+++ b/board/samsung/goni/Makefile
@@ -31,6 +31,8 @@ LIB	= $(obj)lib$(BOARD).o
 COBJS-y	:= goni.o onenand.o
 SOBJS	:= lowlevel_init.o
 
+COBJS-$(CONFIG_CMD_DEVICE_FIRMWARE_UPGRADE) += flash.o
+
 SRCS    := $(SOBJS:.o=.S) $(COBJS-y:.o=.c)
 OBJS	:= $(addprefix $(obj),$(COBJS-y))
 SOBJS	:= $(addprefix $(obj),$(SOBJS))
diff --git a/board/samsung/goni/flash.c b/board/samsung/goni/flash.c
new file mode 100644
index 0000000..a6dd7d2
--- /dev/null
+++ b/board/samsung/goni/flash.c
@@ -0,0 +1,634 @@
+/*
+ * flash.c -- board flashing routines
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * author: Andrzej Pietrasiewicz <andrzej.p at samsung.com>
+ *
+ * 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>
+#include <malloc.h>
+#include <mbr.h>
+#include <mmc.h>
+#include <fat.h>
+#include <flash_entity.h>
+#include <linux/types.h>
+#include <jffs2/load_kernel.h>
+
+#define MMC_BLOCK_SZ		(256 * 1024)
+#define MMC_FAT_BLOCK_SZ	(4 * 1024 * 1024)
+#define MMC_SECTOR_SZ		512
+#define MMC_UBOOT_OFFSET	128
+#define MMC_UBOOT_SZ		512
+#define PARAM_LEN		12
+#define SHORT_PART_NAME		15
+#define LONG_PART_NAME		20
+#define SIGNATURE		((unsigned short) 0xAA55)
+#define CALLOC_STRUCT(n, type)	(struct type *) calloc(n, sizeof(struct type))
+#define DEFAULT_MMC_PART_NAME	"mmc-default-part"
+
+/* partition IDs counted from 0, eg. mmc0-pri-1's ID is 0 */
+#define UIMAGE_PART_ID		1
+#define EXTENDED_PART_ID	3
+#define UMS_PART_ID		7
+#define UIMAGE_PART_NAME	"mmc0-pri-2"
+
+#define USE_MMC_UBOOT
+#define USE_MMC
+
+typedef int (*rw_op)(void *buf, unsigned int len, unsigned long from);
+typedef int (*erase_op)(unsigned int len, unsigned long from);
+
+struct flash_entity_ctx {
+	unsigned long		offset;	/* offset into the device */
+	unsigned long		length; /* size of the entity */
+	u8			*buf; /* buffer for one chunk */
+	unsigned long		buf_len; /* one chunk length */
+	unsigned int		buffered; /* # available bytes in buf */
+	unsigned int		num_done; /* total bytes handled */
+	rw_op			read; /* chunk read op for this medium */
+	rw_op			write; /* chunk write op for this medium */
+	erase_op		erase; /* erase op for this medium or NULL */
+	struct flash_entity	*this_entity; /* the containing entity */
+	void			*associated; /* related entity, if any */
+};
+
+struct mbr_part_data {
+	unsigned long		offset; /* #sectors from mmc begin */
+	unsigned long		length; /* #sectors in this partition*/
+	u8			primary; /* != 0 if primary, 0 if extended */
+};
+
+/*
+ * MMC u-boot partitions
+ */
+static struct mbr_part_data *uboot_pdata;
+
+static u8 uboot_part_num;
+static u8 used_uboot_parts;
+
+int use_uboot(struct mbr_part_data *pdata, u8 i)
+{
+	/*
+	 * Use i and pdata[i] members to decide if the partition is used
+	 */
+	return 1;
+}
+
+char *alloc_uboot_name(u8 i)
+{
+	char *name = calloc(SHORT_PART_NAME, 1);
+
+	if (name) {
+		sprintf(name, "mmc-u-boot");
+		return name;
+	}
+
+	return DEFAULT_MMC_PART_NAME;
+}
+
+/*
+ * MMC partitions and MMC operations
+ */
+struct mmc *mmc;
+
+static struct mbr_part_data *mmc_pdata;
+
+static u8 mmc_part_num;
+static u8 used_mmc_parts;
+
+static u8 mmc_buf[MMC_BLOCK_SZ];
+
+static int extended_lba;
+
+static int mmc_mbr_dev;
+
+static u8 pri_count;
+static u8 ext_count = 4;
+
+/*
+ * Define files available in the UIMAGE partition which has FAT on it.
+ * Only flat structure without subdirectories is supported.
+ */
+static char *uImage_part_files[] = {
+	"uImage",
+};
+#define UIMAGE_PART_NUM_FILES ARRAY_SIZE(uImage_part_files)
+
+static int read_ebr(struct mmc *mmc, struct mbr_partition *mp,
+		int ebr_next, struct mbr_part_data *pd, int parts)
+{
+	struct mbr *ebr;
+	struct mbr_partition *p;
+	char buf[512];
+	int ret, i;
+	int lba = 0;
+
+	if (ebr_next)
+		lba = extended_lba;
+
+	lba += mp->lba;
+	ret = mmc->block_dev.block_read(mmc_mbr_dev, lba, 1, buf);
+	if (ret != 1)
+		return 0;
+
+	ebr = (struct mbr *) buf;
+
+	if (ebr->signature != SIGNATURE) {
+		printf("Signature error 0x%x\n", ebr->signature);
+		return 0;
+	}
+
+	for (i = 0; i < 2; i++) {
+		p = (struct mbr_partition *) &ebr->parts[i];
+
+		if (i == 0) {
+			lba += p->lba;
+			if (p->partition_type == 0x83) {
+				if (pd) {
+					pd[parts].offset = lba;
+					pd[parts].length = p->nsectors;
+					pd[parts].primary = 0;
+				}
+				parts++;
+			}
+		}
+	}
+
+	if (p->lba && p->partition_type == 0x5)
+		parts = read_ebr(mmc, p, 1, pd, parts);
+
+	return parts;
+}
+
+static int read_mbr(struct mmc *mmc, struct mbr_part_data *pd)
+{
+	struct mbr_partition *mp;
+	struct mbr *mbr;
+	char buf[512];
+	int ret, i;
+	int parts = 0;
+
+	ret = mmc->block_dev.block_read(mmc_mbr_dev, 0, 1, buf);
+	if (ret != 1)
+		return 0;
+
+	mbr = (struct mbr *) buf;
+
+	if (mbr->signature != SIGNATURE) {
+		printf("Signature error 0x%x\n", mbr->signature);
+		return 0;
+	}
+
+	for (i = 0; i < 4; i++) {
+		mp = (struct mbr_partition *) &mbr->parts[i];
+
+		if (!mp->partition_type)
+			continue;
+
+		if (mp->partition_type == 0x83) {
+			if (pd) {
+				pd[parts].offset = mp->lba;
+				pd[parts].length = mp->nsectors;
+				pd[parts].primary = 1;
+			}
+			parts++;
+		}
+
+		if (mp->lba && mp->partition_type == 0x5) {
+			extended_lba = mp->lba;
+			parts = read_ebr(mmc, mp, 0, pd, parts);
+		}
+	}
+
+	return parts;
+}
+
+static int rw_mmc(void *buf, char *op, unsigned int len, unsigned long from)
+{
+	char ram_addr[PARAM_LEN];
+	char offset[PARAM_LEN];
+	char length[PARAM_LEN];
+	char *argv[] = {"mmc", op, ram_addr, offset, length};
+
+	sprintf(ram_addr, "0x%lx", (unsigned long)buf);
+	sprintf(offset, "0x%lx", from / MMC_SECTOR_SZ); /* guaranteed integer */
+	sprintf(length, "0x%x", (len + MMC_SECTOR_SZ - 1) / MMC_SECTOR_SZ);
+
+	return do_mmcops(NULL, 0, 6, argv);
+}
+
+static inline int read_mmc(void *buf, unsigned int len, unsigned long from)
+{
+	return rw_mmc(buf, "read", len, from);
+}
+
+static inline int write_mmc(void *buf, unsigned int len, unsigned long from)
+{
+	return rw_mmc(buf, "write", len, from);
+}
+
+/*
+ * Return number of flash entities per this partition
+ */
+u8 use_mmc(struct mbr_part_data *pdata, u8 i)
+{
+	/*
+	 * Use i and pdata[i] members to decide if the partition is used
+	 */
+	if (i == UIMAGE_PART_ID)
+		return UIMAGE_PART_NUM_FILES;
+	if (i == EXTENDED_PART_ID)
+		return 0; /* do not expose the extended partition as a whole */
+	if (i == UMS_PART_ID)
+		return 0; /* do not expose UMS; there is a separate command */
+	return 1;
+}
+
+char *alloc_mmc_name(struct mbr_part_data *pdata, u8 i, u8 l)
+{
+	char *name = calloc(PARAM_LEN, 1);
+
+	if (name) {
+		sprintf(name, "mmc0-");
+		if (pdata[i].primary)
+			sprintf(name + strlen(name), "pri-%d",
+				l ? pri_count : ++pri_count);
+		else
+			sprintf(name + strlen(name), "ext-%d",
+				l ? ext_count : ++ext_count);
+
+		return name;
+	}
+
+	return DEFAULT_MMC_PART_NAME;
+}
+
+/*
+ * FAT operations
+ */
+static u8 fat_buf[MMC_FAT_BLOCK_SZ];
+
+static char *fat_filename;
+static int fat_part_num;
+
+static int read_fat(void *buf, unsigned int len, unsigned long from)
+{
+	int ret;
+
+	ret = fat_register_device(&mmc->block_dev, fat_part_num);
+	if (ret < 0) {
+		printf("error : fat_register_device\n");
+		return 0;
+	}
+	printf("read up to %d B ", MMC_FAT_BLOCK_SZ);
+	return file_fat_read(fat_filename, buf, len);
+}
+
+static int write_fat(void *buf, unsigned int len, unsigned long from)
+{
+#ifdef CONFIG_FAT_WRITE
+	int ret;
+
+	ret = fat_register_device(&mmc->block_dev, fat_part_num);
+	if (ret < 0) {
+		printf("error : fat_register_divce\n");
+		return 0;
+	}
+
+	printf("write up to %d B ", MMC_FAT_BLOCK_SZ);
+	ret = file_fat_write(fat_filename, buf, len);
+
+	/* format and write again */
+	if (ret == 1) {
+		printf("formatting\n");
+		if (mkfs_vfat(&mmc->block_dev, fat_part_num)) {
+			printf("error formatting device\n");
+			return 0;
+		}
+		ret = file_fat_write(fat_filename, buf, len);
+	}
+
+	if (ret < 0) {
+		printf("error : writing %s\n", fat_filename);
+		return 0;
+	}
+#else
+	printf("error : FAT write not supported\n");
+	return 0;
+#endif
+	return len;
+}
+
+/*
+ * Entity-specific prepare and finish
+ */
+static void reset_ctx(struct flash_entity_ctx *ctx)
+{
+	ctx->buffered = 0;
+	ctx->num_done = 0;
+}
+
+static int generic_prepare(void *ctx, u8 mode)
+{
+	struct flash_entity_ctx *ct = ctx;
+
+	reset_ctx(ct);
+	memset(ct->buf, 0, ct->buf_len);
+	if (mode == FLASH_WRITE) {
+		if (ct->erase) {
+			printf("Erase entity: %s ", ct->this_entity->name);
+			ct->erase(ct->length, ct->offset);
+		}
+		printf("Write entity: %s ", ct->this_entity->name);
+	} else if (mode == FLASH_READ) {
+		printf("Read entity: %s ", ct->this_entity->name);
+	}
+	return 0;
+}
+
+static int generic_finish(void *ctx, u8 mode)
+{
+	struct flash_entity_ctx *ct = ctx;
+
+	if (mode == FLASH_WRITE && ct->buffered > 0)
+		ct->write(ct->buf, ct->buffered, ct->offset + ct->num_done);
+
+	return 0;
+}
+
+static int prepare_fat(void *ctx, u8 mode)
+{
+	struct flash_entity_ctx *ct = ctx;
+
+	fat_filename = ct->associated;
+	if (!strncmp(ct->this_entity->name, UIMAGE_PART_NAME,
+		     strlen(UIMAGE_PART_NAME)))
+		fat_part_num = UIMAGE_PART_ID + 1;
+	return generic_prepare(ctx, mode);
+}
+
+/*
+ * Transport layer to storage adaptation
+ */
+
+/*
+ * Adapt transport layer buffer size to storage chunk size
+ *
+ * return < n to indicate no more data to read
+ */
+static int read_block(void *ctx, unsigned int n, void *buf)
+{
+	struct flash_entity_ctx *ct = ctx;
+	unsigned int nread = 0;
+
+	if (n == 0)
+		return n;
+
+	while (nread < n) {
+		unsigned int copy;
+
+		if (ct->num_done >= ct->length)
+			break;
+		if (ct->buffered == 0) {
+			ct->read(ct->buf, ct->buf_len,
+				 ct->offset + ct->num_done);
+			ct->buffered = ct->buf_len;
+		}
+		copy = min(n - nread, ct->buffered);
+
+		memcpy(buf + nread, ct->buf + ct->buf_len - ct->buffered, copy);
+		nread += copy;
+		ct->buffered -= copy;
+		ct->num_done += copy;
+	}
+
+	return nread;
+}
+
+/*
+ * Adapt transport layer buffer size to storage chunk size
+ */
+static int write_block(void *ctx, unsigned int n, void *buf)
+{
+	struct flash_entity_ctx *ct = ctx;
+	unsigned int nwritten = 0;
+
+	if (n == 0)
+		return n;
+
+	while (nwritten < n) {
+		unsigned int copy;
+
+		if (ct->num_done >= ct->length)
+			break;
+		if (ct->buffered >= ct->buf_len) {
+			ct->write(ct->buf, ct->buf_len,
+				  ct->offset + ct->num_done);
+			ct->buffered = 0;
+			ct->num_done += ct->buf_len;
+			if (ct->num_done >= ct->length)
+				break;
+		}
+		copy = min(n - nwritten, ct->buf_len - ct->buffered);
+
+		memcpy(ct->buf + ct->buffered, buf + nwritten, copy);
+		nwritten += copy;
+		ct->buffered += copy;
+	}
+
+	return nwritten;
+}
+
+/*
+ * Flash entities definitions for this board
+ */
+static struct flash_entity *flash_ents;
+
+void customize_entities(struct flash_entity *fe, u8 n_ents)
+{
+	int i;
+	for (i = 0; i < n_ents; ++i) {
+		/* i counts all entities, not just mmc entities */
+		/* add similar "if" blocks for other customizable entities */
+		if (!strcmp(fe[i].name, UIMAGE_PART_NAME)) {
+			struct flash_entity_ctx *ctx;
+			char *name, file_number;
+
+			fe[i].prepare = prepare_fat;
+			ctx = fe[i].ctx;
+			ctx->length = min(ctx->length, MMC_FAT_BLOCK_SZ);
+			ctx->buf = fat_buf;
+			ctx->buf_len = ctx->length;
+			ctx->read = read_fat;
+			ctx->write = write_fat;
+			file_number = (char)ctx->associated;
+			/* by design file_number cannot exceed array size */
+			ctx->associated = uImage_part_files[file_number];
+
+			name = calloc(LONG_PART_NAME, 1);
+			if (name) {
+				sprintf(name, "%s-%s", fe[i].name,
+					(char *)ctx->associated);
+				free(fe[i].name);
+				fe[i].name = name;
+			}
+
+			continue;
+		}
+	}
+}
+
+static inline void generic_flash_entity(struct flash_entity *fe)
+{
+	fe->prepare = generic_prepare;
+	fe->finish = generic_finish;
+	fe->read_block = read_block;
+	fe->write_block = write_block;
+}
+
+static inline void generic_flash_entity_ctx(struct flash_entity_ctx *ctx,
+					    struct flash_entity *fe)
+{
+	ctx->buf = mmc_buf;
+	ctx->buf_len = MMC_BLOCK_SZ;
+	ctx->read = read_mmc;
+	ctx->write = write_mmc;
+	ctx->erase = NULL;
+	ctx->this_entity = fe;
+	fe->ctx = ctx;
+}
+
+void register_flash_areas(void)
+{
+	u8 i, j;
+
+#ifdef USE_MMC
+	mmc = find_mmc_device(mmc_mbr_dev);
+	if (mmc && !mmc_init(mmc)) {
+		mmc_part_num = read_mbr(mmc, NULL);
+		if (mmc_part_num) {
+			mmc_pdata = CALLOC_STRUCT(mmc_part_num,
+						  mbr_part_data);
+			if (mmc_pdata)
+				if (!read_mbr(mmc, mmc_pdata)) {
+					free(mmc_pdata);
+					mmc_pdata = NULL;
+				}
+		}
+	}
+	used_mmc_parts = 0;
+	for (i = 0; mmc_pdata && i < mmc_part_num; ++i)
+		used_mmc_parts += use_mmc(mmc_pdata, i);
+	pri_count = 0;
+	ext_count = 4;
+#endif
+
+#ifdef USE_MMC_UBOOT
+	if (mmc) {
+		uboot_part_num = 1;
+		if (uboot_part_num) {
+			uboot_pdata = CALLOC_STRUCT(uboot_part_num,
+						    mbr_part_data);
+			if (uboot_pdata)
+				for (i = 0; i < uboot_part_num; ++i) {
+					uboot_pdata[i].offset =
+						MMC_UBOOT_OFFSET;
+					uboot_pdata[i].length =
+						MMC_UBOOT_SZ;
+					uboot_pdata[i].primary = 0;
+				}
+		}
+	}
+	used_uboot_parts = 0;
+	for (i = 0; uboot_pdata && i < uboot_part_num; ++i)
+		used_uboot_parts += use_uboot(uboot_pdata, i);
+#endif
+
+	flash_ents = CALLOC_STRUCT(used_uboot_parts + used_mmc_parts,
+				   flash_entity);
+	if (!flash_ents)
+		goto partinfo_alloc_rollback;
+
+	j = 0;
+	for (i = 0; i < uboot_part_num; ++i)
+		if (use_uboot(uboot_pdata, i)) {
+			struct flash_entity *fe;
+			struct flash_entity_ctx *ctx;
+
+			fe = &flash_ents[j++];
+			fe->name = alloc_uboot_name(i);
+			generic_flash_entity(fe);
+
+			ctx = CALLOC_STRUCT(1, flash_entity_ctx);
+			if (!ctx)
+				goto flash_ents_alloc_rollback;
+			generic_flash_entity_ctx(ctx, fe);
+			ctx->offset = uboot_pdata[i].offset * MMC_SECTOR_SZ;
+			ctx->length = uboot_pdata[i].length * MMC_SECTOR_SZ;
+		}
+	uboot_part_num = used_uboot_parts;
+
+	for (i = 0; i < mmc_part_num; ++i) {
+		u8 k = use_mmc(mmc_pdata, i);
+		u8 l;
+		for (l = 0; l < k; ++l) {
+			struct flash_entity *fe;
+			struct flash_entity_ctx *ctx;
+
+			fe = &flash_ents[j++];
+			fe->name = alloc_mmc_name(mmc_pdata, i, l);
+			generic_flash_entity(fe);
+
+			ctx = CALLOC_STRUCT(1, flash_entity_ctx);
+			if (!ctx)
+				goto flash_ents_alloc_rollback;
+			generic_flash_entity_ctx(ctx, fe);
+			ctx->offset = mmc_pdata[i].offset * MMC_SECTOR_SZ;
+			ctx->length = mmc_pdata[i].length * MMC_SECTOR_SZ;
+			ctx->associated = (void *)l;
+		}
+	}
+	mmc_part_num = used_mmc_parts;
+	customize_entities(flash_ents, uboot_part_num + mmc_part_num);
+	register_flash_entities(flash_ents, uboot_part_num + mmc_part_num);
+
+	return;
+
+flash_ents_alloc_rollback:
+	while (j--) {
+		free(flash_ents[j].name);
+		free(flash_ents[j].ctx);
+	}
+	free(flash_ents);
+
+partinfo_alloc_rollback:
+	free(uboot_pdata);
+	free(mmc_pdata);
+}
+
+void unregister_flash_areas(void)
+{
+	int j = uboot_part_num + mmc_part_num;
+	while (j--) {
+		free(flash_ents[j].name);
+		free(flash_ents[j].ctx);
+	}
+	free(flash_ents);
+	free(uboot_pdata);
+	free(mmc_pdata);
+}
+
diff --git a/board/samsung/goni/flash.h b/board/samsung/goni/flash.h
new file mode 100644
index 0000000..ffe4d6d
--- /dev/null
+++ b/board/samsung/goni/flash.h
@@ -0,0 +1,28 @@
+/*
+ * flash.h -- board flashing routines interface
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * author: Andrzej Pietrasiewicz <andrzej.p at samsung.com>
+ *
+ * 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
+ */
+
+#ifndef FLASH_GONI_H_
+#define FLASH_GONI_H_
+
+void register_flash_areas(void);
+void unregister_flash_areas(void);
+
+#endif
diff --git a/board/samsung/goni/goni.c b/board/samsung/goni/goni.c
index c4fc3e9..da9afd0 100644
--- a/board/samsung/goni/goni.c
+++ b/board/samsung/goni/goni.c
@@ -29,6 +29,9 @@
 #include <asm/arch/hs_otg.h>
 #include <asm/arch/cpu.h>
 #include <max8998_pmic.h>
+
+#include "flash.h"
+
 DECLARE_GLOBAL_DATA_PTR;
 
 static struct s5pc110_gpio *s5pc110_gpio;
@@ -144,4 +147,18 @@ struct s3c_plat_otg_data s5pc110_otg_data = {
 	.regs_phy = S5PC110_PHY_BASE,
 	.regs_otg = S5PC110_OTG_BASE,
 };
+
+#ifdef CONFIG_CMD_DEVICE_FIRMWARE_UPGRADE
+void board_dfu_init(void)
+{
+	register_flash_areas();
+	s3c_udc_probe(&s5pc110_otg_data);
+}
+
+void board_dfu_cleanup(void)
+{
+	unregister_flash_areas();
+}
+#endif
+
 #endif
diff --git a/common/Makefile b/common/Makefile
index ae795e0..de926d9 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -162,6 +162,7 @@ COBJS-y += cmd_usb.o
 COBJS-y += usb.o
 COBJS-$(CONFIG_USB_STORAGE) += usb_storage.o
 endif
+COBJS-$(CONFIG_CMD_DEVICE_FIRMWARE_UPGRADE) += cmd_dfu.o
 COBJS-$(CONFIG_CMD_XIMG) += cmd_ximg.o
 COBJS-$(CONFIG_YAFFS2) += cmd_yaffs2.o
 
diff --git a/common/cmd_dfu.c b/common/cmd_dfu.c
new file mode 100644
index 0000000..c85fbb1
--- /dev/null
+++ b/common/cmd_dfu.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 Samsung Electrnoics
+ * author: Andrzej Pietrasiewicz <andrzej.p at samsung.com>
+ *
+ * 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>
+#include <command.h>
+#include <dfu.h>
+#include <flash_entity.h>
+
+static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+	board_dfu_init();
+	dfu_init();
+	while (1) {
+		int irq_res;
+		/* Handle control-c and timeouts */
+		if (ctrlc()) {
+			printf("The remote end did not respond in time.\n");
+			goto fail;
+		}
+
+		irq_res = usb_gadget_handle_interrupts();
+	}
+fail:
+	dfu_cleanup();
+	board_dfu_cleanup();
+	return -1;
+}
+
+U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu,
+	"Use the DFU [Device Firmware Upgrade]",
+	"dfu - device firmware upgrade"
+);
+
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index cd22bbe..4b173e2 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -28,6 +28,7 @@ LIB	:= $(obj)libusb_gadget.o
 # new USB gadget layer dependencies
 ifdef CONFIG_USB_GADGET
 COBJS-y += epautoconf.o config.o usbstring.o
+COBJS-$(CONFIG_DFU_GADGET) += dfu.o
 COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o
 endif
 ifdef CONFIG_USB_ETHER
diff --git a/drivers/usb/gadget/dfu.c b/drivers/usb/gadget/dfu.c
new file mode 100644
index 0000000..535e194
--- /dev/null
+++ b/drivers/usb/gadget/dfu.c
@@ -0,0 +1,920 @@
+/*
+ * dfu.c -- Device Firmware Update gadget
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * author: Andrzej Pietrasiewicz <andrzej.p at samsung.com>
+ *
+ * Based on gadget zero:
+ * Copyright (C) 2003-2007 David Brownell
+ * 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
+ */
+
+#define VERBOSE_DEBUG
+#define DEBUG
+
+/*
+#include <linux/kernel.h>
+#include <linux/utsname.h>
+#include <linux/device.h>
+*/
+
+#include <common.h>
+#include <asm-generic/errno.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include <flash_entity.h>
+
+#include "gadget_chips.h"
+/* #include "epautoconf.c" */
+/* #include "config.c" */
+/* #include "usbstring.c" */
+
+#include <malloc.h>
+#include "dfu.h"
+
+static struct flash_entity *flash_ents;
+static int num_flash_ents;
+
+static struct usb_device_descriptor device_desc = {
+	.bLength =		sizeof device_desc,
+	.bDescriptorType =	USB_DT_DEVICE,
+	.bcdUSB =		__constant_cpu_to_le16(0x0100),
+	.bDeviceClass =		USB_CLASS_VENDOR_SPEC,
+	.idVendor =		__constant_cpu_to_le16(DRIVER_VENDOR_NUM),
+	.idProduct =		__constant_cpu_to_le16(DRIVER_PRODUCT_NUM),
+	.iManufacturer =	STRING_MANUFACTURER,
+	.iProduct =		STRING_PRODUCT,
+	.iSerialNumber =	STRING_SERIAL,
+	.bNumConfigurations =	1,
+};
+
+static struct usb_config_descriptor dfu_config = {
+	.bLength =		sizeof dfu_config,
+	.bDescriptorType =	USB_DT_CONFIG,
+	/* compute wTotalLength on the fly */
+	.bNumInterfaces =	1,
+	.bConfigurationValue =	DFU_CONFIG_VAL,
+	.iConfiguration =	STRING_DFU_NAME,
+	.bmAttributes =		USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+	.bMaxPower =		1,	/* self-powered */
+};
+
+static struct usb_otg_descriptor otg_descriptor = {
+	.bLength =		sizeof otg_descriptor,
+	.bDescriptorType =	USB_DT_OTG,
+	.bmAttributes =		USB_OTG_SRP,
+};
+
+static const struct dfu_function_descriptor dfu_func = {
+	.bLength =		sizeof dfu_func,
+	.bDescriptorType =	DFU_DT_FUNC,
+	.bmAttributes =		DFU_BIT_WILL_DETACH |
+				DFU_BIT_MANIFESTATION_TOLERANT |
+				DFU_BIT_CAN_UPLOAD |
+				DFU_BIT_CAN_DNLOAD,
+	.wDetachTimeOut =	0,
+	.wTransferSize =	USB_BUFSIZ,
+	.bcdDFUVersion =	__constant_cpu_to_le16(0x0110),
+};
+
+static const struct usb_interface_descriptor dfu_intf_runtime = {
+	.bLength =		sizeof dfu_intf_runtime,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	USB_CLASS_APP_SPEC,
+	.bInterfaceSubClass =	1,
+	.bInterfaceProtocol =	1,
+	.iInterface =		STRING_DFU_NAME,
+};
+
+static const struct usb_descriptor_header *dfu_function_runtime[] = {
+	(struct usb_descriptor_header *) &otg_descriptor,
+	(struct usb_descriptor_header *) &dfu_func,
+	(struct usb_descriptor_header *) &dfu_intf_runtime,
+	NULL,
+};
+
+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_VENDOR_SPEC,
+	.bNumConfigurations =	1,
+};
+
+static char manufacturer[50];
+static const char longname[] = "DFU Gadget";
+/* default serial number takes at least two packets */
+static const char serial[] = "0123456789.0123456789.012345678";
+static const char dfu_name[] = "Device Firmware Upgrade";
+static const char shortname[] = "dfu";
+
+/* static strings, in UTF-8 */
+static struct usb_string strings_runtime[] = {
+	{ STRING_MANUFACTURER, manufacturer, },
+	{ STRING_PRODUCT, longname, },
+	{ STRING_SERIAL, serial, },
+	{ STRING_DFU_NAME, dfu_name, },
+	{  }			/* end of list */
+};
+
+static struct usb_gadget_strings stringtab_runtime = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings_runtime,
+};
+
+static struct usb_gadget_strings stringtab_dfu = {
+	.language	= 0x0409,	/* en-us */
+};
+
+static bool is_runtime(struct dfu_dev *dev)
+{
+	return dev->dfu_state == DFU_STATE_appIDLE ||
+		dev->dfu_state == DFU_STATE_appDETACH;
+}
+
+static int config_buf(struct usb_gadget *gadget,
+		u8 *buf, u8 type, unsigned index)
+{
+	int hs = 0;
+	struct dfu_dev *dev = get_gadget_data(gadget);
+	const struct usb_descriptor_header **function;
+	int len;
+
+	if (index > 0)
+		return -EINVAL;
+
+	if (gadget_is_dualspeed(gadget)) {
+		hs = (gadget->speed == USB_SPEED_HIGH);
+		if (type == USB_DT_OTHER_SPEED_CONFIG)
+			hs = !hs;
+	}
+	if (is_runtime(dev))
+		function = dfu_function_runtime;
+	else
+		function = (const struct usb_descriptor_header **)dev->function;
+
+	/* for now, don't advertise srp-only devices */
+	if (!gadget_is_otg(gadget))
+		function++;
+
+	len = usb_gadget_config_buf(&dfu_config,
+			buf, USB_BUFSIZ, function);
+	if (len < 0)
+		return len;
+	((struct usb_config_descriptor *) buf)->bDescriptorType = type;
+	return len;
+}
+
+static void dfu_reset_config(struct dfu_dev *dev)
+{
+	if (dev->config == 0)
+		return;
+
+	DBG(dev, "reset config\n");
+
+	dev->config = 0;
+}
+
+static int dfu_set_config(struct dfu_dev *dev, unsigned number)
+{
+	int result = 0;
+	struct usb_gadget *gadget = dev->gadget;
+	char *speed;
+
+	if (number == dev->config)
+		return 0;
+
+	dfu_reset_config(dev);
+
+	if (DFU_CONFIG_VAL != number) {
+		result = -EINVAL;
+		return result;
+	}
+
+	switch (gadget->speed) {
+	case USB_SPEED_LOW:
+		speed = "low";
+		break;
+	case USB_SPEED_FULL:
+		speed = "full";
+		break;
+	case USB_SPEED_HIGH:
+		speed = "high";
+		break;
+	default:
+		speed = "?";
+		break;
+	}
+
+	dev->config = number;
+	INFO(dev, "%s speed config #%d: %s\n", speed, number, dfu_name);
+	return result;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void empty_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	/* intentionally empty */
+}
+
+static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct dfu_dev *dev = req->context;
+	struct flash_entity *fe = &flash_ents[dev->altsetting];
+
+	if (dev->not_prepared) {
+		printf("DOWNLOAD %s\n", fe->name);
+		fe->prepare(fe->ctx, FLASH_WRITE);
+		dev->not_prepared = false;
+	}
+
+	if (req->length > 0)
+		fe->write_block(fe->ctx, req->length, req->buf);
+	else {
+		fe->finish(fe->ctx, FLASH_WRITE);
+		dev->not_prepared = true;
+	}
+}
+
+static void handle_getstatus(struct usb_request *req)
+{
+	struct dfu_status *dstat = (struct dfu_status *)req->buf;
+	struct dfu_dev *dev = req->context;
+
+	switch (dev->dfu_state) {
+	case DFU_STATE_dfuDNLOAD_SYNC:
+	case DFU_STATE_dfuDNBUSY:
+		dev->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
+		break;
+	case DFU_STATE_dfuMANIFEST_SYNC:
+		break;
+	default:
+		break;
+	}
+
+	/* send status response */
+	dstat->bStatus = dev->dfu_status;
+	/* FIXME: set dstat->bwPollTimeout */
+	dstat->bState = dev->dfu_state;
+	dstat->iString = 0;
+}
+
+static void handle_getstate(struct usb_request *req)
+{
+	struct dfu_dev *dev = req->context;
+
+	((u8 *)req->buf)[0] = dev->dfu_state & 0xff;
+	req->actual = sizeof(u8);
+}
+
+static int handle_upload(struct usb_request *req, u16 len)
+{
+	struct dfu_dev *dev = req->context;
+	struct flash_entity *fe = &flash_ents[dev->altsetting];
+	int n;
+
+	if (dev->not_prepared) {
+		printf("UPLOAD %s\n", fe->name);
+		fe->prepare(fe->ctx, FLASH_READ);
+		dev->not_prepared = false;
+	}
+	n = fe->read_block(fe->ctx, len, req->buf);
+
+	/* no more data to read from this entity */
+	if (n < len) {
+		fe->finish(fe->ctx, FLASH_READ);
+		dev->not_prepared = true;
+	}
+
+	return n;
+}
+
+static int handle_dnload(struct usb_gadget *gadget, u16 len)
+{
+	struct dfu_dev *dev = get_gadget_data(gadget);
+	struct usb_request *req = dev->req;
+
+	if (len == 0)
+		dev->dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
+
+	req->complete = dnload_request_complete;
+
+	return len;
+}
+
+static int
+dfu_handle(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+	struct dfu_dev *dev = get_gadget_data(gadget);
+	struct usb_request *req = dev->req;
+	u16 len = le16_to_cpu(ctrl->wLength);
+	int rc = 0;
+
+	switch (dev->dfu_state) {
+	case DFU_STATE_appIDLE:
+		switch (ctrl->bRequest) {
+		case USB_REQ_DFU_GETSTATUS:
+			handle_getstatus(req);
+			return RET_STAT_LEN;
+		case USB_REQ_DFU_GETSTATE:
+			handle_getstate(req);
+			break;
+		case USB_REQ_DFU_DETACH:
+			dev->dfu_state = DFU_STATE_appDETACH;
+			dev->dfu_state = DFU_STATE_dfuIDLE;
+			return RET_ZLP;
+		default:
+			return RET_STALL;
+		}
+		break;
+	case DFU_STATE_appDETACH:
+		switch (ctrl->bRequest) {
+		case USB_REQ_DFU_GETSTATUS:
+			handle_getstatus(req);
+			return RET_STAT_LEN;
+		case USB_REQ_DFU_GETSTATE:
+			handle_getstate(req);
+			break;
+		default:
+			dev->dfu_state = DFU_STATE_appIDLE;
+			return RET_STALL;
+		}
+		/* FIXME: implement timer to return to appIDLE */
+		break;
+	case DFU_STATE_dfuIDLE:
+		switch (ctrl->bRequest) {
+		case USB_REQ_DFU_DNLOAD:
+			if (len == 0) {
+				dev->dfu_state = DFU_STATE_dfuERROR;
+				return RET_STALL;
+			}
+			dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+			return handle_dnload(gadget, len);
+		case USB_REQ_DFU_UPLOAD:
+			dev->dfu_state = DFU_STATE_dfuUPLOAD_IDLE;
+			return handle_upload(req, len);
+		case USB_REQ_DFU_ABORT:
+			/* no zlp? */
+			return RET_ZLP;
+		case USB_REQ_DFU_GETSTATUS:
+			handle_getstatus(req);
+			return RET_STAT_LEN;
+		case USB_REQ_DFU_GETSTATE:
+			handle_getstate(req);
+			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;
+			dev->dfu_state = DFU_STATE_appIDLE;
+			break;
+		default:
+			dev->dfu_state = DFU_STATE_dfuERROR;
+			return RET_STALL;
+		}
+		break;
+	case DFU_STATE_dfuDNLOAD_SYNC:
+		switch (ctrl->bRequest) {
+		case USB_REQ_DFU_GETSTATUS:
+			handle_getstatus(req);
+			return RET_STAT_LEN;
+			/* FIXME: state transition depending
+			 * on block completeness */
+		case USB_REQ_DFU_GETSTATE:
+			handle_getstate(req);
+			break;
+		default:
+			dev->dfu_state = DFU_STATE_dfuERROR;
+			return RET_STALL;
+		}
+		break;
+	case DFU_STATE_dfuDNBUSY:
+		switch (ctrl->bRequest) {
+		case USB_REQ_DFU_GETSTATUS:
+			/* FIXME: only accept getstatus if bwPollTimeout
+			 * has elapsed */
+			handle_getstatus(req);
+			return RET_STAT_LEN;
+		default:
+			dev->dfu_state = DFU_STATE_dfuERROR;
+			return RET_STALL;
+		}
+		break;
+	case DFU_STATE_dfuDNLOAD_IDLE:
+		switch (ctrl->bRequest) {
+		case USB_REQ_DFU_DNLOAD:
+			dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+			return handle_dnload(gadget, len);
+		case USB_REQ_DFU_ABORT:
+			dev->dfu_state = DFU_STATE_dfuIDLE;
+			return RET_ZLP;
+		case USB_REQ_DFU_GETSTATUS:
+			handle_getstatus(req);
+			return RET_STAT_LEN;
+		case USB_REQ_DFU_GETSTATE:
+			handle_getstate(req);
+			break;
+		default:
+			dev->dfu_state = DFU_STATE_dfuERROR;
+			return RET_STALL;
+		}
+		break;
+	case DFU_STATE_dfuMANIFEST_SYNC:
+		switch (ctrl->bRequest) {
+		case USB_REQ_DFU_GETSTATUS:
+			/* We're MainfestationTolerant */
+			dev->dfu_state = DFU_STATE_dfuIDLE;
+			handle_getstatus(req);
+			return RET_STAT_LEN;
+		case USB_REQ_DFU_GETSTATE:
+			handle_getstate(req);
+			break;
+		default:
+			dev->dfu_state = DFU_STATE_dfuERROR;
+			return RET_STALL;
+		}
+		break;
+	case DFU_STATE_dfuMANIFEST:
+		/* we should never go here */
+		dev->dfu_state = DFU_STATE_dfuERROR;
+		return RET_STALL;
+	case DFU_STATE_dfuMANIFEST_WAIT_RST:
+		/* we should never go here */
+		break;
+	case DFU_STATE_dfuUPLOAD_IDLE:
+		switch (ctrl->bRequest) {
+		case USB_REQ_DFU_UPLOAD:
+			/* state transition if less data then requested */
+			rc = handle_upload(req, len);
+			if (rc >= 0 && rc < len)
+				dev->dfu_state = DFU_STATE_dfuIDLE;
+			return rc;
+		case USB_REQ_DFU_ABORT:
+			dev->dfu_state = DFU_STATE_dfuIDLE;
+			/* no zlp? */
+			return RET_ZLP;
+		case USB_REQ_DFU_GETSTATUS:
+			handle_getstatus(req);
+			return RET_STAT_LEN;
+		case USB_REQ_DFU_GETSTATE:
+			handle_getstate(req);
+			break;
+		default:
+			dev->dfu_state = DFU_STATE_dfuERROR;
+			return RET_STALL;
+		}
+		break;
+	case DFU_STATE_dfuERROR:
+		switch (ctrl->bRequest) {
+		case USB_REQ_DFU_GETSTATUS:
+			handle_getstatus(req);
+			return RET_STAT_LEN;
+		case USB_REQ_DFU_GETSTATE:
+			handle_getstate(req);
+			break;
+		case USB_REQ_DFU_CLRSTATUS:
+			dev->dfu_state = DFU_STATE_dfuIDLE;
+			dev->dfu_status = DFU_STATUS_OK;
+			/* no zlp? */
+			return RET_ZLP;
+		default:
+			dev->dfu_state = DFU_STATE_dfuERROR;
+			return RET_STALL;
+		}
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+dfu_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+	struct dfu_dev *dev = get_gadget_data(gadget);
+	struct usb_request *req = dev->req;
+	int value = -EOPNOTSUPP;
+	u16 w_index = le16_to_cpu(ctrl->wIndex);
+	u16 w_value = le16_to_cpu(ctrl->wValue);
+	u16 w_length = le16_to_cpu(ctrl->wLength);
+
+	req->zero = 0;
+	req->complete = empty_complete;
+	if (!(ctrl->bRequestType & USB_TYPE_CLASS))
+		switch (ctrl->bRequest) {
+
+		case USB_REQ_GET_DESCRIPTOR:
+			if (ctrl->bRequestType != USB_DIR_IN)
+				goto unknown;
+			switch (w_value >> 8) {
+
+			case USB_DT_DEVICE:
+				value = min(w_length, (u16) sizeof device_desc);
+				memcpy(req->buf, &device_desc, value);
+				break;
+			case USB_DT_DEVICE_QUALIFIER:
+				if (!gadget_is_dualspeed(gadget))
+					break;
+				value = min(w_length,
+					    (u16) sizeof dev_qualifier);
+				memcpy(req->buf, &dev_qualifier, value);
+				break;
+
+			case USB_DT_OTHER_SPEED_CONFIG:
+				if (!gadget_is_dualspeed(gadget))
+					break;
+				/* FALLTHROUGH */
+			case USB_DT_CONFIG:
+				value = config_buf(gadget, req->buf,
+						w_value >> 8,
+						w_value & 0xff);
+				if (value >= 0)
+					value = min_t(w_length, (u16) value);
+				break;
+
+			case USB_DT_STRING:
+				/* wIndex == language code. */
+				value = usb_gadget_get_string(
+					is_runtime(dev) ? &stringtab_runtime :
+					&stringtab_dfu,
+					w_value & 0xff, req->buf);
+				if (value >= 0)
+					value = min_t(w_length, (u16) value);
+				break;
+			case DFU_DT_FUNC:
+				value = min(w_length, (u16) sizeof dfu_func);
+				memcpy(req->buf, &dfu_func, value);
+				break;
+			}
+			break;
+
+		case USB_REQ_SET_CONFIGURATION:
+			if (ctrl->bRequestType != 0)
+				goto unknown;
+			if (gadget->a_hnp_support)
+				DBG(dev, "HNP available\n");
+			else if (gadget->a_alt_hnp_support)
+				DBG(dev, "HNP needs a different root port\n");
+			else
+				VDBG(dev, "HNP inactive\n");
+			spin_lock(&dev->lock);
+			value = dfu_set_config(dev, w_value);
+			spin_unlock(&dev->lock);
+			break;
+		case USB_REQ_GET_CONFIGURATION:
+			if (ctrl->bRequestType != USB_DIR_IN)
+				goto unknown;
+			*(u8 *)req->buf = dev->config;
+			value = min(w_length, (u16) 1);
+			break;
+
+		/* until we add altsetting support, or other interfaces,
+		 * only 0/0 are possible.  pxa2xx only supports 0/0 (poorly)
+		 * and already killed pending endpoint I/O.
+		 */
+		case USB_REQ_SET_INTERFACE:
+			if (ctrl->bRequestType != USB_RECIP_INTERFACE)
+				goto unknown;
+			spin_lock(&dev->lock);
+			if (dev->config && w_index == 0) {
+				u8		config = dev->config;
+
+				/* resets interface configuration, forgets about
+				 * previous transaction state (queued bufs, etc)
+				 * and re-inits endpoint state (toggle etc)
+				 * no response queued, just zero
+				 * status == success.
+				 * if we had more than one interface we couldn't
+				 * use this "reset the config" shortcut.
+				 */
+				dfu_reset_config(dev);
+				dfu_set_config(dev, config);
+				dev->altsetting = w_value;
+				value = 0;
+			}
+			spin_unlock(&dev->lock);
+			break;
+		case USB_REQ_GET_INTERFACE:
+			if (ctrl->bRequestType !=
+			    (USB_DIR_IN|USB_RECIP_INTERFACE))
+				goto unknown;
+			if (!dev->config)
+				break;
+			if (w_index != 0) {
+				value = -EDOM;
+				break;
+			}
+			*(u8 *)req->buf = 0;
+			value = min(w_length, (u16) 1);
+			break;
+
+		default:
+unknown:
+			VDBG(dev,
+				"unknown control req%02x.%02x v%04x i%04x l%d\n",
+				ctrl->bRequestType, ctrl->bRequest,
+				w_value, w_index, w_length);
+		}
+	else
+		value = dfu_handle(gadget, ctrl);
+
+	/* respond with data transfer before status phase? */
+	if (value >= 0) {
+		req->length = value;
+		req->zero = value < w_length;
+		value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0) {
+			DBG(dev, "ep_queue --> %d\n", value);
+			req->status = 0;
+		}
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void dfu_disconnect(struct usb_gadget *gadget)
+{
+	struct dfu_dev *dev = get_gadget_data(gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dfu_reset_config(dev);
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static int dfu_prepare_function(struct dfu_dev *dev, int n)
+{
+	struct usb_interface_descriptor *d;
+	int i = 0;
+
+	dev->function = kzalloc((ALTSETTING_BASE + n + 1) *
+				sizeof(struct usb_descriptor_header *),
+				GFP_KERNEL);
+	if (!dev->function)
+		goto enomem;
+
+	dev->function[0] = (struct usb_descriptor_header *)&otg_descriptor;
+	dev->function[1] = (struct usb_descriptor_header *)&dfu_func;
+
+	for (i = 0; i < n; ++i) {
+		d = kzalloc(sizeof(*d), GFP_KERNEL);
+		if (!d)
+			goto enomem;
+
+		d->bLength =		sizeof(*d);
+		d->bDescriptorType =	USB_DT_INTERFACE;
+		d->bAlternateSetting =	i;
+		d->bNumEndpoints =	0;
+		d->bInterfaceClass =	USB_CLASS_APP_SPEC;
+		d->bInterfaceSubClass =	1;
+		d->bInterfaceProtocol =	2;
+		d->iInterface =		DFU_STR_BASE + i;
+
+		dev->function[ALTSETTING_BASE + i] =
+			(struct usb_descriptor_header *)d;
+	}
+	dev->function[ALTSETTING_BASE + i] = NULL;
+
+	return 1;
+
+enomem:
+	while (i) {
+		kfree(dev->function[--i + ALTSETTING_BASE]);
+		dev->function[i + ALTSETTING_BASE] = NULL;
+	}
+	kfree(dev->function);
+
+	return 0;
+}
+
+static int
+dfu_prepare_strings(struct dfu_dev *dev, struct flash_entity *f, int n)
+{
+	int i = 0;
+
+	dev->strings = kzalloc((STRING_ALTSETTING_BASE + n + 1) *
+				sizeof(struct usb_string),
+				GFP_KERNEL);
+	if (!dev->strings)
+		goto enomem;
+
+	dev->strings[0].id = STRING_MANUFACTURER;
+	dev->strings[0].s = manufacturer;
+	dev->strings[1].id = STRING_PRODUCT;
+	dev->strings[1].s = longname;
+	dev->strings[2].id = STRING_SERIAL;
+	dev->strings[2].s = serial;
+	dev->strings[3].id = STRING_DFU_NAME;
+	dev->strings[3].s = dfu_name;
+
+	for (i = 0; i < n; ++i) {
+		char *s;
+
+		dev->strings[STRING_ALTSETTING_BASE + i].id = DFU_STR_BASE + i;
+		s = kzalloc(strlen(f[i].name) + 1, GFP_KERNEL);
+		if (!s)
+			goto enomem;
+
+		strcpy(s, f[i].name);
+		dev->strings[STRING_ALTSETTING_BASE + i].s = s;
+	}
+	dev->strings[STRING_ALTSETTING_BASE + i].id = 0;
+	dev->strings[STRING_ALTSETTING_BASE + i].s = NULL;
+
+	return 1;
+
+enomem:
+	while (i) {
+		kfree((void *)dev->strings[--i + STRING_ALTSETTING_BASE].s);
+		dev->strings[i + STRING_ALTSETTING_BASE].s = NULL;
+	}
+	kfree(dev->strings);
+
+	return 0;
+}
+
+static void dfu_unbind(struct usb_gadget *gadget)
+{
+	struct dfu_dev *dev = get_gadget_data(gadget);
+	int i;
+
+	DBG(dev, "unbind\n");
+
+	if (dev->strings) {
+		i = num_flash_ents;
+		while (i) {
+			kfree((void *)
+			      dev->strings[--i + STRING_ALTSETTING_BASE].s);
+			dev->strings[i + STRING_ALTSETTING_BASE].s = NULL;
+		}
+		kfree(dev->strings);
+	}
+	if (dev->function) {
+		i = num_flash_ents;
+		while (i) {
+			kfree(dev->function[--i + ALTSETTING_BASE]);
+			dev->function[i + ALTSETTING_BASE] = NULL;
+		}
+		kfree(dev->function);
+	}
+	/* we've already been disconnected ... no i/o is active */
+	if (dev->req) {
+		dev->req->length = USB_BUFSIZ;
+		kfree(dev->req->buf);
+		usb_ep_free_request(gadget->ep0, dev->req);
+	}
+	kfree(dev);
+	set_gadget_data(gadget, NULL);
+}
+
+static int __init dfu_bind(struct usb_gadget *gadget)
+{
+	struct dfu_dev *dev;
+	int gcnum;
+
+	usb_ep_autoconfig_reset(gadget);
+
+	gcnum = usb_gadget_controller_number(gadget);
+	if (gcnum >= 0)
+		device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
+	else {
+		pr_warning("%s: controller '%s' not recognized\n",
+			shortname, gadget->name);
+		device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
+	}
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+	spin_lock_init(&dev->lock);
+	dev->gadget = gadget;
+	set_gadget_data(gadget, dev);
+
+	dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+	if (!dev->req)
+		goto enomem;
+	dev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
+	if (!dev->req->buf)
+		goto enomem;
+
+	dev->req->complete = empty_complete;
+
+	device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
+
+	if (gadget_is_dualspeed(gadget))
+		dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0;
+
+	if (gadget_is_otg(gadget)) {
+		otg_descriptor.bmAttributes |= USB_OTG_HNP,
+		dfu_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	}
+
+	usb_gadget_set_selfpowered(gadget);
+
+	dev->dfu_state = DFU_STATE_appIDLE;
+	dev->dfu_status = DFU_STATUS_OK;
+	dev->not_prepared = true;
+
+	if (!dfu_prepare_function(dev, num_flash_ents))
+		goto enomem;
+
+	if (!dfu_prepare_strings(dev, flash_ents, num_flash_ents))
+		goto enomem;
+	stringtab_dfu.strings = dev->strings;
+
+	gadget->ep0->driver_data = dev;
+	dev->req->context = dev;
+
+	INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname);
+
+	/* snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
+		init_utsname()->sysname, init_utsname()->release,
+		gadget->name); */
+
+	return 0;
+
+enomem:
+	dfu_unbind(gadget);
+	return -ENOMEM;
+}
+
+static void dfu_suspend(struct usb_gadget *gadget)
+{
+	if (gadget->speed == USB_SPEED_UNKNOWN)
+		return;
+
+	DBG(dev, "suspend\n");
+}
+
+static void dfu_resume(struct usb_gadget *gadget)
+{
+	DBG(dev, "resume\n");
+}
+
+static struct usb_gadget_driver dfu_driver = {
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+	.speed		= USB_SPEED_HIGH,
+#else
+	.speed		= USB_SPEED_FULL,
+#endif
+	/*.function	= (char *) longname,*/
+	.bind		= dfu_bind,
+	.unbind		= __exit_p(dfu_unbind),
+
+	.setup		= dfu_setup,
+	.disconnect	= dfu_disconnect,
+
+	.suspend	= dfu_suspend,
+	.resume		= dfu_resume,
+
+	/*
+	.driver		= {
+		.name		= (char *) shortname,
+		.owner		= THIS_MODULE,
+	},*/
+};
+
+void register_flash_entities(struct flash_entity *flents, int n)
+{
+	flash_ents = flents;
+	num_flash_ents = n;
+}
+
+int __init dfu_init(void)
+{
+	return usb_gadget_register_driver(&dfu_driver);
+}
+module_init(dfu_init);
+
+void __exit dfu_cleanup(void)
+{
+	usb_gadget_unregister_driver(&dfu_driver);
+}
+module_exit(cleanup);
+
diff --git a/drivers/usb/gadget/dfu.h b/drivers/usb/gadget/dfu.h
new file mode 100644
index 0000000..5a6386e
--- /dev/null
+++ b/drivers/usb/gadget/dfu.h
@@ -0,0 +1,171 @@
+/*
+ * dfu.h -- Device Firmware Update gadget
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * author: Andrzej Pietrasiewicz <andrzej.p at samsung.com>
+ *
+ * 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
+ */
+
+#ifndef DFU_H_
+#define DFU_H_
+
+#include <linux/compiler.h>
+
+/*
+ * Linux kernel compatibility layer
+ */
+#define GFP_ATOMIC				((gfp_t) 0)
+#define GFP_KERNEL				((gfp_t) 0)
+#define true					1
+#define false					0
+#define dev_dbg(...)				do {} while (0)
+#define dev_vdbg(...)				do {} while (0)
+#define dev_err(...)				do {} while (0)
+#define dev_warn(...)				do {} while (0)
+#define dev_info(...)				do {} while (0)
+#define pr_warning(...)				do {} while (0)
+#define spin_lock_init(lock)			do {} while (0)
+#define spin_lock(lock)				do {} while (0)
+#define spin_unlock(lock)			do {} while (0)
+#define spin_lock_irqsave(lock,flags)		do {flags = 1;} while (0)
+#define spin_unlock_irqrestore(lock,flags)	do {flags = 0;} while (0)
+#define kmalloc(x,y)				malloc(x)
+#define kfree(x)				free(x)
+#define kzalloc(size,flags)			calloc((size), 1)
+#define __init
+#define __exit
+#define __exit_p(x)				x
+#define module_init(...)
+#define module_exit(...)
+#define min_t					min
+#define spinlock_t				int
+#define bool					int
+/*
+ * end compatibility layer
+ */
+
+#define DBG(d, fmt, args...) \
+	dev_dbg(&(d)->gadget->dev , fmt , ## args)
+#define VDBG(d, fmt, args...) \
+	dev_vdbg(&(d)->gadget->dev , fmt , ## args)
+#define ERROR(d, fmt, args...) \
+	dev_err(&(d)->gadget->dev , fmt , ## args)
+#define WARN(d, fmt, args...) \
+	dev_warn(&(d)->gadget->dev , fmt , ## args)
+#define INFO(d, fmt, args...) \
+	dev_info(&(d)->gadget->dev , fmt , ## args)
+
+#define DRIVER_VERSION			"Marzanna"
+
+/* Thanks to NetChip Technologies for donating this product ID.  */
+#define DRIVER_VENDOR_NUM		0x0525		/* NetChip */
+#define DRIVER_PRODUCT_NUM		0xffff		/* DFU */
+
+#define STRING_MANUFACTURER		0
+#define STRING_PRODUCT			1
+#define STRING_SERIAL			2
+#define STRING_DFU_NAME			49
+#define DFU_STR_BASE			50
+
+#define	DFU_CONFIG_VAL			1
+#define DFU_DT_FUNC			0x21
+
+#define DFU_BIT_WILL_DETACH		(0x1 << 3)
+#define DFU_BIT_MANIFESTATION_TOLERANT	(0x1 << 2)
+#define DFU_BIT_CAN_UPLOAD		(0x1 << 1)
+#define DFU_BIT_CAN_DNLOAD		0x1
+
+/* big enough to hold our biggest descriptor */
+#define USB_BUFSIZ			4096
+
+#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
+
+#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
+
+#define RET_STALL			-1
+#define RET_ZLP				0
+#define RET_STAT_LEN			6
+
+#define ALTSETTING_BASE			2
+#define STRING_ALTSETTING_BASE		4
+
+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,
+};
+
+struct dfu_status {
+	__u8				bStatus;
+	__u8				bwPollTimeout[3];
+	__u8				bState;
+	__u8				iString;
+} __packed;
+
+struct dfu_function_descriptor {
+	__u8				bLength;
+	__u8				bDescriptorType;
+	__u8				bmAttributes;
+	__le16				wDetachTimeOut;
+	__le16				wTransferSize;
+	__le16				bcdDFUVersion;
+} __packed;
+
+struct dfu_dev {
+	spinlock_t			lock;
+	struct usb_gadget		*gadget;
+	struct usb_request		*req;	/* for control responses */
+
+	/* when configured, we have one config */
+	u8				config;
+	u8				altsetting;
+	enum dfu_state			dfu_state;
+	unsigned int			dfu_status;
+	struct usb_descriptor_header	**function;
+	struct usb_string		*strings;
+	bool				not_prepared;
+};
+
+#endif /* DFU_H_ */
diff --git a/include/configs/s5p_goni.h b/include/configs/s5p_goni.h
index df97802..186da76 100644
--- a/include/configs/s5p_goni.h
+++ b/include/configs/s5p_goni.h
@@ -86,6 +86,8 @@
 #define CONFIG_CMD_ONENAND
 #define CONFIG_CMD_MTDPARTS
 #define CONFIG_CMD_MMC
+#define CONFIG_CMD_FAT
+#define CONFIG_CMD_DEVICE_FIRMWARE_UPGRADE
 
 #define CONFIG_BOOTDELAY		1
 #define CONFIG_ZERO_BOOTDELAY_CHECK
@@ -241,4 +243,8 @@
 #define CONFIG_USB_GADGET_S3C_UDC_OTG	1
 #define CONFIG_USB_GADGET_DUALSPEED	1
 
+#if defined (CONFIG_CMD_DEVICE_FIRMWARE_UPGRADE)
+#define CONFIG_DFU_GADGET
+#endif
+
 #endif	/* __CONFIG_H */
diff --git a/include/dfu.h b/include/dfu.h
new file mode 100644
index 0000000..977ca91
--- /dev/null
+++ b/include/dfu.h
@@ -0,0 +1,31 @@
+/*
+ * dfu.h - Device Firmware Upgrade
+ *
+ * copyright (c) 2011 samsung electronics
+ * author: andrzej pietrasiewicz <andrzej.p at samsung.com>
+ *
+ * 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
+ */
+
+#ifndef __DFU_H__
+#define __DFU_H__
+
+extern void board_dfu_init(void);
+extern int dfu_init(void);
+extern int dfu_cleanup(void);
+extern int board_dfu_cleanup(void);
+extern int usb_gadget_handle_interrupts(void);
+
+#endif
diff --git a/include/flash_entity.h b/include/flash_entity.h
new file mode 100644
index 0000000..daa90ee
--- /dev/null
+++ b/include/flash_entity.h
@@ -0,0 +1,39 @@
+/*
+ * flash_entity.h - flashable area description
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * author: Andrzej Pietrasiewicz <andrzej.p at samsung.com>
+ *
+ * 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
+ */
+
+#ifndef FLASH_ENTITY_H_
+#define FLASH_ENTITY_H_
+
+#define FLASH_READ			0
+#define FLASH_WRITE			1
+
+struct flash_entity {
+	char				*name;
+	void				*ctx;
+	int (*prepare)(void *ctx, u8 mode);
+	int (*read_block)(void *ctx, unsigned int n, void *buf);
+	int (*write_block)(void *ctx, unsigned int n, void *buf);
+	int (*finish)(void *ctx, u8 mode);
+};
+
+void register_flash_entities(struct flash_entity *flents, int n);
+
+#endif /* FLASH_ENTITY_H_ */
diff --git a/include/mbr.h b/include/mbr.h
new file mode 100644
index 0000000..9cbeb2e
--- /dev/null
+++ b/include/mbr.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 Samsung Electrnoics
+ *
+ * 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/compiler.h>
+
+#define MBR_RESERVED_SIZE	0x8000
+#define MBR_START_OFS_BLK	(0x500000 / 512)
+
+struct mbr_partition {
+	char status;
+	char f_chs[3];
+	char partition_type;
+	char l_chs[3];
+	int lba;
+	int nsectors;
+} __packed;
+
+struct mbr {
+	char code_area[440];
+	char disk_signature[4];
+	char nulls[2];
+	struct mbr_partition parts[4];
+	unsigned short signature;
+};
+
+extern unsigned int mbr_offset[16];
+extern unsigned int mbr_parts;
+
+void set_mbr_dev(int dev);
+void set_mbr_info(char *ramaddr, unsigned int len, unsigned int reserved);
+void set_mbr_table(unsigned int start_addr, int parts,
+		unsigned int *blocks, unsigned int *part_offset);
+int get_mbr_table(unsigned int *part_offset);
-- 
1.7.0.4



More information about the U-Boot mailing list