[U-Boot] [PATCH 1/4] dfu: protocol initial implementation
Andrzej Pietrasiewicz
andrzej.p at samsung.com
Wed Nov 9 10:52:20 CET 2011
Signed-off-by: Andrzej Pietrasiewicz <andrzej.p at samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park at 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 at 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 at 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 at 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 at 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_ */
--
1.7.0.4
More information about the U-Boot
mailing list