From 7df2e7d69e39c64fa88387e98c68e5f314459851 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Wed, 4 Nov 2020 02:40:49 +0100 Subject: [PATCH] [example] Add firmware update via USB-MSC and FatFs --- .../stm32f411ceu_mini_f411/usbfatfs/main.cpp | 154 ++++++++++++++++++ .../usbfatfs/project.xml | 15 ++ .../usbfatfs/ramdisk.cpp | 103 ++++++++++++ 3 files changed, 272 insertions(+) create mode 100644 examples/stm32f411ceu_mini_f411/usbfatfs/main.cpp create mode 100644 examples/stm32f411ceu_mini_f411/usbfatfs/project.xml create mode 100644 examples/stm32f411ceu_mini_f411/usbfatfs/ramdisk.cpp diff --git a/examples/stm32f411ceu_mini_f411/usbfatfs/main.cpp b/examples/stm32f411ceu_mini_f411/usbfatfs/main.cpp new file mode 100644 index 0000000000..756efdad6c --- /dev/null +++ b/examples/stm32f411ceu_mini_f411/usbfatfs/main.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2020, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +/* You can test this by updating this firmware with itself: + +If you want to use the sanity check: + + scons bin && \ + cp ../../../build/stm32f411ceu_mini_f411/usb_fatfs/release/usbfatfs.bin . && \ + echo -n "F411" >> usbfatfs.bin && \ + crc32 usbfatfs.bin | xxd -r -p - >> usbfatfs.bin && \ + mv usbfatfs.bin /Volumes/MODM_USB + +Otherwise just copy the file as is and `#define WITH_SANITY_CHECK 0` + + scons bin && \ + cp ../../../build/stm32f411ceu_mini_f411/usb_fatfs/release/usbfatfs.bin /Volumes/MODM_USB + +*/ + +#define WITH_SANITY_CHECK 1 + +// ---------------------------------------------------------------------------- +modm_ramcode +void ram_apply(uint8_t pages, const uint8_t *image, uint32_t length) +{ + modm::atomic::Lock l; + // ONLY RAMCODE FROM HERE ON!!! + for (auto page{0u}; page <= pages; page++) + Flash::erase(page); + for (uintptr_t ptr{0}; ptr < length; ptr += sizeof(Flash::MaxWordType)) + Flash::program(Flash::OriginAddr + ptr, *(Flash::MaxWordType*)(image + ptr)); + NVIC_SystemReset(); +} + +bool is_valid(FIL *fil, size_t size) +{ +#if WITH_SANITY_CHECK + UINT read; + // Validate processor type string: + f_lseek(fil, size-8); + char type[5]; + f_read(fil, type, 4, &read); + if (memcmp(type, "F411", 4)) + return false; + // Validate CRC32 of the entire image + uint32_t file_crc; + f_read(fil, &file_crc, 4, &read); + file_crc = modm::swap(file_crc); + f_rewind(fil); + uint32_t crc{modm::math::crc32_init}; + for (FSIZE_t offset{0}; offset < size-4; offset++) + { + uint8_t data; + f_read(fil, &data, 1, &read); + crc = modm::math::crc32_update(crc, data); + } + f_rewind(fil); + return (~crc == file_crc); +#else + return true; +#endif +} + +void +check_for_update() +{ + // FAT12 max filename length is 8.3, so this is the max: + static const char *firmware_name = "USBFATFS.BIN"; + static constexpr uint8_t buffer_sector{7}; + // Note: This allocates all FatFs buffers *ON THE STACK*! + // It may be desirable to allocate them statically for your code! + if (FATFS fs; f_mount(&fs, "", 0) == FR_OK) + { + DIR dj; + if (FILINFO fno; f_findfirst(&dj, &fno, "", firmware_name) == FR_OK and fno.fname[0]) + { + if (FIL fil; f_open(&fil, firmware_name, FA_READ) == FR_OK) + { + if (is_valid(&fil, fno.fsize)) + { + Board::Led::set(); + // We first copy the file into the last Flash section of 128kB. + // It's not guaranteed that the file is stored in FatFs in *one* + // continous chunk and we cannot access FatFs code in ram_apply!!! + Flash::unlock(); + Flash::erase(buffer_sector); + uint32_t dst_addr{uint32_t(Flash::getAddr(buffer_sector))}; + for (FSIZE_t offset{0}; offset < fno.fsize; + offset += sizeof(Flash::MaxWordType), + dst_addr += sizeof(Flash::MaxWordType)) + { + Flash::MaxWordType buffer; UINT read; + f_read(&fil, &buffer, sizeof(Flash::MaxWordType), &read); + Flash::program(dst_addr, buffer); + } + // Jump into RAM and copy from last flash page to first pages + ram_apply(Flash::getPage(fno.fsize), Flash::getAddr(buffer_sector), fno.fsize); + } + f_close(&fil); + } + } + f_closedir(&dj); + f_mount(NULL, "", 1); + } +} + +void +initializeFatFs() +{ + FATFS fs; + uint8_t fatfs_buffer[FF_MAX_SS]; + f_mount(&fs, "", 0); + + // initialize ramdisk with Fat12 file system + MKFS_PARM param{FM_FAT | FM_SFD, 0, 0, 0, 0}; + f_mkfs("", ¶m, fatfs_buffer, sizeof(fatfs_buffer)); + f_setlabel("MODM_USB"); + + f_mount(NULL, "", 1); +} + +// ---------------------------------------------------------------------------- +int +main() +{ + Board::initialize(); + Board::initializeUsbFs(); + initializeFatFs(); + tusb_init(); + + while (true) + { + tud_task(); + + static modm::PeriodicTimer tmr{1s}; + if (tmr.execute()) check_for_update(); + } + return 0; +} diff --git a/examples/stm32f411ceu_mini_f411/usbfatfs/project.xml b/examples/stm32f411ceu_mini_f411/usbfatfs/project.xml new file mode 100644 index 0000000000..a5cee879a1 --- /dev/null +++ b/examples/stm32f411ceu_mini_f411/usbfatfs/project.xml @@ -0,0 +1,15 @@ + + modm:mini-f411 + + + + + + modm:processing:timer + modm:tinyusb + modm:fatfs + modm:platform:flash + modm:build:scons + modm:math:utils + + diff --git a/examples/stm32f411ceu_mini_f411/usbfatfs/ramdisk.cpp b/examples/stm32f411ceu_mini_f411/usbfatfs/ramdisk.cpp new file mode 100644 index 0000000000..2f1105e600 --- /dev/null +++ b/examples/stm32f411ceu_mini_f411/usbfatfs/ramdisk.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2020, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- +static constexpr uint32_t sector_size{512}; +static constexpr uint32_t sector_count{230}; +// Allocate giant array inside a NOLOAD heap section +modm_section(".heap1") uint8_t ram_disk[sector_count * sector_size]; + +DSTATUS disk_initialize(BYTE pdrv) { return pdrv ? STA_NOINIT : 0; } +DSTATUS disk_status(BYTE pdrv) { return pdrv ? STA_NOINIT : 0; } +DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) +{ + if (pdrv) return RES_NOTRDY; + memcpy(buff, ram_disk + sector * sector_size, count * sector_size); + return RES_OK; +} +DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count) +{ + if (pdrv) return RES_NOTRDY; + memcpy(ram_disk + sector * sector_size, buff, count * sector_size); + return RES_OK; +} +DRESULT disk_ioctl(BYTE pdrv, BYTE ctrl, void *buff) +{ + if (pdrv) return RES_NOTRDY; + switch (ctrl) + { + case CTRL_SYNC: return RES_OK; + case GET_SECTOR_COUNT: *(LBA_t*)buff = sector_count; return RES_OK; + case GET_SECTOR_SIZE: *(WORD*) buff = sector_size; return RES_OK; + case GET_BLOCK_SIZE: *(DWORD*)buff = 64; return RES_OK; + default: return RES_PARERR; + } +} + +// ---------------------------------------------------------------------------- +// TinyUSB MSC callbacks also accessing the same RAM disk +#include + +void tud_msc_inquiry_cb(uint8_t, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) +{ + const char vid[] = "TinyUSB"; + const char pid[] = "Mass Storage"; + const char rev[] = "1.0"; + memcpy(vendor_id , vid, strlen(vid)); + memcpy(product_id , pid, strlen(pid)); + memcpy(product_rev, rev, strlen(rev)); +} +bool tud_msc_test_unit_ready_cb(uint8_t) { return true; } +bool tud_msc_start_stop_cb(uint8_t, uint8_t, bool, bool) { return true; } +void tud_msc_capacity_cb(uint8_t, uint32_t* block_count, uint16_t* block_size) +{ + *block_count = sector_count; + *block_size = sector_size; +} +int32_t tud_msc_read10_cb(uint8_t, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) +{ + uint8_t const* addr = ram_disk + sector_size * lba + offset; + memcpy(buffer, addr, bufsize); + return bufsize; +} +int32_t tud_msc_write10_cb(uint8_t, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) +{ + uint8_t* addr = ram_disk + sector_size * lba + offset; + memcpy(addr, buffer, bufsize); + return bufsize; +} + +int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) +{ + void const* response = NULL; + uint16_t resplen = 0; + bool in_xfer = true; + switch (scsi_cmd[0]) + { + case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + resplen = 0; + break; + + default: + tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + resplen = -1; + break; + } + if (resplen > bufsize) resplen = bufsize; + if (response and (resplen > 0) and in_xfer) + memcpy(buffer, response, resplen); + return resplen; +}