You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by je...@apache.org on 2021/10/15 15:33:40 UTC

[mynewt-core] 01/02: tinyusb: Add DFU support

This is an automated email from the ASF dual-hosted git repository.

jerzy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit 1d71f110cd4dfbbb663ccf95be2d733994ae6a67
Author: Jerzy Kasenberg <je...@codecoup.pl>
AuthorDate: Thu Oct 7 13:56:21 2021 +0200

    tinyusb: Add DFU support
    
    This adds USB standard DFU functionality that can be used
    to populate SLOT1 with new firmware image in application
    instead of mcumgr ways.
    
    It also allows to be used in bootloader where it could be
    enabled when there is no valid image in SLOT0.
---
 hw/usb/tinyusb/dfu/pkg.yml                         |  34 +++++
 hw/usb/tinyusb/dfu/src/dfu.c                       | 167 +++++++++++++++++++++
 hw/usb/tinyusb/dfu/syscfg.yml                      |  85 +++++++++++
 .../tinyusb/std_descriptors/include/tusb_config.h  |   5 +
 .../tinyusb/std_descriptors/src/std_descriptors.c  |  10 ++
 hw/usb/tinyusb/std_descriptors/syscfg.yml          |   3 +
 6 files changed, 304 insertions(+)

diff --git a/hw/usb/tinyusb/dfu/pkg.yml b/hw/usb/tinyusb/dfu/pkg.yml
new file mode 100644
index 0000000..4c61670
--- /dev/null
+++ b/hw/usb/tinyusb/dfu/pkg.yml
@@ -0,0 +1,34 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+pkg.name: hw/usb/tinyusb/dfu
+pkg.description: DFU - device firmware update over USB.
+pkg.author: "Apache Mynewt <de...@mynewt.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+    - dfu
+    - usb
+
+pkg.deps:
+    - "@apache-mynewt-core/hw/hal"
+    - "@apache-mynewt-core/kernel/os"
+    - "@apache-mynewt-core/hw/usb/tinyusb"
+    - "@tinyusb/tinyusb"
+    - "@apache-mynewt-core/mgmt/imgmgr"
+    - "@mcuboot/boot/bootutil"
diff --git a/hw/usb/tinyusb/dfu/src/dfu.c b/hw/usb/tinyusb/dfu/src/dfu.c
new file mode 100644
index 0000000..bb37295
--- /dev/null
+++ b/hw/usb/tinyusb/dfu/src/dfu.c
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <os/mynewt.h>
+
+#include <class/dfu/dfu_device.h>
+
+#include <bsp/bsp.h>
+#include <img_mgmt/img_mgmt.h>
+
+/*
+ * If DFU is activated from bootloader it writes to SLOT0.
+ * If application code handles DFU it writes to SLOT1 and marks slot as
+ * pending.
+ */
+#if MYNEWT_VAL(BOOT_LOADER)
+#define FIRMWARE_SLOT       FLASH_AREA_IMAGE_0
+#else
+#define FIRMWARE_SLOT       FLASH_AREA_IMAGE_1
+#endif
+
+#if MYNEWT_VAL(USBD_DFU_RESET_AFTER_DOWNLOAD)
+
+struct os_callout delayed_reset_callout;
+
+void
+delayed_reset_cb(struct os_event *event)
+{
+    hal_system_reset();
+}
+
+#endif
+
+/*
+ * DFU callbacks
+ * Note: alt is used as the partition number, in order to support multiple partitions like FLASH, EEPROM, etc.
+ */
+
+/*
+ * Invoked right before tud_dfu_download_cb() (state=DFU_DNBUSY) or tud_dfu_manifest_cb() (state=DFU_MANIFEST)
+ * Application return timeout in milliseconds (bwPollTimeout) for the next download/manifest operation.
+ * During this period, USB host won't try to communicate with us.
+ */
+uint32_t
+tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state)
+{
+    if (state == DFU_DNBUSY) {
+        return MYNEWT_VAL(USBD_DFU_BLOCK_WRITE_TIME);
+    }
+
+    /* Since we don't buffer entire image and do any flashing in manifest stage no extra time is needed */
+    return 0;
+}
+
+static bool
+flash_erased(const struct flash_area *fa, uint32_t off, uint32_t size)
+{
+    uint8_t buf[64];
+    uint32_t limit = off + size;
+    uint32_t chunk;
+
+    while (off < limit) {
+        chunk = min(sizeof(buf), size);
+        if (flash_area_read_is_empty(fa, off, buf, chunk)) {
+            size -= chunk;
+            off += chunk;
+        } else {
+            break;
+        }
+    }
+
+    return off >= limit;
+}
+
+/*
+ * Invoked when received DFU_DNLOAD (wLength > 0) following by DFU_GETSTATUS (state=DFU_DNBUSY) requests
+ * This callback could be returned before flashing op is complete (async).
+ * Once finished flashing, application must call tud_dfu_finish_flashing()
+ */
+void
+tud_dfu_download_cb(uint8_t alt, uint16_t block_num, const uint8_t *data, uint16_t length)
+{
+    const struct flash_area *fa;
+    uint32_t status = DFU_STATUS_OK;
+
+    if (flash_area_open(FIRMWARE_SLOT, &fa)) {
+        status = DFU_STATUS_ERR_ADDRESS;
+    } else {
+        if (block_num == 0) {
+            USBD_DFU_LOG_INFO("Download started\n");
+        }
+        if (!flash_erased(fa, block_num * CFG_TUD_DFU_XFER_BUFSIZE, length)) {
+            USBD_DFU_LOG_DEBUG("Erasing flash 0x%X (0x%X bytes)\n", block_num * CFG_TUD_DFU_XFER_BUFSIZE, length);
+            if (flash_area_erase(fa, block_num * CFG_TUD_DFU_XFER_BUFSIZE, length)) {
+                USBD_DFU_LOG_ERROR("Flash erase failed\n");
+                status = DFU_STATUS_ERR_ERASE;
+            }
+        }
+        if (status == DFU_STATUS_OK) {
+            USBD_DFU_LOG_DEBUG("Writing flash 0x%X (0x%X bytes)\n", block_num * CFG_TUD_DFU_XFER_BUFSIZE, length);
+
+            if (flash_area_write(fa, block_num * CFG_TUD_DFU_XFER_BUFSIZE, data, length) < 0) {
+                USBD_DFU_LOG_ERROR("Flash write failed\n");
+                status = DFU_STATUS_ERR_PROG;
+            }
+        }
+        flash_area_close(fa);
+    }
+
+    tud_dfu_finish_flashing(status);
+}
+
+/*
+ * Invoked when download process is complete, received DFU_DNLOAD (wLength = 0) followed by DFU_GETSTATUS (state = Manifest)
+ * Application can do checksum, or actual flashing if buffered entire image previously.
+ * Once finished flashing, application must call tud_dfu_finish_flashing()
+ */
+void
+tud_dfu_manifest_cb(uint8_t alt)
+{
+    (void)alt;
+#if !MYNEWT_VAL(BOOT_LOADER) && MYNEWT_VAL(USBD_DFU_MARK_AS_PENDING)
+    USBD_DFU_LOG_INFO("Download completed, enter manifestation. settings slot 1 as pending\n");
+    img_mgmt_state_set_pending(1, MYNEWT_VAL(USBD_DFU_MARK_AS_CONFIRMED));
+#endif
+
+    /*
+     * Flashing op for manifest is complete without error.
+     * Application can perform checksum, should it fail,
+     * use appropriate status such as errVERIFY.
+     */
+    tud_dfu_finish_flashing(DFU_STATUS_OK);
+
+#if MYNEWT_VAL(USBD_DFU_RESET_AFTER_DOWNLOAD)
+    /*
+     * Device should reboot after download is complete.
+     * It should be done after final DFU_GETSTATUS. But this event is not
+     * propagated to application by TinyUSB stack.
+     * To satisfy DFU tools lets give USB stack some time to respond to finishing
+     * commands before reboot.
+     */
+    os_callout_init(&delayed_reset_callout, os_eventq_dflt_get(), delayed_reset_cb, NULL);
+    os_callout_reset(&delayed_reset_callout, os_time_ms_to_ticks32(MYNEWT_VAL(USBD_DFU_RESET_TIMEOUT)));
+#endif
+}
+
+void
+tud_dfu_detach_cb(void)
+{
+    /* TODO: implement detach if needed */
+}
diff --git a/hw/usb/tinyusb/dfu/syscfg.yml b/hw/usb/tinyusb/dfu/syscfg.yml
new file mode 100644
index 0000000..9989735
--- /dev/null
+++ b/hw/usb/tinyusb/dfu/syscfg.yml
@@ -0,0 +1,85 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+syscfg.defs:
+    USBD_DFU_DESCRIPTOR_STRING:
+        description: String for DFU descriptor
+        value: NULL
+    USBD_DFU_SLOT_NAME:
+        description: Name of firmware slot that will be visible in DFU list.
+        value: '"SLOT1"'
+    USBD_DFU_BLOCK_SIZE:
+        description: >
+            Size of block that will be used during upgrade.
+            It must be equal of greater than minimum block size that can be
+            erased.
+            It is flash dependent. For SPIFLASH typical value is 4096.
+            For on-chip flash values can be different.
+        value:
+    USBD_DFU_RESET_AFTER_DOWNLOAD:
+        description: >
+            Reset device after last packet received during DFU.
+        value: 1
+    USBD_DFU_MARK_AS_PENDING:
+        description: >
+            Mark image as pending after download.
+        value: 1
+    USBD_DFU_MARK_AS_CONFIRMED:
+        description: >
+            Mark image as confirmed after download.
+        value: 0
+    USBD_DFU_BLOCK_WRITE_TIME:
+        description: >
+            Time in milliseconds needed for writing block to the device.
+            It is used by DFU protocol to inform host about possible write delay.
+            It can be increased if flash is very slow.
+        value: 1
+    USBD_DFU_RESET_TIMEOUT:
+        description: >
+            Time in milliseconds before devices is reset (when USBD_DFU_RESET_AFTER_DOWNLOAD).
+            Time is required to gracefully finish DFU protocol exchange.
+            Some messages are not propagated to DFU user code (they are internally processed by
+            TinyUSB stack) and rebooting just after download is finished while does not make
+            any harm to DFU process, confuses DFU utilities.
+        value: 1000
+    USBD_DFU_DETACH_TIMEOUT:
+        description: >
+            Time, in milliseconds, that the device will wait after receipt of the
+            DFU_DETACH request.  If this time elapses without a USB reset, then
+            the device will terminate the Reconfiguration phase and revert
+            back to normal operation.  This represents the maximum time that
+            the device can wait (depending on its timers, etc.).  The host may specify a
+            shorter timeout in the DFU_DETACH request.
+        value: 1000
+
+    USBD_DFU_LOG_MODULE:
+        description: 'Numeric module ID to use for DFU log messages.'
+        value: 43
+    USBD_DFU_LOG_LVL:
+        description: 'Minimum level for the DFU log.'
+        value: 1
+
+syscfg.logs:
+    USBD_DFU_LOG:
+        module: MYNEWT_VAL(USBD_DFU_LOG_MODULE)
+        level: MYNEWT_VAL(USBD_DFU_LOG_LVL)
+
+
+syscfg.vals.BOOTLOADER:
+    USBD_DFU_SLOT_NAME: '"SLOT0"'
diff --git a/hw/usb/tinyusb/std_descriptors/include/tusb_config.h b/hw/usb/tinyusb/std_descriptors/include/tusb_config.h
index 3003ef9..0d65c6b 100755
--- a/hw/usb/tinyusb/std_descriptors/include/tusb_config.h
+++ b/hw/usb/tinyusb/std_descriptors/include/tusb_config.h
@@ -75,6 +75,7 @@ extern "C" {
 #define CFG_TUD_VENDOR           0
 #define CFG_TUD_USBTMC           0
 #define CFG_TUD_DFU_RT           0
+#define CFG_TUD_DFU              MYNEWT_VAL(USBD_DFU)
 #define CFG_TUD_NET              0
 #define CFG_TUD_BTH              MYNEWT_VAL(USBD_BTH)
 
@@ -88,6 +89,10 @@ extern "C" {
 /* HID buffer size Should be sufficient to hold ID (if any) + Data */
 #define CFG_TUD_HID_BUFSIZE      16
 
+#ifndef CFG_TUD_DFU_XFER_BUFSIZE
+#define CFG_TUD_DFU_XFER_BUFSIZE    256
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/hw/usb/tinyusb/std_descriptors/src/std_descriptors.c b/hw/usb/tinyusb/std_descriptors/src/std_descriptors.c
index 12e957f..cd11dcf 100755
--- a/hw/usb/tinyusb/std_descriptors/src/std_descriptors.c
+++ b/hw/usb/tinyusb/std_descriptors/src/std_descriptors.c
@@ -184,6 +184,10 @@ enum {
     ITF_NUM_HID,
 #endif
 
+#if CFG_TUD_DFU
+    ITF_NUM_DFU,
+#endif
+
     ITF_NUM_TOTAL
 };
 
@@ -192,6 +196,7 @@ enum {
                              CFG_TUD_MSC * TUD_MSC_DESC_LEN + \
                              CFG_TUD_HID * TUD_HID_DESC_LEN + \
                              CFG_TUD_BTH * TUD_BTH_DESC_LEN + \
+                             CFG_TUD_DFU * TUD_DFU_DESC_LEN(1) + \
                              0)
 
 const uint8_t desc_configuration[] = {
@@ -219,6 +224,10 @@ const uint8_t desc_configuration[] = {
     TUD_HID_DESCRIPTOR(ITF_NUM_HID, HID_IF_STR_IX, HID_PROTOCOL_NONE, sizeof(desc_hid_report),
                        USBD_HID_REPORT_EP, USBD_HID_REPORT_EP_SIZE, USBD_HID_REPORT_EP_INTERVAL),
 #endif
+
+#if CFG_TUD_DFU
+    TUD_DFU_DESCRIPTOR(ITF_NUM_DFU, 1, 8, DFU_ATTR_CAN_DOWNLOAD, CFG_TUD_DFU_DETACH_TIMEOUT, CFG_TUD_DFU_XFER_BUFSIZE),
+#endif
 };
 
 /**
@@ -241,6 +250,7 @@ const char *string_desc_arr[] = {
     MYNEWT_VAL(USBD_MSC_DESCRIPTOR_STRING),
     MYNEWT_VAL(USBD_HID_DESCRIPTOR_STRING),
     MYNEWT_VAL(USBD_BTH_DESCRIPTOR_STRING),
+    MYNEWT_VAL(USBD_DFU_SLOT_NAME),
 };
 
 static uint16_t desc_string[MYNEWT_VAL(USBD_STRING_DESCRIPTOR_MAX_LENGTH) + 1];
diff --git a/hw/usb/tinyusb/std_descriptors/syscfg.yml b/hw/usb/tinyusb/std_descriptors/syscfg.yml
index d122a52..9c2b675 100644
--- a/hw/usb/tinyusb/std_descriptors/syscfg.yml
+++ b/hw/usb/tinyusb/std_descriptors/syscfg.yml
@@ -65,6 +65,9 @@ syscfg.defs:
     USBD_BTH:
         description: Enable BT HCI device
         value: 0
+    USBD_DFU:
+        description: Enable DFU interface
+        value: 0
 
     USBD_CDC_DATA_OUT_EP:
         description: CDC data out endpoint number