Skip to content

Commit

Permalink
[example] Add firmware update via USB-MSC and FatFs
Browse files Browse the repository at this point in the history
  • Loading branch information
salkinium committed Nov 4, 2020
1 parent 8832a64 commit 7df2e7d
Show file tree
Hide file tree
Showing 3 changed files with 272 additions and 0 deletions.
154 changes: 154 additions & 0 deletions examples/stm32f411ceu_mini_f411/usbfatfs/main.cpp
Original file line number Diff line number Diff line change
@@ -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 <fatfs/ff.h>
#include <tusb.h>
#include <modm/board.hpp>
#include <modm/processing.hpp>
#include <modm/math/utils.hpp>

/* 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("", &param, 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;
}
15 changes: 15 additions & 0 deletions examples/stm32f411ceu_mini_f411/usbfatfs/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<library>
<extends>modm:mini-f411</extends>
<options>
<option name="modm:build:build.path">../../../build/stm32f411ceu_mini_f411/usb_fatfs</option>
<option name="modm:tinyusb:config">device.msc, device.cdc</option>
</options>
<modules>
<module>modm:processing:timer</module>
<module>modm:tinyusb</module>
<module>modm:fatfs</module>
<module>modm:platform:flash</module>
<module>modm:build:scons</module>
<module>modm:math:utils</module>
</modules>
</library>
103 changes: 103 additions & 0 deletions examples/stm32f411ceu_mini_f411/usbfatfs/ramdisk.cpp
Original file line number Diff line number Diff line change
@@ -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 <fatfs/ff.h>
#include <fatfs/diskio.h>
#include <string.h>
#include <modm/architecture/utils.hpp>

// ----------------------------------------------------------------------------
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 <tusb.h>

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;
}

0 comments on commit 7df2e7d

Please sign in to comment.