-
Notifications
You must be signed in to change notification settings - Fork 134
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[example] Add firmware update via USB-MSC and FatFs
- Loading branch information
Showing
3 changed files
with
272 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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("", ¶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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |