[U-Boot] [PATCH 0/7] dfu:usb: Composite USB download gadget with DFU function

Those patches add support for composite USB download gadget. This gadget (at least for now) is equipped with DFU download function.
A separate DFU back-end and front-end have been added. Back-end is placed at ./drivers/dfu directory. The front-end is implemented as USB function.
The back-end is written in a generic manner with storage device specific code separated (eMMC).
DFU specification can be found at: http://wiki.openmoko.org/wiki/USB_DFU_-_The_USB_Device_Firmware_Upgrade_stan...
Example usage:
u-boot side: dfu mmc 0 PC: dfu-util -U IMAGE.bin -a uImage (for upload) dfu-util -D uImage -a uImage (download)
To list the alt settings: dfu mmc 0 list
Test HW: Exynos4210 Trats board
Lukasz Majewski (7): dfu:usb: Support for g_dnl composite download gadget. dfu:usb: DFU USB function (f_dfu) support for g_dnl composite gadget dfu: DFU backend implementation dfu: MMC specific routines for DFU operation dfu:cmd: Support for DFU u-boot command arm:trats: Support for USB UDC driver at TRATS board. arm:trats: Enable g_dnl composite USB gadget with embedded DFU function on TRATS
Makefile | 1 + board/samsung/trats/trats.c | 9 + common/Makefile | 1 + common/cmd_dfu.c | 81 ++++++ drivers/dfu/Makefile | 44 +++ drivers/dfu/dfu.c | 259 +++++++++++++++++ drivers/dfu/dfu_mmc.c | 126 ++++++++ drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_dfu.c | 666 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/f_dfu.h | 100 +++++++ drivers/usb/gadget/g_dnl.c | 235 +++++++++++++++ include/configs/trats.h | 19 ++- include/dfu.h | 99 +++++++ include/g_dnl.h | 33 +++ 14 files changed, 1674 insertions(+), 1 deletions(-) create mode 100644 common/cmd_dfu.c create mode 100644 drivers/dfu/Makefile create mode 100644 drivers/dfu/dfu.c create mode 100644 drivers/dfu/dfu_mmc.c create mode 100644 drivers/usb/gadget/f_dfu.c create mode 100644 drivers/usb/gadget/f_dfu.h create mode 100644 drivers/usb/gadget/g_dnl.c create mode 100644 include/dfu.h create mode 100644 include/g_dnl.h

Composite USB download gadget support (g_dnl) for download functions. This code works on top of composite gadget.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de --- drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/g_dnl.c | 235 +++++++++++++++++++++++++++++++++++++++++++ include/g_dnl.h | 33 ++++++ 3 files changed, 269 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/gadget/g_dnl.c create mode 100644 include/g_dnl.h
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 87d1918..2c067c8 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -29,6 +29,7 @@ LIB := $(obj)libusb_gadget.o ifdef CONFIG_USB_GADGET COBJS-y += epautoconf.o config.o usbstring.o COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o +COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o endif ifdef CONFIG_USB_ETHER COBJS-y += ether.o epautoconf.o config.o usbstring.o diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c new file mode 100644 index 0000000..7925d0e --- /dev/null +++ b/drivers/usb/gadget/g_dnl.c @@ -0,0 +1,235 @@ +/* + * g_dnl.c -- USB Downloader Gadget + * + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski l.majewski@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 <errno.h> +#include <common.h> +#include <malloc.h> +#include <linux/usb/ch9.h> +#include <usbdescriptors.h> +#include <linux/usb/gadget.h> + +#include <mmc.h> +#include <part.h> + +#include <g_dnl.h> +#include "f_dfu.h" + +#include "gadget_chips.h" +#include "composite.c" + +/* Samsung's IDs */ +#define DRIVER_VENDOR_NUM 0x04E8 +#define DRIVER_PRODUCT_NUM 0x6601 + +#define STRING_MANUFACTURER 25 +#define STRING_PRODUCT 2 +#define STRING_USBDOWN 2 +#define CONFIG_USBDOWNLOADER 2 + +#define DRIVER_VERSION "usb_dnl 2.0" + +static const char shortname[] = "usb_dnl_"; +static const char product[] = "SLP USB download gadget"; +static const char manufacturer[] = "Samsung"; + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_COMM, + .bDeviceSubClass = 0x02, /*0x02:CDC-modem , 0x00:CDC-serial*/ + + .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM), + .iProduct = STRING_PRODUCT, + .bNumConfigurations = 1, +}; + +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &(struct usb_otg_descriptor){ + .bLength = sizeof(struct usb_otg_descriptor), + .bDescriptorType = USB_DT_OTG, + .bmAttributes = USB_OTG_SRP, + }, + NULL, +}; + +/* static strings, in UTF-8 */ +static struct usb_string odin_string_defs[] = { + { 0, manufacturer, }, + { 1, product, }, +}; + +static struct usb_gadget_strings odin_string_tab = { + .language = 0x0409, /* en-us */ + .strings = odin_string_defs, +}; + +static struct usb_gadget_strings *g_dnl_composite_strings[] = { + &odin_string_tab, + NULL, +}; + +static int g_dnl_unbind(struct usb_composite_dev *cdev) +{ + debug("%s\n", __func__); + return 0; +} + +static int g_dnl_do_config(struct usb_configuration *c) +{ + int ret = -1; + char *s = (char *) c->cdev->driver->name; + + debug("%s: c: 0x%p cdev: 0x%p\n", __func__, c, c->cdev); + + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + printf("GADGET DRIVER: %s\n", s); + + if (!strcmp(s, "usb_dnl_dfu")) + ret = dfu_add(c); + + return ret; +} + +static int g_dnl_config_register(struct usb_composite_dev *cdev) +{ + debug("%s:\n", __func__); + static struct usb_configuration config = { + .label = "usb_dnload", + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .bConfigurationValue = CONFIG_USBDOWNLOADER, + .iConfiguration = STRING_USBDOWN, + + .bind = g_dnl_do_config, + }; + + return usb_add_config(cdev, &config); +} + +static int g_dnl_bind(struct usb_composite_dev *cdev) +{ + int gcnum; + int id, ret; + struct usb_gadget *gadget = cdev->gadget; + + debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev); + + id = usb_string_id(cdev); + + if (id < 0) + return id; + odin_string_defs[0].id = id; + device_desc.iManufacturer = id; + + id = usb_string_id(cdev); + if (id < 0) + return id; + + odin_string_defs[1].id = id; + device_desc.iProduct = id; + + ret = g_dnl_config_register(cdev); + if (ret) + goto error; + + + gcnum = usb_gadget_controller_number(gadget); + + debug("gcnum: %d\n", gcnum); + if (gcnum >= 0) + device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); + else { + debug("%s: controller '%s' not recognized\n", + shortname, gadget->name); + device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); + } + + return 0; + + error: + g_dnl_unbind(cdev); + return -ENOMEM; +} + +static void g_dnl_suspend(struct usb_composite_dev *cdev) +{ + if (cdev->gadget->speed == USB_SPEED_UNKNOWN) + return; + + debug("suspend\n"); +} + +static void g_dnl_resume(struct usb_composite_dev *cdev) +{ + debug("resume\n"); +} + +static struct usb_composite_driver g_dnl_driver = { + .name = NULL, + .dev = &device_desc, + .strings = g_dnl_composite_strings, + + .bind = g_dnl_bind, + .unbind = g_dnl_unbind, + + .suspend = g_dnl_suspend, + .resume = g_dnl_resume, +}; + +int g_dnl_init(char *s) +{ + int ret; + static char str[16]; + + memset(str, '\0', sizeof(str)); + + strncpy(str, shortname, sizeof(shortname)); + + if (!strncmp(s, "dfu", sizeof(s))) { + strncat(str, s, sizeof(str)); + } else { + printf("%s: unknown command: %s\n", __func__, s); + return -EINVAL; + } + + g_dnl_driver.name = str; + + debug("%s: g_dnl_driver.name: %s\n", __func__, g_dnl_driver.name); + ret = usb_composite_register(&g_dnl_driver); + + if (ret) { + printf("%s: failed!, error:%d ", __func__, ret); + return ret; + } + + return 0; +} + +void g_dnl_cleanup(void) +{ + usb_composite_unregister(&g_dnl_driver); +} diff --git a/include/g_dnl.h b/include/g_dnl.h new file mode 100644 index 0000000..5d0e9a5 --- /dev/null +++ b/include/g_dnl.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski l.majewski@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 __G_DOWNLOAD_H_ +#define __G_DOWNLOAD_H_ + +#include <linux/usb/ch9.h> +#include <usbdescriptors.h> +#include <linux/usb/gadget.h> + +int g_dnl_init(char *s); +void g_dnl_cleanup(void); + +/* USB initialization declaration - board specific*/ +void board_usb_init(void); +#endif /* __G_DOWNLOAD_H_ */

Dear Lukasz Majewski,
Composite USB download gadget support (g_dnl) for download functions. This code works on top of composite gadget.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/g_dnl.c | 235 +++++++++++++++++++++++++++++++++++++++++++ include/g_dnl.h | 33 ++++++ 3 files changed, 269 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/gadget/g_dnl.c create mode 100644 include/g_dnl.h
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 87d1918..2c067c8 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -29,6 +29,7 @@ LIB := $(obj)libusb_gadget.o ifdef CONFIG_USB_GADGET COBJS-y += epautoconf.o config.o usbstring.o COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o +COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o endif ifdef CONFIG_USB_ETHER COBJS-y += ether.o epautoconf.o config.o usbstring.o diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c new file mode 100644 index 0000000..7925d0e --- /dev/null +++ b/drivers/usb/gadget/g_dnl.c @@ -0,0 +1,235 @@ +/*
- g_dnl.c -- USB Downloader Gadget
- Copyright (C) 2012 Samsung Electronics
- Lukasz Majewski l.majewski@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 <errno.h> +#include <common.h> +#include <malloc.h> +#include <linux/usb/ch9.h> +#include <usbdescriptors.h> +#include <linux/usb/gadget.h>
+#include <mmc.h> +#include <part.h>
+#include <g_dnl.h> +#include "f_dfu.h"
+#include "gadget_chips.h" +#include "composite.c"
+/* Samsung's IDs */ +#define DRIVER_VENDOR_NUM 0x04E8 +#define DRIVER_PRODUCT_NUM 0x6601
Is it samsung specific? Or can we use some USB IDs that are not bound to samsung?
+#define STRING_MANUFACTURER 25 +#define STRING_PRODUCT 2 +#define STRING_USBDOWN 2 +#define CONFIG_USBDOWNLOADER 2
+#define DRIVER_VERSION "usb_dnl 2.0"
+static const char shortname[] = "usb_dnl_"; +static const char product[] = "SLP USB download gadget"; +static const char manufacturer[] = "Samsung";
DITTO?
+static struct usb_device_descriptor device_desc = {
- .bLength = sizeof device_desc,
- .bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = __constant_cpu_to_le16(0x0200),
- .bDeviceClass = USB_CLASS_COMM,
- .bDeviceSubClass = 0x02, /*0x02:CDC-modem , 0x00:CDC-serial*/
- .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM),
- .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM),
- .iProduct = STRING_PRODUCT,
- .bNumConfigurations = 1,
+};
+static const struct usb_descriptor_header *otg_desc[] = {
- (struct usb_descriptor_header *) &(struct usb_otg_descriptor){
.bLength = sizeof(struct usb_otg_descriptor),
.bDescriptorType = USB_DT_OTG,
.bmAttributes = USB_OTG_SRP,
- },
- NULL,
+};
+/* static strings, in UTF-8 */ +static struct usb_string odin_string_defs[] = {
- { 0, manufacturer, },
- { 1, product, },
+};
+static struct usb_gadget_strings odin_string_tab = {
- .language = 0x0409, /* en-us */
- .strings = odin_string_defs,
+};
+static struct usb_gadget_strings *g_dnl_composite_strings[] = {
- &odin_string_tab,
- NULL,
+};
+static int g_dnl_unbind(struct usb_composite_dev *cdev) +{
- debug("%s\n", __func__);
- return 0;
+}
+static int g_dnl_do_config(struct usb_configuration *c) +{
- int ret = -1;
- char *s = (char *) c->cdev->driver->name;
- debug("%s: c: 0x%p cdev: 0x%p\n", __func__, c, c->cdev);
Can you elaborate in that debug message more? It's not really useful ;-)
- if (gadget_is_otg(c->cdev->gadget)) {
c->descriptors = otg_desc;
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
- }
- printf("GADGET DRIVER: %s\n", s);
- if (!strcmp(s, "usb_dnl_dfu"))
ret = dfu_add(c);
And if this fails, we should set ret= as well, no?
- return ret;
+}
+static int g_dnl_config_register(struct usb_composite_dev *cdev) +{
- debug("%s:\n", __func__);
- static struct usb_configuration config = {
.label = "usb_dnload",
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
.bConfigurationValue = CONFIG_USBDOWNLOADER,
.iConfiguration = STRING_USBDOWN,
.bind = g_dnl_do_config,
- };
- return usb_add_config(cdev, &config);
+}
+static int g_dnl_bind(struct usb_composite_dev *cdev) +{
- int gcnum;
- int id, ret;
- struct usb_gadget *gadget = cdev->gadget;
- debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev);
- id = usb_string_id(cdev);
- if (id < 0)
return id;
- odin_string_defs[0].id = id;
- device_desc.iManufacturer = id;
- id = usb_string_id(cdev);
- if (id < 0)
return id;
- odin_string_defs[1].id = id;
- device_desc.iProduct = id;
- ret = g_dnl_config_register(cdev);
- if (ret)
goto error;
Two newlines
- gcnum = usb_gadget_controller_number(gadget);
- debug("gcnum: %d\n", gcnum);
- if (gcnum >= 0)
device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
- else {
debug("%s: controller '%s' not recognized\n",
shortname, gadget->name);
device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
- }
- return 0;
- error:
- g_dnl_unbind(cdev);
- return -ENOMEM;
+}
+static void g_dnl_suspend(struct usb_composite_dev *cdev) +{
- if (cdev->gadget->speed == USB_SPEED_UNKNOWN)
return;
- debug("suspend\n");
+}
+static void g_dnl_resume(struct usb_composite_dev *cdev) +{
- debug("resume\n");
+}
Maybe you should check if suspend/resume are set at all. Then you won't need the above stubs.
+static struct usb_composite_driver g_dnl_driver = {
- .name = NULL,
- .dev = &device_desc,
- .strings = g_dnl_composite_strings,
- .bind = g_dnl_bind,
- .unbind = g_dnl_unbind,
- .suspend = g_dnl_suspend,
- .resume = g_dnl_resume,
+};
+int g_dnl_init(char *s) +{
- int ret;
- static char str[16];
Why use '\0' if you can just use 0 ?
- memset(str, '\0', sizeof(str));
- strncpy(str, shortname, sizeof(shortname));
- if (!strncmp(s, "dfu", sizeof(s))) {
strncat(str, s, sizeof(str));
- } else {
printf("%s: unknown command: %s\n", __func__, s);
return -EINVAL;
- }
- g_dnl_driver.name = str;
- debug("%s: g_dnl_driver.name: %s\n", __func__, g_dnl_driver.name);
- ret = usb_composite_register(&g_dnl_driver);
- if (ret) {
printf("%s: failed!, error:%d ", __func__, ret);
Missing newline in printf() (...error:%d ").
return ret;
- }
- return 0;
+}
+void g_dnl_cleanup(void) +{
- usb_composite_unregister(&g_dnl_driver);
+} diff --git a/include/g_dnl.h b/include/g_dnl.h new file mode 100644 index 0000000..5d0e9a5 --- /dev/null +++ b/include/g_dnl.h @@ -0,0 +1,33 @@ +/*
- Copyright (C) 2012 Samsung Electronics
- Lukasz Majewski l.majewski@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 __G_DOWNLOAD_H_ +#define __G_DOWNLOAD_H_
+#include <linux/usb/ch9.h> +#include <usbdescriptors.h> +#include <linux/usb/gadget.h>
+int g_dnl_init(char *s); +void g_dnl_cleanup(void);
+/* USB initialization declaration - board specific*/ +void board_usb_init(void); +#endif /* __G_DOWNLOAD_H_ */

Hi Marek,
Thanks for review.
Dear Lukasz Majewski,
Composite USB download gadget support (g_dnl) for download functions. This code works on top of composite gadget.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/g_dnl.c | 235 +++++++++++++++++++++++++++++++++++++++++++ include/g_dnl.h | 33 ++++++ 3 files changed, 269 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/gadget/g_dnl.c create mode 100644 include/g_dnl.h
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 87d1918..2c067c8 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -29,6 +29,7 @@ LIB := $(obj)libusb_gadget.o ifdef CONFIG_USB_GADGET COBJS-y += epautoconf.o config.o usbstring.o COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o +COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o endif ifdef CONFIG_USB_ETHER COBJS-y += ether.o epautoconf.o config.o usbstring.o diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c new file mode 100644 index 0000000..7925d0e --- /dev/null +++ b/drivers/usb/gadget/g_dnl.c @@ -0,0 +1,235 @@ +/*
- g_dnl.c -- USB Downloader Gadget
- Copyright (C) 2012 Samsung Electronics
- Lukasz Majewski l.majewski@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 <errno.h> +#include <common.h> +#include <malloc.h> +#include <linux/usb/ch9.h> +#include <usbdescriptors.h> +#include <linux/usb/gadget.h>
+#include <mmc.h> +#include <part.h>
+#include <g_dnl.h> +#include "f_dfu.h"
+#include "gadget_chips.h" +#include "composite.c"
+/* Samsung's IDs */ +#define DRIVER_VENDOR_NUM 0x04E8 +#define DRIVER_PRODUCT_NUM 0x6601
Is it samsung specific? Or can we use some USB IDs that are not bound to samsung?
I think, that those IDs shall be defined at include/configs/<board>.h In that way one can use any ID.
+#define STRING_MANUFACTURER 25 +#define STRING_PRODUCT 2 +#define STRING_USBDOWN 2 +#define CONFIG_USBDOWNLOADER 2
+#define DRIVER_VERSION "usb_dnl 2.0"
+static const char shortname[] = "usb_dnl_"; +static const char product[] = "SLP USB download gadget"; +static const char manufacturer[] = "Samsung";
DITTO?
Manufacturer field can be either omitted or changed to other name.
+static struct usb_device_descriptor device_desc = {
- .bLength = sizeof device_desc,
- .bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = __constant_cpu_to_le16(0x0200),
- .bDeviceClass = USB_CLASS_COMM,
- .bDeviceSubClass = 0x02, /*0x02:CDC-modem ,
0x00:CDC-serial*/ +
- .idVendor =
__constant_cpu_to_le16(DRIVER_VENDOR_NUM),
- .idProduct =
__constant_cpu_to_le16(DRIVER_PRODUCT_NUM),
- .iProduct = STRING_PRODUCT,
- .bNumConfigurations = 1,
+};
+static const struct usb_descriptor_header *otg_desc[] = {
- (struct usb_descriptor_header *) &(struct
usb_otg_descriptor){
.bLength = sizeof(struct
usb_otg_descriptor),
.bDescriptorType = USB_DT_OTG,
.bmAttributes = USB_OTG_SRP,
- },
- NULL,
+};
+/* static strings, in UTF-8 */ +static struct usb_string odin_string_defs[] = {
- { 0, manufacturer, },
- { 1, product, },
+};
+static struct usb_gadget_strings odin_string_tab = {
- .language = 0x0409, /* en-us */
- .strings = odin_string_defs,
+};
+static struct usb_gadget_strings *g_dnl_composite_strings[] = {
- &odin_string_tab,
- NULL,
+};
+static int g_dnl_unbind(struct usb_composite_dev *cdev) +{
- debug("%s\n", __func__);
- return 0;
+}
+static int g_dnl_do_config(struct usb_configuration *c) +{
- int ret = -1;
- char *s = (char *) c->cdev->driver->name;
- debug("%s: c: 0x%p cdev: 0x%p\n", __func__, c, c->cdev);
Can you elaborate in that debug message more? It's not really useful ;-)
I will correct that.
- if (gadget_is_otg(c->cdev->gadget)) {
c->descriptors = otg_desc;
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
- }
- printf("GADGET DRIVER: %s\n", s);
- if (!strcmp(s, "usb_dnl_dfu"))
ret = dfu_add(c);
And if this fails, we should set ret= as well, no?
The dynamic variable "ret" is defined with -1 value on the beginning of the function. When comparison fails, the -1 value is returned.
- return ret;
+}
+static int g_dnl_config_register(struct usb_composite_dev *cdev) +{
- debug("%s:\n", __func__);
- static struct usb_configuration config = {
.label = "usb_dnload",
.bmAttributes = USB_CONFIG_ATT_ONE |
USB_CONFIG_ATT_SELFPOWER,
.bConfigurationValue = CONFIG_USBDOWNLOADER,
.iConfiguration = STRING_USBDOWN,
.bind = g_dnl_do_config,
- };
- return usb_add_config(cdev, &config);
+}
+static int g_dnl_bind(struct usb_composite_dev *cdev) +{
- int gcnum;
- int id, ret;
- struct usb_gadget *gadget = cdev->gadget;
- debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget,
cdev); +
- id = usb_string_id(cdev);
- if (id < 0)
return id;
- odin_string_defs[0].id = id;
- device_desc.iManufacturer = id;
- id = usb_string_id(cdev);
- if (id < 0)
return id;
- odin_string_defs[1].id = id;
- device_desc.iProduct = id;
- ret = g_dnl_config_register(cdev);
- if (ret)
goto error;
Two newlines
Will fix.
- gcnum = usb_gadget_controller_number(gadget);
- debug("gcnum: %d\n", gcnum);
- if (gcnum >= 0)
device_desc.bcdDevice = cpu_to_le16(0x0200 +
gcnum);
- else {
debug("%s: controller '%s' not recognized\n",
shortname, gadget->name);
device_desc.bcdDevice =
__constant_cpu_to_le16(0x9999);
- }
- return 0;
- error:
- g_dnl_unbind(cdev);
- return -ENOMEM;
+}
+static void g_dnl_suspend(struct usb_composite_dev *cdev) +{
- if (cdev->gadget->speed == USB_SPEED_UNKNOWN)
return;
- debug("suspend\n");
+}
+static void g_dnl_resume(struct usb_composite_dev *cdev) +{
- debug("resume\n");
+}
Maybe you should check if suspend/resume are set at all. Then you won't need the above stubs.
I will remove them.
+static struct usb_composite_driver g_dnl_driver = {
- .name = NULL,
- .dev = &device_desc,
- .strings = g_dnl_composite_strings,
- .bind = g_dnl_bind,
- .unbind = g_dnl_unbind,
- .suspend = g_dnl_suspend,
- .resume = g_dnl_resume,
+};
+int g_dnl_init(char *s) +{
- int ret;
- static char str[16];
Why use '\0' if you can just use 0 ?
I will change.
- memset(str, '\0', sizeof(str));
- strncpy(str, shortname, sizeof(shortname));
- if (!strncmp(s, "dfu", sizeof(s))) {
strncat(str, s, sizeof(str));
- } else {
printf("%s: unknown command: %s\n", __func__, s);
return -EINVAL;
- }
- g_dnl_driver.name = str;
- debug("%s: g_dnl_driver.name: %s\n", __func__,
g_dnl_driver.name);
- ret = usb_composite_register(&g_dnl_driver);
- if (ret) {
printf("%s: failed!, error:%d ", __func__, ret);
Missing newline in printf() (...error:%d ").
I will change that.
return ret;
- }
- return 0;
+}
+void g_dnl_cleanup(void) +{
- usb_composite_unregister(&g_dnl_driver);
+} diff --git a/include/g_dnl.h b/include/g_dnl.h new file mode 100644 index 0000000..5d0e9a5 --- /dev/null +++ b/include/g_dnl.h @@ -0,0 +1,33 @@ +/*
- Copyright (C) 2012 Samsung Electronics
- Lukasz Majewski l.majewski@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 __G_DOWNLOAD_H_ +#define __G_DOWNLOAD_H_
+#include <linux/usb/ch9.h> +#include <usbdescriptors.h> +#include <linux/usb/gadget.h>
+int g_dnl_init(char *s); +void g_dnl_cleanup(void);
+/* USB initialization declaration - board specific*/ +void board_usb_init(void); +#endif /* __G_DOWNLOAD_H_ */

On Tuesday 03 July 2012 05:38:05 Lukasz Majewski wrote:
--- /dev/null +++ b/drivers/usb/gadget/g_dnl.c
+static const char shortname[] = "usb_dnl_";
shortname -> gadget_name_prefix
+static void g_dnl_suspend(struct usb_composite_dev *cdev) +{
- if (cdev->gadget->speed == USB_SPEED_UNKNOWN)
return;
- debug("suspend\n");
+}
+static void g_dnl_resume(struct usb_composite_dev *cdev) +{
- debug("resume\n");
+}
do suspend/resume funcs make any sense in u-boot ?
+int g_dnl_init(char *s)
const char
+{
- int ret;
- static char str[16];
- memset(str, '\0', sizeof(str));
- strncpy(str, shortname, sizeof(shortname));
no need for the memset. this strncpy looks broken -- the 3rd arg is for how many bytes are available in the *dest* buffer, not how long the source is.
- if (!strncmp(s, "dfu", sizeof(s))) {
sizeof() here is wrong -- that gives you back 4 (the size of a pointer) on 32bit systems
strncat(str, s, sizeof(str));
this is also incorrect. the length given to strncat is how many bytes are left, not the total length.
since this string parsing logic is all just completely broken, i'd suggest replacing it all with:
{ int ret; /* We only allow "dfu" atm, so 3 should be enough */ static char name[sizeof(shortname) + 3];
if (strcmp(s, "dfu")) { printf("%s: unknown command: %s\n", __func__, s); return -EINVAL; }
strcpy(name, shortname); strcat(name, s);
- if (ret) {
printf("%s: failed!, error:%d ", __func__, ret);
needs a space between "error:" and "%d"
--- /dev/null +++ b/include/g_dnl.h
+#include <linux/usb/ch9.h> +#include <usbdescriptors.h> +#include <linux/usb/gadget.h>
unused -> delete
+int g_dnl_init(char *s);
int g_dnl_register(const char *type);
this also needs documentation in the header explaining how to use this func
+void g_dnl_cleanup(void);
void g_dnl_unregister(void);
+/* USB initialization declaration - board specific*/ +void board_usb_init(void);
not used in these files -> delete -mike

Dear Mike Frysinger,
Thank you for thorough review.
On Tuesday 03 July 2012 05:38:05 Lukasz Majewski wrote:
--- /dev/null +++ b/drivers/usb/gadget/g_dnl.c
+static const char shortname[] = "usb_dnl_";
shortname -> gadget_name_prefix
This might be only matter of taste, but in my opinion this name is more readable.
+static void g_dnl_suspend(struct usb_composite_dev *cdev) +{
- if (cdev->gadget->speed == USB_SPEED_UNKNOWN)
return;
- debug("suspend\n");
+}
+static void g_dnl_resume(struct usb_composite_dev *cdev) +{
- debug("resume\n");
+}
do suspend/resume funcs make any sense in u-boot ?
You have reviewed the v1 of this patch series. Marek Vasut has already pointed out this problem and it has been resoled with v2.
+int g_dnl_init(char *s)
const char
Ok.
+{
- int ret;
- static char str[16];
- memset(str, '\0', sizeof(str));
- strncpy(str, shortname, sizeof(shortname));
no need for the memset.
The gadget can be called from many separate commands (e.g. command "dfu" and command "ums") and those commands can be executed without power cycle. Thereof I need to be sure, that str is not polluted by previous name.
this strncpy looks broken -- the 3rd arg is for how many bytes are available in the *dest* buffer, not how long the source is.
After looking deeply into the source I admit that providing the upper bound on the dest is more safe.
- if (!strncmp(s, "dfu", sizeof(s))) {
sizeof() here is wrong -- that gives you back 4 (the size of a pointer) on 32bit systems
... and it works only because the "dfu\0" and "ums\0" is 4 lenght. :/
strncat(str, s, sizeof(str));
this is also incorrect. the length given to strncat is how many bytes are left, not the total length.
I cannot agree. sizeof(str) return 16, which is the destination buffer size.
since this string parsing logic is all just completely broken, i'd suggest replacing it all with:
{ int ret; /* We only allow "dfu" atm, so 3 should be enough */ static char name[sizeof(shortname) + 3];
if (strcmp(s, "dfu")) { printf("%s: unknown command: %s\n", __func__, s); return -EINVAL; }
strcpy(name, shortname); strcat(name, s);
This is a very neat design, but it assumes that there will be only one function ("dfu" in this case). For this particular function +3 applies, but what if another function (like "usb_storage") will be defined? I'm now working on another function - the USB Mass Storage (named "ums" ;-) ).
Another issue is omitting the strncmp/strncpy functions and depending on the: static char name[sizeof(shortname) + 3]; definition to prevent buffer overflow.
The +3 magic number worries me a bit...
- if (ret) {
printf("%s: failed!, error:%d ", __func__, ret);
needs a space between "error:" and "%d"
Will be fixed
--- /dev/null +++ b/include/g_dnl.h
+#include <linux/usb/ch9.h> +#include <usbdescriptors.h> +#include <linux/usb/gadget.h>
unused -> delete
I will remove those includes from g_dnl.c file
+int g_dnl_init(char *s);
int g_dnl_register(const char *type);
this also needs documentation in the header explaining how to use this func
I will provide the working example of this gadget. I will not insist on the function name. It can be g_dnl_register().
+void g_dnl_cleanup(void);
void g_dnl_unregister(void);
Can be g_dnl_unregister() as well.
+/* USB initialization declaration - board specific*/ +void board_usb_init(void);
not used in these files -> delete
But it is used at e.g. samsung/trats/trats.c I think that g_dnl.h is a good place for this.
-mike

On Monday 23 July 2012 11:25:25 Lukasz Majewski wrote:
Dear Mike Frysinger,
On Tuesday 03 July 2012 05:38:05 Lukasz Majewski wrote:
+{
- int ret;
- static char str[16];
- memset(str, '\0', sizeof(str));
- strncpy(str, shortname, sizeof(shortname));
no need for the memset.
The gadget can be called from many separate commands (e.g. command "dfu" and command "ums") and those commands can be executed without power cycle. Thereof I need to be sure, that str is not polluted by previous name.
that makes no sense. please read the documentation of the str*cpy functions -- they do no analysis of the target string and merely copy the source to the destination. thus this code is basically:
str[0] = '\0'; str[1] = '\0'; str[...] = '\0'; str[0] = shortname[0]; str[1] = shortname[1]; str[...] = shortname[...];
it should be fairly obvious now why that memset is pointless.
this strncpy looks broken -- the 3rd arg is for how many bytes are available in the *dest* buffer, not how long the source is.
After looking deeply into the source I admit that providing the upper bound on the dest is more safe.
it isn't a matter of being safe, it's a matter of correctness
strncat(str, s, sizeof(str));
this is also incorrect. the length given to strncat is how many bytes are left, not the total length.
I cannot agree. sizeof(str) return 16, which is the destination buffer size.
which is wrong. please read the strncat specification.
since this string parsing logic is all just completely broken, i'd suggest replacing it all with:
{ int ret; /* We only allow "dfu" atm, so 3 should be enough */ static char name[sizeof(shortname) + 3];
if (strcmp(s, "dfu")) { printf("%s: unknown command: %s\n", __func__, s); return -EINVAL; }
strcpy(name, shortname); strcat(name, s);
This is a very neat design, but it assumes that there will be only one function ("dfu" in this case). For this particular function +3 applies, but what if another function (like "usb_storage") will be defined?
why does that matter ? the snippet i posted above is trivial to extend to support any number of functions. increase the "3" to the max you care about, and then add more strcmp() to the if statement.
I'm now working on another function - the USB Mass Storage (named "ums" ;-) ).
Another issue is omitting the strncmp/strncpy functions and depending on the: static char name[sizeof(shortname) + 3]; definition to prevent buffer overflow.
your existing code is already full of bugs that don't prevent overflow, and having the "3" right next to the "dfu" with a comment makes it pretty clear what is going on. -mike

Support for f_dfu USB function.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de --- drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/f_dfu.c | 666 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/f_dfu.h | 100 +++++++ 3 files changed, 767 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/gadget/f_dfu.c create mode 100644 drivers/usb/gadget/f_dfu.h
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 2c067c8..5bbdd36 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -30,6 +30,7 @@ ifdef CONFIG_USB_GADGET COBJS-y += epautoconf.o config.o usbstring.o COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o +COBJS-$(CONFIG_DFU_FUNCTION) += f_dfu.o endif ifdef CONFIG_USB_ETHER COBJS-y += ether.o epautoconf.o config.o usbstring.o diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c new file mode 100644 index 0000000..c2bf60c --- /dev/null +++ b/drivers/usb/gadget/f_dfu.c @@ -0,0 +1,666 @@ +/* + * f_dfu.c -- Device Firmware Update USB function + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz andrzej.p@samsung.com + * Lukasz Majewski l.majewski@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 <errno.h> +#include <common.h> +#include <malloc.h> + +#include <linux/usb/ch9.h> +#include <usbdescriptors.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> + +#include <dfu.h> +#include "f_dfu.h" + +struct f_dfu { + struct usb_function usb_function; + + struct usb_descriptor_header **function; + struct usb_string *strings; + + /* when configured, we have one config */ + u8 config; + u8 altsetting; + enum dfu_state dfu_state; + unsigned int dfu_status; + + /* Send/received block number is handy for data integrity check */ + int blk_seq_num; +}; + +static inline struct f_dfu *func_to_dfu(struct usb_function *f) +{ + return container_of(f, struct f_dfu, usb_function); +} + +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 = DFU_USB_BUFSIZ, + .bcdDFUVersion = __constant_cpu_to_le16(0x0110), +}; + +static 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 = DYNAMIC */ +}; + +static struct usb_descriptor_header *dfu_runtime_descs[] = { + (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 const char dfu_name[] = "Device Firmware Upgrade"; + +/* static strings, in UTF-8 */ +/* + * dfu_genericiguration specific + */ +static struct usb_string strings_dfu_generic[] = { + [0].s = dfu_name, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dfu_generic = { + .language = 0x0409, /* en-us */ + .strings = strings_dfu_generic, +}; + +static struct usb_gadget_strings *dfu_generic_strings[] = { + &stringtab_dfu_generic, + NULL, +}; + +/* + * usb_function specific + */ +static struct usb_gadget_strings stringtab_dfu = { + .language = 0x0409, /* en-us */ + /* + * .strings + * + * assigned during initialization, + * depends on number of flash entities + * + */ +}; + +static struct usb_gadget_strings *dfu_strings[] = { + &stringtab_dfu, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_dfu *f_dfu = req->context; + + dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf, + req->length, f_dfu->blk_seq_num); + + if (req->length == 0) { + puts("DOWNLOAD ... OK\n"); + puts("Ctrl+C to exit ...\n"); + } +} + +static void handle_getstatus(struct usb_request *req) +{ + struct dfu_status *dstat = (struct dfu_status *)req->buf; + struct f_dfu *f_dfu = req->context; + + switch (f_dfu->dfu_state) { + case DFU_STATE_dfuDNLOAD_SYNC: + case DFU_STATE_dfuDNBUSY: + f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE; + break; + case DFU_STATE_dfuMANIFEST_SYNC: + break; + default: + break; + } + + /* send status response */ + dstat->bStatus = f_dfu->dfu_status; + /* FIXME: set dstat->bwPollTimeout */ + dstat->bState = f_dfu->dfu_state; + dstat->iString = 0; +} + +static void handle_getstate(struct usb_request *req) +{ + struct f_dfu *f_dfu = req->context; + + ((u8 *)req->buf)[0] = f_dfu->dfu_state & 0xff; + req->actual = sizeof(u8); +} + +static inline void to_dfu_mode(struct f_dfu *f_dfu) +{ + f_dfu->usb_function.strings = dfu_strings; + f_dfu->usb_function.hs_descriptors = f_dfu->function; +} + +static inline void to_runtime_mode(struct f_dfu *f_dfu) +{ + f_dfu->usb_function.strings = NULL; + f_dfu->usb_function.hs_descriptors = dfu_runtime_descs; +} + +static int handle_upload(struct usb_request *req, u16 len) +{ + struct f_dfu *f_dfu = req->context; + + return dfu_read(dfu_get_entity(f_dfu->altsetting), req->buf, + req->length, f_dfu->blk_seq_num); +} + +static int handle_dnload(struct usb_gadget *gadget, u16 len) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_request *req = cdev->req; + struct f_dfu *f_dfu = req->context; + + if (len == 0) + f_dfu->dfu_state = DFU_STATE_dfuMANIFEST_SYNC; + + req->complete = dnload_request_complete; + + return len; +} + + +static int +dfu_handle(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct usb_gadget *gadget = f->config->cdev->gadget; + struct usb_request *req = f->config->cdev->req; + struct f_dfu *f_dfu = f->config->cdev->req->context; + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + int value = 0; + int standard; + + standard = (ctrl->bRequestType & USB_TYPE_MASK) + == USB_TYPE_STANDARD; + + debug("w_value: 0x%x len: 0x%x\n", w_value, len); + debug("standard: 0x%x ctrl->bRequest: 0x%x f_dfu->dfu_state: 0x%x\n", + standard, ctrl->bRequest, f_dfu->dfu_state); + + if (!standard) { + switch (f_dfu->dfu_state) { + case DFU_STATE_appIDLE: + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + case USB_REQ_DFU_DETACH: + f_dfu->dfu_state = DFU_STATE_appDETACH; + to_dfu_mode(f_dfu); + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + value = RET_ZLP; + break; + default: + value = RET_STALL; + break; + } + break; + case DFU_STATE_appDETACH: + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_appIDLE; + value = RET_STALL; + break; + } + /* FIXME: implement timer to return to appIDLE */ + break; + case DFU_STATE_dfuIDLE: + switch (ctrl->bRequest) { + case USB_REQ_DFU_DNLOAD: + if (len == 0) { + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; + f_dfu->blk_seq_num = w_value; + value = handle_dnload(gadget, len); + break; + case USB_REQ_DFU_UPLOAD: + f_dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE; + f_dfu->blk_seq_num = 0; + value = handle_upload(req, len); + break; + case USB_REQ_DFU_ABORT: + /* no zlp? */ + value = RET_ZLP; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + 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 :( + */ + f_dfu->dfu_state = + DFU_STATE_dfuMANIFEST_WAIT_RST; + to_runtime_mode(f_dfu); + f_dfu->dfu_state = DFU_STATE_appIDLE; + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + break; + case DFU_STATE_dfuDNLOAD_SYNC: + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + /* FIXME: state transition depending + * on block completeness */ + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + break; + case DFU_STATE_dfuDNBUSY: + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + /* FIXME: only accept getstatus if bwPollTimeout + * has elapsed */ + handle_getstatus(req); + value = RET_STAT_LEN; + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + break; + case DFU_STATE_dfuDNLOAD_IDLE: + switch (ctrl->bRequest) { + case USB_REQ_DFU_DNLOAD: + f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; + f_dfu->blk_seq_num = w_value; + value = handle_dnload(gadget, len); + break; + case USB_REQ_DFU_ABORT: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + value = RET_ZLP; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + break; + case DFU_STATE_dfuMANIFEST_SYNC: + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + /* We're MainfestationTolerant */ + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + handle_getstatus(req); + f_dfu->blk_seq_num = 0; + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + break; + case DFU_STATE_dfuMANIFEST: + /* we should never go here */ + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + 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 */ + f_dfu->blk_seq_num = w_value; + value = handle_upload(req, len); + if (value >= 0 && value < len) + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + + break; + case USB_REQ_DFU_ABORT: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + /* no zlp? */ + value = RET_ZLP; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + break; + case DFU_STATE_dfuERROR: + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + case USB_REQ_DFU_CLRSTATUS: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + f_dfu->dfu_status = DFU_STATUS_OK; + /* no zlp? */ + value = RET_ZLP; + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + break; + default: + value = -1; + } + } else {/* standard */ + if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR && + (w_value >> 8) == DFU_DT_FUNC) { + value = min(len, (u16) sizeof dfu_func); + memcpy(req->buf, &dfu_func, value); + } + } + + if (value >= 0) { + req->length = value; + req->zero = value < len; + value = usb_ep_queue(gadget->ep0, req, 0); + if (value < 0) { + debug("ep_queue --> %d\n", value); + req->status = 0; + } + } + + return value; +} + +/*-------------------------------------------------------------------------*/ + +static int +dfu_prepare_strings(struct f_dfu *f_dfu, int n) +{ + struct dfu_entity *de = NULL; + int i = 0; + + f_dfu->strings = calloc(((n + 1) * sizeof(struct usb_string)), 1); + if (!f_dfu->strings) + goto enomem; + + for (i = 0; i < n; ++i) { + de = dfu_get_entity(i); + f_dfu->strings[i].s = de->name; + } + + f_dfu->strings[i].id = 0; + f_dfu->strings[i].s = NULL; + + return 0; + +enomem: + while (i) + f_dfu->strings[--i].s = NULL; + + kfree(f_dfu->strings); + + return -ENOMEM; +} + +static int dfu_prepare_function(struct f_dfu *f_dfu, int n) +{ + struct usb_interface_descriptor *d; + int i = 0; + + f_dfu->function = calloc(n * sizeof(struct usb_descriptor_header *), 1); + if (!f_dfu->function) + goto enomem; + + 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; + + f_dfu->function[i] = (struct usb_descriptor_header *)d; + } + f_dfu->function[i] = NULL; + + return 0; + +enomem: + while (i) { + kfree(f_dfu->function[--i]); + f_dfu->function[i] = NULL; + } + kfree(f_dfu->function); + + return -ENOMEM; +} + +static int dfu_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_dfu *f_dfu = func_to_dfu(f); + int alt_num = dfu_get_alt_number(); + int rv, id, i; + + id = usb_interface_id(c, f); + if (id < 0) + return id; + dfu_intf_runtime.bInterfaceNumber = id; + + f_dfu->dfu_state = DFU_STATE_appIDLE; + f_dfu->dfu_status = DFU_STATUS_OK; + + rv = dfu_prepare_function(f_dfu, alt_num); + if (rv) + goto error; + + rv = dfu_prepare_strings(f_dfu, alt_num); + if (rv) + goto error; + for (i = 0; i < alt_num; i++) { + id = usb_string_id(cdev); + if (id < 0) + return id; + f_dfu->strings[i].id = id; + ((struct usb_interface_descriptor *)f_dfu->function[i]) + ->iInterface = id; + } + + stringtab_dfu.strings = f_dfu->strings; + + cdev->req->context = f_dfu; + + return 0; +error: + return rv; +} + +static void dfu_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + int alt_num = dfu_get_alt_number(); + int i; + + if (f_dfu->strings) { + i = alt_num; + while (i) + f_dfu->strings[--i].s = NULL; + + kfree(f_dfu->strings); + } + + if (f_dfu->function) { + i = alt_num; + while (i) { + kfree(f_dfu->function[--i]); + f_dfu->function[i] = NULL; + } + kfree(f_dfu->function); + } + + kfree(f_dfu); +} + +static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + + debug("%s: intf:%d alt:%d\n", __func__, intf, alt); + + f_dfu->altsetting = alt; + + return 0; +} + +/* TODO: is this really what we need here? */ +static void dfu_disable(struct usb_function *f) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + if (f_dfu->config == 0) + return; + + debug("%s: reset config\n", __func__); + + f_dfu->config = 0; +} + +static int dfu_bind_config(struct usb_configuration *c) +{ + struct f_dfu *f_dfu; + int status; + + f_dfu = kzalloc(sizeof(*f_dfu), GFP_KERNEL); + if (!f_dfu) + return -ENOMEM; + f_dfu->usb_function.name = "dfu"; + f_dfu->usb_function.hs_descriptors = dfu_runtime_descs; + f_dfu->usb_function.bind = dfu_bind; + f_dfu->usb_function.unbind = dfu_unbind; + f_dfu->usb_function.set_alt = dfu_set_alt; + f_dfu->usb_function.disable = dfu_disable; + f_dfu->usb_function.strings = dfu_generic_strings, + f_dfu->usb_function.setup = dfu_handle, + + status = usb_add_function(c, &f_dfu->usb_function); + if (status) + kfree(f_dfu); + + return status; +} + +int dfu_add(struct usb_configuration *c) +{ + int id; + + id = usb_string_id(c->cdev); + if (id < 0) + return id; + strings_dfu_generic[0].id = id; + dfu_intf_runtime.iInterface = id; + + debug("%s: cdev: 0x%p gadget:0x%p gadget->ep0: 0x%p\n", __func__, + c->cdev, c->cdev->gadget, c->cdev->gadget->ep0); + + return dfu_bind_config(c); +} diff --git a/drivers/usb/gadget/f_dfu.h b/drivers/usb/gadget/f_dfu.h new file mode 100644 index 0000000..023e1ad --- /dev/null +++ b/drivers/usb/gadget/f_dfu.h @@ -0,0 +1,100 @@ +/* + * f_dfu.h -- Device Firmware Update gadget + * + * Copyright (C) 2011-2012 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 __F_DFU_H_ +#define __F_DFU_H_ + +#include <linux/compiler.h> +#include <linux/usb/composite.h> + +#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 DFU_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 + +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; + +/* configuration-specific linkup */ +int dfu_add(struct usb_configuration *c); +#endif /* __F_DFU_H_ */

Dear Lukasz Majewski,
Support for f_dfu USB function.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[...]
+static const char dfu_name[] = "Device Firmware Upgrade";
+/* static strings, in UTF-8 */ +/*
- dfu_genericiguration specific
generi....what?
- */
+static struct usb_string strings_dfu_generic[] = {
- [0].s = dfu_name,
- { } /* end of list */
+};
+static struct usb_gadget_strings stringtab_dfu_generic = {
- .language = 0x0409, /* en-us */
- .strings = strings_dfu_generic,
+};
+static struct usb_gadget_strings *dfu_generic_strings[] = {
- &stringtab_dfu_generic,
- NULL,
+};
+/*
- usb_function specific
- */
+static struct usb_gadget_strings stringtab_dfu = {
- .language = 0x0409, /* en-us */
- /*
* .strings
*
* assigned during initialization,
* depends on number of flash entities
*
*/
+};
+static struct usb_gadget_strings *dfu_strings[] = {
- &stringtab_dfu,
- NULL,
+};
+/*------------------------------------------------------------------------ -*/ + +static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req) +{
- struct f_dfu *f_dfu = req->context;
- dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf,
req->length, f_dfu->blk_seq_num);
- if (req->length == 0) {
puts("DOWNLOAD ... OK\n");
puts("Ctrl+C to exit ...\n");
- }
+}
+static void handle_getstatus(struct usb_request *req) +{
- struct dfu_status *dstat = (struct dfu_status *)req->buf;
- struct f_dfu *f_dfu = req->context;
- switch (f_dfu->dfu_state) {
- case DFU_STATE_dfuDNLOAD_SYNC:
What's this crazy cammel case in here ? :-)
- case DFU_STATE_dfuDNBUSY:
f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
break;
- case DFU_STATE_dfuMANIFEST_SYNC:
break;
- default:
break;
- }
- /* send status response */
- dstat->bStatus = f_dfu->dfu_status;
- /* FIXME: set dstat->bwPollTimeout */
FIXME ... so fix it ;-)
- dstat->bState = f_dfu->dfu_state;
- dstat->iString = 0;
+}
+static void handle_getstate(struct usb_request *req) +{
- struct f_dfu *f_dfu = req->context;
- ((u8 *)req->buf)[0] = f_dfu->dfu_state & 0xff;
Now ... this is ubercrazy ... can't this be made without this typecasting voodoo?
- req->actual = sizeof(u8);
+}
[...]
+static int handle_dnload(struct usb_gadget *gadget, u16 len) +{
- struct usb_composite_dev *cdev = get_gadget_data(gadget);
- struct usb_request *req = cdev->req;
- struct f_dfu *f_dfu = req->context;
- if (len == 0)
f_dfu->dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
- req->complete = dnload_request_complete;
- return len;
+}
One newline too much below.
+static int +dfu_handle(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{
- struct usb_gadget *gadget = f->config->cdev->gadget;
- struct usb_request *req = f->config->cdev->req;
- struct f_dfu *f_dfu = f->config->cdev->req->context;
- u16 len = le16_to_cpu(ctrl->wLength);
- u16 w_value = le16_to_cpu(ctrl->wValue);
- int value = 0;
- int standard;
- standard = (ctrl->bRequestType & USB_TYPE_MASK)
== USB_TYPE_STANDARD;
- debug("w_value: 0x%x len: 0x%x\n", w_value, len);
- debug("standard: 0x%x ctrl->bRequest: 0x%x f_dfu->dfu_state: 0x%x\n",
standard, ctrl->bRequest, f_dfu->dfu_state);
- if (!standard) {
This function doesn't fit on my screen ... that means it's waaaay too long and shall be split into more functions ;-)
That's also remove the need for your crazy conditional assignment of standard.
switch (f_dfu->dfu_state) {
case DFU_STATE_appIDLE:
switch (ctrl->bRequest) {
case USB_REQ_DFU_GETSTATUS:
handle_getstatus(req);
value = RET_STAT_LEN;
break;
[...]
+/*------------------------------------------------------------------------ -*/ + +static int +dfu_prepare_strings(struct f_dfu *f_dfu, int n) +{
- struct dfu_entity *de = NULL;
- int i = 0;
- f_dfu->strings = calloc(((n + 1) * sizeof(struct usb_string)), 1);
calloc(n, 1) ... that's the same as malloc(), isn't it ?
- if (!f_dfu->strings)
goto enomem;
- for (i = 0; i < n; ++i) {
de = dfu_get_entity(i);
f_dfu->strings[i].s = de->name;
- }
- f_dfu->strings[i].id = 0;
- f_dfu->strings[i].s = NULL;
- return 0;
+enomem:
- while (i)
f_dfu->strings[--i].s = NULL;
- kfree(f_dfu->strings);
- return -ENOMEM;
+}
+static int dfu_prepare_function(struct f_dfu *f_dfu, int n) +{
- struct usb_interface_descriptor *d;
- int i = 0;
- f_dfu->function = calloc(n * sizeof(struct usb_descriptor_header *), 1);
DITTO
- if (!f_dfu->function)
goto enomem;
- for (i = 0; i < n; ++i) {
d = kzalloc(sizeof(*d), GFP_KERNEL);
Why use the kernel alternatives ? just use malloc()/free() ?
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;
f_dfu->function[i] = (struct usb_descriptor_header *)d;
- }
- f_dfu->function[i] = NULL;
- return 0;
+enomem:
- while (i) {
kfree(f_dfu->function[--i]);
f_dfu->function[i] = NULL;
- }
- kfree(f_dfu->function);
- return -ENOMEM;
+}
+static int dfu_bind(struct usb_configuration *c, struct usb_function *f) +{
- struct usb_composite_dev *cdev = c->cdev;
- struct f_dfu *f_dfu = func_to_dfu(f);
- int alt_num = dfu_get_alt_number();
- int rv, id, i;
- id = usb_interface_id(c, f);
- if (id < 0)
return id;
- dfu_intf_runtime.bInterfaceNumber = id;
- f_dfu->dfu_state = DFU_STATE_appIDLE;
- f_dfu->dfu_status = DFU_STATUS_OK;
- rv = dfu_prepare_function(f_dfu, alt_num);
- if (rv)
goto error;
- rv = dfu_prepare_strings(f_dfu, alt_num);
- if (rv)
goto error;
- for (i = 0; i < alt_num; i++) {
id = usb_string_id(cdev);
if (id < 0)
return id;
f_dfu->strings[i].id = id;
((struct usb_interface_descriptor *)f_dfu->function[i])
->iInterface = id;
- }
- stringtab_dfu.strings = f_dfu->strings;
- cdev->req->context = f_dfu;
- return 0;
+error:
- return rv;
So just return the retval ...
+}
Every time I review patches and find multiple small issues, I feel bad :-(

Hi Marek,
Dear Lukasz Majewski,
Support for f_dfu USB function.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[...]
+static const char dfu_name[] = "Device Firmware Upgrade";
+/* static strings, in UTF-8 */ +/*
- dfu_genericiguration specific
generi....what?
Misspelled, fixed.
- */
+static struct usb_string strings_dfu_generic[] = {
- [0].s = dfu_name,
- { } /* end of list */
+};
+static struct usb_gadget_strings stringtab_dfu_generic = {
- .language = 0x0409, /* en-us */
- .strings = strings_dfu_generic,
+};
+static struct usb_gadget_strings *dfu_generic_strings[] = {
- &stringtab_dfu_generic,
- NULL,
+};
+/*
- usb_function specific
- */
+static struct usb_gadget_strings stringtab_dfu = {
- .language = 0x0409, /* en-us */
- /*
* .strings
*
* assigned during initialization,
* depends on number of flash entities
*
*/
+};
+static struct usb_gadget_strings *dfu_strings[] = {
- &stringtab_dfu,
- NULL,
+};
+/*------------------------------------------------------------------------ -*/ + +static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req) +{
- struct f_dfu *f_dfu = req->context;
- dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf,
req->length, f_dfu->blk_seq_num);
- if (req->length == 0) {
puts("DOWNLOAD ... OK\n");
puts("Ctrl+C to exit ...\n");
- }
+}
+static void handle_getstatus(struct usb_request *req) +{
- struct dfu_status *dstat = (struct dfu_status *)req->buf;
- struct f_dfu *f_dfu = req->context;
- switch (f_dfu->dfu_state) {
- case DFU_STATE_dfuDNLOAD_SYNC:
What's this crazy cammel case in here ? :-)
I know, that camel case descriptions are not welcome :-), but those are written in this way for purpose.
dfuDNLOAD_SYNC is the exact state name mentioned at DFU specification. Other states - like dfuDNBUSY are exactly the same.
From mine experience it is more readable. Please consider for example the Fig. A1 - Interface state transition diagram from page 28 at DFU_1.1.pdf spec (link below).
http://www.usb.org/developers/devclass_docs/DFU_1.1.pdf
- case DFU_STATE_dfuDNBUSY:
f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
break;
- case DFU_STATE_dfuMANIFEST_SYNC:
break;
- default:
break;
- }
- /* send status response */
- dstat->bStatus = f_dfu->dfu_status;
- /* FIXME: set dstat->bwPollTimeout */
FIXME ... so fix it ;-)
This is not u-boot related - will be removed
- dstat->bState = f_dfu->dfu_state;
- dstat->iString = 0;
+}
+static void handle_getstate(struct usb_request *req) +{
- struct f_dfu *f_dfu = req->context;
- ((u8 *)req->buf)[0] = f_dfu->dfu_state & 0xff;
Now ... this is ubercrazy ... can't this be made without this typecasting voodoo?
The problem is that req->buf is a void pointer. And the goal is to store dfu state at 8 bits.
- req->actual = sizeof(u8);
+}
[...]
+static int handle_dnload(struct usb_gadget *gadget, u16 len) +{
- struct usb_composite_dev *cdev = get_gadget_data(gadget);
- struct usb_request *req = cdev->req;
- struct f_dfu *f_dfu = req->context;
- if (len == 0)
f_dfu->dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
- req->complete = dnload_request_complete;
- return len;
+}
One newline too much below.
Fixed.
+static int +dfu_handle(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{
- struct usb_gadget *gadget = f->config->cdev->gadget;
- struct usb_request *req = f->config->cdev->req;
- struct f_dfu *f_dfu = f->config->cdev->req->context;
- u16 len = le16_to_cpu(ctrl->wLength);
- u16 w_value = le16_to_cpu(ctrl->wValue);
- int value = 0;
- int standard;
- standard = (ctrl->bRequestType & USB_TYPE_MASK)
==
USB_TYPE_STANDARD; +
- debug("w_value: 0x%x len: 0x%x\n", w_value, len);
- debug("standard: 0x%x ctrl->bRequest: 0x%x
f_dfu->dfu_state: 0x%x\n",
standard, ctrl->bRequest, f_dfu->dfu_state);
- if (!standard) {
This function doesn't fit on my screen ... that means it's waaaay too long and shall be split into more functions ;-)
That's also remove the need for your crazy conditional assignment of standard.
You are correct :-). I will split the if(!standard) clause to two separate. It shall improve readability.
switch (f_dfu->dfu_state) {
case DFU_STATE_appIDLE:
switch (ctrl->bRequest) {
case USB_REQ_DFU_GETSTATUS:
handle_getstatus(req);
value = RET_STAT_LEN;
break;
[...]
+/*------------------------------------------------------------------------ -*/ + +static int +dfu_prepare_strings(struct f_dfu *f_dfu, int n) +{
- struct dfu_entity *de = NULL;
- int i = 0;
- f_dfu->strings = calloc(((n + 1) * sizeof(struct
usb_string)), 1);
calloc(n, 1) ... that's the same as malloc(), isn't it ?
I now wonder how mine mind produced this :-)
Correct version:
f_dfu->strings = calloc(sizeof(struct usb_string), n + 1);
I prefer calloc, since it delivers zeroed memory.
- if (!f_dfu->strings)
goto enomem;
- for (i = 0; i < n; ++i) {
de = dfu_get_entity(i);
f_dfu->strings[i].s = de->name;
- }
- f_dfu->strings[i].id = 0;
- f_dfu->strings[i].s = NULL;
- return 0;
+enomem:
- while (i)
f_dfu->strings[--i].s = NULL;
- kfree(f_dfu->strings);
- return -ENOMEM;
+}
+static int dfu_prepare_function(struct f_dfu *f_dfu, int n) +{
- struct usb_interface_descriptor *d;
- int i = 0;
- f_dfu->function = calloc(n * sizeof(struct
usb_descriptor_header *), 1);
DITTO
As above.
- if (!f_dfu->function)
goto enomem;
- for (i = 0; i < n; ++i) {
d = kzalloc(sizeof(*d), GFP_KERNEL);
Why use the kernel alternatives ? just use malloc()/free() ?
I will use calloc(sizeof(*d), 1) to receive cleared memory.
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;
f_dfu->function[i] = (struct usb_descriptor_header
*)d;
- }
- f_dfu->function[i] = NULL;
- return 0;
+enomem:
- while (i) {
kfree(f_dfu->function[--i]);
f_dfu->function[i] = NULL;
- }
- kfree(f_dfu->function);
- return -ENOMEM;
+}
+static int dfu_bind(struct usb_configuration *c, struct usb_function *f) +{
- struct usb_composite_dev *cdev = c->cdev;
- struct f_dfu *f_dfu = func_to_dfu(f);
- int alt_num = dfu_get_alt_number();
- int rv, id, i;
- id = usb_interface_id(c, f);
- if (id < 0)
return id;
- dfu_intf_runtime.bInterfaceNumber = id;
- f_dfu->dfu_state = DFU_STATE_appIDLE;
- f_dfu->dfu_status = DFU_STATUS_OK;
- rv = dfu_prepare_function(f_dfu, alt_num);
- if (rv)
goto error;
- rv = dfu_prepare_strings(f_dfu, alt_num);
- if (rv)
goto error;
- for (i = 0; i < alt_num; i++) {
id = usb_string_id(cdev);
if (id < 0)
return id;
f_dfu->strings[i].id = id;
((struct usb_interface_descriptor
*)f_dfu->function[i])
->iInterface = id;
- }
- stringtab_dfu.strings = f_dfu->strings;
- cdev->req->context = f_dfu;
- return 0;
+error:
- return rv;
So just return the retval ...
Fixed.
+}
Every time I review patches and find multiple small issues, I feel bad :-(
All will be OK :-)

Dear Lukasz Majewski,
[...]
+static void handle_getstatus(struct usb_request *req) +{
- struct dfu_status *dstat = (struct dfu_status *)req->buf;
- struct f_dfu *f_dfu = req->context;
- switch (f_dfu->dfu_state) {
- case DFU_STATE_dfuDNLOAD_SYNC:
What's this crazy cammel case in here ? :-)
I know, that camel case descriptions are not welcome :-), but those are written in this way for purpose.
dfuDNLOAD_SYNC is the exact state name mentioned at DFU specification. Other states - like dfuDNBUSY are exactly the same.
From mine experience it is more readable. Please consider for example the Fig. A1 - Interface state transition diagram from page 28 at DFU_1.1.pdf spec (link below).
OK, good.
- case DFU_STATE_dfuDNBUSY:
f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
break;
- case DFU_STATE_dfuMANIFEST_SYNC:
break;
- default:
break;
- }
- /* send status response */
- dstat->bStatus = f_dfu->dfu_status;
- /* FIXME: set dstat->bwPollTimeout */
FIXME ... so fix it ;-)
This is not u-boot related - will be removed
- dstat->bState = f_dfu->dfu_state;
- dstat->iString = 0;
+}
+static void handle_getstate(struct usb_request *req) +{
- struct f_dfu *f_dfu = req->context;
- ((u8 *)req->buf)[0] = f_dfu->dfu_state & 0xff;
Now ... this is ubercrazy ... can't this be made without this typecasting voodoo?
The problem is that req->buf is a void pointer. And the goal is to store dfu state at 8 bits.
Sure, but why not make the buffer u8 ?
- req->actual = sizeof(u8);
+}
[...]
+static int +dfu_prepare_strings(struct f_dfu *f_dfu, int n) +{
- struct dfu_entity *de = NULL;
- int i = 0;
- f_dfu->strings = calloc(((n + 1) * sizeof(struct
usb_string)), 1);
calloc(n, 1) ... that's the same as malloc(), isn't it ?
I now wonder how mine mind produced this :-)
Correct version:
f_dfu->strings = calloc(sizeof(struct usb_string), n + 1);
I prefer calloc, since it delivers zeroed memory.
ok, that's better :)
[...]

Hi Marek,
Dear Lukasz Majewski,
[...]
+static void handle_getstatus(struct usb_request *req) +{
- struct dfu_status *dstat = (struct dfu_status
*)req->buf;
- struct f_dfu *f_dfu = req->context;
- switch (f_dfu->dfu_state) {
- case DFU_STATE_dfuDNLOAD_SYNC:
What's this crazy cammel case in here ? :-)
I know, that camel case descriptions are not welcome :-), but those are written in this way for purpose.
dfuDNLOAD_SYNC is the exact state name mentioned at DFU specification. Other states - like dfuDNBUSY are exactly the same.
From mine experience it is more readable. Please consider for example the Fig. A1 - Interface state transition diagram from page 28 at DFU_1.1.pdf spec (link below).
OK, good.
- case DFU_STATE_dfuDNBUSY:
f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
break;
- case DFU_STATE_dfuMANIFEST_SYNC:
break;
- default:
break;
- }
- /* send status response */
- dstat->bStatus = f_dfu->dfu_status;
- /* FIXME: set dstat->bwPollTimeout */
FIXME ... so fix it ;-)
This is not u-boot related - will be removed
- dstat->bState = f_dfu->dfu_state;
- dstat->iString = 0;
+}
+static void handle_getstate(struct usb_request *req) +{
- struct f_dfu *f_dfu = req->context;
- ((u8 *)req->buf)[0] = f_dfu->dfu_state & 0xff;
Now ... this is ubercrazy ... can't this be made without this typecasting voodoo?
The problem is that req->buf is a void pointer. And the goal is to store dfu state at 8 bits.
Sure, but why not make the buffer u8 ?
The req->buf is a member of usb_request defined at gadget.h.
It represents the request from USB. I cannot guarantee, that we will always regard data pointed by buf as u8. For flexibility of gadget usage it is safer to leave it as void pointer.
- req->actual = sizeof(u8);
+}
[...]
+static int +dfu_prepare_strings(struct f_dfu *f_dfu, int n) +{
- struct dfu_entity *de = NULL;
- int i = 0;
- f_dfu->strings = calloc(((n + 1) * sizeof(struct
usb_string)), 1);
calloc(n, 1) ... that's the same as malloc(), isn't it ?
I now wonder how mine mind produced this :-)
Correct version:
f_dfu->strings = calloc(sizeof(struct usb_string), n + 1);
I prefer calloc, since it delivers zeroed memory.
ok, that's better :)
[...]

Dear Lukasz Majewski,
[...]
Now ... this is ubercrazy ... can't this be made without this typecasting voodoo?
The problem is that req->buf is a void pointer. And the goal is to store dfu state at 8 bits.
Sure, but why not make the buffer u8 ?
The req->buf is a member of usb_request defined at gadget.h.
It represents the request from USB. I cannot guarantee, that we will always regard data pointed by buf as u8. For flexibility of gadget usage it is safer to leave it as void pointer.
OK, then it's good :)
[...]
Best regards, Marek Vasut

New, separate driver at ./drivers/dfu has been added. It allows platform and storage independent operation of DFU.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de --- Makefile | 1 + drivers/dfu/Makefile | 43 ++++++++ drivers/dfu/dfu.c | 259 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/dfu.h | 99 +++++++++++++++++++ 4 files changed, 402 insertions(+), 0 deletions(-) create mode 100644 drivers/dfu/Makefile create mode 100644 drivers/dfu/dfu.c create mode 100644 include/dfu.h
diff --git a/Makefile b/Makefile index 0197239..c37dcf3 100644 --- a/Makefile +++ b/Makefile @@ -271,6 +271,7 @@ LIBS += drivers/pci/libpci.o LIBS += drivers/pcmcia/libpcmcia.o LIBS += drivers/power/libpower.o LIBS += drivers/spi/libspi.o +LIBS += drivers/dfu/libdfu.o ifeq ($(CPU),mpc83xx) LIBS += drivers/qe/libqe.o LIBS += arch/powerpc/cpu/mpc8xxx/ddr/libddr.o diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile new file mode 100644 index 0000000..7736485 --- /dev/null +++ b/drivers/dfu/Makefile @@ -0,0 +1,43 @@ +# +# Copyright (C) 2012 Samsung Electronics +# Lukasz Majewski l.majewski@samsung.com +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB = $(obj)libdfu.o + +COBJS-$(CONFIG_DFU_FUNCTION) += dfu.o + +SRCS := $(COBJS-y:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS-y)) + +$(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/dfu/dfu.c b/drivers/dfu/dfu.c new file mode 100644 index 0000000..63733fb --- /dev/null +++ b/drivers/dfu/dfu.c @@ -0,0 +1,259 @@ +/* + * dfu.c -- DFU back-end routines + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz andrzej.p@samsung.com + * Lukasz Majewski l.majewski@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 <fat.h> +#include <dfu.h> +#include <linux/list.h> +#include <linux/compiler.h> + +static LIST_HEAD(dfu_list); +static int dfu_alt_num; + +static int dfu_find_alt_num(char *s) +{ + int i = 0; + + for (; *s; s++) + if (*s == ';') + i++; + + return ++i; +} + +static char *dfu_extract_entity(char** env) +{ + char *s = *env; + + strsep(env, ";"); + return s; +} + +char *dfu_extract_token(char** e, int *n) +{ + char *st = *e; + + debug("%s: %s\n", __func__, st); + + strsep(e, " "); + *n = *e - st; + + return st; +} + +static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE) + dfu_buf[DFU_DATA_BUF_SIZE]; + +int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) +{ + static unsigned char *i_buf; + static int i_blk_seq_num; + long w_size = 0; + int ret = 0; + + debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n", + __func__, dfu->name, buf, size, blk_seq_num, i_buf); + + if (blk_seq_num == 0) { + memset(dfu_buf, '\0', sizeof(dfu_buf)); + i_buf = dfu_buf; + i_blk_seq_num = 0; + } + + if (i_blk_seq_num++ != blk_seq_num) { + printf("%s: Wrong sequence number! [%d] [%d]\n", + __func__, i_blk_seq_num, blk_seq_num); + return -1; + } + + memcpy(i_buf, buf, size); + i_buf += size; + + if (size == 0) { + /* Integrity check (if needed) */ + debug("%s: %s %d [B] CRC32: 0x%x\n", __func__, dfu->name, + i_buf - dfu_buf, crc32(0, dfu_buf, i_buf - dfu_buf)); + + w_size = i_buf - dfu_buf; + ret = dfu->write_medium(dfu, dfu_buf, &w_size); + if (ret) + debug("%s: Write error!\n", __func__); + + i_blk_seq_num = 0; + i_buf = NULL; + return ret; + } + + return ret; +} + +int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) +{ + static unsigned char *i_buf; + static int i_blk_seq_num; + static long r_size; + static u32 crc; + int ret = 0; + + debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n", + __func__, dfu->name, buf, size, blk_seq_num, i_buf); + + if (blk_seq_num == 0) { + i_buf = dfu_buf; + memset(dfu_buf, '\0', sizeof(dfu_buf)); + ret = dfu->read_medium(dfu, i_buf, &r_size); + debug("%s: %s %ld [B]\n", __func__, dfu->name, r_size); + i_blk_seq_num = 0; + /* Integrity check (if needed) */ + crc = crc32(0, dfu_buf, r_size); + } + + if (i_blk_seq_num++ != blk_seq_num) { + printf("%s: Wrong sequence number! [%d] [%d]\n", + __func__, i_blk_seq_num, blk_seq_num); + return -1; + } + + if (r_size >= size) { + memcpy(buf, i_buf, size); + i_buf += size; + r_size -= size; + return size; + } else { + memcpy(buf, i_buf, r_size); + i_buf += r_size; + debug("%s: %s CRC32: 0x%x\n", __func__, dfu->name, crc); + puts("UPLOAD ... done\n"); + puts("Ctrl+C to exit ...\n"); + + i_buf = NULL; + i_blk_seq_num = 0; + crc = 0; + return r_size; + } + return ret; +} + +static int dfu_fill_entity(struct dfu_entity *dfu, char* s, int alt, + char *interface, int num) +{ + char *st = NULL; + int n = 0; + + debug("%s: %s interface: %s num: %d\n", __func__, s, interface, num); + + st = dfu_extract_token(&s, &n); + strncpy((char *) &dfu->name, st, n); + + dfu->dev_num = num; + dfu->alt = alt; + + /* Specific for mmc device */ + if (strcmp(interface, "mmc") == 0) { + if (dfu_fill_entity_mmc(dfu, s)) + return -1; + } else { + printf("dfu: Device %s not supported\n", interface); + return -1; + } + + return 0; +} + +int dfu_config_entities(char *env, char *interface, int num) +{ + struct dfu_entity *dfu; + int i, ret; + char *s; + + dfu_alt_num = dfu_find_alt_num(env); + debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num); + + for (i = 0; i < dfu_alt_num; i++) { + dfu = calloc(sizeof(struct dfu_entity), 1); + + s = dfu_extract_entity(&env); + ret = dfu_fill_entity(dfu, s, i, interface, num); + if (ret) + return -1; + + list_add_tail(&dfu->list, &dfu_list); + } + + return 0; +} + +void dfu_free_entities(void) +{ + struct dfu_entity *dfu = NULL, *p = NULL; + + list_for_each_entry_safe_reverse(dfu, p, &dfu_list, list) { + list_del(&dfu->list); + free(dfu); + } + + INIT_LIST_HEAD(&dfu_list); +} + +static char *dfu_get_dev_type(enum dfu_device_type t) +{ + static char *dev_t[] = {NULL, "MMC", "ONENAND", "NAND" }; + return dev_t[t]; +} + +static char *dfu_get_layout(enum dfu_device_type l) +{ + static char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT" }; + return dfu_layout[l]; +} + +void dfu_show_entities(void) +{ + struct dfu_entity *dfu = NULL; + + puts("DFU alt settings list:\n"); + + list_for_each_entry(dfu, &dfu_list, list) { + printf("dev: %s alt: %d name: %s layout: %s\n", + dfu_get_dev_type(dfu->dev_type), dfu->alt, + dfu->name, dfu_get_layout(dfu->layout)); + } +} + +int dfu_get_alt_number(void) +{ + return dfu_alt_num; +} + +struct dfu_entity *dfu_get_entity(int alt) +{ + struct dfu_entity *dfu = NULL; + + list_for_each_entry(dfu, &dfu_list, list) { + if (dfu->alt == alt) + return dfu; + } + + return NULL; +} diff --git a/include/dfu.h b/include/dfu.h new file mode 100644 index 0000000..f7d823b --- /dev/null +++ b/include/dfu.h @@ -0,0 +1,99 @@ +/* + * dfu.h - DFU flashable area description + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz andrzej.p@samsung.com + * Lukasz Majewski l.majewski@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_ENTITY_H_ +#define __DFU_ENTITY_H_ + +#include <common.h> +#include <linux/list.h> +#include <mmc.h> + +enum dfu_device_type { + MMC = 1, + ONENAND, + NAND +}; + +enum dfu_layout { + RAW_ADDR = 1, + FAT, + EXT, +}; + +struct mmc_internal_data { + /* RAW programming */ + unsigned int lba_start; + unsigned int lba_size; + unsigned int lba_blk_size; + + /* FAT/EXT */ + unsigned int dev; + unsigned int part; +}; + +static inline unsigned int get_mmc_blk_size(int dev) +{ + return find_mmc_device(dev)->read_bl_len; +} + +#define DFU_NAME_SIZE 32 +#define DFU_CMD_BUF_SIZE 128 +#define DFU_DATA_BUF_SIZE (1024*1024*4) /* 4 MiB */ + +struct dfu_entity { + char name[DFU_NAME_SIZE]; + int alt; + void *dev_private; + int dev_num; + enum dfu_device_type dev_type; + enum dfu_layout layout; + + union { + struct mmc_internal_data mmc; + } data; + + int (*read_medium)(struct dfu_entity *dfu, void *buf, long *len); + int (*write_medium)(struct dfu_entity *dfu, void *buf, long *len); + + struct list_head list; +}; + +int dfu_config_entities(char *s, char *interface, int num); +void dfu_free_entities(void); +void dfu_show_entities(void); +int dfu_get_alt_number(void); +struct dfu_entity *dfu_get_entity(int alt); +char *dfu_extract_token(char** e, int *n); + +int dfu_read(struct dfu_entity *de, void *buf, int size, int blk_seq_num); +int dfu_write(struct dfu_entity *de, void *buf, int size, int blk_seq_num); +/* Device specific */ +#ifdef CONFIG_DFU_MMC +extern int dfu_fill_entity_mmc(struct dfu_entity *dfu, char* s); +#else +static inline int dfu_fill_entity_mmc(struct dfu_entity *dfu, char* s) +{ + puts("MMC support not available!\n"); + return -1; +} +#endif +#endif /* __DFU_ENTITY_H_ */

Dear Lukasz Majewski,
New, separate driver at ./drivers/dfu has been added. It allows platform and storage independent operation of DFU.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[...]
+#include <common.h> +#include <malloc.h> +#include <mmc.h> +#include <fat.h> +#include <dfu.h> +#include <linux/list.h> +#include <linux/compiler.h>
+static LIST_HEAD(dfu_list); +static int dfu_alt_num;
+static int dfu_find_alt_num(char *s) +{
- int i = 0;
- for (; *s; s++)
if (*s == ';')
i++;
- return ++i;
+}
+static char *dfu_extract_entity(char** env) +{
- char *s = *env;
- strsep(env, ";");
- return s;
+}
Shall we not make these all generic? They seem to be quite helpful components.
+char *dfu_extract_token(char** e, int *n) +{
- char *st = *e;
- debug("%s: %s\n", __func__, st);
- strsep(e, " ");
- *n = *e - st;
- return st;
+}
+static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE)
dfu_buf[DFU_DATA_BUF_SIZE];
Can we not stack-allocate it with ALLOC_CACHE_ALIGN_BUFFER()?
[...]
diff --git a/include/dfu.h b/include/dfu.h new file mode 100644 index 0000000..f7d823b --- /dev/null +++ b/include/dfu.h
[...]
+struct dfu_entity {
- char name[DFU_NAME_SIZE];
- int alt;
- void *dev_private;
- int dev_num;
- enum dfu_device_type dev_type;
- enum dfu_layout layout;
- union {
struct mmc_internal_data mmc;
This union seems redundant ;-)
[...]

Hi Marek,
Dear Lukasz Majewski,
New, separate driver at ./drivers/dfu has been added. It allows platform and storage independent operation of DFU.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[...]
+#include <common.h> +#include <malloc.h> +#include <mmc.h> +#include <fat.h> +#include <dfu.h> +#include <linux/list.h> +#include <linux/compiler.h>
+static LIST_HEAD(dfu_list); +static int dfu_alt_num;
+static int dfu_find_alt_num(char *s) +{
- int i = 0;
- for (; *s; s++)
if (*s == ';')
i++;
- return ++i;
+}
+static char *dfu_extract_entity(char** env) +{
- char *s = *env;
- strsep(env, ";");
- return s;
+}
Shall we not make these all generic? They seem to be quite helpful components.
It is a good topic for a discussion if those functions shall be moved to ./lib/string.c. I regarded them as a "little" helper functions for parsing DFU alt setting env variable. Those are very short and build with methods exported from string.c
+char *dfu_extract_token(char** e, int *n) +{
- char *st = *e;
- debug("%s: %s\n", __func__, st);
- strsep(e, " ");
- *n = *e - st;
- return st;
+}
+static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE)
dfu_buf[DFU_DATA_BUF_SIZE];
Can we not stack-allocate it with ALLOC_CACHE_ALIGN_BUFFER()?
The dfu_buf is 4 MiB (this is the size of DFU_DATA_BUF_SIZE). I don't think, that allocating it on the stack (for stack allocation the ALLOC_CACHE_ALIGN_BUFFER() is designed) is a good idea.
[...]
diff --git a/include/dfu.h b/include/dfu.h new file mode 100644 index 0000000..f7d823b --- /dev/null +++ b/include/dfu.h
[...]
+struct dfu_entity {
- char name[DFU_NAME_SIZE];
- int alt;
- void *dev_private;
- int dev_num;
- enum dfu_device_type dev_type;
- enum dfu_layout layout;
- union {
struct mmc_internal_data mmc;
This union seems redundant ;-)
Good point :-), but I predict, that DFU will be used to program other memory types (OneNAND, or NAND). To support those, one needs to extend the union with e.g struct onenand_internal_data onenand.
Since we don't have so many memory types, I think that union usage is acceptable.
[...]

Dear Lukasz Majewski,
Hi Marek,
Dear Lukasz Majewski,
New, separate driver at ./drivers/dfu has been added. It allows platform and storage independent operation of DFU.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[...]
+#include <common.h> +#include <malloc.h> +#include <mmc.h> +#include <fat.h> +#include <dfu.h> +#include <linux/list.h> +#include <linux/compiler.h>
+static LIST_HEAD(dfu_list); +static int dfu_alt_num;
+static int dfu_find_alt_num(char *s) +{
- int i = 0;
- for (; *s; s++)
if (*s == ';')
i++;
- return ++i;
+}
+static char *dfu_extract_entity(char** env) +{
- char *s = *env;
- strsep(env, ";");
- return s;
+}
Shall we not make these all generic? They seem to be quite helpful components.
It is a good topic for a discussion if those functions shall be moved to ./lib/string.c. I regarded them as a "little" helper functions for parsing DFU alt setting env variable. Those are very short and build with methods exported from string.c
Good point
+char *dfu_extract_token(char** e, int *n) +{
- char *st = *e;
- debug("%s: %s\n", __func__, st);
- strsep(e, " ");
- *n = *e - st;
- return st;
+}
+static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE)
dfu_buf[DFU_DATA_BUF_SIZE];
Can we not stack-allocate it with ALLOC_CACHE_ALIGN_BUFFER()?
The dfu_buf is 4 MiB (this is the size of DFU_DATA_BUF_SIZE). I don't think, that allocating it on the stack (for stack allocation the ALLOC_CACHE_ALIGN_BUFFER() is designed) is a good idea.
Heh, agreed :-)
[...]
diff --git a/include/dfu.h b/include/dfu.h new file mode 100644 index 0000000..f7d823b --- /dev/null +++ b/include/dfu.h
[...]
+struct dfu_entity {
- char name[DFU_NAME_SIZE];
- int alt;
- void *dev_private;
- int dev_num;
- enum dfu_device_type dev_type;
- enum dfu_layout layout;
- union {
struct mmc_internal_data mmc;
This union seems redundant ;-)
Good point :-), but I predict, that DFU will be used to program other memory types (OneNAND, or NAND). To support those, one needs to extend the union with e.g struct onenand_internal_data onenand.
Since we don't have so many memory types, I think that union usage is acceptable.
And will those pieces be implemented any soon ? :)
[...]
Best regards, Marek Vasut

Hi Marek,
Dear Lukasz Majewski,
Hi Marek,
Dear Lukasz Majewski,
New, separate driver at ./drivers/dfu has been added. It allows platform and storage independent operation of DFU.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[...]
+#include <common.h> +#include <malloc.h> +#include <mmc.h> +#include <fat.h> +#include <dfu.h> +#include <linux/list.h> +#include <linux/compiler.h>
+static LIST_HEAD(dfu_list); +static int dfu_alt_num;
+static int dfu_find_alt_num(char *s) +{
- int i = 0;
- for (; *s; s++)
if (*s == ';')
i++;
- return ++i;
+}
+static char *dfu_extract_entity(char** env) +{
- char *s = *env;
- strsep(env, ";");
- return s;
+}
Shall we not make these all generic? They seem to be quite helpful components.
It is a good topic for a discussion if those functions shall be moved to ./lib/string.c. I regarded them as a "little" helper functions for parsing DFU alt setting env variable. Those are very short and build with methods exported from string.c
Good point
+char *dfu_extract_token(char** e, int *n) +{
- char *st = *e;
- debug("%s: %s\n", __func__, st);
- strsep(e, " ");
- *n = *e - st;
- return st;
+}
+static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE)
dfu_buf[DFU_DATA_BUF_SIZE];
Can we not stack-allocate it with ALLOC_CACHE_ALIGN_BUFFER()?
The dfu_buf is 4 MiB (this is the size of DFU_DATA_BUF_SIZE). I don't think, that allocating it on the stack (for stack allocation the ALLOC_CACHE_ALIGN_BUFFER() is designed) is a good idea.
Heh, agreed :-)
[...]
diff --git a/include/dfu.h b/include/dfu.h new file mode 100644 index 0000000..f7d823b --- /dev/null +++ b/include/dfu.h
[...]
+struct dfu_entity {
- char name[DFU_NAME_SIZE];
- int alt;
- void *dev_private;
- int dev_num;
- enum dfu_device_type dev_type;
- enum dfu_layout layout;
- union {
struct mmc_internal_data mmc;
This union seems redundant ;-)
Good point :-), but I predict, that DFU will be used to program other memory types (OneNAND, or NAND). To support those, one needs to extend the union with e.g struct onenand_internal_data onenand.
Since we don't have so many memory types, I think that union usage is acceptable.
And will those pieces be implemented any soon ? :)
:-). Since I'm the OneNAND custodian I shall keep in the back of my head, that support for this memory is important :-)
[...]
Best regards, Marek Vasut

Dear Lukasz Majewski,
[...]
And will those pieces be implemented any soon ? :)
: :-). Since I'm the OneNAND custodian I shall keep in the back of my
head, that support for this memory is important :-)
Good, will look forward to it :)
Best regards, Marek Vasut

On Tuesday 03 July 2012 05:38:07 Lukasz Majewski wrote:
puts("UPLOAD ... done\n");
puts("Ctrl+C to exit ...\n");
combine into a single puts() ?
+static int dfu_fill_entity(struct dfu_entity *dfu, char* s, int alt,
char *interface, int num)
+{
- char *st = NULL;
- int n = 0;
- debug("%s: %s interface: %s num: %d\n", __func__, s, interface, num);
- st = dfu_extract_token(&s, &n);
- strncpy((char *) &dfu->name, st, n);
what's with the pointless cast ? just do: strncpy(dfu->name, st, n);
also, "n" here is wrong. it should be sizeof(dfu->name), not (presumably) the length of st. considering this is the 2nd time i noticed this bug in the dfu patchset, you might want to do a grep on your patches to see if your other string related usage is wrong.
+void dfu_free_entities(void) +{
- struct dfu_entity *dfu = NULL, *p = NULL;
no point in assigning to NULL here
+static char *dfu_get_dev_type(enum dfu_device_type t)
static const char *...
+{
- static char *dev_t[] = {NULL, "MMC", "ONENAND", "NAND" };
static const char * const dev_t[] = {...}
+static char *dfu_get_layout(enum dfu_device_type l)
static const char *...
+{
- static char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT" };
static const char * const dfu_layout[] = {...}
+void dfu_show_entities(void) +{
- struct dfu_entity *dfu = NULL;
no point in assigning to NULL
+struct dfu_entity *dfu_get_entity(int alt) +{
- struct dfu_entity *dfu = NULL;
no point in assigning to NULL -mike

Dear Mike Frysinger,
On Tuesday 03 July 2012 05:38:07 Lukasz Majewski wrote:
puts("UPLOAD ... done\n");
puts("Ctrl+C to exit ...\n");
combine into a single puts() ?
Ok, will be done
+static int dfu_fill_entity(struct dfu_entity *dfu, char* s, int alt,
char *interface, int num)
+{
- char *st = NULL;
- int n = 0;
- debug("%s: %s interface: %s num: %d\n", __func__, s,
interface, num); +
- st = dfu_extract_token(&s, &n);
- strncpy((char *) &dfu->name, st, n);
what's with the pointless cast ? just do: strncpy(dfu->name, st, n);
Yes, you are right.
also, "n" here is wrong. it should be sizeof(dfu->name), not (presumably) the length of st. considering this is the 2nd time i noticed this bug in the dfu patchset, you might want to do a grep on your patches to see if your other string related usage is wrong.
I will double-check the string operations.
+void dfu_free_entities(void) +{
- struct dfu_entity *dfu = NULL, *p = NULL;
no point in assigning to NULL here
Will remove.
+static char *dfu_get_dev_type(enum dfu_device_type t)
static const char *...
Will correct.
+{
- static char *dev_t[] = {NULL, "MMC", "ONENAND", "NAND" };
static const char * const dev_t[] = {...}
Ok
+static char *dfu_get_layout(enum dfu_device_type l)
static const char *...
Ok
+{
- static char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT",
"EXT" };
static const char * const dfu_layout[] = {...}
OK
+void dfu_show_entities(void) +{
- struct dfu_entity *dfu = NULL;
no point in assigning to NULL
OK
+struct dfu_entity *dfu_get_entity(int alt) +{
- struct dfu_entity *dfu = NULL;
no point in assigning to NULL
OK
-mike

Support for MMC storage devices to work with DFU framework.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de --- drivers/dfu/Makefile | 1 + drivers/dfu/dfu_mmc.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 0 deletions(-) create mode 100644 drivers/dfu/dfu_mmc.c
diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index 7736485..7b717bc 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -26,6 +26,7 @@ include $(TOPDIR)/config.mk LIB = $(obj)libdfu.o
COBJS-$(CONFIG_DFU_FUNCTION) += dfu.o +COBJS-$(CONFIG_DFU_MMC) += dfu_mmc.o
SRCS := $(COBJS-y:.o=.c) OBJS := $(addprefix $(obj),$(COBJS-y)) diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c new file mode 100644 index 0000000..3151fbc --- /dev/null +++ b/drivers/dfu/dfu_mmc.c @@ -0,0 +1,126 @@ +/* + * dfu.c -- DFU back-end routines + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz andrzej.p@samsung.com + * Lukasz Majewski l.majewski@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 <dfu.h> + +int dfu_write_medium_mmc(struct dfu_entity *dfu, void *buf, long *len) +{ + ALLOC_CACHE_ALIGN_BUFFER(char, cmd_buf, DFU_CMD_BUF_SIZE); + + memset(cmd_buf, '\0', sizeof(cmd_buf)); + + switch (dfu->layout) { + case RAW_ADDR: + sprintf(cmd_buf, "mmc write 0x%x %x %x", (unsigned int) buf, + dfu->data.mmc.lba_start, dfu->data.mmc.lba_size); + break; + case FAT: + sprintf(cmd_buf, "fatwrite mmc %d:%d 0x%x %s %lx", + dfu->data.mmc.dev, dfu->data.mmc.part, + (unsigned int) buf, dfu->name, *len); + break; + default: + printf("%s: Wrong layout!\n", __func__); + } + + debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); + run_command(cmd_buf, 0); + + return 0; +} + +int dfu_read_medium_mmc(struct dfu_entity *dfu, void *buf, long *len) +{ + ALLOC_CACHE_ALIGN_BUFFER(char, cmd_buf, DFU_CMD_BUF_SIZE); + char *str_env = NULL; + int ret = 0; + + memset(cmd_buf, '\0', sizeof(cmd_buf)); + + switch (dfu->layout) { + case RAW_ADDR: + sprintf(cmd_buf, "mmc read 0x%x %x %x", (unsigned int) buf, + dfu->data.mmc.lba_start, dfu->data.mmc.lba_size); + + *len = dfu->data.mmc.lba_blk_size * dfu->data.mmc.lba_size; + break; + case FAT: + sprintf(cmd_buf, "fatload mmc %d:%d 0x%x %s", + dfu->data.mmc.dev, dfu->data.mmc.part, + (unsigned int) buf, dfu->name); + break; + default: + printf("%s: Wrong layout!\n", __func__); + } + + debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); + + ret = run_command(cmd_buf, 0); + if (ret) { + puts("dfu: Read error!\n"); + return ret; + } + + if (dfu->layout != RAW_ADDR) { + str_env = getenv("filesize"); + if (str_env == NULL) { + puts("dfu: Wrong file size!\n"); + return -1; + } + + *len = simple_strtoul(str_env, NULL, 16); + } + return ret; +} + +int dfu_fill_entity_mmc(struct dfu_entity *dfu, char* s) +{ + char *st = NULL; + int n = 0; + + dfu->dev_type = MMC; + st = dfu_extract_token(&s, &n); + + if (!strncmp(st, "mmc", n)) { + dfu->layout = RAW_ADDR; + + dfu->data.mmc.lba_start = simple_strtoul(s, &s, 16); + dfu->data.mmc.lba_size = simple_strtoul(++s, &s, 16); + dfu->data.mmc.lba_blk_size = get_mmc_blk_size(dfu->dev_num); + + } else if (!strncmp(st, "fat", n)) { + dfu->layout = FAT; + + dfu->data.mmc.dev = simple_strtoul(s, &s, 10); + dfu->data.mmc.part = simple_strtoul(++s, &s, 10); + + } else { + printf("%s: Wrong memory layout!\n", __func__); + } + + dfu->read_medium = dfu_read_medium_mmc; + dfu->write_medium = dfu_write_medium_mmc; + + return 0; +}

Dear Lukasz Majewski,
Support for MMC storage devices to work with DFU framework.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[...]
+#include <common.h> +#include <malloc.h> +#include <dfu.h>
+int dfu_write_medium_mmc(struct dfu_entity *dfu, void *buf, long *len) +{
- ALLOC_CACHE_ALIGN_BUFFER(char, cmd_buf, DFU_CMD_BUF_SIZE);
- memset(cmd_buf, '\0', sizeof(cmd_buf));
- switch (dfu->layout) {
- case RAW_ADDR:
sprintf(cmd_buf, "mmc write 0x%x %x %x", (unsigned int) buf,
dfu->data.mmc.lba_start, dfu->data.mmc.lba_size);
break;
- case FAT:
sprintf(cmd_buf, "fatwrite mmc %d:%d 0x%x %s %lx",
dfu->data.mmc.dev, dfu->data.mmc.part,
(unsigned int) buf, dfu->name, *len);
break;
- default:
printf("%s: Wrong layout!\n", __func__);
- }
- debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
- run_command(cmd_buf, 0);
Holy Moly ... can we not make this into simple calls to those subsystems ? Instead invoking command is crazy ;-)
[...]
Best regards, Marek Vasut

On Tue, Jul 03, 2012 at 11:29:31PM +0200, Marek Vasut wrote:
Dear Lukasz Majewski,
Support for MMC storage devices to work with DFU framework.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[...]
+#include <common.h> +#include <malloc.h> +#include <dfu.h>
+int dfu_write_medium_mmc(struct dfu_entity *dfu, void *buf, long *len) +{
- ALLOC_CACHE_ALIGN_BUFFER(char, cmd_buf, DFU_CMD_BUF_SIZE);
- memset(cmd_buf, '\0', sizeof(cmd_buf));
- switch (dfu->layout) {
- case RAW_ADDR:
sprintf(cmd_buf, "mmc write 0x%x %x %x", (unsigned int) buf,
dfu->data.mmc.lba_start, dfu->data.mmc.lba_size);
break;
- case FAT:
sprintf(cmd_buf, "fatwrite mmc %d:%d 0x%x %s %lx",
dfu->data.mmc.dev, dfu->data.mmc.part,
(unsigned int) buf, dfu->name, *len);
break;
- default:
printf("%s: Wrong layout!\n", __func__);
- }
- debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
- run_command(cmd_buf, 0);
Holy Moly ... can we not make this into simple calls to those subsystems ? Instead invoking command is crazy ;-)
Are they really simple? There's a few other places we do this, and so long as it's documented that DFU depends on CONFIG_FAT_WRITE for writing to fat and so forth.

Dear Tom Rini,
On Tue, Jul 03, 2012 at 11:29:31PM +0200, Marek Vasut wrote:
Dear Lukasz Majewski,
Support for MMC storage devices to work with DFU framework.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[...]
+#include <common.h> +#include <malloc.h> +#include <dfu.h>
+int dfu_write_medium_mmc(struct dfu_entity *dfu, void *buf, long *len) +{
- ALLOC_CACHE_ALIGN_BUFFER(char, cmd_buf, DFU_CMD_BUF_SIZE);
- memset(cmd_buf, '\0', sizeof(cmd_buf));
- switch (dfu->layout) {
- case RAW_ADDR:
sprintf(cmd_buf, "mmc write 0x%x %x %x", (unsigned int) buf,
dfu->data.mmc.lba_start, dfu->data.mmc.lba_size);
break;
- case FAT:
sprintf(cmd_buf, "fatwrite mmc %d:%d 0x%x %s %lx",
dfu->data.mmc.dev, dfu->data.mmc.part,
(unsigned int) buf, dfu->name, *len);
break;
- default:
printf("%s: Wrong layout!\n", __func__);
- }
- debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
- run_command(cmd_buf, 0);
Holy Moly ... can we not make this into simple calls to those subsystems ? Instead invoking command is crazy ;-)
Are they really simple? There's a few other places we do this, and so long as it's documented that DFU depends on CONFIG_FAT_WRITE for writing to fat and so forth.
Well ain't it easier to call fat_write() or similar?
Best regards, Marek Vasut

On Wed, Jul 04, 2012 at 12:01:27AM +0200, Marek Vasut wrote:
Dear Tom Rini,
On Tue, Jul 03, 2012 at 11:29:31PM +0200, Marek Vasut wrote:
Dear Lukasz Majewski,
Support for MMC storage devices to work with DFU framework.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[...]
+#include <common.h> +#include <malloc.h> +#include <dfu.h>
+int dfu_write_medium_mmc(struct dfu_entity *dfu, void *buf, long *len) +{
- ALLOC_CACHE_ALIGN_BUFFER(char, cmd_buf, DFU_CMD_BUF_SIZE);
- memset(cmd_buf, '\0', sizeof(cmd_buf));
- switch (dfu->layout) {
- case RAW_ADDR:
sprintf(cmd_buf, "mmc write 0x%x %x %x", (unsigned int) buf,
dfu->data.mmc.lba_start, dfu->data.mmc.lba_size);
break;
- case FAT:
sprintf(cmd_buf, "fatwrite mmc %d:%d 0x%x %s %lx",
dfu->data.mmc.dev, dfu->data.mmc.part,
(unsigned int) buf, dfu->name, *len);
break;
- default:
printf("%s: Wrong layout!\n", __func__);
- }
- debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
- run_command(cmd_buf, 0);
Holy Moly ... can we not make this into simple calls to those subsystems ? Instead invoking command is crazy ;-)
Are they really simple? There's a few other places we do this, and so long as it's documented that DFU depends on CONFIG_FAT_WRITE for writing to fat and so forth.
Well ain't it easier to call fat_write() or similar?
Assuming that most of the logic in do_fat_fswrite is needed, no. And I think a good portion of it is, at first glance at least.

Dear Tom Rini,
[...]
- debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
- run_command(cmd_buf, 0);
Holy Moly ... can we not make this into simple calls to those subsystems ? Instead invoking command is crazy ;-)
Are they really simple? There's a few other places we do this, and so long as it's documented that DFU depends on CONFIG_FAT_WRITE for writing to fat and so forth.
Well ain't it easier to call fat_write() or similar?
Assuming that most of the logic in do_fat_fswrite is needed, no. And I think a good portion of it is, at first glance at least.
Abstracting it out into a function won't cut it?
Best regards, Marek Vasut

On Wed, Jul 04, 2012 at 12:31:14AM +0200, Marek Vasut wrote:
Dear Tom Rini,
[...]
- debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
- run_command(cmd_buf, 0);
Holy Moly ... can we not make this into simple calls to those subsystems ? Instead invoking command is crazy ;-)
Are they really simple? There's a few other places we do this, and so long as it's documented that DFU depends on CONFIG_FAT_WRITE for writing to fat and so forth.
Well ain't it easier to call fat_write() or similar?
Assuming that most of the logic in do_fat_fswrite is needed, no. And I think a good portion of it is, at first glance at least.
Abstracting it out into a function won't cut it?
My inclination would be to say that seems a bit sillier than just using run_command(...) to call the existing command.

On 07/03/2012 04:33 PM, Tom Rini wrote:
On Wed, Jul 04, 2012 at 12:31:14AM +0200, Marek Vasut wrote:
Dear Tom Rini,
[...]
> + debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); > + run_command(cmd_buf, 0);
Holy Moly ... can we not make this into simple calls to those subsystems ? Instead invoking command is crazy ;-)
Are they really simple? There's a few other places we do this, and so long as it's documented that DFU depends on CONFIG_FAT_WRITE for writing to fat and so forth.
Well ain't it easier to call fat_write() or similar?
Assuming that most of the logic in do_fat_fswrite is needed, no. And I think a good portion of it is, at first glance at least.
Abstracting it out into a function won't cut it?
My inclination would be to say that seems a bit sillier than just using run_command(...) to call the existing command.
Abstracting into a function definitely means the compiler will be able to type-check all the arguments to the function. Is the same true using run_command; I assume everything gets serialized to an array of strings and hence any validation is deferred until run-time then?

On Tue, Jul 03, 2012 at 05:07:04PM -0600, Stephen Warren wrote:
On 07/03/2012 04:33 PM, Tom Rini wrote:
On Wed, Jul 04, 2012 at 12:31:14AM +0200, Marek Vasut wrote:
Dear Tom Rini,
[...]
>> + debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); >> + run_command(cmd_buf, 0); > > Holy Moly ... can we not make this into simple calls to those > subsystems ? Instead invoking command is crazy ;-)
Are they really simple? There's a few other places we do this, and so long as it's documented that DFU depends on CONFIG_FAT_WRITE for writing to fat and so forth.
Well ain't it easier to call fat_write() or similar?
Assuming that most of the logic in do_fat_fswrite is needed, no. And I think a good portion of it is, at first glance at least.
Abstracting it out into a function won't cut it?
My inclination would be to say that seems a bit sillier than just using run_command(...) to call the existing command.
Abstracting into a function definitely means the compiler will be able to type-check all the arguments to the function. Is the same true using run_command; I assume everything gets serialized to an array of strings and hence any validation is deferred until run-time then?
string is constructed with sprintf and that gives us some type checking. Only cast today is buf to an unsigned int. And the information either comes from tools that should/must have done sanity checking or from the 'dfu' command which again should (didn't verify that patch yet) have done sanity checking to get us this far.

On 07/03/2012 05:38 PM, Tom Rini wrote:
On Tue, Jul 03, 2012 at 05:07:04PM -0600, Stephen Warren wrote:
On 07/03/2012 04:33 PM, Tom Rini wrote:
On Wed, Jul 04, 2012 at 12:31:14AM +0200, Marek Vasut wrote:
Dear Tom Rini,
[...]
>>> + debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); >>> + run_command(cmd_buf, 0); >> >> Holy Moly ... can we not make this into simple calls to those >> subsystems ? Instead invoking command is crazy ;-) > > Are they really simple? There's a few other places we do this, and so > long as it's documented that DFU depends on CONFIG_FAT_WRITE for > writing to fat and so forth.
Well ain't it easier to call fat_write() or similar?
Assuming that most of the logic in do_fat_fswrite is needed, no. And I think a good portion of it is, at first glance at least.
Abstracting it out into a function won't cut it?
My inclination would be to say that seems a bit sillier than just using run_command(...) to call the existing command.
Abstracting into a function definitely means the compiler will be able to type-check all the arguments to the function. Is the same true using run_command; I assume everything gets serialized to an array of strings and hence any validation is deferred until run-time then?
string is constructed with sprintf and that gives us some type checking. Only cast today is buf to an unsigned int. And the information either comes from tools that should/must have done sanity checking or from the 'dfu' command which again should (didn't verify that patch yet) have done sanity checking to get us this far.
So that checks the parameters against the sprintf format string, but what checks that you chose the right format string for the command?

Dear Tom Rini,
On Wed, Jul 04, 2012 at 12:31:14AM +0200, Marek Vasut wrote:
Dear Tom Rini,
[...]
> + debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); > + run_command(cmd_buf, 0);
Holy Moly ... can we not make this into simple calls to those subsystems ? Instead invoking command is crazy ;-)
Are they really simple? There's a few other places we do this, and so long as it's documented that DFU depends on CONFIG_FAT_WRITE for writing to fat and so forth.
Well ain't it easier to call fat_write() or similar?
Assuming that most of the logic in do_fat_fswrite is needed, no. And I think a good portion of it is, at first glance at least.
Abstracting it out into a function won't cut it?
My inclination would be to say that seems a bit sillier than just using run_command(...) to call the existing command.
Whose's syntax will likely soon change, breaking all this :-(
Best regards, Marek Vasut

On Tuesday 03 July 2012 20:13:37 Marek Vasut wrote:
Dear Tom Rini,
On Wed, Jul 04, 2012 at 12:31:14AM +0200, Marek Vasut wrote:
Dear Tom Rini,
> > + debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); > > + run_command(cmd_buf, 0); > > Holy Moly ... can we not make this into simple calls to those > subsystems ? Instead invoking command is crazy ;-)
Are they really simple? There's a few other places we do this, and so long as it's documented that DFU depends on CONFIG_FAT_WRITE for writing to fat and so forth.
Well ain't it easier to call fat_write() or similar?
Assuming that most of the logic in do_fat_fswrite is needed, no. And I think a good portion of it is, at first glance at least.
Abstracting it out into a function won't cut it?
My inclination would be to say that seems a bit sillier than just using run_command(...) to call the existing command.
Whose's syntax will likely soon change, breaking all this :-(
and would be pretty much impossible to detect at compile time. extending the API for code to call is the right answer ... abusing the shell at runtime from other code should be an absolute last resort. -mike

Hi Marek,
Dear Tom Rini,
On Tue, Jul 03, 2012 at 11:29:31PM +0200, Marek Vasut wrote:
Dear Lukasz Majewski,
Support for MMC storage devices to work with DFU framework.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[...]
+#include <common.h> +#include <malloc.h> +#include <dfu.h>
+int dfu_write_medium_mmc(struct dfu_entity *dfu, void *buf, long *len) +{
- ALLOC_CACHE_ALIGN_BUFFER(char, cmd_buf,
DFU_CMD_BUF_SIZE); +
- memset(cmd_buf, '\0', sizeof(cmd_buf));
- switch (dfu->layout) {
- case RAW_ADDR:
sprintf(cmd_buf, "mmc write 0x%x %x %x",
(unsigned int) buf,
dfu->data.mmc.lba_start,
dfu->data.mmc.lba_size);
break;
- case FAT:
sprintf(cmd_buf, "fatwrite mmc %d:%d 0x%x %s
%lx",
dfu->data.mmc.dev, dfu->data.mmc.part,
(unsigned int) buf, dfu->name, *len);
break;
- default:
printf("%s: Wrong layout!\n", __func__);
- }
- debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
- run_command(cmd_buf, 0);
Holy Moly ... can we not make this into simple calls to those subsystems ? Instead invoking command is crazy ;-)
Are they really simple? There's a few other places we do this, and so long as it's documented that DFU depends on CONFIG_FAT_WRITE for writing to fat and so forth.
Well ain't it easier to call fat_write() or similar?
I've decided to use run_command on a purpose.
This call provides clean and reliable API. It is very unlikely that the mmc write <dev> <addr> <start> <size> command will change (or any other). On the other hand the fields of struct mmc are changed from time to time.
Moreover, mmc drivers are also a subject to change (like adding dw_mmc recently). Using run_command also takes the burden of mmc_init() related calls.
Of course the run_command's downside is the speed of execution. But is it so important when one considers, the firmware update?
Side note: DFU uses only EP0 (for transfer and configuration), so this is rather slow communication link.
I'm open for discussion.

Dear Lukasz Majewski,
[...]
Holy Moly ... can we not make this into simple calls to those subsystems ? Instead invoking command is crazy ;-)
Are they really simple? There's a few other places we do this, and so long as it's documented that DFU depends on CONFIG_FAT_WRITE for writing to fat and so forth.
Well ain't it easier to call fat_write() or similar?
I've decided to use run_command on a purpose.
This call provides clean and reliable API. It is very unlikely that the mmc write <dev> <addr> <start> <size> command will change (or any other). On the other hand the fields of struct mmc are changed from time to time.
I'm afraid it might change with the driver model soon.
Moreover, mmc drivers are also a subject to change (like adding dw_mmc recently). Using run_command also takes the burden of mmc_init() related calls.
Of course the run_command's downside is the speed of execution. But is it so important when one considers, the firmware update?
But as Stephen pointed out, the type checking is much better when used as function.
Side note: DFU uses only EP0 (for transfer and configuration), so this is rather slow communication link.
I see
I'm open for discussion.
Yes please, I think I started some bad flamewar in here :/
Best regards, Marek Vasut

Hi Marek,
Dear Lukasz Majewski,
[...]
Holy Moly ... can we not make this into simple calls to those subsystems ? Instead invoking command is crazy ;-)
Are they really simple? There's a few other places we do this, and so long as it's documented that DFU depends on CONFIG_FAT_WRITE for writing to fat and so forth.
Well ain't it easier to call fat_write() or similar?
I've decided to use run_command on a purpose.
This call provides clean and reliable API. It is very unlikely that the mmc write <dev> <addr> <start> <size> command will change (or any other). On the other hand the fields of struct mmc are changed from time to time.
I'm afraid it might change with the driver model soon.
You have probably more information than I about the driver model :-)
Since I know u-boot this API was stable. If it changes, I will adjust the sprintf :-)
Moreover, mmc drivers are also a subject to change (like adding dw_mmc recently). Using run_command also takes the burden of mmc_init() related calls.
Of course the run_command's downside is the speed of execution. But is it so important when one considers, the firmware update?
But as Stephen pointed out, the type checking is much better when used as function.
Yes, I agree about the type check.
Contrary, the cmd_mmc.c code is not checking the correctness of passed data. It performs strncmp, then simple_strtoul and with this parameter calls mmc->block_dev.block_read(). For this command such behavior is acceptable.
Side note: DFU uses only EP0 (for transfer and configuration), so this is rather slow communication link.
I see
I'm open for discussion.
Yes please, I think I started some bad flamewar in here :/
Maybe we come up with a better solution thanks to that :-).

Support for u-boot's command line command "dfu <interface> <dev> [list]".
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de --- common/Makefile | 1 + common/cmd_dfu.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 0 deletions(-) create mode 100644 common/cmd_dfu.c
diff --git a/common/Makefile b/common/Makefile index 6e23baa..de43ead 100644 --- a/common/Makefile +++ b/common/Makefile @@ -186,6 +186,7 @@ COBJS-$(CONFIG_MENU) += menu.o COBJS-$(CONFIG_MODEM_SUPPORT) += modem.o COBJS-$(CONFIG_UPDATE_TFTP) += update.o COBJS-$(CONFIG_USB_KEYBOARD) += usb_kbd.o +COBJS-$(CONFIG_CMD_DFU) += cmd_dfu.o endif
ifdef CONFIG_SPL_BUILD diff --git a/common/cmd_dfu.c b/common/cmd_dfu.c new file mode 100644 index 0000000..ceb0f54 --- /dev/null +++ b/common/cmd_dfu.c @@ -0,0 +1,81 @@ +/* + * cmd_dfu.c -- dfu command + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz andrzej.p@samsung.com + * Lukasz Majewski l.majewski@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 <malloc.h> +#include <dfu.h> +#include <asm/errno.h> +#include <g_dnl.h> + +int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *str_env = NULL, *env_bkp = NULL; + static char *s = "dfu"; + int ret = 0; + + if (argc < 3) + return CMD_RET_USAGE; + + str_env = getenv("dfu_alt_info"); + if (str_env == NULL) { + printf("%s: "dfu_alt_info" env variable not defined!\n", + __func__); + return CMD_RET_FAILURE; + } + + env_bkp = strdup(str_env); + ret = dfu_config_entities(env_bkp, argv[1], + (int)simple_strtoul(argv[2], NULL, 10)); + if (ret) + return CMD_RET_FAILURE; + + if (strcmp(argv[3], "list") == 0) { + dfu_show_entities(); + dfu_free_entities(); + free(env_bkp); + return CMD_RET_SUCCESS; + } + + board_usb_init(); + g_dnl_init(s); + while (1) { + if (ctrlc()) + goto exit; + + usb_gadget_handle_interrupts(); + } +exit: + g_dnl_cleanup(); + dfu_free_entities(); + free(env_bkp); + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu, + "Device Firmware Upgrade", + "<interface> <dev> [list]\n" + " - device firmware upgrade on a device <dev>\n" + " attached to interface <interface>\n" + " [list] - list available alt settings" +);

Dear Lukasz Majewski,
Support for u-boot's command line command "dfu <interface> <dev> [list]".
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[...]
+int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{
- char *str_env = NULL, *env_bkp = NULL;
- static char *s = "dfu";
- int ret = 0;
- if (argc < 3)
return CMD_RET_USAGE;
- str_env = getenv("dfu_alt_info");
- if (str_env == NULL) {
printf("%s: \"dfu_alt_info\" env variable not defined!\n",
__func__);
I was always curious if it's not possible to do something like
puts(__func__ "rest of string");
Maybe it'd help the overhead a bit? Certainly, it's beyond the scope of this patchset, I'm just curious :)
return CMD_RET_FAILURE;
- }
[...]
Best regards, Marek Vasut

Hi Marek,
Dear Lukasz Majewski,
Support for u-boot's command line command "dfu <interface> <dev> [list]".
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[...]
+int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{
- char *str_env = NULL, *env_bkp = NULL;
- static char *s = "dfu";
- int ret = 0;
- if (argc < 3)
return CMD_RET_USAGE;
- str_env = getenv("dfu_alt_info");
- if (str_env == NULL) {
printf("%s: \"dfu_alt_info\" env variable not
defined!\n",
__func__);
I was always curious if it's not possible to do something like
puts(__func__ "rest of string");
Maybe it'd help the overhead a bit? Certainly, it's beyond the scope of this patchset, I'm just curious :)
It is a good idea, since many error/info messages are supposed to produce following output:
"dfu_write: Not enough space!"
Putting there the __func__ name would improve structure and speed up finding right place.
return CMD_RET_FAILURE;
- }
[...]
Best regards, Marek Vasut

Dear Lukasz Majewski,
Hi Marek,
Dear Lukasz Majewski,
Support for u-boot's command line command "dfu <interface> <dev> [list]".
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[...]
+int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{
- char *str_env = NULL, *env_bkp = NULL;
- static char *s = "dfu";
- int ret = 0;
- if (argc < 3)
return CMD_RET_USAGE;
- str_env = getenv("dfu_alt_info");
- if (str_env == NULL) {
printf("%s: \"dfu_alt_info\" env variable not
defined!\n",
__func__);
I was always curious if it's not possible to do something like
puts(__func__ "rest of string");
Maybe it'd help the overhead a bit? Certainly, it's beyond the scope of this patchset, I'm just curious :)
It is a good idea, since many error/info messages are supposed to produce following output:
"dfu_write: Not enough space!"
Putting there the __func__ name would improve structure and speed up finding right place.
And if you want to use even __LINE__, look up __stringify patch in the ML archives ;-)
return CMD_RET_FAILURE;
- }
[...]
Best regards, Marek Vasut
Best regards, Marek Vasut

On Wednesday 04 July 2012 10:39:20 Marek Vasut wrote:
Putting there the __func__ name would improve structure and speed up finding right place.
And if you want to use even __LINE__, look up __stringify patch in the ML archives ;-)
ugh, no, let's not use __LINE__ anywhere other than debug(). it has no business in code we ship as it is pointless bloated noise. -mike

Dear Mike Frysinger,
On Wednesday 04 July 2012 10:39:20 Marek Vasut wrote:
Putting there the __func__ name would improve structure and speed up finding right place.
And if you want to use even __LINE__, look up __stringify patch in the ML archives ;-)
ugh, no, let's not use __LINE__ anywhere other than debug(). it has no business in code we ship as it is pointless bloated noise.
Helps find out the problematic place in code ...
-mike
Best regards, Marek Vasut

On Friday 20 July 2012 07:33:49 Marek Vasut wrote:
Dear Mike Frysinger,
On Wednesday 04 July 2012 10:39:20 Marek Vasut wrote:
Putting there the __func__ name would improve structure and speed up finding right place.
And if you want to use even __LINE__, look up __stringify patch in the ML archives ;-)
ugh, no, let's not use __LINE__ anywhere other than debug(). it has no business in code we ship as it is pointless bloated noise.
Helps find out the problematic place in code ...
except the code changes thus invalidating the line numbers, and how often are you putting the same string in multiple places that you can't easily coordinate where it came from ? if people are using the same exact string in multiple places, that sounds like a different problem. -mike

Dear Mike Frysinger,
On Friday 20 July 2012 07:33:49 Marek Vasut wrote:
Dear Mike Frysinger,
On Wednesday 04 July 2012 10:39:20 Marek Vasut wrote:
Putting there the __func__ name would improve structure and speed up finding right place.
And if you want to use even __LINE__, look up __stringify patch in the ML archives ;-)
ugh, no, let's not use __LINE__ anywhere other than debug(). it has no business in code we ship as it is pointless bloated noise.
Helps find out the problematic place in code ...
except the code changes thus invalidating the line numbers, and how often are you putting the same string in multiple places that you can't easily coordinate where it came from ? if people are using the same exact string in multiple places, that sounds like a different problem.
You can always replace the function names with macros, which expand in place. And then simply add __func__ __LINE__ __FILE__ etc.
-mike
Best regards, Marek Vasut

On Friday 20 July 2012 17:11:33 Marek Vasut wrote:
Dear Mike Frysinger,
On Friday 20 July 2012 07:33:49 Marek Vasut wrote:
Dear Mike Frysinger,
On Wednesday 04 July 2012 10:39:20 Marek Vasut wrote:
Putting there the __func__ name would improve structure and speed up finding right place.
And if you want to use even __LINE__, look up __stringify patch in the ML archives ;-)
ugh, no, let's not use __LINE__ anywhere other than debug(). it has no business in code we ship as it is pointless bloated noise.
Helps find out the problematic place in code ...
except the code changes thus invalidating the line numbers, and how often are you putting the same string in multiple places that you can't easily coordinate where it came from ? if people are using the same exact string in multiple places, that sounds like a different problem.
You can always replace the function names with macros, which expand in place. And then simply add __func__ __LINE__ __FILE__ etc.
if you wanted to add it while debugging, that's fine, but the point is that this doesn't belong in normal runtime images. it's even trivial to define such a macro: #ifdef DEBUG # define printf(fmt, args...) printf("%s:%s:%i: " fmt, __FILE__, __LINE__, __func__, ## args) #endif (will obviously need a little more work to handle non-const fmt strings, but you get the idea). -mike

Dear Mike Frysinger,
On Friday 20 July 2012 17:11:33 Marek Vasut wrote:
Dear Mike Frysinger,
On Friday 20 July 2012 07:33:49 Marek Vasut wrote:
Dear Mike Frysinger,
On Wednesday 04 July 2012 10:39:20 Marek Vasut wrote:
> Putting there the __func__ name would improve structure and > speed up finding right place.
And if you want to use even __LINE__, look up __stringify patch in the ML archives ;-)
ugh, no, let's not use __LINE__ anywhere other than debug(). it has no business in code we ship as it is pointless bloated noise.
Helps find out the problematic place in code ...
except the code changes thus invalidating the line numbers, and how often are you putting the same string in multiple places that you can't easily coordinate where it came from ? if people are using the same exact string in multiple places, that sounds like a different problem.
You can always replace the function names with macros, which expand in place. And then simply add __func__ __LINE__ __FILE__ etc.
if you wanted to add it while debugging, that's fine, but the point is that this doesn't belong in normal runtime images.
Well doh ...
it's even trivial to define such a macro: #ifdef DEBUG # define printf(fmt, args...) printf("%s:%s:%i: " fmt, __FILE__, __LINE__, __func__, ## args) #endif
Uh, now I'm not sure what you mean by this stuff above.
(will obviously need a little more work to handle non-const fmt strings, but you get the idea). -mike
Best regards, Marek Vasut

On Tuesday 03 July 2012 17:32:54 Marek Vasut wrote:
+int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{
- char *str_env = NULL, *env_bkp = NULL;
- static char *s = "dfu";
- int ret = 0;
- if (argc < 3)
return CMD_RET_USAGE;
- str_env = getenv("dfu_alt_info");
- if (str_env == NULL) {
printf("%s: \"dfu_alt_info\" env variable not defined!\n",
__func__);
I was always curious if it's not possible to do something like
puts(__func__ "rest of string");
Maybe it'd help the overhead a bit? Certainly, it's beyond the scope of this patchset, I'm just curious :)
not anymore -- gcc disabled support for that sometime ago. and it's good they did as it causes more bloat than good. as soon as you do more than one statement, you get duplication. the string table will have: "some_func: rest of string" "some_func: boo" "some_func: another message" which takes up more space than: "some_func" "%s: rest of string" "%s: boo" "%s: another message" -mike

Dear Mike Frysinger,
On Tuesday 03 July 2012 17:32:54 Marek Vasut wrote:
+int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{
- char *str_env = NULL, *env_bkp = NULL;
- static char *s = "dfu";
- int ret = 0;
- if (argc < 3)
return CMD_RET_USAGE;
- str_env = getenv("dfu_alt_info");
- if (str_env == NULL) {
printf("%s: \"dfu_alt_info\" env variable not defined!\n",
__func__);
I was always curious if it's not possible to do something like
puts(__func__ "rest of string");
Maybe it'd help the overhead a bit? Certainly, it's beyond the scope of this patchset, I'm just curious :)
not anymore -- gcc disabled support for that sometime ago. and it's good they did as it causes more bloat than good. as soon as you do more than one statement, you get duplication. the string table will have: "some_func: rest of string" "some_func: boo" "some_func: another message" which takes up more space than: "some_func" "%s: rest of string" "%s: boo" "%s: another message"
Good knowing this. I'd expect gcc would build some trie in there to optimize the size.
-mike
Best regards, Marek Vasut

On Tuesday 03 July 2012 05:38:09 Lukasz Majewski wrote:
--- /dev/null +++ b/common/cmd_dfu.c
+int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
static
+{
- char *str_env = NULL, *env_bkp = NULL;
no need to assign NULL here. str_env should be const.
- static char *s = "dfu";
no need to declare this static
- int ret = 0;
no need to init to 0
- env_bkp = strdup(str_env);
- ret = dfu_config_entities(env_bkp, argv[1],
(int)simple_strtoul(argv[2], NULL, 10));
- if (ret)
return CMD_RET_FAILURE;
- if (strcmp(argv[3], "list") == 0) {
dfu_show_entities();
dfu_free_entities();
free(env_bkp);
return CMD_RET_SUCCESS;
for these last three statements, you could just do "goto done" and put a done label below ...
+exit:
- g_dnl_cleanup();
done:
- dfu_free_entities();
- free(env_bkp);
- return CMD_RET_SUCCESS;
-mike

Dear Mike,
On Tuesday 03 July 2012 05:38:09 Lukasz Majewski wrote:
--- /dev/null +++ b/common/cmd_dfu.c
+int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
static
It can be static (static int do_dfu). On the other hand the U_BOOT_CMD macro defines:
int (*cmd)(struct cmd_tbl_s *, int, int, char * const []); at struct cmd_tbl_s, which is int.
+{
- char *str_env = NULL, *env_bkp = NULL;
no need to assign NULL here. str_env should be const.
Yes, correct.
- static char *s = "dfu";
no need to declare this static
Why not? The s ptr is further passed to g_dnl_init(s), so my intend was to declare string in the data segment of this translation unit. In this way the data under this ptr will not vanish.
- int ret = 0;
no need to init to 0
Ok.
- env_bkp = strdup(str_env);
- ret = dfu_config_entities(env_bkp, argv[1],
(int)simple_strtoul(argv[2], NULL,
10));
- if (ret)
return CMD_RET_FAILURE;
- if (strcmp(argv[3], "list") == 0) {
dfu_show_entities();
dfu_free_entities();
free(env_bkp);
return CMD_RET_SUCCESS;
for these last three statements, you could just do "goto done" and put a done label below ...
Yes, you are correct. Thanks for tip.
+exit:
- g_dnl_cleanup();
done:
- dfu_free_entities();
- free(env_bkp);
- return CMD_RET_SUCCESS;
-mike

On Monday 23 July 2012 12:01:04 Lukasz Majewski wrote:
Dear Mike,
On Tuesday 03 July 2012 05:38:09 Lukasz Majewski wrote:
--- /dev/null +++ b/common/cmd_dfu.c
+int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
static
It can be static (static int do_dfu). On the other hand the U_BOOT_CMD macro defines:
int (*cmd)(struct cmd_tbl_s *, int, int, char * const []); at struct cmd_tbl_s, which is int.
i don't understand what you're trying to say
- static char *s = "dfu";
no need to declare this static
Why not? The s ptr is further passed to g_dnl_init(s), so my intend was to declare string in the data segment of this translation unit. In this way the data under this ptr will not vanish.
except your g_dnl_init already copies the incoming string to local storage, so where it is stored doesn't matter. further, the "dfu" is going to be in .rodata (thus never going away), it is only the "s" pointer which will be stored in .data, not the thing it points to.
had you done: char s[] = { 'd', 'f', 'u', '\0', }; then you'd get 4 bytes on the stack -mike

Hi Mike,
On Monday 23 July 2012 12:01:04 Lukasz Majewski wrote:
Dear Mike,
On Tuesday 03 July 2012 05:38:09 Lukasz Majewski wrote:
--- /dev/null +++ b/common/cmd_dfu.c
+int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
static
It can be static (static int do_dfu). On the other hand the U_BOOT_CMD macro defines:
int (*cmd)(struct cmd_tbl_s *, int, int, char * const []); at struct cmd_tbl_s, which is int.
i don't understand what you're trying to say
- static char *s = "dfu";
no need to declare this static
Why not? The s ptr is further passed to g_dnl_init(s), so my intend was to declare string in the data segment of this translation unit. In this way the data under this ptr will not vanish.
except your g_dnl_init already copies the incoming string to local storage, so where it is stored doesn't matter. further, the "dfu" is going to be in .rodata (thus never going away), it is only the "s" pointer which will be stored in .data, not the thing it points to.
had you done: char s[] = { 'd', 'f', 'u', '\0', }; then you'd get 4 bytes on the stack
Thanks for clarification
Regards, Lukasz Majewski

Support for USB UDC driver at trats board.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de Cc: Minkyu Kang mk7.kang@samsung.com --- board/samsung/trats/trats.c | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/board/samsung/trats/trats.c b/board/samsung/trats/trats.c index a95a516..2f3a531 100644 --- a/board/samsung/trats/trats.c +++ b/board/samsung/trats/trats.c @@ -59,6 +59,8 @@ static int hwrevision(int rev) return (board_rev & 0xf) == rev; }
+struct s3c_plat_otg_data s5pc210_otg_data; + int board_init(void) { gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100; @@ -259,6 +261,13 @@ struct s3c_plat_otg_data s5pc210_otg_data = { .usb_phy_ctrl = EXYNOS4_USBPHY_CONTROL, .usb_flags = PHY0_SLEEP, }; + +void board_usb_init(void) +{ + puts("USB_udc_probe\n"); + s3c_udc_probe(&s5pc210_otg_data); + +} #endif
static void pmic_reset(void)

Dear Lukasz,
On 3 July 2012 18:38, Lukasz Majewski l.majewski@samsung.com wrote:
Support for USB UDC driver at trats board.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de Cc: Minkyu Kang mk7.kang@samsung.com
board/samsung/trats/trats.c | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/board/samsung/trats/trats.c b/board/samsung/trats/trats.c index a95a516..2f3a531 100644 --- a/board/samsung/trats/trats.c +++ b/board/samsung/trats/trats.c @@ -59,6 +59,8 @@ static int hwrevision(int rev) return (board_rev & 0xf) == rev; }
+struct s3c_plat_otg_data s5pc210_otg_data;
int board_init(void) { gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100; @@ -259,6 +261,13 @@ struct s3c_plat_otg_data s5pc210_otg_data = { .usb_phy_ctrl = EXYNOS4_USBPHY_CONTROL, .usb_flags = PHY0_SLEEP, };
+void board_usb_init(void) +{
puts("USB_udc_probe\n");
is it useful information? or debug message?
s3c_udc_probe(&s5pc210_otg_data);
+} #endif
Thanks. Minkyu Kang.

Hi Minkyu,
Dear Lukasz,
On 3 July 2012 18:38, Lukasz Majewski l.majewski@samsung.com wrote:
Support for USB UDC driver at trats board.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de Cc: Minkyu Kang mk7.kang@samsung.com
board/samsung/trats/trats.c | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/board/samsung/trats/trats.c b/board/samsung/trats/trats.c index a95a516..2f3a531 100644 --- a/board/samsung/trats/trats.c +++ b/board/samsung/trats/trats.c @@ -59,6 +59,8 @@ static int hwrevision(int rev) return (board_rev & 0xf) == rev; }
+struct s3c_plat_otg_data s5pc210_otg_data;
int board_init(void) { gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100; @@ -259,6 +261,13 @@ struct s3c_plat_otg_data s5pc210_otg_data = { .usb_phy_ctrl = EXYNOS4_USBPHY_CONTROL, .usb_flags = PHY0_SLEEP, };
+void board_usb_init(void) +{
puts("USB_udc_probe\n");
is it useful information? or debug message?
Good point. I will change it to debug.
s3c_udc_probe(&s5pc210_otg_data);
+} #endif
Thanks. Minkyu Kang.

Enable the g_dnl composite USB gadget driver with embedded DFU function on it. It now uses the composite gadget framework to support download specific USB functions (like enabled DFU or USB Mass Storage).
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de Cc: Minkyu Kang mk7.kang@samsung.com --- include/configs/trats.h | 19 ++++++++++++++++++- 1 files changed, 18 insertions(+), 1 deletions(-)
diff --git a/include/configs/trats.h b/include/configs/trats.h index 2f96a18..dcbd198 100644 --- a/include/configs/trats.h +++ b/include/configs/trats.h @@ -94,6 +94,16 @@ #undef CONFIG_CMD_ONENAND #undef CONFIG_CMD_MTDPARTS #define CONFIG_CMD_MMC +#define CONFIG_CMD_DFU + +/* FAT */ +#define CONFIG_CMD_FAT +#define CONFIG_FAT_WRITE + +/* USB Composite download gadget - g_dnl */ +#define CONFIG_USBDOWNLOAD_GADGET +#define CONFIG_DFU_FUNCTION +#define CONFIG_DFU_MMC
#define CONFIG_BOOTDELAY 1 #define CONFIG_ZERO_BOOTDELAY_CHECK @@ -104,6 +114,11 @@ #define CONFIG_BOOTBLOCK "10" #define CONFIG_ENV_COMMON_BOOT "${console} ${meminfo}"
+#define CONFIG_DFU_ALT \ + "dfu_alt_info=" \ + "u-boot mmc 80 400;" \ + "uImage fat 0 2\0" \ + #define CONFIG_ENV_OVERWRITE #define CONFIG_SYS_CONSOLE_INFO_QUIET #define CONFIG_SYS_CONSOLE_IS_IN_ENV @@ -146,7 +161,8 @@ "mmcdev=0\0" \ "mmcbootpart=2\0" \ "mmcrootpart=3\0" \ - "opts=always_resume=1" + "opts=always_resume=1\0" \ + CONFIG_DFU_ALT
/* Miscellaneous configurable options */ #define CONFIG_SYS_LONGHELP /* undef to save memory */ @@ -213,6 +229,7 @@ #define CONFIG_USB_GADGET #define CONFIG_USB_GADGET_S3C_UDC_OTG #define CONFIG_USB_GADGET_DUALSPEED +#define CONFIG_USB_GADGET_VBUS_DRAW 2
/* LCD */ #define CONFIG_EXYNOS_FB

On 3 July 2012 18:38, Lukasz Majewski l.majewski@samsung.com wrote:
Enable the g_dnl composite USB gadget driver with embedded DFU function on it. It now uses the composite gadget framework to support download specific USB functions (like enabled DFU or USB Mass Storage).
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de Cc: Minkyu Kang mk7.kang@samsung.com
include/configs/trats.h | 19 ++++++++++++++++++- 1 files changed, 18 insertions(+), 1 deletions(-)
Acked-by: Minkyu Kang mk7.kang@samsung.com

On Tue, Jul 3, 2012 at 6:38 AM, Lukasz Majewski l.majewski@samsung.com wrote:
Those patches add support for composite USB download gadget. This gadget (at least for now) is equipped with DFU download function.
Awesome! :-)

Dear Otavio Salvador,
On Tue, Jul 3, 2012 at 6:38 AM, Lukasz Majewski l.majewski@samsung.com
wrote:
Those patches add support for composite USB download gadget. This gadget (at least for now) is equipped with DFU download function.
Awesome! :-)
Hey Otavio ... we can do the same with our bootrom :-p
Best regards, Marek Vasut

Those patches add support for composite USB download gadget. This gadget (at least for now) is equipped with DFU download function.
A separate DFU back-end and front-end have been added. Back-end is placed at ./drivers/dfu directory. The front-end is implemented as USB function.
The back-end is written in a generic manner with storage device specific code separated (eMMC).
DFU specification can be found at: http://wiki.openmoko.org/wiki/USB_DFU_-_The_USB_Device_Firmware_Upgrade_stan...
Example usage:
u-boot side: dfu mmc 0 PC: dfu-util -U IMAGE.bin -a uImage (for upload) dfu-util -D uImage -a uImage (download)
To list the alt settings: dfu mmc 0 list
Test HW: Exynos4210 Trats board
Lukasz Majewski (7): dfu:usb: Support for g_dnl composite download gadget. dfu:usb: DFU USB function (f_dfu) support for g_dnl composite gadget dfu: DFU backend implementation dfu: MMC specific routines for DFU operation dfu:cmd: Support for DFU u-boot command arm:trats: Support for USB UDC driver at TRATS board. arm:trats: Enable g_dnl composite USB gadget with embedded DFU function on TRATS
Makefile | 1 + board/samsung/trats/trats.c | 8 + common/Makefile | 1 + common/cmd_dfu.c | 81 +++++ drivers/dfu/Makefile | 44 +++ drivers/dfu/dfu.c | 259 +++++++++++++++ drivers/dfu/dfu_mmc.c | 126 +++++++ drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_dfu.c | 753 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/f_dfu.h | 100 ++++++ drivers/usb/gadget/g_dnl.c | 223 +++++++++++++ include/configs/trats.h | 24 ++- include/dfu.h | 99 ++++++ include/g_dnl.h | 33 ++ 14 files changed, 1753 insertions(+), 1 deletions(-) create mode 100644 common/cmd_dfu.c create mode 100644 drivers/dfu/Makefile create mode 100644 drivers/dfu/dfu.c create mode 100644 drivers/dfu/dfu_mmc.c create mode 100644 drivers/usb/gadget/f_dfu.c create mode 100644 drivers/usb/gadget/f_dfu.h create mode 100644 drivers/usb/gadget/g_dnl.c create mode 100644 include/dfu.h create mode 100644 include/g_dnl.h

Composite USB download gadget support (g_dnl) for download functions. This code works on top of composite gadget.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2:
- G_DNL_{VENDOR_NUM, PRODUCT_NUM and MANUFACTURER} defined at ./include/configs/<board>.h - Suspend and resume stub methods removed - '\0' repleaced with plain 0 --- drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/g_dnl.c | 223 +++++++++++++++++++++++++++++++++++++++++++ include/g_dnl.h | 33 +++++++ 3 files changed, 257 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/gadget/g_dnl.c create mode 100644 include/g_dnl.h
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 87d1918..2c067c8 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -29,6 +29,7 @@ LIB := $(obj)libusb_gadget.o ifdef CONFIG_USB_GADGET COBJS-y += epautoconf.o config.o usbstring.o COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o +COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o endif ifdef CONFIG_USB_ETHER COBJS-y += ether.o epautoconf.o config.o usbstring.o diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c new file mode 100644 index 0000000..fe996b4 --- /dev/null +++ b/drivers/usb/gadget/g_dnl.c @@ -0,0 +1,223 @@ +/* + * g_dnl.c -- USB Downloader Gadget + * + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski l.majewski@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 <errno.h> +#include <common.h> +#include <malloc.h> +#include <linux/usb/ch9.h> +#include <usbdescriptors.h> +#include <linux/usb/gadget.h> + +#include <mmc.h> +#include <part.h> + +#include <g_dnl.h> +#include "f_dfu.h" + +#include "gadget_chips.h" +#include "composite.c" + +/* + * One needs to define the following: + * G_DNL_VENDOR_NUM + * G_DNL_PRODUCT_NUM + * G_DNL_MANUFACTURER + * at e.g. ./include/configs/<board>.h + */ + +#define STRING_MANUFACTURER 25 +#define STRING_PRODUCT 2 +#define STRING_USBDOWN 2 +#define CONFIG_USBDOWNLOADER 2 + +#define DRIVER_VERSION "usb_dnl 2.0" + +static const char shortname[] = "usb_dnl_"; +static const char product[] = "USB download gadget"; +static const char manufacturer[] = G_DNL_MANUFACTURER; + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_COMM, + .bDeviceSubClass = 0x02, /*0x02:CDC-modem , 0x00:CDC-serial*/ + + .idVendor = __constant_cpu_to_le16(G_DNL_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16(G_DNL_PRODUCT_NUM), + .iProduct = STRING_PRODUCT, + .bNumConfigurations = 1, +}; + +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &(struct usb_otg_descriptor){ + .bLength = sizeof(struct usb_otg_descriptor), + .bDescriptorType = USB_DT_OTG, + .bmAttributes = USB_OTG_SRP, + }, + NULL, +}; + +/* static strings, in UTF-8 */ +static struct usb_string odin_string_defs[] = { + { 0, manufacturer, }, + { 1, product, }, +}; + +static struct usb_gadget_strings odin_string_tab = { + .language = 0x0409, /* en-us */ + .strings = odin_string_defs, +}; + +static struct usb_gadget_strings *g_dnl_composite_strings[] = { + &odin_string_tab, + NULL, +}; + +static int g_dnl_unbind(struct usb_composite_dev *cdev) +{ + debug("%s\n", __func__); + return 0; +} + +static int g_dnl_do_config(struct usb_configuration *c) +{ + int ret = -1; + char *s = (char *) c->cdev->driver->name; + + debug("%s: configuration: 0x%p composite dev: 0x%p\n", + __func__, c, c->cdev); + + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + printf("GADGET DRIVER: %s\n", s); + + if (!strcmp(s, "usb_dnl_dfu")) + ret = dfu_add(c); + + return ret; +} + +static int g_dnl_config_register(struct usb_composite_dev *cdev) +{ + debug("%s:\n", __func__); + static struct usb_configuration config = { + .label = "usb_dnload", + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .bConfigurationValue = CONFIG_USBDOWNLOADER, + .iConfiguration = STRING_USBDOWN, + + .bind = g_dnl_do_config, + }; + + return usb_add_config(cdev, &config); +} + +static int g_dnl_bind(struct usb_composite_dev *cdev) +{ + int gcnum; + int id, ret; + struct usb_gadget *gadget = cdev->gadget; + + debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev); + + id = usb_string_id(cdev); + + if (id < 0) + return id; + odin_string_defs[0].id = id; + device_desc.iManufacturer = id; + + id = usb_string_id(cdev); + if (id < 0) + return id; + + odin_string_defs[1].id = id; + device_desc.iProduct = id; + + ret = g_dnl_config_register(cdev); + if (ret) + goto error; + + gcnum = usb_gadget_controller_number(gadget); + + debug("gcnum: %d\n", gcnum); + if (gcnum >= 0) + device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); + else { + debug("%s: controller '%s' not recognized\n", + shortname, gadget->name); + device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); + } + + return 0; + + error: + g_dnl_unbind(cdev); + return -ENOMEM; +} + +static struct usb_composite_driver g_dnl_driver = { + .name = NULL, + .dev = &device_desc, + .strings = g_dnl_composite_strings, + + .bind = g_dnl_bind, + .unbind = g_dnl_unbind, +}; + +int g_dnl_init(char *s) +{ + int ret; + static char str[16]; + + memset(str, 0, sizeof(str)); + + strncpy(str, shortname, sizeof(shortname)); + + if (!strncmp(s, "dfu", sizeof(s))) { + strncat(str, s, sizeof(str)); + } else { + printf("%s: unknown command: %s\n", __func__, s); + return -EINVAL; + } + + g_dnl_driver.name = str; + + debug("%s: g_dnl_driver.name: %s\n", __func__, g_dnl_driver.name); + ret = usb_composite_register(&g_dnl_driver); + + if (ret) { + printf("%s: failed!, error:%d\n", __func__, ret); + return ret; + } + + return 0; +} + +void g_dnl_cleanup(void) +{ + usb_composite_unregister(&g_dnl_driver); +} diff --git a/include/g_dnl.h b/include/g_dnl.h new file mode 100644 index 0000000..5d0e9a5 --- /dev/null +++ b/include/g_dnl.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski l.majewski@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 __G_DOWNLOAD_H_ +#define __G_DOWNLOAD_H_ + +#include <linux/usb/ch9.h> +#include <usbdescriptors.h> +#include <linux/usb/gadget.h> + +int g_dnl_init(char *s); +void g_dnl_cleanup(void); + +/* USB initialization declaration - board specific*/ +void board_usb_init(void); +#endif /* __G_DOWNLOAD_H_ */

Dear Lukasz Majewski,
Composite USB download gadget support (g_dnl) for download functions. This code works on top of composite gadget.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
Changes for v2:
- G_DNL_{VENDOR_NUM, PRODUCT_NUM and MANUFACTURER} defined at
make this CONFIG_G_....
The rest seems ok.
Best regards, Marek Vasut

Support for f_dfu USB function.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2:
- Replace kzalloc and kfree with free and calloc - Reorganization of calloc calls - Misspelling corrected - Redesign of DFU state machine from "switch case" to function pointers - Split the dfu_handle method to separate functions for each DFU state --- drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/f_dfu.c | 753 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/f_dfu.h | 100 ++++++ 3 files changed, 854 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/gadget/f_dfu.c create mode 100644 drivers/usb/gadget/f_dfu.h
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 2c067c8..5bbdd36 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -30,6 +30,7 @@ ifdef CONFIG_USB_GADGET COBJS-y += epautoconf.o config.o usbstring.o COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o +COBJS-$(CONFIG_DFU_FUNCTION) += f_dfu.o endif ifdef CONFIG_USB_ETHER COBJS-y += ether.o epautoconf.o config.o usbstring.o diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c new file mode 100644 index 0000000..c1ef70c --- /dev/null +++ b/drivers/usb/gadget/f_dfu.c @@ -0,0 +1,753 @@ +/* + * f_dfu.c -- Device Firmware Update USB function + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz andrzej.p@samsung.com + * Lukasz Majewski l.majewski@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 <errno.h> +#include <common.h> +#include <malloc.h> + +#include <linux/usb/ch9.h> +#include <usbdescriptors.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> + +#include <dfu.h> +#include "f_dfu.h" + +struct f_dfu { + struct usb_function usb_function; + + struct usb_descriptor_header **function; + struct usb_string *strings; + + /* when configured, we have one config */ + u8 config; + u8 altsetting; + enum dfu_state dfu_state; + unsigned int dfu_status; + + /* Send/received block number is handy for data integrity check */ + int blk_seq_num; +}; + +typedef int (*dfu_state_fn) (struct f_dfu *, + const struct usb_ctrlrequest *, + struct usb_gadget *, + struct usb_request *); + +static inline struct f_dfu *func_to_dfu(struct usb_function *f) +{ + return container_of(f, struct f_dfu, usb_function); +} + +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 = DFU_USB_BUFSIZ, + .bcdDFUVersion = __constant_cpu_to_le16(0x0110), +}; + +static 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 = DYNAMIC */ +}; + +static struct usb_descriptor_header *dfu_runtime_descs[] = { + (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 const char dfu_name[] = "Device Firmware Upgrade"; + +/* + * static strings, in UTF-8 + * + * dfu_generic configuration + */ +static struct usb_string strings_dfu_generic[] = { + [0].s = dfu_name, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dfu_generic = { + .language = 0x0409, /* en-us */ + .strings = strings_dfu_generic, +}; + +static struct usb_gadget_strings *dfu_generic_strings[] = { + &stringtab_dfu_generic, + NULL, +}; + +/* + * usb_function specific + */ +static struct usb_gadget_strings stringtab_dfu = { + .language = 0x0409, /* en-us */ + /* + * .strings + * + * assigned during initialization, + * depends on number of flash entities + * + */ +}; + +static struct usb_gadget_strings *dfu_strings[] = { + &stringtab_dfu, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_dfu *f_dfu = req->context; + + dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf, + req->length, f_dfu->blk_seq_num); + + if (req->length == 0) { + puts("DOWNLOAD ... OK\n"); + puts("Ctrl+C to exit ...\n"); + } +} + +static void handle_getstatus(struct usb_request *req) +{ + struct dfu_status *dstat = (struct dfu_status *)req->buf; + struct f_dfu *f_dfu = req->context; + + switch (f_dfu->dfu_state) { + case DFU_STATE_dfuDNLOAD_SYNC: + case DFU_STATE_dfuDNBUSY: + f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE; + break; + case DFU_STATE_dfuMANIFEST_SYNC: + break; + default: + break; + } + + /* send status response */ + dstat->bStatus = f_dfu->dfu_status; + /* FIXME: set dstat->bwPollTimeout */ + dstat->bState = f_dfu->dfu_state; + dstat->iString = 0; +} + +static void handle_getstate(struct usb_request *req) +{ + struct f_dfu *f_dfu = req->context; + + ((u8 *)req->buf)[0] = f_dfu->dfu_state & 0xff; + req->actual = sizeof(u8); +} + +static inline void to_dfu_mode(struct f_dfu *f_dfu) +{ + f_dfu->usb_function.strings = dfu_strings; + f_dfu->usb_function.hs_descriptors = f_dfu->function; +} + +static inline void to_runtime_mode(struct f_dfu *f_dfu) +{ + f_dfu->usb_function.strings = NULL; + f_dfu->usb_function.hs_descriptors = dfu_runtime_descs; +} + +static int handle_upload(struct usb_request *req, u16 len) +{ + struct f_dfu *f_dfu = req->context; + + return dfu_read(dfu_get_entity(f_dfu->altsetting), req->buf, + req->length, f_dfu->blk_seq_num); +} + +static int handle_dnload(struct usb_gadget *gadget, u16 len) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_request *req = cdev->req; + struct f_dfu *f_dfu = req->context; + + if (len == 0) + f_dfu->dfu_state = DFU_STATE_dfuMANIFEST_SYNC; + + req->complete = dnload_request_complete; + + return len; +} + +/*-------------------------------------------------------------------------*/ +/* DFU state machine */ +static int state_app_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + case USB_REQ_DFU_DETACH: + f_dfu->dfu_state = DFU_STATE_appDETACH; + to_dfu_mode(f_dfu); + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + value = RET_ZLP; + break; + default: + value = RET_STALL; + break; + } + + return value; +} + +static int state_app_detach(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_appIDLE; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 len = le16_to_cpu(ctrl->wLength); + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_DNLOAD: + if (len == 0) { + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; + f_dfu->blk_seq_num = w_value; + value = handle_dnload(gadget, len); + break; + case USB_REQ_DFU_UPLOAD: + f_dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE; + f_dfu->blk_seq_num = 0; + value = handle_upload(req, len); + break; + case USB_REQ_DFU_ABORT: + /* no zlp? */ + value = RET_ZLP; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + 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 :( + */ + f_dfu->dfu_state = + DFU_STATE_dfuMANIFEST_WAIT_RST; + to_runtime_mode(f_dfu); + f_dfu->dfu_state = DFU_STATE_appIDLE; + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_dnload_sync(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_dnbusy(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_dnload_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 len = le16_to_cpu(ctrl->wLength); + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_DNLOAD: + f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; + f_dfu->blk_seq_num = w_value; + value = handle_dnload(gadget, len); + break; + case USB_REQ_DFU_ABORT: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + value = RET_ZLP; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_manifest_sync(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + /* We're MainfestationTolerant */ + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + handle_getstatus(req); + f_dfu->blk_seq_num = 0; + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_upload_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 len = le16_to_cpu(ctrl->wLength); + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_UPLOAD: + /* state transition if less data then requested */ + f_dfu->blk_seq_num = w_value; + value = handle_upload(req, len); + if (value >= 0 && value < len) + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + break; + case USB_REQ_DFU_ABORT: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + /* no zlp? */ + value = RET_ZLP; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_error(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + case USB_REQ_DFU_CLRSTATUS: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + f_dfu->dfu_status = DFU_STATUS_OK; + /* no zlp? */ + value = RET_ZLP; + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static dfu_state_fn dfu_state[] = { + state_app_idle, /* DFU_STATE_appIDLE */ + state_app_detach, /* DFU_STATE_appDETACH */ + state_dfu_idle, /* DFU_STATE_dfuIDLE */ + state_dfu_dnload_sync, /* DFU_STATE_dfuDNLOAD_SYNC */ + state_dfu_dnbusy, /* DFU_STATE_dfuDNBUSY */ + state_dfu_dnload_idle, /* DFU_STATE_dfuDNLOAD_IDLE */ + state_dfu_manifest_sync, /* DFU_STATE_dfuMANIFEST_SYNC */ + NULL, /* DFU_STATE_dfuMANIFEST */ + NULL, /* DFU_STATE_dfuMANIFEST_WAIT_RST */ + state_dfu_upload_idle, /* DFU_STATE_dfuUPLOAD_IDLE */ + state_dfu_error /* DFU_STATE_dfuERROR */ +}; + +static int +dfu_handle(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct usb_gadget *gadget = f->config->cdev->gadget; + struct usb_request *req = f->config->cdev->req; + struct f_dfu *f_dfu = f->config->cdev->req->context; + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + int value = 0; + u8 req_type = ctrl->bRequestType & USB_TYPE_MASK; + + debug("w_value: 0x%x len: 0x%x\n", w_value, len); + debug("req_type: 0x%x ctrl->bRequest: 0x%x f_dfu->dfu_state: 0x%x\n", + req_type, ctrl->bRequest, f_dfu->dfu_state); + + + if (req_type == USB_TYPE_STANDARD) { + if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR && + (w_value >> 8) == DFU_DT_FUNC) { + value = min(len, (u16) sizeof dfu_func); + memcpy(req->buf, &dfu_func, value); + } + } else /* DFU specific request */ + value = dfu_state[f_dfu->dfu_state] (f_dfu, ctrl, gadget, req); + + if (value >= 0) { + req->length = value; + req->zero = value < len; + value = usb_ep_queue(gadget->ep0, req, 0); + if (value < 0) { + debug("ep_queue --> %d\n", value); + req->status = 0; + } + } + + return value; +} + +/*-------------------------------------------------------------------------*/ + +static int +dfu_prepare_strings(struct f_dfu *f_dfu, int n) +{ + struct dfu_entity *de = NULL; + int i = 0; + + f_dfu->strings = calloc(sizeof(struct usb_string), n + 1); + if (!f_dfu->strings) + goto enomem; + + for (i = 0; i < n; ++i) { + de = dfu_get_entity(i); + f_dfu->strings[i].s = de->name; + } + + f_dfu->strings[i].id = 0; + f_dfu->strings[i].s = NULL; + + return 0; + +enomem: + while (i) + f_dfu->strings[--i].s = NULL; + + free(f_dfu->strings); + + return -ENOMEM; +} + +static int dfu_prepare_function(struct f_dfu *f_dfu, int n) +{ + struct usb_interface_descriptor *d; + int i = 0; + + f_dfu->function = calloc(sizeof(struct usb_descriptor_header *), n); + if (!f_dfu->function) + goto enomem; + + for (i = 0; i < n; ++i) { + d = calloc(sizeof(*d), 1); + 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; + + f_dfu->function[i] = (struct usb_descriptor_header *)d; + } + f_dfu->function[i] = NULL; + + return 0; + +enomem: + while (i) { + free(f_dfu->function[--i]); + f_dfu->function[i] = NULL; + } + free(f_dfu->function); + + return -ENOMEM; +} + +static int dfu_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_dfu *f_dfu = func_to_dfu(f); + int alt_num = dfu_get_alt_number(); + int rv, id, i; + + id = usb_interface_id(c, f); + if (id < 0) + return id; + dfu_intf_runtime.bInterfaceNumber = id; + + f_dfu->dfu_state = DFU_STATE_appIDLE; + f_dfu->dfu_status = DFU_STATUS_OK; + + rv = dfu_prepare_function(f_dfu, alt_num); + if (rv) + goto error; + + rv = dfu_prepare_strings(f_dfu, alt_num); + if (rv) + goto error; + for (i = 0; i < alt_num; i++) { + id = usb_string_id(cdev); + if (id < 0) + return id; + f_dfu->strings[i].id = id; + ((struct usb_interface_descriptor *)f_dfu->function[i]) + ->iInterface = id; + } + + stringtab_dfu.strings = f_dfu->strings; + + cdev->req->context = f_dfu; + +error: + return rv; +} + +static void dfu_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + int alt_num = dfu_get_alt_number(); + int i; + + if (f_dfu->strings) { + i = alt_num; + while (i) + f_dfu->strings[--i].s = NULL; + + free(f_dfu->strings); + } + + if (f_dfu->function) { + i = alt_num; + while (i) { + free(f_dfu->function[--i]); + f_dfu->function[i] = NULL; + } + free(f_dfu->function); + } + + free(f_dfu); +} + +static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + + debug("%s: intf:%d alt:%d\n", __func__, intf, alt); + + f_dfu->altsetting = alt; + + return 0; +} + +/* TODO: is this really what we need here? */ +static void dfu_disable(struct usb_function *f) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + if (f_dfu->config == 0) + return; + + debug("%s: reset config\n", __func__); + + f_dfu->config = 0; +} + +static int dfu_bind_config(struct usb_configuration *c) +{ + struct f_dfu *f_dfu; + int status; + + f_dfu = calloc(sizeof(*f_dfu), 1); + if (!f_dfu) + return -ENOMEM; + f_dfu->usb_function.name = "dfu"; + f_dfu->usb_function.hs_descriptors = dfu_runtime_descs; + f_dfu->usb_function.bind = dfu_bind; + f_dfu->usb_function.unbind = dfu_unbind; + f_dfu->usb_function.set_alt = dfu_set_alt; + f_dfu->usb_function.disable = dfu_disable; + f_dfu->usb_function.strings = dfu_generic_strings, + f_dfu->usb_function.setup = dfu_handle, + + status = usb_add_function(c, &f_dfu->usb_function); + if (status) + free(f_dfu); + + return status; +} + +int dfu_add(struct usb_configuration *c) +{ + int id; + + id = usb_string_id(c->cdev); + if (id < 0) + return id; + strings_dfu_generic[0].id = id; + dfu_intf_runtime.iInterface = id; + + debug("%s: cdev: 0x%p gadget:0x%p gadget->ep0: 0x%p\n", __func__, + c->cdev, c->cdev->gadget, c->cdev->gadget->ep0); + + return dfu_bind_config(c); +} diff --git a/drivers/usb/gadget/f_dfu.h b/drivers/usb/gadget/f_dfu.h new file mode 100644 index 0000000..023e1ad --- /dev/null +++ b/drivers/usb/gadget/f_dfu.h @@ -0,0 +1,100 @@ +/* + * f_dfu.h -- Device Firmware Update gadget + * + * Copyright (C) 2011-2012 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 __F_DFU_H_ +#define __F_DFU_H_ + +#include <linux/compiler.h> +#include <linux/usb/composite.h> + +#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 DFU_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 + +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; + +/* configuration-specific linkup */ +int dfu_add(struct usb_configuration *c); +#endif /* __F_DFU_H_ */

Dear Lukasz Majewski,
Support for f_dfu USB function.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[...]
+static void handle_getstatus(struct usb_request *req) +{
- struct dfu_status *dstat = (struct dfu_status *)req->buf;
- struct f_dfu *f_dfu = req->context;
- switch (f_dfu->dfu_state) {
- case DFU_STATE_dfuDNLOAD_SYNC:
- case DFU_STATE_dfuDNBUSY:
f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
break;
- case DFU_STATE_dfuMANIFEST_SYNC:
break;
- default:
break;
- }
- /* send status response */
- dstat->bStatus = f_dfu->dfu_status;
- /* FIXME: set dstat->bwPollTimeout */
FIXME? :)
- dstat->bState = f_dfu->dfu_state;
- dstat->iString = 0;
+}
[...]
+static int +dfu_handle(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{
- struct usb_gadget *gadget = f->config->cdev->gadget;
- struct usb_request *req = f->config->cdev->req;
- struct f_dfu *f_dfu = f->config->cdev->req->context;
- u16 len = le16_to_cpu(ctrl->wLength);
- u16 w_value = le16_to_cpu(ctrl->wValue);
- int value = 0;
- u8 req_type = ctrl->bRequestType & USB_TYPE_MASK;
- debug("w_value: 0x%x len: 0x%x\n", w_value, len);
- debug("req_type: 0x%x ctrl->bRequest: 0x%x f_dfu->dfu_state: 0x%x\n",
req_type, ctrl->bRequest, f_dfu->dfu_state);
- if (req_type == USB_TYPE_STANDARD) {
if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR &&
(w_value >> 8) == DFU_DT_FUNC) {
value = min(len, (u16) sizeof dfu_func);
Does the sizeof really miss braces up here ?
[...]

New, separate driver at ./drivers/dfu has been added. It allows platform and storage independent operation of DFU.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2: - None --- Makefile | 1 + drivers/dfu/Makefile | 43 ++++++++ drivers/dfu/dfu.c | 259 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/dfu.h | 99 +++++++++++++++++++ 4 files changed, 402 insertions(+), 0 deletions(-) create mode 100644 drivers/dfu/Makefile create mode 100644 drivers/dfu/dfu.c create mode 100644 include/dfu.h
diff --git a/Makefile b/Makefile index 0197239..c37dcf3 100644 --- a/Makefile +++ b/Makefile @@ -271,6 +271,7 @@ LIBS += drivers/pci/libpci.o LIBS += drivers/pcmcia/libpcmcia.o LIBS += drivers/power/libpower.o LIBS += drivers/spi/libspi.o +LIBS += drivers/dfu/libdfu.o ifeq ($(CPU),mpc83xx) LIBS += drivers/qe/libqe.o LIBS += arch/powerpc/cpu/mpc8xxx/ddr/libddr.o diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile new file mode 100644 index 0000000..7736485 --- /dev/null +++ b/drivers/dfu/Makefile @@ -0,0 +1,43 @@ +# +# Copyright (C) 2012 Samsung Electronics +# Lukasz Majewski l.majewski@samsung.com +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB = $(obj)libdfu.o + +COBJS-$(CONFIG_DFU_FUNCTION) += dfu.o + +SRCS := $(COBJS-y:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS-y)) + +$(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/dfu/dfu.c b/drivers/dfu/dfu.c new file mode 100644 index 0000000..63733fb --- /dev/null +++ b/drivers/dfu/dfu.c @@ -0,0 +1,259 @@ +/* + * dfu.c -- DFU back-end routines + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz andrzej.p@samsung.com + * Lukasz Majewski l.majewski@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 <fat.h> +#include <dfu.h> +#include <linux/list.h> +#include <linux/compiler.h> + +static LIST_HEAD(dfu_list); +static int dfu_alt_num; + +static int dfu_find_alt_num(char *s) +{ + int i = 0; + + for (; *s; s++) + if (*s == ';') + i++; + + return ++i; +} + +static char *dfu_extract_entity(char** env) +{ + char *s = *env; + + strsep(env, ";"); + return s; +} + +char *dfu_extract_token(char** e, int *n) +{ + char *st = *e; + + debug("%s: %s\n", __func__, st); + + strsep(e, " "); + *n = *e - st; + + return st; +} + +static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE) + dfu_buf[DFU_DATA_BUF_SIZE]; + +int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) +{ + static unsigned char *i_buf; + static int i_blk_seq_num; + long w_size = 0; + int ret = 0; + + debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n", + __func__, dfu->name, buf, size, blk_seq_num, i_buf); + + if (blk_seq_num == 0) { + memset(dfu_buf, '\0', sizeof(dfu_buf)); + i_buf = dfu_buf; + i_blk_seq_num = 0; + } + + if (i_blk_seq_num++ != blk_seq_num) { + printf("%s: Wrong sequence number! [%d] [%d]\n", + __func__, i_blk_seq_num, blk_seq_num); + return -1; + } + + memcpy(i_buf, buf, size); + i_buf += size; + + if (size == 0) { + /* Integrity check (if needed) */ + debug("%s: %s %d [B] CRC32: 0x%x\n", __func__, dfu->name, + i_buf - dfu_buf, crc32(0, dfu_buf, i_buf - dfu_buf)); + + w_size = i_buf - dfu_buf; + ret = dfu->write_medium(dfu, dfu_buf, &w_size); + if (ret) + debug("%s: Write error!\n", __func__); + + i_blk_seq_num = 0; + i_buf = NULL; + return ret; + } + + return ret; +} + +int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) +{ + static unsigned char *i_buf; + static int i_blk_seq_num; + static long r_size; + static u32 crc; + int ret = 0; + + debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n", + __func__, dfu->name, buf, size, blk_seq_num, i_buf); + + if (blk_seq_num == 0) { + i_buf = dfu_buf; + memset(dfu_buf, '\0', sizeof(dfu_buf)); + ret = dfu->read_medium(dfu, i_buf, &r_size); + debug("%s: %s %ld [B]\n", __func__, dfu->name, r_size); + i_blk_seq_num = 0; + /* Integrity check (if needed) */ + crc = crc32(0, dfu_buf, r_size); + } + + if (i_blk_seq_num++ != blk_seq_num) { + printf("%s: Wrong sequence number! [%d] [%d]\n", + __func__, i_blk_seq_num, blk_seq_num); + return -1; + } + + if (r_size >= size) { + memcpy(buf, i_buf, size); + i_buf += size; + r_size -= size; + return size; + } else { + memcpy(buf, i_buf, r_size); + i_buf += r_size; + debug("%s: %s CRC32: 0x%x\n", __func__, dfu->name, crc); + puts("UPLOAD ... done\n"); + puts("Ctrl+C to exit ...\n"); + + i_buf = NULL; + i_blk_seq_num = 0; + crc = 0; + return r_size; + } + return ret; +} + +static int dfu_fill_entity(struct dfu_entity *dfu, char* s, int alt, + char *interface, int num) +{ + char *st = NULL; + int n = 0; + + debug("%s: %s interface: %s num: %d\n", __func__, s, interface, num); + + st = dfu_extract_token(&s, &n); + strncpy((char *) &dfu->name, st, n); + + dfu->dev_num = num; + dfu->alt = alt; + + /* Specific for mmc device */ + if (strcmp(interface, "mmc") == 0) { + if (dfu_fill_entity_mmc(dfu, s)) + return -1; + } else { + printf("dfu: Device %s not supported\n", interface); + return -1; + } + + return 0; +} + +int dfu_config_entities(char *env, char *interface, int num) +{ + struct dfu_entity *dfu; + int i, ret; + char *s; + + dfu_alt_num = dfu_find_alt_num(env); + debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num); + + for (i = 0; i < dfu_alt_num; i++) { + dfu = calloc(sizeof(struct dfu_entity), 1); + + s = dfu_extract_entity(&env); + ret = dfu_fill_entity(dfu, s, i, interface, num); + if (ret) + return -1; + + list_add_tail(&dfu->list, &dfu_list); + } + + return 0; +} + +void dfu_free_entities(void) +{ + struct dfu_entity *dfu = NULL, *p = NULL; + + list_for_each_entry_safe_reverse(dfu, p, &dfu_list, list) { + list_del(&dfu->list); + free(dfu); + } + + INIT_LIST_HEAD(&dfu_list); +} + +static char *dfu_get_dev_type(enum dfu_device_type t) +{ + static char *dev_t[] = {NULL, "MMC", "ONENAND", "NAND" }; + return dev_t[t]; +} + +static char *dfu_get_layout(enum dfu_device_type l) +{ + static char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT" }; + return dfu_layout[l]; +} + +void dfu_show_entities(void) +{ + struct dfu_entity *dfu = NULL; + + puts("DFU alt settings list:\n"); + + list_for_each_entry(dfu, &dfu_list, list) { + printf("dev: %s alt: %d name: %s layout: %s\n", + dfu_get_dev_type(dfu->dev_type), dfu->alt, + dfu->name, dfu_get_layout(dfu->layout)); + } +} + +int dfu_get_alt_number(void) +{ + return dfu_alt_num; +} + +struct dfu_entity *dfu_get_entity(int alt) +{ + struct dfu_entity *dfu = NULL; + + list_for_each_entry(dfu, &dfu_list, list) { + if (dfu->alt == alt) + return dfu; + } + + return NULL; +} diff --git a/include/dfu.h b/include/dfu.h new file mode 100644 index 0000000..f7d823b --- /dev/null +++ b/include/dfu.h @@ -0,0 +1,99 @@ +/* + * dfu.h - DFU flashable area description + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz andrzej.p@samsung.com + * Lukasz Majewski l.majewski@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_ENTITY_H_ +#define __DFU_ENTITY_H_ + +#include <common.h> +#include <linux/list.h> +#include <mmc.h> + +enum dfu_device_type { + MMC = 1, + ONENAND, + NAND +}; + +enum dfu_layout { + RAW_ADDR = 1, + FAT, + EXT, +}; + +struct mmc_internal_data { + /* RAW programming */ + unsigned int lba_start; + unsigned int lba_size; + unsigned int lba_blk_size; + + /* FAT/EXT */ + unsigned int dev; + unsigned int part; +}; + +static inline unsigned int get_mmc_blk_size(int dev) +{ + return find_mmc_device(dev)->read_bl_len; +} + +#define DFU_NAME_SIZE 32 +#define DFU_CMD_BUF_SIZE 128 +#define DFU_DATA_BUF_SIZE (1024*1024*4) /* 4 MiB */ + +struct dfu_entity { + char name[DFU_NAME_SIZE]; + int alt; + void *dev_private; + int dev_num; + enum dfu_device_type dev_type; + enum dfu_layout layout; + + union { + struct mmc_internal_data mmc; + } data; + + int (*read_medium)(struct dfu_entity *dfu, void *buf, long *len); + int (*write_medium)(struct dfu_entity *dfu, void *buf, long *len); + + struct list_head list; +}; + +int dfu_config_entities(char *s, char *interface, int num); +void dfu_free_entities(void); +void dfu_show_entities(void); +int dfu_get_alt_number(void); +struct dfu_entity *dfu_get_entity(int alt); +char *dfu_extract_token(char** e, int *n); + +int dfu_read(struct dfu_entity *de, void *buf, int size, int blk_seq_num); +int dfu_write(struct dfu_entity *de, void *buf, int size, int blk_seq_num); +/* Device specific */ +#ifdef CONFIG_DFU_MMC +extern int dfu_fill_entity_mmc(struct dfu_entity *dfu, char* s); +#else +static inline int dfu_fill_entity_mmc(struct dfu_entity *dfu, char* s) +{ + puts("MMC support not available!\n"); + return -1; +} +#endif +#endif /* __DFU_ENTITY_H_ */

Dear Lukasz Majewski,
New, separate driver at ./drivers/dfu has been added. It allows platform and storage independent operation of DFU.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[...]
+char *dfu_extract_token(char** e, int *n) +{
- char *st = *e;
- debug("%s: %s\n", __func__, st);
- strsep(e, " ");
- *n = *e - st;
- return st;
+}
Sigh, what about these? btw don't we have strtok? [...]
Best regards, Marek Vasut

Dear Lukasz Majewski,
In message 1341416922-13792-4-git-send-email-l.majewski@samsung.com you wrote:
New, separate driver at ./drivers/dfu has been added. It allows platform and storage independent operation of DFU.
Sorry for a probably stupid question, but I know basicly zero about DFU. We are talking about "storage independent" here. Does this also mean file system independent?
+static char *dfu_get_dev_type(enum dfu_device_type t) +{
- static char *dev_t[] = {NULL, "MMC", "ONENAND", "NAND" };
- return dev_t[t];
+}
So this currently supports MMC, OneNAND and NAND as storage devices?
+static char *dfu_get_layout(enum dfu_device_type l) +{
- static char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT" };
- return dfu_layout[l];
+}
And FAT (or VFAT?) and EXT (as in EXT2? or EXT3? or ... ?) as file systems?
+enum dfu_device_type {
- MMC = 1,
- ONENAND,
- NAND
+};
+enum dfu_layout {
- RAW_ADDR = 1,
- FAT,
- EXT,
+};
MMC, NAND, FAT and EXT are very generic names that heavily pollute on the global name space. Please chose more specific names, probaly also indicating the meaning (EXT could be some "extension" or "external" or whatever - the name does not indicate that this is a file system type here.
Best regards,
Wolfgang Denk

Dear Wolfgang Denk,
Dear Lukasz Majewski,
In message 1341416922-13792-4-git-send-email-l.majewski@samsung.com you wrote:
New, separate driver at ./drivers/dfu has been added. It allows platform and storage independent operation of DFU.
Sorry for a probably stupid question, but I know basicly zero about DFU. We are talking about "storage independent" here. Does this also mean file system independent?
Some clarification is needed. I've divided DFU support (PATCH v2) to three separate parts: 1. DFU transmission handling (via USB) with ./drivers/usb/gadget/g_dnl.c|f_dfu.c
2. Generic DFU functions ./drivers/dfu/dfu.c - which try to abstract DFU operation to be platform independent. Generic dfu_{write|read} functions have been defined and are accessible from USB code. On the other hand dfu_{write|read} calls function pointers dfu->{read|write}_medium(), which points to medium specific functions.
3. Code for MMC write/read - dfu_mmc.c. It is possible to read/write raw data to MMC (with passing LBA address) or to file systems (like FAT). For now MMC is only supported. It uses (in my opinion) "generic" sprintf+run_command() calls, which can be easily extended. To support OneNAND one needs to define dfu_onenand.c file with OneNAND specific functions.
Considering above, there are already defined "generic" access functions - dfu_{write|read}.
+static char *dfu_get_dev_type(enum dfu_device_type t) +{
- static char *dev_t[] = {NULL, "MMC", "ONENAND", "NAND" };
- return dev_t[t];
+}
So this currently supports MMC, OneNAND and NAND as storage devices?
It currently only supports MMC devices. Others (ONENAND/NAND) have been added as place holders for future usage.
+static char *dfu_get_layout(enum dfu_device_type l) +{
- static char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT",
"EXT" };
- return dfu_layout[l];
+}
And FAT (or VFAT?) and EXT (as in EXT2? or EXT3? or ... ?) as file systems?
+enum dfu_device_type {
- MMC = 1,
- ONENAND,
- NAND
+};
+enum dfu_layout {
- RAW_ADDR = 1,
- FAT,
- EXT,
+};
MMC, NAND, FAT and EXT are very generic names that heavily pollute on the global name space. Please chose more specific names, probaly also indicating the meaning (EXT could be some "extension" or "external" or whatever - the name does not indicate that this is a file system type here.
Ok, no problem with this.
Best regards,
Wolfgang Denk

Dear Lukasz,
In message 20120727151523.41406989@amdc308.digital.local you wrote:
Some clarification is needed. I've divided DFU support (PATCH v2) to three separate parts:
- DFU transmission handling (via USB)
...
- Generic DFU functions ./drivers/dfu/dfu.c - which try to abstract
DFU operation to be platform independent.
...
- Code for MMC write/read - dfu_mmc.c.
OK, than my understanding was mostly correct.
It is possible to read/write raw data to MMC (with passing LBA address) or to file systems (like FAT). For now MMC is only supported. It uses (in my opinion) "generic" sprintf+run_command() calls, which can be easily extended. To support OneNAND one needs to define dfu_onenand.c file with OneNAND specific functions.
Correct. And adaption for other devices (say, NAND or USB mass storage) should be trivial as well.
Considering above, there are already defined "generic" access functions
- dfu_{write|read}.
OK - but the device specific stuff is only used in the sprintf() command then. That's why I recommend to move just this very small function into a separate file, which can be replaced or removed later.
So this currently supports MMC, OneNAND and NAND as storage devices?
It currently only supports MMC devices. Others (ONENAND/NAND) have been added as place holders for future usage.
Yes, I understand. But then, adding such support looks pretty straightforward, and even trivial to me. You provided a pretty clear infrastructure for this, thanks.
Best regards,
Wolfgang Denk

Dear Wolfgang Denk,
Correct. And adaption for other devices (say, NAND or USB mass storage) should be trivial as well.
USB Mass Storage is also under development. It is supposed to be a function (f_ums.c) for g_dnl.c composite gadget.
I will postpone posting it to ML until we agree for the DFU shape (and thereof the g_dnl composite gadget driver).

Support for MMC storage devices to work with DFU framework.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2: - None --- drivers/dfu/Makefile | 1 + drivers/dfu/dfu_mmc.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 0 deletions(-) create mode 100644 drivers/dfu/dfu_mmc.c
diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index 7736485..7b717bc 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -26,6 +26,7 @@ include $(TOPDIR)/config.mk LIB = $(obj)libdfu.o
COBJS-$(CONFIG_DFU_FUNCTION) += dfu.o +COBJS-$(CONFIG_DFU_MMC) += dfu_mmc.o
SRCS := $(COBJS-y:.o=.c) OBJS := $(addprefix $(obj),$(COBJS-y)) diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c new file mode 100644 index 0000000..3151fbc --- /dev/null +++ b/drivers/dfu/dfu_mmc.c @@ -0,0 +1,126 @@ +/* + * dfu.c -- DFU back-end routines + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz andrzej.p@samsung.com + * Lukasz Majewski l.majewski@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 <dfu.h> + +int dfu_write_medium_mmc(struct dfu_entity *dfu, void *buf, long *len) +{ + ALLOC_CACHE_ALIGN_BUFFER(char, cmd_buf, DFU_CMD_BUF_SIZE); + + memset(cmd_buf, '\0', sizeof(cmd_buf)); + + switch (dfu->layout) { + case RAW_ADDR: + sprintf(cmd_buf, "mmc write 0x%x %x %x", (unsigned int) buf, + dfu->data.mmc.lba_start, dfu->data.mmc.lba_size); + break; + case FAT: + sprintf(cmd_buf, "fatwrite mmc %d:%d 0x%x %s %lx", + dfu->data.mmc.dev, dfu->data.mmc.part, + (unsigned int) buf, dfu->name, *len); + break; + default: + printf("%s: Wrong layout!\n", __func__); + } + + debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); + run_command(cmd_buf, 0); + + return 0; +} + +int dfu_read_medium_mmc(struct dfu_entity *dfu, void *buf, long *len) +{ + ALLOC_CACHE_ALIGN_BUFFER(char, cmd_buf, DFU_CMD_BUF_SIZE); + char *str_env = NULL; + int ret = 0; + + memset(cmd_buf, '\0', sizeof(cmd_buf)); + + switch (dfu->layout) { + case RAW_ADDR: + sprintf(cmd_buf, "mmc read 0x%x %x %x", (unsigned int) buf, + dfu->data.mmc.lba_start, dfu->data.mmc.lba_size); + + *len = dfu->data.mmc.lba_blk_size * dfu->data.mmc.lba_size; + break; + case FAT: + sprintf(cmd_buf, "fatload mmc %d:%d 0x%x %s", + dfu->data.mmc.dev, dfu->data.mmc.part, + (unsigned int) buf, dfu->name); + break; + default: + printf("%s: Wrong layout!\n", __func__); + } + + debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); + + ret = run_command(cmd_buf, 0); + if (ret) { + puts("dfu: Read error!\n"); + return ret; + } + + if (dfu->layout != RAW_ADDR) { + str_env = getenv("filesize"); + if (str_env == NULL) { + puts("dfu: Wrong file size!\n"); + return -1; + } + + *len = simple_strtoul(str_env, NULL, 16); + } + return ret; +} + +int dfu_fill_entity_mmc(struct dfu_entity *dfu, char* s) +{ + char *st = NULL; + int n = 0; + + dfu->dev_type = MMC; + st = dfu_extract_token(&s, &n); + + if (!strncmp(st, "mmc", n)) { + dfu->layout = RAW_ADDR; + + dfu->data.mmc.lba_start = simple_strtoul(s, &s, 16); + dfu->data.mmc.lba_size = simple_strtoul(++s, &s, 16); + dfu->data.mmc.lba_blk_size = get_mmc_blk_size(dfu->dev_num); + + } else if (!strncmp(st, "fat", n)) { + dfu->layout = FAT; + + dfu->data.mmc.dev = simple_strtoul(s, &s, 10); + dfu->data.mmc.part = simple_strtoul(++s, &s, 10); + + } else { + printf("%s: Wrong memory layout!\n", __func__); + } + + dfu->read_medium = dfu_read_medium_mmc; + dfu->write_medium = dfu_write_medium_mmc; + + return 0; +}

Dear Lukasz Majewski,
Support for MMC storage devices to work with DFU framework.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
This one opens some questions.
[...]
+int dfu_fill_entity_mmc(struct dfu_entity *dfu, char* s) +{
- char *st = NULL;
- int n = 0;
- dfu->dev_type = MMC;
- st = dfu_extract_token(&s, &n);
- if (!strncmp(st, "mmc", n)) {
dfu->layout = RAW_ADDR;
dfu->data.mmc.lba_start = simple_strtoul(s, &s, 16);
dfu->data.mmc.lba_size = simple_strtoul(++s, &s, 16);
dfu->data.mmc.lba_blk_size = get_mmc_blk_size(dfu->dev_num);
^ one line too much.
- } else if (!strncmp(st, "fat", n)) {
dfu->layout = FAT;
dfu->data.mmc.dev = simple_strtoul(s, &s, 10);
dfu->data.mmc.part = simple_strtoul(++s, &s, 10);
- } else {
printf("%s: Wrong memory layout!\n", __func__);
- }
- dfu->read_medium = dfu_read_medium_mmc;
- dfu->write_medium = dfu_write_medium_mmc;
- return 0;
+}
Best regards, Marek Vasut

On Wed, Jul 04, 2012 at 05:48:39PM +0200, Lukasz Majewski wrote:
Support for MMC storage devices to work with DFU framework.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[snip]
- case RAW_ADDR:
sprintf(cmd_buf, "mmc write 0x%x %x %x", (unsigned int) buf,
dfu->data.mmc.lba_start, dfu->data.mmc.lba_size);
break;
- case FAT:
sprintf(cmd_buf, "fatwrite mmc %d:%d 0x%x %s %lx",
dfu->data.mmc.dev, dfu->data.mmc.part,
(unsigned int) buf, dfu->name, *len);
break;
- default:
printf("%s: Wrong layout!\n", __func__);
- }
- debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
- run_command(cmd_buf, 0);
If we try and take the long-view here, that fatwrite/mmc write don't perform a lot of sanity checking on input isn't good. Lots of commands I believe don't, but we can start somewhere. So, lets do what Marek was suggesting of making common/cmd_mmc.c and common/cmd_fat.c call a sub-function that takes compile-time typecheckable inputs, and call that here. That opens things up for later making the user commands perform better checking and so forth.

Hi Tom,
On Wed, Jul 04, 2012 at 05:48:39PM +0200, Lukasz Majewski wrote:
Support for MMC storage devices to work with DFU framework.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[snip]
- case RAW_ADDR:
sprintf(cmd_buf, "mmc write 0x%x %x %x", (unsigned
int) buf,
dfu->data.mmc.lba_start,
dfu->data.mmc.lba_size);
break;
- case FAT:
sprintf(cmd_buf, "fatwrite mmc %d:%d 0x%x %s %lx",
dfu->data.mmc.dev, dfu->data.mmc.part,
(unsigned int) buf, dfu->name, *len);
break;
- default:
printf("%s: Wrong layout!\n", __func__);
- }
- debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
- run_command(cmd_buf, 0);
If we try and take the long-view here, that fatwrite/mmc write don't perform a lot of sanity checking on input isn't good. Lots of commands I believe don't, but we can start somewhere.
Yes, indeed they don't. But I think, that it is a deeper problem.
When one looks into the cmd_mmc.c, the code is not checking the correctness of passed data. It performs strncmp, then simple_strtoul and with this parameter calls mmc->block_dev.block_read().
But I'm a bit concern if adding function:
do_mmcops_check(unsigned int lba_start, unsigned int lba_end, ...) to
do_mmcops(argc, argv) { int i = simple_strtol(argv[]); return do_mmcops_check(i); }
will help with preventing errors.
As I've written previously, data from prompt is passed in a form of text, which is converted to the well defined type anyway (with e.g. simple_strtoul - returns unsigned long, strict_strtoul - returns int, simple_strtol - returns long, simple_strtoull - returns unsigned long long). Using those functions correctly would ensure correct types passed to e.g. mmc->block_dev.block_read().
When one create the do_mmcops_check() function, the compiler would check if its arguments are correct (i.e. the i in the above example is int). What is the difference between checking at compile time the output of simple_strtoul?
The real problem in my opinion is the lack of checking if arguments passed as text to the do_mmcops are correct or not. This is not done for MMC and I doubt, if adding compile time type checking (in a form of a separate function) would solve/alleviate the problem.
So, lets do what Marek was suggesting of making common/cmd_mmc.c and common/cmd_fat.c call a sub-function that takes compile-time typecheckable inputs, and call that here. That opens things up for later making the user commands perform better checking and so forth.
I'd like to point to the problem with passing and then parsing parameters as text.
User typed parameters aren't checked.
Please correct me if I misunderstood the problem or the proposed solution.

On Tue, Jul 10, 2012 at 12:38:54PM +0200, Lukasz Majewski wrote:
Hi Tom,
On Wed, Jul 04, 2012 at 05:48:39PM +0200, Lukasz Majewski wrote:
Support for MMC storage devices to work with DFU framework.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[snip]
- case RAW_ADDR:
sprintf(cmd_buf, "mmc write 0x%x %x %x", (unsigned
int) buf,
dfu->data.mmc.lba_start,
dfu->data.mmc.lba_size);
break;
- case FAT:
sprintf(cmd_buf, "fatwrite mmc %d:%d 0x%x %s %lx",
dfu->data.mmc.dev, dfu->data.mmc.part,
(unsigned int) buf, dfu->name, *len);
break;
- default:
printf("%s: Wrong layout!\n", __func__);
- }
- debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
- run_command(cmd_buf, 0);
If we try and take the long-view here, that fatwrite/mmc write don't perform a lot of sanity checking on input isn't good. Lots of commands I believe don't, but we can start somewhere.
Yes, indeed they don't. But I think, that it is a deeper problem.
When one looks into the cmd_mmc.c, the code is not checking the correctness of passed data. It performs strncmp, then simple_strtoul and with this parameter calls mmc->block_dev.block_read().
But I'm a bit concern if adding function:
do_mmcops_check(unsigned int lba_start, unsigned int lba_end, ...) to
do_mmcops(argc, argv) { int i = simple_strtol(argv[]); return do_mmcops_check(i); }
Well, what I was suggesting would be:
do_mmcops_real(uint lba_start, ...) { .. most of do_mmcops today .. } do_mmcops_from_cmd(argc, argv) { ... convert user input today, maybe try and sanity check input tomorrow .. }
And then dfu calls do_mmcops_real(lba_start, ...). A further clean-up would be to make the interface the command uses to perform checking of the arguments passed. Does this make sense?

On Wed, 11 Jul 2012 04:54:31 -0700 Tom Rini trini@ti.com wrote:
On Tue, Jul 10, 2012 at 12:38:54PM +0200, Lukasz Majewski wrote:
Hi Tom,
On Wed, Jul 04, 2012 at 05:48:39PM +0200, Lukasz Majewski wrote:
Support for MMC storage devices to work with DFU framework.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[snip]
- case RAW_ADDR:
sprintf(cmd_buf, "mmc write 0x%x %x %x",
(unsigned int) buf,
dfu->data.mmc.lba_start,
dfu->data.mmc.lba_size);
break;
- case FAT:
sprintf(cmd_buf, "fatwrite mmc %d:%d 0x%x %s
%lx",
dfu->data.mmc.dev, dfu->data.mmc.part,
(unsigned int) buf, dfu->name, *len);
break;
- default:
printf("%s: Wrong layout!\n", __func__);
- }
- debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
- run_command(cmd_buf, 0);
If we try and take the long-view here, that fatwrite/mmc write don't perform a lot of sanity checking on input isn't good. Lots of commands I believe don't, but we can start somewhere.
Yes, indeed they don't. But I think, that it is a deeper problem.
When one looks into the cmd_mmc.c, the code is not checking the correctness of passed data. It performs strncmp, then simple_strtoul and with this parameter calls mmc->block_dev.block_read().
But I'm a bit concern if adding function:
do_mmcops_check(unsigned int lba_start, unsigned int lba_end, ...) to
do_mmcops(argc, argv) { int i = simple_strtol(argv[]); return do_mmcops_check(i); }
Well, what I was suggesting would be:
do_mmcops_real(uint lba_start, ...) { .. most of do_mmcops today .. } do_mmcops_from_cmd(argc, argv) { ... convert user input today, maybe try and sanity check input tomorrow .. }
And then dfu calls do_mmcops_real(lba_start, ...). A further clean-up would be to make the interface the command uses to perform checking of the arguments passed. Does this make sense?
Generally it is in my opinion a good way to go.
However, why we aren't first writing sanity checks for passed arguments?
We are adding one more level of abstraction, but don't think of the main problem (checking values of passed arguments)?
Anyway we shall wait for Marek's opinion.

On Thu, Jul 12, 2012 at 02:39:27PM +0200, Lukasz Majewski wrote:
On Wed, 11 Jul 2012 04:54:31 -0700 Tom Rini trini@ti.com wrote:
On Tue, Jul 10, 2012 at 12:38:54PM +0200, Lukasz Majewski wrote:
Hi Tom,
On Wed, Jul 04, 2012 at 05:48:39PM +0200, Lukasz Majewski wrote:
Support for MMC storage devices to work with DFU framework.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[snip]
- case RAW_ADDR:
sprintf(cmd_buf, "mmc write 0x%x %x %x",
(unsigned int) buf,
dfu->data.mmc.lba_start,
dfu->data.mmc.lba_size);
break;
- case FAT:
sprintf(cmd_buf, "fatwrite mmc %d:%d 0x%x %s
%lx",
dfu->data.mmc.dev, dfu->data.mmc.part,
(unsigned int) buf, dfu->name, *len);
break;
- default:
printf("%s: Wrong layout!\n", __func__);
- }
- debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
- run_command(cmd_buf, 0);
If we try and take the long-view here, that fatwrite/mmc write don't perform a lot of sanity checking on input isn't good. Lots of commands I believe don't, but we can start somewhere.
Yes, indeed they don't. But I think, that it is a deeper problem.
When one looks into the cmd_mmc.c, the code is not checking the correctness of passed data. It performs strncmp, then simple_strtoul and with this parameter calls mmc->block_dev.block_read().
But I'm a bit concern if adding function:
do_mmcops_check(unsigned int lba_start, unsigned int lba_end, ...) to
do_mmcops(argc, argv) { int i = simple_strtol(argv[]); return do_mmcops_check(i); }
Well, what I was suggesting would be:
do_mmcops_real(uint lba_start, ...) { .. most of do_mmcops today .. } do_mmcops_from_cmd(argc, argv) { ... convert user input today, maybe try and sanity check input tomorrow .. }
And then dfu calls do_mmcops_real(lba_start, ...). A further clean-up would be to make the interface the command uses to perform checking of the arguments passed. Does this make sense?
Generally it is in my opinion a good way to go.
However, why we aren't first writing sanity checks for passed arguments?
Simply because I didn't want to ask you to do a lot more unrelated work :) If you want to split and check the mmc (and fatwrite) argueuments and then make the DFU series depend on that, by all means please do so!
We are adding one more level of abstraction, but don't think of the main problem (checking values of passed arguments)?
Anyway we shall wait for Marek's opinion.
Yes, a good idea as well.

Dear Tom Rini,
On Thu, Jul 12, 2012 at 02:39:27PM +0200, Lukasz Majewski wrote:
On Wed, 11 Jul 2012 04:54:31 -0700
Tom Rini trini@ti.com wrote:
On Tue, Jul 10, 2012 at 12:38:54PM +0200, Lukasz Majewski wrote:
Hi Tom,
On Wed, Jul 04, 2012 at 05:48:39PM +0200, Lukasz Majewski wrote:
Support for MMC storage devices to work with DFU framework.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
[snip]
- case RAW_ADDR:
sprintf(cmd_buf, "mmc write 0x%x %x %x",
(unsigned int) buf,
dfu->data.mmc.lba_start,
dfu->data.mmc.lba_size);
break;
- case FAT:
sprintf(cmd_buf, "fatwrite mmc %d:%d 0x%x %s
%lx",
dfu->data.mmc.dev, dfu->data.mmc.part,
(unsigned int) buf, dfu->name, *len);
break;
- default:
printf("%s: Wrong layout!\n", __func__);
- }
- debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
- run_command(cmd_buf, 0);
If we try and take the long-view here, that fatwrite/mmc write don't perform a lot of sanity checking on input isn't good. Lots of commands I believe don't, but we can start somewhere.
Yes, indeed they don't. But I think, that it is a deeper problem.
When one looks into the cmd_mmc.c, the code is not checking the correctness of passed data. It performs strncmp, then simple_strtoul and with this parameter calls mmc->block_dev.block_read().
But I'm a bit concern if adding function:
do_mmcops_check(unsigned int lba_start, unsigned int lba_end, ...) to
do_mmcops(argc, argv) {
int i = simple_strtol(argv[]); return do_mmcops_check(i);
}
Well, what I was suggesting would be:
do_mmcops_real(uint lba_start, ...) { .. most of do_mmcops today .. } do_mmcops_from_cmd(argc, argv) {
... convert user input today, maybe try and sanity check input
tomorrow .. }
And then dfu calls do_mmcops_real(lba_start, ...). A further clean-up would be to make the interface the command uses to perform checking of the arguments passed. Does this make sense?
Generally it is in my opinion a good way to go.
However, why we aren't first writing sanity checks for passed arguments?
Simply because I didn't want to ask you to do a lot more unrelated work
:) If you want to split and check the mmc (and fatwrite) argueuments
and then make the DFU series depend on that, by all means please do so!
Would be cool indeed.
We are adding one more level of abstraction, but don't think of the main problem (checking values of passed arguments)?
Anyway we shall wait for Marek's opinion.
Yes, a good idea as well.
My opinion is that if you'll do the sanity checks, that'd be good. We're right before .07 release anyway, so the patches will hit the next merge window. Are you up for doing a lot of unrelated work to make this proper?
Best regards, Marek Vasut

Generally it is in my opinion a good way to go.
However, why we aren't first writing sanity checks for passed arguments?
Simply because I didn't want to ask you to do a lot more unrelated work
:) If you want to split and check the mmc (and fatwrite) argueuments
and then make the DFU series depend on that, by all means please do so!
Would be cool indeed.
We are adding one more level of abstraction, but don't think of the main problem (checking values of passed arguments)?
Anyway we shall wait for Marek's opinion.
Yes, a good idea as well.
My opinion is that if you'll do the sanity checks, that'd be good. We're right before .07 release anyway, so the patches will hit the next merge window. Are you up for doing a lot of unrelated work to make this proper?
I agree with the general sense that adding sanity checking would be good. Just because I was too lazy to add them, doesn't mean I was right. ;)
Andy

Dear Lukasz Majewski,
In message 1341416922-13792-5-git-send-email-l.majewski@samsung.com you wrote:
Support for MMC storage devices to work with DFU framework.
Sorry for jumping in late.
- switch (dfu->layout) {
- case RAW_ADDR:
sprintf(cmd_buf, "mmc write 0x%x %x %x", (unsigned int) buf,
dfu->data.mmc.lba_start, dfu->data.mmc.lba_size);
break;
- case FAT:
sprintf(cmd_buf, "fatwrite mmc %d:%d 0x%x %s %lx",
dfu->data.mmc.dev, dfu->data.mmc.part,
(unsigned int) buf, dfu->name, *len);
break;
- default:
printf("%s: Wrong layout!\n", __func__);
- }
In case of error, you should always print what the unexpected data was. The end user who receives an "Wrong layout!" error is probably pretty much surpised and doesn't know what he did wrong. If you print instead: "EXT2 layout not supported (yet)" he would know exactly what to change to make it work.
There has been some dicussion already if using this sprintf() / run_command() approach is good or not, or how it should be fixed.
It appears, all this discussion forgot to take into account that patch 3/7 dfu: DFU backend implementation promised to add "platform and storage independent operation of DFU." Here we are breaking this promise.
And by adding special functions to the FAT file system code thing sget just worse, as now not only the DFU code needs to be extended when we want to use this with any other file system type, but suddenly this also bleeds into all supported file system code.
I am aware that the current implementation suffers from the fact that we don't have a unified access to file systems - each comes with it's own set of commands and more or less (in)compatible command line and function call interfaces. This will hopefully improve in the context of the device model rework, but we don't want to block you until then. But considering that this is going to change in a foreseeable future, it also makes littel sense to invest big efforts into generic code that covers all potential future uses.
Here is my poposal:
Let's introduce a thin shim layer to abstract the file system interface from the accesses you need here. As far as I can see here, you need exactly 4 functions:
- block_read() - block_write() - file_read() - file_write()
These names could be function pointers to implement the device I/O in a both device and file system agnostic way; you can implement specific versions of the functions like mmc_block_read(), usb_block_read(), ... etc. and use the settings of dfu_device_type and dfu_layout to set the pointers to these functions.
For this shim layer I would actually prefer the original approach (shown above) to use sprintf() to build commands based on the existing command interface - this is the minimal intrusive way to implement this for now. With this approach, no modifications to file system and/or device driver code are needed, and we still maintain full flexibility here in the DFU code. Yes, error checking may not be perfect, and we my not win a price for elegance of design either, but we should get mostly clean, working code quickly.
The advantage I see for this code is that we have separated all this device interface stuff from both the generic DFU code and from the device and file system code as well. As soon as we have a better implementation below, all we need to adjust (or potentially even remove) is this shim layer.
Best regards,
Wolfgang Denk

Dear Wolfgang Denk,
Dear Lukasz Majewski,
In message 1341416922-13792-5-git-send-email-l.majewski@samsung.com you
wrote:
Support for MMC storage devices to work with DFU framework.
Sorry for jumping in late.
- switch (dfu->layout) {
- case RAW_ADDR:
sprintf(cmd_buf, "mmc write 0x%x %x %x", (unsigned int) buf,
dfu->data.mmc.lba_start, dfu->data.mmc.lba_size);
break;
- case FAT:
sprintf(cmd_buf, "fatwrite mmc %d:%d 0x%x %s %lx",
dfu->data.mmc.dev, dfu->data.mmc.part,
(unsigned int) buf, dfu->name, *len);
break;
- default:
printf("%s: Wrong layout!\n", __func__);
- }
In case of error, you should always print what the unexpected data was. The end user who receives an "Wrong layout!" error is probably pretty much surpised and doesn't know what he did wrong. If you print instead: "EXT2 layout not supported (yet)" he would know exactly what to change to make it work.
There has been some dicussion already if using this sprintf() / run_command() approach is good or not, or how it should be fixed.
It appears, all this discussion forgot to take into account that patch 3/7 dfu: DFU backend implementation promised to add "platform and storage independent operation of DFU." Here we are breaking this promise.
And by adding special functions to the FAT file system code thing sget just worse, as now not only the DFU code needs to be extended when we want to use this with any other file system type, but suddenly this also bleeds into all supported file system code.
I am aware that the current implementation suffers from the fact that we don't have a unified access to file systems - each comes with it's own set of commands and more or less (in)compatible command line and function call interfaces. This will hopefully improve in the context of the device model rework, but we don't want to block you until then. But considering that this is going to change in a foreseeable future, it also makes littel sense to invest big efforts into generic code that covers all potential future uses.
Here is my poposal:
Let's introduce a thin shim layer to abstract the file system interface from the accesses you need here. As far as I can see here, you need exactly 4 functions:
- block_read()
- block_write()
- file_read()
- file_write()
These names could be function pointers to implement the device I/O in a both device and file system agnostic way; you can implement specific versions of the functions like mmc_block_read(), usb_block_read(), ... etc. and use the settings of dfu_device_type and dfu_layout to set the pointers to these functions.
For this shim layer I would actually prefer the original approach (shown above) to use sprintf() to build commands based on the existing command interface
We discussed it for a while ... figure out the missing typechecking and abuse of the command line interface is not a way to go.
this is the minimal intrusive way to implement this for now. With this approach, no modifications to file system and/or device driver code are needed, and we still maintain full flexibility here in the DFU code. Yes, error checking may not be perfect, and we my not win a price for elegance of design either, but we should get mostly clean, working code quickly.
But now that we started going in the typechecking way, I'd prefer to go all the way. And once DFU extends properly towards other supported filesystems/devices, the rest of the interface can get cleaned along the way.
The advantage I see for this code is that we have separated all this device interface stuff from both the generic DFU code and from the device and file system code as well. As soon as we have a better implementation below, all we need to adjust (or potentially even remove) is this shim layer.
Shim layer is cool -- abusing command line is not cool ;-)
Best regards,
Wolfgang Denk

Dear Marek Vasut,
In message 201207271443.45189.marex@denx.de you wrote:
For this shim layer I would actually prefer the original approach (shown above) to use sprintf() to build commands based on the existing command interface
We discussed it for a while ... figure out the missing typechecking and abuse of the command line interface is not a way to go.
I agree that this is not the preferred way for a permanent solution, but for now I cxonsider it acceptable, as it allows to easily deal with the inconsistent API of the misc file system handlers. For example instead of
sprintf(cmd_buf, "fatread mmc %d:%d 0x%x %s %lx", ...
we can even do something like
sprintf(cmd_buf, "%s %s %d:%d 0x%x %s %lx", command, device, ...
and set command as needed to "fatread" or "ext2load" or whatever, and device can be set to "mmc" or "usb" or ...
this is the minimal intrusive way to implement this for now. With this approach, no modifications to file system and/or device driver code are needed, and we still maintain full flexibility here in the DFU code. Yes, error checking may not be perfect, and we my not win a price for elegance of design either, but we should get mostly clean, working code quickly.
But now that we started going in the typechecking way, I'd prefer to go all the way. And once DFU extends properly towards other supported filesystems/devices, the rest of the interface can get cleaned along the way.
You can implement the type checking code when you hav ea stable, well define API for storage devices and file systems. For now we don't have that, and it makes ZERO sense to add special code just for that just to throw it away in a few weeks.
Shim layer is cool -- abusing command line is not cool ;-)
Whether this is abuse or clever use remains to be defined, and this is probably largely a matter of taste. In any case, this will be a small piece of code that is of clearly transient nature, to be replaced or removed as soon as we can do better.
Best regards,
Wolfgang Denk

Dear Wolfgang Denk,
Dear Marek Vasut,
In message 201207271443.45189.marex@denx.de you wrote:
For this shim layer I would actually prefer the original approach (shown above) to use sprintf() to build commands based on the existing command interface
We discussed it for a while ... figure out the missing typechecking and abuse of the command line interface is not a way to go.
I agree that this is not the preferred way for a permanent solution, but for now I cxonsider it acceptable, as it allows to easily deal with the inconsistent API of the misc file system handlers. For example instead of
sprintf(cmd_buf, "fatread mmc %d:%d 0x%x %s %lx", ...
we can even do something like
sprintf(cmd_buf, "%s %s %d:%d 0x%x %s %lx", command, device, ...
and set command as needed to "fatread" or "ext2load" or whatever, and device can be set to "mmc" or "usb" or ...
Given that they have slightly different syntax, this is crazy too.
this is the minimal intrusive way to implement this for now. With this approach, no modifications to file system and/or device driver code are needed, and we still maintain full flexibility here in the DFU code. Yes, error checking may not be perfect, and we my not win a price for elegance of design either, but we should get mostly clean, working code quickly.
But now that we started going in the typechecking way, I'd prefer to go all the way. And once DFU extends properly towards other supported filesystems/devices, the rest of the interface can get cleaned along the way.
You can implement the type checking code when you hav ea stable, well define API for storage devices and file systems. For now we don't have that, and it makes ZERO sense to add special code just for that just to throw it away in a few weeks.
Ad API -- adding Pavel to CC. Hope his GMail works better than mine (mine doesn't, crashed again, damned google stuff). Pavel, can you provide us with how the API will look maybe?
Shim layer is cool -- abusing command line is not cool ;-)
Whether this is abuse or clever use remains to be defined, and this is probably largely a matter of taste. In any case, this will be a small piece of code that is of clearly transient nature, to be replaced or removed as soon as we can do better.
Best regards,
Wolfgang Denk
Well ... after this discussion, I feel like I'll just go dig me a grave again.
Best regards, Marek Vasut

Dear Marek Vasut,
In message 201207271515.36085.marex@denx.de you wrote:
sprintf(cmd_buf, "%s %s %d:%d 0x%x %s %lx", command, device, ...
and set command as needed to "fatread" or "ext2load" or whatever, and device can be set to "mmc" or "usb" or ...
Given that they have slightly different syntax, this is crazy too.
Draw the line between clever and crazy, genius and idiot :-)
Well ... after this discussion, I feel like I'll just go dig me a grave again.
Why? I think the discussion has been beneficial - we isolated a poor piece of code, discussed how it should be improved, and now ry to find an intermediate solution that adds not too much work for Lukasz while still keeing the existing code clean and allowing for easy adaption to the new DM interfaces - three worthwile goals at once.
Best regards,
Wolfgang Denk

Dear Wolfgang Denk,
Dear Lukasz Majewski,
In message 1341416922-13792-5-git-send-email-l.majewski@samsung.com you wrote:
Support for MMC storage devices to work with DFU framework.
Sorry for jumping in late.
- switch (dfu->layout) {
- case RAW_ADDR:
sprintf(cmd_buf, "mmc write 0x%x %x %x", (unsigned
int) buf,
dfu->data.mmc.lba_start,
dfu->data.mmc.lba_size);
break;
- case FAT:
sprintf(cmd_buf, "fatwrite mmc %d:%d 0x%x %s %lx",
dfu->data.mmc.dev, dfu->data.mmc.part,
(unsigned int) buf, dfu->name, *len);
break;
- default:
printf("%s: Wrong layout!\n", __func__);
- }
In case of error, you should always print what the unexpected data was. The end user who receives an "Wrong layout!" error is probably pretty much surpised and doesn't know what he did wrong. If you print instead: "EXT2 layout not supported (yet)" he would know exactly what to change to make it work.
Ok, this can be easily corrected.
There has been some dicussion already if using this sprintf() / run_command() approach is good or not, or how it should be fixed.
It appears, all this discussion forgot to take into account that patch 3/7 dfu: DFU backend implementation promised to add "platform and storage independent operation of DFU." Here we are breaking this promise.
And by adding special functions to the FAT file system code thing sget just worse, as now not only the DFU code needs to be extended when we want to use this with any other file system type, but suddenly this also bleeds into all supported file system code.
I am aware that the current implementation suffers from the fact that we don't have a unified access to file systems - each comes with it's own set of commands and more or less (in)compatible command line and function call interfaces. This will hopefully improve in the context of the device model rework, but we don't want to block you until then. But considering that this is going to change in a foreseeable future, it also makes littel sense to invest big efforts into generic code that covers all potential future uses.
Here is my poposal:
Let's introduce a thin shim layer to abstract the file system interface from the accesses you need here. As far as I can see here, you need exactly 4 functions:
- block_read()
- block_write()
- file_read()
- file_write()
These names could be function pointers to implement the device I/O in a both device and file system agnostic way; you can implement specific versions of the functions like mmc_block_read(), usb_block_read(), ... etc. and use the settings of dfu_device_type and dfu_layout to set the pointers to these functions.
For this shim layer I would actually prefer the original approach (shown above) to use sprintf() to build commands based on the existing command interface - this is the minimal intrusive way to implement this for now. With this approach, no modifications to file system and/or device driver code are needed, and we still maintain full flexibility here in the DFU code. Yes, error checking may not be perfect, and we my not win a price for elegance of design either, but we should get mostly clean, working code quickly.
So I suppose, that you are proposing something like this (pseudo code):
int mmc_block_write() { sprintf(cmd_buf, "mmc write 0x%x %x %x") run_command(cmd_buf, 0); }
int mmc_file_write(enum fs_type) { switch (fs_type) case FAT: sprintf(cmd_buf, "fatwrite mmc %d:%d 0x%x %s %lx") break; case EXT4: sprintf(cmd_buf, "ext4write mmc %d:%d 0x%x %s %lx") break; run_command(cmd_buf); }
int dfu_write_medium_mmc(struct dfu_entity *dfu, void *buf, long *len) { ALLOC_CACHE_ALIGN_BUFFER(char, cmd_buf, DFU_CMD_BUF_SIZE);
memset(cmd_buf, '\0', sizeof(cmd_buf));
switch (dfu->layout) { case RAW_ADDR: mmc_block_write() break; case FAT: case EXT3: case EXT4: mmc_file_write(dfu->layout); break; default: printf("%s: Wrong layout %s!\n", __func__, layout_table[dfu->layout]); }
return 0; }
The advantage I see for this code is that we have separated all this device interface stuff from both the generic DFU code and from the device and file system code as well. As soon as we have a better implementation below, all we need to adjust (or potentially even remove) is this shim layer.

Dear Lukasz,
In message 20120727153345.008fde41@amdc308.digital.local you wrote:
So I suppose, that you are proposing something like this (pseudo code):
Yes, very close.
int mmc_block_write() { sprintf(cmd_buf, "mmc write 0x%x %x %x") run_command(cmd_buf, 0); }
int mmc_file_write(enum fs_type) { switch (fs_type) case FAT: sprintf(cmd_buf, "fatwrite mmc %d:%d 0x%x %s %lx") break; case EXT4: sprintf(cmd_buf, "ext4write mmc %d:%d 0x%x %s %lx") break; run_command(cmd_buf); }
int dfu_write_medium_mmc(struct dfu_entity *dfu, void *buf, long *len) {
I suggest just use
int dfu_write_medium(struct dfu_entity *dfu, void *buf, long *len)
here; you can then so something like
switch (dfu->dev_type) { case MMC: block_write = mmc_block_write; file_write = mmc_file_write; break; #ifdef NAND_SUPPORT_AVAILABLE case NAND: block_write = nand_block_write; file_write = nand_block_write; break; #endif ...
and use block_write() resp. file_write() in the rest. So the code is really trivial to extend for other storage devices and file systems.
I feel we are very close, thanks!
Let's see if Pavel adds some comments about the best API to chose to be as compatible with possible with the upcoming block device layer, and then we can go for it.
Best regards,
Wolfgang Denk

Support for u-boot's command line command "dfu <interface> <dev> [list]".
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2: - None --- common/Makefile | 1 + common/cmd_dfu.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 0 deletions(-) create mode 100644 common/cmd_dfu.c
diff --git a/common/Makefile b/common/Makefile index 31175e3..9d60fe1 100644 --- a/common/Makefile +++ b/common/Makefile @@ -184,6 +184,7 @@ COBJS-$(CONFIG_MENU) += menu.o COBJS-$(CONFIG_MODEM_SUPPORT) += modem.o COBJS-$(CONFIG_UPDATE_TFTP) += update.o COBJS-$(CONFIG_USB_KEYBOARD) += usb_kbd.o +COBJS-$(CONFIG_CMD_DFU) += cmd_dfu.o endif
ifdef CONFIG_SPL_BUILD diff --git a/common/cmd_dfu.c b/common/cmd_dfu.c new file mode 100644 index 0000000..ceb0f54 --- /dev/null +++ b/common/cmd_dfu.c @@ -0,0 +1,81 @@ +/* + * cmd_dfu.c -- dfu command + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz andrzej.p@samsung.com + * Lukasz Majewski l.majewski@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 <malloc.h> +#include <dfu.h> +#include <asm/errno.h> +#include <g_dnl.h> + +int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *str_env = NULL, *env_bkp = NULL; + static char *s = "dfu"; + int ret = 0; + + if (argc < 3) + return CMD_RET_USAGE; + + str_env = getenv("dfu_alt_info"); + if (str_env == NULL) { + printf("%s: "dfu_alt_info" env variable not defined!\n", + __func__); + return CMD_RET_FAILURE; + } + + env_bkp = strdup(str_env); + ret = dfu_config_entities(env_bkp, argv[1], + (int)simple_strtoul(argv[2], NULL, 10)); + if (ret) + return CMD_RET_FAILURE; + + if (strcmp(argv[3], "list") == 0) { + dfu_show_entities(); + dfu_free_entities(); + free(env_bkp); + return CMD_RET_SUCCESS; + } + + board_usb_init(); + g_dnl_init(s); + while (1) { + if (ctrlc()) + goto exit; + + usb_gadget_handle_interrupts(); + } +exit: + g_dnl_cleanup(); + dfu_free_entities(); + free(env_bkp); + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu, + "Device Firmware Upgrade", + "<interface> <dev> [list]\n" + " - device firmware upgrade on a device <dev>\n" + " attached to interface <interface>\n" + " [list] - list available alt settings" +);

Support for USB UDC driver at trats board.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de Cc: Minkyu Kang mk7.kang@samsung.com
--- Changes for v2: - replace puts to debug --- board/samsung/trats/trats.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/board/samsung/trats/trats.c b/board/samsung/trats/trats.c index a95a516..a557e5c 100644 --- a/board/samsung/trats/trats.c +++ b/board/samsung/trats/trats.c @@ -59,6 +59,8 @@ static int hwrevision(int rev) return (board_rev & 0xf) == rev; }
+struct s3c_plat_otg_data s5pc210_otg_data; + int board_init(void) { gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100; @@ -259,6 +261,12 @@ struct s3c_plat_otg_data s5pc210_otg_data = { .usb_phy_ctrl = EXYNOS4_USBPHY_CONTROL, .usb_flags = PHY0_SLEEP, }; + +void board_usb_init(void) +{ + debug("USB_udc_probe\n"); + s3c_udc_probe(&s5pc210_otg_data); +} #endif
static void pmic_reset(void)

Enable the g_dnl composite USB gadget driver with embedded DFU function on it. It now uses the composite gadget framework to support download specific USB functions (like enabled DFU or USB Mass Storage).
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de Cc: Minkyu Kang mk7.kang@samsung.com
--- Change for v2: - Move the G_DNL_{VENDOR_NUM, PRODUCT_NUM and MANUFACTURER} definitions to ./include/configs/<board>.h --- include/configs/trats.h | 24 +++++++++++++++++++++++- 1 files changed, 23 insertions(+), 1 deletions(-)
diff --git a/include/configs/trats.h b/include/configs/trats.h index eb269b2..a24e73d 100644 --- a/include/configs/trats.h +++ b/include/configs/trats.h @@ -94,6 +94,21 @@ #undef CONFIG_CMD_ONENAND #undef CONFIG_CMD_MTDPARTS #define CONFIG_CMD_MMC +#define CONFIG_CMD_DFU + +/* FAT */ +#define CONFIG_CMD_FAT +#define CONFIG_FAT_WRITE + +/* USB Composite download gadget - g_dnl */ +#define CONFIG_USBDOWNLOAD_GADGET +#define CONFIG_DFU_FUNCTION +#define CONFIG_DFU_MMC + +/* USB Samsung's IDs */ +#define G_DNL_VENDOR_NUM 0x04E8 +#define G_DNL_PRODUCT_NUM 0x6601 +#define G_DNL_MANUFACTURER "Samsung"
#define CONFIG_BOOTDELAY 1 #define CONFIG_ZERO_BOOTDELAY_CHECK @@ -104,6 +119,11 @@ #define CONFIG_BOOTBLOCK "10" #define CONFIG_ENV_COMMON_BOOT "${console} ${meminfo}"
+#define CONFIG_DFU_ALT \ + "dfu_alt_info=" \ + "u-boot mmc 80 400;" \ + "uImage fat 0 2\0" \ + #define CONFIG_ENV_OVERWRITE #define CONFIG_SYS_CONSOLE_INFO_QUIET #define CONFIG_SYS_CONSOLE_IS_IN_ENV @@ -146,7 +166,8 @@ "mmcdev=0\0" \ "mmcbootpart=2\0" \ "mmcrootpart=3\0" \ - "opts=always_resume=1" + "opts=always_resume=1\0" \ + CONFIG_DFU_ALT
/* Miscellaneous configurable options */ #define CONFIG_SYS_LONGHELP /* undef to save memory */ @@ -209,6 +230,7 @@ #define CONFIG_USB_GADGET #define CONFIG_USB_GADGET_S3C_UDC_OTG #define CONFIG_USB_GADGET_DUALSPEED +#define CONFIG_USB_GADGET_VBUS_DRAW 2
/* LCD */ #define CONFIG_EXYNOS_FB

Hi Marek,
Those patches add support for composite USB download gadget. This gadget (at least for now) is equipped with DFU download function.
A separate DFU back-end and front-end have been added. Back-end is placed at ./drivers/dfu directory. The front-end is implemented as USB function.
The back-end is written in a generic manner with storage device specific code separated (eMMC).
DFU specification can be found at: http://wiki.openmoko.org/wiki/USB_DFU_-_The_USB_Device_Firmware_Upgrade_stan...
Example usage:
u-boot side: dfu mmc 0 PC: dfu-util -U IMAGE.bin -a uImage (for upload) dfu-util -D uImage -a uImage (download)
To list the alt settings: dfu mmc 0 list
Test HW: Exynos4210 Trats board
Lukasz Majewski (7): dfu:usb: Support for g_dnl composite download gadget. dfu:usb: DFU USB function (f_dfu) support for g_dnl composite gadget dfu: DFU backend implementation dfu: MMC specific routines for DFU operation dfu:cmd: Support for DFU u-boot command arm:trats: Support for USB UDC driver at TRATS board. arm:trats: Enable g_dnl composite USB gadget with embedded DFU function on TRATS
Do you have more comments about those patches?

On Mon, Jul 09, 2012 at 01:28:27PM +0200, Lukasz Majewski wrote:
Hi Marek,
Those patches add support for composite USB download gadget. This gadget (at least for now) is equipped with DFU download function.
A separate DFU back-end and front-end have been added. Back-end is placed at ./drivers/dfu directory. The front-end is implemented as USB function.
The back-end is written in a generic manner with storage device specific code separated (eMMC).
DFU specification can be found at: http://wiki.openmoko.org/wiki/USB_DFU_-_The_USB_Device_Firmware_Upgrade_stan...
Example usage:
u-boot side: dfu mmc 0 PC: dfu-util -U IMAGE.bin -a uImage (for upload) dfu-util -D uImage -a uImage (download)
To list the alt settings: dfu mmc 0 list
Test HW: Exynos4210 Trats board
Lukasz Majewski (7): dfu:usb: Support for g_dnl composite download gadget. dfu:usb: DFU USB function (f_dfu) support for g_dnl composite gadget dfu: DFU backend implementation dfu: MMC specific routines for DFU operation dfu:cmd: Support for DFU u-boot command arm:trats: Support for USB UDC driver at TRATS board. arm:trats: Enable g_dnl composite USB gadget with embedded DFU function on TRATS
Do you have more comments about those patches?
I _think_ he's been busy with exams and preparing for the U-Boot BoF this week. I'll make sure this gets brought up then however.

Dear Tom Rini,
On Mon, Jul 09, 2012 at 01:28:27PM +0200, Lukasz Majewski wrote:
Hi Marek,
Those patches add support for composite USB download gadget. This gadget (at least for now) is equipped with DFU download function.
A separate DFU back-end and front-end have been added. Back-end is placed at ./drivers/dfu directory. The front-end is implemented as USB function.
The back-end is written in a generic manner with storage device specific code separated (eMMC).
DFU specification can be found at: http://wiki.openmoko.org/wiki/USB_DFU_-_The_USB_Device_Firmware_Upgrade _standard
Example usage:
u-boot side: dfu mmc 0 PC: dfu-util -U IMAGE.bin -a uImage (for upload)
dfu-util -D uImage -a uImage (download)
To list the alt settings: dfu mmc 0 list
Test HW: Exynos4210 Trats board
Lukasz Majewski (7): dfu:usb: Support for g_dnl composite download gadget. dfu:usb: DFU USB function (f_dfu) support for g_dnl composite gadget dfu: DFU backend implementation dfu: MMC specific routines for DFU operation dfu:cmd: Support for DFU u-boot command arm:trats: Support for USB UDC driver at TRATS board. arm:trats: Enable g_dnl composite USB gadget with embedded DFU
function on TRATS
Do you have more comments about those patches?
I _think_ he's been busy with exams and preparing for the U-Boot BoF this week. I'll make sure this gets brought up then however.
Exams are long past, but there's a lot of other stuff -- the driver model, the denx work, the LSM this week -- I wish I could cut myself in four or so.
But weren't there some open questions, especially about the function vs. command stuff? The rest were really minor things and I believe you closed them with ease. There are just the few big questions, but it doesn't seem people are too interested to discuss them.
Best regards, Marek Vasut

Hi Marek,
Dear Tom Rini,
On Mon, Jul 09, 2012 at 01:28:27PM +0200, Lukasz Majewski wrote:
Hi Marek,
Those patches add support for composite USB download gadget. This gadget (at least for now) is equipped with DFU download function.
A separate DFU back-end and front-end have been added. Back-end is placed at ./drivers/dfu directory. The front-end is implemented as USB function.
The back-end is written in a generic manner with storage device specific code separated (eMMC).
DFU specification can be found at: http://wiki.openmoko.org/wiki/USB_DFU_-_The_USB_Device_Firmware_Upgrade _standard
Example usage:
u-boot side: dfu mmc 0 PC: dfu-util -U IMAGE.bin -a uImage (for upload)
dfu-util -D uImage -a uImage (download)
To list the alt settings: dfu mmc 0 list
Test HW: Exynos4210 Trats board
Lukasz Majewski (7): dfu:usb: Support for g_dnl composite download gadget. dfu:usb: DFU USB function (f_dfu) support for g_dnl composite gadget dfu: DFU backend implementation dfu: MMC specific routines for DFU operation dfu:cmd: Support for DFU u-boot command arm:trats: Support for USB UDC driver at TRATS board. arm:trats: Enable g_dnl composite USB gadget with embedded DFU
function on TRATS
Do you have more comments about those patches?
I _think_ he's been busy with exams and preparing for the U-Boot BoF this week. I'll make sure this gets brought up then however.
Exams are long past, but there's a lot of other stuff -- the driver model, the denx work, the LSM this week -- I wish I could cut myself in four or so.
As a side note - I'm looking forward to see your keynote video about the new driver model :-).
But weren't there some open questions, especially about the function vs. command stuff? The rest were really minor things and I believe you closed them with ease. There are just the few big questions, but it doesn't seem people are too interested to discuss them.
I'm open for a discussion :-)
I will post the v3, after the LSM and discussion about the command vs. function issue.

Dear Lukasz Majewski,
[...]
Do you have more comments about those patches?
I _think_ he's been busy with exams and preparing for the U-Boot BoF this week. I'll make sure this gets brought up then however.
Exams are long past, but there's a lot of other stuff -- the driver model, the denx work, the LSM this week -- I wish I could cut myself in four or so.
As a side note - I'm looking forward to see your keynote video about the new driver model :-).
Oh, just rub it in even more. I'm just starting to get seriously nervous about the talk. Just a while ago, it fell on me ;-) I hope you'll like it though.
But weren't there some open questions, especially about the function vs. command stuff? The rest were really minor things and I believe you closed them with ease. There are just the few big questions, but it doesn't seem people are too interested to discuss them.
I'm open for a discussion :-)
I'm glad for that, really.
I will post the v3, after the LSM and discussion about the command vs. function issue.
Best regards, Marek Vasut

Dear Lukasz Majewski,
Those patches add support for composite USB download gadget. This gadget (at least for now) is equipped with DFU download function.
A separate DFU back-end and front-end have been added. Back-end is placed at ./drivers/dfu directory. The front-end is implemented as USB function.
The back-end is written in a generic manner with storage device specific code separated (eMMC).
DFU specification can be found at: http://wiki.openmoko.org/wiki/USB_DFU_-_The_USB_Device_Firmware_Upgrade_sta ndard
[...]
will we see a next version of this series please? Or where did the discussion get?
Best regards, Marek Vasut

Hi Marek,
Dear Lukasz Majewski,
Those patches add support for composite USB download gadget. This gadget (at least for now) is equipped with DFU download function.
A separate DFU back-end and front-end have been added. Back-end is placed at ./drivers/dfu directory. The front-end is implemented as USB function.
The back-end is written in a generic manner with storage device specific code separated (eMMC).
DFU specification can be found at: http://wiki.openmoko.org/wiki/USB_DFU_-_The_USB_Device_Firmware_Upgrade_sta ndard
[...]
will we see a next version of this series please?
I've just come back from my holiday. I will continue work on this.
Or where did the discussion get?
As fair as I remember, the discussion ended up with a conclusion, that new layer of abstraction (in a form of function call) is needed.
I will rehack the cmd_mmc.c to include new functions in it. It will be a prepatch for the DFU.

Dear Lukasz Majewski,
Hi Marek,
Dear Lukasz Majewski,
Those patches add support for composite USB download gadget. This gadget (at least for now) is equipped with DFU download function.
A separate DFU back-end and front-end have been added. Back-end is placed at ./drivers/dfu directory. The front-end is implemented as USB function.
The back-end is written in a generic manner with storage device specific code separated (eMMC).
DFU specification can be found at: http://wiki.openmoko.org/wiki/USB_DFU_-_The_USB_Device_Firmware_Upgrade _sta ndard
[...]
will we see a next version of this series please?
I've just come back from my holiday. I will continue work on this.
Or where did the discussion get?
As fair as I remember, the discussion ended up with a conclusion, that new layer of abstraction (in a form of function call) is needed.
I will rehack the cmd_mmc.c to include new functions in it. It will be a prepatch for the DFU.
Thanks a lot! I didn't intend to torture you, really ... I just wanted this to be done properly.
Best regards, Marek Vasut

Those patches add support for composite USB download gadget. This gadget (at least for now) is equipped with DFU download function.
A separate DFU back-end and front-end have been added. Back-end is placed at ./drivers/dfu directory. The front-end is implemented as USB function.
The back-end is written in a generic manner with storage device specific code separated (eMMC).
DFU specification can be found at: http://wiki.openmoko.org/wiki/USB_DFU_-_The_USB_Device_Firmware_Upgrade_stan...
Example usage:
u-boot side: dfu mmc 0 PC: dfu-util -U IMAGE.bin -a uImage (for upload) dfu-util -D uImage -a uImage (download)
To list the alt settings: dfu mmc 0 list
Test HW: Exynos4210 Trats board
Lukasz Majewski (7): dfu:usb: Support for g_dnl composite download gadget. dfu:usb: DFU USB function (f_dfu) support for g_dnl composite gadget dfu: DFU backend implementation dfu: MMC specific routines for DFU operation dfu:cmd: Support for DFU u-boot command arm:trats: Support for USB UDC driver at TRATS board. arm:trats: Enable g_dnl composite USB gadget with embedded DFU function on TRATS
Makefile | 1 + board/samsung/trats/trats.c | 8 + common/Makefile | 1 + common/cmd_dfu.c | 81 +++++ drivers/dfu/Makefile | 44 +++ drivers/dfu/dfu.c | 237 ++++++++++++++ drivers/dfu/dfu_mmc.c | 165 ++++++++++ drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_dfu.c | 751 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/f_dfu.h | 100 ++++++ drivers/usb/gadget/g_dnl.c | 218 +++++++++++++ include/configs/trats.h | 24 ++- include/dfu.h | 103 ++++++ include/g_dnl.h | 33 ++ 14 files changed, 1767 insertions(+), 1 deletions(-) create mode 100644 common/cmd_dfu.c create mode 100644 drivers/dfu/Makefile create mode 100644 drivers/dfu/dfu.c create mode 100644 drivers/dfu/dfu_mmc.c create mode 100644 drivers/usb/gadget/f_dfu.c create mode 100644 drivers/usb/gadget/f_dfu.h create mode 100644 drivers/usb/gadget/g_dnl.c create mode 100644 include/dfu.h create mode 100644 include/g_dnl.h

Composite USB download gadget support (g_dnl) for download functions. This code works on top of composite gadget.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2:
- G_DNL_{VENDOR_NUM, PRODUCT_NUM and MANUFACTURER} defined at ./include/configs/<board>.h - Suspend and resume stub methods removed - '\0' repleaced with plain 0
Changes for v3: - Remove unused #includes - Replace strncpy and strncat with strcpy and strcat. It was possible due to new approach to g_dnl name generation (at g_dnl_register function) - Rename the g_dnl_{init|cleanup} to g_dnl_{register|unregister} - Replace the G_DNL_* CONFIG_G_DNL_* --- drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/g_dnl.c | 218 +++++++++++++++++++++++++++++++++++++++++++ include/g_dnl.h | 33 +++++++ 3 files changed, 252 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/gadget/g_dnl.c create mode 100644 include/g_dnl.h
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 87d1918..2c067c8 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -29,6 +29,7 @@ LIB := $(obj)libusb_gadget.o ifdef CONFIG_USB_GADGET COBJS-y += epautoconf.o config.o usbstring.o COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o +COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o endif ifdef CONFIG_USB_ETHER COBJS-y += ether.o epautoconf.o config.o usbstring.o diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c new file mode 100644 index 0000000..a77da30 --- /dev/null +++ b/drivers/usb/gadget/g_dnl.c @@ -0,0 +1,218 @@ +/* + * g_dnl.c -- USB Downloader Gadget + * + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski l.majewski@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 <errno.h> +#include <common.h> +#include <malloc.h> + +#include <mmc.h> +#include <part.h> + +#include <g_dnl.h> +#include "f_dfu.h" + +#include "gadget_chips.h" +#include "composite.c" + +/* + * One needs to define the following: + * CONFIG_G_DNL_VENDOR_NUM + * CONFIG_G_DNL_PRODUCT_NUM + * CONFIG_G_DNL_MANUFACTURER + * at e.g. ./include/configs/<board>.h + */ + +#define STRING_MANUFACTURER 25 +#define STRING_PRODUCT 2 +#define STRING_USBDOWN 2 +#define CONFIG_USBDOWNLOADER 2 + +#define DRIVER_VERSION "usb_dnl 2.0" + +static const char shortname[] = "usb_dnl_"; +static const char product[] = "USB download gadget"; +static const char manufacturer[] = CONFIG_G_DNL_MANUFACTURER; + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_COMM, + .bDeviceSubClass = 0x02, /*0x02:CDC-modem , 0x00:CDC-serial*/ + + .idVendor = __constant_cpu_to_le16(CONFIG_G_DNL_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16(CONFIG_G_DNL_PRODUCT_NUM), + .iProduct = STRING_PRODUCT, + .bNumConfigurations = 1, +}; + +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &(struct usb_otg_descriptor){ + .bLength = sizeof(struct usb_otg_descriptor), + .bDescriptorType = USB_DT_OTG, + .bmAttributes = USB_OTG_SRP, + }, + NULL, +}; + +/* static strings, in UTF-8 */ +static struct usb_string odin_string_defs[] = { + { 0, manufacturer, }, + { 1, product, }, +}; + +static struct usb_gadget_strings odin_string_tab = { + .language = 0x0409, /* en-us */ + .strings = odin_string_defs, +}; + +static struct usb_gadget_strings *g_dnl_composite_strings[] = { + &odin_string_tab, + NULL, +}; + +static int g_dnl_unbind(struct usb_composite_dev *cdev) +{ + debug("%s\n", __func__); + return 0; +} + +static int g_dnl_do_config(struct usb_configuration *c) +{ + int ret = -1; + char *s = (char *) c->cdev->driver->name; + + debug("%s: configuration: 0x%p composite dev: 0x%p\n", + __func__, c, c->cdev); + + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + printf("GADGET DRIVER: %s\n", s); + + if (!strcmp(s, "usb_dnl_dfu")) + ret = dfu_add(c); + + return ret; +} + +static int g_dnl_config_register(struct usb_composite_dev *cdev) +{ + debug("%s:\n", __func__); + static struct usb_configuration config = { + .label = "usb_dnload", + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .bConfigurationValue = CONFIG_USBDOWNLOADER, + .iConfiguration = STRING_USBDOWN, + + .bind = g_dnl_do_config, + }; + + return usb_add_config(cdev, &config); +} + +static int g_dnl_bind(struct usb_composite_dev *cdev) +{ + int gcnum; + int id, ret; + struct usb_gadget *gadget = cdev->gadget; + + debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev); + + id = usb_string_id(cdev); + + if (id < 0) + return id; + odin_string_defs[0].id = id; + device_desc.iManufacturer = id; + + id = usb_string_id(cdev); + if (id < 0) + return id; + + odin_string_defs[1].id = id; + device_desc.iProduct = id; + + ret = g_dnl_config_register(cdev); + if (ret) + goto error; + + gcnum = usb_gadget_controller_number(gadget); + + debug("gcnum: %d\n", gcnum); + if (gcnum >= 0) + device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); + else { + debug("%s: controller '%s' not recognized\n", + shortname, gadget->name); + device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); + } + + return 0; + + error: + g_dnl_unbind(cdev); + return -ENOMEM; +} + +static struct usb_composite_driver g_dnl_driver = { + .name = NULL, + .dev = &device_desc, + .strings = g_dnl_composite_strings, + + .bind = g_dnl_bind, + .unbind = g_dnl_unbind, +}; + +int g_dnl_register(const char *type) +{ + int ret; + /* We only allow "dfu" atm, so 3 should be enough */ + static char name[sizeof(shortname) + 3]; + + if (!strcmp(type, "dfu")) { + strcpy(name, shortname); + strcat(name, type); + } else { + printf("%s: unknown command: %s\n", __func__, type); + return -EINVAL; + } + + g_dnl_driver.name = name; + + debug("%s: g_dnl_driver.name: %s\n", __func__, g_dnl_driver.name); + ret = usb_composite_register(&g_dnl_driver); + + if (ret) { + printf("%s: failed!, error: %d\n", __func__, ret); + return ret; + } + + return 0; +} + +void g_dnl_unregister(void) +{ + usb_composite_unregister(&g_dnl_driver); +} diff --git a/include/g_dnl.h b/include/g_dnl.h new file mode 100644 index 0000000..d65214f --- /dev/null +++ b/include/g_dnl.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski l.majewski@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 __G_DOWNLOAD_H_ +#define __G_DOWNLOAD_H_ + +#include <linux/usb/ch9.h> +#include <usbdescriptors.h> +#include <linux/usb/gadget.h> + +int g_dnl_register(const char *s); +void g_dnl_unregister(void); + +/* USB initialization declaration - board specific*/ +void board_usb_init(void); +#endif /* __G_DOWNLOAD_H_ */

On Tuesday 31 July 2012 02:36:57 Lukasz Majewski wrote:
--- /dev/null +++ b/drivers/usb/gadget/g_dnl.c
+static const struct usb_descriptor_header *otg_desc[] = {
- (struct usb_descriptor_header *) &(struct usb_otg_descriptor){
since you're just casting away things, you could just use (void*) to make it a bit more compact
also, can this be: static const struct usb_descriptor_header * const otg_desc[] (notice the 2nd const)
+static struct usb_gadget_strings *g_dnl_composite_strings[] = {
here too: static struct usb_gadget_strings * const g_dnl_composite_strings[] = {
+static int g_dnl_do_config(struct usb_configuration *c) +{
- int ret = -1;
- char *s = (char *) c->cdev->driver->name;
pretty sure this should be: const char *s = c->cdev->driver->name;
+static int g_dnl_bind(struct usb_composite_dev *cdev) +{
- int gcnum;
- int id, ret;
- struct usb_gadget *gadget = cdev->gadget;
can we stick to a single space for indentation between the type and the variable name please ?
+int g_dnl_register(const char *type) +{
- int ret;
- /* We only allow "dfu" atm, so 3 should be enough */
- static char name[sizeof(shortname) + 3];
- if (!strcmp(type, "dfu")) {
strcpy(name, shortname);
strcat(name, type);
if u-boot had stpcpy(), we could do: strcpy(stpcpy(name, shortname), type);
--- /dev/null +++ b/include/g_dnl.h
+/* USB initialization declaration - board specific*/
needs a space before that "*/" -mike

Hi Mike,
On Tuesday 31 July 2012 02:36:57 Lukasz Majewski wrote:
--- /dev/null +++ b/drivers/usb/gadget/g_dnl.c
+static const struct usb_descriptor_header *otg_desc[] = {
- (struct usb_descriptor_header *) &(struct
usb_otg_descriptor){
since you're just casting away things, you could just use (void*) to make it a bit more compact
also, can this be: static const struct usb_descriptor_header * const otg_desc[] (notice the 2nd const)
After looking into the code I can say that otg_support is not needed (at least for this gadget) in u-boot. Will be removed.
+static struct usb_gadget_strings *g_dnl_composite_strings[] = {
here too: static struct usb_gadget_strings * const g_dnl_composite_strings[] = {
This const is anyway discarded by compiler (GCC 4.6.1) when .string field of struct usb_composite_driver is initialized.
+static int g_dnl_do_config(struct usb_configuration *c) +{
- int ret = -1;
- char *s = (char *) c->cdev->driver->name;
pretty sure this should be: const char *s = c->cdev->driver->name;
Yes, you are right. Corrected.
+static int g_dnl_bind(struct usb_composite_dev *cdev) +{
- int gcnum;
- int id, ret;
- struct usb_gadget *gadget = cdev->gadget;
can we stick to a single space for indentation between the type and the variable name please ?
Corrected.
+int g_dnl_register(const char *type) +{
- int ret;
- /* We only allow "dfu" atm, so 3 should be enough */
- static char name[sizeof(shortname) + 3];
- if (!strcmp(type, "dfu")) {
strcpy(name, shortname);
strcat(name, type);
if u-boot had stpcpy(), we could do: strcpy(stpcpy(name, shortname), type);
--- /dev/null +++ b/include/g_dnl.h
+/* USB initialization declaration - board specific*/
needs a space before that "*/"
Corrected.
-mike

Support for f_dfu USB function.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2:
- Replace kzalloc and kfree with free and calloc - Reorganization of calloc calls - Misspelling corrected - Redesign of DFU state machine from "switch case" to function pointers - Split the dfu_handle method to separate functions for each DFU state
Changes for v3: - Missing parenthesis added to sizeof call --- drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/f_dfu.c | 751 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/f_dfu.h | 100 ++++++ 3 files changed, 852 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/gadget/f_dfu.c create mode 100644 drivers/usb/gadget/f_dfu.h
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 2c067c8..5bbdd36 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -30,6 +30,7 @@ ifdef CONFIG_USB_GADGET COBJS-y += epautoconf.o config.o usbstring.o COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o +COBJS-$(CONFIG_DFU_FUNCTION) += f_dfu.o endif ifdef CONFIG_USB_ETHER COBJS-y += ether.o epautoconf.o config.o usbstring.o diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c new file mode 100644 index 0000000..17b656c --- /dev/null +++ b/drivers/usb/gadget/f_dfu.c @@ -0,0 +1,751 @@ +/* + * f_dfu.c -- Device Firmware Update USB function + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz andrzej.p@samsung.com + * Lukasz Majewski l.majewski@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 <errno.h> +#include <common.h> +#include <malloc.h> + +#include <linux/usb/ch9.h> +#include <usbdescriptors.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> + +#include <dfu.h> +#include "f_dfu.h" + +struct f_dfu { + struct usb_function usb_function; + + struct usb_descriptor_header **function; + struct usb_string *strings; + + /* when configured, we have one config */ + u8 config; + u8 altsetting; + enum dfu_state dfu_state; + unsigned int dfu_status; + + /* Send/received block number is handy for data integrity check */ + int blk_seq_num; +}; + +typedef int (*dfu_state_fn) (struct f_dfu *, + const struct usb_ctrlrequest *, + struct usb_gadget *, + struct usb_request *); + +static inline struct f_dfu *func_to_dfu(struct usb_function *f) +{ + return container_of(f, struct f_dfu, usb_function); +} + +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 = DFU_USB_BUFSIZ, + .bcdDFUVersion = __constant_cpu_to_le16(0x0110), +}; + +static 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 = DYNAMIC */ +}; + +static struct usb_descriptor_header *dfu_runtime_descs[] = { + (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 const char dfu_name[] = "Device Firmware Upgrade"; + +/* + * static strings, in UTF-8 + * + * dfu_generic configuration + */ +static struct usb_string strings_dfu_generic[] = { + [0].s = dfu_name, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dfu_generic = { + .language = 0x0409, /* en-us */ + .strings = strings_dfu_generic, +}; + +static struct usb_gadget_strings *dfu_generic_strings[] = { + &stringtab_dfu_generic, + NULL, +}; + +/* + * usb_function specific + */ +static struct usb_gadget_strings stringtab_dfu = { + .language = 0x0409, /* en-us */ + /* + * .strings + * + * assigned during initialization, + * depends on number of flash entities + * + */ +}; + +static struct usb_gadget_strings *dfu_strings[] = { + &stringtab_dfu, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_dfu *f_dfu = req->context; + + dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf, + req->length, f_dfu->blk_seq_num); + + if (req->length == 0) { + puts("DOWNLOAD ... OK\n"); + puts("Ctrl+C to exit ...\n"); + } +} + +static void handle_getstatus(struct usb_request *req) +{ + struct dfu_status *dstat = (struct dfu_status *)req->buf; + struct f_dfu *f_dfu = req->context; + + switch (f_dfu->dfu_state) { + case DFU_STATE_dfuDNLOAD_SYNC: + case DFU_STATE_dfuDNBUSY: + f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE; + break; + case DFU_STATE_dfuMANIFEST_SYNC: + break; + default: + break; + } + + /* send status response */ + dstat->bStatus = f_dfu->dfu_status; + dstat->bState = f_dfu->dfu_state; + dstat->iString = 0; +} + +static void handle_getstate(struct usb_request *req) +{ + struct f_dfu *f_dfu = req->context; + + ((u8 *)req->buf)[0] = f_dfu->dfu_state & 0xff; + req->actual = sizeof(u8); +} + +static inline void to_dfu_mode(struct f_dfu *f_dfu) +{ + f_dfu->usb_function.strings = dfu_strings; + f_dfu->usb_function.hs_descriptors = f_dfu->function; +} + +static inline void to_runtime_mode(struct f_dfu *f_dfu) +{ + f_dfu->usb_function.strings = NULL; + f_dfu->usb_function.hs_descriptors = dfu_runtime_descs; +} + +static int handle_upload(struct usb_request *req, u16 len) +{ + struct f_dfu *f_dfu = req->context; + + return dfu_read(dfu_get_entity(f_dfu->altsetting), req->buf, + req->length, f_dfu->blk_seq_num); +} + +static int handle_dnload(struct usb_gadget *gadget, u16 len) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_request *req = cdev->req; + struct f_dfu *f_dfu = req->context; + + if (len == 0) + f_dfu->dfu_state = DFU_STATE_dfuMANIFEST_SYNC; + + req->complete = dnload_request_complete; + + return len; +} + +/*-------------------------------------------------------------------------*/ +/* DFU state machine */ +static int state_app_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + case USB_REQ_DFU_DETACH: + f_dfu->dfu_state = DFU_STATE_appDETACH; + to_dfu_mode(f_dfu); + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + value = RET_ZLP; + break; + default: + value = RET_STALL; + break; + } + + return value; +} + +static int state_app_detach(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_appIDLE; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 len = le16_to_cpu(ctrl->wLength); + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_DNLOAD: + if (len == 0) { + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; + f_dfu->blk_seq_num = w_value; + value = handle_dnload(gadget, len); + break; + case USB_REQ_DFU_UPLOAD: + f_dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE; + f_dfu->blk_seq_num = 0; + value = handle_upload(req, len); + break; + case USB_REQ_DFU_ABORT: + /* no zlp? */ + value = RET_ZLP; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + 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 :( + */ + f_dfu->dfu_state = + DFU_STATE_dfuMANIFEST_WAIT_RST; + to_runtime_mode(f_dfu); + f_dfu->dfu_state = DFU_STATE_appIDLE; + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_dnload_sync(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_dnbusy(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_dnload_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 len = le16_to_cpu(ctrl->wLength); + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_DNLOAD: + f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; + f_dfu->blk_seq_num = w_value; + value = handle_dnload(gadget, len); + break; + case USB_REQ_DFU_ABORT: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + value = RET_ZLP; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_manifest_sync(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + /* We're MainfestationTolerant */ + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + handle_getstatus(req); + f_dfu->blk_seq_num = 0; + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_upload_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 len = le16_to_cpu(ctrl->wLength); + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_UPLOAD: + /* state transition if less data then requested */ + f_dfu->blk_seq_num = w_value; + value = handle_upload(req, len); + if (value >= 0 && value < len) + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + break; + case USB_REQ_DFU_ABORT: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + /* no zlp? */ + value = RET_ZLP; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_error(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + case USB_REQ_DFU_CLRSTATUS: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + f_dfu->dfu_status = DFU_STATUS_OK; + /* no zlp? */ + value = RET_ZLP; + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static dfu_state_fn dfu_state[] = { + state_app_idle, /* DFU_STATE_appIDLE */ + state_app_detach, /* DFU_STATE_appDETACH */ + state_dfu_idle, /* DFU_STATE_dfuIDLE */ + state_dfu_dnload_sync, /* DFU_STATE_dfuDNLOAD_SYNC */ + state_dfu_dnbusy, /* DFU_STATE_dfuDNBUSY */ + state_dfu_dnload_idle, /* DFU_STATE_dfuDNLOAD_IDLE */ + state_dfu_manifest_sync, /* DFU_STATE_dfuMANIFEST_SYNC */ + NULL, /* DFU_STATE_dfuMANIFEST */ + NULL, /* DFU_STATE_dfuMANIFEST_WAIT_RST */ + state_dfu_upload_idle, /* DFU_STATE_dfuUPLOAD_IDLE */ + state_dfu_error /* DFU_STATE_dfuERROR */ +}; + +static int +dfu_handle(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct usb_gadget *gadget = f->config->cdev->gadget; + struct usb_request *req = f->config->cdev->req; + struct f_dfu *f_dfu = f->config->cdev->req->context; + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + int value = 0; + u8 req_type = ctrl->bRequestType & USB_TYPE_MASK; + + debug("w_value: 0x%x len: 0x%x\n", w_value, len); + debug("req_type: 0x%x ctrl->bRequest: 0x%x f_dfu->dfu_state: 0x%x\n", + req_type, ctrl->bRequest, f_dfu->dfu_state); + + if (req_type == USB_TYPE_STANDARD) { + if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR && + (w_value >> 8) == DFU_DT_FUNC) { + value = min(len, (u16) sizeof(dfu_func)); + memcpy(req->buf, &dfu_func, value); + } + } else /* DFU specific request */ + value = dfu_state[f_dfu->dfu_state] (f_dfu, ctrl, gadget, req); + + if (value >= 0) { + req->length = value; + req->zero = value < len; + value = usb_ep_queue(gadget->ep0, req, 0); + if (value < 0) { + debug("ep_queue --> %d\n", value); + req->status = 0; + } + } + + return value; +} + +/*-------------------------------------------------------------------------*/ + +static int +dfu_prepare_strings(struct f_dfu *f_dfu, int n) +{ + struct dfu_entity *de = NULL; + int i = 0; + + f_dfu->strings = calloc(sizeof(struct usb_string), n + 1); + if (!f_dfu->strings) + goto enomem; + + for (i = 0; i < n; ++i) { + de = dfu_get_entity(i); + f_dfu->strings[i].s = de->name; + } + + f_dfu->strings[i].id = 0; + f_dfu->strings[i].s = NULL; + + return 0; + +enomem: + while (i) + f_dfu->strings[--i].s = NULL; + + free(f_dfu->strings); + + return -ENOMEM; +} + +static int dfu_prepare_function(struct f_dfu *f_dfu, int n) +{ + struct usb_interface_descriptor *d; + int i = 0; + + f_dfu->function = calloc(sizeof(struct usb_descriptor_header *), n); + if (!f_dfu->function) + goto enomem; + + for (i = 0; i < n; ++i) { + d = calloc(sizeof(*d), 1); + 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; + + f_dfu->function[i] = (struct usb_descriptor_header *)d; + } + f_dfu->function[i] = NULL; + + return 0; + +enomem: + while (i) { + free(f_dfu->function[--i]); + f_dfu->function[i] = NULL; + } + free(f_dfu->function); + + return -ENOMEM; +} + +static int dfu_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_dfu *f_dfu = func_to_dfu(f); + int alt_num = dfu_get_alt_number(); + int rv, id, i; + + id = usb_interface_id(c, f); + if (id < 0) + return id; + dfu_intf_runtime.bInterfaceNumber = id; + + f_dfu->dfu_state = DFU_STATE_appIDLE; + f_dfu->dfu_status = DFU_STATUS_OK; + + rv = dfu_prepare_function(f_dfu, alt_num); + if (rv) + goto error; + + rv = dfu_prepare_strings(f_dfu, alt_num); + if (rv) + goto error; + for (i = 0; i < alt_num; i++) { + id = usb_string_id(cdev); + if (id < 0) + return id; + f_dfu->strings[i].id = id; + ((struct usb_interface_descriptor *)f_dfu->function[i]) + ->iInterface = id; + } + + stringtab_dfu.strings = f_dfu->strings; + + cdev->req->context = f_dfu; + +error: + return rv; +} + +static void dfu_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + int alt_num = dfu_get_alt_number(); + int i; + + if (f_dfu->strings) { + i = alt_num; + while (i) + f_dfu->strings[--i].s = NULL; + + free(f_dfu->strings); + } + + if (f_dfu->function) { + i = alt_num; + while (i) { + free(f_dfu->function[--i]); + f_dfu->function[i] = NULL; + } + free(f_dfu->function); + } + + free(f_dfu); +} + +static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + + debug("%s: intf:%d alt:%d\n", __func__, intf, alt); + + f_dfu->altsetting = alt; + + return 0; +} + +/* TODO: is this really what we need here? */ +static void dfu_disable(struct usb_function *f) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + if (f_dfu->config == 0) + return; + + debug("%s: reset config\n", __func__); + + f_dfu->config = 0; +} + +static int dfu_bind_config(struct usb_configuration *c) +{ + struct f_dfu *f_dfu; + int status; + + f_dfu = calloc(sizeof(*f_dfu), 1); + if (!f_dfu) + return -ENOMEM; + f_dfu->usb_function.name = "dfu"; + f_dfu->usb_function.hs_descriptors = dfu_runtime_descs; + f_dfu->usb_function.bind = dfu_bind; + f_dfu->usb_function.unbind = dfu_unbind; + f_dfu->usb_function.set_alt = dfu_set_alt; + f_dfu->usb_function.disable = dfu_disable; + f_dfu->usb_function.strings = dfu_generic_strings, + f_dfu->usb_function.setup = dfu_handle, + + status = usb_add_function(c, &f_dfu->usb_function); + if (status) + free(f_dfu); + + return status; +} + +int dfu_add(struct usb_configuration *c) +{ + int id; + + id = usb_string_id(c->cdev); + if (id < 0) + return id; + strings_dfu_generic[0].id = id; + dfu_intf_runtime.iInterface = id; + + debug("%s: cdev: 0x%p gadget:0x%p gadget->ep0: 0x%p\n", __func__, + c->cdev, c->cdev->gadget, c->cdev->gadget->ep0); + + return dfu_bind_config(c); +} diff --git a/drivers/usb/gadget/f_dfu.h b/drivers/usb/gadget/f_dfu.h new file mode 100644 index 0000000..023e1ad --- /dev/null +++ b/drivers/usb/gadget/f_dfu.h @@ -0,0 +1,100 @@ +/* + * f_dfu.h -- Device Firmware Update gadget + * + * Copyright (C) 2011-2012 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 __F_DFU_H_ +#define __F_DFU_H_ + +#include <linux/compiler.h> +#include <linux/usb/composite.h> + +#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 DFU_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 + +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; + +/* configuration-specific linkup */ +int dfu_add(struct usb_configuration *c); +#endif /* __F_DFU_H_ */

On Tuesday 31 July 2012 02:36:58 Lukasz Majewski wrote:
--- /dev/null +++ b/drivers/usb/gadget/f_dfu.c
+static struct usb_interface_descriptor dfu_intf_runtime = {
can this be made const ?
+static struct usb_descriptor_header *dfu_runtime_descs[] = {
- (struct usb_descriptor_header *) &dfu_intf_runtime,
can you change the descs array to be const ? static const struct usb_descriptor_header * const dfu_runtime_descs[] = {
then i think you can drop the cast there ...
+static struct usb_qualifier_descriptor dev_qualifier = { +static struct usb_gadget_strings stringtab_dfu_generic = { +static struct usb_gadget_strings *dfu_generic_strings[] = { +static struct usb_gadget_strings stringtab_dfu = { +static struct usb_gadget_strings *dfu_strings[] = {
can these be made const ?
+static void handle_getstate(struct usb_request *req) +{
- struct f_dfu *f_dfu = req->context;
- ((u8 *)req->buf)[0] = f_dfu->dfu_state & 0xff;
pretty sure you don't need that "& 0xff"
+static int state_app_idle(struct f_dfu *f_dfu,
const struct usb_ctrlrequest *ctrl,
struct usb_gadget *gadget,
struct usb_request *req)
+{
- int value = 0;
might be good to push this down into the 1 case statement (USB_REQ_DFU_GETSTATE) that uses it rather than init it up top
same goes for all the other funcs below that follow this style -mike

Dear Mike,
On Tuesday 31 July 2012 02:36:58 Lukasz Majewski wrote:
--- /dev/null +++ b/drivers/usb/gadget/f_dfu.c
+static struct usb_interface_descriptor dfu_intf_runtime = {
can this be made const ?
Unfortunately those structs cannot be const, since some of their fields are filled during running DFU code(when switching between "normal" and "dfu" mode).
+static struct usb_descriptor_header *dfu_runtime_descs[] = {
- (struct usb_descriptor_header *) &dfu_intf_runtime,
can you change the descs array to be const ? static const struct usb_descriptor_header * const dfu_runtime_descs[] = {
The same as above. Moreover the DFU implementation passes the information about available alt settings as descriptors filled at runtime.
then i think you can drop the cast there ...
+static struct usb_qualifier_descriptor dev_qualifier = {
Struct above can only be defined as const.
+static struct usb_gadget_strings stringtab_dfu_generic = { +static struct usb_gadget_strings *dfu_generic_strings[] = { +static struct usb_gadget_strings stringtab_dfu = { +static struct usb_gadget_strings *dfu_strings[] = {
can these be made const ?
+static void handle_getstate(struct usb_request *req) +{
- struct f_dfu *f_dfu = req->context;
- ((u8 *)req->buf)[0] = f_dfu->dfu_state & 0xff;
pretty sure you don't need that "& 0xff"
OK.
+static int state_app_idle(struct f_dfu *f_dfu,
const struct usb_ctrlrequest *ctrl,
struct usb_gadget *gadget,
struct usb_request *req)
+{
- int value = 0;
might be good to push this down into the 1 case statement (USB_REQ_DFU_GETSTATE) that uses it rather than init it up top
I haven't understood this comment.
Do you suggest to combine all those functions (like state_app_* or state_dfu_*) in one big switch - case clause? The state machine was initially implemented in this way, but for sake of clearness Marek Vasut asked to split it down do separate functions as defined at dfu_state[]. I also think that this approach is better than one bit switch - case.
same goes for all the other funcs below that follow this style -mike

New, separate driver at ./drivers/dfu has been added. It allows platform and storage independent operation of DFU. It has been extended to use new MMC level of command abstraction.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2: - None
Changes for v3: - Remove unnecessary NULL and 0 initialization of dynamic variables - Combine two puts to one - Adding const qualifier to device and layout definitions - Remove unnecessary casting at dfu->name passing - Provide more meaningful names for dfu layouts and device types - Removal of dfu_extract_{token|entity} functions and replace them with strsep calls --- Makefile | 1 + drivers/dfu/Makefile | 43 +++++++++ drivers/dfu/dfu.c | 237 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/dfu.h | 103 ++++++++++++++++++++++ 4 files changed, 384 insertions(+), 0 deletions(-) create mode 100644 drivers/dfu/Makefile create mode 100644 drivers/dfu/dfu.c create mode 100644 include/dfu.h
diff --git a/Makefile b/Makefile index d57c15e..bd469f4 100644 --- a/Makefile +++ b/Makefile @@ -271,6 +271,7 @@ LIBS += drivers/pci/libpci.o LIBS += drivers/pcmcia/libpcmcia.o LIBS += drivers/power/libpower.o LIBS += drivers/spi/libspi.o +LIBS += drivers/dfu/libdfu.o ifeq ($(CPU),mpc83xx) LIBS += drivers/qe/libqe.o LIBS += arch/powerpc/cpu/mpc8xxx/ddr/libddr.o diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile new file mode 100644 index 0000000..7736485 --- /dev/null +++ b/drivers/dfu/Makefile @@ -0,0 +1,43 @@ +# +# Copyright (C) 2012 Samsung Electronics +# Lukasz Majewski l.majewski@samsung.com +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB = $(obj)libdfu.o + +COBJS-$(CONFIG_DFU_FUNCTION) += dfu.o + +SRCS := $(COBJS-y:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS-y)) + +$(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/dfu/dfu.c b/drivers/dfu/dfu.c new file mode 100644 index 0000000..8f48b49 --- /dev/null +++ b/drivers/dfu/dfu.c @@ -0,0 +1,237 @@ +/* + * dfu.c -- DFU back-end routines + * + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski l.majewski@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 <fat.h> +#include <dfu.h> +#include <linux/list.h> +#include <linux/compiler.h> + +static LIST_HEAD(dfu_list); +static int dfu_alt_num; + +static int dfu_find_alt_num(char *s) +{ + int i = 0; + + for (; *s; s++) + if (*s == ';') + i++; + + return ++i; +} + +static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE) + dfu_buf[DFU_DATA_BUF_SIZE]; + +int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) +{ + static unsigned char *i_buf; + static int i_blk_seq_num; + long w_size = 0; + int ret = 0; + + debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n", + __func__, dfu->name, buf, size, blk_seq_num, i_buf); + + if (blk_seq_num == 0) { + memset(dfu_buf, '\0', sizeof(dfu_buf)); + i_buf = dfu_buf; + i_blk_seq_num = 0; + } + + if (i_blk_seq_num++ != blk_seq_num) { + printf("%s: Wrong sequence number! [%d] [%d]\n", + __func__, i_blk_seq_num, blk_seq_num); + return -1; + } + + memcpy(i_buf, buf, size); + i_buf += size; + + if (size == 0) { + /* Integrity check (if needed) */ + debug("%s: %s %d [B] CRC32: 0x%x\n", __func__, dfu->name, + i_buf - dfu_buf, crc32(0, dfu_buf, i_buf - dfu_buf)); + + w_size = i_buf - dfu_buf; + ret = dfu->write_medium(dfu, dfu_buf, &w_size); + if (ret) + debug("%s: Write error!\n", __func__); + + i_blk_seq_num = 0; + i_buf = NULL; + return ret; + } + + return ret; +} + +int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) +{ + static unsigned char *i_buf; + static int i_blk_seq_num; + static long r_size; + static u32 crc; + int ret = 0; + + debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n", + __func__, dfu->name, buf, size, blk_seq_num, i_buf); + + if (blk_seq_num == 0) { + i_buf = dfu_buf; + memset(dfu_buf, '\0', sizeof(dfu_buf)); + ret = dfu->read_medium(dfu, i_buf, &r_size); + debug("%s: %s %ld [B]\n", __func__, dfu->name, r_size); + i_blk_seq_num = 0; + /* Integrity check (if needed) */ + crc = crc32(0, dfu_buf, r_size); + } + + if (i_blk_seq_num++ != blk_seq_num) { + printf("%s: Wrong sequence number! [%d] [%d]\n", + __func__, i_blk_seq_num, blk_seq_num); + return -1; + } + + if (r_size >= size) { + memcpy(buf, i_buf, size); + i_buf += size; + r_size -= size; + return size; + } else { + memcpy(buf, i_buf, r_size); + i_buf += r_size; + debug("%s: %s CRC32: 0x%x\n", __func__, dfu->name, crc); + puts("UPLOAD ... done\nCtrl+C to exit ...\n"); + + i_buf = NULL; + i_blk_seq_num = 0; + crc = 0; + return r_size; + } + return ret; +} + +static int dfu_fill_entity(struct dfu_entity *dfu, char* s, int alt, + char *interface, int num) +{ + char *st; + + debug("%s: %s interface: %s num: %d\n", __func__, s, interface, num); + st = strsep(&s, " "); + strcpy(dfu->name, st); + + dfu->dev_num = num; + dfu->alt = alt; + + /* Specific for mmc device */ + if (strcmp(interface, "mmc") == 0) { + if (dfu_fill_entity_mmc(dfu, s)) + return -1; + } else { + printf("%s: Device %s not (yet) supported!\n", + __func__, interface); + return -1; + } + + return 0; +} + +int dfu_config_entities(char *env, char *interface, int num) +{ + struct dfu_entity *dfu; + int i, ret; + char *s; + + dfu_alt_num = dfu_find_alt_num(env); + debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num); + + for (i = 0; i < dfu_alt_num; i++) { + dfu = calloc(sizeof(struct dfu_entity), 1); + + s = strsep(&env, ";"); + ret = dfu_fill_entity(dfu, s, i, interface, num); + if (ret) + return -1; + + list_add_tail(&dfu->list, &dfu_list); + } + + return 0; +} + +void dfu_free_entities(void) +{ + struct dfu_entity *dfu, *p; + + list_for_each_entry_safe_reverse(dfu, p, &dfu_list, list) { + list_del(&dfu->list); + free(dfu); + } + + INIT_LIST_HEAD(&dfu_list); +} + +const char *dfu_get_dev_type(enum dfu_device_type t) +{ + const char *dev_t[] = {NULL, "eMMC", "OneNAND", "NAND" }; + return dev_t[t]; +} + +const char *dfu_get_layout(enum dfu_layout l) +{ + const char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2", + "EXT3", "EXT4" }; + return dfu_layout[l]; +} + +void dfu_show_entities(void) +{ + struct dfu_entity *dfu; + + puts("DFU alt settings list:\n"); + + list_for_each_entry(dfu, &dfu_list, list) { + printf("dev: %s alt: %d name: %s layout: %s\n", + dfu_get_dev_type(dfu->dev_type), dfu->alt, + dfu->name, dfu_get_layout(dfu->layout)); + } +} + +int dfu_get_alt_number(void) +{ + return dfu_alt_num; +} + +struct dfu_entity *dfu_get_entity(int alt) +{ + struct dfu_entity *dfu; + + list_for_each_entry(dfu, &dfu_list, list) { + if (dfu->alt == alt) + return dfu; + } + + return NULL; +} diff --git a/include/dfu.h b/include/dfu.h new file mode 100644 index 0000000..d908f18 --- /dev/null +++ b/include/dfu.h @@ -0,0 +1,103 @@ +/* + * dfu.h - DFU flashable area description + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz andrzej.p@samsung.com + * Lukasz Majewski l.majewski@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_ENTITY_H_ +#define __DFU_ENTITY_H_ + +#include <common.h> +#include <linux/list.h> +#include <mmc.h> + +enum dfu_device_type { + DFU_DEV_MMC = 1, + DFU_DEV_ONENAND, + DFU_DEV_NAND, +}; + +enum dfu_layout { + DFU_RAW_ADDR = 1, + DFU_FS_FAT, + DFU_FS_EXT2, + DFU_FS_EXT3, + DFU_FS_EXT4, +}; + +struct mmc_internal_data { + /* RAW programming */ + unsigned int lba_start; + unsigned int lba_size; + unsigned int lba_blk_size; + + /* FAT/EXT */ + unsigned int dev; + unsigned int part; +}; + +static inline unsigned int get_mmc_blk_size(int dev) +{ + return find_mmc_device(dev)->read_bl_len; +} + +#define DFU_NAME_SIZE 32 +#define DFU_CMD_BUF_SIZE 128 +#define DFU_DATA_BUF_SIZE (1024*1024*4) /* 4 MiB */ + +struct dfu_entity { + char name[DFU_NAME_SIZE]; + int alt; + void *dev_private; + int dev_num; + enum dfu_device_type dev_type; + enum dfu_layout layout; + + union { + struct mmc_internal_data mmc; + } data; + + int (*read_medium)(struct dfu_entity *dfu, void *buf, long *len); + int (*write_medium)(struct dfu_entity *dfu, void *buf, long *len); + + struct list_head list; +}; + +int dfu_config_entities(char *s, char *interface, int num); +void dfu_free_entities(void); +void dfu_show_entities(void); +int dfu_get_alt_number(void); +const char *dfu_get_dev_type(enum dfu_device_type t); +const char *dfu_get_layout(enum dfu_layout l); +struct dfu_entity *dfu_get_entity(int alt); +char *dfu_extract_token(char** e, int *n); + +int dfu_read(struct dfu_entity *de, void *buf, int size, int blk_seq_num); +int dfu_write(struct dfu_entity *de, void *buf, int size, int blk_seq_num); +/* Device specific */ +#ifdef CONFIG_DFU_MMC +extern int dfu_fill_entity_mmc(struct dfu_entity *dfu, char* s); +#else +static inline int dfu_fill_entity_mmc(struct dfu_entity *dfu, char* s) +{ + puts("MMC support not available!\n"); + return -1; +} +#endif +#endif /* __DFU_ENTITY_H_ */

On Tuesday 31 July 2012 02:36:59 Lukasz Majewski wrote:
--- /dev/null +++ b/drivers/dfu/dfu.c
+static int dfu_find_alt_num(char *s)
const char *s
+{
- int i = 0;
- for (; *s; s++)
if (*s == ';')
i++;
- return ++i;
+}
looks kind of like: return (strrchr(s, ';') - s) + 1;
+int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) +{
- static unsigned char *i_buf;
- static int i_blk_seq_num;
- long w_size = 0;
- int ret = 0;
- if (blk_seq_num == 0) {
memset(dfu_buf, '\0', sizeof(dfu_buf));
...
- memcpy(i_buf, buf, size);
- i_buf += size;
why bother clearing it ? since right below we memcpy() in the data we care about from buf, i'd skip the memset() completely.
+int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) +{
- static unsigned char *i_buf;
- static int i_blk_seq_num;
- static long r_size;
- static u32 crc;
- int ret = 0;
- if (blk_seq_num == 0) {
i_buf = dfu_buf;
memset(dfu_buf, '\0', sizeof(dfu_buf));
ret = dfu->read_medium(dfu, i_buf, &r_size);
debug("%s: %s %ld [B]\n", __func__, dfu->name, r_size);
i_blk_seq_num = 0;
/* Integrity check (if needed) */
crc = crc32(0, dfu_buf, r_size);
- }
same here -- punt the memset()
+static int dfu_fill_entity(struct dfu_entity *dfu, char* s, int alt,
"char *s", not "char* s"
+int dfu_config_entities(char *env, char *interface, int num) +{
- struct dfu_entity *dfu;
- int i, ret;
- char *s;
- dfu_alt_num = dfu_find_alt_num(env);
- debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num);
- for (i = 0; i < dfu_alt_num; i++) {
dfu = calloc(sizeof(struct dfu_entity), 1);
seems like you can do this in a single call outside of the for loop: dfu = calloc(sizeof(*dfu), dfu_alt_num); if (!dfu) return -1; for (i = 0; i < dfu_alt_num; i++) { s = strsep(&env, ";"); ret = dfu_fill_entity(&dfu[i], s, i, interface, num); if (ret) return -1; list_add_tail(&dfu[i].list, &dfu_list); }
--- /dev/null +++ b/include/dfu.h
+char *dfu_extract_token(char** e, int *n); +extern int dfu_fill_entity_mmc(struct dfu_entity *dfu, char* s); +static inline int dfu_fill_entity_mmc(struct dfu_entity *dfu, char* s)
"char *s", not "char* s" -mike

Dear Mike Frysinger,
On Tuesday 31 July 2012 02:36:59 Lukasz Majewski wrote:
--- /dev/null +++ b/drivers/dfu/dfu.c
+static int dfu_find_alt_num(char *s)
const char *s
Good point.
+{
- int i = 0;
- for (; *s; s++)
if (*s == ';')
i++;
- return ++i;
+}
In this function I count how many times the ';' separator appears. I didn't found proper function at ./lib/string.c.
looks kind of like: return (strrchr(s, ';') - s) + 1;
The above code returns position of the last occurrence of the ';' separator.
+int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) +{
- static unsigned char *i_buf;
- static int i_blk_seq_num;
- long w_size = 0;
- int ret = 0;
- if (blk_seq_num == 0) {
memset(dfu_buf, '\0', sizeof(dfu_buf));
...
- memcpy(i_buf, buf, size);
- i_buf += size;
why bother clearing it ? since right below we memcpy() in the data we care about from buf, i'd skip the memset() completely.
You are right, removed.
+int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) +{
- static unsigned char *i_buf;
- static int i_blk_seq_num;
- static long r_size;
- static u32 crc;
- int ret = 0;
- if (blk_seq_num == 0) {
i_buf = dfu_buf;
memset(dfu_buf, '\0', sizeof(dfu_buf));
ret = dfu->read_medium(dfu, i_buf, &r_size);
debug("%s: %s %ld [B]\n", __func__, dfu->name,
r_size);
i_blk_seq_num = 0;
/* Integrity check (if needed) */
crc = crc32(0, dfu_buf, r_size);
- }
same here -- punt the memset()
OK,
+static int dfu_fill_entity(struct dfu_entity *dfu, char* s, int alt,
"char *s", not "char* s"
OK,
+int dfu_config_entities(char *env, char *interface, int num) +{
- struct dfu_entity *dfu;
- int i, ret;
- char *s;
- dfu_alt_num = dfu_find_alt_num(env);
- debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num);
- for (i = 0; i < dfu_alt_num; i++) {
dfu = calloc(sizeof(struct dfu_entity), 1);
seems like you can do this in a single call outside of the for loop: dfu = calloc(sizeof(*dfu), dfu_alt_num); if (!dfu) return -1; for (i = 0; i < dfu_alt_num; i++) { s = strsep(&env, ";"); ret = dfu_fill_entity(&dfu[i], s, i, interface, num); if (ret) return -1; list_add_tail(&dfu[i].list, &dfu_list); }
I'd prefer to leave it as it is (since IMHO is more readable) with the addition of following code:
for (i = 0; i < dfu_alt_num; i++) { dfu = calloc(sizeof(struct dfu_entity), 1); if (!dfu) { dfu_free_entities(); return -1; } }
--- /dev/null +++ b/include/dfu.h
+char *dfu_extract_token(char** e, int *n); +extern int dfu_fill_entity_mmc(struct dfu_entity *dfu, char* s); +static inline int dfu_fill_entity_mmc(struct dfu_entity *dfu, char* s)
"char *s", not "char* s"
OK
-mike

On Thursday 02 August 2012 09:55:24 Lukasz Majewski wrote:
Dear Mike Frysinger,
On Tuesday 31 July 2012 02:36:59 Lukasz Majewski wrote:
+{
- int i = 0;
- for (; *s; s++)
if (*s == ';')
i++;
- return ++i;
+}
In this function I count how many times the ';' separator appears.
well, to be more specific, i think it's counting the number of elements if you were to split on ';'. so if there was one ';', this would return 2. but you're correct of course that my suggested replacement is not equivalent.
+int dfu_config_entities(char *env, char *interface, int num) +{
- struct dfu_entity *dfu;
- int i, ret;
- char *s;
- dfu_alt_num = dfu_find_alt_num(env);
- debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num);
- for (i = 0; i < dfu_alt_num; i++) {
dfu = calloc(sizeof(struct dfu_entity), 1);
seems like you can do this in a single call outside of the for loop: dfu = calloc(sizeof(*dfu), dfu_alt_num); if (!dfu)
return -1;
for (i = 0; i < dfu_alt_num; i++) {
s = strsep(&env, ";"); ret = dfu_fill_entity(&dfu[i], s, i, interface, num); if (ret)
return -1; list_add_tail(&dfu[i].list, &dfu_list);
}
I'd prefer to leave it as it is (since IMHO is more readable) with the addition of following code:
it might be more slightly more readable, but doing a single function call results in better runtime performance -mike

Dear Mike Frysinger,
On Thursday 02 August 2012 09:55:24 Lukasz Majewski wrote:
Dear Mike Frysinger,
On Tuesday 31 July 2012 02:36:59 Lukasz Majewski wrote:
+{
- int i = 0;
- for (; *s; s++)
if (*s == ';')
i++;
- return ++i;
+}
In this function I count how many times the ';' separator appears.
well, to be more specific, i think it's counting the number of elements if you were to split on ';'. so if there was one ';', this would return 2. but you're correct of course that my suggested replacement is not equivalent.
+int dfu_config_entities(char *env, char *interface, int num) +{
- struct dfu_entity *dfu;
- int i, ret;
- char *s;
- dfu_alt_num = dfu_find_alt_num(env);
- debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num);
- for (i = 0; i < dfu_alt_num; i++) {
dfu = calloc(sizeof(struct dfu_entity), 1);
seems like you can do this in a single call outside of the for loop: dfu = calloc(sizeof(*dfu), dfu_alt_num); if (!dfu)
return -1;
for (i = 0; i < dfu_alt_num; i++) {
s = strsep(&env, ";"); ret = dfu_fill_entity(&dfu[i], s, i, interface, num); if (ret)
return -1; list_add_tail(&dfu[i].list, &dfu_list);
}
I'd prefer to leave it as it is (since IMHO is more readable) with the
addition of following code:
it might be more slightly more readable, but doing a single function call results in better runtime performance
Doesn't the compiler optimize it as it sees fit?
Best regards, Marek Vasut

On Saturday 04 August 2012 03:47:34 Marek Vasut wrote:
Dear Mike Frysinger,
On Thursday 02 August 2012 09:55:24 Lukasz Majewski wrote:
Dear Mike Frysinger,
On Tuesday 31 July 2012 02:36:59 Lukasz Majewski wrote:
+int dfu_config_entities(char *env, char *interface, int num) +{
- struct dfu_entity *dfu;
- int i, ret;
- char *s;
- dfu_alt_num = dfu_find_alt_num(env);
- debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num);
- for (i = 0; i < dfu_alt_num; i++) {
dfu = calloc(sizeof(struct dfu_entity), 1);
seems like you can do this in a single call outside of the for loop: dfu = calloc(sizeof(*dfu), dfu_alt_num); if (!dfu)
return -1;
for (i = 0; i < dfu_alt_num; i++) {
s = strsep(&env, ";"); ret = dfu_fill_entity(&dfu[i], s, i, interface, num); if (ret)
return -1; list_add_tail(&dfu[i].list, &dfu_list);
}
I'd prefer to leave it as it is (since IMHO is more readable) with the
addition of following code:
it might be more slightly more readable, but doing a single function call results in better runtime performance
Doesn't the compiler optimize it as it sees fit?
gcc can't know to optimize malloc calls like this -mike

Support for MMC storage devices to work with DFU framework.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2: - None
Changes for v3: - Provide special abstraction layer (mmc_{block|file}_{read|write}) to alleviate switch to new device model (DM) - More verbose messages when not supported layout encountered - Calls to strncmp() replaced with strcmp() --- drivers/dfu/Makefile | 1 + drivers/dfu/dfu_mmc.c | 165 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+), 0 deletions(-) create mode 100644 drivers/dfu/dfu_mmc.c
diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index 7736485..7b717bc 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -26,6 +26,7 @@ include $(TOPDIR)/config.mk LIB = $(obj)libdfu.o
COBJS-$(CONFIG_DFU_FUNCTION) += dfu.o +COBJS-$(CONFIG_DFU_MMC) += dfu_mmc.o
SRCS := $(COBJS-y:.o=.c) OBJS := $(addprefix $(obj),$(COBJS-y)) diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c new file mode 100644 index 0000000..3909910 --- /dev/null +++ b/drivers/dfu/dfu_mmc.c @@ -0,0 +1,165 @@ +/* + * dfu.c -- DFU back-end routines + * + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski l.majewski@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 <dfu.h> + +enum dfu_mmc_op { + DFU_OP_READ = 1, + DFU_OP_WRITE, +}; + +static int mmc_block_op(enum dfu_mmc_op op, struct dfu_entity *dfu, + void *buf, long *len) +{ + ALLOC_CACHE_ALIGN_BUFFER(char, cmd_buf, DFU_CMD_BUF_SIZE); + memset(cmd_buf, '\0', sizeof(cmd_buf)); + + sprintf(cmd_buf, "mmc %s 0x%x %x %x", + op == DFU_OP_READ ? "read" : "write", + (unsigned int) buf, + dfu->data.mmc.lba_start, + dfu->data.mmc.lba_size); + + if (op == DFU_OP_READ) + *len = dfu->data.mmc.lba_blk_size * dfu->data.mmc.lba_size; + + debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); + return run_command(cmd_buf, 0); +} + +static inline int mmc_block_write(struct dfu_entity *dfu, void *buf, long *len) +{ + return mmc_block_op(DFU_OP_WRITE, dfu, buf, len); +} + +static inline int mmc_block_read(struct dfu_entity *dfu, void *buf, long *len) +{ + return mmc_block_op(DFU_OP_READ, dfu, buf, len); +} + +static int mmc_file_op(enum dfu_mmc_op op, struct dfu_entity *dfu, + void *buf, long *len) +{ + ALLOC_CACHE_ALIGN_BUFFER(char, cmd_buf, DFU_CMD_BUF_SIZE); + char *str_env; + int ret; + + memset(cmd_buf, '\0', sizeof(cmd_buf)); + + sprintf(cmd_buf, "fat%s mmc %d:%d 0x%x %s %lx", + op == DFU_OP_READ ? "load" : "write", + dfu->data.mmc.dev, dfu->data.mmc.part, + (unsigned int) buf, dfu->name, *len); + + debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); + + ret = run_command(cmd_buf, 0); + if (ret) { + puts("dfu: Read error!\n"); + return ret; + } + + if (dfu->layout != DFU_RAW_ADDR) { + str_env = getenv("filesize"); + if (str_env == NULL) { + puts("dfu: Wrong file size!\n"); + return -1; + } + *len = simple_strtoul(str_env, NULL, 16); + } + + return ret; +} + +static inline int mmc_file_write(struct dfu_entity *dfu, void *buf, long *len) +{ + return mmc_file_op(DFU_OP_WRITE, dfu, buf, len); +} + +static inline int mmc_file_read(struct dfu_entity *dfu, void *buf, long *len) +{ + return mmc_file_op(DFU_OP_READ, dfu, buf, len); +} + +int dfu_write_medium_mmc(struct dfu_entity *dfu, void *buf, long *len) +{ + int ret = -1; + + switch (dfu->layout) { + case DFU_RAW_ADDR: + ret = mmc_block_write(dfu, buf, len); + break; + case DFU_FS_FAT: + ret = mmc_file_write(dfu, buf, len); + break; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + } + + return ret; +} + +int dfu_read_medium_mmc(struct dfu_entity *dfu, void *buf, long *len) +{ + int ret = -1; + + switch (dfu->layout) { + case DFU_RAW_ADDR: + ret = mmc_block_read(dfu, buf, len); + break; + case DFU_FS_FAT: + ret = mmc_file_read(dfu, buf, len); + break; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + } + + return ret; +} + +int dfu_fill_entity_mmc(struct dfu_entity *dfu, char* s) +{ + char *st; + + dfu->dev_type = DFU_DEV_MMC; + st = strsep(&s, " "); + if (!strcmp(st, "mmc")) { + dfu->layout = DFU_RAW_ADDR; + dfu->data.mmc.lba_start = simple_strtoul(s, &s, 16); + dfu->data.mmc.lba_size = simple_strtoul(++s, &s, 16); + dfu->data.mmc.lba_blk_size = get_mmc_blk_size(dfu->dev_num); + } else if (!strcmp(st, "fat")) { + dfu->layout = DFU_FS_FAT; + dfu->data.mmc.dev = simple_strtoul(s, &s, 10); + dfu->data.mmc.part = simple_strtoul(++s, &s, 10); + } else { + printf("%s: Memory layout (%s) not supported!\n", __func__, st); + } + + dfu->read_medium = dfu_read_medium_mmc; + dfu->write_medium = dfu_write_medium_mmc; + + return 0; +}

On Tuesday 31 July 2012 02:37:00 Lukasz Majewski wrote:
--- /dev/null +++ b/drivers/dfu/dfu_mmc.c
+static int mmc_block_op(enum dfu_mmc_op op, struct dfu_entity *dfu,
void *buf, long *len)
+{
- ALLOC_CACHE_ALIGN_BUFFER(char, cmd_buf, DFU_CMD_BUF_SIZE);
ugh, what ? you're passing this string to run_command so there is no point in aligning it (not to mention the topic of u-boot code internally calling run_command() is seriously wrong.
- memset(cmd_buf, '\0', sizeof(cmd_buf));
- sprintf(cmd_buf, "mmc %s 0x%x %x %x",
that memset is pointless. delete it.
+static int mmc_file_op(enum dfu_mmc_op op, struct dfu_entity *dfu,
void *buf, long *len)
+{
came comments for this func as above
+int dfu_fill_entity_mmc(struct dfu_entity *dfu, char* s)
"char *s", not "char* s". please search all your patches for this mistake as it seems to have come up a lot. -mike

Dear Mike Frysinger,
On Tuesday 31 July 2012 02:37:00 Lukasz Majewski wrote:
--- /dev/null +++ b/drivers/dfu/dfu_mmc.c
+static int mmc_block_op(enum dfu_mmc_op op, struct dfu_entity *dfu,
void *buf, long *len)
+{
- ALLOC_CACHE_ALIGN_BUFFER(char, cmd_buf, DFU_CMD_BUF_SIZE);
ugh, what ? you're passing this string to run_command so there is no point in aligning it
Ok, I will change this.
(not to mention the topic of u-boot code internally calling run_command() is seriously wrong.
We are all aware that this approach is a compromise. And this code will be rewritten when new DM appears.
- memset(cmd_buf, '\0', sizeof(cmd_buf));
- sprintf(cmd_buf, "mmc %s 0x%x %x %x",
that memset is pointless. delete it.
Done.
+static int mmc_file_op(enum dfu_mmc_op op, struct dfu_entity *dfu,
void *buf, long *len)
+{
came comments for this func as above
+int dfu_fill_entity_mmc(struct dfu_entity *dfu, char* s)
"char *s", not "char* s". please search all your patches for this mistake as it seems to have come up a lot.
Hmm, ./tools/checkpatch wasn't complaining about this...
-mike

Support for u-boot's "dfu <interface> <dev> [list]" command.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2: - None
Changes for v3: - Remove unnecessary initialization to NULL of dynamic variables - goto done added to reduce code duplication - static definition of do_dfu() --- common/Makefile | 1 + common/cmd_dfu.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 0 deletions(-) create mode 100644 common/cmd_dfu.c
diff --git a/common/Makefile b/common/Makefile index 483eb4d..32d44e5 100644 --- a/common/Makefile +++ b/common/Makefile @@ -183,6 +183,7 @@ COBJS-$(CONFIG_MENU) += menu.o COBJS-$(CONFIG_MODEM_SUPPORT) += modem.o COBJS-$(CONFIG_UPDATE_TFTP) += update.o COBJS-$(CONFIG_USB_KEYBOARD) += usb_kbd.o +COBJS-$(CONFIG_CMD_DFU) += cmd_dfu.o endif
ifdef CONFIG_SPL_BUILD diff --git a/common/cmd_dfu.c b/common/cmd_dfu.c new file mode 100644 index 0000000..72efb60 --- /dev/null +++ b/common/cmd_dfu.c @@ -0,0 +1,81 @@ +/* + * cmd_dfu.c -- dfu command + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz andrzej.p@samsung.com + * Lukasz Majewski l.majewski@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 <malloc.h> +#include <dfu.h> +#include <asm/errno.h> +#include <g_dnl.h> + +static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + static char *s = "dfu"; + const char *str_env; + char *env_bkp; + int ret; + + if (argc < 3) + return CMD_RET_USAGE; + + str_env = getenv("dfu_alt_info"); + if (str_env == NULL) { + printf("%s: "dfu_alt_info" env variable not defined!\n", + __func__); + return CMD_RET_FAILURE; + } + + env_bkp = strdup(str_env); + ret = dfu_config_entities(env_bkp, argv[1], + (int)simple_strtoul(argv[2], NULL, 10)); + if (ret) + return CMD_RET_FAILURE; + + if (strcmp(argv[3], "list") == 0) { + dfu_show_entities(); + goto done; + } + + board_usb_init(); + g_dnl_register(s); + while (1) { + if (ctrlc()) + goto exit; + + usb_gadget_handle_interrupts(); + } +exit: + g_dnl_unregister(); +done: + dfu_free_entities(); + free(env_bkp); + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu, + "Device Firmware Upgrade", + "<interface> <dev> [list]\n" + " - device firmware upgrade on a device <dev>\n" + " attached to interface <interface>\n" + " [list] - list available alt settings" +);

On 07/31/2012 12:37 AM, Lukasz Majewski wrote:
Support for u-boot's "dfu <interface> <dev> [list]" command.
+U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu,
- "Device Firmware Upgrade",
- "<interface> <dev> [list]\n"
- " - device firmware upgrade on a device <dev>\n"
- " attached to interface <interface>\n"
- " [list] - list available alt settings"
+);
Hmm. Is there any way to make this work without specifying "interface dev", or to allow specifying multiple "interface dev" entries? On a system with all of eMMC, NAND, and SPI, I'd like to just run "dfu" as the U-Boot command, and have the host specify which of those "devices" it wants to download to using the DFU protocol. So, if flashing a bunch of devices, there is no need to interact with U-Boot over both serial and USB in order to invoke the dfu command multiple times.
Somewhat related to this, it looks like the eMMC support doesn't allow the HW partition to be specified; it would be nice to expose alt settings for all of:
a) Each individual HW partition (boot0/1 if present, general0/1/2/3 if present, the user area, maybe the replay block)
b) Perhaps also a linearized view of the raw eMMC (LBAs 0..boot_size-1 write to boot 0, LBAs boot_size..(2*boot_size)-1 write to boot1, LBAs 2*boot_size..end_of_device write to user area for example).

Hi Stephen Warren,
On 07/31/2012 12:37 AM, Lukasz Majewski wrote:
Support for u-boot's "dfu <interface> <dev> [list]" command.
+U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu,
- "Device Firmware Upgrade",
- "<interface> <dev> [list]\n"
- " - device firmware upgrade on a device <dev>\n"
- " attached to interface <interface>\n"
- " [list] - list available alt settings"
+);
Hmm. Is there any way to make this work without specifying "interface dev", or to allow specifying multiple "interface dev" entries? On a system with all of eMMC, NAND, and SPI, I'd like to just run "dfu" as the U-Boot command, and have the host specify which of those "devices" it wants to download to using the DFU protocol. So, if flashing a bunch of devices, there is no need to interact with U-Boot over both serial and USB in order to invoke the dfu command multiple times.
It would be possible by specifying proper altsettings e.g.: a1 mmc-boot a2 mmc-uImage a3 nand-part0 a4 nand-part1 etc.
However, I think that for the start, the approach proposed here (as dfu mmc 0 command call) is sufficient.
This approach is used with several file systems calls (e.g. fatload mmc ... , fatwrite mmc ... etc.) and in my opinion it is consistent.
Somewhat related to this, it looks like the eMMC support doesn't allow the HW partition to be specified; it would be nice to expose alt settings for all of:
a) Each individual HW partition (boot0/1 if present, general0/1/2/3 if present, the user area, maybe the replay block)
I'm fully aware of this problem. In the eMMC case, the access to boot partitions will be served by defining special alt settings for DFU.
Those altsettings will be defined as follows: -a[N] boot0 -a[N+1] boot1
Prerequisite for this functionality is support of "boot" command, which will allow switching of the MMC available address spaces (e.g. between boot0/boot1 and user accessible space).
b) Perhaps also a linearized view of the raw eMMC (LBAs 0..boot_size-1 write to boot 0, LBAs boot_size..(2*boot_size)-1 write to boot1, LBAs 2*boot_size..end_of_device write to user area for example).
Access to partitions will be done differently. I assume that each eMMC memory (the user accessible part) will be equipped with MBR or GPT definition.
For this reason I'm now developing the USB Mass Storage (UMS) gadget to export eMMC to host PC. This solves the problem with accessing separate partitions.
Please also be aware, that DFU shall be used only for relatively small files (due to small EP0 bandwidth).
Large files shall be copied with UMS.

On 08/01/2012 01:16 AM, Lukasz Majewski wrote:
Hi Stephen Warren,
On 07/31/2012 12:37 AM, Lukasz Majewski wrote:
Support for u-boot's "dfu <interface> <dev> [list]" command.
+U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu,
- "Device Firmware Upgrade",
- "<interface> <dev> [list]\n"
- " - device firmware upgrade on a device <dev>\n"
- " attached to interface <interface>\n"
- " [list] - list available alt settings"
+);
...
Somewhat related to this, it looks like the eMMC support doesn't allow the HW partition to be specified; it would be nice to expose alt settings for all of:
a) Each individual HW partition (boot0/1 if present, general0/1/2/3 if present, the user area, maybe the replay block)
I'm fully aware of this problem. In the eMMC case, the access to boot partitions will be served by defining special alt settings for DFU.
Those altsettings will be defined as follows: -a[N] boot0 -a[N+1] boot1
Prerequisite for this functionality is support of "boot" command, which will allow switching of the MMC available address spaces (e.g. between boot0/boot1 and user accessible space).
Is this "boot" command a DFU protocol command, or a U-Boot command-line command? I note that the U-Boot command-line already allows HW partition selection using an additional parameter to "mmc" - "mmc dev $mmc_device_id $partition_id".
b) Perhaps also a linearized view of the raw eMMC (LBAs 0..boot_size-1 write to boot 0, LBAs boot_size..(2*boot_size)-1 write to boot1, LBAs 2*boot_size..end_of_device write to user area for example).
Access to partitions will be done differently. I assume that each eMMC memory (the user accessible part) will be equipped with MBR or GPT definition.
That's a different kind of partition though.
In general, there's no need for the eMMC device to contain any kind of standardized SW-level partition table. On Tegra, the boot ROM can boot directly from an eMMC device, and that requires raw data in the partitions, not a standardized SW partition table.
For this reason I'm now developing the USB Mass Storage (UMS) gadget to export eMMC to host PC. This solves the problem with accessing separate partitions.
OK, if the raw eMMC device is exposed using USB storage, we should be able to dd directly to it and the presence-or-lack-thereof of any MBR/GPT wouldn't even be relevant.
It'd still be useful to have a linearized view of the flash that concatenated all the HW partitions into a single raw device, but I guess an alt setting for that probably could be added later.
Please also be aware, that DFU shall be used only for relatively small files (due to small EP0 bandwidth).
Large files shall be copied with UMS.
Oh, didn't know that. Just out of curiosity, are you thinking of implementing that too?

On Wed, 01 Aug 2012 11:13:14 -0600 Stephen Warren swarren@wwwdotorg.org wrote:
On 08/01/2012 01:16 AM, Lukasz Majewski wrote:
Hi Stephen Warren,
On 07/31/2012 12:37 AM, Lukasz Majewski wrote:
Support for u-boot's "dfu <interface> <dev> [list]" command.
+U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu,
- "Device Firmware Upgrade",
- "<interface> <dev> [list]\n"
- " - device firmware upgrade on a device <dev>\n"
- " attached to interface <interface>\n"
- " [list] - list available alt settings"
+);
...
Somewhat related to this, it looks like the eMMC support doesn't allow the HW partition to be specified; it would be nice to expose alt settings for all of:
a) Each individual HW partition (boot0/1 if present, general0/1/2/3 if present, the user area, maybe the replay block)
I'm fully aware of this problem. In the eMMC case, the access to boot partitions will be served by defining special alt settings for DFU.
Those altsettings will be defined as follows: -a[N] boot0 -a[N+1] boot1
Prerequisite for this functionality is support of "boot" command, which will allow switching of the MMC available address spaces (e.g. between boot0/boot1 and user accessible space).
Is this "boot" command a DFU protocol command, or a U-Boot command-line command? I note that the U-Boot command-line already allows HW partition selection using an additional parameter to "mmc"
- "mmc dev $mmc_device_id $partition_id".
I'm rather thinking of a U-boot command (as it is easier to integrate). The command would be "mmc boot <dev> <params .. boot partition>"
Although it is not yet implemented, it is on the top of mine "TO DO" list :-).
b) Perhaps also a linearized view of the raw eMMC (LBAs 0..boot_size-1 write to boot 0, LBAs boot_size..(2*boot_size)-1 write to boot1, LBAs 2*boot_size..end_of_device write to user area for example).
Access to partitions will be done differently. I assume that each eMMC memory (the user accessible part) will be equipped with MBR or GPT definition.
That's a different kind of partition though.
In general, there's no need for the eMMC device to contain any kind of standardized SW-level partition table. On Tegra, the boot ROM can boot directly from an eMMC device, and that requires raw data in the partitions, not a standardized SW partition table.
For this reason I'm now developing the USB Mass Storage (UMS) gadget to export eMMC to host PC. This solves the problem with accessing separate partitions.
OK, if the raw eMMC device is exposed using USB storage, we should be able to dd directly to it and the presence-or-lack-thereof of any MBR/GPT wouldn't even be relevant.
With the UMS you would see the whole mmc address space as one partition. Then you can use dd with several parameters to "dump" data to a specific LBA address.
When correct MBR or GPT is present, then we can specify partitions to be accessed.
It'd still be useful to have a linearized view of the flash that concatenated all the HW partitions into a single raw device, but I guess an alt setting for that probably could be added later.
I think that UMS solves this issue. However, correct me if I'm wrong.
Let's assume, that UMS causes host to see following partitions: sde: sde1 sde2 sde3 sde4 < sde5 .. sdeN >
Is the "mount -t vfat /dev/sde /mnt" not allowing access to the whole eMMC device (despite of the partitions)?
Please also be aware, that DFU shall be used only for relatively small files (due to small EP0 bandwidth).
Large files shall be copied with UMS.
Oh, didn't know that. Just out of curiosity, are you thinking of implementing that too?
The USB Mass Storage gadget is now working with the g_dnl composite gadget as a f_ums function.
Unfortunately it is not polished enough to be posted to ML.
I'd prefer that DFU related patches would be pulled to ML first. Afterwards I would post the UMS function.

On 08/02/2012 02:31 AM, Lukasz Majewski wrote:
On Wed, 01 Aug 2012 11:13:14 -0600 Stephen Warren swarren@wwwdotorg.org wrote:
On 08/01/2012 01:16 AM, Lukasz Majewski wrote:
Hi Stephen Warren,
On 07/31/2012 12:37 AM, Lukasz Majewski wrote:
Support for u-boot's "dfu <interface> <dev> [list]" command.
+U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu,
- "Device Firmware Upgrade",
- "<interface> <dev> [list]\n"
- " - device firmware upgrade on a device <dev>\n"
- " attached to interface <interface>\n"
- " [list] - list available alt settings"
+);
...
Somewhat related to this, it looks like the eMMC support doesn't allow the HW partition to be specified; it would be nice to expose alt settings for all of:
a) Each individual HW partition (boot0/1 if present, general0/1/2/3 if present, the user area, maybe the replay block)
I'm fully aware of this problem. In the eMMC case, the access to boot partitions will be served by defining special alt settings for DFU.
Those altsettings will be defined as follows: -a[N] boot0 -a[N+1] boot1
Prerequisite for this functionality is support of "boot" command, which will allow switching of the MMC available address spaces (e.g. between boot0/boot1 and user accessible space).
Is this "boot" command a DFU protocol command, or a U-Boot command-line command? I note that the U-Boot command-line already allows HW partition selection using an additional parameter to "mmc"
- "mmc dev $mmc_device_id $partition_id".
I'm rather thinking of a U-boot command (as it is easier to integrate). The command would be "mmc boot <dev> <params .. boot partition>"
Although it is not yet implemented, it is on the top of mine "TO DO" list :-).
I guess I'm confused what that "boot" command would actually do then... The only thing I can think that it might do is already covered by the "mmc" command's partition parameter.
b) Perhaps also a linearized view of the raw eMMC (LBAs 0..boot_size-1 write to boot 0, LBAs boot_size..(2*boot_size)-1 write to boot1, LBAs 2*boot_size..end_of_device write to user area for example).
Access to partitions will be done differently. I assume that each eMMC memory (the user accessible part) will be equipped with MBR or GPT definition.
That's a different kind of partition though.
In general, there's no need for the eMMC device to contain any kind of standardized SW-level partition table. On Tegra, the boot ROM can boot directly from an eMMC device, and that requires raw data in the partitions, not a standardized SW partition table.
For this reason I'm now developing the USB Mass Storage (UMS) gadget to export eMMC to host PC. This solves the problem with accessing separate partitions.
OK, if the raw eMMC device is exposed using USB storage, we should be able to dd directly to it and the presence-or-lack-thereof of any MBR/GPT wouldn't even be relevant.
With the UMS you would see the whole mmc address space as one partition. Then you can use dd with several parameters to "dump" data to a specific LBA address.
When correct MBR or GPT is present, then we can specify partitions to be accessed.
It'd still be useful to have a linearized view of the flash that concatenated all the HW partitions into a single raw device, but I guess an alt setting for that probably could be added later.
I think that UMS solves this issue. However, correct me if I'm wrong.
Let's assume, that UMS causes host to see following partitions: sde: sde1 sde2 sde3 sde4 < sde5 .. sdeN >
Is the "mount -t vfat /dev/sde /mnt" not allowing access to the whole eMMC device (despite of the partitions)?
Again, this is confusing two different kinds of partitions.
There are HW-level partitions/regions/areas within the eMMC HW itself. You need to send commands to the eMMC device to select whether read/write commands act on the boot0/boot1/general*/user HW partition.
There are (or can be) SW-level partitions within any/all of those HW partitions. This is the level at which an MBR/GPT partition would exist.
With DFU, I'd expect an alt setting for each of the HW partitions at least.
With UMS, I'd expect a device to appear for each of the HW partitions. (these may show up as say /dev/sdb for boot0, /dev/sdc for boot1, /dev/sdd for the user area). If an MBR/GPT is present within any of those, Linux may then create a device for each SW partition, so e.g. /dev/sdd1, /dev/sdd2, etc.

Dear Stephen Warren,
Again, this is confusing two different kinds of partitions.
There are HW-level partitions/regions/areas within the eMMC HW itself. You need to send commands to the eMMC device to select whether read/write commands act on the boot0/boot1/general*/user HW partition.
This will be done via "mmc boot [dev] [HW partition (boot0/1/user]" command (from u-boot prompt). This command is not yet implemented at u-boot (at least for Trats development board). After its implementation it will be used as a helper function for dfu.
As a result the access to those partition will be done via proper DFU's alt settings: mmc-boot0 mmc-boot1 etc.
There are (or can be) SW-level partitions within any/all of those HW partitions. This is the level at which an MBR/GPT partition would exist.
With DFU, I'd expect an alt setting for each of the HW partitions at least.
And this is my goal. Now only the "user" HW partition is supported. After "mmc boot ..." command implementation support for other partitions will be added for DFU as well.
With UMS, I'd expect a device to appear for each of the HW partitions. (these may show up as say /dev/sdb for boot0, /dev/sdc for boot1, /dev/sdd for the user area).
Frankly speaking I've thought of providing access only to user HW partition for initial UMS implementation (the UMS v1 implementation). However access to all HW partitions from UMS is worth consideration.
If an MBR/GPT is present within any of those, Linux may then create a device for each SW partition, so e.g. /dev/sdd1, /dev/sdd2, etc.
This is the functionality which now UMS (v1) provides.

On 08/03/2012 12:13 AM, Lukasz Majewski wrote:
Dear Stephen Warren,
Again, this is confusing two different kinds of partitions.
There are HW-level partitions/regions/areas within the eMMC HW itself. You need to send commands to the eMMC device to select whether read/write commands act on the boot0/boot1/general*/user HW partition.
This will be done via "mmc boot [dev] [HW partition (boot0/1/user]" command (from u-boot prompt).
Hmm. I still fail to see how this is any different to the existing "mmc dev" command. Are the following two commands not exactly identical:
mmc dev $dev $part # already exists today mmc boot $dev $part # new command you're proposing
?
This command is not yet implemented at u-boot (at least for Trats development board). After its implementation it will be used as a helper function for dfu.

Dear Stephen Warren,
On 08/03/2012 12:13 AM, Lukasz Majewski wrote:
Dear Stephen Warren,
Again, this is confusing two different kinds of partitions.
There are HW-level partitions/regions/areas within the eMMC HW itself. You need to send commands to the eMMC device to select whether read/write commands act on the boot0/boot1/general*/user HW partition.
This will be done via "mmc boot [dev] [HW partition (boot0/1/user]" command (from u-boot prompt).
Hmm. I still fail to see how this is any different to the existing "mmc dev" command. Are the following two commands not exactly identical:
mmc dev $dev $part # already exists today mmc boot $dev $part # new command you're proposing
?
You are totally right the $part part is the thing I'm looking for. I had in mind a previous version of "mmc dev" command with only the $dev part.
As I can see in the code - the mmc_switch() call does exactly the things that I need :-)
This command is not yet implemented at u-boot (at least for Trats development board). After its implementation it will be used as a helper function for dfu.

Dear Mike Frysinger,
On Tuesday 31 July 2012 02:37:01 Lukasz Majewski wrote:
--- /dev/null +++ b/common/cmd_dfu.c
- static char *s = "dfu";
no need for this to be static
It can be pulled out of the function and be made into const static
-mike
Best regards, Marek Vasut

Dear Marek Vasut,
Dear Mike Frysinger,
On Tuesday 31 July 2012 02:37:01 Lukasz Majewski wrote:
--- /dev/null +++ b/common/cmd_dfu.c
- static char *s = "dfu";
no need for this to be static
It can be pulled out of the function and be made into const static
Ok.
-mike
Best regards, Marek Vasut

On Thursday 02 August 2012 03:16:18 Marek Vasut wrote:
Dear Mike Frysinger,
On Tuesday 31 July 2012 02:37:01 Lukasz Majewski wrote:
--- /dev/null +++ b/common/cmd_dfu.c
- static char *s = "dfu";
no need for this to be static
It can be pulled out of the function and be made into const static
err, there's no point. the static markings here are for the pointer "s", not the stuff it's pointing to "dfu".
use: char s[] = "dfu"; and it'll probably all optimize away correctly -mike

Support for USB UDC driver at trats board.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de Cc: Minkyu Kang mk7.kang@samsung.com
--- Changes for v2: - replace puts to debug --- board/samsung/trats/trats.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/board/samsung/trats/trats.c b/board/samsung/trats/trats.c index a8b2b11..4f9cb5a 100644 --- a/board/samsung/trats/trats.c +++ b/board/samsung/trats/trats.c @@ -59,6 +59,8 @@ static int hwrevision(int rev) return (board_rev & 0xf) == rev; }
+struct s3c_plat_otg_data s5pc210_otg_data; + int board_init(void) { gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100; @@ -259,6 +261,12 @@ struct s3c_plat_otg_data s5pc210_otg_data = { .usb_phy_ctrl = EXYNOS4_USBPHY_CONTROL, .usb_flags = PHY0_SLEEP, }; + +void board_usb_init(void) +{ + debug("USB_udc_probe\n"); + s3c_udc_probe(&s5pc210_otg_data); +} #endif
static void pmic_reset(void)

On 31 July 2012 15:37, Lukasz Majewski l.majewski@samsung.com wrote:
Support for USB UDC driver at trats board.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de Cc: Minkyu Kang mk7.kang@samsung.com
Changes for v2:
- replace puts to debug
board/samsung/trats/trats.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
Acked-by: Minkyu Kang mk7.kang@samsung.com
Thanks. Minkyu Kang.

Enable the g_dnl composite USB gadget driver with embedded DFU function on it. It now uses the composite gadget framework to support download specific USB functions (like enabled DFU or USB Mass Storage).
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de Cc: Minkyu Kang mk7.kang@samsung.com
--- Change for v2: - Move the G_DNL_{VENDOR_NUM, PRODUCT_NUM and MANUFACTURER} definitions to ./include/configs/<board>.h
Changes for v3: - None --- include/configs/trats.h | 24 +++++++++++++++++++++++- 1 files changed, 23 insertions(+), 1 deletions(-)
diff --git a/include/configs/trats.h b/include/configs/trats.h index eb269b2..75a23b0 100644 --- a/include/configs/trats.h +++ b/include/configs/trats.h @@ -94,6 +94,21 @@ #undef CONFIG_CMD_ONENAND #undef CONFIG_CMD_MTDPARTS #define CONFIG_CMD_MMC +#define CONFIG_CMD_DFU + +/* FAT */ +#define CONFIG_CMD_FAT +#define CONFIG_FAT_WRITE + +/* USB Composite download gadget - g_dnl */ +#define CONFIG_USBDOWNLOAD_GADGET +#define CONFIG_DFU_FUNCTION +#define CONFIG_DFU_MMC + +/* USB Samsung's IDs */ +#define CONFIG_G_DNL_VENDOR_NUM 0x04E8 +#define CONFIG_G_DNL_PRODUCT_NUM 0x6601 +#define CONFIG_G_DNL_MANUFACTURER "Samsung"
#define CONFIG_BOOTDELAY 1 #define CONFIG_ZERO_BOOTDELAY_CHECK @@ -104,6 +119,11 @@ #define CONFIG_BOOTBLOCK "10" #define CONFIG_ENV_COMMON_BOOT "${console} ${meminfo}"
+#define CONFIG_DFU_ALT \ + "dfu_alt_info=" \ + "u-boot mmc 80 400;" \ + "uImage fat 0 2\0" \ + #define CONFIG_ENV_OVERWRITE #define CONFIG_SYS_CONSOLE_INFO_QUIET #define CONFIG_SYS_CONSOLE_IS_IN_ENV @@ -146,7 +166,8 @@ "mmcdev=0\0" \ "mmcbootpart=2\0" \ "mmcrootpart=3\0" \ - "opts=always_resume=1" + "opts=always_resume=1\0" \ + CONFIG_DFU_ALT
/* Miscellaneous configurable options */ #define CONFIG_SYS_LONGHELP /* undef to save memory */ @@ -209,6 +230,7 @@ #define CONFIG_USB_GADGET #define CONFIG_USB_GADGET_S3C_UDC_OTG #define CONFIG_USB_GADGET_DUALSPEED +#define CONFIG_USB_GADGET_VBUS_DRAW 2
/* LCD */ #define CONFIG_EXYNOS_FB

On 31 July 2012 15:37, Lukasz Majewski l.majewski@samsung.com wrote:
Enable the g_dnl composite USB gadget driver with embedded DFU function on it. It now uses the composite gadget framework to support download specific USB functions (like enabled DFU or USB Mass Storage).
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de Cc: Minkyu Kang mk7.kang@samsung.com
Change for v2:
- Move the G_DNL_{VENDOR_NUM, PRODUCT_NUM and MANUFACTURER} definitions to ./include/configs/<board>.h
Changes for v3:
- None
include/configs/trats.h | 24 +++++++++++++++++++++++- 1 files changed, 23 insertions(+), 1 deletions(-)
Acked-by: Minkyu Kang mk7.kang@samsung.com
Thanks. Minkyu Kang.

Those patches add support for composite USB download gadget. This gadget (at least for now) is equipped with DFU download function.
A separate DFU back-end and front-end have been added. Back-end is placed at ./drivers/dfu directory. The front-end is implemented as USB function.
The back-end is written in a generic manner with storage device specific code separated (eMMC).
DFU specification can be found at: http://wiki.openmoko.org/wiki/USB_DFU_-_The_USB_Device_Firmware_Upgrade_stan...
Example usage:
u-boot side: dfu mmc 0 PC: dfu-util -U IMAGE.bin -a uImage (for upload) dfu-util -D uImage -a uImage (download)
To list the alt settings: dfu mmc 0 list
Test HW: Exynos4210 Trats board
Lukasz Majewski (7): dfu:usb: Support for g_dnl composite download gadget. dfu:usb: DFU USB function (f_dfu) support for g_dnl composite gadget dfu: DFU backend implementation dfu: MMC specific routines for DFU operation dfu:cmd: Support for DFU u-boot command arm:trats: Support for USB UDC driver at TRATS board. arm:trats: Enable g_dnl composite USB gadget with embedded DFU function on TRATS
Makefile | 1 + board/samsung/trats/trats.c | 8 + common/Makefile | 1 + common/cmd_dfu.c | 81 +++++ drivers/dfu/Makefile | 44 +++ drivers/dfu/dfu.c | 239 ++++++++++++++ drivers/dfu/dfu_mmc.c | 162 ++++++++++ drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_dfu.c | 749 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/f_dfu.h | 100 ++++++ drivers/usb/gadget/g_dnl.c | 202 ++++++++++++ include/configs/trats.h | 24 ++- include/dfu.h | 103 ++++++ include/g_dnl.h | 33 ++ 14 files changed, 1748 insertions(+), 1 deletions(-) create mode 100644 common/cmd_dfu.c create mode 100644 drivers/dfu/Makefile create mode 100644 drivers/dfu/dfu.c create mode 100644 drivers/dfu/dfu_mmc.c create mode 100644 drivers/usb/gadget/f_dfu.c create mode 100644 drivers/usb/gadget/f_dfu.h create mode 100644 drivers/usb/gadget/g_dnl.c create mode 100644 include/dfu.h create mode 100644 include/g_dnl.h

Composite USB download gadget support (g_dnl) for download functions. This code works on top of composite gadget.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2:
- G_DNL_{VENDOR_NUM, PRODUCT_NUM and MANUFACTURER} defined at ./include/configs/<board>.h - Suspend and resume stub methods removed - '\0' repleaced with plain 0
Changes for v3: - Remove unused #includes - Replace strncpy and strncat with strcpy and strcat. It was possible due to new approach to g_dnl name generation (at g_dnl_register function) - Rename the g_dnl_{init|cleanup} to g_dnl_{register|unregister} - Replace the G_DNL_* CONFIG_G_DNL_*
Changes for v4: - Remove unnecessary otg calls - odin prefix replaced with g_dnl - Single space indentation between type and variable name --- drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/g_dnl.c | 202 +++++++++++++++++++++++++++++++++++++++++++ include/g_dnl.h | 33 +++++++ 3 files changed, 236 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/gadget/g_dnl.c create mode 100644 include/g_dnl.h
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 87d1918..2c067c8 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -29,6 +29,7 @@ LIB := $(obj)libusb_gadget.o ifdef CONFIG_USB_GADGET COBJS-y += epautoconf.o config.o usbstring.o COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o +COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o endif ifdef CONFIG_USB_ETHER COBJS-y += ether.o epautoconf.o config.o usbstring.o diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c new file mode 100644 index 0000000..7d87050 --- /dev/null +++ b/drivers/usb/gadget/g_dnl.c @@ -0,0 +1,202 @@ +/* + * g_dnl.c -- USB Downloader Gadget + * + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski l.majewski@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 <errno.h> +#include <common.h> +#include <malloc.h> + +#include <mmc.h> +#include <part.h> + +#include <g_dnl.h> +#include "f_dfu.h" + +#include "gadget_chips.h" +#include "composite.c" + +/* + * One needs to define the following: + * CONFIG_G_DNL_VENDOR_NUM + * CONFIG_G_DNL_PRODUCT_NUM + * CONFIG_G_DNL_MANUFACTURER + * at e.g. ./include/configs/<board>.h + */ + +#define STRING_MANUFACTURER 25 +#define STRING_PRODUCT 2 +#define STRING_USBDOWN 2 +#define CONFIG_USBDOWNLOADER 2 + +#define DRIVER_VERSION "usb_dnl 2.0" + +static const char shortname[] = "usb_dnl_"; +static const char product[] = "USB download gadget"; +static const char manufacturer[] = CONFIG_G_DNL_MANUFACTURER; + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_COMM, + .bDeviceSubClass = 0x02, /*0x02:CDC-modem , 0x00:CDC-serial*/ + + .idVendor = __constant_cpu_to_le16(CONFIG_G_DNL_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16(CONFIG_G_DNL_PRODUCT_NUM), + .iProduct = STRING_PRODUCT, + .bNumConfigurations = 1, +}; + +/* static strings, in UTF-8 */ +static struct usb_string g_dnl_string_defs[] = { + { 0, manufacturer, }, + { 1, product, }, +}; + +static struct usb_gadget_strings g_dnl_string_tab = { + .language = 0x0409, /* en-us */ + .strings = g_dnl_string_defs, +}; + +static struct usb_gadget_strings *g_dnl_composite_strings[] = { + &g_dnl_string_tab, + NULL, +}; + +static int g_dnl_unbind(struct usb_composite_dev *cdev) +{ + debug("%s\n", __func__); + return 0; +} + +static int g_dnl_do_config(struct usb_configuration *c) +{ + const char *s = c->cdev->driver->name; + int ret = -1; + + debug("%s: configuration: 0x%p composite dev: 0x%p\n", + __func__, c, c->cdev); + + printf("GADGET DRIVER: %s\n", s); + if (!strcmp(s, "usb_dnl_dfu")) + ret = dfu_add(c); + + return ret; +} + +static int g_dnl_config_register(struct usb_composite_dev *cdev) +{ + static struct usb_configuration config = { + .label = "usb_dnload", + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .bConfigurationValue = CONFIG_USBDOWNLOADER, + .iConfiguration = STRING_USBDOWN, + + .bind = g_dnl_do_config, + }; + + return usb_add_config(cdev, &config); +} + +static int g_dnl_bind(struct usb_composite_dev *cdev) +{ + struct usb_gadget *gadget = cdev->gadget; + int id, ret; + int gcnum; + + debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev); + + id = usb_string_id(cdev); + + if (id < 0) + return id; + g_dnl_string_defs[0].id = id; + device_desc.iManufacturer = id; + + id = usb_string_id(cdev); + if (id < 0) + return id; + + g_dnl_string_defs[1].id = id; + device_desc.iProduct = id; + + ret = g_dnl_config_register(cdev); + if (ret) + goto error; + + gcnum = usb_gadget_controller_number(gadget); + + debug("gcnum: %d\n", gcnum); + if (gcnum >= 0) + device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); + else { + debug("%s: controller '%s' not recognized\n", + shortname, gadget->name); + device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); + } + + return 0; + + error: + g_dnl_unbind(cdev); + return -ENOMEM; +} + +static struct usb_composite_driver g_dnl_driver = { + .name = NULL, + .dev = &device_desc, + .strings = g_dnl_composite_strings, + + .bind = g_dnl_bind, + .unbind = g_dnl_unbind, +}; + +int g_dnl_register(const char *type) +{ + /* We only allow "dfu" atm, so 3 should be enough */ + static char name[sizeof(shortname) + 3]; + int ret; + + if (!strcmp(type, "dfu")) { + strcpy(name, shortname); + strcat(name, type); + } else { + printf("%s: unknown command: %s\n", __func__, type); + return -EINVAL; + } + + g_dnl_driver.name = name; + + debug("%s: g_dnl_driver.name: %s\n", __func__, g_dnl_driver.name); + ret = usb_composite_register(&g_dnl_driver); + + if (ret) { + printf("%s: failed!, error: %d\n", __func__, ret); + return ret; + } + + return 0; +} + +void g_dnl_unregister(void) +{ + usb_composite_unregister(&g_dnl_driver); +} diff --git a/include/g_dnl.h b/include/g_dnl.h new file mode 100644 index 0000000..0ec7440 --- /dev/null +++ b/include/g_dnl.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski l.majewski@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 __G_DOWNLOAD_H_ +#define __G_DOWNLOAD_H_ + +#include <linux/usb/ch9.h> +#include <usbdescriptors.h> +#include <linux/usb/gadget.h> + +int g_dnl_register(const char *s); +void g_dnl_unregister(void); + +/* USB initialization declaration - board specific */ +void board_usb_init(void); +#endif /* __G_DOWNLOAD_H_ */

Support for f_dfu USB function.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2:
- Replace kzalloc and kfree with free and calloc - Reorganization of calloc calls - Misspelling corrected - Redesign of DFU state machine from "switch case" to function pointers - Split the dfu_handle method to separate functions for each DFU state
Changes for v3: - Missing parenthesis added to sizeof call
Changes for v4: - Replace two puts with one - Remove the 0xff at handle_getstate function - Add const qualifier to struct definitions when possible --- drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/f_dfu.c | 749 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/f_dfu.h | 100 ++++++ 3 files changed, 850 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/gadget/f_dfu.c create mode 100644 drivers/usb/gadget/f_dfu.h
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 2c067c8..5bbdd36 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -30,6 +30,7 @@ ifdef CONFIG_USB_GADGET COBJS-y += epautoconf.o config.o usbstring.o COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o +COBJS-$(CONFIG_DFU_FUNCTION) += f_dfu.o endif ifdef CONFIG_USB_ETHER COBJS-y += ether.o epautoconf.o config.o usbstring.o diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c new file mode 100644 index 0000000..3ec4c65 --- /dev/null +++ b/drivers/usb/gadget/f_dfu.c @@ -0,0 +1,749 @@ +/* + * f_dfu.c -- Device Firmware Update USB function + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz andrzej.p@samsung.com + * Lukasz Majewski l.majewski@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 <errno.h> +#include <common.h> +#include <malloc.h> + +#include <linux/usb/ch9.h> +#include <usbdescriptors.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> + +#include <dfu.h> +#include "f_dfu.h" + +struct f_dfu { + struct usb_function usb_function; + + struct usb_descriptor_header **function; + struct usb_string *strings; + + /* when configured, we have one config */ + u8 config; + u8 altsetting; + enum dfu_state dfu_state; + unsigned int dfu_status; + + /* Send/received block number is handy for data integrity check */ + int blk_seq_num; +}; + +typedef int (*dfu_state_fn) (struct f_dfu *, + const struct usb_ctrlrequest *, + struct usb_gadget *, + struct usb_request *); + +static inline struct f_dfu *func_to_dfu(struct usb_function *f) +{ + return container_of(f, struct f_dfu, usb_function); +} + +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 = DFU_USB_BUFSIZ, + .bcdDFUVersion = __constant_cpu_to_le16(0x0110), +}; + +static 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 = DYNAMIC */ +}; + +static struct usb_descriptor_header *dfu_runtime_descs[] = { + (struct usb_descriptor_header *) &dfu_intf_runtime, + NULL, +}; + +static const 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 const char dfu_name[] = "Device Firmware Upgrade"; + +/* + * static strings, in UTF-8 + * + * dfu_generic configuration + */ +static struct usb_string strings_dfu_generic[] = { + [0].s = dfu_name, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dfu_generic = { + .language = 0x0409, /* en-us */ + .strings = strings_dfu_generic, +}; + +static struct usb_gadget_strings *dfu_generic_strings[] = { + &stringtab_dfu_generic, + NULL, +}; + +/* + * usb_function specific + */ +static struct usb_gadget_strings stringtab_dfu = { + .language = 0x0409, /* en-us */ + /* + * .strings + * + * assigned during initialization, + * depends on number of flash entities + * + */ +}; + +static struct usb_gadget_strings *dfu_strings[] = { + &stringtab_dfu, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_dfu *f_dfu = req->context; + + dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf, + req->length, f_dfu->blk_seq_num); + + if (req->length == 0) + puts("DOWNLOAD ... OK\nCtrl+C to exit ...\n"); +} + +static void handle_getstatus(struct usb_request *req) +{ + struct dfu_status *dstat = (struct dfu_status *)req->buf; + struct f_dfu *f_dfu = req->context; + + switch (f_dfu->dfu_state) { + case DFU_STATE_dfuDNLOAD_SYNC: + case DFU_STATE_dfuDNBUSY: + f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE; + break; + case DFU_STATE_dfuMANIFEST_SYNC: + break; + default: + break; + } + + /* send status response */ + dstat->bStatus = f_dfu->dfu_status; + dstat->bState = f_dfu->dfu_state; + dstat->iString = 0; +} + +static void handle_getstate(struct usb_request *req) +{ + struct f_dfu *f_dfu = req->context; + + ((u8 *)req->buf)[0] = f_dfu->dfu_state; + req->actual = sizeof(u8); +} + +static inline void to_dfu_mode(struct f_dfu *f_dfu) +{ + f_dfu->usb_function.strings = dfu_strings; + f_dfu->usb_function.hs_descriptors = f_dfu->function; +} + +static inline void to_runtime_mode(struct f_dfu *f_dfu) +{ + f_dfu->usb_function.strings = NULL; + f_dfu->usb_function.hs_descriptors = dfu_runtime_descs; +} + +static int handle_upload(struct usb_request *req, u16 len) +{ + struct f_dfu *f_dfu = req->context; + + return dfu_read(dfu_get_entity(f_dfu->altsetting), req->buf, + req->length, f_dfu->blk_seq_num); +} + +static int handle_dnload(struct usb_gadget *gadget, u16 len) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_request *req = cdev->req; + struct f_dfu *f_dfu = req->context; + + if (len == 0) + f_dfu->dfu_state = DFU_STATE_dfuMANIFEST_SYNC; + + req->complete = dnload_request_complete; + + return len; +} + +/*-------------------------------------------------------------------------*/ +/* DFU state machine */ +static int state_app_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + case USB_REQ_DFU_DETACH: + f_dfu->dfu_state = DFU_STATE_appDETACH; + to_dfu_mode(f_dfu); + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + value = RET_ZLP; + break; + default: + value = RET_STALL; + break; + } + + return value; +} + +static int state_app_detach(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_appIDLE; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 len = le16_to_cpu(ctrl->wLength); + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_DNLOAD: + if (len == 0) { + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; + f_dfu->blk_seq_num = w_value; + value = handle_dnload(gadget, len); + break; + case USB_REQ_DFU_UPLOAD: + f_dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE; + f_dfu->blk_seq_num = 0; + value = handle_upload(req, len); + break; + case USB_REQ_DFU_ABORT: + /* no zlp? */ + value = RET_ZLP; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + 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 :( + */ + f_dfu->dfu_state = + DFU_STATE_dfuMANIFEST_WAIT_RST; + to_runtime_mode(f_dfu); + f_dfu->dfu_state = DFU_STATE_appIDLE; + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_dnload_sync(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_dnbusy(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_dnload_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 len = le16_to_cpu(ctrl->wLength); + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_DNLOAD: + f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; + f_dfu->blk_seq_num = w_value; + value = handle_dnload(gadget, len); + break; + case USB_REQ_DFU_ABORT: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + value = RET_ZLP; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_manifest_sync(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + /* We're MainfestationTolerant */ + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + handle_getstatus(req); + f_dfu->blk_seq_num = 0; + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_upload_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 len = le16_to_cpu(ctrl->wLength); + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_UPLOAD: + /* state transition if less data then requested */ + f_dfu->blk_seq_num = w_value; + value = handle_upload(req, len); + if (value >= 0 && value < len) + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + break; + case USB_REQ_DFU_ABORT: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + /* no zlp? */ + value = RET_ZLP; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_error(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + case USB_REQ_DFU_CLRSTATUS: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + f_dfu->dfu_status = DFU_STATUS_OK; + /* no zlp? */ + value = RET_ZLP; + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static dfu_state_fn dfu_state[] = { + state_app_idle, /* DFU_STATE_appIDLE */ + state_app_detach, /* DFU_STATE_appDETACH */ + state_dfu_idle, /* DFU_STATE_dfuIDLE */ + state_dfu_dnload_sync, /* DFU_STATE_dfuDNLOAD_SYNC */ + state_dfu_dnbusy, /* DFU_STATE_dfuDNBUSY */ + state_dfu_dnload_idle, /* DFU_STATE_dfuDNLOAD_IDLE */ + state_dfu_manifest_sync, /* DFU_STATE_dfuMANIFEST_SYNC */ + NULL, /* DFU_STATE_dfuMANIFEST */ + NULL, /* DFU_STATE_dfuMANIFEST_WAIT_RST */ + state_dfu_upload_idle, /* DFU_STATE_dfuUPLOAD_IDLE */ + state_dfu_error /* DFU_STATE_dfuERROR */ +}; + +static int +dfu_handle(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct usb_gadget *gadget = f->config->cdev->gadget; + struct usb_request *req = f->config->cdev->req; + struct f_dfu *f_dfu = f->config->cdev->req->context; + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + int value = 0; + u8 req_type = ctrl->bRequestType & USB_TYPE_MASK; + + debug("w_value: 0x%x len: 0x%x\n", w_value, len); + debug("req_type: 0x%x ctrl->bRequest: 0x%x f_dfu->dfu_state: 0x%x\n", + req_type, ctrl->bRequest, f_dfu->dfu_state); + + if (req_type == USB_TYPE_STANDARD) { + if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR && + (w_value >> 8) == DFU_DT_FUNC) { + value = min(len, (u16) sizeof(dfu_func)); + memcpy(req->buf, &dfu_func, value); + } + } else /* DFU specific request */ + value = dfu_state[f_dfu->dfu_state] (f_dfu, ctrl, gadget, req); + + if (value >= 0) { + req->length = value; + req->zero = value < len; + value = usb_ep_queue(gadget->ep0, req, 0); + if (value < 0) { + debug("ep_queue --> %d\n", value); + req->status = 0; + } + } + + return value; +} + +/*-------------------------------------------------------------------------*/ + +static int +dfu_prepare_strings(struct f_dfu *f_dfu, int n) +{ + struct dfu_entity *de = NULL; + int i = 0; + + f_dfu->strings = calloc(sizeof(struct usb_string), n + 1); + if (!f_dfu->strings) + goto enomem; + + for (i = 0; i < n; ++i) { + de = dfu_get_entity(i); + f_dfu->strings[i].s = de->name; + } + + f_dfu->strings[i].id = 0; + f_dfu->strings[i].s = NULL; + + return 0; + +enomem: + while (i) + f_dfu->strings[--i].s = NULL; + + free(f_dfu->strings); + + return -ENOMEM; +} + +static int dfu_prepare_function(struct f_dfu *f_dfu, int n) +{ + struct usb_interface_descriptor *d; + int i = 0; + + f_dfu->function = calloc(sizeof(struct usb_descriptor_header *), n); + if (!f_dfu->function) + goto enomem; + + for (i = 0; i < n; ++i) { + d = calloc(sizeof(*d), 1); + 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; + + f_dfu->function[i] = (struct usb_descriptor_header *)d; + } + f_dfu->function[i] = NULL; + + return 0; + +enomem: + while (i) { + free(f_dfu->function[--i]); + f_dfu->function[i] = NULL; + } + free(f_dfu->function); + + return -ENOMEM; +} + +static int dfu_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_dfu *f_dfu = func_to_dfu(f); + int alt_num = dfu_get_alt_number(); + int rv, id, i; + + id = usb_interface_id(c, f); + if (id < 0) + return id; + dfu_intf_runtime.bInterfaceNumber = id; + + f_dfu->dfu_state = DFU_STATE_appIDLE; + f_dfu->dfu_status = DFU_STATUS_OK; + + rv = dfu_prepare_function(f_dfu, alt_num); + if (rv) + goto error; + + rv = dfu_prepare_strings(f_dfu, alt_num); + if (rv) + goto error; + for (i = 0; i < alt_num; i++) { + id = usb_string_id(cdev); + if (id < 0) + return id; + f_dfu->strings[i].id = id; + ((struct usb_interface_descriptor *)f_dfu->function[i]) + ->iInterface = id; + } + + stringtab_dfu.strings = f_dfu->strings; + + cdev->req->context = f_dfu; + +error: + return rv; +} + +static void dfu_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + int alt_num = dfu_get_alt_number(); + int i; + + if (f_dfu->strings) { + i = alt_num; + while (i) + f_dfu->strings[--i].s = NULL; + + free(f_dfu->strings); + } + + if (f_dfu->function) { + i = alt_num; + while (i) { + free(f_dfu->function[--i]); + f_dfu->function[i] = NULL; + } + free(f_dfu->function); + } + + free(f_dfu); +} + +static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + + debug("%s: intf:%d alt:%d\n", __func__, intf, alt); + + f_dfu->altsetting = alt; + + return 0; +} + +/* TODO: is this really what we need here? */ +static void dfu_disable(struct usb_function *f) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + if (f_dfu->config == 0) + return; + + debug("%s: reset config\n", __func__); + + f_dfu->config = 0; +} + +static int dfu_bind_config(struct usb_configuration *c) +{ + struct f_dfu *f_dfu; + int status; + + f_dfu = calloc(sizeof(*f_dfu), 1); + if (!f_dfu) + return -ENOMEM; + f_dfu->usb_function.name = "dfu"; + f_dfu->usb_function.hs_descriptors = dfu_runtime_descs; + f_dfu->usb_function.bind = dfu_bind; + f_dfu->usb_function.unbind = dfu_unbind; + f_dfu->usb_function.set_alt = dfu_set_alt; + f_dfu->usb_function.disable = dfu_disable; + f_dfu->usb_function.strings = dfu_generic_strings, + f_dfu->usb_function.setup = dfu_handle, + + status = usb_add_function(c, &f_dfu->usb_function); + if (status) + free(f_dfu); + + return status; +} + +int dfu_add(struct usb_configuration *c) +{ + int id; + + id = usb_string_id(c->cdev); + if (id < 0) + return id; + strings_dfu_generic[0].id = id; + dfu_intf_runtime.iInterface = id; + + debug("%s: cdev: 0x%p gadget:0x%p gadget->ep0: 0x%p\n", __func__, + c->cdev, c->cdev->gadget, c->cdev->gadget->ep0); + + return dfu_bind_config(c); +} diff --git a/drivers/usb/gadget/f_dfu.h b/drivers/usb/gadget/f_dfu.h new file mode 100644 index 0000000..023e1ad --- /dev/null +++ b/drivers/usb/gadget/f_dfu.h @@ -0,0 +1,100 @@ +/* + * f_dfu.h -- Device Firmware Update gadget + * + * Copyright (C) 2011-2012 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 __F_DFU_H_ +#define __F_DFU_H_ + +#include <linux/compiler.h> +#include <linux/usb/composite.h> + +#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 DFU_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 + +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; + +/* configuration-specific linkup */ +int dfu_add(struct usb_configuration *c); +#endif /* __F_DFU_H_ */

New, separate driver at ./drivers/dfu has been added. It allows platform and storage independent operation of DFU. It has been extended to use new MMC level of command abstraction.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2: - None
Changes for v3: - Remove unnecessary NULL and 0 initialization of dynamic variables - Combine two puts to one - Adding const qualifier to device and layout definitions - Remove unnecessary casting at dfu->name passing - Provide more meaningful names for dfu layouts and device types - Removal of dfu_extract_{token|entity} functions and replace them with strsep calls
Changes for v4: - Remove unnecessary memset - const added where applicable - Handling calloc failure in dfu_config_entries() --- Makefile | 1 + drivers/dfu/Makefile | 43 +++++++++ drivers/dfu/dfu.c | 239 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/dfu.h | 103 ++++++++++++++++++++++ 4 files changed, 386 insertions(+), 0 deletions(-) create mode 100644 drivers/dfu/Makefile create mode 100644 drivers/dfu/dfu.c create mode 100644 include/dfu.h
diff --git a/Makefile b/Makefile index eb37ea1..67c627d 100644 --- a/Makefile +++ b/Makefile @@ -271,6 +271,7 @@ LIBS += drivers/pci/libpci.o LIBS += drivers/pcmcia/libpcmcia.o LIBS += drivers/power/libpower.o LIBS += drivers/spi/libspi.o +LIBS += drivers/dfu/libdfu.o ifeq ($(CPU),mpc83xx) LIBS += drivers/qe/libqe.o LIBS += arch/powerpc/cpu/mpc8xxx/ddr/libddr.o diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile new file mode 100644 index 0000000..7736485 --- /dev/null +++ b/drivers/dfu/Makefile @@ -0,0 +1,43 @@ +# +# Copyright (C) 2012 Samsung Electronics +# Lukasz Majewski l.majewski@samsung.com +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB = $(obj)libdfu.o + +COBJS-$(CONFIG_DFU_FUNCTION) += dfu.o + +SRCS := $(COBJS-y:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS-y)) + +$(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/dfu/dfu.c b/drivers/dfu/dfu.c new file mode 100644 index 0000000..36d7bee --- /dev/null +++ b/drivers/dfu/dfu.c @@ -0,0 +1,239 @@ +/* + * dfu.c -- DFU back-end routines + * + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski l.majewski@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 <fat.h> +#include <dfu.h> +#include <linux/list.h> +#include <linux/compiler.h> + +static LIST_HEAD(dfu_list); +static int dfu_alt_num; + +static int dfu_find_alt_num(const char *s) +{ + int i = 0; + + for (; *s; s++) + if (*s == ';') + i++; + + return ++i; +} + +static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE) + dfu_buf[DFU_DATA_BUF_SIZE]; + +int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) +{ + static unsigned char *i_buf; + static int i_blk_seq_num; + long w_size = 0; + int ret = 0; + + debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n", + __func__, dfu->name, buf, size, blk_seq_num, i_buf); + + if (blk_seq_num == 0) { + i_buf = dfu_buf; + i_blk_seq_num = 0; + } + + if (i_blk_seq_num++ != blk_seq_num) { + printf("%s: Wrong sequence number! [%d] [%d]\n", + __func__, i_blk_seq_num, blk_seq_num); + return -1; + } + + memcpy(i_buf, buf, size); + i_buf += size; + + if (size == 0) { + /* Integrity check (if needed) */ + debug("%s: %s %d [B] CRC32: 0x%x\n", __func__, dfu->name, + i_buf - dfu_buf, crc32(0, dfu_buf, i_buf - dfu_buf)); + + w_size = i_buf - dfu_buf; + ret = dfu->write_medium(dfu, dfu_buf, &w_size); + if (ret) + debug("%s: Write error!\n", __func__); + + i_blk_seq_num = 0; + i_buf = NULL; + return ret; + } + + return ret; +} + +int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) +{ + static unsigned char *i_buf; + static int i_blk_seq_num; + static long r_size; + static u32 crc; + int ret = 0; + + debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n", + __func__, dfu->name, buf, size, blk_seq_num, i_buf); + + if (blk_seq_num == 0) { + i_buf = dfu_buf; + ret = dfu->read_medium(dfu, i_buf, &r_size); + debug("%s: %s %ld [B]\n", __func__, dfu->name, r_size); + i_blk_seq_num = 0; + /* Integrity check (if needed) */ + crc = crc32(0, dfu_buf, r_size); + } + + if (i_blk_seq_num++ != blk_seq_num) { + printf("%s: Wrong sequence number! [%d] [%d]\n", + __func__, i_blk_seq_num, blk_seq_num); + return -1; + } + + if (r_size >= size) { + memcpy(buf, i_buf, size); + i_buf += size; + r_size -= size; + return size; + } else { + memcpy(buf, i_buf, r_size); + i_buf += r_size; + debug("%s: %s CRC32: 0x%x\n", __func__, dfu->name, crc); + puts("UPLOAD ... done\nCtrl+C to exit ...\n"); + + i_buf = NULL; + i_blk_seq_num = 0; + crc = 0; + return r_size; + } + return ret; +} + +static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt, + char *interface, int num) +{ + char *st; + + debug("%s: %s interface: %s num: %d\n", __func__, s, interface, num); + st = strsep(&s, " "); + strcpy(dfu->name, st); + + dfu->dev_num = num; + dfu->alt = alt; + + /* Specific for mmc device */ + if (strcmp(interface, "mmc") == 0) { + if (dfu_fill_entity_mmc(dfu, s)) + return -1; + } else { + printf("%s: Device %s not (yet) supported!\n", + __func__, interface); + return -1; + } + + return 0; +} + +void dfu_free_entities(void) +{ + struct dfu_entity *dfu, *p; + + list_for_each_entry_safe_reverse(dfu, p, &dfu_list, list) { + list_del(&dfu->list); + free(dfu); + } + + INIT_LIST_HEAD(&dfu_list); +} + +int dfu_config_entities(char *env, char *interface, int num) +{ + struct dfu_entity *dfu; + int i, ret; + char *s; + + dfu_alt_num = dfu_find_alt_num(env); + debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num); + + for (i = 0; i < dfu_alt_num; i++) { + dfu = calloc(sizeof(struct dfu_entity), 1); + if (!dfu) { + dfu_free_entities(); + return -1; + } + + s = strsep(&env, ";"); + ret = dfu_fill_entity(dfu, s, i, interface, num); + if (ret) + return -1; + + list_add_tail(&dfu->list, &dfu_list); + } + + return 0; +} + +const char *dfu_get_dev_type(enum dfu_device_type t) +{ + const char *dev_t[] = {NULL, "eMMC", "OneNAND", "NAND" }; + return dev_t[t]; +} + +const char *dfu_get_layout(enum dfu_layout l) +{ + const char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2", + "EXT3", "EXT4" }; + return dfu_layout[l]; +} + +void dfu_show_entities(void) +{ + struct dfu_entity *dfu; + + puts("DFU alt settings list:\n"); + + list_for_each_entry(dfu, &dfu_list, list) { + printf("dev: %s alt: %d name: %s layout: %s\n", + dfu_get_dev_type(dfu->dev_type), dfu->alt, + dfu->name, dfu_get_layout(dfu->layout)); + } +} + +int dfu_get_alt_number(void) +{ + return dfu_alt_num; +} + +struct dfu_entity *dfu_get_entity(int alt) +{ + struct dfu_entity *dfu; + + list_for_each_entry(dfu, &dfu_list, list) { + if (dfu->alt == alt) + return dfu; + } + + return NULL; +} diff --git a/include/dfu.h b/include/dfu.h new file mode 100644 index 0000000..5350d79 --- /dev/null +++ b/include/dfu.h @@ -0,0 +1,103 @@ +/* + * dfu.h - DFU flashable area description + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz andrzej.p@samsung.com + * Lukasz Majewski l.majewski@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_ENTITY_H_ +#define __DFU_ENTITY_H_ + +#include <common.h> +#include <linux/list.h> +#include <mmc.h> + +enum dfu_device_type { + DFU_DEV_MMC = 1, + DFU_DEV_ONENAND, + DFU_DEV_NAND, +}; + +enum dfu_layout { + DFU_RAW_ADDR = 1, + DFU_FS_FAT, + DFU_FS_EXT2, + DFU_FS_EXT3, + DFU_FS_EXT4, +}; + +struct mmc_internal_data { + /* RAW programming */ + unsigned int lba_start; + unsigned int lba_size; + unsigned int lba_blk_size; + + /* FAT/EXT */ + unsigned int dev; + unsigned int part; +}; + +static inline unsigned int get_mmc_blk_size(int dev) +{ + return find_mmc_device(dev)->read_bl_len; +} + +#define DFU_NAME_SIZE 32 +#define DFU_CMD_BUF_SIZE 128 +#define DFU_DATA_BUF_SIZE (1024*1024*4) /* 4 MiB */ + +struct dfu_entity { + char name[DFU_NAME_SIZE]; + int alt; + void *dev_private; + int dev_num; + enum dfu_device_type dev_type; + enum dfu_layout layout; + + union { + struct mmc_internal_data mmc; + } data; + + int (*read_medium)(struct dfu_entity *dfu, void *buf, long *len); + int (*write_medium)(struct dfu_entity *dfu, void *buf, long *len); + + struct list_head list; +}; + +int dfu_config_entities(char *s, char *interface, int num); +void dfu_free_entities(void); +void dfu_show_entities(void); +int dfu_get_alt_number(void); +const char *dfu_get_dev_type(enum dfu_device_type t); +const char *dfu_get_layout(enum dfu_layout l); +struct dfu_entity *dfu_get_entity(int alt); +char *dfu_extract_token(char** e, int *n); + +int dfu_read(struct dfu_entity *de, void *buf, int size, int blk_seq_num); +int dfu_write(struct dfu_entity *de, void *buf, int size, int blk_seq_num); +/* Device specific */ +#ifdef CONFIG_DFU_MMC +extern int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s); +#else +static inline int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) +{ + puts("MMC support not available!\n"); + return -1; +} +#endif +#endif /* __DFU_ENTITY_H_ */

Support for MMC storage devices to work with DFU framework.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2: - None
Changes for v3: - Provide special abstraction layer (mmc_{block|file}_{read|write}) to alleviate switch to new device model (DM) - More verbose messages when not supported layout encountered - Calls to strncmp() replaced with strcmp()
Changes for v4: - Remove superfluous memset() calls - Replace ALLOC_CACHE_ALIGN_BUFFER with plain table definition - Cosmetic changes with pointers definition --- drivers/dfu/Makefile | 1 + drivers/dfu/dfu_mmc.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 0 deletions(-) create mode 100644 drivers/dfu/dfu_mmc.c
diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index 7736485..7b717bc 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -26,6 +26,7 @@ include $(TOPDIR)/config.mk LIB = $(obj)libdfu.o
COBJS-$(CONFIG_DFU_FUNCTION) += dfu.o +COBJS-$(CONFIG_DFU_MMC) += dfu_mmc.o
SRCS := $(COBJS-y:.o=.c) OBJS := $(addprefix $(obj),$(COBJS-y)) diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c new file mode 100644 index 0000000..060145b --- /dev/null +++ b/drivers/dfu/dfu_mmc.c @@ -0,0 +1,162 @@ +/* + * dfu.c -- DFU back-end routines + * + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski l.majewski@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 <dfu.h> + +enum dfu_mmc_op { + DFU_OP_READ = 1, + DFU_OP_WRITE, +}; + +static int mmc_block_op(enum dfu_mmc_op op, struct dfu_entity *dfu, + void *buf, long *len) +{ + char cmd_buf[DFU_CMD_BUF_SIZE]; + + sprintf(cmd_buf, "mmc %s 0x%x %x %x", + op == DFU_OP_READ ? "read" : "write", + (unsigned int) buf, + dfu->data.mmc.lba_start, + dfu->data.mmc.lba_size); + + if (op == DFU_OP_READ) + *len = dfu->data.mmc.lba_blk_size * dfu->data.mmc.lba_size; + + debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); + return run_command(cmd_buf, 0); +} + +static inline int mmc_block_write(struct dfu_entity *dfu, void *buf, long *len) +{ + return mmc_block_op(DFU_OP_WRITE, dfu, buf, len); +} + +static inline int mmc_block_read(struct dfu_entity *dfu, void *buf, long *len) +{ + return mmc_block_op(DFU_OP_READ, dfu, buf, len); +} + +static int mmc_file_op(enum dfu_mmc_op op, struct dfu_entity *dfu, + void *buf, long *len) +{ + char cmd_buf[DFU_CMD_BUF_SIZE]; + char *str_env; + int ret; + + sprintf(cmd_buf, "fat%s mmc %d:%d 0x%x %s %lx", + op == DFU_OP_READ ? "load" : "write", + dfu->data.mmc.dev, dfu->data.mmc.part, + (unsigned int) buf, dfu->name, *len); + + debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); + + ret = run_command(cmd_buf, 0); + if (ret) { + puts("dfu: Read error!\n"); + return ret; + } + + if (dfu->layout != DFU_RAW_ADDR) { + str_env = getenv("filesize"); + if (str_env == NULL) { + puts("dfu: Wrong file size!\n"); + return -1; + } + *len = simple_strtoul(str_env, NULL, 16); + } + + return ret; +} + +static inline int mmc_file_write(struct dfu_entity *dfu, void *buf, long *len) +{ + return mmc_file_op(DFU_OP_WRITE, dfu, buf, len); +} + +static inline int mmc_file_read(struct dfu_entity *dfu, void *buf, long *len) +{ + return mmc_file_op(DFU_OP_READ, dfu, buf, len); +} + +int dfu_write_medium_mmc(struct dfu_entity *dfu, void *buf, long *len) +{ + int ret = -1; + + switch (dfu->layout) { + case DFU_RAW_ADDR: + ret = mmc_block_write(dfu, buf, len); + break; + case DFU_FS_FAT: + ret = mmc_file_write(dfu, buf, len); + break; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + } + + return ret; +} + +int dfu_read_medium_mmc(struct dfu_entity *dfu, void *buf, long *len) +{ + int ret = -1; + + switch (dfu->layout) { + case DFU_RAW_ADDR: + ret = mmc_block_read(dfu, buf, len); + break; + case DFU_FS_FAT: + ret = mmc_file_read(dfu, buf, len); + break; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + } + + return ret; +} + +int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) +{ + char *st; + + dfu->dev_type = DFU_DEV_MMC; + st = strsep(&s, " "); + if (!strcmp(st, "mmc")) { + dfu->layout = DFU_RAW_ADDR; + dfu->data.mmc.lba_start = simple_strtoul(s, &s, 16); + dfu->data.mmc.lba_size = simple_strtoul(++s, &s, 16); + dfu->data.mmc.lba_blk_size = get_mmc_blk_size(dfu->dev_num); + } else if (!strcmp(st, "fat")) { + dfu->layout = DFU_FS_FAT; + dfu->data.mmc.dev = simple_strtoul(s, &s, 10); + dfu->data.mmc.part = simple_strtoul(++s, &s, 10); + } else { + printf("%s: Memory layout (%s) not supported!\n", __func__, st); + } + + dfu->read_medium = dfu_read_medium_mmc; + dfu->write_medium = dfu_write_medium_mmc; + + return 0; +}

Support for u-boot's command line command "dfu <interface> <dev> [list]".
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2: - None
Changes for v3: - Remove unnecessary initialization to NULL of dynamic variables - goto done added to reduce code duplication - static definition of do_dfu()
Changes for v4: - Define "dfu" string as an array --- common/Makefile | 1 + common/cmd_dfu.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 0 deletions(-) create mode 100644 common/cmd_dfu.c
diff --git a/common/Makefile b/common/Makefile index 483eb4d..32d44e5 100644 --- a/common/Makefile +++ b/common/Makefile @@ -183,6 +183,7 @@ COBJS-$(CONFIG_MENU) += menu.o COBJS-$(CONFIG_MODEM_SUPPORT) += modem.o COBJS-$(CONFIG_UPDATE_TFTP) += update.o COBJS-$(CONFIG_USB_KEYBOARD) += usb_kbd.o +COBJS-$(CONFIG_CMD_DFU) += cmd_dfu.o endif
ifdef CONFIG_SPL_BUILD diff --git a/common/cmd_dfu.c b/common/cmd_dfu.c new file mode 100644 index 0000000..62fb890 --- /dev/null +++ b/common/cmd_dfu.c @@ -0,0 +1,81 @@ +/* + * cmd_dfu.c -- dfu command + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz andrzej.p@samsung.com + * Lukasz Majewski l.majewski@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 <malloc.h> +#include <dfu.h> +#include <asm/errno.h> +#include <g_dnl.h> + +static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + const char *str_env; + char s[] = "dfu"; + char *env_bkp; + int ret; + + if (argc < 3) + return CMD_RET_USAGE; + + str_env = getenv("dfu_alt_info"); + if (str_env == NULL) { + printf("%s: "dfu_alt_info" env variable not defined!\n", + __func__); + return CMD_RET_FAILURE; + } + + env_bkp = strdup(str_env); + ret = dfu_config_entities(env_bkp, argv[1], + (int)simple_strtoul(argv[2], NULL, 10)); + if (ret) + return CMD_RET_FAILURE; + + if (strcmp(argv[3], "list") == 0) { + dfu_show_entities(); + goto done; + } + + board_usb_init(); + g_dnl_register(s); + while (1) { + if (ctrlc()) + goto exit; + + usb_gadget_handle_interrupts(); + } +exit: + g_dnl_unregister(); +done: + dfu_free_entities(); + free(env_bkp); + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu, + "Device Firmware Upgrade", + "<interface> <dev> [list]\n" + " - device firmware upgrade on a device <dev>\n" + " attached to interface <interface>\n" + " [list] - list available alt settings" +);

Support for USB UDC driver at trats board.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de Cc: Minkyu Kang mk7.kang@samsung.com
--- Changes for v2: - replace puts to debug --- board/samsung/trats/trats.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/board/samsung/trats/trats.c b/board/samsung/trats/trats.c index a8b2b11..4f9cb5a 100644 --- a/board/samsung/trats/trats.c +++ b/board/samsung/trats/trats.c @@ -59,6 +59,8 @@ static int hwrevision(int rev) return (board_rev & 0xf) == rev; }
+struct s3c_plat_otg_data s5pc210_otg_data; + int board_init(void) { gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100; @@ -259,6 +261,12 @@ struct s3c_plat_otg_data s5pc210_otg_data = { .usb_phy_ctrl = EXYNOS4_USBPHY_CONTROL, .usb_flags = PHY0_SLEEP, }; + +void board_usb_init(void) +{ + debug("USB_udc_probe\n"); + s3c_udc_probe(&s5pc210_otg_data); +} #endif
static void pmic_reset(void)

Enable the g_dnl composite USB gadget driver with embedded DFU function on it. It now uses the composite gadget framework to support download specific USB functions (like enabled DFU or USB Mass Storage).
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de Cc: Minkyu Kang mk7.kang@samsung.com
--- Change for v2: - Move the G_DNL_{VENDOR_NUM, PRODUCT_NUM and MANUFACTURER} definitions to ./include/configs/<board>.h
Changes for v3: - None --- include/configs/trats.h | 24 +++++++++++++++++++++++- 1 files changed, 23 insertions(+), 1 deletions(-)
diff --git a/include/configs/trats.h b/include/configs/trats.h index eb269b2..75a23b0 100644 --- a/include/configs/trats.h +++ b/include/configs/trats.h @@ -94,6 +94,21 @@ #undef CONFIG_CMD_ONENAND #undef CONFIG_CMD_MTDPARTS #define CONFIG_CMD_MMC +#define CONFIG_CMD_DFU + +/* FAT */ +#define CONFIG_CMD_FAT +#define CONFIG_FAT_WRITE + +/* USB Composite download gadget - g_dnl */ +#define CONFIG_USBDOWNLOAD_GADGET +#define CONFIG_DFU_FUNCTION +#define CONFIG_DFU_MMC + +/* USB Samsung's IDs */ +#define CONFIG_G_DNL_VENDOR_NUM 0x04E8 +#define CONFIG_G_DNL_PRODUCT_NUM 0x6601 +#define CONFIG_G_DNL_MANUFACTURER "Samsung"
#define CONFIG_BOOTDELAY 1 #define CONFIG_ZERO_BOOTDELAY_CHECK @@ -104,6 +119,11 @@ #define CONFIG_BOOTBLOCK "10" #define CONFIG_ENV_COMMON_BOOT "${console} ${meminfo}"
+#define CONFIG_DFU_ALT \ + "dfu_alt_info=" \ + "u-boot mmc 80 400;" \ + "uImage fat 0 2\0" \ + #define CONFIG_ENV_OVERWRITE #define CONFIG_SYS_CONSOLE_INFO_QUIET #define CONFIG_SYS_CONSOLE_IS_IN_ENV @@ -146,7 +166,8 @@ "mmcdev=0\0" \ "mmcbootpart=2\0" \ "mmcrootpart=3\0" \ - "opts=always_resume=1" + "opts=always_resume=1\0" \ + CONFIG_DFU_ALT
/* Miscellaneous configurable options */ #define CONFIG_SYS_LONGHELP /* undef to save memory */ @@ -209,6 +230,7 @@ #define CONFIG_USB_GADGET #define CONFIG_USB_GADGET_S3C_UDC_OTG #define CONFIG_USB_GADGET_DUALSPEED +#define CONFIG_USB_GADGET_VBUS_DRAW 2
/* LCD */ #define CONFIG_EXYNOS_FB

Those patches add support for composite USB download gadget. This gadget (at least for now) is equipped with DFU download function.
A separate DFU back-end and front-end have been added. Back-end is placed at ./drivers/dfu directory. The front-end is implemented as USB function.
The back-end is written in a generic manner with storage device specific code separated (eMMC).
DFU specification can be found at: http://wiki.openmoko.org/wiki/USB_DFU_-_The_USB_Device_Firmware_Upgrade_stan...
Example usage:
u-boot side: dfu mmc 0 PC: dfu-util -U IMAGE.bin -a uImage (for upload) dfu-util -D uImage -a uImage (download)
To list the alt settings: dfu mmc 0 list
Test HW: Exynos4210 Trats board
Lukasz Majewski (7): dfu:usb: Support for g_dnl composite download gadget. dfu:usb: DFU USB function (f_dfu) support for g_dnl composite gadget dfu: DFU backend implementation dfu: MMC specific routines for DFU operation dfu:cmd: Support for DFU u-boot command arm:trats: Support for USB UDC driver at TRATS board. arm:trats: Enable g_dnl composite USB gadget with embedded DFU function on TRATS
Makefile | 1 + board/samsung/trats/trats.c | 8 + common/Makefile | 1 + common/cmd_dfu.c | 81 +++++ drivers/dfu/Makefile | 44 +++ drivers/dfu/dfu.c | 238 ++++++++++++++ drivers/dfu/dfu_mmc.c | 162 ++++++++++ drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_dfu.c | 749 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/f_dfu.h | 100 ++++++ drivers/usb/gadget/g_dnl.c | 202 ++++++++++++ include/configs/trats.h | 24 ++- include/dfu.h | 103 ++++++ include/g_dnl.h | 33 ++ 14 files changed, 1747 insertions(+), 1 deletions(-) create mode 100644 common/cmd_dfu.c create mode 100644 drivers/dfu/Makefile create mode 100644 drivers/dfu/dfu.c create mode 100644 drivers/dfu/dfu_mmc.c create mode 100644 drivers/usb/gadget/f_dfu.c create mode 100644 drivers/usb/gadget/f_dfu.h create mode 100644 drivers/usb/gadget/g_dnl.c create mode 100644 include/dfu.h create mode 100644 include/g_dnl.h

Composite USB download gadget support (g_dnl) for download functions. This code works on top of composite gadget.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2:
- G_DNL_{VENDOR_NUM, PRODUCT_NUM and MANUFACTURER} defined at ./include/configs/<board>.h - Suspend and resume stub methods removed - '\0' repleaced with plain 0
Changes for v3: - Remove unused #includes - Replace strncpy and strncat with strcpy and strcat. It was possible due to new approach to g_dnl name generation (at g_dnl_register function) - Rename the g_dnl_{init|cleanup} to g_dnl_{register|unregister} - Replace the G_DNL_* CONFIG_G_DNL_*
Changes for v4: - Remove unnecessary otg calls - odin prefix replaced with g_dnl - Single space indentation between type and variable name
Changes for v5: - None --- drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/g_dnl.c | 202 +++++++++++++++++++++++++++++++++++++++++++ include/g_dnl.h | 33 +++++++ 3 files changed, 236 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/gadget/g_dnl.c create mode 100644 include/g_dnl.h
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 87d1918..2c067c8 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -29,6 +29,7 @@ LIB := $(obj)libusb_gadget.o ifdef CONFIG_USB_GADGET COBJS-y += epautoconf.o config.o usbstring.o COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o +COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o endif ifdef CONFIG_USB_ETHER COBJS-y += ether.o epautoconf.o config.o usbstring.o diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c new file mode 100644 index 0000000..7d87050 --- /dev/null +++ b/drivers/usb/gadget/g_dnl.c @@ -0,0 +1,202 @@ +/* + * g_dnl.c -- USB Downloader Gadget + * + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski l.majewski@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 <errno.h> +#include <common.h> +#include <malloc.h> + +#include <mmc.h> +#include <part.h> + +#include <g_dnl.h> +#include "f_dfu.h" + +#include "gadget_chips.h" +#include "composite.c" + +/* + * One needs to define the following: + * CONFIG_G_DNL_VENDOR_NUM + * CONFIG_G_DNL_PRODUCT_NUM + * CONFIG_G_DNL_MANUFACTURER + * at e.g. ./include/configs/<board>.h + */ + +#define STRING_MANUFACTURER 25 +#define STRING_PRODUCT 2 +#define STRING_USBDOWN 2 +#define CONFIG_USBDOWNLOADER 2 + +#define DRIVER_VERSION "usb_dnl 2.0" + +static const char shortname[] = "usb_dnl_"; +static const char product[] = "USB download gadget"; +static const char manufacturer[] = CONFIG_G_DNL_MANUFACTURER; + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_COMM, + .bDeviceSubClass = 0x02, /*0x02:CDC-modem , 0x00:CDC-serial*/ + + .idVendor = __constant_cpu_to_le16(CONFIG_G_DNL_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16(CONFIG_G_DNL_PRODUCT_NUM), + .iProduct = STRING_PRODUCT, + .bNumConfigurations = 1, +}; + +/* static strings, in UTF-8 */ +static struct usb_string g_dnl_string_defs[] = { + { 0, manufacturer, }, + { 1, product, }, +}; + +static struct usb_gadget_strings g_dnl_string_tab = { + .language = 0x0409, /* en-us */ + .strings = g_dnl_string_defs, +}; + +static struct usb_gadget_strings *g_dnl_composite_strings[] = { + &g_dnl_string_tab, + NULL, +}; + +static int g_dnl_unbind(struct usb_composite_dev *cdev) +{ + debug("%s\n", __func__); + return 0; +} + +static int g_dnl_do_config(struct usb_configuration *c) +{ + const char *s = c->cdev->driver->name; + int ret = -1; + + debug("%s: configuration: 0x%p composite dev: 0x%p\n", + __func__, c, c->cdev); + + printf("GADGET DRIVER: %s\n", s); + if (!strcmp(s, "usb_dnl_dfu")) + ret = dfu_add(c); + + return ret; +} + +static int g_dnl_config_register(struct usb_composite_dev *cdev) +{ + static struct usb_configuration config = { + .label = "usb_dnload", + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .bConfigurationValue = CONFIG_USBDOWNLOADER, + .iConfiguration = STRING_USBDOWN, + + .bind = g_dnl_do_config, + }; + + return usb_add_config(cdev, &config); +} + +static int g_dnl_bind(struct usb_composite_dev *cdev) +{ + struct usb_gadget *gadget = cdev->gadget; + int id, ret; + int gcnum; + + debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev); + + id = usb_string_id(cdev); + + if (id < 0) + return id; + g_dnl_string_defs[0].id = id; + device_desc.iManufacturer = id; + + id = usb_string_id(cdev); + if (id < 0) + return id; + + g_dnl_string_defs[1].id = id; + device_desc.iProduct = id; + + ret = g_dnl_config_register(cdev); + if (ret) + goto error; + + gcnum = usb_gadget_controller_number(gadget); + + debug("gcnum: %d\n", gcnum); + if (gcnum >= 0) + device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); + else { + debug("%s: controller '%s' not recognized\n", + shortname, gadget->name); + device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); + } + + return 0; + + error: + g_dnl_unbind(cdev); + return -ENOMEM; +} + +static struct usb_composite_driver g_dnl_driver = { + .name = NULL, + .dev = &device_desc, + .strings = g_dnl_composite_strings, + + .bind = g_dnl_bind, + .unbind = g_dnl_unbind, +}; + +int g_dnl_register(const char *type) +{ + /* We only allow "dfu" atm, so 3 should be enough */ + static char name[sizeof(shortname) + 3]; + int ret; + + if (!strcmp(type, "dfu")) { + strcpy(name, shortname); + strcat(name, type); + } else { + printf("%s: unknown command: %s\n", __func__, type); + return -EINVAL; + } + + g_dnl_driver.name = name; + + debug("%s: g_dnl_driver.name: %s\n", __func__, g_dnl_driver.name); + ret = usb_composite_register(&g_dnl_driver); + + if (ret) { + printf("%s: failed!, error: %d\n", __func__, ret); + return ret; + } + + return 0; +} + +void g_dnl_unregister(void) +{ + usb_composite_unregister(&g_dnl_driver); +} diff --git a/include/g_dnl.h b/include/g_dnl.h new file mode 100644 index 0000000..0ec7440 --- /dev/null +++ b/include/g_dnl.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski l.majewski@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 __G_DOWNLOAD_H_ +#define __G_DOWNLOAD_H_ + +#include <linux/usb/ch9.h> +#include <usbdescriptors.h> +#include <linux/usb/gadget.h> + +int g_dnl_register(const char *s); +void g_dnl_unregister(void); + +/* USB initialization declaration - board specific */ +void board_usb_init(void); +#endif /* __G_DOWNLOAD_H_ */

Support for f_dfu USB function.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2:
- Replace kzalloc and kfree with free and calloc - Reorganization of calloc calls - Misspelling corrected - Redesign of DFU state machine from "switch case" to function pointers - Split the dfu_handle method to separate functions for each DFU state
Changes for v3: - Missing parenthesis added to sizeof call
Changes for v4: - Replace two puts with one - Remove the 0xff at handle_getstate function - Add const qualifier to struct definitions when possible
Changes for v5: - None --- drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/f_dfu.c | 749 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/f_dfu.h | 100 ++++++ 3 files changed, 850 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/gadget/f_dfu.c create mode 100644 drivers/usb/gadget/f_dfu.h
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 2c067c8..5bbdd36 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -30,6 +30,7 @@ ifdef CONFIG_USB_GADGET COBJS-y += epautoconf.o config.o usbstring.o COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o +COBJS-$(CONFIG_DFU_FUNCTION) += f_dfu.o endif ifdef CONFIG_USB_ETHER COBJS-y += ether.o epautoconf.o config.o usbstring.o diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c new file mode 100644 index 0000000..3ec4c65 --- /dev/null +++ b/drivers/usb/gadget/f_dfu.c @@ -0,0 +1,749 @@ +/* + * f_dfu.c -- Device Firmware Update USB function + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz andrzej.p@samsung.com + * Lukasz Majewski l.majewski@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 <errno.h> +#include <common.h> +#include <malloc.h> + +#include <linux/usb/ch9.h> +#include <usbdescriptors.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> + +#include <dfu.h> +#include "f_dfu.h" + +struct f_dfu { + struct usb_function usb_function; + + struct usb_descriptor_header **function; + struct usb_string *strings; + + /* when configured, we have one config */ + u8 config; + u8 altsetting; + enum dfu_state dfu_state; + unsigned int dfu_status; + + /* Send/received block number is handy for data integrity check */ + int blk_seq_num; +}; + +typedef int (*dfu_state_fn) (struct f_dfu *, + const struct usb_ctrlrequest *, + struct usb_gadget *, + struct usb_request *); + +static inline struct f_dfu *func_to_dfu(struct usb_function *f) +{ + return container_of(f, struct f_dfu, usb_function); +} + +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 = DFU_USB_BUFSIZ, + .bcdDFUVersion = __constant_cpu_to_le16(0x0110), +}; + +static 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 = DYNAMIC */ +}; + +static struct usb_descriptor_header *dfu_runtime_descs[] = { + (struct usb_descriptor_header *) &dfu_intf_runtime, + NULL, +}; + +static const 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 const char dfu_name[] = "Device Firmware Upgrade"; + +/* + * static strings, in UTF-8 + * + * dfu_generic configuration + */ +static struct usb_string strings_dfu_generic[] = { + [0].s = dfu_name, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dfu_generic = { + .language = 0x0409, /* en-us */ + .strings = strings_dfu_generic, +}; + +static struct usb_gadget_strings *dfu_generic_strings[] = { + &stringtab_dfu_generic, + NULL, +}; + +/* + * usb_function specific + */ +static struct usb_gadget_strings stringtab_dfu = { + .language = 0x0409, /* en-us */ + /* + * .strings + * + * assigned during initialization, + * depends on number of flash entities + * + */ +}; + +static struct usb_gadget_strings *dfu_strings[] = { + &stringtab_dfu, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_dfu *f_dfu = req->context; + + dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf, + req->length, f_dfu->blk_seq_num); + + if (req->length == 0) + puts("DOWNLOAD ... OK\nCtrl+C to exit ...\n"); +} + +static void handle_getstatus(struct usb_request *req) +{ + struct dfu_status *dstat = (struct dfu_status *)req->buf; + struct f_dfu *f_dfu = req->context; + + switch (f_dfu->dfu_state) { + case DFU_STATE_dfuDNLOAD_SYNC: + case DFU_STATE_dfuDNBUSY: + f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE; + break; + case DFU_STATE_dfuMANIFEST_SYNC: + break; + default: + break; + } + + /* send status response */ + dstat->bStatus = f_dfu->dfu_status; + dstat->bState = f_dfu->dfu_state; + dstat->iString = 0; +} + +static void handle_getstate(struct usb_request *req) +{ + struct f_dfu *f_dfu = req->context; + + ((u8 *)req->buf)[0] = f_dfu->dfu_state; + req->actual = sizeof(u8); +} + +static inline void to_dfu_mode(struct f_dfu *f_dfu) +{ + f_dfu->usb_function.strings = dfu_strings; + f_dfu->usb_function.hs_descriptors = f_dfu->function; +} + +static inline void to_runtime_mode(struct f_dfu *f_dfu) +{ + f_dfu->usb_function.strings = NULL; + f_dfu->usb_function.hs_descriptors = dfu_runtime_descs; +} + +static int handle_upload(struct usb_request *req, u16 len) +{ + struct f_dfu *f_dfu = req->context; + + return dfu_read(dfu_get_entity(f_dfu->altsetting), req->buf, + req->length, f_dfu->blk_seq_num); +} + +static int handle_dnload(struct usb_gadget *gadget, u16 len) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_request *req = cdev->req; + struct f_dfu *f_dfu = req->context; + + if (len == 0) + f_dfu->dfu_state = DFU_STATE_dfuMANIFEST_SYNC; + + req->complete = dnload_request_complete; + + return len; +} + +/*-------------------------------------------------------------------------*/ +/* DFU state machine */ +static int state_app_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + case USB_REQ_DFU_DETACH: + f_dfu->dfu_state = DFU_STATE_appDETACH; + to_dfu_mode(f_dfu); + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + value = RET_ZLP; + break; + default: + value = RET_STALL; + break; + } + + return value; +} + +static int state_app_detach(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_appIDLE; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 len = le16_to_cpu(ctrl->wLength); + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_DNLOAD: + if (len == 0) { + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; + f_dfu->blk_seq_num = w_value; + value = handle_dnload(gadget, len); + break; + case USB_REQ_DFU_UPLOAD: + f_dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE; + f_dfu->blk_seq_num = 0; + value = handle_upload(req, len); + break; + case USB_REQ_DFU_ABORT: + /* no zlp? */ + value = RET_ZLP; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + 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 :( + */ + f_dfu->dfu_state = + DFU_STATE_dfuMANIFEST_WAIT_RST; + to_runtime_mode(f_dfu); + f_dfu->dfu_state = DFU_STATE_appIDLE; + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_dnload_sync(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_dnbusy(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_dnload_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 len = le16_to_cpu(ctrl->wLength); + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_DNLOAD: + f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; + f_dfu->blk_seq_num = w_value; + value = handle_dnload(gadget, len); + break; + case USB_REQ_DFU_ABORT: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + value = RET_ZLP; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_manifest_sync(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + /* We're MainfestationTolerant */ + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + handle_getstatus(req); + f_dfu->blk_seq_num = 0; + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_upload_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 len = le16_to_cpu(ctrl->wLength); + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_UPLOAD: + /* state transition if less data then requested */ + f_dfu->blk_seq_num = w_value; + value = handle_upload(req, len); + if (value >= 0 && value < len) + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + break; + case USB_REQ_DFU_ABORT: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + /* no zlp? */ + value = RET_ZLP; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_error(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + case USB_REQ_DFU_CLRSTATUS: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + f_dfu->dfu_status = DFU_STATUS_OK; + /* no zlp? */ + value = RET_ZLP; + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static dfu_state_fn dfu_state[] = { + state_app_idle, /* DFU_STATE_appIDLE */ + state_app_detach, /* DFU_STATE_appDETACH */ + state_dfu_idle, /* DFU_STATE_dfuIDLE */ + state_dfu_dnload_sync, /* DFU_STATE_dfuDNLOAD_SYNC */ + state_dfu_dnbusy, /* DFU_STATE_dfuDNBUSY */ + state_dfu_dnload_idle, /* DFU_STATE_dfuDNLOAD_IDLE */ + state_dfu_manifest_sync, /* DFU_STATE_dfuMANIFEST_SYNC */ + NULL, /* DFU_STATE_dfuMANIFEST */ + NULL, /* DFU_STATE_dfuMANIFEST_WAIT_RST */ + state_dfu_upload_idle, /* DFU_STATE_dfuUPLOAD_IDLE */ + state_dfu_error /* DFU_STATE_dfuERROR */ +}; + +static int +dfu_handle(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct usb_gadget *gadget = f->config->cdev->gadget; + struct usb_request *req = f->config->cdev->req; + struct f_dfu *f_dfu = f->config->cdev->req->context; + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + int value = 0; + u8 req_type = ctrl->bRequestType & USB_TYPE_MASK; + + debug("w_value: 0x%x len: 0x%x\n", w_value, len); + debug("req_type: 0x%x ctrl->bRequest: 0x%x f_dfu->dfu_state: 0x%x\n", + req_type, ctrl->bRequest, f_dfu->dfu_state); + + if (req_type == USB_TYPE_STANDARD) { + if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR && + (w_value >> 8) == DFU_DT_FUNC) { + value = min(len, (u16) sizeof(dfu_func)); + memcpy(req->buf, &dfu_func, value); + } + } else /* DFU specific request */ + value = dfu_state[f_dfu->dfu_state] (f_dfu, ctrl, gadget, req); + + if (value >= 0) { + req->length = value; + req->zero = value < len; + value = usb_ep_queue(gadget->ep0, req, 0); + if (value < 0) { + debug("ep_queue --> %d\n", value); + req->status = 0; + } + } + + return value; +} + +/*-------------------------------------------------------------------------*/ + +static int +dfu_prepare_strings(struct f_dfu *f_dfu, int n) +{ + struct dfu_entity *de = NULL; + int i = 0; + + f_dfu->strings = calloc(sizeof(struct usb_string), n + 1); + if (!f_dfu->strings) + goto enomem; + + for (i = 0; i < n; ++i) { + de = dfu_get_entity(i); + f_dfu->strings[i].s = de->name; + } + + f_dfu->strings[i].id = 0; + f_dfu->strings[i].s = NULL; + + return 0; + +enomem: + while (i) + f_dfu->strings[--i].s = NULL; + + free(f_dfu->strings); + + return -ENOMEM; +} + +static int dfu_prepare_function(struct f_dfu *f_dfu, int n) +{ + struct usb_interface_descriptor *d; + int i = 0; + + f_dfu->function = calloc(sizeof(struct usb_descriptor_header *), n); + if (!f_dfu->function) + goto enomem; + + for (i = 0; i < n; ++i) { + d = calloc(sizeof(*d), 1); + 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; + + f_dfu->function[i] = (struct usb_descriptor_header *)d; + } + f_dfu->function[i] = NULL; + + return 0; + +enomem: + while (i) { + free(f_dfu->function[--i]); + f_dfu->function[i] = NULL; + } + free(f_dfu->function); + + return -ENOMEM; +} + +static int dfu_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_dfu *f_dfu = func_to_dfu(f); + int alt_num = dfu_get_alt_number(); + int rv, id, i; + + id = usb_interface_id(c, f); + if (id < 0) + return id; + dfu_intf_runtime.bInterfaceNumber = id; + + f_dfu->dfu_state = DFU_STATE_appIDLE; + f_dfu->dfu_status = DFU_STATUS_OK; + + rv = dfu_prepare_function(f_dfu, alt_num); + if (rv) + goto error; + + rv = dfu_prepare_strings(f_dfu, alt_num); + if (rv) + goto error; + for (i = 0; i < alt_num; i++) { + id = usb_string_id(cdev); + if (id < 0) + return id; + f_dfu->strings[i].id = id; + ((struct usb_interface_descriptor *)f_dfu->function[i]) + ->iInterface = id; + } + + stringtab_dfu.strings = f_dfu->strings; + + cdev->req->context = f_dfu; + +error: + return rv; +} + +static void dfu_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + int alt_num = dfu_get_alt_number(); + int i; + + if (f_dfu->strings) { + i = alt_num; + while (i) + f_dfu->strings[--i].s = NULL; + + free(f_dfu->strings); + } + + if (f_dfu->function) { + i = alt_num; + while (i) { + free(f_dfu->function[--i]); + f_dfu->function[i] = NULL; + } + free(f_dfu->function); + } + + free(f_dfu); +} + +static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + + debug("%s: intf:%d alt:%d\n", __func__, intf, alt); + + f_dfu->altsetting = alt; + + return 0; +} + +/* TODO: is this really what we need here? */ +static void dfu_disable(struct usb_function *f) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + if (f_dfu->config == 0) + return; + + debug("%s: reset config\n", __func__); + + f_dfu->config = 0; +} + +static int dfu_bind_config(struct usb_configuration *c) +{ + struct f_dfu *f_dfu; + int status; + + f_dfu = calloc(sizeof(*f_dfu), 1); + if (!f_dfu) + return -ENOMEM; + f_dfu->usb_function.name = "dfu"; + f_dfu->usb_function.hs_descriptors = dfu_runtime_descs; + f_dfu->usb_function.bind = dfu_bind; + f_dfu->usb_function.unbind = dfu_unbind; + f_dfu->usb_function.set_alt = dfu_set_alt; + f_dfu->usb_function.disable = dfu_disable; + f_dfu->usb_function.strings = dfu_generic_strings, + f_dfu->usb_function.setup = dfu_handle, + + status = usb_add_function(c, &f_dfu->usb_function); + if (status) + free(f_dfu); + + return status; +} + +int dfu_add(struct usb_configuration *c) +{ + int id; + + id = usb_string_id(c->cdev); + if (id < 0) + return id; + strings_dfu_generic[0].id = id; + dfu_intf_runtime.iInterface = id; + + debug("%s: cdev: 0x%p gadget:0x%p gadget->ep0: 0x%p\n", __func__, + c->cdev, c->cdev->gadget, c->cdev->gadget->ep0); + + return dfu_bind_config(c); +} diff --git a/drivers/usb/gadget/f_dfu.h b/drivers/usb/gadget/f_dfu.h new file mode 100644 index 0000000..023e1ad --- /dev/null +++ b/drivers/usb/gadget/f_dfu.h @@ -0,0 +1,100 @@ +/* + * f_dfu.h -- Device Firmware Update gadget + * + * Copyright (C) 2011-2012 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 __F_DFU_H_ +#define __F_DFU_H_ + +#include <linux/compiler.h> +#include <linux/usb/composite.h> + +#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 DFU_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 + +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; + +/* configuration-specific linkup */ +int dfu_add(struct usb_configuration *c); +#endif /* __F_DFU_H_ */

New, separate driver at ./drivers/dfu has been added. It allows platform and storage independent operation of DFU. It has been extended to use new MMC level of command abstraction.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2: - None
Changes for v3: - Remove unnecessary NULL and 0 initialization of dynamic variables - Combine two puts to one - Adding const qualifier to device and layout definitions - Remove unnecessary casting at dfu->name passing - Provide more meaningful names for dfu layouts and device types - Removal of dfu_extract_{token|entity} functions and replace them with strsep calls
Changes for v4: - Remove unnecessary memset - const added where applicable - Handling calloc failure in dfu_config_entries()
Changes for v5: - Replace several calls to calloc(sizeof(struct dfu_entity), 1); with only one, done outside the for loop --- Makefile | 1 + drivers/dfu/Makefile | 43 +++++++++ drivers/dfu/dfu.c | 238 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/dfu.h | 103 ++++++++++++++++++++++ 4 files changed, 385 insertions(+), 0 deletions(-) create mode 100644 drivers/dfu/Makefile create mode 100644 drivers/dfu/dfu.c create mode 100644 include/dfu.h
diff --git a/Makefile b/Makefile index eb37ea1..67c627d 100644 --- a/Makefile +++ b/Makefile @@ -271,6 +271,7 @@ LIBS += drivers/pci/libpci.o LIBS += drivers/pcmcia/libpcmcia.o LIBS += drivers/power/libpower.o LIBS += drivers/spi/libspi.o +LIBS += drivers/dfu/libdfu.o ifeq ($(CPU),mpc83xx) LIBS += drivers/qe/libqe.o LIBS += arch/powerpc/cpu/mpc8xxx/ddr/libddr.o diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile new file mode 100644 index 0000000..7736485 --- /dev/null +++ b/drivers/dfu/Makefile @@ -0,0 +1,43 @@ +# +# Copyright (C) 2012 Samsung Electronics +# Lukasz Majewski l.majewski@samsung.com +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB = $(obj)libdfu.o + +COBJS-$(CONFIG_DFU_FUNCTION) += dfu.o + +SRCS := $(COBJS-y:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS-y)) + +$(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/dfu/dfu.c b/drivers/dfu/dfu.c new file mode 100644 index 0000000..e8477fb --- /dev/null +++ b/drivers/dfu/dfu.c @@ -0,0 +1,238 @@ +/* + * dfu.c -- DFU back-end routines + * + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski l.majewski@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 <fat.h> +#include <dfu.h> +#include <linux/list.h> +#include <linux/compiler.h> + +static LIST_HEAD(dfu_list); +static int dfu_alt_num; + +static int dfu_find_alt_num(const char *s) +{ + int i = 0; + + for (; *s; s++) + if (*s == ';') + i++; + + return ++i; +} + +static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE) + dfu_buf[DFU_DATA_BUF_SIZE]; + +int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) +{ + static unsigned char *i_buf; + static int i_blk_seq_num; + long w_size = 0; + int ret = 0; + + debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n", + __func__, dfu->name, buf, size, blk_seq_num, i_buf); + + if (blk_seq_num == 0) { + i_buf = dfu_buf; + i_blk_seq_num = 0; + } + + if (i_blk_seq_num++ != blk_seq_num) { + printf("%s: Wrong sequence number! [%d] [%d]\n", + __func__, i_blk_seq_num, blk_seq_num); + return -1; + } + + memcpy(i_buf, buf, size); + i_buf += size; + + if (size == 0) { + /* Integrity check (if needed) */ + debug("%s: %s %d [B] CRC32: 0x%x\n", __func__, dfu->name, + i_buf - dfu_buf, crc32(0, dfu_buf, i_buf - dfu_buf)); + + w_size = i_buf - dfu_buf; + ret = dfu->write_medium(dfu, dfu_buf, &w_size); + if (ret) + debug("%s: Write error!\n", __func__); + + i_blk_seq_num = 0; + i_buf = NULL; + return ret; + } + + return ret; +} + +int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) +{ + static unsigned char *i_buf; + static int i_blk_seq_num; + static long r_size; + static u32 crc; + int ret = 0; + + debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n", + __func__, dfu->name, buf, size, blk_seq_num, i_buf); + + if (blk_seq_num == 0) { + i_buf = dfu_buf; + ret = dfu->read_medium(dfu, i_buf, &r_size); + debug("%s: %s %ld [B]\n", __func__, dfu->name, r_size); + i_blk_seq_num = 0; + /* Integrity check (if needed) */ + crc = crc32(0, dfu_buf, r_size); + } + + if (i_blk_seq_num++ != blk_seq_num) { + printf("%s: Wrong sequence number! [%d] [%d]\n", + __func__, i_blk_seq_num, blk_seq_num); + return -1; + } + + if (r_size >= size) { + memcpy(buf, i_buf, size); + i_buf += size; + r_size -= size; + return size; + } else { + memcpy(buf, i_buf, r_size); + i_buf += r_size; + debug("%s: %s CRC32: 0x%x\n", __func__, dfu->name, crc); + puts("UPLOAD ... done\nCtrl+C to exit ...\n"); + + i_buf = NULL; + i_blk_seq_num = 0; + crc = 0; + return r_size; + } + return ret; +} + +static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt, + char *interface, int num) +{ + char *st; + + debug("%s: %s interface: %s num: %d\n", __func__, s, interface, num); + st = strsep(&s, " "); + strcpy(dfu->name, st); + + dfu->dev_num = num; + dfu->alt = alt; + + /* Specific for mmc device */ + if (strcmp(interface, "mmc") == 0) { + if (dfu_fill_entity_mmc(dfu, s)) + return -1; + } else { + printf("%s: Device %s not (yet) supported!\n", + __func__, interface); + return -1; + } + + return 0; +} + +void dfu_free_entities(void) +{ + struct dfu_entity *dfu, *p, *t = NULL; + + list_for_each_entry_safe_reverse(dfu, p, &dfu_list, list) { + list_del(&dfu->list); + t = dfu; + } + if (t) + free(t); + INIT_LIST_HEAD(&dfu_list); +} + +int dfu_config_entities(char *env, char *interface, int num) +{ + struct dfu_entity *dfu; + int i, ret; + char *s; + + dfu_alt_num = dfu_find_alt_num(env); + debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num); + + dfu = calloc(sizeof(*dfu), dfu_alt_num); + if (!dfu) + return -1; + for (i = 0; i < dfu_alt_num; i++) { + + s = strsep(&env, ";"); + ret = dfu_fill_entity(&dfu[i], s, i, interface, num); + if (ret) + return -1; + + list_add_tail(&dfu[i].list, &dfu_list); + } + + return 0; +} + +const char *dfu_get_dev_type(enum dfu_device_type t) +{ + const char *dev_t[] = {NULL, "eMMC", "OneNAND", "NAND" }; + return dev_t[t]; +} + +const char *dfu_get_layout(enum dfu_layout l) +{ + const char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2", + "EXT3", "EXT4" }; + return dfu_layout[l]; +} + +void dfu_show_entities(void) +{ + struct dfu_entity *dfu; + + puts("DFU alt settings list:\n"); + + list_for_each_entry(dfu, &dfu_list, list) { + printf("dev: %s alt: %d name: %s layout: %s\n", + dfu_get_dev_type(dfu->dev_type), dfu->alt, + dfu->name, dfu_get_layout(dfu->layout)); + } +} + +int dfu_get_alt_number(void) +{ + return dfu_alt_num; +} + +struct dfu_entity *dfu_get_entity(int alt) +{ + struct dfu_entity *dfu; + + list_for_each_entry(dfu, &dfu_list, list) { + if (dfu->alt == alt) + return dfu; + } + + return NULL; +} diff --git a/include/dfu.h b/include/dfu.h new file mode 100644 index 0000000..5350d79 --- /dev/null +++ b/include/dfu.h @@ -0,0 +1,103 @@ +/* + * dfu.h - DFU flashable area description + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz andrzej.p@samsung.com + * Lukasz Majewski l.majewski@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_ENTITY_H_ +#define __DFU_ENTITY_H_ + +#include <common.h> +#include <linux/list.h> +#include <mmc.h> + +enum dfu_device_type { + DFU_DEV_MMC = 1, + DFU_DEV_ONENAND, + DFU_DEV_NAND, +}; + +enum dfu_layout { + DFU_RAW_ADDR = 1, + DFU_FS_FAT, + DFU_FS_EXT2, + DFU_FS_EXT3, + DFU_FS_EXT4, +}; + +struct mmc_internal_data { + /* RAW programming */ + unsigned int lba_start; + unsigned int lba_size; + unsigned int lba_blk_size; + + /* FAT/EXT */ + unsigned int dev; + unsigned int part; +}; + +static inline unsigned int get_mmc_blk_size(int dev) +{ + return find_mmc_device(dev)->read_bl_len; +} + +#define DFU_NAME_SIZE 32 +#define DFU_CMD_BUF_SIZE 128 +#define DFU_DATA_BUF_SIZE (1024*1024*4) /* 4 MiB */ + +struct dfu_entity { + char name[DFU_NAME_SIZE]; + int alt; + void *dev_private; + int dev_num; + enum dfu_device_type dev_type; + enum dfu_layout layout; + + union { + struct mmc_internal_data mmc; + } data; + + int (*read_medium)(struct dfu_entity *dfu, void *buf, long *len); + int (*write_medium)(struct dfu_entity *dfu, void *buf, long *len); + + struct list_head list; +}; + +int dfu_config_entities(char *s, char *interface, int num); +void dfu_free_entities(void); +void dfu_show_entities(void); +int dfu_get_alt_number(void); +const char *dfu_get_dev_type(enum dfu_device_type t); +const char *dfu_get_layout(enum dfu_layout l); +struct dfu_entity *dfu_get_entity(int alt); +char *dfu_extract_token(char** e, int *n); + +int dfu_read(struct dfu_entity *de, void *buf, int size, int blk_seq_num); +int dfu_write(struct dfu_entity *de, void *buf, int size, int blk_seq_num); +/* Device specific */ +#ifdef CONFIG_DFU_MMC +extern int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s); +#else +static inline int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) +{ + puts("MMC support not available!\n"); + return -1; +} +#endif +#endif /* __DFU_ENTITY_H_ */

Support for MMC storage devices to work with DFU framework.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2: - None
Changes for v3: - Provide special abstraction layer (mmc_{block|file}_{read|write}) to alleviate switch to new device model (DM) - More verbose messages when not supported layout encountered - Calls to strncmp() replaced with strcmp()
Changes for v4: - Remove superfluous memset() calls - Replace ALLOC_CACHE_ALIGN_BUFFER with plain table definition - Cosmetic changes with pointers definition
Changes for v5: - None --- drivers/dfu/Makefile | 1 + drivers/dfu/dfu_mmc.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 0 deletions(-) create mode 100644 drivers/dfu/dfu_mmc.c
diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index 7736485..7b717bc 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -26,6 +26,7 @@ include $(TOPDIR)/config.mk LIB = $(obj)libdfu.o
COBJS-$(CONFIG_DFU_FUNCTION) += dfu.o +COBJS-$(CONFIG_DFU_MMC) += dfu_mmc.o
SRCS := $(COBJS-y:.o=.c) OBJS := $(addprefix $(obj),$(COBJS-y)) diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c new file mode 100644 index 0000000..060145b --- /dev/null +++ b/drivers/dfu/dfu_mmc.c @@ -0,0 +1,162 @@ +/* + * dfu.c -- DFU back-end routines + * + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski l.majewski@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 <dfu.h> + +enum dfu_mmc_op { + DFU_OP_READ = 1, + DFU_OP_WRITE, +}; + +static int mmc_block_op(enum dfu_mmc_op op, struct dfu_entity *dfu, + void *buf, long *len) +{ + char cmd_buf[DFU_CMD_BUF_SIZE]; + + sprintf(cmd_buf, "mmc %s 0x%x %x %x", + op == DFU_OP_READ ? "read" : "write", + (unsigned int) buf, + dfu->data.mmc.lba_start, + dfu->data.mmc.lba_size); + + if (op == DFU_OP_READ) + *len = dfu->data.mmc.lba_blk_size * dfu->data.mmc.lba_size; + + debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); + return run_command(cmd_buf, 0); +} + +static inline int mmc_block_write(struct dfu_entity *dfu, void *buf, long *len) +{ + return mmc_block_op(DFU_OP_WRITE, dfu, buf, len); +} + +static inline int mmc_block_read(struct dfu_entity *dfu, void *buf, long *len) +{ + return mmc_block_op(DFU_OP_READ, dfu, buf, len); +} + +static int mmc_file_op(enum dfu_mmc_op op, struct dfu_entity *dfu, + void *buf, long *len) +{ + char cmd_buf[DFU_CMD_BUF_SIZE]; + char *str_env; + int ret; + + sprintf(cmd_buf, "fat%s mmc %d:%d 0x%x %s %lx", + op == DFU_OP_READ ? "load" : "write", + dfu->data.mmc.dev, dfu->data.mmc.part, + (unsigned int) buf, dfu->name, *len); + + debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); + + ret = run_command(cmd_buf, 0); + if (ret) { + puts("dfu: Read error!\n"); + return ret; + } + + if (dfu->layout != DFU_RAW_ADDR) { + str_env = getenv("filesize"); + if (str_env == NULL) { + puts("dfu: Wrong file size!\n"); + return -1; + } + *len = simple_strtoul(str_env, NULL, 16); + } + + return ret; +} + +static inline int mmc_file_write(struct dfu_entity *dfu, void *buf, long *len) +{ + return mmc_file_op(DFU_OP_WRITE, dfu, buf, len); +} + +static inline int mmc_file_read(struct dfu_entity *dfu, void *buf, long *len) +{ + return mmc_file_op(DFU_OP_READ, dfu, buf, len); +} + +int dfu_write_medium_mmc(struct dfu_entity *dfu, void *buf, long *len) +{ + int ret = -1; + + switch (dfu->layout) { + case DFU_RAW_ADDR: + ret = mmc_block_write(dfu, buf, len); + break; + case DFU_FS_FAT: + ret = mmc_file_write(dfu, buf, len); + break; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + } + + return ret; +} + +int dfu_read_medium_mmc(struct dfu_entity *dfu, void *buf, long *len) +{ + int ret = -1; + + switch (dfu->layout) { + case DFU_RAW_ADDR: + ret = mmc_block_read(dfu, buf, len); + break; + case DFU_FS_FAT: + ret = mmc_file_read(dfu, buf, len); + break; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + } + + return ret; +} + +int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) +{ + char *st; + + dfu->dev_type = DFU_DEV_MMC; + st = strsep(&s, " "); + if (!strcmp(st, "mmc")) { + dfu->layout = DFU_RAW_ADDR; + dfu->data.mmc.lba_start = simple_strtoul(s, &s, 16); + dfu->data.mmc.lba_size = simple_strtoul(++s, &s, 16); + dfu->data.mmc.lba_blk_size = get_mmc_blk_size(dfu->dev_num); + } else if (!strcmp(st, "fat")) { + dfu->layout = DFU_FS_FAT; + dfu->data.mmc.dev = simple_strtoul(s, &s, 10); + dfu->data.mmc.part = simple_strtoul(++s, &s, 10); + } else { + printf("%s: Memory layout (%s) not supported!\n", __func__, st); + } + + dfu->read_medium = dfu_read_medium_mmc; + dfu->write_medium = dfu_write_medium_mmc; + + return 0; +}

Support for u-boot's command line command "dfu <interface> <dev> [list]".
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2: - None
Changes for v3: - Remove unnecessary initialization to NULL of dynamic variables - goto done added to reduce code duplication - static definition of do_dfu()
Changes for v4: - Define "dfu" string as an array
Changes for v5: - None --- common/Makefile | 1 + common/cmd_dfu.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 0 deletions(-) create mode 100644 common/cmd_dfu.c
diff --git a/common/Makefile b/common/Makefile index 483eb4d..32d44e5 100644 --- a/common/Makefile +++ b/common/Makefile @@ -183,6 +183,7 @@ COBJS-$(CONFIG_MENU) += menu.o COBJS-$(CONFIG_MODEM_SUPPORT) += modem.o COBJS-$(CONFIG_UPDATE_TFTP) += update.o COBJS-$(CONFIG_USB_KEYBOARD) += usb_kbd.o +COBJS-$(CONFIG_CMD_DFU) += cmd_dfu.o endif
ifdef CONFIG_SPL_BUILD diff --git a/common/cmd_dfu.c b/common/cmd_dfu.c new file mode 100644 index 0000000..62fb890 --- /dev/null +++ b/common/cmd_dfu.c @@ -0,0 +1,81 @@ +/* + * cmd_dfu.c -- dfu command + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz andrzej.p@samsung.com + * Lukasz Majewski l.majewski@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 <malloc.h> +#include <dfu.h> +#include <asm/errno.h> +#include <g_dnl.h> + +static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + const char *str_env; + char s[] = "dfu"; + char *env_bkp; + int ret; + + if (argc < 3) + return CMD_RET_USAGE; + + str_env = getenv("dfu_alt_info"); + if (str_env == NULL) { + printf("%s: "dfu_alt_info" env variable not defined!\n", + __func__); + return CMD_RET_FAILURE; + } + + env_bkp = strdup(str_env); + ret = dfu_config_entities(env_bkp, argv[1], + (int)simple_strtoul(argv[2], NULL, 10)); + if (ret) + return CMD_RET_FAILURE; + + if (strcmp(argv[3], "list") == 0) { + dfu_show_entities(); + goto done; + } + + board_usb_init(); + g_dnl_register(s); + while (1) { + if (ctrlc()) + goto exit; + + usb_gadget_handle_interrupts(); + } +exit: + g_dnl_unregister(); +done: + dfu_free_entities(); + free(env_bkp); + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu, + "Device Firmware Upgrade", + "<interface> <dev> [list]\n" + " - device firmware upgrade on a device <dev>\n" + " attached to interface <interface>\n" + " [list] - list available alt settings" +);

Support for USB UDC driver at trats board.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de Cc: Minkyu Kang mk7.kang@samsung.com
--- Changes for v2: - replace puts to debug
Changes for v3: - None
Changes for v4: - None
Changes for v5: - None --- board/samsung/trats/trats.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/board/samsung/trats/trats.c b/board/samsung/trats/trats.c index a8b2b11..4f9cb5a 100644 --- a/board/samsung/trats/trats.c +++ b/board/samsung/trats/trats.c @@ -59,6 +59,8 @@ static int hwrevision(int rev) return (board_rev & 0xf) == rev; }
+struct s3c_plat_otg_data s5pc210_otg_data; + int board_init(void) { gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100; @@ -259,6 +261,12 @@ struct s3c_plat_otg_data s5pc210_otg_data = { .usb_phy_ctrl = EXYNOS4_USBPHY_CONTROL, .usb_flags = PHY0_SLEEP, }; + +void board_usb_init(void) +{ + debug("USB_udc_probe\n"); + s3c_udc_probe(&s5pc210_otg_data); +} #endif
static void pmic_reset(void)

Enable the g_dnl composite USB gadget driver with embedded DFU function on it. It now uses the composite gadget framework to support download specific USB functions (like enabled DFU or USB Mass Storage).
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Cc: Marek Vasut marex@denx.de Cc: Minkyu Kang mk7.kang@samsung.com
--- Change for v2: - Move the G_DNL_{VENDOR_NUM, PRODUCT_NUM and MANUFACTURER} definitions to ./include/configs/<board>.h
Changes for v3: - None
Changes for v4: - None
Changes for v5: - None --- include/configs/trats.h | 24 +++++++++++++++++++++++- 1 files changed, 23 insertions(+), 1 deletions(-)
diff --git a/include/configs/trats.h b/include/configs/trats.h index eb269b2..75a23b0 100644 --- a/include/configs/trats.h +++ b/include/configs/trats.h @@ -94,6 +94,21 @@ #undef CONFIG_CMD_ONENAND #undef CONFIG_CMD_MTDPARTS #define CONFIG_CMD_MMC +#define CONFIG_CMD_DFU + +/* FAT */ +#define CONFIG_CMD_FAT +#define CONFIG_FAT_WRITE + +/* USB Composite download gadget - g_dnl */ +#define CONFIG_USBDOWNLOAD_GADGET +#define CONFIG_DFU_FUNCTION +#define CONFIG_DFU_MMC + +/* USB Samsung's IDs */ +#define CONFIG_G_DNL_VENDOR_NUM 0x04E8 +#define CONFIG_G_DNL_PRODUCT_NUM 0x6601 +#define CONFIG_G_DNL_MANUFACTURER "Samsung"
#define CONFIG_BOOTDELAY 1 #define CONFIG_ZERO_BOOTDELAY_CHECK @@ -104,6 +119,11 @@ #define CONFIG_BOOTBLOCK "10" #define CONFIG_ENV_COMMON_BOOT "${console} ${meminfo}"
+#define CONFIG_DFU_ALT \ + "dfu_alt_info=" \ + "u-boot mmc 80 400;" \ + "uImage fat 0 2\0" \ + #define CONFIG_ENV_OVERWRITE #define CONFIG_SYS_CONSOLE_INFO_QUIET #define CONFIG_SYS_CONSOLE_IS_IN_ENV @@ -146,7 +166,8 @@ "mmcdev=0\0" \ "mmcbootpart=2\0" \ "mmcrootpart=3\0" \ - "opts=always_resume=1" + "opts=always_resume=1\0" \ + CONFIG_DFU_ALT
/* Miscellaneous configurable options */ #define CONFIG_SYS_LONGHELP /* undef to save memory */ @@ -209,6 +230,7 @@ #define CONFIG_USB_GADGET #define CONFIG_USB_GADGET_S3C_UDC_OTG #define CONFIG_USB_GADGET_DUALSPEED +#define CONFIG_USB_GADGET_VBUS_DRAW 2
/* LCD */ #define CONFIG_EXYNOS_FB

Dear Lukasz Majewski,
Those patches add support for composite USB download gadget. This gadget (at least for now) is equipped with DFU download function.
A separate DFU back-end and front-end have been added. Back-end is placed at ./drivers/dfu directory. The front-end is implemented as USB function.
The back-end is written in a generic manner with storage device specific code separated (eMMC).
DFU specification can be found at: http://wiki.openmoko.org/wiki/USB_DFU_-_The_USB_Device_Firmware_Upgrade_sta ndard
[...]
Applied the series and pushed to u-boot-usb ... now guys, please do final rounds of tests etc. so I can submit pullRQ.
Best regards, Marek Vasut
participants (11)
-
Andy Fleming
-
Lukasz Majewski
-
Lukasz Majewski
-
Marek Vasut
-
Marek Vasut
-
Mike Frysinger
-
Minkyu Kang
-
Otavio Salvador
-
Stephen Warren
-
Tom Rini
-
Wolfgang Denk