[U-Boot] [PATCH 2/3] add USB DFU driver
Marcel Janssen
korgull at home.nl
Sat Feb 12 17:20:03 CET 2011
From: Marcel <korgull at home.nl>
Signed-off-by: Marcel <korgull at home.nl>
---
common/update_dfu.c | 90 +++
doc/README.dfu | 129 ++++
drivers/usb/gadget/Makefile | 10 +-
drivers/usb/gadget/usbdfu.c | 1470 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1697 insertions(+), 2 deletions(-)
create mode 100644 common/update_dfu.c
create mode 100644 doc/README.dfu
create mode 100644 drivers/usb/gadget/usbdfu.c
diff --git a/common/update_dfu.c b/common/update_dfu.c
new file mode 100644
index 0000000..5f8da42
--- /dev/null
+++ b/common/update_dfu.c
@@ -0,0 +1,90 @@
+/*
+ * (C) Copyright 2008 Semihalf
+ *
+ * Written by: Rafal Czubak <rcz at semihalf.com>
+ * Bartlomiej Sieka <tur at semihalf.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+#include <common.h>
+DECLARE_GLOBAL_DATA_PTR;
+
+#include <malloc.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <asm-generic/errno.h>
+
+#include <linux/usb/ch9.h>
+
+#include <linux/usb/gadget.h>
+#include <command.h>
+#include <usb/atmel_usba_udc.h>
+#include <asm/arch/gpio.h>
+
+int dfu_finished;
+int usb_dfu_init(void);
+
+#ifdef CONFIG_USB_GADGET_ATMEL_USBA
+struct platform_data dfubrd = {
+ .board = {
+ //.vbus_pin = AT91_PIN_PC9,// AT91_PIN_PC0, },
+ .vbus_pin = AT91_PIN_PC0,
+ .pullup_pin = 1,
+ },
+ .udc_clk = AT91SAM9G45_ID_UDPHS,
+};
+#endif
+
+int dfu_loop(void)
+{
+ int rcv;
+
+ dfu_finished = 0;
+#ifdef CONFIG_USB_GADGET_ATMEL_USBA
+ usba_udc_probe(&dfubrd);
+#endif
+#ifdef CONFIG_USB_GADGET_AT91
+ at91udc_probe(&dfubrd);
+#endif
+ udelay(100000);
+
+ if(usb_dfu_init() == 0)
+ {
+
+ while (!dfu_finished ) {
+ /* Handle control-c and timeouts */
+ rcv = usb_gadget_handle_interrupts();
+ if(rcv)
+ if (ctrlc()) goto dfu_end;
+ }
+ return 0;
+ }
+ return -1;
+dfu_end:
+ return -1;
+}
+
+U_BOOT_CMD(
+ dfu, 1, 0, dfu_loop,
+ "Start DFU function",
+ "No params, see README.dfu"
+);
+
\ No newline at end of file
diff --git a/doc/README.dfu b/doc/README.dfu
new file mode 100644
index 0000000..04b7b76
--- /dev/null
+++ b/doc/README.dfu
@@ -0,0 +1,129 @@
+USBD DFU mode
+
+Initially written by Marcel Janssen (marcel.janssen at admesy.nl).
+Based on parts from OpenMoko, ether.c and update.c
+
+
+========================================
+
+This describes the DFU implementation in u-boot.
+
+The implementation works with dfu-utils to upgrade NAND partitions defined by mtdparts.
+The board configuration file needs serveral CONFIG options to be set.
+DFU is implemented to be executed as a command "dfu". This command should start the USB device
+controller and the DFU driver. This is done in common/update_dfu.c
+
+A typical implementation would be that a script is executed, that will check whether DFU should
+be started. If so, it executes 'dfu" and the device will announce itself to the host as a DFU
+capable device. dfu-util can than be used to upgrade the partitions defined by mtdparts.
+
+Description of flow :
+dfu-utils sets the alternate interface which corresponds to the selected partition.
+The file (uImage, rootfs.arm.jffs2) is loaded fully to RAM first.
+U-boot nand routines are used to write from RAM to NAND.
+
+LED usage :
+Status LED's can be defined to show DFU action.
+Define the RED and GREEN leds to make this happen.
+
+Initial testing example :
+This was done on the in-circuit sam9g45_oem board. This board uses atmel_usbd_udc.c
+
+========================================
+
+
+
+To make DFU work you need a working USB controller, for example at91_udc or atmel_usba_udc.
+Make sure to set it in the board config file
+
+========================================
+USBD CONFIG options
+----------------------------------------
+
+#define CONFIG_USB_GADGET
+#define CONFIG_USB_GADGET_ATMEL_USBA (or #define CONFIG_USB_GADGET_AT91_UDC )
+#define CONFIG_USB_GADGET_DUALSPEED
+
+----------------------------------------
+USBD CONFIG options end
+========================================
+
+
+
+The DFU driver has a few options.
+Make sure that CONFIG_USBD_DFU_XFER_SIZE does not exceed your USB_BUFSIZE.
+
+========================================
+DFU CONFIG options
+----------------------------------------
+
+#define CONFIG_USBD_DFU 1
+#ifdef CONFIG_USBD_DFU
+#define CONFIG_USBD_VENDORID 0x23CF /* Admesy - Use this for testing purposes only */
+#define CONFIG_USBD_PRODUCTID_DFU 0xBEEF /* donated number */
+#define CONFIG_USBD_MANUFACTURER "Admesy"
+#define CONFIG_USBD_PRODUCT_NAME "Admesy DFU 001"
+#define CONFIG_USBD_DFU_XFER_SIZE 4096 /* 4096 is maximum or increase USB_BUFSIZ*/
+#define CONFIG_USBD_DFU_INTERFACE 0
+#define DFU_NUM_ALTERNATES 3 /* 3 partitions */
+#define LOAD_ADDR ((unsigned char *)0x70400000) /* RAM address to use to write files to */
+#endif
+
+----------------------------------------
+DFU CONFIG options end
+========================================
+
+
+
+In order to make DFU work with dfu-utils, mtdparts need to be defined.
+See the example futher below on how to do this.
+
+========================================
+mtdparts example with dfu-utils
+----------------------------------------
+
+mtdparts add nand0 0x2000000 kernel
+mtdparts add nand0 0x1000000 at 0x00200000 root
+mtdparts add nand0 0xEE00000 at 0x01200000 data
+saveenv
+
+Your mtdparts than should look like this :
+
+board> mtdparts
+device nand0 <nand.0>, # parts = 3
+ #: name size offset mask_flags
+ 0: kernel 0x00200000 0x00000000 0
+ 1: root 0x01000000 0x00200000 0
+ 2: data 0x0ee00000 0x01200000 0
+
+active partition: nand0,0 - (kernel) 0x00200000 @ 0x00000000
+
+
+After the mtdparts have been defined, dfu-utils can be used to upgrade the kernel and root partition.
+Make sure you have read/write access to the DFU device !
+
+cd dfu-utils/src
+./dfu-util -a0 -D uImage
+./dfu-util -a1 -D rootfs.arm.jffs2 -R
+
+----------------------------------------
+mtdparts example end
+========================================
+
+
+
+Possible changes for the near future :
+1) integrate DFU and Ethernet and/or tty by making it a composite driver.
+2) allow partitions larger than RAM by implementing per-buffer NAND writing.
+ Openmoko does this, but I didn't need it and liked to write to RAM first.
+3) Allow DFU to flash NOR (could be added to handle_dnload )
+
+
+
+
+
+
+
+
+
+
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 024844d..91246da 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -26,10 +26,15 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libusb_gadget.o
# new USB gadget layer dependencies
-ifdef CONFIG_USB_ETHER
-COBJS-y += ether.o epautoconf.o config.o usbstring.o
+#ifdef CONFIG_USB_ETHER
+#COBJS-y += ether.o epautoconf.o config.o usbstring.o
+
+ifdef CONFIG_USBD_DFU
+COBJS-y += usbdfu.o epautoconf.o config.o usbstring.o
+
COBJS-$(CONFIG_USB_GADGET_AT91) += at91_udc.o
COBJS-$(CONFIG_USB_GADGET_ATMEL_USBA) += atmel_usba_udc.o
+
else
# Devices not related to the new gadget layer depend on CONFIG_USB_DEVICE
ifdef CONFIG_USB_DEVICE
@@ -43,6 +48,7 @@ COBJS-$(CONFIG_SPEARUDC) += spr_udc.o
endif
endif
+
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
diff --git a/drivers/usb/gadget/usbdfu.c b/drivers/usb/gadget/usbdfu.c
new file mode 100644
index 0000000..8c6199e
--- /dev/null
+++ b/drivers/usb/gadget/usbdfu.c
@@ -0,0 +1,1470 @@
+/* (C) 2011 Admesy B.V (Marcel Janssen)
+ * (C) 2007 by OpenMoko, Inc.
+ * Author: Harald Welte <laforge at openmoko.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * TODO:
+ * - make NAND support reasonably self-contained and put in apropriate
+ * ifdefs
+ * - add some means of synchronization, i.e. block commandline access
+ * while DFU transfer is in progress, and return to commandline once
+ * we're finished
+ * - add VERIFY support after writing to flash
+ * - sanely free() resources allocated during first uppload/download
+ * request when aborting
+ * - sanely free resources when another alternate interface is selected
+ *
+ * Maybe:
+ * - add something like uImage or some other header that provides CRC
+ * checking?
+ * - make 'dnstate' attached to 'struct dfu_dev'
+ */
+
+#include <config.h>
+#if defined(CONFIG_USBD_DFU)
+
+#include <common.h>
+DECLARE_GLOBAL_DATA_PTR;
+
+#include <malloc.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <asm-generic/errno.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include "gadget_chips.h"
+
+#include <usb_dfu.h>
+#include <usb_dfu_descriptors.h>
+#include <usb_dfu_trailer.h>
+#include <linux/ctype.h>
+#include <linux/porting-compat.h>
+
+#include <nand.h>
+#include <jffs2/load_kernel.h>
+#if defined(CONFIG_CMD_MTDPARTS)
+
+/* partition handling routines */
+int mtdparts_init(void);
+
+int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num);
+int find_dev_and_part(const char *id, struct mtd_device **dev,
+ u8 *part_num, struct part_info **part);
+#endif
+
+extern struct list_head devices;
+extern int dfu_finished;
+//extern struct usb_string_descriptor usb_strings[DFU_STR_COUNT];
+
+//#include "usbdcore_s3c2410.h"
+//#include <usb/atmel_usba_udc.h>
+//#include "../drivers/serial/usbtty.h" /* for STR_* defs */
+
+#define RET_NOTHING 0
+#define RET_ZLP 1
+#define RET_STALL 2
+
+/* this should be done differently */
+#define EP0_MAX_PACKET_SIZE 64 /* MUSB_EP0_FIFOSIZE */
+
+unsigned char ledcount;
+
+#define GFP_ATOMIC ((gfp_t) 0)
+//#define GFP_KERNEL ((gfp_t) 0)
+
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+#define DEVSPEED USB_SPEED_HIGH
+#else
+#define DEVSPEED USB_SPEED_FULL
+#endif
+
+#define USB_CONNECT_TIMEOUT (30 * CONFIG_SYS_HZ)
+volatile enum dfu_state *system_dfu_state; /* for 3rd parties */
+
+char rtm_dfu = 0;
+
+/*
+ * Some systems will want different product identifers published in the
+ * device descriptor, either numbers or strings or both. These string
+ * parameters are in UTF-8 (superset of ASCII's 7 bit characters).
+ */
+#define DRIVER_DESC "USBD-DFU device"
+#define FIXED_SERIAL "1234567" /* usually only one device should be in DFU mode, this may be fixed */
+static ushort bcdDevice;
+#if defined(CONFIG_USBD_MANUFACTURER)
+static char *iManufacturer = CONFIG_USBD_MANUFACTURER;
+#else
+static char *iManufacturer = "U-boot-dfu";
+#endif
+static char *iProduct;
+static char *iSerialNumber;
+
+/* holds our biggest descriptor */
+#define USB_BUFSIZ 4096
+
+#ifndef CONFIG_USBD_PRODUCTID_DFU
+#define #error YOU need to define a USBD product ID for DFU
+#endif
+#ifndef LOAD_ADDR
+#define LOAD_ADDR ((unsigned char *)0x70400000)
+#endif
+
+static struct dfu_dev l_dfudev;
+//static struct dfu_device l_dfudevice;
+static struct usb_gadget_driver dfu_driver;
+static u8 control_req[USB_BUFSIZ];
+
+
+struct usb_device_descriptor
+dfu_dev_descriptor = {
+ .bLength = USB_DT_DEVICE_SIZE,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = __constant_cpu_to_le16(0x0100),
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize0 = EP0_MAX_PACKET_SIZE,
+ .idVendor = __constant_cpu_to_le16(CONFIG_USBD_VENDORID),
+ .idProduct = __constant_cpu_to_le16(CONFIG_USBD_PRODUCTID_DFU),
+ .bcdDevice = 0x0000,
+ .iManufacturer = DFU_STR_MANUFACTURER,
+ .iProduct = DFU_STR_PRODUCT,
+ .iSerialNumber = DFU_STR_SERIAL,
+ .bNumConfigurations = 0x01,
+};
+
+/**/static struct usb_config_descriptor
+dfu_config = {
+ .bLength = USB_DT_CONFIG_SIZE,
+ .bDescriptorType = USB_DT_CONFIG,
+
+ //compute wTotalLength on the fly
+ .wTotalLength = USB_DT_CONFIG_SIZE +
+ USB_DT_INTERFACE_SIZE + USB_DT_DFU_SIZE,
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = DFU_STR_CONFIG,
+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+ .bMaxPower = 0x60,
+};
+
+static struct usb_interface_descriptor
+control_intf = {
+ .bLength = sizeof control_intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bInterfaceNumber = CONFIG_USBD_DFU_INTERFACE,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_APP_SPEC,
+ .bInterfaceSubClass = USB_DFU_SUBCLASS,
+ .bInterfaceProtocol = 1,
+ .iInterface = DFU_STR_CONFIG,
+};
+
+static struct usb_dfu_func_descriptor
+dfu_func = {
+ .bLength = USB_DT_DFU_SIZE,
+ .bDescriptorType = USB_DT_DFU,
+ .bmAttributes = USB_DFU_CAN_UPLOAD | USB_DFU_CAN_DOWNLOAD ,
+ .wDetachTimeOut = 0xff00,
+ .wTransferSize = CONFIG_USBD_DFU_XFER_SIZE,
+ .bcdDFUVersion = 0x0100,
+};
+
+static struct _dfu_desc dfu_cfg_descriptor = {
+ .ucfg = {
+ .bLength = USB_DT_CONFIG_SIZE,
+ .bDescriptorType = USB_DT_CONFIG,
+ .wTotalLength = USB_DT_CONFIG_SIZE +
+ DFU_NUM_ALTERNATES * USB_DT_INTERFACE_SIZE +
+ USB_DT_DFU_SIZE,
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = DFU_STR_CONFIG,
+ //FIXME BMATTRIBUTE_RESERVED
+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+ .bMaxPower = 200,
+ },
+ .func_dfu = DFU_FUNC_DESC,
+};
+
+static struct usb_qualifier_descriptor
+dev_qualifier = {
+ .bLength = sizeof dev_qualifier,
+ .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
+
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_APP_SPEC,
+
+ .bNumConfigurations = 1,
+};
+
+
+static const struct usb_descriptor_header *hs_rtm_function[3] = {
+ (struct usb_descriptor_header *) &control_intf,
+ (struct usb_descriptor_header *) &dfu_func,
+ NULL,
+};
+
+static const struct usb_descriptor_header *hs_dfu_function[5] = {
+ (struct usb_descriptor_header *) &dfu_cfg_descriptor.uif[0],
+ (struct usb_descriptor_header *) &dfu_cfg_descriptor.uif[1],
+ (struct usb_descriptor_header *) &dfu_cfg_descriptor.uif[2],
+ (struct usb_descriptor_header *) &dfu_cfg_descriptor.func_dfu,
+ NULL,
+};
+
+struct dnload_state {
+ nand_info_t *nand;
+ struct part_info *part;
+ unsigned int part_net_size; /* net 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 struct part_info *get_partition_nand(int idx)
+{
+ struct mtd_device *dev;
+ struct part_info *part;
+ struct list_head *dentry;
+ struct list_head *pentry;
+ int i;
+
+ if (mtdparts_init())
+ return NULL;
+
+ list_for_each(dentry, &devices) {
+ dev = list_entry(dentry, struct mtd_device, link);
+ if (dev->id->type == MTD_DEV_TYPE_NAND) {
+ i = 0;
+ list_for_each(pentry, &dev->parts) {
+ if (i == idx) {
+ part = list_entry(pentry,
+ struct part_info, link);
+ return part;
+ }
+ i++;
+ }
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+/* descriptors that are built on-demand */
+static char manufacturer[50];
+static char product_desc[40] = CONFIG_USBD_PRODUCT_NAME;//DRIVER_DESC;
+static char serial_number[20] = FIXED_SERIAL;
+
+/* static strings, in UTF-8 */
+static struct usb_string strings[] = {
+ { DFU_STR_MANUFACTURER, manufacturer, },
+ { DFU_STR_PRODUCT, product_desc, },
+ { DFU_STR_SERIAL, serial_number, },
+ { DFU_STR_CONFIG, "USB DFU Config", },
+ { DFU_STR_ALT1, "DFU part1", },
+ { DFU_STR_ALT2, "DFU part2", },
+ { DFU_STR_ALT3, "DFU part3", },
+ { DFU_STR_ALT4, "DFU part4", },
+ { DFU_STR_ALT5, "DFU part5", },
+ { DFU_STR_ALT6, "DFU part6", },
+ { } // end of list
+};
+
+struct usb_gadget_strings stringtab = {
+ .language = 0x0409, /* en-us */
+ .strings = strings,
+};
+
+/*============================================================================*/
+
+/**
+ * strlcpy - Copy a %NUL terminated string into a sized buffer
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ * @size: size of destination buffer
+ *
+ * Compatible with *BSD: the result is always a valid
+ * NUL-terminated string that fits in the buffer (unless,
+ * of course, the buffer size is zero). It does not pad
+ * out the result like strncpy() does.
+ */
+size_t strlcpy(char *dest, const char *src, size_t size)
+{
+ size_t ret = strlen(src);
+
+ if (size) {
+ size_t len = (ret >= size) ? size - 1 : ret;
+ memcpy(dest, src, len);
+ dest[len] = '\0';
+ }
+ return ret;
+}
+
+/*
+ * one config, two interfaces: control, data.
+ * complications: class descriptors, and an altsetting.
+ */
+static int
+config_buf(struct usb_gadget *g, u8 *buf, u8 type, unsigned index)
+{
+ int len;
+ const struct usb_config_descriptor *config;
+ const struct usb_descriptor_header **function;
+ int hs = 0;
+
+ debug("Set configuration - type %d - index %d\n", type, index);
+
+ if (gadget_is_dualspeed(g)) {
+ hs = (g->speed == USB_SPEED_HIGH);
+ if (type == USB_DT_OTHER_SPEED_CONFIG)
+ hs = !hs;
+ }
+
+ if (index >= dfu_dev_descriptor.bNumConfigurations)
+ return -EINVAL;
+
+ switch (type)
+ {
+ case 2:
+ {
+ config = &dfu_config;
+ if(rtm_dfu == 0) function = hs_rtm_function;
+ else function = hs_dfu_function;
+
+ }
+ break;
+ }
+ len = usb_gadget_config_buf(config, buf, USB_BUFSIZ, function);
+ if (len < 0)
+ return len;
+ ((struct usb_config_descriptor *) buf)->bDescriptorType = type;
+ return len;
+}
+
+static int
+set_dfu_config(struct dfu_dev *dev, gfp_t gfp_flags)
+{
+ const struct usb_config_descriptor *config;
+
+ printf("Set DFU config\n");
+
+ config = &dfu_cfg_descriptor.ucfg;
+
+ return 0;
+}
+
+static int handle_dnload(struct usb_gadget *gadget, u_int16_t val, u_int16_t len, int first)
+{
+ struct dfu_dev *dev = &l_dfudev;
+ struct usb_request *req = dev->req;
+ struct dnload_state *ds = &_dnstate;
+ int rc;
+ ulong addr;
+ size_t rwsize;
+
+ debug("download(len=%u, first=%u) \n", len, first);
+
+ if (len > CONFIG_USBD_DFU_XFER_SIZE) {
+ /* Too big. Not that we'd really care, but it's a
+ * DFU protocol violation */
+ printf("length exceeds flash page size ");
+ 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 */
+ /* the following code crashes I guess because of env_buf too small [32] */
+ char *mtdp = getenv("mtdparts");
+ if(mtdp) printf("Valid MTD partitions found\n");
+ //if (!mtdp)
+ //run_command("dynpart", 0);
+ else
+ {
+ dev->dfu_state = DFU_STATE_dfuERROR;
+ dev->dfu_status = DFU_STATUS_errADDRESS;
+ return RET_STALL;
+ }
+ }
+
+ if (len == 0) {
+ debug("zero-size write -> MANIFEST_SYNC ");
+ dev->dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
+
+ /* cleanup */
+ switch (dev->alternate) {
+ char buf[12];
+ /* we don't actually handle partitions differently */
+ /* the original Openmoko driver did use the alternate setting to do different things */
+ /* we have no need for that */
+ /* a big difference is that our driver first reads the full firmware to RAM */
+ /* and than writes it to flash after */
+ /* we could also not write to flash but let a script handle it via "nand write" */
+ default:
+ sprintf(buf, "0x%08x", ds->ptr - ds->buf);
+ addr = (ulong)LOAD_ADDR;
+ rwsize = ds->ptr - ds->buf;
+ printf("Filesize = %s\n",buf);
+ setenv("filesize", buf);
+ rc = 0;
+ rc = mtdparts_init();
+ if(rc)
+ {
+ printf("Error initilizing mtdparts\n");
+ break;
+ }
+ ds->part = get_partition_nand(dev->alternate);
+ if( ds->part == NULL)
+ {
+ printf("Error reading partition info\n");
+ break;
+ }
+
+ ds->erase_opts.offset = ds->part->offset;
+ ds->erase_opts.length = ds->part->size;
+ if (!rc && !strcmp(ds->part->name, "root")) ds->erase_opts.jffs2 = 1;
+ else ds->erase_opts.jffs2 = 0;
+ ds->erase_opts.quiet = 0;
+ ds->erase_opts.spread = 1;
+ ds->erase_opts.scrub = 0; /* make sure it was not set previously */
+ ds->nand = &nand_info[ds->part->dev->id->num];
+
+ printf("Using partition : %s ,", ds->part->name);
+ if(ds->erase_opts.jffs2) printf(" with jffs2 option\n");
+ else printf(" without jffs2 option\n");
+ printf("Partition size=%lx offset=%lx\n", (unsigned long)ds->part->size , (unsigned long)ds->part->offset );
+
+#ifdef CONFIG_GREEN_LED
+ green_LED_off();
+#endif
+#ifdef CONFIG_RED_LED
+ red_LED_off();
+#endif
+ rc = nand_erase_opts(ds->nand, &ds->erase_opts);
+ if( rc )
+ {
+ printf("NAND erase failed\n");
+ break;
+ }
+ else printf("NAND erased succesfully\n");
+#ifdef CONFIG_GREEN_LED
+ green_LED_on();
+#endif
+#ifdef CONFIG_RED_LED
+ red_LED_off();
+#endif
+ rc = nand_write_skip_bad(ds->nand, ds->part->offset, &rwsize, (u_char *)addr);
+ if( rc )
+ {
+ printf("NAND write failed\n");
+ break;
+ }
+ else printf("NAND Written succesfully\n");
+#ifdef CONFIG_GREEN_LED
+ green_LED_on();
+#endif
+#ifdef CONFIG_RED_LED
+ red_LED_on();
+#endif
+ break;
+
+ }
+ //dfu_finished = 1;
+ return RET_ZLP;
+ }
+
+ if (req->length != len) {
+ printf("req->length(%u) != len(%u) ?!? ",
+ req->length, len);
+ dev->dfu_state = DFU_STATE_dfuERROR;
+ dev->dfu_status = DFU_STATUS_errADDRESS;
+ return RET_STALL;
+ }
+
+ if (first && ds->buf && ds->buf != ds->_buf && ds->buf != LOAD_ADDR) {
+ printf("free buffer \n");
+ free(ds->buf);
+ ds->buf = ds->_buf;
+ }
+
+ debug("alternate %d , first = %d\n",dev->alternate, first);
+ switch (dev->alternate) {
+ default:
+ if (first) {
+ //printf("Starting DFU DOWNLOAD to RAM %s\n",
+ // LOAD_ADDR);
+ ds->buf = LOAD_ADDR;
+ ds->ptr = ds->buf;
+ printf("Starting DFU DOWNLOAD to RAM %p\n",ds->ptr);
+ }
+ memcpy(ds->ptr, req->buf, len);
+ ds->ptr += len;
+ debug("data in buf %x, next address = %p\n", *(char *)req->buf, ds->ptr);
+ break;
+ }
+ debug("Copy finished at %p\n", ds->ptr);
+ return RET_ZLP;
+}
+
+static int handle_upload(struct usb_gadget *gadget, u_int16_t val, u_int16_t len, int first)
+{
+ struct dfu_dev *dev = &l_dfudev;//get_gadget_data(gadget);
+ //struct usb_request *req = dev->req;
+ //struct dnload_state *ds = &_dnstate;
+ //unsigned int remain;
+ //int rc;
+
+ printf("upload(val=0x%02x, len=%u, first=%u) ", val, len, first);
+
+ if (len > CONFIG_USBD_DFU_XFER_SIZE) {
+ /* Too big */
+ dev->dfu_state = DFU_STATE_dfuERROR;
+ dev->dfu_status = DFU_STATUS_errADDRESS;
+ //udc_ep0_send_stall();
+ debug("Error: Transfer size > CONFIG_USBD_DFU_XFER_SIZE ");
+ return -EINVAL;
+ }
+
+ switch (dev->alternate) {
+ default:
+ /* this needs to be implemented */
+ break;
+ }
+
+ printf("returning len=%u\n", len);
+ return len;
+}
+
+static void handle_getstatus(struct usb_gadget *gadget, int max)
+{
+ struct dfu_dev *dev = &l_dfudev;//get_gadget_data(gadget);
+ struct usb_request *req = dev->req;
+ struct dfu_status *dstat = (struct dfu_status *) req->buf;
+
+ //if (!req->buf || req->length < sizeof(*dstat)) {
+ // debug("invalid ctrl! ");
+ // return;
+ //}
+
+ switch (dev->dfu_state) {
+ case DFU_STATE_dfuDNLOAD_SYNC:
+ //printf("DNLOAD_IDLE ");
+ //dev->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
+ //break;
+ 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 */
+ req->length = MIN(sizeof(*dstat), max);
+
+ debug("status : bStatus=0x%x bState=0x%x \n",dstat->bStatus, dstat->bState);
+
+ /* we don't need to explicitly send data here, will
+ * be done by the original caller! */
+}
+
+static void handle_getstate(struct usb_gadget *gadget, int max)
+{
+ struct dfu_dev *dev = &l_dfudev;//get_gadget_data(gadget);
+ struct usb_request *req = dev->req;
+
+ printf("getstate \n");
+
+ if (!req->buf || req->length < sizeof(u_int8_t)) {
+ debug("invalid ctrl! ");
+ return;
+ }
+
+ *(char *)(req->buf) = (char)dev->dfu_state & 0xff;
+ req->length = sizeof(u_int8_t);
+}
+
+/*
+ * change our operational config. must agree with the code
+ * that returns config descriptors, and altsetting code.
+ */
+static int dfu_set_config(struct dfu_dev *dev, unsigned number,
+ gfp_t gfp_flags)
+{
+ int result = 0;
+ struct usb_gadget *gadget = dev->gadget;
+ //struct usb_request *req = dev->req;
+
+ if (gadget_is_sa1100(gadget)
+ && dev->config
+ && dev->tx_qlen != 0) {
+ /* tx fifo is full, but we can't clear it...*/
+ error("can't change configurations");
+ return -ESPIPE;
+ }
+
+ if (rtm_dfu == 0)
+ {
+ printf("DFU set config : We should switch to DFU set !\n");
+ //dfu_reset_config(dev);
+
+ if (number > 0)
+ {
+ if (number <= DFU_NUM_ALTERNATES)
+ {
+ printf("DFU set config : %d\n", number);
+ result = set_dfu_config(dev, gfp_flags);
+ }
+ else result = -EINVAL;
+ }
+ }
+ char *speed;
+ unsigned power;
+
+ power = 2 * dfu_cfg_descriptor.ucfg.bMaxPower;
+ usb_gadget_vbus_draw(gadget, power);
+
+ switch (gadget->speed) {
+ case USB_SPEED_FULL:
+ speed = "full"; break;
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+ case USB_SPEED_HIGH:
+ speed = "high"; break;
+#endif
+ default:
+ speed = "?"; break;
+ }
+
+ dev->config = number;
+ printf("%s speed config #%d: %d mA\n",
+ speed, number, power);
+
+ return result;
+}
+
+static void dfu_setup_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ debug("dfu_setup_complete\n");
+
+ if (req->status || req->actual != req->length)
+ printf("setup complete --> %d, %d/%d\n",
+ req->status, req->actual, req->length);
+}
+
+static void dfu_status_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct dfu_dev *dev = &l_dfudev;
+ struct usb_gadget *gadget = dev->gadget;
+ const struct usb_ctrlrequest *ctrl = dev->ctrl;
+ u16 wValue = le16_to_cpu(ctrl->wValue);
+ u16 wLength = le16_to_cpu(ctrl->wLength);
+ char ret = RET_NOTHING;
+ int value = -EOPNOTSUPP;
+
+ debug("dfu_status_complete , status = %d\n",dev->dfu_state);
+#ifdef CONFIG_GREEN_LED
+ if(ledcount < 127) green_LED_on();
+ else green_LED_off();
+ ledcount -= 5;
+#endif
+ switch (dev->dfu_state) {
+ case DFU_STATE_dfuIDLE:
+ debug("handle first download\n");
+ dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+ ret = handle_dnload(gadget, wValue, wLength, 1);
+ value = req->length;
+ break;
+ case DFU_STATE_dfuDNLOAD_IDLE:
+ debug("handle DFU_STATE_dfuDNLOAD_IDLE\n");
+ dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+ ret = handle_dnload(gadget, wValue, wLength, 0);
+ value = req->length;
+ break;
+ case DFU_STATE_appIDLE: break;
+ case DFU_STATE_dfuDNLOAD_SYNC:
+ debug("handle sequential download\n");
+ dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+ ret = handle_dnload(gadget, wValue, wLength, 0);
+ value = req->length;
+ break;
+ case DFU_STATE_dfuDNBUSY: break;
+
+ case DFU_STATE_dfuMANIFEST_SYNC: break;
+ case DFU_STATE_dfuMANIFEST: break;
+ case DFU_STATE_dfuMANIFEST_WAIT_RST: break;
+ case DFU_STATE_dfuUPLOAD_IDLE: break;
+ case DFU_STATE_appDETACH: break;
+ case DFU_STATE_dfuERROR: break;
+ }
+}
+
+int dfu_ep0_handler(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+ struct dfu_dev *dev = &l_dfudev;//get_gadget_data(gadget);
+ struct usb_request *req = dev->req;
+ int rc, complete_status;
+ char ret = RET_NOTHING;
+ int value = -EOPNOTSUPP;
+ u8 requesttype = le16_to_cpu(ctrl->bRequestType);
+ u8 request = le16_to_cpu(ctrl->bRequest);
+ u16 wValue = le16_to_cpu(ctrl->wValue);
+ u16 wLength = le16_to_cpu(ctrl->wLength);
+ u16 wIndex = le16_to_cpu(ctrl->wIndex);
+
+ debug("dfu_ep0 (req=0x%x, bRequest=0x%x wVal=0x%x, wLen=%u) old_state = %u \n",
+ req, request, wValue, wLength, dev->dfu_state);
+
+ /* Handle reset of the device */
+ if ( (requesttype == 0x41) && (request == 0x08) )
+ {
+ printf("Device reset called \n");
+ do_reset (NULL, 0, 0, NULL);
+ }
+
+ dev->ctrl = ctrl;
+ complete_status = 0;
+ req->complete = dfu_setup_complete;
+ switch (request)
+ {
+
+ case USB_REQ_GET_DESCRIPTOR:
+ if (ctrl->bRequestType != USB_DIR_IN)
+ break;
+
+ switch (wValue >> 8) {
+
+ case USB_DT_DEVICE:
+ debug("USB_DT_DEVICE\n");
+ value = min(wLength, (u16) sizeof dfu_dev_descriptor);
+ memcpy(req->buf, &dfu_dev_descriptor, value);
+ break;
+ case USB_DT_DEVICE_QUALIFIER:
+ if (!gadget_is_dualspeed(gadget))
+ break;
+ debug("USB_DT_DEVICE_QUALIFIER not supported\n");
+ value = min(wLength, (u16) sizeof dev_qualifier);
+ memcpy(req->buf, &dev_qualifier, value);
+ break;
+
+ case USB_DT_OTHER_SPEED_CONFIG:
+ debug("USB_DT_OTHER_SPEED_CONFIG\n");
+ if (!gadget_is_dualspeed(gadget))
+ break;
+ /* FALLTHROUGH */
+ case USB_DT_CONFIG:
+ debug("USB_DT_CONFIG\n");
+ rtm_dfu = 1;
+ value = config_buf(gadget, req->buf,
+ wValue >> 8,
+ wValue & 0xff);
+ if (value >= 0)
+ value = min(wLength, (u16) value);
+ break;
+
+ case USB_DT_STRING:
+ debug("USB_DT_STRING\n");
+ value = usb_gadget_get_string(&stringtab,
+ wValue & 0xff, req->buf);
+
+ if (value >= 0)
+ value = min(wLength, (u16) value);
+ break;
+
+ case USB_DT_INTERFACE:
+ debug("USB_DT_INTERFACE is not needed \n");
+ break;
+ }
+ break;
+
+ case USB_REQ_SET_CONFIGURATION:
+ debug("USB_REQ_SET_CONFIGURATION %d\n", wValue);
+ if( wValue >= 1)
+ value = dfu_set_config(dev, wValue, GFP_ATOMIC);
+ if (req->status != -ECONNRESET ) dev->dfu_started = 1;
+ dev->dfu_state = DFU_STATE_dfuIDLE;
+ break;
+ case USB_REQ_GET_CONFIGURATION:
+ debug("USB_REQ_GET_CONFIGURATION %d\n", wValue);
+ if (ctrl->bRequestType != USB_DIR_IN)
+ break;
+ *(u8 *)req->buf = dev->config;
+ value = min(wLength, (u16) 1);
+ break;
+
+ case USB_REQ_SET_INTERFACE:
+ /*printf("USB_REQ_SET_INTERFACE %d , %d\n", wValue, wIndex);
+ if (ctrl->bRequestType != USB_RECIP_INTERFACE
+ || !dev->config
+ || wIndex > 1)
+ break;*/
+
+ dev->interface = le16_to_cpu (wIndex);
+ dev->alternate = le16_to_cpu (wValue);
+
+ debug("SET_INTERFACE(%u,%u) old_state = %u \n",
+ dev->interface, dev->alternate,
+ dev->dfu_state);
+ req->complete = dfu_setup_complete;
+ value = 0;
+ break;
+
+ case USB_REQ_GET_INTERFACE:
+ debug("USB_REQ_GET_INTERFACE\n");
+ if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)
+ || !dev->config
+ || wIndex > 1)
+ break;
+ if (wIndex != 0)
+ break;
+
+ /* for CDC, iff carrier is on, data interface is active. */
+ if (wIndex != 1)
+ *(u8 *)req->buf = 0;
+ else {
+ /* *(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0; */
+ /* carrier always ok ...*/
+ *(u8 *)req->buf = 1 ;
+ }
+ value = min(wLength, (u16) 1);
+ break;
+
+ default:
+ debug("DFU State = %d\n", dev->dfu_state);
+
+ switch (dev->dfu_state) {
+
+ case DFU_STATE_appIDLE:
+ switch (request) {
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(gadget, wLength);
+ value = req->length;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(gadget, wLength);
+ value = req->length;
+ break;
+ case USB_REQ_DFU_DETACH:
+ dev->dfu_state = DFU_STATE_appDETACH;
+ ret = RET_ZLP;
+ goto out;
+ break;
+ default:
+ ret = RET_STALL;
+ goto out;
+ }
+ break;
+ case DFU_STATE_appDETACH:
+ switch (request) {
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(gadget, wLength);
+ value = req->length;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(gadget, wLength);
+ value = req->length;
+ break;
+ default:
+ dev->dfu_state = DFU_STATE_appIDLE;
+ ret = RET_STALL;
+ goto out;
+ break;
+ }
+ /* FIXME: implement timer to return to appIDLE */
+ value = req->length;
+ break;
+ case DFU_STATE_dfuIDLE:
+ switch (request) {
+ case USB_REQ_DFU_DNLOAD:
+ if (wLength == 0) {
+ printf("first packet can not be zero length\n");
+ dev->dfu_state = DFU_STATE_dfuERROR;
+ ret = RET_STALL;
+ goto out;
+ }
+ //dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+ debug("idle DNL : length %x, wValue %x\n", wLength,wValue );
+ req->length = wLength;
+ complete_status =1;
+ //ret = handle_dnload(gadget, wValue, wLength, 1);
+ //value = req->length;
+ break;
+ case USB_REQ_DFU_UPLOAD:
+ dev->dfu_state = DFU_STATE_dfuUPLOAD_IDLE;
+ handle_upload(gadget, wValue, wLength, 1);
+ value = req->length;
+ break;
+ case USB_REQ_DFU_ABORT:
+ /* no zlp? */
+ ret = RET_ZLP;
+ goto out;
+ break;
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(gadget, wLength);
+ value = req->length;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(gadget, wLength);
+ value = req->length;
+ break;
+ case USB_REQ_DFU_DETACH:
+ /* Proprietary extension: 'detach' from idle mode and
+ * get back to runtime mode in case of USB Reset. As
+ * much as I dislike this, we just can't use every USB
+ * bus reset to switch back to runtime mode, since at
+ * least the Linux USB stack likes to send a number of resets
+ * in a row :( */
+ dev->dfu_state = DFU_STATE_dfuMANIFEST_WAIT_RST;
+ value = 0;
+ break;
+ default:
+ dev->dfu_state = DFU_STATE_dfuERROR;
+ ret = RET_STALL;
+ goto out;
+ break;
+ }
+ debug("DFU packet length = %d bRequest=0x%x\n",req->length, ctrl->bRequest);
+ value = req->length;
+ break;
+ case DFU_STATE_dfuDNLOAD_SYNC:
+ switch (request) {
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(gadget, wLength);
+ /* FIXME: state transition depending on block completeness */
+ value = req->length;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(gadget, wLength);
+ value = req->length;
+ break;
+ default:
+ dev->dfu_state = DFU_STATE_dfuERROR;
+ ret = RET_STALL;
+ goto out;
+ }
+ break;
+ case DFU_STATE_dfuDNBUSY:
+ switch (request) {
+ case USB_REQ_DFU_GETSTATUS:
+ /* FIXME: only accept getstatus if bwPollTimeout
+ * has elapsed */
+ handle_getstatus(gadget, wLength);
+ value = req->length;
+ break;
+ default:
+ dev->dfu_state = DFU_STATE_dfuERROR;
+ ret = RET_STALL;
+ goto out;
+ }
+ break;
+ case DFU_STATE_dfuDNLOAD_IDLE:
+ switch (request) {
+ case USB_REQ_DFU_DNLOAD:
+ if (wLength == 0) {
+ debug("We finished the download\n");
+ }
+ //dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+ debug("idle DNL 2: length %x, wValue %x , state = %x\n", wLength,wValue, dev->dfu_state );
+ req->length = wLength;
+ complete_status = 1;
+ break;
+ case USB_REQ_DFU_ABORT:
+ dev->dfu_state = DFU_STATE_dfuIDLE;
+ ret = RET_ZLP;
+ value = 0;
+ break;
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(gadget, wLength);
+ value = req->length;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(gadget, wLength);
+ value = req->length;
+ break;
+ default:
+ dev->dfu_state = DFU_STATE_dfuERROR;
+ ret = RET_STALL;
+ value = 0;
+ break;
+ }
+ debug("DFU packet length = %d bRequest=0x%x\n",req->length, ctrl->bRequest);
+ value = req->length;
+ break;
+ case DFU_STATE_dfuMANIFEST_SYNC:
+ switch (request) {
+ case USB_REQ_DFU_GETSTATUS:
+ /* We're MainfestationTolerant */
+ dev->dfu_state = DFU_STATE_dfuIDLE;
+ handle_getstatus(gadget, wLength);
+ value = req->length;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(gadget, wLength);
+ break;
+ default:
+ dev->dfu_state = DFU_STATE_dfuERROR;
+ ret = RET_STALL;
+ value = 0;
+ break;
+ }
+ break;
+ case DFU_STATE_dfuMANIFEST:
+ /* we should never go here */
+ dev->dfu_state = DFU_STATE_dfuERROR;
+ ret = RET_STALL;
+ break;
+ case DFU_STATE_dfuMANIFEST_WAIT_RST:
+ /* we should never go here */
+ break;
+ case DFU_STATE_dfuUPLOAD_IDLE:
+ switch (request) {
+ case USB_REQ_DFU_UPLOAD:
+ /* state transition if less data then requested */
+ rc = handle_upload(gadget, wValue, wLength, 0);
+ if (rc >= 0 && rc < wLength)
+ dev->dfu_state = DFU_STATE_dfuIDLE;
+ value = req->length;
+ break;
+ case USB_REQ_DFU_ABORT:
+ dev->dfu_state = DFU_STATE_dfuIDLE;
+ /* no zlp? */
+ ret = RET_ZLP;
+ value = 0;
+ break;
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(gadget, wLength);
+ value = req->length;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(gadget, wLength);
+ value = req->length;
+ break;
+ default:
+ dev->dfu_state = DFU_STATE_dfuERROR;
+ ret = RET_STALL;
+ value = 0;
+ break;
+ }
+ break;
+ case DFU_STATE_dfuERROR:
+ switch (request) {
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(gadget, wLength);
+ value = req->length;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(gadget, wLength);
+ value = req->length;
+ break;
+ case USB_REQ_DFU_CLRSTATUS:
+ debug("Clear DFU status\n");
+ dev->dfu_state = DFU_STATE_dfuIDLE;
+ dev->dfu_status = DFU_STATUS_OK;
+ /* no zlp? */
+ ret = RET_ZLP;
+ value = 0;
+ break;
+ default:
+ dev->dfu_state = DFU_STATE_dfuERROR;
+ ret = RET_STALL;
+ value = 0;
+ break;
+ }
+ break;
+ default:
+ printf("unknown control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ wValue, wIndex, wLength);
+ return DFU_EP0_UNHANDLED;
+ break;
+ }
+
+
+ }
+ /* respond with data transfer before status phase? */
+ if (value >= 0) {
+ debug("respond with data transfer before status phase\n");
+ req->length = value;
+ req->zero = value < wLength
+ && (value % gadget->ep0->maxpacket) == 0;
+ value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+ debug("ep_queue %d req%02x.%02x v%04x i%04x l%d\n",
+ req->length, ctrl->bRequestType, ctrl->bRequest,
+ wValue, wIndex, wLength);
+ if (value < 0) {
+ debug("ep_queue --> %d\n", value);
+ req->status = 0;
+ dfu_setup_complete(gadget->ep0, req);
+ }
+ }
+
+ if(complete_status) req->complete = dfu_status_complete;
+
+ return DFU_EP0_DATA;
+
+out:
+ debug("new_state = %u, ret = %u\n", dev->dfu_state, ret);
+
+ /*FIXME the following code needs a review */
+ switch (ret) {
+ case RET_ZLP:
+ debug("ZERO LENGTH packet\n");
+ req->length = 0;
+ req->zero = value < wLength
+ && (value % gadget->ep0->maxpacket) == 0;
+ value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+ //usb_gadget_handle_interrupts();
+ req->complete = dfu_status_complete;
+ return DFU_EP0_ZLP;
+ break;
+ case RET_STALL:
+ req->complete = dfu_status_complete;
+ return DFU_EP0_STALL;
+ break;
+ case RET_NOTHING:
+ break;
+ }
+ return DFU_EP0_DATA;
+}
+
+/*
+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;
+}
+*/
+#ifdef CONFIG_NAND_DYNPART
+
+void dfu_update_strings(void)
+{
+ int i;
+
+ if (!system_dfu_state) {
+ printf("NASTY SURPRISE: system_dfu_state not set\n");
+ return;
+ }
+
+ for (i = 1; i != DFU_NUM_ALTERNATES; i++) {
+ struct part_info *part = get_partition_nand(i-1);
+ struct usb_string_descriptor *strdesc, **slot;
+
+ if (part)
+ strdesc = create_usbstring(part->name);
+ else
+ strdesc = create_usbstring("undefined partition");
+ if (!strdesc)
+ continue;
+ slot = usb_strings+STR_COUNT+i+1;
+ if (*slot)
+ free(*slot);
+ *slot = strdesc;
+ }
+}
+
+#endif /* CONFIG_NAND_DYNPART */
+
+
+static void dfu_unbind(struct usb_gadget *gadget)
+{
+ struct dfu_dev *dev = get_gadget_data(gadget);
+
+ debug("%s...\n", __func__);
+
+ /* we've already been disconnected ... no i/o is active */
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+
+ dev->gadget = NULL;
+ set_gadget_data(gadget, NULL);
+}
+
+static int dfu_bind(struct usb_gadget *gadget)
+{
+ struct dfu_dev *dev = &l_dfudev;
+ u8 zlp = 1; //cdc = 1, ;
+ int gcnum;
+ //u8 tmp[7];
+ debug("DFU bind\n");
+ /*
+ * Because most host side USB stacks handle CDC Ethernet, that
+ * standard protocol is _strongly_ preferred for interop purposes.
+ * (By everyone except Microsoft.)
+ */
+ if (gadget_is_musbhdrc(gadget)) {
+ /* reduce tx dma overhead by avoiding special cases */
+ zlp = 0;
+ } else if (gadget_is_sa1100(gadget)) {
+ /* hardware can't write zlps */
+ zlp = 0;
+ }
+
+ gcnum = usb_gadget_controller_number(gadget);
+ if (gcnum >= 0)
+ dfu_dev_descriptor.bcdDevice = cpu_to_le16(0x0300 + gcnum);
+ else {
+ /*
+ * can't assume CDC works. don't want to default to
+ * anything less functional on CDC-capable hardware,
+ * so we fail in this case.
+ */
+ printf("ERROR : controller not recognised\n");
+ error("controller '%s' not recognized",
+ gadget->name);
+ return -ENODEV;
+ }
+
+#if defined(CONFIG_USBD_VENDORID) && defined(CONFIG_USBD_PRODUCTID_DFU)
+ dfu_dev_descriptor.idVendor = cpu_to_le16(CONFIG_USBD_VENDORID);
+ dfu_dev_descriptor.idProduct = cpu_to_le16(CONFIG_USBD_PRODUCTID_DFU);
+#endif
+
+ if (bcdDevice)
+ dfu_dev_descriptor.bcdDevice = cpu_to_le16(bcdDevice);
+ if (iManufacturer)
+ strlcpy(manufacturer, iManufacturer, sizeof manufacturer);
+ if (iProduct)
+ strlcpy(product_desc, iProduct, sizeof product_desc);
+ if (iSerialNumber) {
+ dfu_dev_descriptor.iSerialNumber = DFU_STR_SERIAL,
+ strlcpy(serial_number, iSerialNumber, sizeof serial_number);
+ }
+
+ /* all we really need is Ep0 */
+
+ usb_ep_autoconfig_reset(gadget);
+
+ dfu_dev_descriptor.bMaxPacketSize0 = gadget->ep0->maxpacket;
+ usb_gadget_set_selfpowered(gadget);
+
+ if (gadget_is_dualspeed(gadget)) {
+
+ //dev_qualifier.bDeviceClass = USB_CLASS_VENDOR_SPEC;
+
+ /* assumes ep0 uses the same value for both speeds ... */
+ //dev_qualifier.bMaxPacketSize0 = dfu_dev_descriptor.bMaxPacketSize0;
+ }
+
+ /* preallocate control message data and buffer */
+ dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+ if (!dev->req)
+ goto fail;
+ dev->req->buf = control_req;
+ dev->req->complete = dfu_setup_complete;
+
+ /* finish hookup to lower layer ... */
+ dev->gadget = gadget;
+ set_gadget_data(gadget, dev);
+ gadget->ep0->driver_data = dev;
+
+ return 0;
+
+fail:
+ error("%s failed", __func__);
+ dfu_unbind(gadget);
+ return -ENOMEM;
+}
+
+int usb_dfu_init(void)
+{
+ struct dfu_dev *dev = &l_dfudev;
+ struct usb_gadget *gadget;
+ unsigned long ts;
+ unsigned long timeout = USB_CONNECT_TIMEOUT;
+ int ret;
+ int i;
+
+ /* set up interface descriptors fro each partition */
+ /* this must be done better */
+ for (i = 0; i < DFU_NUM_ALTERNATES; i++) {
+ struct usb_interface_descriptor *uif =
+ dfu_cfg_descriptor.uif+i;
+
+ uif->bLength = USB_DT_INTERFACE_SIZE;
+ uif->bDescriptorType = USB_DT_INTERFACE;
+ uif->bAlternateSetting = i;
+ uif->bInterfaceClass = 0xfe;
+ uif->bInterfaceSubClass = 1;
+ uif->bInterfaceProtocol = 2;
+ uif->iInterface = DFU_STR_ALT(i);
+ }
+
+ dev->dfu_started = 0;
+ dev->dfu_state = DFU_STATE_appIDLE;
+ dev->dfu_status = DFU_STATUS_OK;
+
+ if (system_dfu_state)
+ printf("SURPRISE: system_dfu_state is already set\n");
+ system_dfu_state = &dev->dfu_state;
+
+ ret = usb_gadget_register_driver(&dfu_driver);
+ if (ret < 0)
+ {
+ if (ret == -EBUSY); /*driver already registered */
+ else
+ {
+ error("USB gadget driver failed to register\n");
+ goto fail;
+ }
+ }
+
+ gadget = dev->gadget;
+ usb_gadget_connect(gadget);
+
+ if (getenv("dfu_connect_timeout"))
+ timeout = simple_strtoul(getenv("dfu_connect_timeout"),
+ NULL, 10) * CONFIG_SYS_HZ;
+ ts = get_timer(0);
+ debug("trying to connect\n");
+ while (!dev->dfu_started) {
+ /* Handle control-c and timeouts */
+ if (ctrlc() || (get_timer(ts) > timeout)) {
+ error("The remote end did not respond in time.");
+ goto fail;
+ }
+ usb_gadget_handle_interrupts();
+ }
+#ifdef CONFIG_GREEN_LED
+ green_LED_on();
+ ledcount = 100;
+#endif
+#ifdef CONFIG_RED_LED
+ red_LED_on();
+#endif
+
+ return 0;
+fail:
+ debug("usb_dfu_init failed\n");
+ system_dfu_state = NULL;
+#ifdef CONFIG_GREEN_LED
+ green_LED_off();
+ ledcount = 100;
+#endif
+#ifdef CONFIG_RED_LED
+ red_LED_off();
+#endif
+ return -1;
+}
+
+/*void usb_dfu_halt(struct dfu_device *dfudev)
+{
+ struct dfu_dev *dev = &l_dfudev;
+
+ debug("usb_dfu_halt\n");
+
+ if (!dfudev) {
+ error("received NULL ptr");
+ return;
+ }
+
+ // If the gadget not registered, simple return
+ if (!dev->gadget)
+ return;
+
+ usb_gadget_disconnect(dev->gadget);
+ usb_gadget_unregister_driver(&dfu_driver);
+}
+*/
+static void dfu_suspend(struct usb_gadget *gadget)
+{
+ /* Not used */
+}
+
+static void dfu_resume(struct usb_gadget *gadget)
+{
+ /* Not used */
+}
+
+/* we need to bind/unbind and have ep0 */
+static struct usb_gadget_driver dfu_driver = {
+ .speed = DEVSPEED,
+
+ .bind = dfu_bind,
+ .unbind = dfu_unbind,
+
+ .setup = dfu_ep0_handler,
+ //.disconnect = dfu_disconnect,
+
+ .suspend = dfu_suspend,
+ .resume = dfu_resume,
+};
+
+U_BOOT_CMD(
+ dfuinit, 1, 0, usb_dfu_init,
+ "DFU initialization",
+ "No params, see README.dfu"
+);
+
+
+#endif /* CONFIG_USBD_DFU */
--
1.7.3.4
More information about the U-Boot
mailing list