From 491b936c67225ef311a58b799c9e4280fe523fec Mon Sep 17 00:00:00 2001 From: Andrea Adami Date: Thu, 7 May 2009 23:55:30 +0200 Subject: zaurus-clamshell.inc: remove unused KEXECBOOT_IMAGEDEPENDS - KEXECBOOT_IMAGEDEPENDS was introduced just for Zaurus (fixed now) --- conf/machine/include/zaurus-clamshell.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/conf/machine/include/zaurus-clamshell.inc b/conf/machine/include/zaurus-clamshell.inc index 34e803e418..4fa9f75eb3 100644 --- a/conf/machine/include/zaurus-clamshell.inc +++ b/conf/machine/include/zaurus-clamshell.inc @@ -1,7 +1,6 @@ include conf/machine/include/tune-xscale.inc EXTRA_IMAGEDEPENDS += "sharp-flash-header-c700" -KEXECBOOT_IMAGEDEPENDS += "sharp-flash-header-c700" IMAGE_CMD_jffs2 = "mkfs.jffs2 --root=${IMAGE_ROOTFS} --faketime -m favourlzo \ --output=${T}/${IMAGE_NAME}.rootfs.jffs2 --pad --little-endian --eraseblock=${ERASEBLOCKSIZE} -n \ -- cgit v1.2.3 From 8fecb42426d33e78eee8c332455a3c5b2fcdd2fd Mon Sep 17 00:00:00 2001 From: Koen Kooi Date: Fri, 8 May 2009 10:36:15 +0200 Subject: u-boot: add dfu patches for beagleboard --- recipes/u-boot/files/dfu-second-patch.patch | 53 + recipes/u-boot/files/omap-usb-dev-dfu.patch | 14478 ++++++++++++++++++++++++++ recipes/u-boot/u-boot_git.bb | 8 +- 3 files changed, 14536 insertions(+), 3 deletions(-) create mode 100644 recipes/u-boot/files/dfu-second-patch.patch create mode 100644 recipes/u-boot/files/omap-usb-dev-dfu.patch 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 +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 +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 ++#include + + #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 + #include "usbdcore.h" ++#include + + #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 + #include "usbdcore.h" ++#ifdef CONFIG_USBD_DFU ++#include ++#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 ++ * ++ * based on existing SAM7DFU code from OpenPCD: ++ * (C) Copyright 2006 by Harald Welte ++ * ++ * 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 ++#if defined(CONFIG_USBD_DFU) ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++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; ++ default: ++ dev->dfu_state = DFU_STATE_dfuERROR; ++ ret = RET_STALL; ++ goto out; ++ } ++ break; ++ case DFU_STATE_dfuDNBUSY: ++ switch (req) { ++ case USB_REQ_DFU_GETSTATUS: ++ /* FIXME: only accept getstatus if bwPollTimeout ++ * has elapsed */ ++ handle_getstatus(urb, len); ++ break; ++ default: ++ dev->dfu_state = DFU_STATE_dfuERROR; ++ ret = RET_STALL; ++ goto out; ++ } ++ break; ++ case DFU_STATE_dfuDNLOAD_IDLE: ++ switch (req) { ++ case USB_REQ_DFU_DNLOAD: ++ dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; ++ ret = handle_dnload(urb, val, len, 0); ++ break; ++ case USB_REQ_DFU_ABORT: ++ dev->dfu_state = DFU_STATE_dfuIDLE; ++ ret = RET_ZLP; ++ break; ++ case USB_REQ_DFU_GETSTATUS: ++ handle_getstatus(urb, len); ++ break; ++ case USB_REQ_DFU_GETSTATE: ++ handle_getstate(urb, len); ++ break; ++ default: ++ dev->dfu_state = DFU_STATE_dfuERROR; ++ ret = RET_STALL; ++ break; ++ } ++ break; ++ case DFU_STATE_dfuMANIFEST_SYNC: ++ switch (req) { ++ case USB_REQ_DFU_GETSTATUS: ++ /* We're MainfestationTolerant */ ++ dev->dfu_state = DFU_STATE_dfuIDLE; ++ handle_getstatus(urb, len); ++ break; ++ case USB_REQ_DFU_GETSTATE: ++ handle_getstate(urb, len); ++ break; ++ default: ++ dev->dfu_state = DFU_STATE_dfuERROR; ++ ret = RET_STALL; ++ break; ++ } ++ break; ++ case DFU_STATE_dfuMANIFEST: ++ /* we should never go here */ ++ dev->dfu_state = DFU_STATE_dfuERROR; ++ ret = RET_STALL; ++ break; ++ case DFU_STATE_dfuMANIFEST_WAIT_RST: ++ /* we should never go here */ ++ break; ++ case DFU_STATE_dfuUPLOAD_IDLE: ++ switch (req) { ++ case USB_REQ_DFU_UPLOAD: ++ /* state transition if less data then requested */ ++ rc = handle_upload(urb, val, len, 0); ++ if (rc >= 0 && rc < len) ++ dev->dfu_state = DFU_STATE_dfuIDLE; ++ break; ++ case USB_REQ_DFU_ABORT: ++ dev->dfu_state = DFU_STATE_dfuIDLE; ++ /* no zlp? */ ++ ret = RET_ZLP; ++ break; ++ case USB_REQ_DFU_GETSTATUS: ++ handle_getstatus(urb, len); ++ break; ++ case USB_REQ_DFU_GETSTATE: ++ handle_getstate(urb, len); ++ break; ++ default: ++ dev->dfu_state = DFU_STATE_dfuERROR; ++ ret = RET_STALL; ++ break; ++ } ++ break; ++ case DFU_STATE_dfuERROR: ++ switch (req) { ++ case USB_REQ_DFU_GETSTATUS: ++ handle_getstatus(urb, len); ++ break; ++ case USB_REQ_DFU_GETSTATE: ++ handle_getstate(urb, len); ++ break; ++ case USB_REQ_DFU_CLRSTATUS: ++ dev->dfu_state = DFU_STATE_dfuIDLE; ++ dev->dfu_status = DFU_STATUS_OK; ++ /* no zlp? */ ++ ret = RET_ZLP; ++ break; ++ default: ++ dev->dfu_state = DFU_STATE_dfuERROR; ++ ret = RET_STALL; ++ break; ++ } ++ break; ++ default: ++ return DFU_EP0_UNHANDLED; ++ break; ++ } ++ ++out: ++ debug("new_state = %u, ret = %u\n", dev->dfu_state, ret); ++ ++ switch (ret) { ++ case RET_ZLP: ++ //udc_ep0_send_zlp(); ++ urb->actual_length = 0; ++ return DFU_EP0_ZLP; ++ break; ++ case RET_STALL: ++ //udc_ep0_send_stall(); ++ return DFU_EP0_STALL; ++ break; ++ case RET_NOTHING: ++ break; ++ } ++ ++ return DFU_EP0_DATA; ++} ++ ++void str2wide (char *str, u16 * wide); ++static struct usb_string_descriptor *create_usbstring(char *string) ++{ ++ struct usb_string_descriptor *strdesc; ++ int size = sizeof(*strdesc) + strlen(string)*2; ++ ++ if (size > 255) ++ return NULL; ++ ++ strdesc = malloc(size); ++ if (!strdesc) ++ return NULL; ++ ++ strdesc->bLength = size; ++ strdesc->bDescriptorType = USB_DT_STRING; ++ str2wide(string, strdesc->wData); ++ ++ return strdesc; ++} ++ ++ ++static void dfu_init_strings(struct usb_device_instance *dev) ++{ ++ int i; ++ struct usb_string_descriptor *strdesc; ++ ++ strdesc = create_usbstring(CONFIG_DFU_CFG_STR); ++ usb_strings[DFU_STR_CONFIG] = strdesc; ++ ++ for (i = 0; i < DFU_NUM_ALTERNATES; i++) { ++ if (i == 0) { ++ strdesc = create_usbstring(CONFIG_DFU_ALT0_STR); ++ } else { ++ struct part_info *part = get_partition_nand(i-1); ++ ++ if (part) ++ strdesc = create_usbstring(part->name); ++ else ++ strdesc = ++ create_usbstring("undefined partition"); ++ } ++ if (!strdesc) ++ continue; ++ usb_strings[STR_COUNT+i+1] = strdesc; ++ } ++} ++ ++int dfu_init_instance(struct usb_device_instance *dev) ++{ ++ dev->dfu_dev_desc = &dfu_dev_descriptor; ++ dev->dfu_cfg_desc = &dfu_cfg_descriptor; ++ dev->dfu_state = DFU_STATE_appIDLE; ++ dev->dfu_status = DFU_STATUS_OK; ++ ++ if (system_dfu_state) ++ printf("SURPRISE: system_dfu_state is already set\n"); ++ system_dfu_state = &dev->dfu_state; ++ ++ dfu_init_strings(dev); ++ ++ return 0; ++} ++ ++static int stdout_switched; ++ ++/* event handler for usb device state events */ ++void dfu_event(struct usb_device_instance *device, ++ usb_device_event_t event, int data) ++{ ++ char *out; ++ ++ switch (event) { ++ case DEVICE_RESET: ++ switch (device->dfu_state) { ++ case DFU_STATE_appDETACH: ++ device->dfu_state = DFU_STATE_dfuIDLE; ++ out = getenv("stdout"); ++ if (out && !strcmp(out, "usbtty")) { ++ setenv("stdin", "serial"); ++ setenv("stdout", "serial"); ++ setenv("stderr", "serial"); ++ stdout_switched = 1; ++ } ++ printf("DFU: Switching to DFU Mode\n"); ++ break; ++ case DFU_STATE_dfuMANIFEST_WAIT_RST: ++ device->dfu_state = DFU_STATE_appIDLE; ++ printf("DFU: Switching back to Runtime mode\n"); ++ if (stdout_switched) { ++ setenv("stdin", "usbtty"); ++ setenv("stdout", "usbtty"); ++ setenv("stderr", "usbtty"); ++ stdout_switched = 0; ++ } ++ break; ++ default: ++ break; ++ } ++ break; ++ case DEVICE_CONFIGURED: ++ case DEVICE_DE_CONFIGURED: ++ debug("SET_CONFIGURATION(%u) ", device->configuration); ++ /* fallthrough */ ++ case DEVICE_SET_INTERFACE: ++ debug("SET_INTERFACE(%u,%u) old_state = %u ", ++ device->interface, device->alternate, ++ device->dfu_state); ++ switch (device->dfu_state) { ++ case DFU_STATE_appIDLE: ++ case DFU_STATE_appDETACH: ++ case DFU_STATE_dfuIDLE: ++ case DFU_STATE_dfuMANIFEST_WAIT_RST: ++ /* do nothing, we're fine */ ++ break; ++ case DFU_STATE_dfuDNLOAD_SYNC: ++ case DFU_STATE_dfuDNBUSY: ++ case DFU_STATE_dfuDNLOAD_IDLE: ++ case DFU_STATE_dfuMANIFEST: ++ device->dfu_state = DFU_STATE_dfuERROR; ++ device->dfu_status = DFU_STATUS_errNOTDONE; ++ /* FIXME: free malloc()ed buffer! */ ++ break; ++ case DFU_STATE_dfuMANIFEST_SYNC: ++ case DFU_STATE_dfuUPLOAD_IDLE: ++ case DFU_STATE_dfuERROR: ++ device->dfu_state = DFU_STATE_dfuERROR; ++ device->dfu_status = DFU_STATUS_errUNKNOWN; ++ break; ++ } ++ debug("new_state = %u\n", device->dfu_state); ++ break; ++ default: ++ break; ++ } ++} ++#endif /* CONFIG_USBD_DFU */ +diff --git a/include/configs/omap3_beagle.h b/include/configs/omap3_beagle.h +index 5641ddb..0086618 100644 +--- a/include/configs/omap3_beagle.h ++++ b/include/configs/omap3_beagle.h +@@ -108,6 +108,11 @@ + #define CONFIG_MUSB 1 /* Enable USB driver*/ + #define CONFIG_TWL4030_USB 1 /* Enable TWL4030 USB */ + ++#define CONFIG_USBD_DFU 1 /* Enable DFU support */ ++#define CONFIG_USBD_DFU_XFER_SIZE 0x4000 ++#define CONFIG_USBD_DFU_INTERFACE 2 ++#define CONFIG_USBD_DFU_LOAD_ADDR 0x82000000 ++ + /* Allow console in serial and USB at the same time */ + #define CONFIG_CONSOLE_MUX 1 + #define CONFIG_SYS_CONSOLE_IS_IN_ENV 1 +@@ -164,12 +169,8 @@ + #define CONFIG_SYS_NAND_WP + + #define CONFIG_JFFS2_NAND +-/* nand device jffs2 lives on */ +-#define CONFIG_JFFS2_DEV "nand0" +-/* start of jffs2 partition */ +-#define CONFIG_JFFS2_PART_OFFSET 0x680000 +-#define CONFIG_JFFS2_PART_SIZE 0xf980000 /* size of jffs2 */ +- /* partition */ ++#define CONFIG_JFFS2_CMDLINE ++#define MTDPARTS_DEFAULT "mtdparts=nand0:32k(x-loader),2400k(u-boot),128k(u-boot_env),4096(kernel),-(fs)" + + /* Environment information */ + #define CONFIG_BOOTDELAY 10 +@@ -196,7 +197,7 @@ + "bootm ${loadaddr}\0" \ + "nandboot=echo Booting from nand ...; " \ + "run nandargs; " \ +- "nand read ${loadaddr} 280000 400000; " \ ++ "nboot kernel; " \ + "bootm ${loadaddr}\0" \ + "usbtty=cdc_acm\0"\ + "stdout=serial,usbtty\0" \ +diff --git a/include/usb_dfu.h b/include/usb_dfu.h +new file mode 100644 +index 0000000..4904196 +--- /dev/null ++++ b/include/usb_dfu.h +@@ -0,0 +1,113 @@ ++#ifndef _DFU_H ++#define _DFU_H ++ ++/* USB Device Firmware Update Implementation for u-boot ++ * (C) 2007 by OpenMoko, Inc. ++ * Author: Harald Welte ++ * ++ * based on: USB Device Firmware Update Implementation for OpenPCD ++ * (C) 2006 by Harald Welte ++ * ++ * 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 ++#include ++#include ++#include ++ ++/* 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, \ ++ .wDetachTimeOut = 0xff00, \ ++ .wTransferSize = CONFIG_USBD_DFU_XFER_SIZE, \ ++ .bcdDFUVersion = 0x0100, \ ++} ++ ++/* USB Interface descriptor in Runtime mode */ ++#ifdef CONFIG_USB_STRING ++#define DFU_RT_IF_DESC { \ ++ .bLength = USB_DT_INTERFACE_SIZE, \ ++ .bDescriptorType = USB_DT_INTERFACE, \ ++ .bInterfaceNumber = CONFIG_USBD_DFU_INTERFACE, \ ++ .bAlternateSetting = 0x00, \ ++ .bNumEndpoints = 0x00, \ ++ .bInterfaceClass = 0xfe, \ ++ .bInterfaceSubClass = 0x01, \ ++ .bInterfaceProtocol = 0x01, \ ++ .iInterface = 1, \ ++} ++#else ++#define DFU_RT_IF_DESC { \ ++ .bLength = USB_DT_INTERFACE_SIZE, \ ++ .bDescriptorType = USB_DT_INTERFACE, \ ++ .bInterfaceNumber = CONFIG_USBD_DFU_INTERFACE, \ ++ .bAlternateSetting = 0x00, \ ++ .bNumEndpoints = 0x00, \ ++ .bInterfaceClass = 0xfe, \ ++ .bInterfaceSubClass = 0x01, \ ++ .bInterfaceProtocol = 0x01, \ ++ .iInterface = 0, \ ++} ++#endif ++ ++#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) ++ ++#define DFU_NUM_ALTERNATES 6 ++ ++#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_ALT0 (STR_COUNT+1) ++#define DFU_STR_ALT1 (STR_COUNT+2) ++#define DFU_STR_ALT2 (STR_COUNT+3) ++#define DFU_STR_ALT3 (STR_COUNT+4) ++#define DFU_STR_ALT4 (STR_COUNT+5) ++#define DFU_STR_ALT5 (STR_COUNT+6) ++#define DFU_STR_COUNT (STR_COUNT+7) ++ ++#define DFU_NUM_STRINGS (STR_COUNT+8) ++ ++#define CONFIG_DFU_CFG_STR "USB Device Firmware Upgrade" ++#define CONFIG_DFU_ALT0_STR "RAM CONFIG_USBD_DFU_LOAD_ADDR" ++ ++struct _dfu_desc { ++ struct usb_configuration_descriptor ucfg; ++ struct usb_interface_descriptor uif[DFU_NUM_ALTERNATES]; ++ struct usb_dfu_func_descriptor func_dfu; ++}; ++ ++int dfu_init_instance(struct usb_device_instance *dev); ++ ++#define DFU_EP0_NONE 0 ++#define DFU_EP0_UNHANDLED 1 ++#define DFU_EP0_STALL 2 ++#define DFU_EP0_ZLP 3 ++#define DFU_EP0_DATA 4 ++ ++extern volatile enum dfu_state *system_dfu_state; /* for 3rd parties */ ++ ++int dfu_ep0_handler(struct urb *urb); ++ ++void dfu_event(struct usb_device_instance *device, ++ usb_device_event_t event, int data); ++ ++#endif /* _DFU_H */ +diff --git a/include/usb_dfu_descriptors.h b/include/usb_dfu_descriptors.h +new file mode 100644 +index 0000000..d4d6a4d +--- /dev/null ++++ b/include/usb_dfu_descriptors.h +@@ -0,0 +1,94 @@ ++#ifndef _USB_DFU_H ++#define _USB_DFU_H ++/* USB Device Firmware Update Implementation for OpenPCD ++ * (C) 2006 by Harald Welte ++ * ++ * 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 ++ ++#define USB_DT_DFU 0x21 ++ ++struct usb_dfu_func_descriptor { ++ u_int8_t bLength; ++ u_int8_t bDescriptorType; ++ u_int8_t bmAttributes; ++#define USB_DFU_CAN_DOWNLOAD (1 << 0) ++#define USB_DFU_CAN_UPLOAD (1 << 1) ++#define USB_DFU_MANIFEST_TOL (1 << 2) ++#define USB_DFU_WILL_DETACH (1 << 3) ++ u_int16_t wDetachTimeOut; ++ u_int16_t wTransferSize; ++ u_int16_t bcdDFUVersion; ++} __attribute__ ((packed)); ++ ++#define USB_DT_DFU_SIZE 9 ++ ++#define USB_TYPE_DFU (USB_TYPE_CLASS|USB_RECIP_INTERFACE) ++ ++/* DFU class-specific requests (Section 3, DFU Rev 1.1) */ ++#define USB_REQ_DFU_DETACH 0x00 ++#define USB_REQ_DFU_DNLOAD 0x01 ++#define USB_REQ_DFU_UPLOAD 0x02 ++#define USB_REQ_DFU_GETSTATUS 0x03 ++#define USB_REQ_DFU_CLRSTATUS 0x04 ++#define USB_REQ_DFU_GETSTATE 0x05 ++#define USB_REQ_DFU_ABORT 0x06 ++ ++struct dfu_status { ++ u_int8_t bStatus; ++ u_int8_t bwPollTimeout[3]; ++ u_int8_t bState; ++ u_int8_t iString; ++} __attribute__((packed)); ++ ++#define DFU_STATUS_OK 0x00 ++#define DFU_STATUS_errTARGET 0x01 ++#define DFU_STATUS_errFILE 0x02 ++#define DFU_STATUS_errWRITE 0x03 ++#define DFU_STATUS_errERASE 0x04 ++#define DFU_STATUS_errCHECK_ERASED 0x05 ++#define DFU_STATUS_errPROG 0x06 ++#define DFU_STATUS_errVERIFY 0x07 ++#define DFU_STATUS_errADDRESS 0x08 ++#define DFU_STATUS_errNOTDONE 0x09 ++#define DFU_STATUS_errFIRMWARE 0x0a ++#define DFU_STATUS_errVENDOR 0x0b ++#define DFU_STATUS_errUSBR 0x0c ++#define DFU_STATUS_errPOR 0x0d ++#define DFU_STATUS_errUNKNOWN 0x0e ++#define DFU_STATUS_errSTALLEDPKT 0x0f ++ ++enum dfu_state { ++ DFU_STATE_appIDLE = 0, ++ DFU_STATE_appDETACH = 1, ++ DFU_STATE_dfuIDLE = 2, ++ DFU_STATE_dfuDNLOAD_SYNC = 3, ++ DFU_STATE_dfuDNBUSY = 4, ++ DFU_STATE_dfuDNLOAD_IDLE = 5, ++ DFU_STATE_dfuMANIFEST_SYNC = 6, ++ DFU_STATE_dfuMANIFEST = 7, ++ DFU_STATE_dfuMANIFEST_WAIT_RST = 8, ++ DFU_STATE_dfuUPLOAD_IDLE = 9, ++ DFU_STATE_dfuERROR = 10, ++}; ++ ++#endif /* _USB_DFU_H */ +diff --git a/include/usb_dfu_trailer.h b/include/usb_dfu_trailer.h +new file mode 100644 +index 0000000..3903b85 +--- /dev/null ++++ b/include/usb_dfu_trailer.h +@@ -0,0 +1,31 @@ ++#ifndef _USB_DFU_TRAILER_H ++#define _USB_DFU_TRAILER_H ++ ++/* trailer handling for DFU files */ ++ ++#define UBOOT_DFU_TRAILER_V1 1 ++#define UBOOT_DFU_TRAILER_MAGIC 0x19731978 ++struct uboot_dfu_trailer { ++ u_int32_t magic; ++ u_int16_t version; ++ u_int16_t length; ++ u_int16_t vendor; ++ u_int16_t product; ++ u_int32_t revision; ++} __attribute__((packed)); ++ ++/* we mirror the trailer because we want it to be longer in later versions ++ * while keeping backwards compatibility */ ++static inline void dfu_trailer_mirror(struct uboot_dfu_trailer *trailer, ++ unsigned char *eof) ++{ ++ int i; ++ int len = sizeof(struct uboot_dfu_trailer); ++ unsigned char *src = eof - len; ++ unsigned char *dst = (unsigned char *) trailer; ++ ++ for (i = 0; i < len; i++) ++ dst[len-1-i] = src[i]; ++} ++ ++#endif /* _USB_DFU_TRAILER_H */ +diff --git a/include/usbdcore.h b/include/usbdcore.h +index 206dbbc..e966f34 100644 +--- a/include/usbdcore.h ++++ b/include/usbdcore.h +@@ -33,6 +33,7 @@ + + #include + #include "usbdescriptors.h" ++#include + + + #define MAX_URBS_QUEUED 5 +@@ -467,7 +468,11 @@ typedef struct urb_link { + * function driver to inform it that data has arrived. + */ + ++#ifdef CONFIG_USBD_DFU ++#define URB_BUF_SIZE (128+CONFIG_USBD_DFU_XFER_SIZE) ++#else + #define URB_BUF_SIZE 128 /* in linux we'd malloc this, but in u-boot we prefer static data */ ++#endif + struct urb { + + struct usb_endpoint_instance *endpoint; +@@ -595,6 +600,12 @@ struct usb_device_instance { + unsigned long usbd_rxtx_timestamp; + unsigned long usbd_last_rxtx_timestamp; + ++#ifdef CONFIG_USBD_DFU ++ struct usb_device_descriptor *dfu_dev_desc; ++ struct _dfu_desc *dfu_cfg_desc; ++ enum dfu_state dfu_state; ++ u_int8_t dfu_status; ++#endif + }; + + /* Bus Interface configuration structure +@@ -624,6 +635,8 @@ extern char *usbd_device_status[]; + extern char *usbd_device_requests[]; + extern char *usbd_device_descriptors[]; + ++extern struct usb_string_descriptor **usb_strings; ++ + void urb_link_init (urb_link * ul); + void urb_detach (struct urb *urb); + urb_link *first_urb_link (urb_link * hd); +diff --git a/tools/dfu-util/COPYING b/tools/dfu-util/COPYING +new file mode 100644 +index 0000000..d60c31a +--- /dev/null ++++ b/tools/dfu-util/COPYING +@@ -0,0 +1,340 @@ ++ GNU GENERAL PUBLIC LICENSE ++ Version 2, June 1991 ++ ++ Copyright (C) 1989, 1991 Free Software Foundation, Inc. ++ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ Everyone is permitted to copy and distribute verbatim copies ++ of this license document, but changing it is not allowed. ++ ++ Preamble ++ ++ The licenses for most software are designed to take away your ++freedom to share and change it. By contrast, the GNU General Public ++License is intended to guarantee your freedom to share and change free ++software--to make sure the software is free for all its users. This ++General Public License applies to most of the Free Software ++Foundation's software and to any other program whose authors commit to ++using it. (Some other Free Software Foundation software is covered by ++the GNU Library General Public License instead.) You can apply it to ++your programs, too. ++ ++ When we speak of free software, we are referring to freedom, not ++price. Our General Public Licenses are designed to make sure that you ++have the freedom to distribute copies of free software (and charge for ++this service if you wish), that you receive source code or can get it ++if you want it, that you can change the software or use pieces of it ++in new free programs; and that you know you can do these things. ++ ++ To protect your rights, we need to make restrictions that forbid ++anyone to deny you these rights or to ask you to surrender the rights. ++These restrictions translate to certain responsibilities for you if you ++distribute copies of the software, or if you modify it. ++ ++ For example, if you distribute copies of such a program, whether ++gratis or for a fee, you must give the recipients all the rights that ++you have. You must make sure that they, too, receive or can get the ++source code. And you must show them these terms so they know their ++rights. ++ ++ We protect your rights with two steps: (1) copyright the software, and ++(2) offer you this license which gives you legal permission to copy, ++distribute and/or modify the software. ++ ++ Also, for each author's protection and ours, we want to make certain ++that everyone understands that there is no warranty for this free ++software. If the software is modified by someone else and passed on, we ++want its recipients to know that what they have is not the original, so ++that any problems introduced by others will not reflect on the original ++authors' reputations. ++ ++ Finally, any free program is threatened constantly by software ++patents. We wish to avoid the danger that redistributors of a free ++program will individually obtain patent licenses, in effect making the ++program proprietary. To prevent this, we have made it clear that any ++patent must be licensed for everyone's free use or not licensed at all. ++ ++ The precise terms and conditions for copying, distribution and ++modification follow. ++ ++ GNU GENERAL PUBLIC LICENSE ++ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION ++ ++ 0. This License applies to any program or other work which contains ++a notice placed by the copyright holder saying it may be distributed ++under the terms of this General Public License. The "Program", below, ++refers to any such program or work, and a "work based on the Program" ++means either the Program or any derivative work under copyright law: ++that is to say, a work containing the Program or a portion of it, ++either verbatim or with modifications and/or translated into another ++language. (Hereinafter, translation is included without limitation in ++the term "modification".) Each licensee is addressed as "you". ++ ++Activities other than copying, distribution and modification are not ++covered by this License; they are outside its scope. The act of ++running the Program is not restricted, and the output from the Program ++is covered only if its contents constitute a work based on the ++Program (independent of having been made by running the Program). ++Whether that is true depends on what the Program does. ++ ++ 1. You may copy and distribute verbatim copies of the Program's ++source code as you receive it, in any medium, provided that you ++conspicuously and appropriately publish on each copy an appropriate ++copyright notice and disclaimer of warranty; keep intact all the ++notices that refer to this License and to the absence of any warranty; ++and give any other recipients of the Program a copy of this License ++along with the Program. ++ ++You may charge a fee for the physical act of transferring a copy, and ++you may at your option offer warranty protection in exchange for a fee. ++ ++ 2. You may modify your copy or copies of the Program or any portion ++of it, thus forming a work based on the Program, and copy and ++distribute such modifications or work under the terms of Section 1 ++above, provided that you also meet all of these conditions: ++ ++ a) You must cause the modified files to carry prominent notices ++ stating that you changed the files and the date of any change. ++ ++ b) You must cause any work that you distribute or publish, that in ++ whole or in part contains or is derived from the Program or any ++ part thereof, to be licensed as a whole at no charge to all third ++ parties under the terms of this License. ++ ++ c) If the modified program normally reads commands interactively ++ when run, you must cause it, when started running for such ++ interactive use in the most ordinary way, to print or display an ++ announcement including an appropriate copyright notice and a ++ notice that there is no warranty (or else, saying that you provide ++ a warranty) and that users may redistribute the program under ++ these conditions, and telling the user how to view a copy of this ++ License. (Exception: if the Program itself is interactive but ++ does not normally print such an announcement, your work based on ++ the Program is not required to print an announcement.) ++ ++These requirements apply to the modified work as a whole. If ++identifiable sections of that work are not derived from the Program, ++and can be reasonably considered independent and separate works in ++themselves, then this License, and its terms, do not apply to those ++sections when you distribute them as separate works. But when you ++distribute the same sections as part of a whole which is a work based ++on the Program, the distribution of the whole must be on the terms of ++this License, whose permissions for other licensees extend to the ++entire whole, and thus to each and every part regardless of who wrote it. ++ ++Thus, it is not the intent of this section to claim rights or contest ++your rights to work written entirely by you; rather, the intent is to ++exercise the right to control the distribution of derivative or ++collective works based on the Program. ++ ++In addition, mere aggregation of another work not based on the Program ++with the Program (or with a work based on the Program) on a volume of ++a storage or distribution medium does not bring the other work under ++the scope of this License. ++ ++ 3. You may copy and distribute the Program (or a work based on it, ++under Section 2) in object code or executable form under the terms of ++Sections 1 and 2 above provided that you also do one of the following: ++ ++ a) Accompany it with the complete corresponding machine-readable ++ source code, which must be distributed under the terms of Sections ++ 1 and 2 above on a medium customarily used for software interchange; or, ++ ++ b) Accompany it with a written offer, valid for at least three ++ years, to give any third party, for a charge no more than your ++ cost of physically performing source distribution, a complete ++ machine-readable copy of the corresponding source code, to be ++ distributed under the terms of Sections 1 and 2 above on a medium ++ customarily used for software interchange; or, ++ ++ c) Accompany it with the information you received as to the offer ++ to distribute corresponding source code. (This alternative is ++ allowed only for noncommercial distribution and only if you ++ received the program in object code or executable form with such ++ an offer, in accord with Subsection b above.) ++ ++The source code for a work means the preferred form of the work for ++making modifications to it. For an executable work, complete source ++code means all the source code for all modules it contains, plus any ++associated interface definition files, plus the scripts used to ++control compilation and installation of the executable. However, as a ++special exception, the source code distributed need not include ++anything that is normally distributed (in either source or binary ++form) with the major components (compiler, kernel, and so on) of the ++operating system on which the executable runs, unless that component ++itself accompanies the executable. ++ ++If distribution of executable or object code is made by offering ++access to copy from a designated place, then offering equivalent ++access to copy the source code from the same place counts as ++distribution of the source code, even though third parties are not ++compelled to copy the source along with the object code. ++ ++ 4. You may not copy, modify, sublicense, or distribute the Program ++except as expressly provided under this License. Any attempt ++otherwise to copy, modify, sublicense or distribute the Program is ++void, and will automatically terminate your rights under this License. ++However, parties who have received copies, or rights, from you under ++this License will not have their licenses terminated so long as such ++parties remain in full compliance. ++ ++ 5. You are not required to accept this License, since you have not ++signed it. However, nothing else grants you permission to modify or ++distribute the Program or its derivative works. These actions are ++prohibited by law if you do not accept this License. Therefore, by ++modifying or distributing the Program (or any work based on the ++Program), you indicate your acceptance of this License to do so, and ++all its terms and conditions for copying, distributing or modifying ++the Program or works based on it. ++ ++ 6. Each time you redistribute the Program (or any work based on the ++Program), the recipient automatically receives a license from the ++original licensor to copy, distribute or modify the Program subject to ++these terms and conditions. You may not impose any further ++restrictions on the recipients' exercise of the rights granted herein. ++You are not responsible for enforcing compliance by third parties to ++this License. ++ ++ 7. If, as a consequence of a court judgment or allegation of patent ++infringement or for any other reason (not limited to patent issues), ++conditions are imposed on you (whether by court order, agreement or ++otherwise) that contradict the conditions of this License, they do not ++excuse you from the conditions of this License. If you cannot ++distribute so as to satisfy simultaneously your obligations under this ++License and any other pertinent obligations, then as a consequence you ++may not distribute the Program at all. For example, if a patent ++license would not permit royalty-free redistribution of the Program by ++all those who receive copies directly or indirectly through you, then ++the only way you could satisfy both it and this License would be to ++refrain entirely from distribution of the Program. ++ ++If any portion of this section is held invalid or unenforceable under ++any particular circumstance, the balance of the section is intended to ++apply and the section as a whole is intended to apply in other ++circumstances. ++ ++It is not the purpose of this section to induce you to infringe any ++patents or other property right claims or to contest validity of any ++such claims; this section has the sole purpose of protecting the ++integrity of the free software distribution system, which is ++implemented by public license practices. Many people have made ++generous contributions to the wide range of software distributed ++through that system in reliance on consistent application of that ++system; it is up to the author/donor to decide if he or she is willing ++to distribute software through any other system and a licensee cannot ++impose that choice. ++ ++This section is intended to make thoroughly clear what is believed to ++be a consequence of the rest of this L