mirror of
https://git.yoctoproject.org/poky
synced 2026-04-27 03:32:12 +02:00
git-svn-id: https://svn.o-hand.com/repos/poky/trunk@3014 311d38ba-8fff-0310-9ca6-ca027cbcb966
2082 lines
57 KiB
Diff
2082 lines
57 KiB
Diff
Index: u-boot/drivers/usbdcore_ep0.c
|
|
===================================================================
|
|
--- u-boot.orig/drivers/usbdcore_ep0.c
|
|
+++ u-boot/drivers/usbdcore_ep0.c
|
|
@@ -42,10 +42,15 @@
|
|
*/
|
|
|
|
#include <common.h>
|
|
+DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
#if defined(CONFIG_USB_DEVICE)
|
|
#include "usbdcore.h"
|
|
|
|
+#ifdef CONFIG_USBD_DFU
|
|
+#include <usb_dfu.h>
|
|
+#endif
|
|
+
|
|
#if 0
|
|
#define dbg_ep0(lvl,fmt,args...) serial_printf("[%s] %s:%d: "fmt"\n",__FILE__,__FUNCTION__,__LINE__,##args)
|
|
#else
|
|
@@ -213,7 +218,7 @@
|
|
urb->buffer = device_descriptor;
|
|
urb->actual_length = MIN(sizeof(*device_descriptor), max);
|
|
}
|
|
- /*dbg_ep0(3, "copied device configuration, actual_length: %x", urb->actual_length); */
|
|
+ dbg_ep0(3, "using device configuration, actual_length: %x", urb->actual_length);
|
|
break;
|
|
|
|
case USB_DESCRIPTOR_TYPE_CONFIGURATION:
|
|
@@ -267,7 +272,24 @@
|
|
return -1;
|
|
case USB_DESCRIPTOR_TYPE_ENDPOINT:
|
|
return -1;
|
|
+ /* This really means "Class Specific Descriptor #1 == USB_DT_DFU */
|
|
case USB_DESCRIPTOR_TYPE_HID:
|
|
+#ifdef CONFIG_USBD_DFU
|
|
+ {
|
|
+ int bNumInterface =
|
|
+ le16_to_cpu(urb->device_request.wIndex);
|
|
+
|
|
+ /* In runtime mode, we only respond to the DFU INTERFACE,
|
|
+ * whereas in DFU mode, we respond for all intrfaces */
|
|
+ if (device->dfu_state != DFU_STATE_appIDLE &&
|
|
+ device->dfu_state != DFU_STATE_appDETACH ||
|
|
+ bNumInterface == CONFIG_USBD_DFU_INTERFACE) {
|
|
+ urb->buffer = &device->dfu_cfg_desc->func_dfu;
|
|
+ urb->actual_length = sizeof(struct usb_dfu_func_descriptor);
|
|
+ } else
|
|
+ return -1;
|
|
+ }
|
|
+#else /* CONFIG_USBD_DFU */
|
|
{
|
|
return -1; /* unsupported at this time */
|
|
#if 0
|
|
@@ -294,6 +316,7 @@
|
|
max);
|
|
#endif
|
|
}
|
|
+#endif /* CONFIG_USBD_DFU */
|
|
break;
|
|
case USB_DESCRIPTOR_TYPE_REPORT:
|
|
{
|
|
@@ -388,6 +411,24 @@
|
|
le16_to_cpu (request->wLength),
|
|
USBD_DEVICE_REQUESTS (request->bRequest));
|
|
|
|
+#ifdef CONFIG_USBD_DFU
|
|
+ if ((request->bmRequestType & 0x3f) == USB_TYPE_DFU &&
|
|
+ (device->dfu_state != DFU_STATE_appIDLE ||
|
|
+ le16_to_cpu(request->wIndex) == CONFIG_USBD_DFU_INTERFACE)) {
|
|
+ int rc = dfu_ep0_handler(urb);
|
|
+ switch (rc) {
|
|
+ case DFU_EP0_NONE:
|
|
+ case DFU_EP0_UNHANDLED:
|
|
+ break;
|
|
+ case DFU_EP0_ZLP:
|
|
+ case DFU_EP0_DATA:
|
|
+ return 0;
|
|
+ case DFU_EP0_STALL:
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+#endif /* CONFIG_USB_DFU */
|
|
+
|
|
/* handle USB Standard Request (c.f. USB Spec table 9-2) */
|
|
if ((request->bmRequestType & USB_REQ_TYPE_MASK) != 0) {
|
|
if (device->device_state <= STATE_CONFIGURED)
|
|
@@ -570,7 +611,8 @@
|
|
device->interface = le16_to_cpu (request->wIndex);
|
|
device->alternate = le16_to_cpu (request->wValue);
|
|
/*dbg_ep0(2, "set interface: %d alternate: %d", device->interface, device->alternate); */
|
|
- serial_printf ("DEVICE_SET_INTERFACE.. event?\n");
|
|
+ usbd_device_event_irq(device, DEVICE_SET_INTERFACE,
|
|
+ (request->wIndex << 16 | request->wValue));
|
|
return 0;
|
|
|
|
case USB_REQ_GET_STATUS:
|
|
Index: u-boot/drivers/usbdfu.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ u-boot/drivers/usbdfu.c
|
|
@@ -0,0 +1,1069 @@
|
|
+/*
|
|
+ * (C) 2007 by OpenMoko, Inc.
|
|
+ * Author: Harald Welte <laforge@openmoko.org>
|
|
+ *
|
|
+ * based on existing SAM7DFU code from OpenPCD:
|
|
+ * (C) Copyright 2006 by Harald Welte <hwelte@hmw-consulting.de>
|
|
+ *
|
|
+ * See file CREDITS for list of people who contributed to this
|
|
+ * project.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public License as
|
|
+ * published by the Free Software Foundation; either version 2 of
|
|
+ * the License, or (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
+ * MA 02111-1307 USA
|
|
+ *
|
|
+ * TODO:
|
|
+ * - make NAND support reasonably self-contained and put in apropriate
|
|
+ * ifdefs
|
|
+ * - add some means of synchronization, i.e. block commandline access
|
|
+ * while DFU transfer is in progress, and return to commandline once
|
|
+ * we're finished
|
|
+ * - add VERIFY support after writing to flash
|
|
+ * - sanely free() resources allocated during first uppload/download
|
|
+ * request when aborting
|
|
+ * - sanely free resources when another alternate interface is selected
|
|
+ *
|
|
+ * Maybe:
|
|
+ * - add something like uImage or some other header that provides CRC
|
|
+ * checking?
|
|
+ * - make 'dnstate' attached to 'struct usb_device_instance'
|
|
+ */
|
|
+
|
|
+#include <config.h>
|
|
+#if defined(CONFIG_USBD_DFU)
|
|
+
|
|
+#include <common.h>
|
|
+DECLARE_GLOBAL_DATA_PTR;
|
|
+
|
|
+#include <malloc.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/list.h>
|
|
+#include <asm/errno.h>
|
|
+#include <usbdcore.h>
|
|
+#include <usb_dfu.h>
|
|
+#include <usb_dfu_descriptors.h>
|
|
+#include <usb_dfu_trailer.h>
|
|
+
|
|
+#include <nand.h>
|
|
+#include <jffs2/load_kernel.h>
|
|
+int mtdparts_init(void);
|
|
+extern struct list_head devices;
|
|
+
|
|
+#include "usbdcore_s3c2410.h"
|
|
+#include "usbtty.h" /* for STR_* defs */
|
|
+
|
|
+#define RET_NOTHING 0
|
|
+#define RET_ZLP 1
|
|
+#define RET_STALL 2
|
|
+
|
|
+volatile enum dfu_state *system_dfu_state; /* for 3rd parties */
|
|
+
|
|
+
|
|
+struct dnload_state {
|
|
+ nand_info_t *nand;
|
|
+ struct part_info *part;
|
|
+ unsigned int part_net_size; /* net sizee (excl. bad blocks) of part */
|
|
+
|
|
+ nand_erase_options_t erase_opts;
|
|
+ nand_write_options_t write_opts;
|
|
+ nand_read_options_t read_opts;
|
|
+
|
|
+ unsigned char *ptr; /* pointer to next empty byte in buffer */
|
|
+ unsigned int off; /* offset of current erase page in flash chip */
|
|
+ unsigned char *buf; /* pointer to allocated erase page buffer */
|
|
+
|
|
+ /* unless doing an atomic transfer, we use the static buffer below.
|
|
+ * This saves us from having to clean up dynamic allications in the
|
|
+ * various error paths of the code. Also, it will always work, no
|
|
+ * matter what the memory situation is. */
|
|
+ unsigned char _buf[0x20000]; /* FIXME: depends flash page size */
|
|
+};
|
|
+
|
|
+static struct dnload_state _dnstate;
|
|
+
|
|
+static int dfu_trailer_matching(const struct uboot_dfu_trailer *trailer)
|
|
+{
|
|
+ if (trailer->magic != UBOOT_DFU_TRAILER_MAGIC ||
|
|
+ trailer->version != UBOOT_DFU_TRAILER_V1 ||
|
|
+ trailer->vendor != CONFIG_USBD_VENDORID ||
|
|
+ (trailer->product != CONFIG_USBD_PRODUCTID_CDCACM &&
|
|
+ trailer->product != CONFIG_USBD_PRODUCTID_GSERIAL))
|
|
+ return 0;
|
|
+#ifdef CONFIG_REVISION_TAG
|
|
+ if (trailer->revision != get_board_rev())
|
|
+ return 0;
|
|
+#endif
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static struct part_info *get_partition_nand(int idx)
|
|
+{
|
|
+ struct mtd_device *dev;
|
|
+ struct part_info *part;
|
|
+ struct list_head *pentry;
|
|
+ int i;
|
|
+
|
|
+ if (mtdparts_init())
|
|
+ return NULL;
|
|
+ if (list_empty(&devices))
|
|
+ return NULL;
|
|
+
|
|
+ dev = list_entry(devices.next, struct mtd_device, link);
|
|
+ i = 0;
|
|
+ list_for_each(pentry, &dev->parts) {
|
|
+ if (i == idx) {
|
|
+ part = list_entry(pentry, struct part_info, link);
|
|
+ return part;
|
|
+ }
|
|
+ i++;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+#define LOAD_ADDR ((unsigned char *)0x32000000)
|
|
+
|
|
+static int initialize_ds_nand(struct usb_device_instance *dev, struct dnload_state *ds)
|
|
+{
|
|
+ ds->part = get_partition_nand(dev->alternate - 1);
|
|
+ if (!ds->part) {
|
|
+ printf("DFU: unable to find partition %u\b", dev->alternate-1);
|
|
+ dev->dfu_state = DFU_STATE_dfuERROR;
|
|
+ dev->dfu_status = DFU_STATUS_errADDRESS;
|
|
+ return RET_STALL;
|
|
+ }
|
|
+ ds->nand = &nand_info[ds->part->dev->id->num];
|
|
+ ds->off = ds->part->offset;
|
|
+ ds->part_net_size = nand_net_part_size(ds->part);
|
|
+
|
|
+ if (ds->nand->erasesize > sizeof(ds->_buf)) {
|
|
+ printf("*** Warning - NAND ERASESIZE bigger than static buffer\n");
|
|
+ ds->buf = malloc(ds->nand->erasesize);
|
|
+ if (!ds->buf) {
|
|
+ printf("DFU: can't allocate %u bytes\n", ds->nand->erasesize);
|
|
+ dev->dfu_state = DFU_STATE_dfuERROR;
|
|
+ dev->dfu_status = DFU_STATUS_errADDRESS;
|
|
+ return RET_STALL;
|
|
+ }
|
|
+ } else
|
|
+ ds->buf = ds->_buf;
|
|
+
|
|
+ ds->ptr = ds->buf;
|
|
+
|
|
+ memset(&ds->read_opts, 0, sizeof(ds->read_opts));
|
|
+
|
|
+ memset(&ds->erase_opts, 0, sizeof(ds->erase_opts));
|
|
+ ds->erase_opts.quiet = 1;
|
|
+ /* FIXME: do this more dynamic */
|
|
+ if (!strcmp(ds->part->name, "rootfs"))
|
|
+ ds->erase_opts.jffs2 = 1;
|
|
+
|
|
+ memset(&ds->write_opts, 0, sizeof(ds->write_opts));
|
|
+ ds->write_opts.pad = 1;
|
|
+ ds->write_opts.blockalign = 1;
|
|
+ ds->write_opts.quiet = 1;
|
|
+
|
|
+ debug("initialize_ds_nand(dev=%p, ds=%p): ", dev, ds);
|
|
+ debug("nand=%p, ptr=%p, buf=%p, off=0x%x\n", ds->nand, ds->ptr, ds->buf, ds->off);
|
|
+
|
|
+ return RET_NOTHING;
|
|
+}
|
|
+
|
|
+static int erase_flash_verify_nand(struct urb *urb, struct dnload_state *ds,
|
|
+ unsigned long erasesize, unsigned long size)
|
|
+{
|
|
+ struct usb_device_instance *dev = urb->device;
|
|
+ int rc;
|
|
+
|
|
+ debug("erase_flash_verify_nand(urb=%p, ds=%p, erase=0x%x size=0x%x)\n",
|
|
+ urb, ds, erasesize, size);
|
|
+
|
|
+ if (erasesize == ds->nand->erasesize) {
|
|
+ /* we're only writing a single block and need to
|
|
+ * do bad block skipping / offset adjustments our own */
|
|
+ while (ds->nand->block_isbad(ds->nand, ds->off)) {
|
|
+ debug("SKIP_ONE_BLOCK(0x%08x)!!\n", ds->off);
|
|
+ ds->off += ds->nand->erasesize;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* we have finished one eraseblock, flash it */
|
|
+ ds->erase_opts.offset = ds->off;
|
|
+ ds->erase_opts.length = erasesize;
|
|
+ debug("Erasing 0x%x bytes @ offset 0x%x (jffs=%u)\n",
|
|
+ ds->erase_opts.length, ds->erase_opts.offset,
|
|
+ ds->erase_opts.jffs2);
|
|
+ rc = nand_erase_opts(ds->nand, &ds->erase_opts);
|
|
+ if (rc) {
|
|
+ debug("Error erasing\n");
|
|
+ dev->dfu_state = DFU_STATE_dfuERROR;
|
|
+ dev->dfu_status = DFU_STATUS_errERASE;
|
|
+ return RET_STALL;
|
|
+ }
|
|
+
|
|
+ ds->write_opts.buffer = ds->buf;
|
|
+ ds->write_opts.length = size;
|
|
+ ds->write_opts.offset = ds->off;
|
|
+ debug("Writing 0x%x bytes @ offset 0x%x\n", size, ds->off);
|
|
+ rc = nand_write_opts(ds->nand, &ds->write_opts);
|
|
+ if (rc) {
|
|
+ debug("Error writing\n");
|
|
+ dev->dfu_state = DFU_STATE_dfuERROR;
|
|
+ dev->dfu_status = DFU_STATUS_errWRITE;
|
|
+ return RET_STALL;
|
|
+ }
|
|
+
|
|
+ ds->off += size;
|
|
+ ds->ptr = ds->buf;
|
|
+
|
|
+ /* FIXME: implement verify! */
|
|
+ return RET_NOTHING;
|
|
+}
|
|
+
|
|
+static int erase_tail_clean_nand(struct urb *urb, struct dnload_state *ds)
|
|
+{
|
|
+ struct usb_device_instance *dev = urb->device;
|
|
+ int rc;
|
|
+
|
|
+ ds->erase_opts.offset = ds->off;
|
|
+ ds->erase_opts.length = ds->part->size-ds->off;
|
|
+ debug("Erasing 0x%x bytes @ offset 0x%x (jffs=%u)\n",
|
|
+ ds->erase_opts.length, ds->erase_opts.offset,
|
|
+ ds->erase_opts.jffs2);
|
|
+ rc = nand_erase_opts(ds->nand, &ds->erase_opts);
|
|
+ if (rc) {
|
|
+ debug("Error erasing\n");
|
|
+ dev->dfu_state = DFU_STATE_dfuERROR;
|
|
+ dev->dfu_status = DFU_STATUS_errERASE;
|
|
+ return RET_STALL;
|
|
+ }
|
|
+
|
|
+ ds->off += ds->part->size; /* for consistency */
|
|
+
|
|
+ return RET_NOTHING;
|
|
+}
|
|
+
|
|
+/* Read the next erase blcok from NAND into buffer */
|
|
+static int read_next_nand(struct urb *urb, struct dnload_state *ds)
|
|
+{
|
|
+ struct usb_device_instance *dev = urb->device;
|
|
+ int rc;
|
|
+
|
|
+ ds->read_opts.buffer = ds->buf;
|
|
+ ds->read_opts.length = ds->nand->erasesize;
|
|
+ ds->read_opts.offset = ds->off;
|
|
+ ds->read_opts.quiet = 1;
|
|
+
|
|
+ debug("Reading 0x%x@0x%x to 0x%08p\n", ds->nand->erasesize,
|
|
+ ds->off, ds->buf);
|
|
+ rc = nand_read_opts(ds->nand, &ds->read_opts);
|
|
+ if (rc) {
|
|
+ debug("Error reading\n");
|
|
+ dev->dfu_state = DFU_STATE_dfuERROR;
|
|
+ dev->dfu_status = DFU_STATUS_errWRITE;
|
|
+ return RET_STALL;
|
|
+ }
|
|
+ ds->off += ds->nand->erasesize;
|
|
+ ds->ptr = ds->buf;
|
|
+
|
|
+ return RET_NOTHING;
|
|
+}
|
|
+
|
|
+
|
|
+static int handle_dnload(struct urb *urb, u_int16_t val, u_int16_t len, int first)
|
|
+{
|
|
+ struct usb_device_instance *dev = urb->device;
|
|
+ struct dnload_state *ds = &_dnstate;
|
|
+ unsigned int actual_len = len;
|
|
+ unsigned int remain_len;
|
|
+ unsigned long size;
|
|
+ int rc;
|
|
+
|
|
+ debug("download(len=%u, first=%u) ", len, first);
|
|
+
|
|
+ if (len > CONFIG_USBD_DFU_XFER_SIZE) {
|
|
+ /* Too big. Not that we'd really care, but it's a
|
|
+ * DFU protocol violation */
|
|
+ debug("length exceeds flash page size ");
|
|
+ dev->dfu_state = DFU_STATE_dfuERROR;
|
|
+ dev->dfu_status = DFU_STATUS_errADDRESS;
|
|
+ return RET_STALL;
|
|
+ }
|
|
+
|
|
+ if (first) {
|
|
+ /* Make sure that we have a valid mtd partition table */
|
|
+ char *mtdp = getenv("mtdparts");
|
|
+ if (!mtdp)
|
|
+ run_command("dynpart", 0);
|
|
+ }
|
|
+
|
|
+ if (len == 0) {
|
|
+ debug("zero-size write -> MANIFEST_SYNC ");
|
|
+ dev->dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
|
|
+
|
|
+ /* cleanup */
|
|
+ switch (dev->alternate) {
|
|
+ char buf[12];
|
|
+ case 0:
|
|
+ sprintf(buf, "%lx", ds->ptr - ds->buf);
|
|
+ setenv("filesize", buf);
|
|
+ ds->ptr = ds->buf;
|
|
+ break;
|
|
+ case 1:
|
|
+ if (ds->ptr >
|
|
+ ds->buf + sizeof(struct uboot_dfu_trailer)) {
|
|
+ struct uboot_dfu_trailer trailer;
|
|
+ dfu_trailer_mirror(&trailer, ds->ptr);
|
|
+ if (!dfu_trailer_matching(&trailer)) {
|
|
+ printf("DFU TRAILER NOT OK\n");
|
|
+ dev->dfu_state = DFU_STATE_dfuERROR;
|
|
+ dev->dfu_status = DFU_STATUS_errTARGET;
|
|
+ return RET_STALL;
|
|
+ }
|
|
+
|
|
+ rc = erase_flash_verify_nand(urb, ds,
|
|
+ ds->part->size,
|
|
+ ds->part_net_size);
|
|
+ /* re-write dynenv marker in OOB */
|
|
+ run_command("dynenv set u-boot_env", 0);
|
|
+ }
|
|
+ ds->nand = NULL;
|
|
+ free(ds->buf);
|
|
+ ds->ptr = ds->buf = ds->_buf;
|
|
+ break;
|
|
+ default:
|
|
+ rc = 0;
|
|
+ if (ds->ptr > ds->buf)
|
|
+ rc = erase_flash_verify_nand(urb, ds,
|
|
+ ds->nand->erasesize,
|
|
+ ds->nand->erasesize);
|
|
+ /* rootfs partition */
|
|
+ if (!rc && dev->alternate == 5)
|
|
+ rc = erase_tail_clean_nand(urb, ds);
|
|
+
|
|
+ ds->nand = NULL;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return RET_ZLP;
|
|
+ }
|
|
+
|
|
+ if (urb->actual_length != len) {
|
|
+ debug("urb->actual_length(%u) != len(%u) ?!? ",
|
|
+ urb->actual_length, len);
|
|
+ dev->dfu_state = DFU_STATE_dfuERROR;
|
|
+ dev->dfu_status = DFU_STATUS_errADDRESS;
|
|
+ return RET_STALL;
|
|
+ }
|
|
+
|
|
+ if (first && ds->buf && ds->buf != ds->_buf && ds->buf != LOAD_ADDR) {
|
|
+ free(ds->buf);
|
|
+ ds->buf = ds->_buf;
|
|
+ }
|
|
+
|
|
+ switch (dev->alternate) {
|
|
+ case 0:
|
|
+ if (first) {
|
|
+ printf("Starting DFU DOWNLOAD to RAM (0x%08p)\n",
|
|
+ LOAD_ADDR);
|
|
+ ds->buf = LOAD_ADDR;
|
|
+ ds->ptr = ds->buf;
|
|
+ }
|
|
+
|
|
+ memcpy(ds->ptr, urb->buffer, len);
|
|
+ ds->ptr += len;
|
|
+ break;
|
|
+ case 1:
|
|
+ if (first) {
|
|
+ rc = initialize_ds_nand(dev, ds);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+ ds->buf = malloc(ds->part_net_size);
|
|
+ if (!ds->buf) {
|
|
+ printf("No memory for atomic buffer!!\n");
|
|
+ dev->dfu_state = DFU_STATE_dfuERROR;
|
|
+ dev->dfu_status = DFU_STATUS_errUNKNOWN;
|
|
+ return RET_STALL;
|
|
+ }
|
|
+ ds->ptr = ds->buf;
|
|
+ printf("Starting Atomic DFU DOWNLOAD to partition '%s'\n",
|
|
+ ds->part->name);
|
|
+ }
|
|
+
|
|
+ remain_len = (ds->buf + ds->part_net_size) - ds->ptr;
|
|
+ if (remain_len < len) {
|
|
+ len = remain_len;
|
|
+ printf("End of write exceeds partition end\n");
|
|
+ dev->dfu_state = DFU_STATE_dfuERROR;
|
|
+ dev->dfu_status = DFU_STATUS_errADDRESS;
|
|
+ return RET_STALL;
|
|
+ }
|
|
+ memcpy(ds->ptr, urb->buffer, len);
|
|
+ ds->ptr += len;
|
|
+ break;
|
|
+ default:
|
|
+ if (first) {
|
|
+ rc = initialize_ds_nand(dev, ds);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+ printf("Starting DFU DOWNLOAD to partition '%s'\n",
|
|
+ ds->part->name);
|
|
+ }
|
|
+
|
|
+ size = ds->nand->erasesize;
|
|
+ remain_len = ds->buf + size - ds->ptr;
|
|
+ if (remain_len < len)
|
|
+ actual_len = remain_len;
|
|
+
|
|
+ memcpy(ds->ptr, urb->buffer, actual_len);
|
|
+ ds->ptr += actual_len;
|
|
+
|
|
+ /* check partition end */
|
|
+ if (ds->off + (ds->ptr - ds->buf) > ds->part->offset + ds->part->size) {
|
|
+ printf("End of write exceeds partition end\n");
|
|
+ dev->dfu_state = DFU_STATE_dfuERROR;
|
|
+ dev->dfu_status = DFU_STATUS_errADDRESS;
|
|
+ return RET_STALL;
|
|
+ }
|
|
+
|
|
+ if (ds->ptr >= ds->buf + size) {
|
|
+ rc = erase_flash_verify_nand(urb, ds,
|
|
+ ds->nand->erasesize,
|
|
+ ds->nand->erasesize);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+ /* copy remainder of data into buffer */
|
|
+ memcpy(ds->ptr, urb->buffer + actual_len, len - actual_len);
|
|
+ ds->ptr += (len - actual_len);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return RET_ZLP;
|
|
+}
|
|
+
|
|
+static int handle_upload(struct urb *urb, u_int16_t val, u_int16_t len, int first)
|
|
+{
|
|
+ struct usb_device_instance *dev = urb->device;
|
|
+ struct dnload_state *ds = &_dnstate;
|
|
+ unsigned int remain;
|
|
+ int rc;
|
|
+
|
|
+ debug("upload(val=0x%02x, len=%u, first=%u) ", val, len, first);
|
|
+
|
|
+ if (len > CONFIG_USBD_DFU_XFER_SIZE) {
|
|
+ /* Too big */
|
|
+ dev->dfu_state = DFU_STATE_dfuERROR;
|
|
+ dev->dfu_status = DFU_STATUS_errADDRESS;
|
|
+ //udc_ep0_send_stall();
|
|
+ debug("Error: Transfer size > CONFIG_USBD_DFU_XFER_SIZE ");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ switch (dev->alternate) {
|
|
+ case 0:
|
|
+ if (first) {
|
|
+ printf("Starting DFU Upload of RAM (0x%08p)\n",
|
|
+ LOAD_ADDR);
|
|
+ ds->ptr = ds->buf;
|
|
+ }
|
|
+
|
|
+ /* FIXME: end at some more dynamic point */
|
|
+ if (ds->ptr + len > LOAD_ADDR + 0x200000)
|
|
+ len = (LOAD_ADDR + 0x200000) - ds->ptr;
|
|
+
|
|
+ urb->buffer = ds->ptr;
|
|
+ urb->actual_length = len;
|
|
+ ds->ptr += len;
|
|
+ break;
|
|
+ default:
|
|
+ if (first) {
|
|
+ rc = initialize_ds_nand(dev, ds);
|
|
+ if (rc)
|
|
+ return -EINVAL;
|
|
+ printf("Starting DFU Upload of partition '%s'\n",
|
|
+ ds->part->name);
|
|
+ rc = read_next_nand(urb, ds);
|
|
+ if (rc)
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (len > ds->nand->erasesize) {
|
|
+ printf("We don't support transfers bigger than %u\n",
|
|
+ ds->nand->erasesize);
|
|
+ len = ds->nand->erasesize;
|
|
+ }
|
|
+
|
|
+ remain = ds->nand->erasesize - (ds->ptr - ds->buf);
|
|
+ if (len < remain)
|
|
+ remain = len;
|
|
+
|
|
+ debug("copying %u bytes ", remain);
|
|
+ urb->buffer = ds->ptr;
|
|
+ ds->ptr += remain;
|
|
+ urb->actual_length = remain;
|
|
+
|
|
+ if (ds->ptr >= ds->buf + ds->nand->erasesize &&
|
|
+ ds->off < ds->part->offset + ds->part->size) {
|
|
+ rc = read_next_nand(urb, ds);
|
|
+ if (rc)
|
|
+ return -EINVAL;
|
|
+ if (len > remain) {
|
|
+ debug("copying another %u bytes ", len - remain);
|
|
+ memcpy(urb->buffer + remain, ds->ptr, len - remain);
|
|
+ ds->ptr += (len - remain);
|
|
+ urb->actual_length += (len - remain);
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ debug("returning len=%u\n", len);
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static void handle_getstatus(struct urb *urb, int max)
|
|
+{
|
|
+ struct usb_device_instance *dev = urb->device;
|
|
+ struct dfu_status *dstat = (struct dfu_status *) urb->buffer;
|
|
+
|
|
+ debug("getstatus ");
|
|
+
|
|
+ if (!urb->buffer || urb->buffer_length < sizeof(*dstat)) {
|
|
+ debug("invalid urb! ");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ switch (dev->dfu_state) {
|
|
+ case DFU_STATE_dfuDNLOAD_SYNC:
|
|
+ case DFU_STATE_dfuDNBUSY:
|
|
+#if 0
|
|
+ if (fsr & AT91C_MC_PROGE) {
|
|
+ debug("errPROG ");
|
|
+ dev->dfu_status = DFU_STATUS_errPROG;
|
|
+ dev->dfu_state = DFU_STATE_dfuERROR;
|
|
+ } else if (fsr & AT91C_MC_LOCKE) {
|
|
+ debug("errWRITE ");
|
|
+ dev->dfu_status = DFU_STATUS_errWRITE;
|
|
+ dev->dfu_state = DFU_STATE_dfuERROR;
|
|
+ } else if (fsr & AT91C_MC_FRDY) {
|
|
+#endif
|
|
+ debug("DNLOAD_IDLE ");
|
|
+ dev->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
|
|
+#if 0
|
|
+ } else {
|
|
+ debug("DNBUSY ");
|
|
+ dev->dfu_state = DFU_STATE_dfuDNBUSY;
|
|
+ }
|
|
+#endif
|
|
+ break;
|
|
+ case DFU_STATE_dfuMANIFEST_SYNC:
|
|
+ break;
|
|
+ default:
|
|
+ //return;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* send status response */
|
|
+ dstat->bStatus = dev->dfu_status;
|
|
+ dstat->bState = dev->dfu_state;
|
|
+ dstat->iString = 0;
|
|
+ /* FIXME: set dstat->bwPollTimeout */
|
|
+ urb->actual_length = MIN(sizeof(*dstat), max);
|
|
+
|
|
+ /* we don't need to explicitly send data here, will
|
|
+ * be done by the original caller! */
|
|
+}
|
|
+
|
|
+static void handle_getstate(struct urb *urb, int max)
|
|
+{
|
|
+ debug("getstate ");
|
|
+
|
|
+ if (!urb->buffer || urb->buffer_length < sizeof(u_int8_t)) {
|
|
+ debug("invalid urb! ");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ urb->buffer[0] = urb->device->dfu_state & 0xff;
|
|
+ urb->actual_length = sizeof(u_int8_t);
|
|
+}
|
|
+
|
|
+#ifndef CONFIG_USBD_PRODUCTID_DFU
|
|
+#define CONFIG_USBD_PRODUCTID_DFU CONFIG_USBD_PRODUCTID_CDCACM
|
|
+#endif
|
|
+
|
|
+static const struct usb_device_descriptor dfu_dev_descriptor = {
|
|
+ .bLength = USB_DT_DEVICE_SIZE,
|
|
+ .bDescriptorType = USB_DT_DEVICE,
|
|
+ .bcdUSB = 0x0100,
|
|
+ .bDeviceClass = 0x00,
|
|
+ .bDeviceSubClass = 0x00,
|
|
+ .bDeviceProtocol = 0x00,
|
|
+ .bMaxPacketSize0 = EP0_MAX_PACKET_SIZE,
|
|
+ .idVendor = CONFIG_USBD_VENDORID,
|
|
+ .idProduct = CONFIG_USBD_PRODUCTID_DFU,
|
|
+ .bcdDevice = 0x0000,
|
|
+ .iManufacturer = DFU_STR_MANUFACTURER,
|
|
+ .iProduct = DFU_STR_PRODUCT,
|
|
+ .iSerialNumber = DFU_STR_SERIAL,
|
|
+ .bNumConfigurations = 0x01,
|
|
+};
|
|
+
|
|
+static 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("stdout", "vga");
|
|
+ setenv("stderr", "vga");
|
|
+ stdout_switched = 1;
|
|
+ }
|
|
+ printf("DFU: Switching to DFU Mode\n");
|
|
+ break;
|
|
+ case DFU_STATE_dfuMANIFEST_WAIT_RST:
|
|
+ device->dfu_state = DFU_STATE_appIDLE;
|
|
+ printf("DFU: Switching back to Runtime mode\n");
|
|
+ if (stdout_switched) {
|
|
+ setenv("stdout", "usbtty");
|
|
+ setenv("stderr", "usbtty");
|
|
+ stdout_switched = 0;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+ case DEVICE_CONFIGURED:
|
|
+ case DEVICE_DE_CONFIGURED:
|
|
+ debug("SET_CONFIGURATION(%u) ", device->configuration);
|
|
+ /* fallthrough */
|
|
+ case DEVICE_SET_INTERFACE:
|
|
+ debug("SET_INTERFACE(%u,%u) old_state = %u ",
|
|
+ device->interface, device->alternate,
|
|
+ device->dfu_state);
|
|
+ switch (device->dfu_state) {
|
|
+ case DFU_STATE_appIDLE:
|
|
+ case DFU_STATE_appDETACH:
|
|
+ case DFU_STATE_dfuIDLE:
|
|
+ case DFU_STATE_dfuMANIFEST_WAIT_RST:
|
|
+ /* do nothing, we're fine */
|
|
+ break;
|
|
+ case DFU_STATE_dfuDNLOAD_SYNC:
|
|
+ case DFU_STATE_dfuDNBUSY:
|
|
+ case DFU_STATE_dfuDNLOAD_IDLE:
|
|
+ case DFU_STATE_dfuMANIFEST:
|
|
+ device->dfu_state = DFU_STATE_dfuERROR;
|
|
+ device->dfu_status = DFU_STATUS_errNOTDONE;
|
|
+ /* FIXME: free malloc()ed buffer! */
|
|
+ break;
|
|
+ case DFU_STATE_dfuMANIFEST_SYNC:
|
|
+ case DFU_STATE_dfuUPLOAD_IDLE:
|
|
+ case DFU_STATE_dfuERROR:
|
|
+ device->dfu_state = DFU_STATE_dfuERROR;
|
|
+ device->dfu_status = DFU_STATUS_errUNKNOWN;
|
|
+ break;
|
|
+ }
|
|
+ debug("new_state = %u\n", device->dfu_state);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+#endif /* CONFIG_USBD_DFU */
|
|
Index: u-boot/drivers/Makefile
|
|
===================================================================
|
|
--- u-boot.orig/drivers/Makefile
|
|
+++ u-boot/drivers/Makefile
|
|
@@ -47,7 +47,7 @@
|
|
status_led.o sym53c8xx.o systemace.o ahci.o \
|
|
ti_pci1410a.o tigon3.o tsec.o \
|
|
tsi108_eth.o tsi108_i2c.o tsi108_pci.o \
|
|
- usbdcore.o usbdcore_ep0.o usbdcore_omap1510.o usbdcore_s3c2410.o usbtty.o \
|
|
+ usbdcore.o usbdfu.o usbdcore_ep0.o usbdcore_omap1510.o usbdcore_s3c2410.o usbtty.o \
|
|
videomodes.o w83c553f.o \
|
|
ks8695eth.o \
|
|
pcf50606.o \
|
|
Index: u-boot/drivers/usbdcore.c
|
|
===================================================================
|
|
--- u-boot.orig/drivers/usbdcore.c
|
|
+++ u-boot/drivers/usbdcore.c
|
|
@@ -31,6 +31,7 @@
|
|
|
|
#include <malloc.h>
|
|
#include "usbdcore.h"
|
|
+#include <usb_dfu.h>
|
|
|
|
#define MAX_INTERFACES 2
|
|
|
|
@@ -212,6 +213,10 @@
|
|
*/
|
|
struct usb_device_descriptor *usbd_device_device_descriptor (struct usb_device_instance *device, int port)
|
|
{
|
|
+#ifdef CONFIG_USBD_DFU
|
|
+ if (device->dfu_state != DFU_STATE_appIDLE)
|
|
+ return device->dfu_dev_desc;
|
|
+#endif
|
|
return (device->device_descriptor);
|
|
}
|
|
|
|
@@ -232,6 +237,10 @@
|
|
if (!(configuration_instance = usbd_device_configuration_instance (device, port, configuration))) {
|
|
return NULL;
|
|
}
|
|
+#ifdef CONFIG_USBD_DFU
|
|
+ if (device->dfu_state != DFU_STATE_appIDLE)
|
|
+ return (&device->dfu_cfg_desc->ucfg);
|
|
+#endif
|
|
return (configuration_instance->configuration_descriptor);
|
|
}
|
|
|
|
@@ -253,6 +262,13 @@
|
|
if (!(interface_instance = usbd_device_interface_instance (device, port, configuration, interface))) {
|
|
return NULL;
|
|
}
|
|
+#ifdef CONFIG_USBD_DFU
|
|
+ if (device->dfu_state != DFU_STATE_appIDLE) {
|
|
+ if (alternate < 0 || alternate >= DFU_NUM_ALTERNATES)
|
|
+ return NULL;
|
|
+ return &device->dfu_cfg_desc->uif[alternate];
|
|
+ }
|
|
+#endif
|
|
if ((alternate < 0) || (alternate >= interface_instance->alternates)) {
|
|
return NULL;
|
|
}
|
|
@@ -681,4 +697,7 @@
|
|
/* usbdbg("calling device->event"); */
|
|
device->event(device, event, data);
|
|
}
|
|
+#ifdef CONFIG_USBD_DFU
|
|
+ dfu_event(device, event, data);
|
|
+#endif
|
|
}
|
|
Index: u-boot/drivers/usbtty.c
|
|
===================================================================
|
|
--- u-boot.orig/drivers/usbtty.c
|
|
+++ u-boot/drivers/usbtty.c
|
|
@@ -31,6 +31,8 @@
|
|
#include "usbtty.h"
|
|
#include "usb_cdc_acm.h"
|
|
#include "usbdescriptors.h"
|
|
+#include <usb_dfu_descriptors.h>
|
|
+#include <usb_dfu.h>
|
|
#include <config.h> /* If defined, override Linux identifiers with
|
|
* vendor specific ones */
|
|
|
|
@@ -118,7 +120,7 @@
|
|
static unsigned short rx_endpoint = 0;
|
|
static unsigned short tx_endpoint = 0;
|
|
static unsigned short interface_count = 0;
|
|
-static struct usb_string_descriptor *usbtty_string_table[STR_COUNT];
|
|
+static struct usb_string_descriptor *usbtty_string_table[NUM_STRINGS];
|
|
|
|
/* USB Descriptor Strings */
|
|
static u8 wstrLang[4] = {4,USB_DT_STRING,0x9,0x4};
|
|
@@ -169,6 +171,10 @@
|
|
struct usb_interface_descriptor data_class_interface;
|
|
struct usb_endpoint_descriptor
|
|
data_endpoints[NUM_ENDPOINTS-1] __attribute__((packed));
|
|
+#ifdef CONFIG_USBD_DFU
|
|
+ struct usb_interface_descriptor uif_dfu;
|
|
+ struct usb_dfu_func_descriptor func_dfu;
|
|
+#endif
|
|
} __attribute__((packed));
|
|
|
|
static struct acm_config_desc acm_configuration_descriptors[NUM_CONFIGS] = {
|
|
@@ -179,7 +185,11 @@
|
|
.bDescriptorType = USB_DT_CONFIG,
|
|
.wTotalLength =
|
|
cpu_to_le16(sizeof(struct acm_config_desc)),
|
|
+#ifdef CONFIG_USBD_DFU
|
|
+ .bNumInterfaces = NUM_ACM_INTERFACES +1,
|
|
+#else
|
|
.bNumInterfaces = NUM_ACM_INTERFACES,
|
|
+#endif
|
|
.bConfigurationValue = 1,
|
|
.iConfiguration = STR_CONFIG,
|
|
.bmAttributes =
|
|
@@ -278,6 +288,11 @@
|
|
.bInterval = 0xFF,
|
|
},
|
|
},
|
|
+#ifdef CONFIG_USBD_DFU
|
|
+ /* Interface 3 */
|
|
+ .uif_dfu = DFU_RT_IF_DESC,
|
|
+ .func_dfu = DFU_FUNC_DESC,
|
|
+#endif
|
|
},
|
|
};
|
|
|
|
@@ -390,7 +405,7 @@
|
|
void usbtty_poll (void);
|
|
|
|
/* utility function for converting char* to wide string used by USB */
|
|
-static void str2wide (char *str, u16 * wide)
|
|
+void str2wide (char *str, u16 * wide)
|
|
{
|
|
int i;
|
|
for (i = 0; i < strlen (str) && str[i]; i++){
|
|
@@ -652,6 +667,9 @@
|
|
device_instance->bus = bus_instance;
|
|
device_instance->configurations = NUM_CONFIGS;
|
|
device_instance->configuration_instance_array = config_instance;
|
|
+#ifdef CONFIG_USBD_DFU
|
|
+ dfu_init_instance(device_instance);
|
|
+#endif
|
|
|
|
/* initialize bus instance */
|
|
memset (bus_instance, 0, sizeof (struct usb_bus_instance));
|
|
Index: u-boot/include/configs/neo1973_gta01.h
|
|
===================================================================
|
|
--- u-boot.orig/include/configs/neo1973_gta01.h
|
|
+++ u-boot/include/configs/neo1973_gta01.h
|
|
@@ -167,7 +167,7 @@
|
|
*/
|
|
#define CONFIG_STACKSIZE (128*1024) /* regular stack */
|
|
#ifdef CONFIG_USE_IRQ
|
|
-#define CONFIG_STACKSIZE_IRQ (4*1024) /* IRQ stack */
|
|
+#define CONFIG_STACKSIZE_IRQ (8*1024) /* IRQ stack */
|
|
#define CONFIG_STACKSIZE_FIQ (4*1024) /* FIQ stack */
|
|
#endif
|
|
|
|
@@ -184,6 +184,10 @@
|
|
#define CONFIG_USBD_MANUFACTURER "OpenMoko, Inc"
|
|
#define CONFIG_USBD_PRODUCT_NAME "Neo1973 Bootloader " U_BOOT_VERSION
|
|
#define CONFIG_EXTRA_ENV_SETTINGS "usbtty=cdc_acm\0"
|
|
+#define CONFIG_USBD_DFU 1
|
|
+#define CONFIG_USBD_DFU_XFER_SIZE 4096 /* 0x4000 */
|
|
+#define CONFIG_USBD_DFU_INTERFACE 2
|
|
+
|
|
|
|
/*-----------------------------------------------------------------------
|
|
* Physical Memory Map
|
|
Index: u-boot/include/usb_dfu.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ u-boot/include/usb_dfu.h
|
|
@@ -0,0 +1,99 @@
|
|
+#ifndef _DFU_H
|
|
+#define _DFU_H
|
|
+
|
|
+/* USB Device Firmware Update Implementation for u-boot
|
|
+ * (C) 2007 by OpenMoko, Inc.
|
|
+ * Author: Harald Welte <laforge@openmoko.org>
|
|
+ *
|
|
+ * based on: USB Device Firmware Update Implementation for OpenPCD
|
|
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
|
|
+ *
|
|
+ * This ought to be compliant to the USB DFU Spec 1.0 as available from
|
|
+ * http://www.usb.org/developers/devclass_docs/usbdfu10.pdf
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
+ */
|
|
+
|
|
+#include <asm/types.h>
|
|
+#include <usbdescriptors.h>
|
|
+#include <usb_dfu_descriptors.h>
|
|
+#include <config.h>
|
|
+
|
|
+/* USB DFU functional descriptor */
|
|
+#define DFU_FUNC_DESC { \
|
|
+ .bLength = USB_DT_DFU_SIZE, \
|
|
+ .bDescriptorType = USB_DT_DFU, \
|
|
+ .bmAttributes = USB_DFU_CAN_UPLOAD | USB_DFU_CAN_DOWNLOAD | USB_DFU_MANIFEST_TOL, \
|
|
+ .wDetachTimeOut = 0xff00, \
|
|
+ .wTransferSize = CONFIG_USBD_DFU_XFER_SIZE, \
|
|
+ .bcdDFUVersion = 0x0100, \
|
|
+}
|
|
+
|
|
+/* USB Interface descriptor in Runtime mode */
|
|
+#define DFU_RT_IF_DESC { \
|
|
+ .bLength = USB_DT_INTERFACE_SIZE, \
|
|
+ .bDescriptorType = USB_DT_INTERFACE, \
|
|
+ .bInterfaceNumber = CONFIG_USBD_DFU_INTERFACE, \
|
|
+ .bAlternateSetting = 0x00, \
|
|
+ .bNumEndpoints = 0x00, \
|
|
+ .bInterfaceClass = 0xfe, \
|
|
+ .bInterfaceSubClass = 0x01, \
|
|
+ .bInterfaceProtocol = 0x01, \
|
|
+ .iInterface = DFU_STR_CONFIG, \
|
|
+}
|
|
+
|
|
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
|
+
|
|
+#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 0x32000000"
|
|
+
|
|
+struct _dfu_desc {
|
|
+ struct usb_configuration_descriptor ucfg;
|
|
+ struct usb_interface_descriptor uif[DFU_NUM_ALTERNATES];
|
|
+ struct usb_dfu_func_descriptor func_dfu;
|
|
+};
|
|
+
|
|
+int dfu_init_instance(struct usb_device_instance *dev);
|
|
+
|
|
+#define DFU_EP0_NONE 0
|
|
+#define DFU_EP0_UNHANDLED 1
|
|
+#define DFU_EP0_STALL 2
|
|
+#define DFU_EP0_ZLP 3
|
|
+#define DFU_EP0_DATA 4
|
|
+
|
|
+extern volatile enum dfu_state *system_dfu_state; /* for 3rd parties */
|
|
+
|
|
+int dfu_ep0_handler(struct urb *urb);
|
|
+
|
|
+void dfu_event(struct usb_device_instance *device,
|
|
+ usb_device_event_t event, int data);
|
|
+
|
|
+#endif /* _DFU_H */
|
|
Index: u-boot/include/usb_dfu_descriptors.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ u-boot/include/usb_dfu_descriptors.h
|
|
@@ -0,0 +1,94 @@
|
|
+#ifndef _USB_DFU_H
|
|
+#define _USB_DFU_H
|
|
+/* USB Device Firmware Update Implementation for OpenPCD
|
|
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
|
|
+ *
|
|
+ * Protocol definitions for USB DFU
|
|
+ *
|
|
+ * This ought to be compliant to the USB DFU Spec 1.0 as available from
|
|
+ * http://www.usb.org/developers/devclass_docs/usbdfu10.pdf
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
+ */
|
|
+
|
|
+#include <linux/types.h>
|
|
+
|
|
+#define USB_DT_DFU 0x21
|
|
+
|
|
+struct usb_dfu_func_descriptor {
|
|
+ u_int8_t bLength;
|
|
+ u_int8_t bDescriptorType;
|
|
+ u_int8_t bmAttributes;
|
|
+#define USB_DFU_CAN_DOWNLOAD (1 << 0)
|
|
+#define USB_DFU_CAN_UPLOAD (1 << 1)
|
|
+#define USB_DFU_MANIFEST_TOL (1 << 2)
|
|
+#define USB_DFU_WILL_DETACH (1 << 3)
|
|
+ u_int16_t wDetachTimeOut;
|
|
+ u_int16_t wTransferSize;
|
|
+ u_int16_t bcdDFUVersion;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#define USB_DT_DFU_SIZE 9
|
|
+
|
|
+#define USB_TYPE_DFU (USB_TYPE_CLASS|USB_RECIP_INTERFACE)
|
|
+
|
|
+/* DFU class-specific requests (Section 3, DFU Rev 1.1) */
|
|
+#define USB_REQ_DFU_DETACH 0x00
|
|
+#define USB_REQ_DFU_DNLOAD 0x01
|
|
+#define USB_REQ_DFU_UPLOAD 0x02
|
|
+#define USB_REQ_DFU_GETSTATUS 0x03
|
|
+#define USB_REQ_DFU_CLRSTATUS 0x04
|
|
+#define USB_REQ_DFU_GETSTATE 0x05
|
|
+#define USB_REQ_DFU_ABORT 0x06
|
|
+
|
|
+struct dfu_status {
|
|
+ u_int8_t bStatus;
|
|
+ u_int8_t bwPollTimeout[3];
|
|
+ u_int8_t bState;
|
|
+ u_int8_t iString;
|
|
+} __attribute__((packed));
|
|
+
|
|
+#define DFU_STATUS_OK 0x00
|
|
+#define DFU_STATUS_errTARGET 0x01
|
|
+#define DFU_STATUS_errFILE 0x02
|
|
+#define DFU_STATUS_errWRITE 0x03
|
|
+#define DFU_STATUS_errERASE 0x04
|
|
+#define DFU_STATUS_errCHECK_ERASED 0x05
|
|
+#define DFU_STATUS_errPROG 0x06
|
|
+#define DFU_STATUS_errVERIFY 0x07
|
|
+#define DFU_STATUS_errADDRESS 0x08
|
|
+#define DFU_STATUS_errNOTDONE 0x09
|
|
+#define DFU_STATUS_errFIRMWARE 0x0a
|
|
+#define DFU_STATUS_errVENDOR 0x0b
|
|
+#define DFU_STATUS_errUSBR 0x0c
|
|
+#define DFU_STATUS_errPOR 0x0d
|
|
+#define DFU_STATUS_errUNKNOWN 0x0e
|
|
+#define DFU_STATUS_errSTALLEDPKT 0x0f
|
|
+
|
|
+enum dfu_state {
|
|
+ DFU_STATE_appIDLE = 0,
|
|
+ DFU_STATE_appDETACH = 1,
|
|
+ DFU_STATE_dfuIDLE = 2,
|
|
+ DFU_STATE_dfuDNLOAD_SYNC = 3,
|
|
+ DFU_STATE_dfuDNBUSY = 4,
|
|
+ DFU_STATE_dfuDNLOAD_IDLE = 5,
|
|
+ DFU_STATE_dfuMANIFEST_SYNC = 6,
|
|
+ DFU_STATE_dfuMANIFEST = 7,
|
|
+ DFU_STATE_dfuMANIFEST_WAIT_RST = 8,
|
|
+ DFU_STATE_dfuUPLOAD_IDLE = 9,
|
|
+ DFU_STATE_dfuERROR = 10,
|
|
+};
|
|
+
|
|
+#endif /* _USB_DFU_H */
|
|
Index: u-boot/include/usbdcore.h
|
|
===================================================================
|
|
--- u-boot.orig/include/usbdcore.h
|
|
+++ u-boot/include/usbdcore.h
|
|
@@ -33,6 +33,7 @@
|
|
|
|
#include <common.h>
|
|
#include "usbdescriptors.h"
|
|
+#include <usb_dfu_descriptors.h>
|
|
|
|
|
|
#define MAX_URBS_QUEUED 5
|
|
@@ -475,7 +476,11 @@
|
|
* function driver to inform it that data has arrived.
|
|
*/
|
|
|
|
+#ifdef CONFIG_USBD_DFU
|
|
+#define URB_BUF_SIZE (128+CONFIG_USBD_DFU_XFER_SIZE)
|
|
+#else
|
|
#define URB_BUF_SIZE 128 /* in linux we'd malloc this, but in u-boot we prefer static data */
|
|
+#endif
|
|
struct urb {
|
|
|
|
struct usb_endpoint_instance *endpoint;
|
|
@@ -603,6 +608,12 @@
|
|
unsigned long usbd_rxtx_timestamp;
|
|
unsigned long usbd_last_rxtx_timestamp;
|
|
|
|
+#ifdef CONFIG_USBD_DFU
|
|
+ const struct usb_device_descriptor *dfu_dev_desc;
|
|
+ const struct _dfu_desc *dfu_cfg_desc;
|
|
+ enum dfu_state dfu_state;
|
|
+ u_int8_t dfu_status;
|
|
+#endif
|
|
};
|
|
|
|
/* Bus Interface configuration structure
|
|
@@ -632,6 +643,8 @@
|
|
extern char *usbd_device_requests[];
|
|
extern char *usbd_device_descriptors[];
|
|
|
|
+extern struct usb_string_descriptor **usb_strings;
|
|
+
|
|
void urb_link_init (urb_link * ul);
|
|
void urb_detach (struct urb *urb);
|
|
urb_link *first_urb_link (urb_link * hd);
|
|
Index: u-boot/drivers/usbtty.h
|
|
===================================================================
|
|
--- u-boot.orig/drivers/usbtty.h
|
|
+++ u-boot/drivers/usbtty.h
|
|
@@ -71,4 +71,10 @@
|
|
#define STR_CTRL_INTERFACE 0x06
|
|
#define STR_COUNT 0x07
|
|
|
|
+#ifdef CONFIG_USBD_DFU
|
|
+#define NUM_STRINGS DFU_STR_COUNT
|
|
+#else
|
|
+#define NUM_STRINGS STR_COUNT
|
|
+#endif
|
|
+
|
|
#endif
|
|
Index: u-boot/include/configs/qt2410.h
|
|
===================================================================
|
|
--- u-boot.orig/include/configs/qt2410.h
|
|
+++ u-boot/include/configs/qt2410.h
|
|
@@ -199,7 +199,8 @@
|
|
#define CONFIG_USBD_PRODUCT_NAME "QT2410 Bootloader " U_BOOT_VERSION
|
|
#define CONFIG_EXTRA_ENV_SETTINGS "usbtty=cdc_acm\0"
|
|
#define CONFIG_USBD_DFU 1
|
|
-#define CONFIG_USBD_DFU_XFER_SIZE 0x4000
|
|
+#define CONFIG_USBD_DFU_XFER_SIZE 4096
|
|
+#define CONFIG_USBD_DFU_INTERFACE 2
|
|
|
|
/*-----------------------------------------------------------------------
|
|
* Physical Memory Map
|
|
Index: u-boot/tools/Makefile
|
|
===================================================================
|
|
--- u-boot.orig/tools/Makefile
|
|
+++ u-boot/tools/Makefile
|
|
@@ -21,10 +21,10 @@
|
|
# MA 02111-1307 USA
|
|
#
|
|
|
|
-BIN_FILES = img2srec$(SFX) mkimage$(SFX) envcrc$(SFX) gen_eth_addr$(SFX) bmp_logo$(SFX)
|
|
+BIN_FILES = img2srec$(SFX) mkimage$(SFX) envcrc$(SFX) gen_eth_addr$(SFX) bmp_logo$(SFX) mkudfu$(SFX)
|
|
|
|
OBJ_LINKS = environment.o crc32.o
|
|
-OBJ_FILES = img2srec.o mkimage.o envcrc.o gen_eth_addr.o bmp_logo.o
|
|
+OBJ_FILES = img2srec.o mkimage.o envcrc.o gen_eth_addr.o bmp_logo.o mkudfu.o
|
|
|
|
ifeq ($(ARCH),mips)
|
|
BIN_FILES += inca-swap-bytes$(SFX)
|
|
@@ -137,6 +137,10 @@
|
|
$(CC) $(CFLAGS) $(HOST_LDFLAGS) -o $@ $^
|
|
$(STRIP) $@
|
|
|
|
+$(obj)mkudfu$(SFX): $(obj)mkudfu.o
|
|
+ $(CC) $(CFLAGS) $(HOST_LDFLAGS) -o $@ $^
|
|
+ $(STRIP) $@
|
|
+
|
|
$(obj)ncb$(SFX): $(obj)ncb.o
|
|
$(CC) $(CFLAGS) $(HOST_LDFLAGS) -o $@ $^
|
|
$(STRIP) $@
|
|
Index: u-boot/tools/mkudfu.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ u-boot/tools/mkudfu.c
|
|
@@ -0,0 +1,314 @@
|
|
+/*
|
|
+ * USB DFU file trailer tool
|
|
+ * (C) Copyright by OpenMoko, Inc.
|
|
+ * Author: Harald Welte <laforge@openmoko.org>
|
|
+ *
|
|
+ * based on mkimage.c, copyright information as follows:
|
|
+ *
|
|
+ * (C) Copyright 2000-2004
|
|
+ * DENX Software Engineering
|
|
+ * Wolfgang Denk, wd@denx.de
|
|
+ * All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public License as
|
|
+ * published by the Free Software Foundation; either version 2 of
|
|
+ * the License, or (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
+ * MA 02111-1307 USA
|
|
+ */
|
|
+
|
|
+#include <errno.h>
|
|
+#include <fcntl.h>
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <string.h>
|
|
+#ifndef __WIN32__
|
|
+#include <netinet/in.h> /* for host / network byte order conversions */
|
|
+#endif
|
|
+#include <sys/mman.h>
|
|
+#include <sys/stat.h>
|
|
+#include <time.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+#if defined(__BEOS__) || defined(__NetBSD__) || defined(__APPLE__)
|
|
+#include <inttypes.h>
|
|
+#endif
|
|
+
|
|
+#ifdef __WIN32__
|
|
+typedef unsigned int __u32;
|
|
+
|
|
+#define SWAP_LONG(x) \
|
|
+ ((__u32)( \
|
|
+ (((__u32)(x) & (__u32)0x000000ffUL) << 24) | \
|
|
+ (((__u32)(x) & (__u32)0x0000ff00UL) << 8) | \
|
|
+ (((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | \
|
|
+ (((__u32)(x) & (__u32)0xff000000UL) >> 24) ))
|
|
+typedef unsigned char uint8_t;
|
|
+typedef unsigned short uint16_t;
|
|
+typedef unsigned int uint32_t;
|
|
+
|
|
+#define ntohl(a) SWAP_LONG(a)
|
|
+#define htonl(a) SWAP_LONG(a)
|
|
+#endif /* __WIN32__ */
|
|
+
|
|
+#ifndef O_BINARY /* should be define'd on __WIN32__ */
|
|
+#define O_BINARY 0
|
|
+#endif
|
|
+
|
|
+#include <usb_dfu_trailer.h>
|
|
+
|
|
+extern int errno;
|
|
+
|
|
+#ifndef MAP_FAILED
|
|
+#define MAP_FAILED (-1)
|
|
+#endif
|
|
+
|
|
+static char *cmdname;
|
|
+
|
|
+static char *datafile;
|
|
+static char *imagefile;
|
|
+
|
|
+
|
|
+static void usage()
|
|
+{
|
|
+ fprintf (stderr, "%s - create / display u-boot DFU trailer\n", cmdname);
|
|
+ fprintf (stderr, "Usage: %s -l image\n"
|
|
+ " -l ==> list image header information\n"
|
|
+ " %s -v VID -p PID -r REV -d data_file image\n",
|
|
+ cmdname, cmdname);
|
|
+ fprintf (stderr, " -v ==> set vendor ID to 'VID'\n"
|
|
+ " -p ==> set product ID system to 'PID'\n"
|
|
+ " -r ==> set hardware revision to 'REV'\n"
|
|
+ " -d ==> use 'data_file' as input file\n"
|
|
+ );
|
|
+ exit (EXIT_FAILURE);
|
|
+}
|
|
+
|
|
+static void print_trailer(struct uboot_dfu_trailer *trailer)
|
|
+{
|
|
+ printf("===> DFU Trailer information:\n");
|
|
+ printf("Trailer Vers.: %d\n", trailer->version);
|
|
+ printf("Trailer Length: %d\n", trailer->length);
|
|
+ printf("VendorID: 0x%04x\n", trailer->vendor);
|
|
+ printf("ProductID: 0x%04x\n", trailer->product);
|
|
+ printf("HW Revision: 0x%04x\n", trailer->revision);
|
|
+}
|
|
+
|
|
+static void copy_file (int ifd, const char *datafile, int pad)
|
|
+{
|
|
+ int dfd;
|
|
+ struct stat sbuf;
|
|
+ unsigned char *ptr;
|
|
+ int tail;
|
|
+ int zero = 0;
|
|
+ int offset = 0;
|
|
+ int size;
|
|
+
|
|
+ if ((dfd = open(datafile, O_RDONLY|O_BINARY)) < 0) {
|
|
+ fprintf (stderr, "%s: Can't open %s: %s\n",
|
|
+ cmdname, datafile, strerror(errno));
|
|
+ exit (EXIT_FAILURE);
|
|
+ }
|
|
+
|
|
+ if (fstat(dfd, &sbuf) < 0) {
|
|
+ fprintf (stderr, "%s: Can't stat %s: %s\n",
|
|
+ cmdname, datafile, strerror(errno));
|
|
+ exit (EXIT_FAILURE);
|
|
+ }
|
|
+
|
|
+ ptr = (unsigned char *)mmap(0, sbuf.st_size,
|
|
+ PROT_READ, MAP_SHARED, dfd, 0);
|
|
+ if (ptr == (unsigned char *)MAP_FAILED) {
|
|
+ fprintf (stderr, "%s: Can't read %s: %s\n",
|
|
+ cmdname, datafile, strerror(errno));
|
|
+ exit (EXIT_FAILURE);
|
|
+ }
|
|
+
|
|
+ size = sbuf.st_size - offset;
|
|
+ if (write(ifd, ptr + offset, size) != size) {
|
|
+ fprintf (stderr, "%s: Write error on %s: %s\n",
|
|
+ cmdname, imagefile, strerror(errno));
|
|
+ exit (EXIT_FAILURE);
|
|
+ }
|
|
+
|
|
+ if (pad && ((tail = size % 4) != 0)) {
|
|
+
|
|
+ if (write(ifd, (char *)&zero, 4-tail) != 4-tail) {
|
|
+ fprintf (stderr, "%s: Write error on %s: %s\n",
|
|
+ cmdname, imagefile, strerror(errno));
|
|
+ exit (EXIT_FAILURE);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ (void) munmap((void *)ptr, sbuf.st_size);
|
|
+ (void) close (dfd);
|
|
+}
|
|
+
|
|
+
|
|
+int main(int argc, char **argv)
|
|
+{
|
|
+ int ifd;
|
|
+ int lflag = 0;
|
|
+ struct stat sbuf;
|
|
+ u_int16_t opt_vendor, opt_product, opt_revision;
|
|
+ struct uboot_dfu_trailer _hdr, _mirror, *hdr = &_hdr;
|
|
+
|
|
+ opt_vendor = opt_product = opt_revision = 0;
|
|
+
|
|
+ cmdname = *argv;
|
|
+
|
|
+ while (--argc > 0 && **++argv == '-') {
|
|
+ while (*++*argv) {
|
|
+ switch (**argv) {
|
|
+ case 'l':
|
|
+ lflag = 1;
|
|
+ break;
|
|
+ case 'v':
|
|
+ if (--argc <= 0)
|
|
+ usage ();
|
|
+ opt_vendor = strtoul(*++argv, NULL, 16);
|
|
+ goto NXTARG;
|
|
+ case 'p':
|
|
+ if (--argc <= 0)
|
|
+ usage ();
|
|
+ opt_product = strtoul(*++argv, NULL, 16);
|
|
+ goto NXTARG;
|
|
+ case 'r':
|
|
+ if (--argc <= 0)
|
|
+ usage ();
|
|
+ opt_revision = strtoul(*++argv, NULL, 16);
|
|
+ goto NXTARG;
|
|
+ case 'd':
|
|
+ if (--argc <= 0)
|
|
+ usage ();
|
|
+ datafile = *++argv;
|
|
+ goto NXTARG;
|
|
+ case 'h':
|
|
+ usage();
|
|
+ break;
|
|
+ default:
|
|
+ usage();
|
|
+ }
|
|
+ }
|
|
+NXTARG: ;
|
|
+ }
|
|
+
|
|
+ if (argc != 1)
|
|
+ usage();
|
|
+
|
|
+ imagefile = *argv;
|
|
+
|
|
+ if (lflag)
|
|
+ ifd = open(imagefile, O_RDONLY|O_BINARY);
|
|
+ else
|
|
+ ifd = open(imagefile, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0666);
|
|
+
|
|
+ if (ifd < 0) {
|
|
+ fprintf (stderr, "%s: Can't open %s: %s\n",
|
|
+ cmdname, imagefile, strerror(errno));
|
|
+ exit (EXIT_FAILURE);
|
|
+ }
|
|
+
|
|
+ if (lflag) {
|
|
+ unsigned char *ptr;
|
|
+ /* list header information of existing image */
|
|
+ if (fstat(ifd, &sbuf) < 0) {
|
|
+ fprintf (stderr, "%s: Can't stat %s: %s\n",
|
|
+ cmdname, imagefile, strerror(errno));
|
|
+ exit (EXIT_FAILURE);
|
|
+ }
|
|
+
|
|
+ if ((unsigned)sbuf.st_size < sizeof(struct uboot_dfu_trailer)) {
|
|
+ fprintf (stderr,
|
|
+ "%s: Bad size: \"%s\" is no valid image\n",
|
|
+ cmdname, imagefile);
|
|
+ exit (EXIT_FAILURE);
|
|
+ }
|
|
+
|
|
+ ptr = (unsigned char *)mmap(0, sbuf.st_size,
|
|
+ PROT_READ, MAP_SHARED, ifd, 0);
|
|
+ if ((caddr_t)ptr == (caddr_t)-1) {
|
|
+ fprintf (stderr, "%s: Can't read %s: %s\n",
|
|
+ cmdname, imagefile, strerror(errno));
|
|
+ exit (EXIT_FAILURE);
|
|
+ }
|
|
+
|
|
+ dfu_trailer_mirror(hdr, ptr+sbuf.st_size);
|
|
+
|
|
+ if (hdr->magic != UBOOT_DFU_TRAILER_MAGIC) {
|
|
+ fprintf (stderr,
|
|
+ "%s: Bad Magic Number: \"%s\" is no valid image\n",
|
|
+ cmdname, imagefile);
|
|
+ exit (EXIT_FAILURE);
|
|
+ }
|
|
+
|
|
+ /* for multi-file images we need the data part, too */
|
|
+ print_trailer(hdr);
|
|
+
|
|
+ (void) munmap((void *)ptr, sbuf.st_size);
|
|
+ (void) close (ifd);
|
|
+
|
|
+ exit (EXIT_SUCCESS);
|
|
+ }
|
|
+
|
|
+ /* if we're not listing: */
|
|
+
|
|
+ copy_file (ifd, datafile, 0);
|
|
+
|
|
+ memset (hdr, 0, sizeof(struct uboot_dfu_trailer));
|
|
+
|
|
+ /* Build new header */
|
|
+ hdr->version = UBOOT_DFU_TRAILER_V1;
|
|
+ hdr->magic = UBOOT_DFU_TRAILER_MAGIC;
|
|
+ hdr->length = sizeof(struct uboot_dfu_trailer);
|
|
+ hdr->vendor = opt_vendor;
|
|
+ hdr->product = opt_product;
|
|
+ hdr->revision = opt_revision;
|
|
+
|
|
+ print_trailer(hdr);
|
|
+ dfu_trailer_mirror(&_mirror, (unsigned char *)hdr+sizeof(*hdr));
|
|
+
|
|
+ if (write(ifd, &_mirror, sizeof(struct uboot_dfu_trailer))
|
|
+ != sizeof(struct uboot_dfu_trailer)) {
|
|
+ fprintf (stderr, "%s: Write error on %s: %s\n",
|
|
+ cmdname, imagefile, strerror(errno));
|
|
+ exit (EXIT_FAILURE);
|
|
+ }
|
|
+
|
|
+ /* We're a bit of paranoid */
|
|
+#if defined(_POSIX_SYNCHRONIZED_IO) && !defined(__sun__) && !defined(__FreeBSD__)
|
|
+ (void) fdatasync (ifd);
|
|
+#else
|
|
+ (void) fsync (ifd);
|
|
+#endif
|
|
+
|
|
+ if (fstat(ifd, &sbuf) < 0) {
|
|
+ fprintf (stderr, "%s: Can't stat %s: %s\n",
|
|
+ cmdname, imagefile, strerror(errno));
|
|
+ exit (EXIT_FAILURE);
|
|
+ }
|
|
+
|
|
+ /* We're a bit of paranoid */
|
|
+#if defined(_POSIX_SYNCHRONIZED_IO) && !defined(__sun__) && !defined(__FreeBSD__)
|
|
+ (void) fdatasync (ifd);
|
|
+#else
|
|
+ (void) fsync (ifd);
|
|
+#endif
|
|
+
|
|
+ if (close(ifd)) {
|
|
+ fprintf (stderr, "%s: Write error on %s: %s\n",
|
|
+ cmdname, imagefile, strerror(errno));
|
|
+ exit (EXIT_FAILURE);
|
|
+ }
|
|
+
|
|
+ exit (EXIT_SUCCESS);
|
|
+}
|
|
Index: u-boot/include/usb_dfu_trailer.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ u-boot/include/usb_dfu_trailer.h
|
|
@@ -0,0 +1,31 @@
|
|
+#ifndef _USB_DFU_TRAILER_H
|
|
+#define _USB_DFU_TRAILER_H
|
|
+
|
|
+/* trailer handling for DFU files */
|
|
+
|
|
+#define UBOOT_DFU_TRAILER_V1 1
|
|
+#define UBOOT_DFU_TRAILER_MAGIC 0x19731978
|
|
+struct uboot_dfu_trailer {
|
|
+ u_int32_t magic;
|
|
+ u_int16_t version;
|
|
+ u_int16_t length;
|
|
+ u_int16_t vendor;
|
|
+ u_int16_t product;
|
|
+ u_int32_t revision;
|
|
+} __attribute__((packed));
|
|
+
|
|
+/* we mirror the trailer because we want it to be longer in later versions
|
|
+ * while keeping backwards compatibility */
|
|
+static inline void dfu_trailer_mirror(struct uboot_dfu_trailer *trailer,
|
|
+ unsigned char *eof)
|
|
+{
|
|
+ int i;
|
|
+ int len = sizeof(struct uboot_dfu_trailer);
|
|
+ unsigned char *src = eof - len;
|
|
+ unsigned char *dst = (unsigned char *) trailer;
|
|
+
|
|
+ for (i = 0; i < len; i++)
|
|
+ dst[len-1-i] = src[i];
|
|
+}
|
|
+
|
|
+#endif /* _USB_DFU_TRAILER_H */
|
|
Index: u-boot/Makefile
|
|
===================================================================
|
|
--- u-boot.orig/Makefile
|
|
+++ u-boot/Makefile
|
|
@@ -261,6 +261,12 @@
|
|
$(obj)u-boot.bin: $(obj)u-boot
|
|
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
|
|
|
|
+$(obj)u-boot.udfu: $(obj)u-boot.bin
|
|
+ ./tools/mkudfu -v $(CONFIG_USB_DFU_VENDOR) \
|
|
+ -p $(CONFIG_USB_DFU_PRODUCT) \
|
|
+ -r $(CONFIG_USB_DFU_REVISION) \
|
|
+ -d $< $@
|
|
+
|
|
$(obj)u-boot.img: $(obj)u-boot.bin
|
|
./tools/mkimage -A $(ARCH) -T firmware -C none \
|
|
-a $(TEXT_BASE) -e 0 \
|
|
Index: u-boot/board/neo1973/gta01/split_by_variant.sh
|
|
===================================================================
|
|
--- u-boot.orig/board/neo1973/gta01/split_by_variant.sh
|
|
+++ u-boot/board/neo1973/gta01/split_by_variant.sh
|
|
@@ -15,37 +15,44 @@
|
|
echo "$0:: No parameters - using GTA01Bv3 config"
|
|
echo "#define CONFIG_ARCH_GTA01B_v3" > $CFGINC
|
|
echo "GTA01_BIG_RAM=y" > $CFGTMP
|
|
+ echo "CONFIG_USB_DFU_REVISION=0x0230" > $CFGTMP
|
|
else
|
|
case "$1" in
|
|
gta01v4_config)
|
|
echo "#define CONFIG_ARCH_GTA01_v4" > $CFGINC
|
|
echo "GTA01_BIG_RAM=n" > $CFGTMP
|
|
+ echo "CONFIG_USB_DFU_REVISION=0x0140" > $CFGTMP
|
|
;;
|
|
|
|
gta01v3_config)
|
|
echo "#define CONFIG_ARCH_GTA01_v3" > $CFGINC
|
|
echo "GTA01_BIG_RAM=n" > $CFGTMP
|
|
+ echo "CONFIG_USB_DFU_REVISION=0x0130" > $CFGTMP
|
|
;;
|
|
|
|
gta01bv2_config)
|
|
echo "#define CONFIG_ARCH_GTA01B_v2" > $CFGINC
|
|
echo "GTA01_BIG_RAM=y" > $CFGTMP
|
|
+ echo "CONFIG_USB_DFU_REVISION=0x0220" > $CFGTMP
|
|
;;
|
|
|
|
gta01bv3_config)
|
|
echo "#define CONFIG_ARCH_GTA01B_v3" > $CFGINC
|
|
echo "GTA01_BIG_RAM=y" > $CFGTMP
|
|
+ echo "CONFIG_USB_DFU_REVISION=0x0230" > $CFGTMP
|
|
;;
|
|
|
|
gta01bv4_config)
|
|
echo "#define CONFIG_ARCH_GTA01B_v4" > $CFGINC
|
|
echo "GTA01_BIG_RAM=y" > $CFGTMP
|
|
+ echo "CONFIG_USB_DFU_REVISION=0x0240" > $CFGTMP
|
|
;;
|
|
|
|
*)
|
|
echo "$0:: Unrecognised config - using GTA01Bv4 config"
|
|
echo "#define CONFIG_ARCH_GTA01B_v4" > $CFGINC
|
|
echo "GTA01_BIG_RAM=y" > $CFGTMP
|
|
+ echo "CONFIG_USB_DFU_REVISION=0x0240" > $CFGTMP
|
|
;;
|
|
|
|
esac
|
|
Index: u-boot/board/neo1973/gta01/config.mk
|
|
===================================================================
|
|
--- u-boot.orig/board/neo1973/gta01/config.mk
|
|
+++ u-boot/board/neo1973/gta01/config.mk
|
|
@@ -24,6 +24,9 @@
|
|
#
|
|
# download area is 3200'0000 or 3300'0000
|
|
|
|
+CONFIG_USB_DFU_VENDOR=0x1457
|
|
+CONFIG_USB_DFU_PRODUCT=0x5119
|
|
+
|
|
sinclude $(OBJTREE)/board/$(BOARDDIR)/config.tmp
|
|
|
|
ifeq ($(GTA01_BIG_RAM),y)
|