[U-Boot] [PATCH v2 0/4] DFU initial implementation

Dear All,
This is a quick-and-dirty proof-of-concept for splitting the dfu into core dfu gadget, generic parts of dfu backend, dfu command and target-specific implementation for Goni.
This patch definitely needs some coding style cleanups.
Also, the place for storing generic parts of dfu was chosen somewhat arbitrary and can be changed if a better alternative is found.
The original idea of the flash_entity was to equip it with a void * to a context structure, which is not interpreted by the dfu gadget but passed to the dfu backend and understood there. I am still hesitating if flash_entity_ctx is generic enough to be generalized, as of now I assumed it is. If it wasn't then less common code could be extracted.
@Stefan: you need the first 3 patches from this series, and then you need to implement your own target-specific parts. In fact, all is expected from the target, is to provide board_dfu_init/cleanup pair and call register_flash_entities.
Andrzej Pietrasiewicz (4): dfu: protocol initial implementation dfu: generic backend parts dfu: command implementation dfu: target-specific implementation for Goni
Makefile | 1 + board/samsung/goni/Makefile | 2 + board/samsung/goni/flash.c | 341 ++++++++++++++++ board/samsung/goni/flash.h | 28 ++ board/samsung/goni/goni.c | 17 + common/Makefile | 1 + common/cmd_dfu.c | 51 +++ drivers/usb/dfu/Makefile | 33 ++ drivers/usb/dfu/dfu.c | 109 +++++ drivers/usb/dfu/fat.c | 77 ++++ drivers/usb/dfu/mmc.c | 124 ++++++ drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/dfu.c | 920 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/dfu.h | 171 ++++++++ include/configs/s5p_goni.h | 4 + include/dfu.h | 28 ++ include/dfu_backend.h | 71 ++++ include/flash_entity.h | 39 ++ include/mbr.h | 49 +++ 19 files changed, 2067 insertions(+), 0 deletions(-) create mode 100644 board/samsung/goni/flash.c create mode 100644 board/samsung/goni/flash.h create mode 100644 common/cmd_dfu.c create mode 100644 drivers/usb/dfu/Makefile create mode 100644 drivers/usb/dfu/dfu.c create mode 100644 drivers/usb/dfu/fat.c create mode 100644 drivers/usb/dfu/mmc.c create mode 100644 drivers/usb/gadget/dfu.c create mode 100644 drivers/usb/gadget/dfu.h create mode 100644 include/dfu.h create mode 100644 include/dfu_backend.h create mode 100644 include/flash_entity.h create mode 100644 include/mbr.h

