From: Marcel <korg...@home.nl> Signed-off-by: Marcel <korg...@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 <r...@semihalf.com> + * Bartlomiej Sieka <t...@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.jans...@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@0x00200000 root +mtdparts add nand0 0xEE00000@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 <lafo...@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 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot