You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by ja...@apache.org on 2026/10/27 09:33:34 UTC

(mynewt-core) 08/08: hw/usb/tinyusb/msc_flash: Preserve flash content on block write

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

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

commit c82d3ba3e2a56f88e44cf7a128ec878b539fe88d
Author: Szymon Janc <sz...@codecoup.pl>
AuthorDate: Mon Oct 26 17:13:17 2026 +0100

    hw/usb/tinyusb/msc_flash: Preserve flash content on block write
    
    Before writing a 512 bytes block that was not erased, the whole flash
    sector containing it was erased. With sectors larger than block, e.g.
    4 KB on nRF52, this destroyed content of other blocks in the sector,
    corrupting file system on every overwrite.
    
    When written block is not erased, read the sector into buffer, erase
    it and write it back with new block content. Size of the buffer is set
    with MSC_FLASH_SECTOR_BUF_SIZE, writes to flash with larger sectors
    fail unless written block is erased. The buffer is moved out of
    lun_data, which is initialized data, so it is not stored in flash
    image, and it is shared by all LUNs as writes are serialized.
    
    Assisted-by: Claude:claude-opus-5-5
---
 hw/usb/tinyusb/msc_flash/src/msc_flash.c | 87 ++++++++++++++++++++++++--------
 hw/usb/tinyusb/msc_flash/syscfg.yml      |  6 +++
 2 files changed, 73 insertions(+), 20 deletions(-)

diff --git a/hw/usb/tinyusb/msc_flash/src/msc_flash.c b/hw/usb/tinyusb/msc_flash/src/msc_flash.c
index 2726d399c..e67554885 100644
--- a/hw/usb/tinyusb/msc_flash/src/msc_flash.c
+++ b/hw/usb/tinyusb/msc_flash/src/msc_flash.c
@@ -45,11 +45,11 @@ struct lun_data {
     /* Address of exported part of flash */
     uint32_t flash_start;
     uint32_t flash_end;
-
-    /* buffer for checking if flash is empty */
-    uint8_t flash_sector_buffer[512];
 };
 
+/* Buffer for read-modify-write of flash sector */
+static uint8_t flash_sector_buffer[MYNEWT_VAL(MSC_FLASH_SECTOR_BUF_SIZE)];
+
 struct lun_data luns[1] = {
     {
         .medium_state = MEDIUM_PRESENT,
@@ -134,31 +134,79 @@ flash_write_non_erased(struct lun_data *lun_data, uint32_t addr,
     return 0;
 }
 
+/*
+ * Writes data that does not cross sector boundary. If flash is not erased,
+ * sector content is preserved by reading it to buffer, erasing sector and
+ * writing it back with new data.
+ */
 static int
-flash_write(struct lun_data *lun_data, uint32_t addr, const uint8_t *buffer,
-            uint32_t bufsize)
+flash_write_in_sector(struct lun_data *lun_data, uint32_t sector_start,
+                      uint32_t sector_size, uint32_t addr,
+                      const uint8_t *buffer, uint32_t len)
 {
-    return flash_write_non_erased(lun_data, lun_data->flash_start + addr,
-                                  buffer, bufsize);
+    int rc;
+
+    rc = hal_flash_isempty(lun_data->flash_id, addr, flash_sector_buffer, len);
+    if (rc == 1) {
+        return flash_write_non_erased(lun_data, addr, buffer, len);
+    } else if (rc < 0) {
+        return rc;
+    }
+
+    if (sector_size > sizeof(flash_sector_buffer)) {
+        MSC_FLASH_LOG_ERROR("Sector size %u exceeds buffer size\n",
+                            (unsigned)sector_size);
+        return -1;
+    }
+
+    rc = hal_flash_read(lun_data->flash_id, sector_start, flash_sector_buffer,
+                        sector_size);
+    if (rc) {
+        return rc;
+    }
+    memcpy(flash_sector_buffer + (addr - sector_start), buffer, len);
+
+    rc = hal_flash_erase_sector(lun_data->flash_id, sector_start);
+    if (rc) {
+        return rc;
+    }
+
+    return flash_write_non_erased(lun_data, sector_start, flash_sector_buffer,
+                                  sector_size);
 }
 
 static int
-flash_erase_if_needed(struct lun_data *lun_data, uint32_t addr, uint32_t size)
+flash_write(struct lun_data *lun_data, uint32_t addr, const uint8_t *buffer,
+            uint32_t bufsize)
 {
-    int left = size;
-    int rc = 0;
-
-    while (left > 0 && rc == 0) {
-        if (!hal_flash_isempty(lun_data->flash_id, lun_data->flash_start + addr,
-                               lun_data->flash_sector_buffer, 512)) {
-            rc = hal_flash_erase(lun_data->flash_id,
-                                 lun_data->flash_start + addr, 512);
+    uint32_t sector_start;
+    uint32_t sector_size;
+    uint32_t n;
+    int rc;
+
+    addr += lun_data->flash_start;
+
+    while (bufsize > 0) {
+        rc = flash_sector_find(lun_data->flash_id, addr, &sector_start, &sector_size);
+        if (rc) {
+            return rc;
+        }
+
+        n = min(bufsize, sector_start + sector_size - addr);
+        rc = flash_write_in_sector(lun_data, sector_start, sector_size, addr,
+                                   buffer, n);
+        if (rc) {
+            return rc;
         }
-        left -= 512;
+
+        addr += n;
+        buffer += n;
+        bufsize -= n;
     }
 
-    return rc;
+    return 0;
 }
+
 #define IN_ARRAY(arr, ix) ((ix) < ARRAY_SIZE(arr))
 
 static struct lun_data *
@@ -368,8 +416,7 @@ tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer,
         if (!lba_to_flash_addr(lun_data, lba, offset, bufsize, &flash_addr)) {
             return -1;
         }
-        if (flash_erase_if_needed(lun_data, flash_addr, bufsize) != 0 ||
-            flash_write(lun_data, flash_addr, buffer, bufsize) != 0) {
+        if (flash_write(lun_data, flash_addr, buffer, bufsize) != 0) {
             return -1;
         }
     }
diff --git a/hw/usb/tinyusb/msc_flash/syscfg.yml b/hw/usb/tinyusb/msc_flash/syscfg.yml
index 87cb6000d..687829c47 100644
--- a/hw/usb/tinyusb/msc_flash/syscfg.yml
+++ b/hw/usb/tinyusb/msc_flash/syscfg.yml
@@ -27,6 +27,12 @@ syscfg.defs:
     MSC_FLASH_FLASH_AREA_ID:
         description: 'Target flash area'
         value:
+    MSC_FLASH_SECTOR_BUF_SIZE:
+        description: >
+            Size of buffer used to preserve content of flash sector when
+            block within it is written. Writes to flash with sectors larger
+            than this value fail unless written area is already erased.
+        value: 4096
     MSC_FLASH_MBR:
         description: >
             Add partition table. It can prevent Windows to popup request