Signed-off-by: Andrzej Pietrasiewicz andrzej.p@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/dfu.c | 920 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/dfu.h | 171 ++++++++ include/dfu.h | 28 ++ include/flash_entity.h | 39 ++ 5 files changed, 1159 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/gadget/dfu.c create mode 100644 drivers/usb/gadget/dfu.h create mode 100644 include/dfu.h create mode 100644 include/flash_entity.h
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index cd22bbe..4b173e2 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -28,6 +28,7 @@ LIB := $(obj)libusb_gadget.o # new USB gadget layer dependencies ifdef CONFIG_USB_GADGET COBJS-y += epautoconf.o config.o usbstring.o +COBJS-$(CONFIG_DFU_GADGET) += dfu.o COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o endif ifdef CONFIG_USB_ETHER diff --git a/drivers/usb/gadget/dfu.c b/drivers/usb/gadget/dfu.c new file mode 100644 index 0000000..535e194 --- /dev/null +++ b/drivers/usb/gadget/dfu.c @@ -0,0 +1,920 @@ +/* + * dfu.c -- Device Firmware Update gadget + * + * Copyright (C) 2011 Samsung Electronics + * author: Andrzej Pietrasiewicz andrzej.p@samsung.com + * + * Based on gadget zero: + * Copyright (C) 2003-2007 David Brownell + * 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 + */ + +#define VERBOSE_DEBUG +#define DEBUG + +/* +#include <linux/kernel.h> +#include <linux/utsname.h> +#include <linux/device.h> +*/ + +#include <common.h> +#include <asm-generic/errno.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include <flash_entity.h> + +#include "gadget_chips.h" +/* #include "epautoconf.c" */ +/* #include "config.c" */ +/* #include "usbstring.c" */ + +#include <malloc.h> +#include "dfu.h" + +static struct flash_entity *flash_ents; +static int num_flash_ents; + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = __constant_cpu_to_le16(0x0100), + .bDeviceClass = USB_CLASS_VENDOR_SPEC, + .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM), + .iManufacturer = STRING_MANUFACTURER, + .iProduct = STRING_PRODUCT, + .iSerialNumber = STRING_SERIAL, + .bNumConfigurations = 1, +}; + +static struct usb_config_descriptor dfu_config = { + .bLength = sizeof dfu_config, + .bDescriptorType = USB_DT_CONFIG, + /* compute wTotalLength on the fly */ + .bNumInterfaces = 1, + .bConfigurationValue = DFU_CONFIG_VAL, + .iConfiguration = STRING_DFU_NAME, + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 1, /* self-powered */ +}; + +static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + .bmAttributes = USB_OTG_SRP, +}; + +static const struct dfu_function_descriptor dfu_func = { + .bLength = sizeof dfu_func, + .bDescriptorType = DFU_DT_FUNC, + .bmAttributes = DFU_BIT_WILL_DETACH | + DFU_BIT_MANIFESTATION_TOLERANT | + DFU_BIT_CAN_UPLOAD | + DFU_BIT_CAN_DNLOAD, + .wDetachTimeOut = 0, + .wTransferSize = USB_BUFSIZ, + .bcdDFUVersion = __constant_cpu_to_le16(0x0110), +}; + +static const struct usb_interface_descriptor dfu_intf_runtime = { + .bLength = sizeof dfu_intf_runtime, + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_APP_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 1, + .iInterface = STRING_DFU_NAME, +}; + +static const struct usb_descriptor_header *dfu_function_runtime[] = { + (struct usb_descriptor_header *) &otg_descriptor, + (struct usb_descriptor_header *) &dfu_func, + (struct usb_descriptor_header *) &dfu_intf_runtime, + NULL, +}; + +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_VENDOR_SPEC, + .bNumConfigurations = 1, +}; + +static char manufacturer[50]; +static const char longname[] = "DFU Gadget"; +/* default serial number takes at least two packets */ +static const char serial[] = "0123456789.0123456789.012345678"; +static const char dfu_name[] = "Device Firmware Upgrade"; +static const char shortname[] = "dfu"; + +/* static strings, in UTF-8 */ +static struct usb_string strings_runtime[] = { + { STRING_MANUFACTURER, manufacturer, }, + { STRING_PRODUCT, longname, }, + { STRING_SERIAL, serial, }, + { STRING_DFU_NAME, dfu_name, }, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_runtime = { + .language = 0x0409, /* en-us */ + .strings = strings_runtime, +}; + +static struct usb_gadget_strings stringtab_dfu = { + .language = 0x0409, /* en-us */ +}; + +static bool is_runtime(struct dfu_dev *dev) +{ + return dev->dfu_state == DFU_STATE_appIDLE || + dev->dfu_state == DFU_STATE_appDETACH; +} + +static int config_buf(struct usb_gadget *gadget, + u8 *buf, u8 type, unsigned index) +{ + int hs = 0; + struct dfu_dev *dev = get_gadget_data(gadget); + const struct usb_descriptor_header **function; + int len; + + if (index > 0) + return -EINVAL; + + if (gadget_is_dualspeed(gadget)) { + hs = (gadget->speed == USB_SPEED_HIGH); + if (type == USB_DT_OTHER_SPEED_CONFIG) + hs = !hs; + } + if (is_runtime(dev)) + function = dfu_function_runtime; + else + function = (const struct usb_descriptor_header **)dev->function; + + /* for now, don't advertise srp-only devices */ + if (!gadget_is_otg(gadget)) + function++; + + len = usb_gadget_config_buf(&dfu_config, + buf, USB_BUFSIZ, function); + if (len < 0) + return len; + ((struct usb_config_descriptor *) buf)->bDescriptorType = type; + return len; +} + +static void dfu_reset_config(struct dfu_dev *dev) +{ + if (dev->config == 0) + return; + + DBG(dev, "reset config\n"); + + dev->config = 0; +} + +static int dfu_set_config(struct dfu_dev *dev, unsigned number) +{ + int result = 0; + struct usb_gadget *gadget = dev->gadget; + char *speed; + + if (number == dev->config) + return 0; + + dfu_reset_config(dev); + + if (DFU_CONFIG_VAL != number) { + result = -EINVAL; + return result; + } + + switch (gadget->speed) { + case USB_SPEED_LOW: + speed = "low"; + break; + case USB_SPEED_FULL: + speed = "full"; + break; + case USB_SPEED_HIGH: + speed = "high"; + break; + default: + speed = "?"; + break; + } + + dev->config = number; + INFO(dev, "%s speed config #%d: %s\n", speed, number, dfu_name); + return result; +} + +/*-------------------------------------------------------------------------*/ + +static void empty_complete(struct usb_ep *ep, struct usb_request *req) +{ + /* intentionally empty */ +} + +static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct dfu_dev *dev = req->context; + struct flash_entity *fe = &flash_ents[dev->altsetting]; + + if (dev->not_prepared) { + printf("DOWNLOAD %s\n", fe->name); + fe->prepare(fe->ctx, FLASH_WRITE); + dev->not_prepared = false; + } + + if (req->length > 0) + fe->write_block(fe->ctx, req->length, req->buf); + else { + fe->finish(fe->ctx, FLASH_WRITE); + dev->not_prepared = true; + } +} + +static void handle_getstatus(struct usb_request *req) +{ + struct dfu_status *dstat = (struct dfu_status *)req->buf; + struct dfu_dev *dev = req->context; + + switch (dev->dfu_state) { + case DFU_STATE_dfuDNLOAD_SYNC: + case DFU_STATE_dfuDNBUSY: + dev->dfu_state = DFU_STATE_dfuDNLOAD_IDLE; + break; + case DFU_STATE_dfuMANIFEST_SYNC: + break; + default: + break; + } + + /* send status response */ + dstat->bStatus = dev->dfu_status; + /* FIXME: set dstat->bwPollTimeout */ + dstat->bState = dev->dfu_state; + dstat->iString = 0; +} + +static void handle_getstate(struct usb_request *req) +{ + struct dfu_dev *dev = req->context; + + ((u8 *)req->buf)[0] = dev->dfu_state & 0xff; + req->actual = sizeof(u8); +} + +static int handle_upload(struct usb_request *req, u16 len) +{ + struct dfu_dev *dev = req->context; + struct flash_entity *fe = &flash_ents[dev->altsetting]; + int n; + + if (dev->not_prepared) { + printf("UPLOAD %s\n", fe->name); + fe->prepare(fe->ctx, FLASH_READ); + dev->not_prepared = false; + } + n = fe->read_block(fe->ctx, len, req->buf); + + /* no more data to read from this entity */ + if (n < len) { + fe->finish(fe->ctx, FLASH_READ); + dev->not_prepared = true; + } + + return n; +} + +static int handle_dnload(struct usb_gadget *gadget, u16 len) +{ + struct dfu_dev *dev = get_gadget_data(gadget); + struct usb_request *req = dev->req; + + if (len == 0) + dev->dfu_state = DFU_STATE_dfuMANIFEST_SYNC; + + req->complete = dnload_request_complete; + + return len; +} + +static int +dfu_handle(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +{ + struct dfu_dev *dev = get_gadget_data(gadget); + struct usb_request *req = dev->req; + u16 len = le16_to_cpu(ctrl->wLength); + int rc = 0; + + switch (dev->dfu_state) { + case DFU_STATE_appIDLE: + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + return RET_STAT_LEN; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + case USB_REQ_DFU_DETACH: + dev->dfu_state = DFU_STATE_appDETACH; + dev->dfu_state = DFU_STATE_dfuIDLE; + return RET_ZLP; + default: + return RET_STALL; + } + break; + case DFU_STATE_appDETACH: + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + return RET_STAT_LEN; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + dev->dfu_state = DFU_STATE_appIDLE; + return RET_STALL; + } + /* FIXME: implement timer to return to appIDLE */ + break; + case DFU_STATE_dfuIDLE: + switch (ctrl->bRequest) { + case USB_REQ_DFU_DNLOAD: + if (len == 0) { + dev->dfu_state = DFU_STATE_dfuERROR; + return RET_STALL; + } + dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; + return handle_dnload(gadget, len); + case USB_REQ_DFU_UPLOAD: + dev->dfu_state = DFU_STATE_dfuUPLOAD_IDLE; + return handle_upload(req, len); + case USB_REQ_DFU_ABORT: + /* no zlp? */ + return RET_ZLP; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + return RET_STAT_LEN; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + 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; + dev->dfu_state = DFU_STATE_appIDLE; + break; + default: + dev->dfu_state = DFU_STATE_dfuERROR; + return RET_STALL; + } + break; + case DFU_STATE_dfuDNLOAD_SYNC: + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + return RET_STAT_LEN; + /* FIXME: state transition depending + * on block completeness */ + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + dev->dfu_state = DFU_STATE_dfuERROR; + return RET_STALL; + } + break; + case DFU_STATE_dfuDNBUSY: + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + /* FIXME: only accept getstatus if bwPollTimeout + * has elapsed */ + handle_getstatus(req); + return RET_STAT_LEN; + default: + dev->dfu_state = DFU_STATE_dfuERROR; + return RET_STALL; + } + break; + case DFU_STATE_dfuDNLOAD_IDLE: + switch (ctrl->bRequest) { + case USB_REQ_DFU_DNLOAD: + dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; + return handle_dnload(gadget, len); + case USB_REQ_DFU_ABORT: + dev->dfu_state = DFU_STATE_dfuIDLE; + return RET_ZLP; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + return RET_STAT_LEN; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + dev->dfu_state = DFU_STATE_dfuERROR; + return RET_STALL; + } + break; + case DFU_STATE_dfuMANIFEST_SYNC: + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + /* We're MainfestationTolerant */ + dev->dfu_state = DFU_STATE_dfuIDLE; + handle_getstatus(req); + return RET_STAT_LEN; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + dev->dfu_state = DFU_STATE_dfuERROR; + return RET_STALL; + } + break; + case DFU_STATE_dfuMANIFEST: + /* we should never go here */ + dev->dfu_state = DFU_STATE_dfuERROR; + return RET_STALL; + case DFU_STATE_dfuMANIFEST_WAIT_RST: + /* we should never go here */ + break; + case DFU_STATE_dfuUPLOAD_IDLE: + switch (ctrl->bRequest) { + case USB_REQ_DFU_UPLOAD: + /* state transition if less data then requested */ + rc = handle_upload(req, len); + if (rc >= 0 && rc < len) + dev->dfu_state = DFU_STATE_dfuIDLE; + return rc; + case USB_REQ_DFU_ABORT: + dev->dfu_state = DFU_STATE_dfuIDLE; + /* no zlp? */ + return RET_ZLP; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + return RET_STAT_LEN; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + dev->dfu_state = DFU_STATE_dfuERROR; + return RET_STALL; + } + break; + case DFU_STATE_dfuERROR: + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + return RET_STAT_LEN; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + case USB_REQ_DFU_CLRSTATUS: + dev->dfu_state = DFU_STATE_dfuIDLE; + dev->dfu_status = DFU_STATUS_OK; + /* no zlp? */ + return RET_ZLP; + default: + dev->dfu_state = DFU_STATE_dfuERROR; + return RET_STALL; + } + break; + default: + return -1; + } + + return 0; +} + +static int +dfu_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +{ + struct dfu_dev *dev = get_gadget_data(gadget); + struct usb_request *req = dev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + req->zero = 0; + req->complete = empty_complete; + if (!(ctrl->bRequestType & USB_TYPE_CLASS)) + switch (ctrl->bRequest) { + + case USB_REQ_GET_DESCRIPTOR: + if (ctrl->bRequestType != USB_DIR_IN) + goto unknown; + switch (w_value >> 8) { + + case USB_DT_DEVICE: + value = min(w_length, (u16) sizeof device_desc); + memcpy(req->buf, &device_desc, value); + break; + case USB_DT_DEVICE_QUALIFIER: + if (!gadget_is_dualspeed(gadget)) + break; + value = min(w_length, + (u16) sizeof dev_qualifier); + memcpy(req->buf, &dev_qualifier, value); + break; + + case USB_DT_OTHER_SPEED_CONFIG: + if (!gadget_is_dualspeed(gadget)) + break; + /* FALLTHROUGH */ + case USB_DT_CONFIG: + value = config_buf(gadget, req->buf, + w_value >> 8, + w_value & 0xff); + if (value >= 0) + value = min_t(w_length, (u16) value); + break; + + case USB_DT_STRING: + /* wIndex == language code. */ + value = usb_gadget_get_string( + is_runtime(dev) ? &stringtab_runtime : + &stringtab_dfu, + w_value & 0xff, req->buf); + if (value >= 0) + value = min_t(w_length, (u16) value); + break; + case DFU_DT_FUNC: + value = min(w_length, (u16) sizeof dfu_func); + memcpy(req->buf, &dfu_func, value); + break; + } + break; + + case USB_REQ_SET_CONFIGURATION: + if (ctrl->bRequestType != 0) + goto unknown; + if (gadget->a_hnp_support) + DBG(dev, "HNP available\n"); + else if (gadget->a_alt_hnp_support) + DBG(dev, "HNP needs a different root port\n"); + else + VDBG(dev, "HNP inactive\n"); + spin_lock(&dev->lock); + value = dfu_set_config(dev, w_value); + spin_unlock(&dev->lock); + break; + case USB_REQ_GET_CONFIGURATION: + if (ctrl->bRequestType != USB_DIR_IN) + goto unknown; + *(u8 *)req->buf = dev->config; + value = min(w_length, (u16) 1); + break; + + /* until we add altsetting support, or other interfaces, + * only 0/0 are possible. pxa2xx only supports 0/0 (poorly) + * and already killed pending endpoint I/O. + */ + case USB_REQ_SET_INTERFACE: + if (ctrl->bRequestType != USB_RECIP_INTERFACE) + goto unknown; + spin_lock(&dev->lock); + if (dev->config && w_index == 0) { + u8 config = dev->config; + + /* resets interface configuration, forgets about + * previous transaction state (queued bufs, etc) + * and re-inits endpoint state (toggle etc) + * no response queued, just zero + * status == success. + * if we had more than one interface we couldn't + * use this "reset the config" shortcut. + */ + dfu_reset_config(dev); + dfu_set_config(dev, config); + dev->altsetting = w_value; + value = 0; + } + spin_unlock(&dev->lock); + break; + case USB_REQ_GET_INTERFACE: + if (ctrl->bRequestType != + (USB_DIR_IN|USB_RECIP_INTERFACE)) + goto unknown; + if (!dev->config) + break; + if (w_index != 0) { + value = -EDOM; + break; + } + *(u8 *)req->buf = 0; + value = min(w_length, (u16) 1); + break; + + default: +unknown: + VDBG(dev, + "unknown control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + } + else + value = dfu_handle(gadget, ctrl); + + /* respond with data transfer before status phase? */ + if (value >= 0) { + req->length = value; + req->zero = value < w_length; + value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); + if (value < 0) { + DBG(dev, "ep_queue --> %d\n", value); + req->status = 0; + } + } + + /* device either stalls (value < 0) or reports success */ + return value; +} + +/*-------------------------------------------------------------------------*/ + +static void dfu_disconnect(struct usb_gadget *gadget) +{ + struct dfu_dev *dev = get_gadget_data(gadget); + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + dfu_reset_config(dev); + spin_unlock_irqrestore(&dev->lock, flags); +} + +static int dfu_prepare_function(struct dfu_dev *dev, int n) +{ + struct usb_interface_descriptor *d; + int i = 0; + + dev->function = kzalloc((ALTSETTING_BASE + n + 1) * + sizeof(struct usb_descriptor_header *), + GFP_KERNEL); + if (!dev->function) + goto enomem; + + dev->function[0] = (struct usb_descriptor_header *)&otg_descriptor; + dev->function[1] = (struct usb_descriptor_header *)&dfu_func; + + for (i = 0; i < n; ++i) { + d = kzalloc(sizeof(*d), GFP_KERNEL); + if (!d) + goto enomem; + + d->bLength = sizeof(*d); + d->bDescriptorType = USB_DT_INTERFACE; + d->bAlternateSetting = i; + d->bNumEndpoints = 0; + d->bInterfaceClass = USB_CLASS_APP_SPEC; + d->bInterfaceSubClass = 1; + d->bInterfaceProtocol = 2; + d->iInterface = DFU_STR_BASE + i; + + dev->function[ALTSETTING_BASE + i] = + (struct usb_descriptor_header *)d; + } + dev->function[ALTSETTING_BASE + i] = NULL; + + return 1; + +enomem: + while (i) { + kfree(dev->function[--i + ALTSETTING_BASE]); + dev->function[i + ALTSETTING_BASE] = NULL; + } + kfree(dev->function); + + return 0; +} + +static int +dfu_prepare_strings(struct dfu_dev *dev, struct flash_entity *f, int n) +{ + int i = 0; + + dev->strings = kzalloc((STRING_ALTSETTING_BASE + n + 1) * + sizeof(struct usb_string), + GFP_KERNEL); + if (!dev->strings) + goto enomem; + + dev->strings[0].id = STRING_MANUFACTURER; + dev->strings[0].s = manufacturer; + dev->strings[1].id = STRING_PRODUCT; + dev->strings[1].s = longname; + dev->strings[2].id = STRING_SERIAL; + dev->strings[2].s = serial; + dev->strings[3].id = STRING_DFU_NAME; + dev->strings[3].s = dfu_name; + + for (i = 0; i < n; ++i) { + char *s; + + dev->strings[STRING_ALTSETTING_BASE + i].id = DFU_STR_BASE + i; + s = kzalloc(strlen(f[i].name) + 1, GFP_KERNEL); + if (!s) + goto enomem; + + strcpy(s, f[i].name); + dev->strings[STRING_ALTSETTING_BASE + i].s = s; + } + dev->strings[STRING_ALTSETTING_BASE + i].id = 0; + dev->strings[STRING_ALTSETTING_BASE + i].s = NULL; + + return 1; + +enomem: + while (i) { + kfree((void *)dev->strings[--i + STRING_ALTSETTING_BASE].s); + dev->strings[i + STRING_ALTSETTING_BASE].s = NULL; + } + kfree(dev->strings); + + return 0; +} + +static void dfu_unbind(struct usb_gadget *gadget) +{ + struct dfu_dev *dev = get_gadget_data(gadget); + int i; + + DBG(dev, "unbind\n"); + + if (dev->strings) { + i = num_flash_ents; + while (i) { + kfree((void *) + dev->strings[--i + STRING_ALTSETTING_BASE].s); + dev->strings[i + STRING_ALTSETTING_BASE].s = NULL; + } + kfree(dev->strings); + } + if (dev->function) { + i = num_flash_ents; + while (i) { + kfree(dev->function[--i + ALTSETTING_BASE]); + dev->function[i + ALTSETTING_BASE] = NULL; + } + kfree(dev->function); + } + /* we've already been disconnected ... no i/o is active */ + if (dev->req) { + dev->req->length = USB_BUFSIZ; + kfree(dev->req->buf); + usb_ep_free_request(gadget->ep0, dev->req); + } + kfree(dev); + set_gadget_data(gadget, NULL); +} + +static int __init dfu_bind(struct usb_gadget *gadget) +{ + struct dfu_dev *dev; + int gcnum; + + usb_ep_autoconfig_reset(gadget); + + gcnum = usb_gadget_controller_number(gadget); + if (gcnum >= 0) + device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); + else { + pr_warning("%s: controller '%s' not recognized\n", + shortname, gadget->name); + device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + spin_lock_init(&dev->lock); + dev->gadget = gadget; + set_gadget_data(gadget, dev); + + dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); + if (!dev->req) + goto enomem; + dev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL); + if (!dev->req->buf) + goto enomem; + + dev->req->complete = empty_complete; + + device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; + + if (gadget_is_dualspeed(gadget)) + dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0; + + if (gadget_is_otg(gadget)) { + otg_descriptor.bmAttributes |= USB_OTG_HNP, + dfu_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + usb_gadget_set_selfpowered(gadget); + + dev->dfu_state = DFU_STATE_appIDLE; + dev->dfu_status = DFU_STATUS_OK; + dev->not_prepared = true; + + if (!dfu_prepare_function(dev, num_flash_ents)) + goto enomem; + + if (!dfu_prepare_strings(dev, flash_ents, num_flash_ents)) + goto enomem; + stringtab_dfu.strings = dev->strings; + + gadget->ep0->driver_data = dev; + dev->req->context = dev; + + INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname); + + /* snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", + init_utsname()->sysname, init_utsname()->release, + gadget->name); */ + + return 0; + +enomem: + dfu_unbind(gadget); + return -ENOMEM; +} + +static void dfu_suspend(struct usb_gadget *gadget) +{ + if (gadget->speed == USB_SPEED_UNKNOWN) + return; + + DBG(dev, "suspend\n"); +} + +static void dfu_resume(struct usb_gadget *gadget) +{ + DBG(dev, "resume\n"); +} + +static struct usb_gadget_driver dfu_driver = { +#ifdef CONFIG_USB_GADGET_DUALSPEED + .speed = USB_SPEED_HIGH, +#else + .speed = USB_SPEED_FULL, +#endif + /*.function = (char *) longname,*/ + .bind = dfu_bind, + .unbind = __exit_p(dfu_unbind), + + .setup = dfu_setup, + .disconnect = dfu_disconnect, + + .suspend = dfu_suspend, + .resume = dfu_resume, + + /* + .driver = { + .name = (char *) shortname, + .owner = THIS_MODULE, + },*/ +}; + +void register_flash_entities(struct flash_entity *flents, int n) +{ + flash_ents = flents; + num_flash_ents = n; +} + +int __init dfu_init(void) +{ + return usb_gadget_register_driver(&dfu_driver); +} +module_init(dfu_init); + +void __exit dfu_cleanup(void) +{ + usb_gadget_unregister_driver(&dfu_driver); +} +module_exit(cleanup); + diff --git a/drivers/usb/gadget/dfu.h b/drivers/usb/gadget/dfu.h new file mode 100644 index 0000000..5559273 --- /dev/null +++ b/drivers/usb/gadget/dfu.h @@ -0,0 +1,171 @@ +/* + * dfu.h -- Device Firmware Update gadget + * + * Copyright (C) 2011 Samsung Electronics + * author: Andrzej Pietrasiewicz andrzej.p@samsung.com + * + * 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 + */ + +#ifndef DFU_H_ +#define DFU_H_ + +#include <linux/compiler.h> + +/* + * Linux kernel compatibility layer + */ +#define GFP_ATOMIC ((gfp_t) 0) +#define GFP_KERNEL ((gfp_t) 0) +#define true 1 +#define false 0 +#define dev_dbg(...) do {} while (0) +#define dev_vdbg(...) do {} while (0) +#define dev_err(...) do {} while (0) +#define dev_warn(...) do {} while (0) +#define dev_info(...) do {} while (0) +#define pr_warning(...) do {} while (0) +#define spin_lock_init(lock) do {} while (0) +#define spin_lock(lock) do {} while (0) +#define spin_unlock(lock) do {} while (0) +#define spin_lock_irqsave(lock,flags) do {flags = 1;} while (0) +#define spin_unlock_irqrestore(lock,flags) do {flags = 0;} while (0) +#define kmalloc(x,y) malloc(x) +#define kfree(x) free(x) +#define kzalloc(size,flags) calloc((size), 1) +#define __init +#define __exit +#define __exit_p(x) x +#define module_init(...) +#define module_exit(...) +#define min_t min +#define spinlock_t int +#define bool int +/* + * end compatibility layer + */ + +#define DBG(d, fmt, args...) \ + dev_dbg(&(d)->gadget->dev , fmt , ## args) +#define VDBG(d, fmt, args...) \ + dev_vdbg(&(d)->gadget->dev , fmt , ## args) +#define ERROR(d, fmt, args...) \ + dev_err(&(d)->gadget->dev , fmt , ## args) +#define WARN(d, fmt, args...) \ + dev_warn(&(d)->gadget->dev , fmt , ## args) +#define INFO(d, fmt, args...) \ + dev_info(&(d)->gadget->dev , fmt , ## args) + +#define DRIVER_VERSION "Msciwoj" + +/* Thanks to NetChip Technologies for donating this product ID. */ +#define DRIVER_VENDOR_NUM 0x0525 /* NetChip */ +#define DRIVER_PRODUCT_NUM 0xffff /* DFU */ + +#define STRING_MANUFACTURER 0 +#define STRING_PRODUCT 1 +#define STRING_SERIAL 2 +#define STRING_DFU_NAME 49 +#define DFU_STR_BASE 50 + +#define DFU_CONFIG_VAL 1 +#define DFU_DT_FUNC 0x21 + +#define DFU_BIT_WILL_DETACH (0x1 << 3) +#define DFU_BIT_MANIFESTATION_TOLERANT (0x1 << 2) +#define DFU_BIT_CAN_UPLOAD (0x1 << 1) +#define DFU_BIT_CAN_DNLOAD 0x1 + +/* big enough to hold our biggest descriptor */ +#define USB_BUFSIZ 4096 + +#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 + +#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 + +#define RET_STALL -1 +#define RET_ZLP 0 +#define RET_STAT_LEN 6 + +#define ALTSETTING_BASE 2 +#define STRING_ALTSETTING_BASE 4 + +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, +}; + +struct dfu_status { + __u8 bStatus; + __u8 bwPollTimeout[3]; + __u8 bState; + __u8 iString; +} __packed; + +struct dfu_function_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bmAttributes; + __le16 wDetachTimeOut; + __le16 wTransferSize; + __le16 bcdDFUVersion; +} __packed; + +struct dfu_dev { + spinlock_t lock; + struct usb_gadget *gadget; + struct usb_request *req; /* for control responses */ + + /* when configured, we have one config */ + u8 config; + u8 altsetting; + enum dfu_state dfu_state; + unsigned int dfu_status; + struct usb_descriptor_header **function; + struct usb_string *strings; + bool not_prepared; +}; + +#endif /* DFU_H_ */ diff --git a/include/dfu.h b/include/dfu.h new file mode 100644 index 0000000..21b89f2 --- /dev/null +++ b/include/dfu.h @@ -0,0 +1,28 @@ +/* + * dfu.h - Device Firmware Upgrade + * + * copyright (c) 2011 samsung electronics + * author: andrzej pietrasiewicz andrzej.p@samsung.com + * + * 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 + */ + +#ifndef __DFU_H__ +#define __DFU_H__ + +extern int dfu_init(void); +extern int dfu_cleanup(void); + +#endif diff --git a/include/flash_entity.h b/include/flash_entity.h new file mode 100644 index 0000000..daa90ee --- /dev/null +++ b/include/flash_entity.h @@ -0,0 +1,39 @@ +/* + * flash_entity.h - flashable area description + * + * Copyright (C) 2011 Samsung Electronics + * author: Andrzej Pietrasiewicz andrzej.p@samsung.com + * + * 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 + */ + +#ifndef FLASH_ENTITY_H_ +#define FLASH_ENTITY_H_ + +#define FLASH_READ 0 +#define FLASH_WRITE 1 + +struct flash_entity { + char *name; + void *ctx; + int (*prepare)(void *ctx, u8 mode); + int (*read_block)(void *ctx, unsigned int n, void *buf); + int (*write_block)(void *ctx, unsigned int n, void *buf); + int (*finish)(void *ctx, u8 mode); +}; + +void register_flash_entities(struct flash_entity *flents, int n); + +#endif /* FLASH_ENTITY_H_ */

Signed-off-by: Andrzej Pietrasiewicz andrzej.p@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- Makefile | 1 + drivers/usb/dfu/Makefile | 33 ++++++++++++ drivers/usb/dfu/dfu.c | 109 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/dfu/fat.c | 77 ++++++++++++++++++++++++++++ drivers/usb/dfu/mmc.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++ include/dfu_backend.h | 71 ++++++++++++++++++++++++++ include/mbr.h | 49 ++++++++++++++++++ 7 files changed, 464 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/dfu/Makefile create mode 100644 drivers/usb/dfu/dfu.c create mode 100644 drivers/usb/dfu/fat.c create mode 100644 drivers/usb/dfu/mmc.c create mode 100644 include/dfu_backend.h create mode 100644 include/mbr.h
diff --git a/Makefile b/Makefile index bd72286..83ea3d0 100644 --- a/Makefile +++ b/Makefile @@ -283,6 +283,7 @@ LIBS += drivers/usb/gadget/libusb_gadget.o LIBS += drivers/usb/host/libusb_host.o LIBS += drivers/usb/musb/libusb_musb.o LIBS += drivers/usb/phy/libusb_phy.o +LIBS += drivers/usb/dfu/libusb_dfu.o LIBS += drivers/video/libvideo.o LIBS += drivers/watchdog/libwatchdog.o LIBS += common/libcommon.o diff --git a/drivers/usb/dfu/Makefile b/drivers/usb/dfu/Makefile new file mode 100644 index 0000000..c2fe049 --- /dev/null +++ b/drivers/usb/dfu/Makefile @@ -0,0 +1,33 @@ +include $(TOPDIR)/config.mk + +LIB := $(obj)libusb_dfu.o + +# new USB gadget layer dependencies +COBJS-y += dfu.o + +ifdef CONFIG_MMC +COBJS-y += mmc.o +endif + +ifdef CONFIG_CMD_FAT +COBJS-y += fat.o +endif + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### + diff --git a/drivers/usb/dfu/dfu.c b/drivers/usb/dfu/dfu.c new file mode 100644 index 0000000..2f5492d --- /dev/null +++ b/drivers/usb/dfu/dfu.c @@ -0,0 +1,109 @@ +#include <common.h> +#include <dfu_backend.h> +#include <flash_entity.h> + +/* + * Adapt transport layer buffer size to storage chunk size + * + * return < n to indicate no more data to read + */ +int read_block(void *ctx, unsigned int n, void *buf) +{ + struct flash_entity_ctx *ct = ctx; + unsigned int nread = 0; + + if (n == 0) + return n; + + while (nread < n) { + unsigned int copy; + + if (ct->num_done >= ct->length) + break; + if (ct->buffered == 0) { + ct->read(ct->buf, ct->buf_len, + ct->offset + ct->num_done); + ct->buffered = ct->buf_len; + } + copy = min(n - nread, ct->buffered); + + memcpy(buf + nread, ct->buf + ct->buf_len - ct->buffered, copy); + nread += copy; + ct->buffered -= copy; + ct->num_done += copy; + } + + return nread; +} + +/* + * Adapt transport layer buffer size to storage chunk size + */ +int write_block(void *ctx, unsigned int n, void *buf) +{ + struct flash_entity_ctx *ct = ctx; + unsigned int nwritten = 0; + + if (n == 0) + return n; + + while (nwritten < n) { + unsigned int copy; + + if (ct->num_done >= ct->length) + break; + if (ct->buffered >= ct->buf_len) { + ct->write(ct->buf, ct->buf_len, + ct->offset + ct->num_done); + ct->buffered = 0; + ct->num_done += ct->buf_len; + if (ct->num_done >= ct->length) + break; + } + copy = min(n - nwritten, ct->buf_len - ct->buffered); + + memcpy(ct->buf + ct->buffered, buf + nwritten, copy); + nwritten += copy; + ct->buffered += copy; + } + + return nwritten; +} + +/* + * Entity-specific prepare and finish + */ +static void reset_ctx(struct flash_entity_ctx *ctx) +{ + ctx->buffered = 0; + ctx->num_done = 0; +} + +int generic_prepare(void *ctx, u8 mode) +{ + struct flash_entity_ctx *ct = ctx; + + reset_ctx(ct); + memset(ct->buf, 0, ct->buf_len); + if (mode == FLASH_WRITE) { + if (ct->erase) { + printf("Erase entity: %s ", ct->this_entity->name); + ct->erase(ct->length, ct->offset); + } + printf("Write entity: %s ", ct->this_entity->name); + } else if (mode == FLASH_READ) { + printf("Read entity: %s ", ct->this_entity->name); + } + return 0; +} + +int generic_finish(void *ctx, u8 mode) +{ + struct flash_entity_ctx *ct = ctx; + + if (mode == FLASH_WRITE && ct->buffered > 0) + ct->write(ct->buf, ct->buffered, ct->offset + ct->num_done); + + return 0; +} + diff --git a/drivers/usb/dfu/fat.c b/drivers/usb/dfu/fat.c new file mode 100644 index 0000000..6f0125f --- /dev/null +++ b/drivers/usb/dfu/fat.c @@ -0,0 +1,77 @@ +#include <common.h> +#include <fat.h> +#include <dfu_backend.h> + +static const char *fat_filename; +static int fat_part_num; +static block_dev_desc_t *fat_dev; + +inline int set_fat_part_num(int pnum) +{ + fat_part_num = pnum; + + return pnum; +} + +inline const char *set_fat_filename(const char *fn) +{ + fat_filename = fn; + + return fn; +} + +inline block_dev_desc_t *set_fat_dev(block_dev_desc_t *d) +{ + fat_dev = d; + + return d; +} + +int read_fat(void *buf, unsigned int len, unsigned long from) +{ + int ret; + + ret = fat_register_device(fat_dev, fat_part_num); + if (ret < 0) { + printf("error : fat_register_device\n"); + return 0; + } + printf("read up to %d B ", MMC_FAT_BLOCK_SZ); + return file_fat_read(fat_filename, buf, len); +} + +int write_fat(void *buf, unsigned int len, unsigned long from) +{ +#ifdef CONFIG_FAT_WRITE + int ret; + + ret = fat_register_device(fat_dev, fat_part_num); + if (ret < 0) { + printf("error : fat_register_divce\n"); + return 0; + } + + printf("write up to %d B ", MMC_FAT_BLOCK_SZ); + ret = file_fat_write(fat_filename, buf, len); + + /* format and write again */ + if (ret == 1) { + printf("formatting\n"); + if (mkfs_vfat(fat_dev, fat_part_num)) { + printf("error formatting device\n"); + return 0; + } + ret = file_fat_write(fat_filename, buf, len); + } + + if (ret < 0) { + printf("error : writing %s\n", fat_filename); + return 0; + } +#else + printf("error : FAT write not supported\n"); + return 0; +#endif + return len; +} + diff --git a/drivers/usb/dfu/mmc.c b/drivers/usb/dfu/mmc.c new file mode 100644 index 0000000..221d6ff --- /dev/null +++ b/drivers/usb/dfu/mmc.c @@ -0,0 +1,124 @@ +#include <common.h> +#include <mbr.h> +#include <mmc.h> +#include <dfu_backend.h> +#include <flash_entity.h> + +#define SIGNATURE ((unsigned short) 0xAA55) +#define PARAM_LEN 12 + +static int read_ebr(struct mmc *mmc, struct mbr_partition *mp, + int ebr_next, struct mbr_part_data *pd, int parts, + int *extended_lba, int mmc_mbr_dev) +{ + struct mbr *ebr; + struct mbr_partition *p; + char buf[512]; + int ret, i; + int lba = 0; + + if (ebr_next) + lba = *extended_lba; + + lba += mp->lba; + ret = mmc->block_dev.block_read(mmc_mbr_dev, lba, 1, buf); + if (ret != 1) + return 0; + + ebr = (struct mbr *) buf; + + if (ebr->signature != SIGNATURE) { + printf("Signature error 0x%x\n", ebr->signature); + return 0; + } + + for (i = 0; i < 2; i++) { + p = (struct mbr_partition *) &ebr->parts[i]; + + if (i == 0) { + lba += p->lba; + if (p->partition_type == 0x83) { + if (pd) { + pd[parts].offset = lba; + pd[parts].length = p->nsectors; + pd[parts].primary = 0; + } + parts++; + } + } + } + + if (p->lba && p->partition_type == 0x5) + parts = read_ebr(mmc, p, 1, pd, parts, extended_lba, mmc_mbr_dev); + + return parts; +} + +int read_mbr(struct mmc *mmc, struct mbr_part_data *pd, int *extended_lba, + int mmc_mbr_dev) +{ + struct mbr_partition *mp; + struct mbr *mbr; + char buf[512]; + int ret, i; + int parts = 0; + + ret = mmc->block_dev.block_read(mmc_mbr_dev, 0, 1, buf); + if (ret != 1) + return 0; + + mbr = (struct mbr *) buf; + + if (mbr->signature != SIGNATURE) { + printf("Signature error 0x%x\n", mbr->signature); + return 0; + } + + for (i = 0; i < 4; i++) { + mp = (struct mbr_partition *) &mbr->parts[i]; + + if (!mp->partition_type) + continue; + + if (mp->partition_type == 0x83) { + if (pd) { + pd[parts].offset = mp->lba; + pd[parts].length = mp->nsectors; + pd[parts].primary = 1; + } + parts++; + } + + if (mp->lba && mp->partition_type == 0x5) { + *extended_lba = mp->lba; + parts = read_ebr(mmc, mp, 0, pd, parts, extended_lba, mmc_mbr_dev); + } + } + + return parts; +} + +static int rw_mmc(void *buf, char *op, unsigned int len, unsigned long from) +{ + char ram_addr[PARAM_LEN]; + char offset[PARAM_LEN]; + char length[PARAM_LEN]; + char *argv[] = {"mmc", op, ram_addr, offset, length}; + + sprintf(ram_addr, "0x%lx", (unsigned long)buf); + sprintf(offset, "0x%lx", from / MMC_SECTOR_SZ); /* guaranteed integer */ + sprintf(length, "0x%x", (len + MMC_SECTOR_SZ - 1) / MMC_SECTOR_SZ); + + return do_mmcops(NULL, 0, 6, argv); +} + +inline int read_mmc(void *buf, unsigned int len, unsigned long from) +{ + return rw_mmc(buf, "read", len, from); +} + +inline int write_mmc(void *buf, unsigned int len, unsigned long from) +{ + return rw_mmc(buf, "write", len, from); +} + diff --git a/include/dfu_backend.h b/include/dfu_backend.h new file mode 100644 index 0000000..8c608d1 --- /dev/null +++ b/include/dfu_backend.h @@ -0,0 +1,71 @@ +/* + * dfu.h - Device Firmware Upgrade + * + * copyright (c) 2011 samsung electronics + * author: andrzej pietrasiewicz andrzej.p@samsung.com + * + * 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 + */ + +#ifndef __DFU_BACKEND_H__ +#define __DFU_BACKEND_H__ + +struct mbr_part_data { + unsigned long offset; /* #sectors from mmc begin */ + unsigned long length; /* #sectors in this partition*/ + u8 primary; /* != 0 if primary, 0 if extended */ +}; + +typedef int (*rw_op)(void *buf, unsigned int len, unsigned long from); +typedef int (*erase_op)(unsigned int len, unsigned long from); + +struct flash_entity_ctx { + unsigned long offset; /* offset into the device */ + unsigned long length; /* size of the entity */ + u8 *buf; /* buffer for one chunk */ + unsigned long buf_len; /* one chunk length */ + unsigned int buffered; /* # available bytes in buf */ + unsigned int num_done; /* total bytes handled */ + rw_op read; /* chunk read op for this medium */ + rw_op write; /* chunk write op for this medium */ + erase_op erase; /* erase op for this medium or NULL */ + struct flash_entity *this_entity; /* the containing entity */ + void *associated; /* related entity, if any */ +}; + +extern void board_dfu_init(void); +extern int board_dfu_cleanup(void); +extern int usb_gadget_handle_interrupts(void); + +extern int read_mmc(void *buf, unsigned int len, unsigned long from); +extern int write_mmc(void *buf, unsigned int len, unsigned long from); + +extern block_dev_desc_t *set_fat_dev(block_dev_desc_t *d); +extern int set_fat_part_num(int pnum); +extern const char *set_fat_filename(const char *fn); +extern int read_fat(void *buf, unsigned int len, unsigned long from); +extern int write_fat(void *buf, unsigned int len, unsigned long from); + +extern int read_mbr(struct mmc *mmc, struct mbr_part_data *pd, int *extended_lba, int mmc_mbr_dev); + +extern int read_block(void *ctx, unsigned int n, void *buf); +extern int write_block(void *ctx, unsigned int n, void *buf); +extern int generic_prepare(void *ctx, u8 mode); +extern int generic_finish(void *ctx, u8 mode); + +#define MMC_SECTOR_SZ 512 +#define MMC_FAT_BLOCK_SZ (4 * 1024 * 1024) + +#endif diff --git a/include/mbr.h b/include/mbr.h new file mode 100644 index 0000000..9cbeb2e --- /dev/null +++ b/include/mbr.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 Samsung Electrnoics + * + * 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/compiler.h> + +#define MBR_RESERVED_SIZE 0x8000 +#define MBR_START_OFS_BLK (0x500000 / 512) + +struct mbr_partition { + char status; + char f_chs[3]; + char partition_type; + char l_chs[3]; + int lba; + int nsectors; +} __packed; + +struct mbr { + char code_area[440]; + char disk_signature[4]; + char nulls[2]; + struct mbr_partition parts[4]; + unsigned short signature; +}; + +extern unsigned int mbr_offset[16]; +extern unsigned int mbr_parts; + +void set_mbr_dev(int dev); +void set_mbr_info(char *ramaddr, unsigned int len, unsigned int reserved); +void set_mbr_table(unsigned int start_addr, int parts, + unsigned int *blocks, unsigned int *part_offset); +int get_mbr_table(unsigned int *part_offset);

