summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKoen Kooi <koen@openembedded.org>2009-05-08 10:36:15 +0200
committerKoen Kooi <koen@openembedded.org>2009-05-08 10:36:15 +0200
commit8fecb42426d33e78eee8c332455a3c5b2fcdd2fd (patch)
treed94006b7885a2e2101e8d6c251668775e669c349
parent491b936c67225ef311a58b799c9e4280fe523fec (diff)
u-boot: add dfu patches for beagleboard
-rw-r--r--recipes/u-boot/files/dfu-second-patch.patch53
-rw-r--r--recipes/u-boot/files/omap-usb-dev-dfu.patch14478
-rw-r--r--recipes/u-boot/u-boot_git.bb8
3 files changed, 14536 insertions, 3 deletions
diff --git a/recipes/u-boot/files/dfu-second-patch.patch b/recipes/u-boot/files/dfu-second-patch.patch
new file mode 100644
index 0000000000..be56e88110
--- /dev/null
+++ b/recipes/u-boot/files/dfu-second-patch.patch
@@ -0,0 +1,53 @@
+commit 123a795c1f7a3f427672dfba6dfc4d0e5056488b
+Author: Diego Dompe <ddompe@Aleph.(none)>
+Date: Wed May 6 14:00:21 2009 -0600
+
+ Fix mappings issues
+
+diff --git a/drivers/serial/usbtty.c b/drivers/serial/usbtty.c
+index 2699e0f..bada5e2 100644
+--- a/drivers/serial/usbtty.c
++++ b/drivers/serial/usbtty.c
+@@ -50,7 +50,7 @@
+ * Defines
+ */
+ #define NUM_CONFIGS 1
+-#define MAX_INTERFACES 2
++#define MAX_INTERFACES 3
+ #define NUM_ENDPOINTS 3
+ #define ACM_TX_ENDPOINT 3
+ #define ACM_RX_ENDPOINT 2
+diff --git a/include/configs/omap3_beagle.h b/include/configs/omap3_beagle.h
+index 0086618..a97d232 100644
+--- a/include/configs/omap3_beagle.h
++++ b/include/configs/omap3_beagle.h
+@@ -58,7 +58,7 @@
+ */
+ #define CONFIG_ENV_SIZE SZ_128K /* Total Size Environment */
+ /* Sector */
+-#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + SZ_128K)
++#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + SZ_128K + 0x10000)
+ #define CONFIG_SYS_GBL_DATA_SIZE 128 /* bytes reserved for */
+ /* initial data */
+
+@@ -170,7 +170,8 @@
+
+ #define CONFIG_JFFS2_NAND
+ #define CONFIG_JFFS2_CMDLINE
+-#define MTDPARTS_DEFAULT "mtdparts=nand0:32k(x-loader),2400k(u-boot),128k(u-boot_env),4096(kernel),-(fs)"
++#define MTDIDS_DEFAULT "nand0=omapnand"
++#define MTDPARTS_DEFAULT "mtdparts=omapnand:512k(x-loader),1920k(u-boot),128k(u-boot_env),4096k(kernel),-(fs)"
+
+ /* Environment information */
+ #define CONFIG_BOOTDELAY 10
+@@ -199,6 +200,7 @@
+ "run nandargs; " \
+ "nboot kernel; " \
+ "bootm ${loadaddr}\0" \
++ "mtdparts=omapnand:512k(x-loader),1920k(u-boot),128k(u-boot_env),4096k(kernel),-(fs)\0" \
+ "usbtty=cdc_acm\0"\
+ "stdout=serial,usbtty\0" \
+ "stdin=serial,usbtty\0" \
+diff --git a/tools/dfu-util/configure b/tools/dfu-util/configure
+old mode 100644
+new mode 100755
diff --git a/recipes/u-boot/files/omap-usb-dev-dfu.patch b/recipes/u-boot/files/omap-usb-dev-dfu.patch
new file mode 100644
index 0000000000..f4a8d08d9f
--- /dev/null
+++ b/recipes/u-boot/files/omap-usb-dev-dfu.patch
@@ -0,0 +1,14478 @@
+commit 988a83b84142fccb850b82a6cd14b2b4979f1720
+Author: Diego Dompe <ddompe@Aleph.(none)>
+Date: Fri May 1 16:51:21 2009 -0600
+
+ First pass of porting the DFU patches
+
+diff --git a/drivers/serial/usbtty.c b/drivers/serial/usbtty.c
+index 2624e6f..2699e0f 100644
+--- a/drivers/serial/usbtty.c
++++ b/drivers/serial/usbtty.c
+@@ -28,6 +28,8 @@
+ #include "usbtty.h"
+ #include "usb_cdc_acm.h"
+ #include "usbdescriptors.h"
++#include <usb_dfu_descriptors.h>
++#include <usb_dfu.h>
+
+ #ifdef DEBUG
+ #define TTYDBG(fmt,args...)\
+@@ -101,7 +103,7 @@ extern struct usb_string_descriptor **usb_strings;
+ static unsigned short rx_endpoint = 0;
+ static unsigned short tx_endpoint = 0;
+ static unsigned short interface_count = 0;
+-static struct usb_string_descriptor *usbtty_string_table[STR_COUNT];
++static struct usb_string_descriptor *usbtty_string_table[NUM_STRINGS];
+
+ /* USB Descriptor Strings */
+ static u8 wstrLang[4] = {4,USB_DT_STRING,0x9,0x4};
+@@ -151,6 +153,10 @@ struct acm_config_desc {
+ /* Slave Interface */
+ struct usb_interface_descriptor data_class_interface;
+ struct usb_endpoint_descriptor data_endpoints[NUM_ENDPOINTS-1];
++#ifdef CONFIG_USBD_DFU
++ struct usb_interface_descriptor uif_dfu;
++ struct usb_dfu_func_descriptor func_dfu;
++#endif
+ } __attribute__((packed));
+
+ static struct acm_config_desc acm_configuration_descriptors[NUM_CONFIGS] = {
+@@ -161,7 +167,11 @@ static struct acm_config_desc acm_configuration_descriptors[NUM_CONFIGS] = {
+ .bDescriptorType = USB_DT_CONFIG,
+ .wTotalLength =
+ cpu_to_le16(sizeof(struct acm_config_desc)),
+- .bNumInterfaces = NUM_ACM_INTERFACES,
++#ifdef CONFIG_USBD_DFU
++ .bNumInterfaces = NUM_ACM_INTERFACES +1,
++#else
++ .bNumInterfaces = NUM_ACM_INTERFACES,
++#endif
+ .bConfigurationValue = 1,
+ .iConfiguration = STR_CONFIG,
+ .bmAttributes =
+@@ -260,6 +270,11 @@ static struct acm_config_desc acm_configuration_descriptors[NUM_CONFIGS] = {
+ .bInterval = 0xFF,
+ },
+ },
++#ifdef CONFIG_USBD_DFU
++ /* Interface 3 */
++ .uif_dfu = DFU_RT_IF_DESC,
++ .func_dfu = DFU_FUNC_DESC,
++#endif
+ },
+ };
+
+@@ -370,7 +385,7 @@ static int fill_buffer (circbuf_t * buf);
+ void usbtty_poll (void);
+
+ /* utility function for converting char* to wide string used by USB */
+-static void str2wide (char *str, u16 * wide)
++void str2wide (char *str, u16 * wide)
+ {
+ int i;
+ for (i = 0; i < strlen (str) && str[i]; i++){
+@@ -639,6 +654,9 @@ static void usbtty_init_instances (void)
+ device_instance->bus = bus_instance;
+ device_instance->configurations = NUM_CONFIGS;
+ device_instance->configuration_instance_array = config_instance;
++#ifdef CONFIG_USBD_DFU
++ dfu_init_instance(device_instance);
++#endif
+
+ /* initialize bus instance */
+ memset (bus_instance, 0, sizeof (struct usb_bus_instance));
+@@ -759,7 +777,11 @@ static void usbtty_init_terminal_type(short type)
+ &acm_configuration_descriptors;
+
+ /* Interface count */
++#ifdef CONFIG_USBD_DFU
++ interface_count = NUM_ACM_INTERFACES + 1;
++#else
+ interface_count = NUM_ACM_INTERFACES;
++#endif
+ break;
+
+ /* BULK IN/OUT & Default */
+diff --git a/drivers/serial/usbtty.h b/drivers/serial/usbtty.h
+index 35a5e9e..dc502df 100644
+--- a/drivers/serial/usbtty.h
++++ b/drivers/serial/usbtty.h
+@@ -80,4 +80,10 @@
+ #define STR_CTRL_INTERFACE 0x06
+ #define STR_COUNT 0x07
+
++#ifdef CONFIG_USBD_DFU
++#define NUM_STRINGS DFU_STR_COUNT
++#else
++#define NUM_STRINGS STR_COUNT
++#endif
++
+ #endif
+diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
+index ab98ac7..16ce383 100644
+--- a/drivers/usb/Makefile
++++ b/drivers/usb/Makefile
+@@ -50,6 +50,7 @@ COBJS-$(CONFIG_OMAP1610) += usbdcore_omap1510.o
+ COBJS-$(CONFIG_MPC885_FAMILY) += usbdcore_mpc8xx.o
+ COBJS-$(CONFIG_MUSB) += usbdcore_musb.o
+ COBJS-$(CONFIG_TWL4030_USB) += twl4030_usb.o
++COBJS-$(CONFIG_USBD_DFU) += usbdfu.o
+ endif
+
+ COBJS := $(COBJS-y)
+diff --git a/drivers/usb/usbdcore.c b/drivers/usb/usbdcore.c
+index 53ed669..f2ac755 100644
+--- a/drivers/usb/usbdcore.c
++++ b/drivers/usb/usbdcore.c
+@@ -31,6 +31,7 @@
+
+ #include <malloc.h>
+ #include "usbdcore.h"
++#include <usb_dfu.h>
+
+ #define MAX_INTERFACES 2
+
+@@ -209,6 +210,10 @@ struct usb_alternate_instance *usbd_device_alternate_instance (struct usb_device
+ */
+ struct usb_device_descriptor *usbd_device_device_descriptor (struct usb_device_instance *device, int port)
+ {
++#ifdef CONFIG_USBD_DFU
++ if (device->dfu_state != DFU_STATE_appIDLE)
++ return device->dfu_dev_desc;
++#endif
+ return (device->device_descriptor);
+ }
+
+@@ -229,6 +234,10 @@ struct usb_configuration_descriptor *usbd_device_configuration_descriptor (struc
+ if (!(configuration_instance = usbd_device_configuration_instance (device, port, configuration))) {
+ return NULL;
+ }
++#ifdef CONFIG_USBD_DFU
++ if (device->dfu_state != DFU_STATE_appIDLE)
++ return (&device->dfu_cfg_desc->ucfg);
++#endif
+ return (configuration_instance->configuration_descriptor);
+ }
+
+@@ -250,6 +259,13 @@ struct usb_interface_descriptor *usbd_device_interface_descriptor (struct usb_de
+ if (!(interface_instance = usbd_device_interface_instance (device, port, configuration, interface))) {
+ return NULL;
+ }
++#ifdef CONFIG_USBD_DFU
++ if (device->dfu_state != DFU_STATE_appIDLE) {
++ if (alternate < 0 || alternate >= DFU_NUM_ALTERNATES)
++ return NULL;
++ return &device->dfu_cfg_desc->uif[alternate];
++ }
++#endif
+ if ((alternate < 0) || (alternate >= interface_instance->alternates)) {
+ return NULL;
+ }
+@@ -622,6 +638,12 @@ void usbd_device_event_irq (struct usb_device_instance *device, usb_device_event
+ case DEVICE_RESET:
+ device->device_state = STATE_DEFAULT;
+ device->address = 0;
++#ifdef CONFIG_USBD_DFU
++ if (device->dfu_state == DFU_STATE_appDETACH) {
++ debug("DFU SWITCH\n");
++ device->dfu_state = DFU_STATE_dfuIDLE;
++ }
++#endif
+ break;
+
+ case DEVICE_ADDRESS_ASSIGNED:
+@@ -680,4 +702,7 @@ void usbd_device_event_irq (struct usb_device_instance *device, usb_device_event
+ /* usbdbg("calling device->event"); */
+ device->event(device, event, data);
+ }
++#ifdef CONFIG_USBD_DFU
++ dfu_event(device, event, data);
++#endif
+ }
+diff --git a/drivers/usb/usbdcore_ep0.c b/drivers/usb/usbdcore_ep0.c
+index f6e017d..a916bee 100644
+--- a/drivers/usb/usbdcore_ep0.c
++++ b/drivers/usb/usbdcore_ep0.c
+@@ -52,6 +52,9 @@
+
+ #include <common.h>
+ #include "usbdcore.h"
++#ifdef CONFIG_USBD_DFU
++#include <usb_dfu.h>
++#endif
+
+ #if 0
+ #define dbg_ep0(lvl,fmt,args...) serial_printf("[%s] %s:%d: "fmt"\n",__FILE__,__FUNCTION__,__LINE__,##args)
+@@ -224,6 +227,7 @@ static int ep0_get_descriptor (struct usb_device_instance *device,
+
+ case USB_DESCRIPTOR_TYPE_CONFIGURATION:
+ {
++ int bNumInterface;
+ struct usb_configuration_descriptor
+ *configuration_descriptor;
+ struct usb_device_descriptor *device_descriptor;
+@@ -276,7 +280,24 @@ static int ep0_get_descriptor (struct usb_device_instance *device,
+ case USB_DESCRIPTOR_TYPE_ENDPOINT:
+ serial_printf("USB_DESCRIPTOR_TYPE_ENDPOINT - error not implemented\n");
+ return -1;
++ /* This really means "Class Specific Descriptor #1 == USB_DT_DFU */
+ case USB_DESCRIPTOR_TYPE_HID:
++#ifdef CONFIG_USBD_DFU
++ {
++ int bNumInterface =
++ le16_to_cpu(urb->device_request.wIndex);
++
++ /* In runtime mode, we only respond to the DFU INTERFACE,
++ * whereas in DFU mode, we respond for all intrfaces */
++ if (device->dfu_state != DFU_STATE_appIDLE &&
++ device->dfu_state != DFU_STATE_appDETACH ||
++ bNumInterface == CONFIG_USBD_DFU_INTERFACE) {
++ memcpy(urb->buffer,&device->dfu_cfg_desc->func_dfu,sizeof(struct usb_dfu_func_descriptor));
++ urb->actual_length = sizeof(struct usb_dfu_func_descriptor);
++ } else
++ return -1;
++ }
++#else /* CONFIG_USBD_DFU */
+ {
+ serial_printf("USB_DESCRIPTOR_TYPE_HID - error not implemented\n");
+ return -1; /* unsupported at this time */
+@@ -304,6 +325,7 @@ static int ep0_get_descriptor (struct usb_device_instance *device,
+ max);
+ #endif
+ }
++#endif /* CONFIG_USBD_DFU */
+ break;
+ case USB_DESCRIPTOR_TYPE_REPORT:
+ {
+@@ -405,6 +427,24 @@ int ep0_recv_setup (struct urb *urb)
+ le16_to_cpu (request->wLength),
+ USBD_DEVICE_REQUESTS (request->bRequest));
+
++#ifdef CONFIG_USBD_DFU
++ if ((request->bmRequestType & 0x3f) == USB_TYPE_DFU &&
++ (device->dfu_state != DFU_STATE_appIDLE ||
++ le16_to_cpu(request->wIndex) == CONFIG_USBD_DFU_INTERFACE)) {
++ int rc = dfu_ep0_handler(urb);
++ switch (rc) {
++ case DFU_EP0_NONE:
++ case DFU_EP0_UNHANDLED:
++ break;
++ case DFU_EP0_ZLP:
++ case DFU_EP0_DATA:
++ return 0;
++ case DFU_EP0_STALL:
++ return -1;
++ }
++ }
++#endif /* CONFIG_USB_DFU */
++
+ /* handle USB Standard Request (c.f. USB Spec table 9-2) */
+ if ((request->bmRequestType & USB_REQ_TYPE_MASK) != 0) {
+ if(device->device_state <= STATE_CONFIGURED){
+diff --git a/drivers/usb/usbdfu.c b/drivers/usb/usbdfu.c
+new file mode 100644
+index 0000000..9adf951
+--- /dev/null
++++ b/drivers/usb/usbdfu.c
+@@ -0,0 +1,1024 @@
++/*
++ * (C) 2007 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge@openmoko.org>
++ *
++ * based on existing SAM7DFU code from OpenPCD:
++ * (C) Copyright 2006 by Harald Welte <hwelte@hmw-consulting.de>
++ *
++ * See file CREDITS for list of people who contributed to this
++ * project.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ *
++ * TODO:
++ * - make NAND support reasonably self-contained and put in apropriate
++ * ifdefs
++ * - add some means of synchronization, i.e. block commandline access
++ * while DFU transfer is in progress, and return to commandline once
++ * we're finished
++ * - add VERIFY support after writing to flash
++ * - sanely free() resources allocated during first uppload/download
++ * request when aborting
++ * - sanely free resources when another alternate interface is selected
++ *
++ * Maybe:
++ * - add something like uImage or some other header that provides CRC
++ * checking?
++ * - make 'dnstate' attached to 'struct usb_device_instance'
++ */
++
++#include <config.h>
++#if defined(CONFIG_USBD_DFU)
++
++#include <common.h>
++
++#include <malloc.h>
++#include <linux/types.h>
++#include <linux/list.h>
++#include <asm/errno.h>
++#include <usbdcore.h>
++#include <usb_dfu.h>
++#include <usb_dfu_descriptors.h>
++#include <usb_dfu_trailer.h>
++
++#include <nand.h>
++#include <jffs2/load_kernel.h>
++int mtdparts_init(void);
++extern struct list_head devices;
++
++#include "../serial/usbtty.h" /* for STR_* defs */
++
++#define RET_NOTHING 0
++#define RET_ZLP 1
++#define RET_STALL 2
++
++volatile enum dfu_state *system_dfu_state; /* for 3rd parties */
++
++
++struct dnload_state {
++ nand_info_t *nand;
++ struct part_info *part;
++ unsigned int part_net_size; /* net sizee (excl. bad blocks) of part */
++
++ nand_erase_options_t erase_opts;
++
++ unsigned char *ptr; /* pointer to next empty byte in buffer */
++ unsigned int off; /* offset of current erase page in flash chip */
++ unsigned char *buf; /* pointer to allocated erase page buffer */
++
++ /* unless doing an atomic transfer, we use the static buffer below.
++ * This saves us from having to clean up dynamic allications in the
++ * various error paths of the code. Also, it will always work, no
++ * matter what the memory situation is. */
++ unsigned char _buf[0x20000]; /* FIXME: depends flash page size */
++};
++
++static struct dnload_state _dnstate;
++
++static int dfu_trailer_matching(const struct uboot_dfu_trailer *trailer)
++{
++ if (trailer->magic != UBOOT_DFU_TRAILER_MAGIC ||
++ trailer->version != UBOOT_DFU_TRAILER_V1 ||
++ trailer->vendor != CONFIG_USBD_VENDORID ||
++ (trailer->product != CONFIG_USBD_PRODUCTID_CDCACM &&
++ trailer->product != CONFIG_USBD_PRODUCTID_GSERIAL))
++ return 0;
++#ifdef CONFIG_REVISION_TAG
++ if (trailer->revision != get_board_rev())
++ return 0;
++#endif
++
++ return 1;
++}
++
++static struct part_info *get_partition_nand(int idx)
++{
++ struct mtd_device *dev;
++ struct part_info *part;
++ struct list_head *pentry;
++ int i;
++
++ if (mtdparts_init())
++ return NULL;
++ if (list_empty(&devices))
++ return NULL;
++
++ dev = list_entry(devices.next, struct mtd_device, link);
++ i = 0;
++ list_for_each(pentry, &dev->parts) {
++ if (i == idx) {
++ part = list_entry(pentry, struct part_info, link);
++ return part;
++ }
++ i++;
++ }
++
++ return NULL;
++}
++
++#define LOAD_ADDR ((unsigned char *)CONFIG_USBD_DFU_LOAD_ADDR)
++
++static int initialize_ds_nand(struct usb_device_instance *dev, struct dnload_state *ds)
++{
++ ds->part = get_partition_nand(dev->alternate - 1);
++ if (!ds->part) {
++ printf("DFU: unable to find partition %u\b", dev->alternate-1);
++ dev->dfu_state = DFU_STATE_dfuERROR;
++ dev->dfu_status = DFU_STATUS_errADDRESS;
++ return RET_STALL;
++ }
++ ds->nand = &nand_info[ds->part->dev->id->num];
++ ds->off = ds->part->offset;
++ ds->part_net_size = ds->part->size;
++
++ if (ds->nand->erasesize > sizeof(ds->_buf)) {
++ printf("*** Warning - NAND ERASESIZE bigger than static buffer\n");
++ ds->buf = malloc(ds->nand->erasesize);
++ if (!ds->buf) {
++ printf("DFU: can't allocate %u bytes\n", ds->nand->erasesize);
++ dev->dfu_state = DFU_STATE_dfuERROR;
++ dev->dfu_status = DFU_STATUS_errADDRESS;
++ return RET_STALL;
++ }
++ } else
++ ds->buf = ds->_buf;
++
++ ds->ptr = ds->buf;
++
++ memset(&ds->erase_opts, 0, sizeof(ds->erase_opts));
++ ds->erase_opts.quiet = 1;
++ /* FIXME: do this more dynamic */
++ if (!strcmp(ds->part->name, "rootfs"))
++ ds->erase_opts.jffs2 = 1;
++
++ debug("initialize_ds_nand(dev=%p, ds=%p): ", dev, ds);
++ debug("nand=%p, ptr=%p, buf=%p, off=0x%x\n", ds->nand, ds->ptr, ds->buf, ds->off);
++
++ return RET_NOTHING;
++}
++
++static int erase_flash_verify_nand(struct urb *urb, struct dnload_state *ds,
++ unsigned long erasesize, unsigned long size)
++{
++ struct usb_device_instance *dev = urb->device;
++ int rc;
++
++ debug("erase_flash_verify_nand(urb=%p, ds=%p, erase=0x%x size=0x%x)\n",
++ urb, ds, erasesize, size);
++
++ if (erasesize == ds->nand->erasesize) {
++ /* we're only writing a single block and need to
++ * do bad block skipping / offset adjustments our own */
++ while (ds->nand->block_isbad(ds->nand, ds->off)) {
++ debug("SKIP_ONE_BLOCK(0x%08x)!!\n", ds->off);
++ ds->off += ds->nand->erasesize;
++ }
++ }
++
++ /* we have finished one eraseblock, flash it */
++ ds->erase_opts.offset = ds->off;
++ ds->erase_opts.length = erasesize;
++ debug("Erasing 0x%x bytes @ offset 0x%x (jffs=%u)\n",
++ ds->erase_opts.length, ds->erase_opts.offset,
++ ds->erase_opts.jffs2);
++ rc = nand_erase_opts(ds->nand, &ds->erase_opts);
++ if (rc) {
++ debug("Error erasing\n");
++ dev->dfu_state = DFU_STATE_dfuERROR;
++ dev->dfu_status = DFU_STATUS_errERASE;
++ return RET_STALL;
++ }
++
++ debug("Writing 0x%x bytes @ offset 0x%x\n", size, ds->off);
++ rc = nand_write_skip_bad(ds->nand, ds->off,size,ds->buf);
++ if (rc) {
++ debug("Error writing\n");
++ dev->dfu_state = DFU_STATE_dfuERROR;
++ dev->dfu_status = DFU_STATUS_errWRITE;
++ return RET_STALL;
++ }
++
++ ds->off += size;
++ ds->ptr = ds->buf;
++
++ /* FIXME: implement verify! */
++ return RET_NOTHING;
++}
++
++static int erase_tail_clean_nand(struct urb *urb, struct dnload_state *ds)
++{
++ struct usb_device_instance *dev = urb->device;
++ int rc;
++
++ ds->erase_opts.offset = ds->off;
++ ds->erase_opts.length = ds->part->size - (ds->off - ds->part->offset);
++ debug("Erasing tail of 0x%x bytes @ offset 0x%x (jffs=%u)\n",
++ ds->erase_opts.length, ds->erase_opts.offset,
++ ds->erase_opts.jffs2);
++ rc = nand_erase_opts(ds->nand, &ds->erase_opts);
++ if (rc) {
++ printf("Error erasing tail\n");
++ dev->dfu_state = DFU_STATE_dfuERROR;
++ dev->dfu_status = DFU_STATUS_errERASE;
++ return RET_STALL;
++ }
++
++ ds->off += ds->erase_opts.length; /* for consistency */
++
++ return RET_NOTHING;
++}
++
++/* Read the next erase blcok from NAND into buffer */
++static int read_next_nand(struct urb *urb, struct dnload_state *ds)
++{
++ struct usb_device_instance *dev = urb->device;
++ int rc;
++
++ debug("Reading 0x%x@0x%x to 0x%08p\n", ds->nand->erasesize,
++ ds->off, ds->buf);
++ rc = nand_read_skip_bad(ds->nand, ds->off, ds->nand->erasesize, ds->buf);
++ if (rc) {
++ debug("Error reading\n");
++ dev->dfu_state = DFU_STATE_dfuERROR;
++ dev->dfu_status = DFU_STATUS_errWRITE;
++ return RET_STALL;
++ }
++ ds->off += ds->nand->erasesize;
++ ds->ptr = ds->buf;
++
++ return RET_NOTHING;
++}
++
++
++static int handle_dnload(struct urb *urb, u_int16_t val, u_int16_t len, int first)
++{
++ struct usb_device_instance *dev = urb->device;
++ struct dnload_state *ds = &_dnstate;
++ unsigned int actual_len = len;
++ unsigned int remain_len;
++ unsigned long size;
++ int rc;
++
++ debug("download(len=%u, first=%u) ", len, first);
++
++ if (len > CONFIG_USBD_DFU_XFER_SIZE) {
++ /* Too big. Not that we'd really care, but it's a
++ * DFU protocol violation */
++ debug("length exceeds flash page size ");
++ dev->dfu_state = DFU_STATE_dfuERROR;
++ dev->dfu_status = DFU_STATUS_errADDRESS;
++ return RET_STALL;
++ }
++
++ if (first) {
++ /* Make sure that we have a valid mtd partition table */
++ char *mtdp = getenv("mtdparts");
++ if (!mtdp)
++ run_command("mtdparts default",0);
++ }
++
++ if (len == 0) {
++ debug("zero-size write -> MANIFEST_SYNC ");
++ dev->dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
++
++ /* cleanup */
++ switch (dev->alternate) {
++ char buf[12];
++ case 0:
++ sprintf(buf, "%lx", ds->ptr - ds->buf);
++ setenv("filesize", buf);
++ ds->ptr = ds->buf;
++ break;
++ case 1:
++ if (ds->ptr >
++ ds->buf + sizeof(struct uboot_dfu_trailer)) {
++ struct uboot_dfu_trailer trailer;
++ dfu_trailer_mirror(&trailer, ds->ptr);
++ if (!dfu_trailer_matching(&trailer)) {
++ printf("DFU TRAILER NOT OK\n");
++ dev->dfu_state = DFU_STATE_dfuERROR;
++ dev->dfu_status = DFU_STATUS_errTARGET;
++ return RET_STALL;
++ }
++
++ rc = erase_flash_verify_nand(urb, ds,
++ ds->part->size,
++ ds->part_net_size);
++/// /* re-write dynenv marker in OOB */
++/// run_command("dynenv set u-boot_env", 0);
++ }
++ ds->nand = NULL;
++ free(ds->buf);
++ ds->ptr = ds->buf = ds->_buf;
++ break;
++ default:
++ rc = 0;
++ if (ds->ptr > ds->buf)
++ rc = erase_flash_verify_nand(urb, ds,
++ ds->nand->erasesize,
++ ds->nand->erasesize);
++ /* rootfs partition */
++ if (!rc && !strcmp(ds->part->name, "rootfs"))
++ rc = erase_tail_clean_nand(urb, ds);
++
++ ds->nand = NULL;
++ break;
++ }
++
++ return RET_ZLP;
++ }
++
++ if (urb->actual_length != len) {
++ debug("urb->actual_length(%u) != len(%u) ?!? ",
++ urb->actual_length, len);
++ dev->dfu_state = DFU_STATE_dfuERROR;
++ dev->dfu_status = DFU_STATUS_errADDRESS;
++ return RET_STALL;
++ }
++
++ if (first && ds->buf && ds->buf != ds->_buf && ds->buf != LOAD_ADDR) {
++ free(ds->buf);
++ ds->buf = ds->_buf;
++ }
++
++ switch (dev->alternate) {
++ case 0:
++ if (first) {
++ printf("Starting DFU DOWNLOAD to RAM (0x%08p)\n",
++ LOAD_ADDR);
++ ds->buf = LOAD_ADDR;
++ ds->ptr = ds->buf;
++ }
++
++ memcpy(ds->ptr, urb->buffer, len);
++ ds->ptr += len;
++ break;
++ default:
++ if (first) {
++ rc = initialize_ds_nand(dev, ds);
++ if (rc)
++ return rc;
++ printf("Starting DFU DOWNLOAD to partition '%s'\n",
++ ds->part->name);
++ }
++
++ size = ds->nand->erasesize;
++ remain_len = ds->buf + size - ds->ptr;
++ if (remain_len < len)
++ actual_len = remain_len;
++
++ memcpy(ds->ptr, urb->buffer, actual_len);
++ ds->ptr += actual_len;
++
++ /* check partition end */
++ if (ds->off + (ds->ptr - ds->buf) > ds->part->offset + ds->part->size) {
++ printf("End of write exceeds partition end\n");
++ dev->dfu_state = DFU_STATE_dfuERROR;
++ dev->dfu_status = DFU_STATUS_errADDRESS;
++ return RET_STALL;
++ }
++
++ if (ds->ptr >= ds->buf + size) {
++ rc = erase_flash_verify_nand(urb, ds,
++ ds->nand->erasesize,
++ ds->nand->erasesize);
++ if (rc)
++ return rc;
++ /* copy remainder of data into buffer */
++ memcpy(ds->ptr, urb->buffer + actual_len, len - actual_len);
++ ds->ptr += (len - actual_len);
++ }
++ break;
++ }
++
++ return RET_ZLP;
++}
++
++static int handle_upload(struct urb *urb, u_int16_t val, u_int16_t len, int first)
++{
++ struct usb_device_instance *dev = urb->device;
++ struct dnload_state *ds = &_dnstate;
++ unsigned int remain;
++ int rc;
++
++ debug("upload(val=0x%02x, len=%u, first=%u) ", val, len, first);
++
++ if (len > CONFIG_USBD_DFU_XFER_SIZE) {
++ /* Too big */
++ dev->dfu_state = DFU_STATE_dfuERROR;
++ dev->dfu_status = DFU_STATUS_errADDRESS;
++ //udc_ep0_send_stall();
++ debug("Error: Transfer size > CONFIG_USBD_DFU_XFER_SIZE ");
++ return -EINVAL;
++ }
++
++ switch (dev->alternate) {
++ case 0:
++ if (first) {
++ printf("Starting DFU Upload of RAM (0x%08p)\n",
++ LOAD_ADDR);
++ ds->ptr = ds->buf;
++ }
++
++ /* FIXME: end at some more dynamic point */
++ if (ds->ptr + len > LOAD_ADDR + 0x200000)
++ len = (LOAD_ADDR + 0x200000) - ds->ptr;
++
++ urb->buffer = ds->ptr;
++ urb->actual_length = len;
++ ds->ptr += len;
++ break;
++ default:
++ if (first) {
++ rc = initialize_ds_nand(dev, ds);
++ if (rc)
++ return -EINVAL;
++ printf("Starting DFU Upload of partition '%s'\n",
++ ds->part->name);
++ rc = read_next_nand(urb, ds);
++ if (rc)
++ return -EINVAL;
++ }
++
++ if (len > ds->nand->erasesize) {
++ printf("We don't support transfers bigger than %u\n",
++ ds->nand->erasesize);
++ len = ds->nand->erasesize;
++ }
++
++ remain = ds->nand->erasesize - (ds->ptr - ds->buf);
++ if (len < remain)
++ remain = len;
++
++ debug("copying %u bytes ", remain);
++ urb->buffer = ds->ptr;
++ ds->ptr += remain;
++ urb->actual_length = remain;
++
++ if (ds->ptr >= ds->buf + ds->nand->erasesize &&
++ ds->off < ds->part->offset + ds->part->size) {
++ rc = read_next_nand(urb, ds);
++ if (rc)
++ return -EINVAL;
++ if (len > remain) {
++ debug("copying another %u bytes ", len - remain);
++ memcpy(urb->buffer + remain, ds->ptr, len - remain);
++ ds->ptr += (len - remain);
++ urb->actual_length += (len - remain);
++ }
++ }
++ break;
++ }
++
++ debug("returning len=%u\n", len);
++ return len;
++}
++
++static void handle_getstatus(struct urb *urb, int max)
++{
++ struct usb_device_instance *dev = urb->device;
++ struct dfu_status *dstat = (struct dfu_status *) urb->buffer;
++
++ debug("getstatus ");
++
++ if (!urb->buffer || urb->buffer_length < sizeof(*dstat)) {
++ debug("invalid urb! ");
++ return;
++ }
++
++ switch (dev->dfu_state) {
++ case DFU_STATE_dfuDNLOAD_SYNC:
++ case DFU_STATE_dfuDNBUSY:
++#if 0
++ if (fsr & AT91C_MC_PROGE) {
++ debug("errPROG ");
++ dev->dfu_status = DFU_STATUS_errPROG;
++ dev->dfu_state = DFU_STATE_dfuERROR;
++ } else if (fsr & AT91C_MC_LOCKE) {
++ debug("errWRITE ");
++ dev->dfu_status = DFU_STATUS_errWRITE;
++ dev->dfu_state = DFU_STATE_dfuERROR;
++ } else if (fsr & AT91C_MC_FRDY) {
++#endif
++ debug("DNLOAD_IDLE ");
++ dev->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
++#if 0
++ } else {
++ debug("DNBUSY ");
++ dev->dfu_state = DFU_STATE_dfuDNBUSY;
++ }
++#endif
++ break;
++ case DFU_STATE_dfuMANIFEST_SYNC:
++ break;
++ default:
++ //return;
++ break;
++ }
++
++ /* send status response */
++ dstat->bStatus = dev->dfu_status;
++ dstat->bState = dev->dfu_state;
++ dstat->iString = 0;
++ /* FIXME: set dstat->bwPollTimeout */
++ urb->actual_length = MIN(sizeof(*dstat), max);
++
++ /* we don't need to explicitly send data here, will
++ * be done by the original caller! */
++}
++
++static void handle_getstate(struct urb *urb, int max)
++{
++ debug("getstate ");
++
++ if (!urb->buffer || urb->buffer_length < sizeof(u_int8_t)) {
++ debug("invalid urb! ");
++ return;
++ }
++
++ urb->buffer[0] = urb->device->dfu_state & 0xff;
++ urb->actual_length = sizeof(u_int8_t);
++}
++
++#ifndef CONFIG_USBD_PRODUCTID_DFU
++#define CONFIG_USBD_PRODUCTID_DFU CONFIG_USBD_PRODUCTID_CDCACM
++#endif
++
++static const struct usb_device_descriptor dfu_dev_descriptor = {
++ .bLength = USB_DT_DEVICE_SIZE,
++ .bDescriptorType = USB_DT_DEVICE,
++ .bcdUSB = 0x0100,
++ .bDeviceClass = 0x00,
++ .bDeviceSubClass = 0x00,
++ .bDeviceProtocol = 0x00,
++ .bMaxPacketSize0 = EP0_MAX_PACKET_SIZE,
++ .idVendor = CONFIG_USBD_VENDORID,
++ .idProduct = CONFIG_USBD_PRODUCTID_DFU,
++ .bcdDevice = 0x0000,
++ .iManufacturer = DFU_STR_MANUFACTURER,
++ .iProduct = DFU_STR_PRODUCT,
++ .iSerialNumber = DFU_STR_SERIAL,
++ .bNumConfigurations = 0x01,
++};
++
++static const 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 = 5,
++ .bConfigurationValue = 1,
++ .iConfiguration = DFU_STR_CONFIG,
++ .bmAttributes = BMATTRIBUTE_RESERVED,
++ .bMaxPower = 50,
++ },
++ .uif[0] = {
++ .bLength = USB_DT_INTERFACE_SIZE,
++ .bDescriptorType = USB_DT_INTERFACE,
++ .bInterfaceNumber = 0x00,
++ .bAlternateSetting = 0x00,
++ .bNumEndpoints = 0x00,
++ .bInterfaceClass = 0xfe,
++ .bInterfaceSubClass = 0x01,
++ .bInterfaceProtocol = 0x02,
++ .iInterface = DFU_STR_ALT0,
++ },
++ .uif[1] = {
++ .bLength = USB_DT_INTERFACE_SIZE,
++ .bDescriptorType = USB_DT_INTERFACE,
++ .bInterfaceNumber = 0x00,
++ .bAlternateSetting = 0x01,
++ .bNumEndpoints = 0x00,
++ .bInterfaceClass = 0xfe,
++ .bInterfaceSubClass = 0x01,
++ .bInterfaceProtocol = 0x02,
++ .iInterface = DFU_STR_ALT1,
++ },
++ .uif[2] = {
++ .bLength = USB_DT_INTERFACE_SIZE,
++ .bDescriptorType = USB_DT_INTERFACE,
++ .bInterfaceNumber = 0x00,
++ .bAlternateSetting = 0x02,
++ .bNumEndpoints = 0x00,
++ .bInterfaceClass = 0xfe,
++ .bInterfaceSubClass = 0x01,
++ .bInterfaceProtocol = 0x02,
++ .iInterface = DFU_STR_ALT2,
++ },
++ .uif[3] = {
++ .bLength = USB_DT_INTERFACE_SIZE,
++ .bDescriptorType = USB_DT_INTERFACE,
++ .bInterfaceNumber = 0x00,
++ .bAlternateSetting = 0x03,
++ .bNumEndpoints = 0x00,
++ .bInterfaceClass = 0xfe,
++ .bInterfaceSubClass = 0x01,
++ .bInterfaceProtocol = 0x02,
++ .iInterface = DFU_STR_ALT3,
++ },
++ .uif[4] = {
++ .bLength = USB_DT_INTERFACE_SIZE,
++ .bDescriptorType = USB_DT_INTERFACE,
++ .bInterfaceNumber = 0x00,
++ .bAlternateSetting = 0x04,
++ .bNumEndpoints = 0x00,
++ .bInterfaceClass = 0xfe,
++ .bInterfaceSubClass = 0x01,
++ .bInterfaceProtocol = 0x02,
++ .iInterface = DFU_STR_ALT4,
++ },
++ .uif[5] = {
++ .bLength = USB_DT_INTERFACE_SIZE,
++ .bDescriptorType = USB_DT_INTERFACE,
++ .bInterfaceNumber = 0x00,
++ .bAlternateSetting = 0x05,
++ .bNumEndpoints = 0x00,
++ .bInterfaceClass = 0xfe,
++ .bInterfaceSubClass = 0x01,
++ .bInterfaceProtocol = 0x02,
++ .iInterface = DFU_STR_ALT5,
++ },
++ .func_dfu = DFU_FUNC_DESC,
++};
++
++int dfu_ep0_handler(struct urb *urb)
++{
++ int rc, ret = RET_NOTHING;
++ u_int8_t req = urb->device_request.bRequest;
++ u_int16_t val = urb->device_request.wValue;
++ u_int16_t len = urb->device_request.wLength;
++ struct usb_device_instance *dev = urb->device;
++
++ debug("dfu_ep0(req=0x%x, val=0x%x, len=%u) old_state = %u ",
++ req, val, len, dev->dfu_state);
++
++ switch (dev->dfu_state) {
++ case DFU_STATE_appIDLE:
++ switch (req) {
++ case USB_REQ_DFU_GETSTATUS:
++ handle_getstatus(urb, len);
++ break;
++ case USB_REQ_DFU_GETSTATE:
++ handle_getstate(urb, len);
++ break;
++ case USB_REQ_DFU_DETACH:
++ dev->dfu_state = DFU_STATE_appDETACH;
++ ret = RET_ZLP;
++ goto out;
++ break;
++ default:
++ ret = RET_STALL;
++ }
++ break;
++ case DFU_STATE_appDETACH:
++ switch (req) {
++ case USB_REQ_DFU_GETSTATUS:
++ handle_getstatus(urb, len);
++ break;
++ case USB_REQ_DFU_GETSTATE:
++ handle_getstate(urb, len);
++ break;
++ default:
++ dev->dfu_state = DFU_STATE_appIDLE;
++ ret = RET_STALL;
++ goto out;
++ break;
++ }
++ /* FIXME: implement timer to return to appIDLE */
++ break;
++ case DFU_STATE_dfuIDLE:
++ switch (req) {
++ case USB_REQ_DFU_DNLOAD:
++ if (len == 0) {
++ dev->dfu_state = DFU_STATE_dfuERROR;
++ ret = RET_STALL;
++ goto out;
++ }
++ dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
++ ret = handle_dnload(urb, val, len, 1);
++ break;
++ case USB_REQ_DFU_UPLOAD:
++ dev->dfu_state = DFU_STATE_dfuUPLOAD_IDLE;
++ handle_upload(urb, val, len, 1);
++ break;
++ case USB_REQ_DFU_ABORT:
++ /* no zlp? */
++ ret = RET_ZLP;
++ break;
++ case USB_REQ_DFU_GETSTATUS:
++ handle_getstatus(urb, len);
++ break;
++ case USB_REQ_DFU_GETSTATE:
++ handle_getstate(urb, len);
++ break;
++ case USB_REQ_DFU_DETACH:
++ /* Proprietary extension: 'detach' from idle mode and
++ * get back to runtime mode in case of USB Reset. As
++ * much as I dislike this, we just can't use every USB
++ * bus reset to switch back to runtime mode, since at
++ * least the Linux USB stack likes to send a number of resets
++ * in a row :( */
++ dev->dfu_state = DFU_STATE_dfuMANIFEST_WAIT_RST;
++ break;
++ default:
++ dev->dfu_state = DFU_STATE_dfuERROR;
++ ret = RET_STALL;
++ goto out;
++ break;
++ }
++ break;
++ case DFU_STATE_dfuDNLOAD_SYNC:
++ switch (req) {
++ case USB_REQ_DFU_GETSTATUS:
++ handle_getstatus(urb, len);
++ /* FIXME: state transition depending on block completeness */
++ break;
++ case USB_REQ_DFU_GETSTATE:
++ handle_getstate(urb, len);
++ break;