On Wednesday 09 November 2011 04:52:21 Andrzej Pietrasiewicz wrote:
--- /dev/null +++ b/drivers/usb/dfu/Makefile
+ifdef CONFIG_MMC +COBJS-y += mmc.o +endif
+ifdef CONFIG_CMD_FAT +COBJS-y += fat.o +endif
COBJS-$(CONFIG_XXX) += foo.o
--- /dev/null +++ b/drivers/usb/dfu/fat.c @@ -0,0 +1,77 @@ +#include <common.h> +#include <fat.h> +#include <dfu_backend.h>
missing intro comment block
+inline int set_fat_part_num(int pnum)
there seems to be a lot of funcs in this file and others that have misplaced "inline" markings
--- /dev/null +++ b/include/dfu_backend.h
+extern void board_dfu_init(void); +extern int board_dfu_cleanup(void); +extern int usb_gadget_handle_interrupts(void);
+extern int read_mmc(void *buf, unsigned int len, unsigned long from); +extern int write_mmc(void *buf, unsigned int len, unsigned long from);
+extern block_dev_desc_t *set_fat_dev(block_dev_desc_t *d); +extern int set_fat_part_num(int pnum); +extern const char *set_fat_filename(const char *fn); +extern int read_fat(void *buf, unsigned int len, unsigned long from); +extern int write_fat(void *buf, unsigned int len, unsigned long from);
+extern int read_mbr(struct mmc *mmc, struct mbr_part_data *pd, int *extended_lba, int mmc_mbr_dev); + +extern int read_block(void *ctx, unsigned int n, void *buf); +extern int write_block(void *ctx, unsigned int n, void *buf); +extern int generic_prepare(void *ctx, u8 mode); +extern int generic_finish(void *ctx, u8 mode);
API needs to be namespaced here with like "dfu_" prefixes
--- /dev/null +++ b/include/mbr.h
file needs protection against multiple inclusion
+void set_mbr_dev(int dev); +void set_mbr_info(char *ramaddr, unsigned int len, unsigned int reserved); +void set_mbr_table(unsigned int start_addr, int parts,
unsigned int *blocks, unsigned int *part_offset);
+int get_mbr_table(unsigned int *part_offset);
seems like you declare funcs here that aren't actually defined ... -mike

Hello Mike,
On Sunday, January 08, 2012 6:14 AM Mike Frysinger wrote:
-----Original Message----- From: Mike Frysinger [mailto:vapier@gentoo.org] Sent: Sunday, January 08, 2012 6:14 AM To: u-boot@lists.denx.de Cc: Andrzej Pietrasiewicz; Kyungmin Park; Marek Szyprowski Subject: Re: [U-Boot] [PATCH 2/4] dfu: generic backend parts
<...>
COBJS-$(CONFIG_XXX) += foo.o
<...>
missing intro comment block
<...>
there seems to be a lot of funcs in this file and others that have misplaced "inline" markings
<...>
API needs to be namespaced here with like "dfu_" prefixes
<...>
file needs protection against multiple inclusion
<...>
seems like you declare funcs here that aren't actually defined ... -mike
Thank you for your review. As far as I remember this patch series was a "quick-and-dirty" attempt at splitting the implementation into parts so that Stefan Shmidt could continue his work with his own dfu backends.
However, Wolfgang Denk was not convinced about introducing dfu support to u-boot the way it was proposed. Are there any new circumstances?
Andrzej

On Monday 09 January 2012 02:50:00 Andrzej Pietrasiewicz wrote:
However, Wolfgang Denk was not convinced about introducing dfu support to u-boot the way it was proposed. Are there any new circumstances?
i just had the thread marked unread, so i read it & reviewed for some high level details. that's all. -mike

Dear Andrzej Pietrasiewicz,
In message 000601cccea3$4a41f750$dec5e5f0$%p@samsung.com you wrote:
Thank you for your review. As far as I remember this patch series was a "quick-and-dirty" attempt at splitting the implementation into parts so that Stefan Shmidt could continue his work with his own dfu backends.
Is there any such work goig on? I haven't seen any updates / fixes for Mike's review comments.
However, Wolfgang Denk was not convinced about introducing dfu support to u-boot the way it was proposed. Are there any new circumstances?
Indeed I would have liked to see a more general approach, and I still feel it should be possible. But if nobody is picking up this idea, it is probably better to support the feature with the current implemen- tation than not at all.
The review comments need to be addressed, though.
Best regards,
Wolfgang Denk

Hello.
On Mon, 2012-01-09 at 08:50, Andrzej Pietrasiewicz wrote:
Thank you for your review. As far as I remember this patch series was a "quick-and-dirty" attempt at splitting the implementation into parts so that Stefan Shmidt could continue his work with his own dfu backends.
Time run out for me on this project and I'm going to relocate to another country for new job this months. In short I want have time to work on this the next months.
Personally I'm still interested in this and may start working on it when everything else settled down. Can not tell when this is though. If anyone else wants to pick this up and push it forward that would be more then welcome. A small patchset with my current state will follow this mail.
Some words about it. As I don't have hardware which is working with U-boot mainline and provides a UDC driver for the new gadget framework I could not directly test your code. Having a DFU implementation for both the old and the new gadget framework turned out to be more trouble then expected. I split the nand backend part out, but it still needs some fixing and especially testing.
For systems with old gadget framework UDC drivers, like the beagleboard with musb, it might still be wise to use the original version I posted earlier here for reference.
Lets hope someone, or maybe myself in some months, will pick this up and finally bring it into mainline.
regards Stefan Schmidt

--- drivers/usb/gadget/dfu.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/gadget/dfu.c b/drivers/usb/gadget/dfu.c index 535e194..7aed344 100644 --- a/drivers/usb/gadget/dfu.c +++ b/drivers/usb/gadget/dfu.c @@ -47,6 +47,10 @@ #include <malloc.h> #include "dfu.h"
+#ifndef POLL_TIMEOUT_MILLISECONDS +#define POLL_TIMEOUT_MILLISECONDS 5 +#endif + static struct flash_entity *flash_ents; static int num_flash_ents;
@@ -271,9 +275,14 @@ static void handle_getstatus(struct usb_request *req)
/* send status response */ dstat->bStatus = dev->dfu_status; - /* FIXME: set dstat->bwPollTimeout */ dstat->bState = dev->dfu_state; dstat->iString = 0; + /* FIXME: Use real values from flash subsystem here instead a hardcoded + * value */ + dstat->bwPollTimeout[0] = POLL_TIMEOUT_MILLISECONDS & 0xff; + dstat->bwPollTimeout[1] = (POLL_TIMEOUT_MILLISECONDS >> 8) & 0xff; + dstat->bwPollTimeout[2] = (POLL_TIMEOUT_MILLISECONDS >> 16) & 0xff; + //req->actual = MIN(sizeof(*dstat), max); }
static void handle_getstate(struct usb_request *req)

A vendor independent standard from the USB forum to upgrade firmware on devices over USB. U-Boot is seen as the device to be upgraded here and exposes the functionality over the USB gadget framework.
Signed-off-by: Stefan Schmidt stefan@datenfreihafen.org --- drivers/serial/usbtty.c | 22 +- drivers/serial/usbtty.h | 6 + drivers/usb/dfu/nand.c | 386 +++++++++++++++++++++++++++++ drivers/usb/gadget/Makefile | 4 +- drivers/usb/gadget/core.c | 19 ++ drivers/usb/gadget/dfu.c | 57 ++++- drivers/usb/gadget/dfu_old.c | 521 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/ep0.c | 50 +++- include/configs/omap3_beagle.h | 3 + include/dfu_backend.h | 16 ++ include/usb_dfu.h | 97 ++++++++ include/usb_dfu_descriptors.h | 94 ++++++++ include/usbdevice.h | 13 + 13 files changed, 1274 insertions(+), 14 deletions(-) create mode 100644 drivers/usb/dfu/nand.c create mode 100644 drivers/usb/gadget/dfu_old.c create mode 100644 include/usb_dfu.h create mode 100644 include/usb_dfu_descriptors.h
diff --git a/drivers/serial/usbtty.c b/drivers/serial/usbtty.c index cffd5a2..b13b497 100644 --- a/drivers/serial/usbtty.c +++ b/drivers/serial/usbtty.c @@ -22,6 +22,8 @@ */
#include <common.h> +#include <usb_dfu_descriptors.h> +#include <usb_dfu.h> #include <config.h> #include <circbuf.h> #include <stdio_dev.h> @@ -101,7 +103,7 @@ extern struct usb_string_descriptor **usb_strings; static unsigned short rx_endpoint = 0; static unsigned short tx_endpoint = 0; static unsigned short interface_count = 0; -static struct usb_string_descriptor *usbtty_string_table[STR_COUNT]; +static struct usb_string_descriptor *usbtty_string_table[NUM_STRINGS];
/* USB Descriptor Strings */ static u8 wstrLang[4] = {4,USB_DT_STRING,0x9,0x4}; @@ -151,6 +153,10 @@ struct acm_config_desc { /* Slave Interface */ struct usb_interface_descriptor data_class_interface; struct usb_endpoint_descriptor data_endpoints[NUM_ENDPOINTS-1]; +#ifdef CONFIG_USBD_DFU + struct usb_interface_descriptor uif_dfu; + struct usb_dfu_func_descriptor func_dfu; +#endif } __attribute__((packed));
static struct acm_config_desc acm_configuration_descriptors[NUM_CONFIGS] = { @@ -161,7 +167,11 @@ static struct acm_config_desc acm_configuration_descriptors[NUM_CONFIGS] = { .bDescriptorType = USB_DT_CONFIG, .wTotalLength = cpu_to_le16(sizeof(struct acm_config_desc)), +#ifdef CONFIG_USBD_DFU + .bNumInterfaces = NUM_ACM_INTERFACES + 1, +#else .bNumInterfaces = NUM_ACM_INTERFACES, +#endif .bConfigurationValue = 1, .iConfiguration = STR_CONFIG, .bmAttributes = @@ -260,6 +270,11 @@ static struct acm_config_desc acm_configuration_descriptors[NUM_CONFIGS] = { .bInterval = 0xFF, }, }, +#ifdef CONFIG_USBD_DFU + /* Interface 3 */ + .uif_dfu = DFU_RT_IF_DESC, + .func_dfu = DFU_FUNC_DESC, +#endif }, };
@@ -370,7 +385,7 @@ static int fill_buffer (circbuf_t * buf); void usbtty_poll (void);
/* utility function for converting char* to wide string used by USB */ -static void str2wide (char *str, u16 * wide) +void str2wide (char *str, u16 * wide) { int i; for (i = 0; i < strlen (str) && str[i]; i++){ @@ -639,6 +654,9 @@ static void usbtty_init_instances (void) device_instance->bus = bus_instance; device_instance->configurations = NUM_CONFIGS; device_instance->configuration_instance_array = config_instance; +#ifdef CONFIG_USBD_DFU + dfu_init_instance(device_instance); +#endif
/* initialize bus instance */ memset (bus_instance, 0, sizeof (struct usb_bus_instance)); diff --git a/drivers/serial/usbtty.h b/drivers/serial/usbtty.h index 14961c1..16ae86e 100644 --- a/drivers/serial/usbtty.h +++ b/drivers/serial/usbtty.h @@ -84,4 +84,10 @@ #define STR_CTRL_INTERFACE 0x06 #define STR_COUNT 0x07
+#ifdef CONFIG_USBD_DFU +#define NUM_STRINGS DFU_STR_COUNT +#else +#define NUM_STRINGS STR_COUNT +#endif + #endif diff --git a/drivers/usb/dfu/nand.c b/drivers/usb/dfu/nand.c new file mode 100644 index 0000000..619ba0f --- /dev/null +++ b/drivers/usb/dfu/nand.c @@ -0,0 +1,386 @@ +#include <common.h> +#include <nand.h> +#include <jffs2/load_kernel.h> +#include <dfu_backend.h> +#include <flash_entity.h> +int mtdparts_init(void); +extern struct list_head devices; + +}; + +static struct flash_entity_ctx ctx; + +/* Return the 'net size' of the partition (i.e. excluding any bad blocks) */ +unsigned int nand_net_part_size(struct part_info *part) +{ + struct mtd_info *mtd; + unsigned int offs; + unsigned int bb_delta = 0; + + if (!part || !part->dev || !part->dev->id || + part->dev->id->num >= CONFIG_SYS_MAX_NAND_DEVICE) + return 0; + + mtd = &nand_info[part->dev->id->num]; + + for (offs = part->offset; offs < part->offset + part->size; + offs += mtd->erasesize) { + if (nand_isbad_bbt(mtd, offs, 0)) + bb_delta += mtd->erasesize; + } + + return part->size - bb_delta; +} + +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; +} + +static int initialize_ds_nand(struct usb_device_instance *dev, + struct flash_entity_ctx *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->erase_opts, 0, sizeof(ds->erase_opts)); + ds->erase_opts.quiet = 1; + /* FIXME: do this more dynamic */ + if ((!strcmp(ds->part->name, "rootfs")) || + (!strcmp(ds->part->name, "fs"))) + ds->erase_opts.jffs2 = 1; + + /* FIXME: How to set these options without 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 flash_entity_ctx *ds, + unsigned long erasesize, size_t size) +{ + struct usb_device_instance *dev = urb->device; + int rc; + + debug("erase_flash_verify_nand(urb=%p, ds=%p, erase=0x%lx 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", + (unsigned int)ds->erase_opts.length, + (unsigned int)ds->erase_opts.offset, + ds->erase_opts.jffs2); + rc = nand_erase_opts(ds->nand, &ds->erase_opts); + if (rc) { + debug("Error erasing\n"); + dev->dfu_state = DFU_STATE_dfuERROR; + dev->dfu_status = DFU_STATUS_errERASE; + return RET_STALL; + } + + debug("Writing 0x%x bytes @ offset 0x%x\n", size, ds->off); + /* FIXME handle oob */ + rc = nand_write_skip_bad(ds->nand, ds->off, &size, ds->buf, 0); + 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 flash_entity_ctx *ds) +{ + struct usb_device_instance *dev = urb->device; + int rc; + + ds->erase_opts.offset = ds->off; + ds->erase_opts.length = ds->part->size - (ds->off - ds->part->offset); + debug("Erasing tail of 0x%x bytes @ offset 0x%x (jffs=%u)\n", + (unsigned int)ds->erase_opts.length, + (unsigned int)ds->erase_opts.offset, + ds->erase_opts.jffs2); + rc = nand_erase_opts(ds->nand, &ds->erase_opts); + if (rc) { + printf("Error erasing tail\n"); + dev->dfu_state = DFU_STATE_dfuERROR; + dev->dfu_status = DFU_STATUS_errERASE; + return RET_STALL; + } + + ds->off += ds->erase_opts.length; /* for consistency */ + + return RET_NOTHING; +} + +/* Read the next erase block from NAND into buffer */ +static int read_next_nand(struct urb *urb, struct flash_entity_ctx *ds, size_t len) +{ + struct usb_device_instance *dev = urb->device; + int rc; + + debug("Reading 0x%x@0x%x to 0x%p\n", len, ds->off, ds->buf); + rc = nand_read_skip_bad(ds->nand, ds->off, &len, ds->buf); + if (rc) { + debug("Error reading\n"); + dev->dfu_state = DFU_STATE_dfuERROR; + dev->dfu_status = DFU_STATUS_errWRITE; + return RET_STALL; + } + ds->off += len; + ds->ptr = ds->buf; + + return RET_NOTHING; +} + +static int get_dfu_loadaddr(uint8_t **loadaddr) +{ + const char *s; + s = getenv("loadaddr"); + if (s != NULL) { + *loadaddr = (uint8_t *)simple_strtoul(s, NULL, 16); + return 1; + } else + return 0; +} + +static int handle_nand_dnload(struct urb *urb, u_int16_t len, int first) +{ + struct usb_device_instance *dev = urb->device; + struct flash_entity_ctx *ds = &ctx; + unsigned int actual_len = len; + unsigned int remain_len; + unsigned long size; + uint8_t *loadaddr; + int rc; + + debug("download(len=%u, first=%u) ", len, first); + + if (!get_dfu_loadaddr(&loadaddr)) { + printf("Error: DFU Download requires loadaddr to be set.\n"); + dev->dfu_state = DFU_STATE_dfuERROR; + dev->dfu_status = DFU_STATUS_errADDRESS; + return RET_STALL; + } + + 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 && dev->alternate != 0) { + /* Make sure that we have a valid mtd partition table */ + char *mtdp = getenv("mtdparts"); + if (!mtdp) + run_command("dynpart", 0); + } + + if (len == 0) { + /* cleanup */ + rc = 0; + if (ds->ptr > ds->buf) + rc = erase_flash_verify_nand(urb, ds, + ds->nand->erasesize, + ds->nand->erasesize); + /* rootfs partition */ + if (!rc && ((!strcmp(ds->part->name, "rootfs")) + || (!strcmp(ds->part->name, "fs")))) + rc = erase_tail_clean_nand(urb, ds); + + ds->nand = NULL; + + 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; + } + + + // HOWTO handle this with preapre instead first? + if (first && ds->buf && ds->buf != ds->_buf && ds->buf != loadaddr) { + free(ds->buf); + ds->buf = ds->_buf; + } + + if (first) { // Will be called from prepare() + 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); + } + + return RET_ZLP; +} + +static int handle_nand_upload(struct urb *urb, u_int16_t len, int first) +{ + struct usb_device_instance *dev = urb->device; + struct flash_entity_ctx *ds = &ctx; + unsigned int remain; + int rc; + + if (len > CONFIG_USBD_DFU_XFER_SIZE) { + /* Too big */ + dev->dfu_state = DFU_STATE_dfuERROR; + dev->dfu_status = DFU_STATUS_errADDRESS; + debug("Error: Transfer size > CONFIG_USBD_DFU_XFER_SIZE "); + return -EINVAL; + } + + switch (dev->alternate) { +#if 0 + case 0: + if (first) { + printf("Starting DFU Upload of RAM (0x%p)\n", loadaddr); + ds->buf = loadaddr; + ds->ptr = ds->buf; + } + + if (ds->ptr + len > loadaddr + filesize) + len = (loadaddr + filesize) - ds->ptr; + + memcpy(urb->buffer, ds->ptr, len); + urb->actual_length = len; + ds->ptr += len; + break; +#endif + default: + if (first) { + rc = initialize_ds_nand(dev, ds); + if (rc) + return -EINVAL; + printf("Starting DFU Upload of partition '%s'\n", + ds->part->name); + } + + if (len > ds->nand->erasesize) { + printf("We don't support transfers bigger than %u\n", + ds->nand->erasesize); + len = ds->nand->erasesize; + } + + /* limit length to whatever number of bytes is left in + * this partition */ + remain = (ds->part->offset + ds->part->size) - ds->off; + if (len > remain) + len = remain; + + rc = read_next_nand(urb, ds, len); + if (rc) + return -EINVAL; + + debug("uploading %u bytes ", len); + urb->buffer = ds->buf; + urb->actual_length = len; + break; + } + + debug("returning len=%u\n", len); + return len; +} diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 38af8c4..a97d953 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -28,13 +28,15 @@ LIB := $(obj)libusb_gadget.o # new USB gadget layer dependencies ifdef CONFIG_USB_ETHER COBJS-y += ether.o epautoconf.o config.o usbstring.o +COBJS-$(CONFIG_DFU_GADGET) += dfu.o COBJS-$(CONFIG_USB_ETH_RNDIS) += rndis.o else -COBJS-$(CONFIG_DFU_GADGET) += dfu.o # Devices not related to the new gadget layer depend on CONFIG_USB_DEVICE ifdef CONFIG_USB_DEVICE COBJS-y += core.o COBJS-y += ep0.o +COBJS-y += dfu_old.o +COBJS-$(CONFIG_DFU_GADGET) += dfu.o COBJS-$(CONFIG_OMAP1510) += omap1510_udc.o COBJS-$(CONFIG_OMAP1610) += omap1510_udc.o COBJS-$(CONFIG_MPC885_FAMILY) += mpc8xx_udc.o diff --git a/drivers/usb/gadget/core.c b/drivers/usb/gadget/core.c index 67b6681..e5ee9f6 100644 --- a/drivers/usb/gadget/core.c +++ b/drivers/usb/gadget/core.c @@ -31,6 +31,7 @@
#include <malloc.h> #include <usbdevice.h> +#include <usb_dfu.h>
#define MAX_INTERFACES 2
@@ -209,6 +210,10 @@ struct usb_alternate_instance *usbd_device_alternate_instance (struct usb_device */ struct usb_device_descriptor *usbd_device_device_descriptor (struct usb_device_instance *device, int port) { +#ifdef CONFIG_USBD_DFU + if (device->dfu_state != DFU_STATE_appIDLE) + return device->dfu_dev_desc; +#endif return (device->device_descriptor); }
@@ -229,6 +234,10 @@ struct usb_configuration_descriptor *usbd_device_configuration_descriptor (struc if (!(configuration_instance = usbd_device_configuration_instance (device, port, configuration))) { return NULL; } +#ifdef CONFIG_USBD_DFU + if (device->dfu_state != DFU_STATE_appIDLE) + return (&device->dfu_cfg_desc->ucfg); +#endif return (configuration_instance->configuration_descriptor); }
@@ -250,6 +259,13 @@ struct usb_interface_descriptor *usbd_device_interface_descriptor (struct usb_de if (!(interface_instance = usbd_device_interface_instance (device, port, configuration, interface))) { return NULL; } +#ifdef CONFIG_USBD_DFU + if (device->dfu_state != DFU_STATE_appIDLE) { + if (alternate < 0 || alternate >= DFU_NUM_ALTERNATES) + return NULL; + return &device->dfu_cfg_desc->uif[alternate]; + } +#endif if ((alternate < 0) || (alternate >= interface_instance->alternates)) { return NULL; } @@ -680,4 +696,7 @@ void usbd_device_event_irq (struct usb_device_instance *device, usb_device_event /* usbdbg("calling device->event"); */ device->event(device, event, data); } +#ifdef CONFIG_USBD_DFU + dfu_event(device, event, data); +#endif } diff --git a/drivers/usb/gadget/dfu.c b/drivers/usb/gadget/dfu.c index 7aed344..86f5dd8 100644 --- a/drivers/usb/gadget/dfu.c +++ b/drivers/usb/gadget/dfu.c @@ -4,10 +4,18 @@ * Copyright (C) 2011 Samsung Electronics * author: Andrzej Pietrasiewicz andrzej.p@samsung.com * + * Copyright (C) 2011-2012 Stefan Schmidt stefan@datenfreihafen.org + * * Based on gadget zero: * Copyright (C) 2003-2007 David Brownell * All rights reserved. * + * (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 + * * 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 @@ -87,7 +95,7 @@ static struct usb_otg_descriptor otg_descriptor = { static const struct dfu_function_descriptor dfu_func = { .bLength = sizeof dfu_func, .bDescriptorType = DFU_DT_FUNC, - .bmAttributes = DFU_BIT_WILL_DETACH | + .bmAttributes = DFU_BIT_WILL_DETACH | /*FIXME */ DFU_BIT_MANIFESTATION_TOLERANT | DFU_BIT_CAN_UPLOAD | DFU_BIT_CAN_DNLOAD, @@ -126,7 +134,7 @@ static const char longname[] = "DFU Gadget"; /* default serial number takes at least two packets */ static const char serial[] = "0123456789.0123456789.012345678"; static const char dfu_name[] = "Device Firmware Upgrade"; -static const char shortname[] = "dfu"; +static const char shortname[] = "DFU";
/* static strings, in UTF-8 */ static struct usb_string strings_runtime[] = { @@ -176,9 +184,10 @@ static int config_buf(struct usb_gadget *gadget, /* for now, don't advertise srp-only devices */ if (!gadget_is_otg(gadget)) function++; - +#ifdef CONFIG_USB_ETHER len = usb_gadget_config_buf(&dfu_config, buf, USB_BUFSIZ, function); +#endif if (len < 0) return len; ((struct usb_config_descriptor *) buf)->bDescriptorType = type; @@ -282,6 +291,8 @@ static void handle_getstatus(struct usb_request *req) dstat->bwPollTimeout[0] = POLL_TIMEOUT_MILLISECONDS & 0xff; dstat->bwPollTimeout[1] = (POLL_TIMEOUT_MILLISECONDS >> 8) & 0xff; dstat->bwPollTimeout[2] = (POLL_TIMEOUT_MILLISECONDS >> 16) & 0xff; + /* Without the following line getstatus breaks with dfu-util. Maybe this + * value gets set correctly by the new gadget layer nowadays, check */ //req->actual = MIN(sizeof(*dstat), max); }
@@ -336,18 +347,22 @@ dfu_handle(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) u16 len = le16_to_cpu(ctrl->wLength); int rc = 0;
+// u_int16_t len = urb->device_request.wLength; +// handle_getstatus(urb, len); + switch (dev->dfu_state) { case DFU_STATE_appIDLE: switch (ctrl->bRequest) { case USB_REQ_DFU_GETSTATUS: handle_getstatus(req); + //break; return RET_STAT_LEN; case USB_REQ_DFU_GETSTATE: handle_getstate(req); break; case USB_REQ_DFU_DETACH: dev->dfu_state = DFU_STATE_appDETACH; - dev->dfu_state = DFU_STATE_dfuIDLE; + dev->dfu_state = DFU_STATE_dfuIDLE; // Why? return RET_ZLP; default: return RET_STALL; @@ -357,6 +372,7 @@ dfu_handle(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) switch (ctrl->bRequest) { case USB_REQ_DFU_GETSTATUS: handle_getstatus(req); + // break; return RET_STAT_LEN; case USB_REQ_DFU_GETSTATE: handle_getstate(req); @@ -375,16 +391,18 @@ dfu_handle(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) return RET_STALL; } dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; + // ret = handle_dnload(urb, val, len, 1); return handle_dnload(gadget, len); case USB_REQ_DFU_UPLOAD: dev->dfu_state = DFU_STATE_dfuUPLOAD_IDLE; + // handle_upload(urb, val, len, 1); return handle_upload(req, len); case USB_REQ_DFU_ABORT: /* no zlp? */ return RET_ZLP; case USB_REQ_DFU_GETSTATUS: handle_getstatus(req); - return RET_STAT_LEN; + return RET_STAT_LEN; // Why? case USB_REQ_DFU_GETSTATE: handle_getstate(req); break; @@ -397,7 +415,7 @@ dfu_handle(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) * resets in a row :( */ dev->dfu_state = DFU_STATE_dfuMANIFEST_WAIT_RST; - dev->dfu_state = DFU_STATE_appIDLE; + dev->dfu_state = DFU_STATE_appIDLE; // Why? break; default: dev->dfu_state = DFU_STATE_dfuERROR; @@ -408,7 +426,7 @@ dfu_handle(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) switch (ctrl->bRequest) { case USB_REQ_DFU_GETSTATUS: handle_getstatus(req); - return RET_STAT_LEN; + return RET_STAT_LEN; // Why? /* FIXME: state transition depending * on block completeness */ case USB_REQ_DFU_GETSTATE: @@ -425,7 +443,7 @@ dfu_handle(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) /* FIXME: only accept getstatus if bwPollTimeout * has elapsed */ handle_getstatus(req); - return RET_STAT_LEN; + return RET_STAT_LEN; // Why? default: dev->dfu_state = DFU_STATE_dfuERROR; return RET_STALL; @@ -435,6 +453,7 @@ dfu_handle(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) switch (ctrl->bRequest) { case USB_REQ_DFU_DNLOAD: dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; + //ret = handle_dnload(urb, len, 0); return handle_dnload(gadget, len); case USB_REQ_DFU_ABORT: dev->dfu_state = DFU_STATE_dfuIDLE; @@ -476,6 +495,7 @@ dfu_handle(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) switch (ctrl->bRequest) { case USB_REQ_DFU_UPLOAD: /* state transition if less data then requested */ + //rc = handle_upload(urb, len, 0); rc = handle_upload(req, len); if (rc >= 0 && rc < len) dev->dfu_state = DFU_STATE_dfuIDLE; @@ -518,6 +538,24 @@ dfu_handle(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) }
return 0; +#if 0 +out: + debug("new_state = %u, ret = %u\n", dev->dfu_state, ret); + + switch (ret) { + case RET_ZLP: + urb->actual_length = 0; + return DFU_EP0_ZLP; + break; + case RET_STALL: + return DFU_EP0_STALL; + break; + case RET_NOTHING: + break; + } + + return DFU_EP0_DATA; +#endif }
static int @@ -915,6 +953,7 @@ void register_flash_entities(struct flash_entity *flents, int n) num_flash_ents = n; }
+#ifdef CONFIG_USB_ETHER int __init dfu_init(void) { return usb_gadget_register_driver(&dfu_driver); @@ -926,4 +965,4 @@ void __exit dfu_cleanup(void) usb_gadget_unregister_driver(&dfu_driver); } module_exit(cleanup); - +#endif diff --git a/drivers/usb/gadget/dfu_old.c b/drivers/usb/gadget/dfu_old.c new file mode 100644 index 0000000..52686a8 --- /dev/null +++ b/drivers/usb/gadget/dfu_old.c @@ -0,0 +1,521 @@ +/* + * (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 + */ + +#include <config.h> +#if defined(CONFIG_USBD_DFU) + +/* FIXME disbale debug */ +#define DEBUG + +#include <common.h> +DECLARE_GLOBAL_DATA_PTR; /* FIXME needed? */ + +#include <malloc.h> +#include <linux/types.h> +#include <linux/list.h> +#include <asm/errno.h> +#include <usbdevice.h> +#include <usb_dfu.h> +#include <usb_dfu_descriptors.h> + +#include <flash_entity.h> + +#include "../../serial/usbtty.h" /* for STR_* defs */ + +#define POLL_TIMEOUT_MILLISECONDS 5 + +static struct flash_entity *flash_ents; +static int num_flash_ents; + +/* HACK to include nand backend code for now */ +#include "../dfu/nand.c" + +void register_flash_entities2(struct flash_entity *flents, int n) +{ + flash_ents = flents; + num_flash_ents = n; +} + +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; + + 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: + debug("DNLOAD_IDLE "); + dev->dfu_state = DFU_STATE_dfuDNLOAD_IDLE; + 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: Use real values from flash subsystem here instead a hardcoded + * value */ + dstat->bwPollTimeout[0] = POLL_TIMEOUT_MILLISECONDS & 0xff; + dstat->bwPollTimeout[1] = (POLL_TIMEOUT_MILLISECONDS >> 8) & 0xff; + dstat->bwPollTimeout[2] = (POLL_TIMEOUT_MILLISECONDS >> 16) & 0xff; + 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) +{ + 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); +} + +static int handle_dnload(struct urb *urb, u_int16_t len, int first) +{ + struct usb_device_instance *dev = urb->device; + + if (len == 0) + dev->dfu_state = DFU_STATE_dfuMANIFEST_SYNC; + + return handle_nand_dnload(urb, len, first); +} + +static int handle_upload(struct urb *urb, u_int16_t len, int first) +{ + return handle_nand_upload(urb, len, first); +} + +#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 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, + .bmAttributes = BMATTRIBUTE_RESERVED, + .bMaxPower = 50, + }, + .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 len = urb->device_request.wLength; + struct usb_device_instance *dev = urb->device; + + debug("dfu_ep0(req=0x%x, len=%u) old_state = %u ", + req, 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); + 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); + 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, len, 1); + break; + case USB_REQ_DFU_UPLOAD: + dev->dfu_state = DFU_STATE_dfuUPLOAD_IDLE; + handle_upload(urb, 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); + 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); + 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, 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); + 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); + 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, 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); + 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); + 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: + urb->actual_length = 0; + return DFU_EP0_ZLP; + break; + case RET_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) +{ + int i; + + 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; /* FIXME: Corect number? */ + uif->iInterface = DFU_STR_ALT(i); + } + + 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; + + dfu_init_strings(dev); + + return 0; +} + +/* event handler for usb device state events */ +void dfu_event(struct usb_device_instance *device, + usb_device_event_t event, int data) +{ + switch (event) { + case DEVICE_RESET: + switch (device->dfu_state) { + case DFU_STATE_appDETACH: + device->dfu_state = DFU_STATE_dfuIDLE; + 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"); + break; + default: + break; + } + break; + case DEVICE_CONFIGURED: + case DEVICE_DE_CONFIGURED: + debug("SET_CONFIGURATION(%u) ", device->configuration); + /* fallthrough */ + case DEVICE_SET_INTERFACE: + debug("SET_INTERFACE(%u,%u) old_state = %u ", + device->interface, device->alternate, + device->dfu_state); + switch (device->dfu_state) { + case DFU_STATE_appIDLE: + case DFU_STATE_appDETACH: + case DFU_STATE_dfuIDLE: + case DFU_STATE_dfuMANIFEST_WAIT_RST: + /* do nothing, we're fine */ + break; + case DFU_STATE_dfuDNLOAD_SYNC: + case DFU_STATE_dfuDNBUSY: + case DFU_STATE_dfuDNLOAD_IDLE: + case DFU_STATE_dfuMANIFEST: + device->dfu_state = DFU_STATE_dfuERROR; + device->dfu_status = DFU_STATUS_errNOTDONE; + /* FIXME: free malloc()ed buffer! */ + break; + case DFU_STATE_dfuMANIFEST_SYNC: + case DFU_STATE_dfuUPLOAD_IDLE: + case DFU_STATE_dfuERROR: + device->dfu_state = DFU_STATE_dfuERROR; + device->dfu_status = DFU_STATUS_errUNKNOWN; + break; + } + debug("new_state = %u\n", device->dfu_state); + break; + default: + break; + } +} +#endif /* CONFIG_USBD_DFU */ diff --git a/drivers/usb/gadget/ep0.c b/drivers/usb/gadget/ep0.c index 2b4ec44..ddeed93 100644 --- a/drivers/usb/gadget/ep0.c +++ b/drivers/usb/gadget/ep0.c @@ -52,6 +52,11 @@
#include <common.h> #include <usbdevice.h> +DECLARE_GLOBAL_DATA_PTR; + +#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) @@ -198,7 +203,7 @@ static int ep0_get_descriptor (struct usb_device_instance *device, urb->actual_length = 0; cp = (char*)urb->buffer;
- dbg_ep0 (2, "%s", USBD_DEVICE_DESCRIPTORS (descriptor_type)); + dbg_ep0 (2, "%s (%d)", USBD_DEVICE_DESCRIPTORS (descriptor_type), descriptor_type);
switch (descriptor_type) { case USB_DESCRIPTOR_TYPE_DEVICE: @@ -276,7 +281,28 @@ static int ep0_get_descriptor (struct usb_device_instance *device, case USB_DESCRIPTOR_TYPE_ENDPOINT: serial_printf("USB_DESCRIPTOR_TYPE_ENDPOINT - error not implemented\n"); return -1; + /* This really means "Class Specific Descriptor #1 == USB_DT_DFU */ case USB_DESCRIPTOR_TYPE_HID: +#ifdef CONFIG_USBD_DFU + { + int bNumInterface = + le16_to_cpu(urb->device_request.wIndex); + + /* In runtime mode, we only respond to the DFU INTERFACE, + * whereas in DFU mode, we respond for all intrfaces */ + if (device->dfu_state != DFU_STATE_appIDLE && + device->dfu_state != DFU_STATE_appDETACH || + bNumInterface == CONFIG_USBD_DFU_INTERFACE) { + + /* copy descriptor for this device */ + copy_config (urb, &device->dfu_cfg_desc->func_dfu, + sizeof (struct usb_dfu_func_descriptor), + max); + } else { + return -1; + } + } +#else /* CONFIG_USBD_DFU */ { serial_printf("USB_DESCRIPTOR_TYPE_HID - error not implemented\n"); return -1; /* unsupported at this time */ @@ -304,6 +330,7 @@ static int ep0_get_descriptor (struct usb_device_instance *device, max); #endif } +#endif /* CONFIG_USBD_DFU */ break; case USB_DESCRIPTOR_TYPE_REPORT: { @@ -405,6 +432,24 @@ int ep0_recv_setup (struct urb *urb) le16_to_cpu (request->wLength), USBD_DEVICE_REQUESTS (request->bRequest));
+#ifdef CONFIG_USBD_DFU + if ((request->bmRequestType & 0x3f) == USB_TYPE_DFU && + (device->dfu_state != DFU_STATE_appIDLE || + le16_to_cpu(request->wIndex) == CONFIG_USBD_DFU_INTERFACE)) { + int rc = dfu_ep0_handler(urb); + switch (rc) { + case DFU_EP0_NONE: + case DFU_EP0_UNHANDLED: + break; + case DFU_EP0_ZLP: + case DFU_EP0_DATA: + return 0; + case DFU_EP0_STALL: + return -1; + } + } +#endif /* CONFIG_USBD_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){ @@ -582,7 +627,8 @@ int ep0_recv_setup (struct urb *urb) 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: diff --git a/include/configs/omap3_beagle.h b/include/configs/omap3_beagle.h index 15e40c5..6be4d10 100644 --- a/include/configs/omap3_beagle.h +++ b/include/configs/omap3_beagle.h @@ -138,6 +138,9 @@ #define CONFIG_USB_ETHER_SMSC95XX #define CONFIG_USB_ETHER_ASIX
+#define CONFIG_DFU_GADGET +//#define CONFIG_USB_ETHER +
/* commands to include */ #include <config_cmd_default.h> diff --git a/include/dfu_backend.h b/include/dfu_backend.h index 8c608d1..a93f181 100644 --- a/include/dfu_backend.h +++ b/include/dfu_backend.h @@ -21,6 +21,7 @@
#ifndef __DFU_BACKEND_H__ #define __DFU_BACKEND_H__ +#include <nand.h>
struct mbr_part_data { unsigned long offset; /* #sectors from mmc begin */ @@ -43,6 +44,21 @@ struct flash_entity_ctx { erase_op erase; /* erase op for this medium or NULL */ struct flash_entity *this_entity; /* the containing entity */ void *associated; /* related entity, if any */ + + + nand_info_t *nand; + struct part_info *part; + unsigned int part_net_size; /* net size (no bad blocks) of part */ + nand_erase_options_t erase_opts; + + unsigned char *ptr; /* pointer to next empty byte in buffer */ + unsigned int off; /* offset of current erase page in flash chip */ + + /* 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 */ };
extern void board_dfu_init(void); diff --git a/include/usb_dfu.h b/include/usb_dfu.h new file mode 100644 index 0000000..588dcfb --- /dev/null +++ b/include/usb_dfu.h @@ -0,0 +1,97 @@ +#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 <usbdevice.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])) + +#ifndef DFU_NUM_ALTERNATES +#define DFU_NUM_ALTERNATES 6 +#endif + +#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_ALT(n) (STR_COUNT+(n)+1) +#define DFU_STR_COUNT DFU_STR_ALT(DFU_NUM_ALTERNATES) + +#define CONFIG_DFU_CFG_STR "USB Device Firmware Upgrade (DIRTY)" +#define CONFIG_DFU_ALT0_STR "RAM" + +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 + +#define RET_NOTHING 0 +#define RET_ZLP 1 +#define RET_STALL 2 + +int dfu_ep0_handler(struct urb *urb); + +void dfu_event(struct usb_device_instance *device, + usb_device_event_t event, int data); + +#endif /* _DFU_H */ diff --git a/include/usb_dfu_descriptors.h b/include/usb_dfu_descriptors.h new file mode 100644 index 0000000..d4d6a4d --- /dev/null +++ b/include/usb_dfu_descriptors.h @@ -0,0 +1,94 @@ +#ifndef _USB_DFU_H +#define _USB_DFU_H +/* USB Device Firmware Update Implementation for OpenPCD + * (C) 2006 by Harald Welte 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 */ diff --git a/include/usbdevice.h b/include/usbdevice.h index 4171636..6c2fc27 100644 --- a/include/usbdevice.h +++ b/include/usbdevice.h @@ -33,6 +33,7 @@
#include <common.h> #include "usbdescriptors.h" +#include <usb_dfu_descriptors.h>
#define MAX_URBS_QUEUED 5 @@ -467,7 +468,11 @@ typedef struct urb_link { * function driver to inform it that data has arrived. */
+#ifdef CONFIG_USBD_DFU +#define URB_BUF_SIZE (128+CONFIG_USBD_DFU_XFER_SIZE) +#else #define URB_BUF_SIZE 128 /* in linux we'd malloc this, but in u-boot we prefer static data */ +#endif struct urb {
struct usb_endpoint_instance *endpoint; @@ -595,6 +600,12 @@ struct usb_device_instance { unsigned long usbd_rxtx_timestamp; unsigned long usbd_last_rxtx_timestamp;
+#ifdef CONFIG_USBD_DFU + 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 @@ -624,6 +635,8 @@ extern char *usbd_device_status[]; extern char *usbd_device_requests[]; extern char *usbd_device_descriptors[];
+extern struct usb_string_descriptor **usb_strings; + void urb_link_init (urb_link * ul); void urb_detach (struct urb *urb); urb_link *first_urb_link (urb_link * hd);

--- board/ti/beagle/beagle.c | 17 +++++++++++++++++ board/ti/beagle/config.mk | 4 ++++ include/configs/omap3_beagle.h | 9 ++++++++- 3 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/board/ti/beagle/beagle.c b/board/ti/beagle/beagle.c index 9482c5e..e904b10 100644 --- a/board/ti/beagle/beagle.c +++ b/board/ti/beagle/beagle.c @@ -528,3 +528,20 @@ U_BOOT_CMD( "Return the status of the BeagleBoard USER button", "" ); + +void register_flash_areas(void); +void unregister_flash_areas(void); + +void board_dfu_init(void) +{ + register_flash_areas(); +} + +void board_dfu_cleanup(void) +{ + unregister_flash_areas(); +} + +void dfu_init(void) {} +void dfu_cleanup(void){} +void usb_gadget_handle_interrupts(void){} diff --git a/board/ti/beagle/config.mk b/board/ti/beagle/config.mk index cf055db..203d920 100644 --- a/board/ti/beagle/config.mk +++ b/board/ti/beagle/config.mk @@ -31,3 +31,7 @@
# For use with external or internal boots. CONFIG_SYS_TEXT_BASE = 0x80008000 + +CONFIG_USB_DFU_VENDOR=0x1457 +CONFIG_USB_DFU_PRODUCT=0x5119 +CONFIG_USB_DFU_REVISION=0x0230 diff --git a/include/configs/omap3_beagle.h b/include/configs/omap3_beagle.h index 6be4d10..08c4699 100644 --- a/include/configs/omap3_beagle.h +++ b/include/configs/omap3_beagle.h @@ -139,8 +139,15 @@ #define CONFIG_USB_ETHER_ASIX
#define CONFIG_DFU_GADGET +#define CONFIG_CMD_DEVICE_FIRMWARE_UPGRADE //#define CONFIG_USB_ETHER
+#define CONFIG_USBD_DFU 1 +#define CONFIG_USBD_DFU_XFER_SIZE 0x1000 +#define CONFIG_USBD_DFU_INTERFACE 2 + +#define DFU_NUM_ALTERNATES 6 +
/* commands to include */ #include <config_cmd_default.h> @@ -341,7 +348,7 @@ */ #define CONFIG_STACKSIZE (128 << 10) /* regular stack 128 KiB */ #ifdef CONFIG_USE_IRQ -#define CONFIG_STACKSIZE_IRQ (4 << 10) /* IRQ stack 4 KiB */ +#define CONFIG_STACKSIZE_IRQ (8 << 10) /* IRQ stack 8 KiB */ #define CONFIG_STACKSIZE_FIQ (4 << 10) /* FIQ stack 4 KiB */ #endif

Dear Stefan Schmidt,
Nice stuff, can you CC me when it comes to USB stuff, accidentally I became the new maintainer ? ;-)
drivers/usb/gadget/dfu.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/gadget/dfu.c b/drivers/usb/gadget/dfu.c index 535e194..7aed344 100644 --- a/drivers/usb/gadget/dfu.c +++ b/drivers/usb/gadget/dfu.c @@ -47,6 +47,10 @@ #include <malloc.h> #include "dfu.h"
+#ifndef POLL_TIMEOUT_MILLISECONDS +#define POLL_TIMEOUT_MILLISECONDS 5 +#endif
static struct flash_entity *flash_ents; static int num_flash_ents;
@@ -271,9 +275,14 @@ static void handle_getstatus(struct usb_request *req)
/* send status response */ dstat->bStatus = dev->dfu_status;
- /* FIXME: set dstat->bwPollTimeout */ dstat->bState = dev->dfu_state; dstat->iString = 0;
- /* FIXME: Use real values from flash subsystem here instead a hardcoded
* value */
- dstat->bwPollTimeout[0] = POLL_TIMEOUT_MILLISECONDS & 0xff;
- dstat->bwPollTimeout[1] = (POLL_TIMEOUT_MILLISECONDS >> 8) & 0xff;
- dstat->bwPollTimeout[2] = (POLL_TIMEOUT_MILLISECONDS >> 16) & 0xff;
- //req->actual = MIN(sizeof(*dstat), max);
What do you expect from me to tell you about this patch? Or do you expect someone else to comment on this?
}
static void handle_getstate(struct usb_request *req)
Best regards, Marek Vasut

On Thursday 05 April 2012 20:18:16 Marek Vasut wrote:
What do you expect from me to tell you about this patch? Or do you expect someone else to comment on this?
he mentioned in the summary of the series. he's run out of time, so he's dumping his WIP state in case someone else picks it up. -mike

Dear Mike Frysinger,
On Thursday 05 April 2012 20:18:16 Marek Vasut wrote:
What do you expect from me to tell you about this patch? Or do you expect someone else to comment on this?
he mentioned in the summary of the series. he's run out of time, so he's dumping his WIP state in case someone else picks it up.
"Re: [U-Boot] [PATCH 2/4] dfu: generic backend parts" this summary? I see, didn't notice it, but just read it.
Stefan, thanks for your work, I hope you'll be back eventually :)
-mike
Best regards, Marek Vasut

Hello.
On Fri, 2012-04-06 at 15:06, Marek Vasut wrote:
On Thursday 05 April 2012 20:18:16 Marek Vasut wrote:
What do you expect from me to tell you about this patch? Or do you expect someone else to comment on this?
he mentioned in the summary of the series. he's run out of time, so he's dumping his WIP state in case someone else picks it up.
"Re: [U-Boot] [PATCH 2/4] dfu: generic backend parts" this summary? I see, didn't notice it, but just read it.
Stefan, thanks for your work, I hope you'll be back eventually :)
Time will tell. I still think its a worthwhile feature. I just need to find some time, motivation and hardware to work on it again. Will take at leats a couple of months though before I would start anything. Just relocated to another country and still in need for a flat to rent and sort out life and work. :)
regards Stefan Schmidt

Dear Stefan Schmidt,
Hello.
On Fri, 2012-04-06 at 15:06, Marek Vasut wrote:
On Thursday 05 April 2012 20:18:16 Marek Vasut wrote:
What do you expect from me to tell you about this patch? Or do you expect someone else to comment on this?
he mentioned in the summary of the series. he's run out of time, so he's dumping his WIP state in case someone else picks it up.
"Re: [U-Boot] [PATCH 2/4] dfu: generic backend parts" this summary? I see, didn't notice it, but just read it.
Stefan, thanks for your work, I hope you'll be back eventually :)
Time will tell. I still think its a worthwhile feature. I just need to find some time, motivation and hardware to work on it again. Will take at leats a couple of months though before I would start anything. Just relocated to another country and still in need for a flat to rent and sort out life and work. :)
Good luck on that :-)
regards Stefan Schmidt
Best regards, Marek Vasut

Dear Marek, Stefan
Dear Stefan Schmidt,
Hello.
On Fri, 2012-04-06 at 15:06, Marek Vasut wrote:
On Thursday 05 April 2012 20:18:16 Marek Vasut wrote:
What do you expect from me to tell you about this patch? Or do you expect someone else to comment on this?
he mentioned in the summary of the series. he's run out of time, so he's dumping his WIP state in case someone else picks it up.
"Re: [U-Boot] [PATCH 2/4] dfu: generic backend parts" this summary? I see, didn't notice it, but just read it.
Stefan, thanks for your work, I hope you'll be back eventually :)
Time will tell. I still think its a worthwhile feature. I just need to find some time, motivation and hardware to work on it again. Will take at leats a couple of months though before I would start anything. Just relocated to another country and still in need for a flat to rent and sort out life and work. :)
Good luck on that :-)
I'm working on the composite gadget (as Marek know already :-)). On top of the g_dnl USB composite download driver it is possible to lay down the DFU function.
Actualy we (I mean Andrzej Pietrasiewicz and me), have managed to provide some proof-of-concept DFU code running with g_dnl driver (both DFU + THOR). Unfortunately this code is not yet "finished" and THOR protocol support has higher priority now.
When we post DFU patches we will add you guys on the Cc.

Dear Lukasz Majewski,
Dear Marek, Stefan
Dear Stefan Schmidt,
Hello.
On Fri, 2012-04-06 at 15:06, Marek Vasut wrote:
On Thursday 05 April 2012 20:18:16 Marek Vasut wrote:
What do you expect from me to tell you about this patch? Or do you expect someone else to comment on this?
he mentioned in the summary of the series. he's run out of time, so he's dumping his WIP state in case someone else picks it up.
"Re: [U-Boot] [PATCH 2/4] dfu: generic backend parts" this summary? I see, didn't notice it, but just read it.
Stefan, thanks for your work, I hope you'll be back eventually :)
Time will tell. I still think its a worthwhile feature. I just need to find some time, motivation and hardware to work on it again. Will take at leats a couple of months though before I would start anything. Just relocated to another country and still in need for a flat to rent and sort out life and work. :)
Good luck on that :-)
I'm working on the composite gadget (as Marek know already :-)). On top of the g_dnl USB composite download driver it is possible to lay down the DFU function.
Actualy we (I mean Andrzej Pietrasiewicz and me), have managed to provide some proof-of-concept DFU code running with g_dnl driver (both DFU + THOR). Unfortunately this code is not yet "finished" and THOR protocol support has higher priority now.
When we post DFU patches we will add you guys on the Cc.
I hope to review your stuff ASAP
Best regards, Marek Vasut

Hello.
On Mon, 2012-04-23 at 09:27, Lukasz Majewski wrote:
On Fri, 2012-04-06 at 15:06, Marek Vasut wrote:
On Thursday 05 April 2012 20:18:16 Marek Vasut wrote:
What do you expect from me to tell you about this patch? Or do you expect someone else to comment on this?
he mentioned in the summary of the series. he's run out of time, so he's dumping his WIP state in case someone else picks it up.
"Re: [U-Boot] [PATCH 2/4] dfu: generic backend parts" this summary? I see, didn't notice it, but just read it.
Stefan, thanks for your work, I hope you'll be back eventually :)
Time will tell. I still think its a worthwhile feature. I just need to find some time, motivation and hardware to work on it again. Will take at leats a couple of months though before I would start anything. Just relocated to another country and still in need for a flat to rent and sort out life and work. :)
Good luck on that :-)
I'm working on the composite gadget (as Marek know already :-)). On top of the g_dnl USB composite download driver it is possible to lay down the DFU function.
Yeah, I have seen this patches. That is all fine with me. I'm not bound to my patches at all. Having DFU in u-boot mainline is the goal. :)
One of the problems I had was that I did not have hardware with a UDC driver that is compatible with new gadget layer from the kernel ported to u-boot. I agree that its the way to go to have composite and more but it is quite hard to test against when only some drivers are supporting it. The Beagleboard I used for most of the tests was still using the musb driver with some glue and does not support it for example.
That should not be a problem on your side as all Samsung UDCs are written for the new gadget framework anyway.
Actualy we (I mean Andrzej Pietrasiewicz and me), have managed to provide some proof-of-concept DFU code running with g_dnl driver (both DFU + THOR). Unfortunately this code is not yet "finished" and THOR protocol support has higher priority now.
When we post DFU patches we will add you guys on the Cc.
Thanks.
regards Stefan Schmidt

Hello.
On Fri, 2012-04-06 at 02:18, Marek Vasut wrote:
Nice stuff, can you CC me when it comes to USB stuff, accidentally I became the new maintainer ? ;-)
Yeah, forgot about this while I was trying to get at leats my latest state out of the door. If I will send an updated version I will put you in CC.
regards Stefan Schmidt

Signed-off-by: Andrzej Pietrasiewicz andrzej.p@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- common/Makefile | 1 + common/cmd_dfu.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 0 deletions(-) create mode 100644 common/cmd_dfu.c
diff --git a/common/Makefile b/common/Makefile index 1b672ad..3322b97 100644 --- a/common/Makefile +++ b/common/Makefile @@ -162,6 +162,7 @@ COBJS-y += cmd_usb.o COBJS-y += usb.o COBJS-$(CONFIG_USB_STORAGE) += usb_storage.o endif +COBJS-$(CONFIG_CMD_DEVICE_FIRMWARE_UPGRADE) += cmd_dfu.o COBJS-$(CONFIG_CMD_XIMG) += cmd_ximg.o COBJS-$(CONFIG_YAFFS2) += cmd_yaffs2.o
diff --git a/common/cmd_dfu.c b/common/cmd_dfu.c new file mode 100644 index 0000000..93d8918 --- /dev/null +++ b/common/cmd_dfu.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2011 Samsung Electrnoics + * author: Andrzej Pietrasiewicz andrzej.p@samsung.com + * + * 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> +#include <command.h> +#include <dfu.h> +#include <dfu_backend.h> +#include <flash_entity.h> + +static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + board_dfu_init(); + dfu_init(); + while (1) { + int irq_res; + /* Handle control-c and timeouts */ + if (ctrlc()) { + printf("The remote end did not respond in time.\n"); + goto fail; + } + + irq_res = usb_gadget_handle_interrupts(); + } +fail: + dfu_cleanup(); + board_dfu_cleanup(); + return -1; +} + +U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu, + "Use the DFU [Device Firmware Upgrade]", + "dfu - device firmware upgrade" +); +

Signed-off-by: Andrzej Pietrasiewicz andrzej.p@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- board/samsung/goni/Makefile | 2 + board/samsung/goni/flash.c | 341 +++++++++++++++++++++++++++++++++++++++++++ board/samsung/goni/flash.h | 28 ++++ board/samsung/goni/goni.c | 17 ++ include/configs/s5p_goni.h | 4 + 5 files changed, 392 insertions(+), 0 deletions(-) create mode 100644 board/samsung/goni/flash.c create mode 100644 board/samsung/goni/flash.h
diff --git a/board/samsung/goni/Makefile b/board/samsung/goni/Makefile index ecde7a7..3f13cb8 100644 --- a/board/samsung/goni/Makefile +++ b/board/samsung/goni/Makefile @@ -31,6 +31,8 @@ LIB = $(obj)lib$(BOARD).o COBJS-y := goni.o onenand.o SOBJS := lowlevel_init.o
+COBJS-$(CONFIG_DFU_FOR_BOARD) += flash.o + SRCS := $(SOBJS:.o=.S) $(COBJS-y:.o=.c) OBJS := $(addprefix $(obj),$(COBJS-y)) SOBJS := $(addprefix $(obj),$(SOBJS)) diff --git a/board/samsung/goni/flash.c b/board/samsung/goni/flash.c new file mode 100644 index 0000000..29c7ec1 --- /dev/null +++ b/board/samsung/goni/flash.c @@ -0,0 +1,341 @@ +/* + * flash.c -- board flashing routines + * + * Copyright (C) 2011 Samsung Electronics + * author: Andrzej Pietrasiewicz andrzej.p@samsung.com + * + * 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> +#include <malloc.h> +#include <mmc.h> +#include <dfu_backend.h> +#include <flash_entity.h> +#include <linux/types.h> +#include <jffs2/load_kernel.h> + +#define MMC_BLOCK_SZ (256 * 1024) +#define MMC_UBOOT_OFFSET 128 +#define MMC_UBOOT_SZ 512 +#define MMC_NAME_LEN 12 +#define SHORT_PART_NAME 15 +#define LONG_PART_NAME 20 +#define CALLOC_STRUCT(n, type) (struct type *) calloc(n, sizeof(struct type)) +#define DEFAULT_MMC_PART_NAME "mmc-default-part" + +/* partition IDs counted from 0, eg. mmc0-pri-1's ID is 0 */ +#define UIMAGE_PART_ID 1 +#define EXTENDED_PART_ID 3 +#define UMS_PART_ID 7 +#define UIMAGE_PART_NAME "mmc0-pri-2" + +#define USE_MMC_UBOOT +#define USE_MMC + +/* + * MMC u-boot partitions + */ +static struct mbr_part_data *uboot_pdata; + +static u8 uboot_part_num; +static u8 used_uboot_parts; + +int use_uboot(struct mbr_part_data *pdata, u8 i) +{ + /* + * Use i and pdata[i] members to decide if the partition is used + */ + return 1; +} + +char *alloc_uboot_name(u8 i) +{ + char *name = calloc(SHORT_PART_NAME, 1); + + if (name) { + sprintf(name, "mmc-u-boot"); + return name; + } + + return DEFAULT_MMC_PART_NAME; +} + +/* + * MMC partitions and MMC operations + */ +struct mmc *mmc; + +static struct mbr_part_data *mmc_pdata; + +static u8 mmc_part_num; +static u8 used_mmc_parts; + +static u8 mmc_buf[MMC_BLOCK_SZ]; + +static int extended_lba; + +static int mmc_mbr_dev; + +static u8 pri_count; +static u8 ext_count = 4; + +/* + * Define files available in the UIMAGE partition which has FAT on it. + * Only flat structure without subdirectories is supported. + */ +static char *uImage_part_files[] = { + "uImage", +}; +#define UIMAGE_PART_NUM_FILES ARRAY_SIZE(uImage_part_files) + +/* + * Return number of flash entities per this partition + */ +u8 use_mmc(struct mbr_part_data *pdata, u8 i) +{ + /* + * Use i and pdata[i] members to decide if the partition is used + */ + if (i == UIMAGE_PART_ID) + return UIMAGE_PART_NUM_FILES; + if (i == EXTENDED_PART_ID) + return 0; /* do not expose the extended partition as a whole */ + if (i == UMS_PART_ID) + return 0; /* do not expose UMS; there is a separate command */ + return 1; +} + +char *alloc_mmc_name(struct mbr_part_data *pdata, u8 i, u8 l) +{ + char *name = calloc(MMC_NAME_LEN, 1); + + if (name) { + sprintf(name, "mmc0-"); + if (pdata[i].primary) + sprintf(name + strlen(name), "pri-%d", + l ? pri_count : ++pri_count); + else + sprintf(name + strlen(name), "ext-%d", + l ? ext_count : ++ext_count); + + return name; + } + + return DEFAULT_MMC_PART_NAME; +} + +/* + * FAT operations + */ +static u8 fat_buf[MMC_FAT_BLOCK_SZ]; + +/* + * Transport layer to storage adaptation + */ + +static int prepare_fat(void *ctx, u8 mode) +{ + struct flash_entity_ctx *ct = ctx; + + set_fat_filename(ct->associated); + if (!strncmp(ct->this_entity->name, UIMAGE_PART_NAME, + strlen(UIMAGE_PART_NAME))) + set_fat_part_num(UIMAGE_PART_ID + 1); + return generic_prepare(ctx, mode); +} + +/* + * Flash entities definitions for this board + */ +static struct flash_entity *flash_ents; + +static void customize_entities(struct flash_entity *fe, u8 n_ents) +{ + int i; + for (i = 0; i < n_ents; ++i) { + /* i counts all entities, not just mmc entities */ + /* add similar "if" blocks for other customizable entities */ + if (!strcmp(fe[i].name, UIMAGE_PART_NAME)) { + struct flash_entity_ctx *ctx; + char *name, file_number; + + fe[i].prepare = prepare_fat; + ctx = fe[i].ctx; + ctx->length = min(ctx->length, MMC_FAT_BLOCK_SZ); + ctx->buf = fat_buf; + ctx->buf_len = ctx->length; + ctx->read = read_fat; + ctx->write = write_fat; + file_number = (char)ctx->associated; + /* by design file_number cannot exceed array size */ + ctx->associated = uImage_part_files[file_number]; + + name = calloc(LONG_PART_NAME, 1); + if (name) { + sprintf(name, "%s-%s", fe[i].name, + (char *)ctx->associated); + free(fe[i].name); + fe[i].name = name; + } + + continue; + } + } +} + +static inline void generic_flash_entity(struct flash_entity *fe) +{ + fe->prepare = generic_prepare; + fe->finish = generic_finish; + fe->read_block = read_block; + fe->write_block = write_block; +} + +static inline void generic_flash_entity_ctx(struct flash_entity_ctx *ctx, + struct flash_entity *fe) +{ + ctx->buf = mmc_buf; + ctx->buf_len = MMC_BLOCK_SZ; + ctx->read = read_mmc; + ctx->write = write_mmc; + ctx->erase = NULL; + ctx->this_entity = fe; + fe->ctx = ctx; +} + +void register_flash_areas(void) +{ + u8 i, j; + +#ifdef USE_MMC + mmc = find_mmc_device(mmc_mbr_dev); + if (mmc && !mmc_init(mmc)) { + mmc_part_num = read_mbr(mmc, NULL, &extended_lba, mmc_mbr_dev); + if (mmc_part_num) { + mmc_pdata = CALLOC_STRUCT(mmc_part_num, + mbr_part_data); + if (mmc_pdata) { + if (!read_mbr(mmc, mmc_pdata, &extended_lba, mmc_mbr_dev)) { + free(mmc_pdata); + mmc_pdata = NULL; + } + set_fat_dev(&mmc->block_dev); + } + } + } + used_mmc_parts = 0; + for (i = 0; mmc_pdata && i < mmc_part_num; ++i) + used_mmc_parts += use_mmc(mmc_pdata, i); + pri_count = 0; + ext_count = 4; +#endif + +#ifdef USE_MMC_UBOOT + if (mmc) { + uboot_part_num = 1; + if (uboot_part_num) { + uboot_pdata = CALLOC_STRUCT(uboot_part_num, + mbr_part_data); + if (uboot_pdata) + for (i = 0; i < uboot_part_num; ++i) { + uboot_pdata[i].offset = + MMC_UBOOT_OFFSET; + uboot_pdata[i].length = + MMC_UBOOT_SZ; + uboot_pdata[i].primary = 0; + } + } + } + used_uboot_parts = 0; + for (i = 0; uboot_pdata && i < uboot_part_num; ++i) + used_uboot_parts += use_uboot(uboot_pdata, i); +#endif + + flash_ents = CALLOC_STRUCT(used_uboot_parts + used_mmc_parts, + flash_entity); + if (!flash_ents) + goto partinfo_alloc_rollback; + + j = 0; + for (i = 0; i < uboot_part_num; ++i) + if (use_uboot(uboot_pdata, i)) { + struct flash_entity *fe; + struct flash_entity_ctx *ctx; + + fe = &flash_ents[j++]; + fe->name = alloc_uboot_name(i); + generic_flash_entity(fe); + + ctx = CALLOC_STRUCT(1, flash_entity_ctx); + if (!ctx) + goto flash_ents_alloc_rollback; + generic_flash_entity_ctx(ctx, fe); + ctx->offset = uboot_pdata[i].offset * MMC_SECTOR_SZ; + ctx->length = uboot_pdata[i].length * MMC_SECTOR_SZ; + } + uboot_part_num = used_uboot_parts; + + for (i = 0; i < mmc_part_num; ++i) { + u8 k = use_mmc(mmc_pdata, i); + u8 l; + for (l = 0; l < k; ++l) { + struct flash_entity *fe; + struct flash_entity_ctx *ctx; + + fe = &flash_ents[j++]; + fe->name = alloc_mmc_name(mmc_pdata, i, l); + generic_flash_entity(fe); + + ctx = CALLOC_STRUCT(1, flash_entity_ctx); + if (!ctx) + goto flash_ents_alloc_rollback; + generic_flash_entity_ctx(ctx, fe); + ctx->offset = mmc_pdata[i].offset * MMC_SECTOR_SZ; + ctx->length = mmc_pdata[i].length * MMC_SECTOR_SZ; + ctx->associated = (void *)l; + } + } + mmc_part_num = used_mmc_parts; + customize_entities(flash_ents, uboot_part_num + mmc_part_num); + register_flash_entities(flash_ents, uboot_part_num + mmc_part_num); +printf("NUM ENTITIES:%d\n", uboot_part_num + mmc_part_num); + + return; + +flash_ents_alloc_rollback: + while (j--) { + free(flash_ents[j].name); + free(flash_ents[j].ctx); + } + free(flash_ents); + +partinfo_alloc_rollback: + free(uboot_pdata); + free(mmc_pdata); +} + +void unregister_flash_areas(void) +{ + int j = uboot_part_num + mmc_part_num; + while (j--) { + free(flash_ents[j].name); + free(flash_ents[j].ctx); + } + free(flash_ents); + free(uboot_pdata); + free(mmc_pdata); +} + diff --git a/board/samsung/goni/flash.h b/board/samsung/goni/flash.h new file mode 100644 index 0000000..ffe4d6d --- /dev/null +++ b/board/samsung/goni/flash.h @@ -0,0 +1,28 @@ +/* + * flash.h -- board flashing routines interface + * + * Copyright (C) 2011 Samsung Electronics + * author: Andrzej Pietrasiewicz andrzej.p@samsung.com + * + * 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 + */ + +#ifndef FLASH_GONI_H_ +#define FLASH_GONI_H_ + +void register_flash_areas(void); +void unregister_flash_areas(void); + +#endif diff --git a/board/samsung/goni/goni.c b/board/samsung/goni/goni.c index c4fc3e9..7e693be 100644 --- a/board/samsung/goni/goni.c +++ b/board/samsung/goni/goni.c @@ -29,6 +29,9 @@ #include <asm/arch/hs_otg.h> #include <asm/arch/cpu.h> #include <max8998_pmic.h> + +#include "flash.h" + DECLARE_GLOBAL_DATA_PTR;
static struct s5pc110_gpio *s5pc110_gpio; @@ -144,4 +147,18 @@ struct s3c_plat_otg_data s5pc110_otg_data = { .regs_phy = S5PC110_PHY_BASE, .regs_otg = S5PC110_OTG_BASE, }; + +#ifdef CONFIG_DFU_FOR_BOARD +void board_dfu_init(void) +{ + register_flash_areas(); + s3c_udc_probe(&s5pc110_otg_data); +} + +void board_dfu_cleanup(void) +{ + unregister_flash_areas(); +} +#endif + #endif diff --git a/include/configs/s5p_goni.h b/include/configs/s5p_goni.h index df97802..3881f92 100644 --- a/include/configs/s5p_goni.h +++ b/include/configs/s5p_goni.h @@ -86,6 +86,8 @@ #define CONFIG_CMD_ONENAND #define CONFIG_CMD_MTDPARTS #define CONFIG_CMD_MMC +#define CONFIG_CMD_FAT +#define CONFIG_CMD_DEVICE_FIRMWARE_UPGRADE
#define CONFIG_BOOTDELAY 1 #define CONFIG_ZERO_BOOTDELAY_CHECK @@ -241,4 +243,6 @@ #define CONFIG_USB_GADGET_S3C_UDC_OTG 1 #define CONFIG_USB_GADGET_DUALSPEED 1
+#define CONFIG_DFU_GADGET +#define CONFIG_DFU_FOR_BOARD #endif /* __CONFIG_H */
participants (6)
-
Andrzej Pietrasiewicz
-
Lukasz Majewski
-
Marek Vasut
-
Mike Frysinger
-
Stefan Schmidt
-
Wolfgang Denk