mirror of
https://github.com/cesanta/mongoose.git
synced 2024-12-26 22:41:03 +08:00
Add sys_flash.c for common mg_flash_{load,save}
This commit is contained in:
parent
a0f8a197c9
commit
822b0c011a
316
mongoose.c
316
mongoose.c
@ -5108,155 +5108,6 @@ bool mg_ota_rollback(void) {
|
||||
MG_DEBUG(("Rolling firmware back"));
|
||||
return mg_flash_swap_bank();
|
||||
}
|
||||
|
||||
// Flash can be written only if it is erased. Erased flash is 0xff (all bits 1)
|
||||
// Writes must be mg_flash_write_align() - aligned. Thus if we want to save an
|
||||
// object, we pad it at the end for alignment.
|
||||
//
|
||||
// Objects in the flash sector are stored sequentially:
|
||||
// | 32-bit size | 32-bit KEY | ..data.. | ..pad.. | 32-bit size | ......
|
||||
//
|
||||
// In order to get to the next object, read its size, then align up.
|
||||
|
||||
// Traverse the list of saved objects
|
||||
size_t mg_flash_next(char *p, char *end, uint32_t *key, size_t *size) {
|
||||
size_t aligned_size = 0, align = mg_flash_write_align(), left = end - p;
|
||||
uint32_t *p32 = (uint32_t *) p, min_size = sizeof(uint32_t) * 2;
|
||||
if (p32[0] != 0xffffffff && left > MG_ROUND_UP(min_size, align)) {
|
||||
if (size) *size = (size_t) p32[0];
|
||||
if (key) *key = p32[1];
|
||||
aligned_size = MG_ROUND_UP(p32[0] + sizeof(uint32_t) * 2, align);
|
||||
if (left < aligned_size) aligned_size = 0; // Out of bounds, fail
|
||||
}
|
||||
return aligned_size;
|
||||
}
|
||||
|
||||
// Return the last sector of Bank 2
|
||||
static char *flash_last_sector(void) {
|
||||
size_t ss = mg_flash_sector_size(), size = mg_flash_size();
|
||||
char *base = (char *) mg_flash_start(), *last = base + size - ss;
|
||||
if (mg_flash_bank() == 2) last -= size / 2;
|
||||
return last;
|
||||
}
|
||||
|
||||
// Find a saved object with a given key
|
||||
bool mg_flash_load(void *sector, uint32_t key, void *buf, size_t len) {
|
||||
char *base = (char *) mg_flash_start(), *s = (char *) sector, *res = NULL;
|
||||
size_t ss = mg_flash_sector_size(), ofs = 0, n, sz;
|
||||
bool ok = false;
|
||||
if (s == NULL) s = flash_last_sector();
|
||||
if (s < base || s >= base + mg_flash_size()) {
|
||||
MG_ERROR(("%p is outsize of flash", sector));
|
||||
} else if (((s - base) % ss) != 0) {
|
||||
MG_ERROR(("%p is not a sector boundary", sector));
|
||||
} else {
|
||||
uint32_t k, scanned = 0;
|
||||
while ((n = mg_flash_next(s + ofs, s + ss, &k, &sz)) > 0) {
|
||||
// MG_DEBUG((" > obj %lu, ofs %lu, key %x/%x", scanned, ofs, k, key));
|
||||
// mg_hexdump(s + ofs, n);
|
||||
if (k == key && sz == len) {
|
||||
res = s + ofs + sizeof(uint32_t) * 2;
|
||||
memcpy(buf, res, len); // Copy object
|
||||
ok = true; // Keep scanning for the newer versions of it
|
||||
}
|
||||
ofs += n, scanned++;
|
||||
}
|
||||
MG_DEBUG(("Scanned %u objects, key %x is @ %p", scanned, key, res));
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool mg_flash_writev(char *location, struct mg_str *strings, size_t n) {
|
||||
size_t align = mg_flash_write_align(), i, j, k = 0, nwritten = 0;
|
||||
char buf[align];
|
||||
bool ok = true;
|
||||
for (i = 0; ok && i < n; i++) {
|
||||
for (j = 0; ok && j < strings[i].len; j++) {
|
||||
buf[k++] = strings[i].ptr[j];
|
||||
if (k >= sizeof(buf)) {
|
||||
ok = mg_flash_write(location + nwritten, buf, sizeof(buf));
|
||||
k = 0, nwritten += sizeof(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (k > 0) {
|
||||
while (k < sizeof(buf)) buf[k++] = 0xff;
|
||||
ok = mg_flash_write(location + nwritten, buf, sizeof(buf));
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
// For all saved objects in the sector, delete old versions of objects
|
||||
static void mg_flash_sector_cleanup(char *sector) {
|
||||
// Buffer all saved objects into an IO buffer (backed by RAM)
|
||||
// erase sector, and re-save them.
|
||||
struct mg_iobuf io = {0, 0, 0, 2048};
|
||||
size_t ss = mg_flash_sector_size();
|
||||
size_t n, size, size2, ofs = 0, hs = sizeof(uint32_t) * 2;
|
||||
uint32_t key;
|
||||
// Traverse all objects
|
||||
MG_DEBUG(("Cleaning up sector %p", sector));
|
||||
while ((n = mg_flash_next(sector + ofs, sector + ss, &key, &size)) > 0) {
|
||||
// Delete an old copy of this object in the cache
|
||||
for (size_t o = 0; o < io.len; o += size2 + hs) {
|
||||
uint32_t k = *(uint32_t *) (io.buf + o + sizeof(uint32_t));
|
||||
size2 = *(uint32_t *) (io.buf + o);
|
||||
if (k == key) {
|
||||
mg_iobuf_del(&io, o, size2 + hs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// And add the new copy
|
||||
mg_iobuf_add(&io, io.len, sector + ofs, size + hs);
|
||||
ofs += n;
|
||||
}
|
||||
// All objects are cached in RAM now
|
||||
if (mg_flash_erase(sector)) { // Erase sector. If successful,
|
||||
for (ofs = 0; ofs < io.len; ofs += size + hs) { // Traverse cached objects
|
||||
size = *(uint32_t *) (io.buf + ofs);
|
||||
key = *(uint32_t *) (io.buf + ofs + sizeof(uint32_t));
|
||||
mg_flash_save(sector, key, io.buf + ofs + hs, size); // Save to flash
|
||||
}
|
||||
}
|
||||
mg_iobuf_free(&io);
|
||||
}
|
||||
|
||||
// Save an object with a given key - append to the end of an object list
|
||||
bool mg_flash_save(void *sector, uint32_t key, const void *buf, size_t len) {
|
||||
char *base = (char *) mg_flash_start(), *s = (char *) sector;
|
||||
size_t ss = mg_flash_sector_size(), ofs = 0, n;
|
||||
bool ok = false;
|
||||
if (s == NULL) s = flash_last_sector();
|
||||
if (s < base || s >= base + mg_flash_size()) {
|
||||
MG_ERROR(("%p is outsize of flash", sector));
|
||||
} else if (((s - base) % ss) != 0) {
|
||||
MG_ERROR(("%p is not a sector boundary", sector));
|
||||
} else {
|
||||
size_t needed = sizeof(uint32_t) * 2 + len;
|
||||
size_t needed_aligned = MG_ROUND_UP(needed, mg_flash_write_align());
|
||||
while ((n = mg_flash_next(s + ofs, s + ss, NULL, NULL)) > 0) ofs += n;
|
||||
|
||||
// If there is not enough space left, cleanup sector and re-eval ofs
|
||||
if (ofs + needed_aligned > ss) {
|
||||
mg_flash_sector_cleanup(s);
|
||||
ofs = 0;
|
||||
while ((n = mg_flash_next(s + ofs, s + ss, NULL, NULL)) > 0) ofs += n;
|
||||
}
|
||||
|
||||
if (ofs + needed_aligned <= ss) {
|
||||
// Enough space to save this object
|
||||
uint32_t hdr[2] = {(uint32_t) len, key};
|
||||
struct mg_str data[] = {mg_str_n((char *) hdr, sizeof(hdr)),
|
||||
mg_str_n(buf, len)};
|
||||
ok = mg_flash_writev(s + ofs, data, 2);
|
||||
MG_DEBUG(("Saving %lu bytes @ %p, key %x: %d", len, s + ofs, key, ok));
|
||||
MG_DEBUG(("Sector space left: %lu bytes", ss - ofs - needed_aligned));
|
||||
} else {
|
||||
MG_ERROR(("Sector is full"));
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MG_ENABLE_LINES
|
||||
@ -6879,10 +6730,174 @@ void mg_sys_reset(void) {
|
||||
#endif
|
||||
|
||||
#ifdef MG_ENABLE_LINES
|
||||
#line 1 "src/sys_stm32h5.c"
|
||||
#line 1 "src/sys_flash.c"
|
||||
#endif
|
||||
|
||||
|
||||
#if MG_SYS == MG_SYS_STM32H7 || MG_SYS == MG_SYS_STM32H5
|
||||
// Flash can be written only if it is erased. Erased flash is 0xff (all bits 1)
|
||||
// Writes must be mg_flash_write_align() - aligned. Thus if we want to save an
|
||||
// object, we pad it at the end for alignment.
|
||||
//
|
||||
// Objects in the flash sector are stored sequentially:
|
||||
// | 32-bit size | 32-bit KEY | ..data.. | ..pad.. | 32-bit size | ......
|
||||
//
|
||||
// In order to get to the next object, read its size, then align up.
|
||||
|
||||
// Traverse the list of saved objects
|
||||
size_t mg_flash_next(char *p, char *end, uint32_t *key, size_t *size) {
|
||||
size_t aligned_size = 0, align = mg_flash_write_align(), left = end - p;
|
||||
uint32_t *p32 = (uint32_t *) p, min_size = sizeof(uint32_t) * 2;
|
||||
if (p32[0] != 0xffffffff && left > MG_ROUND_UP(min_size, align)) {
|
||||
if (size) *size = (size_t) p32[0];
|
||||
if (key) *key = p32[1];
|
||||
aligned_size = MG_ROUND_UP(p32[0] + sizeof(uint32_t) * 2, align);
|
||||
if (left < aligned_size) aligned_size = 0; // Out of bounds, fail
|
||||
}
|
||||
return aligned_size;
|
||||
}
|
||||
|
||||
// Return the last sector of Bank 2
|
||||
static char *flash_last_sector(void) {
|
||||
size_t ss = mg_flash_sector_size(), size = mg_flash_size();
|
||||
char *base = (char *) mg_flash_start(), *last = base + size - ss;
|
||||
if (mg_flash_bank() == 2) last -= size / 2;
|
||||
return last;
|
||||
}
|
||||
|
||||
// Find a saved object with a given key
|
||||
bool mg_flash_load(void *sector, uint32_t key, void *buf, size_t len) {
|
||||
char *base = (char *) mg_flash_start(), *s = (char *) sector, *res = NULL;
|
||||
size_t ss = mg_flash_sector_size(), ofs = 0, n, sz;
|
||||
bool ok = false;
|
||||
if (s == NULL) s = flash_last_sector();
|
||||
if (s < base || s >= base + mg_flash_size()) {
|
||||
MG_ERROR(("%p is outsize of flash", sector));
|
||||
} else if (((s - base) % ss) != 0) {
|
||||
MG_ERROR(("%p is not a sector boundary", sector));
|
||||
} else {
|
||||
uint32_t k, scanned = 0;
|
||||
while ((n = mg_flash_next(s + ofs, s + ss, &k, &sz)) > 0) {
|
||||
// MG_DEBUG((" > obj %lu, ofs %lu, key %x/%x", scanned, ofs, k, key));
|
||||
// mg_hexdump(s + ofs, n);
|
||||
if (k == key && sz == len) {
|
||||
res = s + ofs + sizeof(uint32_t) * 2;
|
||||
memcpy(buf, res, len); // Copy object
|
||||
ok = true; // Keep scanning for the newer versions of it
|
||||
}
|
||||
ofs += n, scanned++;
|
||||
}
|
||||
MG_DEBUG(("Scanned %u objects, key %x is @ %p", scanned, key, res));
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool mg_flash_writev(char *location, struct mg_str *strings, size_t n) {
|
||||
size_t align = mg_flash_write_align(), i, j, k = 0, nwritten = 0;
|
||||
char buf[align];
|
||||
bool ok = true;
|
||||
for (i = 0; ok && i < n; i++) {
|
||||
for (j = 0; ok && j < strings[i].len; j++) {
|
||||
buf[k++] = strings[i].ptr[j];
|
||||
if (k >= sizeof(buf)) {
|
||||
ok = mg_flash_write(location + nwritten, buf, sizeof(buf));
|
||||
k = 0, nwritten += sizeof(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (k > 0) {
|
||||
while (k < sizeof(buf)) buf[k++] = 0xff;
|
||||
ok = mg_flash_write(location + nwritten, buf, sizeof(buf));
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
// For all saved objects in the sector, delete old versions of objects
|
||||
static void mg_flash_sector_cleanup(char *sector) {
|
||||
// Buffer all saved objects into an IO buffer (backed by RAM)
|
||||
// erase sector, and re-save them.
|
||||
struct mg_iobuf io = {0, 0, 0, 2048};
|
||||
size_t ss = mg_flash_sector_size();
|
||||
size_t n, size, size2, ofs = 0, hs = sizeof(uint32_t) * 2;
|
||||
uint32_t key;
|
||||
// Traverse all objects
|
||||
MG_DEBUG(("Cleaning up sector %p", sector));
|
||||
while ((n = mg_flash_next(sector + ofs, sector + ss, &key, &size)) > 0) {
|
||||
// Delete an old copy of this object in the cache
|
||||
for (size_t o = 0; o < io.len; o += size2 + hs) {
|
||||
uint32_t k = *(uint32_t *) (io.buf + o + sizeof(uint32_t));
|
||||
size2 = *(uint32_t *) (io.buf + o);
|
||||
if (k == key) {
|
||||
mg_iobuf_del(&io, o, size2 + hs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// And add the new copy
|
||||
mg_iobuf_add(&io, io.len, sector + ofs, size + hs);
|
||||
ofs += n;
|
||||
}
|
||||
// All objects are cached in RAM now
|
||||
if (mg_flash_erase(sector)) { // Erase sector. If successful,
|
||||
for (ofs = 0; ofs < io.len; ofs += size + hs) { // Traverse cached objects
|
||||
size = *(uint32_t *) (io.buf + ofs);
|
||||
key = *(uint32_t *) (io.buf + ofs + sizeof(uint32_t));
|
||||
mg_flash_save(sector, key, io.buf + ofs + hs, size); // Save to flash
|
||||
}
|
||||
}
|
||||
mg_iobuf_free(&io);
|
||||
}
|
||||
|
||||
// Save an object with a given key - append to the end of an object list
|
||||
bool mg_flash_save(void *sector, uint32_t key, const void *buf, size_t len) {
|
||||
char *base = (char *) mg_flash_start(), *s = (char *) sector;
|
||||
size_t ss = mg_flash_sector_size(), ofs = 0, n;
|
||||
bool ok = false;
|
||||
if (s == NULL) s = flash_last_sector();
|
||||
if (s < base || s >= base + mg_flash_size()) {
|
||||
MG_ERROR(("%p is outsize of flash", sector));
|
||||
} else if (((s - base) % ss) != 0) {
|
||||
MG_ERROR(("%p is not a sector boundary", sector));
|
||||
} else {
|
||||
size_t needed = sizeof(uint32_t) * 2 + len;
|
||||
size_t needed_aligned = MG_ROUND_UP(needed, mg_flash_write_align());
|
||||
while ((n = mg_flash_next(s + ofs, s + ss, NULL, NULL)) > 0) ofs += n;
|
||||
|
||||
// If there is not enough space left, cleanup sector and re-eval ofs
|
||||
if (ofs + needed_aligned > ss) {
|
||||
mg_flash_sector_cleanup(s);
|
||||
ofs = 0;
|
||||
while ((n = mg_flash_next(s + ofs, s + ss, NULL, NULL)) > 0) ofs += n;
|
||||
}
|
||||
|
||||
if (ofs + needed_aligned <= ss) {
|
||||
// Enough space to save this object
|
||||
uint32_t hdr[2] = {(uint32_t) len, key};
|
||||
struct mg_str data[] = {mg_str_n((char *) hdr, sizeof(hdr)),
|
||||
mg_str_n(buf, len)};
|
||||
ok = mg_flash_writev(s + ofs, data, 2);
|
||||
MG_DEBUG(("Saving %lu bytes @ %p, key %x: %d", len, s + ofs, key, ok));
|
||||
MG_DEBUG(("Sector space left: %lu bytes", ss - ofs - needed_aligned));
|
||||
} else {
|
||||
MG_ERROR(("Sector is full"));
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
#else
|
||||
bool mg_flash_save(void *sector, uint32_t key, const void *buf, size_t len) {
|
||||
(void) sector, (void) key, (void) buf, (void) len;
|
||||
return false;
|
||||
}
|
||||
bool mg_flash_load(void *sector, uint32_t key, void *buf, size_t len) {
|
||||
(void) sector, (void) key, (void) buf, (void) len;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MG_ENABLE_LINES
|
||||
#line 1 "src/sys_stm32h5.c"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if MG_SYS == MG_SYS_STM32H5
|
||||
@ -7031,7 +7046,6 @@ void mg_sys_reset(void) {
|
||||
|
||||
|
||||
|
||||
|
||||
#if MG_SYS == MG_SYS_STM32H7
|
||||
|
||||
#define FLASH_BASE1 0x52002000 // Base address for bank1
|
||||
|
@ -1699,6 +1699,8 @@ bool mg_ota_rollback(void); // Rollback to the previous firmware
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define MG_SYS_NONE 0 // Dummy system
|
||||
#define MG_SYS_STM32H5 1 // STM32 H5
|
||||
#define MG_SYS_STM32H7 2 // STM32 H7
|
||||
|
149
src/ota_flash.c
149
src/ota_flash.c
@ -122,153 +122,4 @@ bool mg_ota_rollback(void) {
|
||||
MG_DEBUG(("Rolling firmware back"));
|
||||
return mg_flash_swap_bank();
|
||||
}
|
||||
|
||||
// Flash can be written only if it is erased. Erased flash is 0xff (all bits 1)
|
||||
// Writes must be mg_flash_write_align() - aligned. Thus if we want to save an
|
||||
// object, we pad it at the end for alignment.
|
||||
//
|
||||
// Objects in the flash sector are stored sequentially:
|
||||
// | 32-bit size | 32-bit KEY | ..data.. | ..pad.. | 32-bit size | ......
|
||||
//
|
||||
// In order to get to the next object, read its size, then align up.
|
||||
|
||||
// Traverse the list of saved objects
|
||||
size_t mg_flash_next(char *p, char *end, uint32_t *key, size_t *size) {
|
||||
size_t aligned_size = 0, align = mg_flash_write_align(), left = end - p;
|
||||
uint32_t *p32 = (uint32_t *) p, min_size = sizeof(uint32_t) * 2;
|
||||
if (p32[0] != 0xffffffff && left > MG_ROUND_UP(min_size, align)) {
|
||||
if (size) *size = (size_t) p32[0];
|
||||
if (key) *key = p32[1];
|
||||
aligned_size = MG_ROUND_UP(p32[0] + sizeof(uint32_t) * 2, align);
|
||||
if (left < aligned_size) aligned_size = 0; // Out of bounds, fail
|
||||
}
|
||||
return aligned_size;
|
||||
}
|
||||
|
||||
// Return the last sector of Bank 2
|
||||
static char *flash_last_sector(void) {
|
||||
size_t ss = mg_flash_sector_size(), size = mg_flash_size();
|
||||
char *base = (char *) mg_flash_start(), *last = base + size - ss;
|
||||
if (mg_flash_bank() == 2) last -= size / 2;
|
||||
return last;
|
||||
}
|
||||
|
||||
// Find a saved object with a given key
|
||||
bool mg_flash_load(void *sector, uint32_t key, void *buf, size_t len) {
|
||||
char *base = (char *) mg_flash_start(), *s = (char *) sector, *res = NULL;
|
||||
size_t ss = mg_flash_sector_size(), ofs = 0, n, sz;
|
||||
bool ok = false;
|
||||
if (s == NULL) s = flash_last_sector();
|
||||
if (s < base || s >= base + mg_flash_size()) {
|
||||
MG_ERROR(("%p is outsize of flash", sector));
|
||||
} else if (((s - base) % ss) != 0) {
|
||||
MG_ERROR(("%p is not a sector boundary", sector));
|
||||
} else {
|
||||
uint32_t k, scanned = 0;
|
||||
while ((n = mg_flash_next(s + ofs, s + ss, &k, &sz)) > 0) {
|
||||
// MG_DEBUG((" > obj %lu, ofs %lu, key %x/%x", scanned, ofs, k, key));
|
||||
// mg_hexdump(s + ofs, n);
|
||||
if (k == key && sz == len) {
|
||||
res = s + ofs + sizeof(uint32_t) * 2;
|
||||
memcpy(buf, res, len); // Copy object
|
||||
ok = true; // Keep scanning for the newer versions of it
|
||||
}
|
||||
ofs += n, scanned++;
|
||||
}
|
||||
MG_DEBUG(("Scanned %u objects, key %x is @ %p", scanned, key, res));
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool mg_flash_writev(char *location, struct mg_str *strings, size_t n) {
|
||||
size_t align = mg_flash_write_align(), i, j, k = 0, nwritten = 0;
|
||||
char buf[align];
|
||||
bool ok = true;
|
||||
for (i = 0; ok && i < n; i++) {
|
||||
for (j = 0; ok && j < strings[i].len; j++) {
|
||||
buf[k++] = strings[i].ptr[j];
|
||||
if (k >= sizeof(buf)) {
|
||||
ok = mg_flash_write(location + nwritten, buf, sizeof(buf));
|
||||
k = 0, nwritten += sizeof(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (k > 0) {
|
||||
while (k < sizeof(buf)) buf[k++] = 0xff;
|
||||
ok = mg_flash_write(location + nwritten, buf, sizeof(buf));
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
// For all saved objects in the sector, delete old versions of objects
|
||||
static void mg_flash_sector_cleanup(char *sector) {
|
||||
// Buffer all saved objects into an IO buffer (backed by RAM)
|
||||
// erase sector, and re-save them.
|
||||
struct mg_iobuf io = {0, 0, 0, 2048};
|
||||
size_t ss = mg_flash_sector_size();
|
||||
size_t n, size, size2, ofs = 0, hs = sizeof(uint32_t) * 2;
|
||||
uint32_t key;
|
||||
// Traverse all objects
|
||||
MG_DEBUG(("Cleaning up sector %p", sector));
|
||||
while ((n = mg_flash_next(sector + ofs, sector + ss, &key, &size)) > 0) {
|
||||
// Delete an old copy of this object in the cache
|
||||
for (size_t o = 0; o < io.len; o += size2 + hs) {
|
||||
uint32_t k = *(uint32_t *) (io.buf + o + sizeof(uint32_t));
|
||||
size2 = *(uint32_t *) (io.buf + o);
|
||||
if (k == key) {
|
||||
mg_iobuf_del(&io, o, size2 + hs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// And add the new copy
|
||||
mg_iobuf_add(&io, io.len, sector + ofs, size + hs);
|
||||
ofs += n;
|
||||
}
|
||||
// All objects are cached in RAM now
|
||||
if (mg_flash_erase(sector)) { // Erase sector. If successful,
|
||||
for (ofs = 0; ofs < io.len; ofs += size + hs) { // Traverse cached objects
|
||||
size = *(uint32_t *) (io.buf + ofs);
|
||||
key = *(uint32_t *) (io.buf + ofs + sizeof(uint32_t));
|
||||
mg_flash_save(sector, key, io.buf + ofs + hs, size); // Save to flash
|
||||
}
|
||||
}
|
||||
mg_iobuf_free(&io);
|
||||
}
|
||||
|
||||
// Save an object with a given key - append to the end of an object list
|
||||
bool mg_flash_save(void *sector, uint32_t key, const void *buf, size_t len) {
|
||||
char *base = (char *) mg_flash_start(), *s = (char *) sector;
|
||||
size_t ss = mg_flash_sector_size(), ofs = 0, n;
|
||||
bool ok = false;
|
||||
if (s == NULL) s = flash_last_sector();
|
||||
if (s < base || s >= base + mg_flash_size()) {
|
||||
MG_ERROR(("%p is outsize of flash", sector));
|
||||
} else if (((s - base) % ss) != 0) {
|
||||
MG_ERROR(("%p is not a sector boundary", sector));
|
||||
} else {
|
||||
size_t needed = sizeof(uint32_t) * 2 + len;
|
||||
size_t needed_aligned = MG_ROUND_UP(needed, mg_flash_write_align());
|
||||
while ((n = mg_flash_next(s + ofs, s + ss, NULL, NULL)) > 0) ofs += n;
|
||||
|
||||
// If there is not enough space left, cleanup sector and re-eval ofs
|
||||
if (ofs + needed_aligned > ss) {
|
||||
mg_flash_sector_cleanup(s);
|
||||
ofs = 0;
|
||||
while ((n = mg_flash_next(s + ofs, s + ss, NULL, NULL)) > 0) ofs += n;
|
||||
}
|
||||
|
||||
if (ofs + needed_aligned <= ss) {
|
||||
// Enough space to save this object
|
||||
uint32_t hdr[2] = {(uint32_t) len, key};
|
||||
struct mg_str data[] = {mg_str_n((char *) hdr, sizeof(hdr)),
|
||||
mg_str_n(buf, len)};
|
||||
ok = mg_flash_writev(s + ofs, data, 2);
|
||||
MG_DEBUG(("Saving %lu bytes @ %p, key %x: %d", len, s + ofs, key, ok));
|
||||
MG_DEBUG(("Sector space left: %lu bytes", ss - ofs - needed_aligned));
|
||||
} else {
|
||||
MG_ERROR(("Sector is full"));
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
#endif
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "arch.h"
|
||||
|
||||
#define MG_SYS_NONE 0 // Dummy system
|
||||
#define MG_SYS_STM32H5 1 // STM32 H5
|
||||
#define MG_SYS_STM32H7 2 // STM32 H7
|
||||
|
161
src/sys_flash.c
Normal file
161
src/sys_flash.c
Normal file
@ -0,0 +1,161 @@
|
||||
#include "sys.h"
|
||||
|
||||
#if MG_SYS == MG_SYS_STM32H7 || MG_SYS == MG_SYS_STM32H5
|
||||
// Flash can be written only if it is erased. Erased flash is 0xff (all bits 1)
|
||||
// Writes must be mg_flash_write_align() - aligned. Thus if we want to save an
|
||||
// object, we pad it at the end for alignment.
|
||||
//
|
||||
// Objects in the flash sector are stored sequentially:
|
||||
// | 32-bit size | 32-bit KEY | ..data.. | ..pad.. | 32-bit size | ......
|
||||
//
|
||||
// In order to get to the next object, read its size, then align up.
|
||||
|
||||
// Traverse the list of saved objects
|
||||
size_t mg_flash_next(char *p, char *end, uint32_t *key, size_t *size) {
|
||||
size_t aligned_size = 0, align = mg_flash_write_align(), left = end - p;
|
||||
uint32_t *p32 = (uint32_t *) p, min_size = sizeof(uint32_t) * 2;
|
||||
if (p32[0] != 0xffffffff && left > MG_ROUND_UP(min_size, align)) {
|
||||
if (size) *size = (size_t) p32[0];
|
||||
if (key) *key = p32[1];
|
||||
aligned_size = MG_ROUND_UP(p32[0] + sizeof(uint32_t) * 2, align);
|
||||
if (left < aligned_size) aligned_size = 0; // Out of bounds, fail
|
||||
}
|
||||
return aligned_size;
|
||||
}
|
||||
|
||||
// Return the last sector of Bank 2
|
||||
static char *flash_last_sector(void) {
|
||||
size_t ss = mg_flash_sector_size(), size = mg_flash_size();
|
||||
char *base = (char *) mg_flash_start(), *last = base + size - ss;
|
||||
if (mg_flash_bank() == 2) last -= size / 2;
|
||||
return last;
|
||||
}
|
||||
|
||||
// Find a saved object with a given key
|
||||
bool mg_flash_load(void *sector, uint32_t key, void *buf, size_t len) {
|
||||
char *base = (char *) mg_flash_start(), *s = (char *) sector, *res = NULL;
|
||||
size_t ss = mg_flash_sector_size(), ofs = 0, n, sz;
|
||||
bool ok = false;
|
||||
if (s == NULL) s = flash_last_sector();
|
||||
if (s < base || s >= base + mg_flash_size()) {
|
||||
MG_ERROR(("%p is outsize of flash", sector));
|
||||
} else if (((s - base) % ss) != 0) {
|
||||
MG_ERROR(("%p is not a sector boundary", sector));
|
||||
} else {
|
||||
uint32_t k, scanned = 0;
|
||||
while ((n = mg_flash_next(s + ofs, s + ss, &k, &sz)) > 0) {
|
||||
// MG_DEBUG((" > obj %lu, ofs %lu, key %x/%x", scanned, ofs, k, key));
|
||||
// mg_hexdump(s + ofs, n);
|
||||
if (k == key && sz == len) {
|
||||
res = s + ofs + sizeof(uint32_t) * 2;
|
||||
memcpy(buf, res, len); // Copy object
|
||||
ok = true; // Keep scanning for the newer versions of it
|
||||
}
|
||||
ofs += n, scanned++;
|
||||
}
|
||||
MG_DEBUG(("Scanned %u objects, key %x is @ %p", scanned, key, res));
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool mg_flash_writev(char *location, struct mg_str *strings, size_t n) {
|
||||
size_t align = mg_flash_write_align(), i, j, k = 0, nwritten = 0;
|
||||
char buf[align];
|
||||
bool ok = true;
|
||||
for (i = 0; ok && i < n; i++) {
|
||||
for (j = 0; ok && j < strings[i].len; j++) {
|
||||
buf[k++] = strings[i].ptr[j];
|
||||
if (k >= sizeof(buf)) {
|
||||
ok = mg_flash_write(location + nwritten, buf, sizeof(buf));
|
||||
k = 0, nwritten += sizeof(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (k > 0) {
|
||||
while (k < sizeof(buf)) buf[k++] = 0xff;
|
||||
ok = mg_flash_write(location + nwritten, buf, sizeof(buf));
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
// For all saved objects in the sector, delete old versions of objects
|
||||
static void mg_flash_sector_cleanup(char *sector) {
|
||||
// Buffer all saved objects into an IO buffer (backed by RAM)
|
||||
// erase sector, and re-save them.
|
||||
struct mg_iobuf io = {0, 0, 0, 2048};
|
||||
size_t ss = mg_flash_sector_size();
|
||||
size_t n, size, size2, ofs = 0, hs = sizeof(uint32_t) * 2;
|
||||
uint32_t key;
|
||||
// Traverse all objects
|
||||
MG_DEBUG(("Cleaning up sector %p", sector));
|
||||
while ((n = mg_flash_next(sector + ofs, sector + ss, &key, &size)) > 0) {
|
||||
// Delete an old copy of this object in the cache
|
||||
for (size_t o = 0; o < io.len; o += size2 + hs) {
|
||||
uint32_t k = *(uint32_t *) (io.buf + o + sizeof(uint32_t));
|
||||
size2 = *(uint32_t *) (io.buf + o);
|
||||
if (k == key) {
|
||||
mg_iobuf_del(&io, o, size2 + hs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// And add the new copy
|
||||
mg_iobuf_add(&io, io.len, sector + ofs, size + hs);
|
||||
ofs += n;
|
||||
}
|
||||
// All objects are cached in RAM now
|
||||
if (mg_flash_erase(sector)) { // Erase sector. If successful,
|
||||
for (ofs = 0; ofs < io.len; ofs += size + hs) { // Traverse cached objects
|
||||
size = *(uint32_t *) (io.buf + ofs);
|
||||
key = *(uint32_t *) (io.buf + ofs + sizeof(uint32_t));
|
||||
mg_flash_save(sector, key, io.buf + ofs + hs, size); // Save to flash
|
||||
}
|
||||
}
|
||||
mg_iobuf_free(&io);
|
||||
}
|
||||
|
||||
// Save an object with a given key - append to the end of an object list
|
||||
bool mg_flash_save(void *sector, uint32_t key, const void *buf, size_t len) {
|
||||
char *base = (char *) mg_flash_start(), *s = (char *) sector;
|
||||
size_t ss = mg_flash_sector_size(), ofs = 0, n;
|
||||
bool ok = false;
|
||||
if (s == NULL) s = flash_last_sector();
|
||||
if (s < base || s >= base + mg_flash_size()) {
|
||||
MG_ERROR(("%p is outsize of flash", sector));
|
||||
} else if (((s - base) % ss) != 0) {
|
||||
MG_ERROR(("%p is not a sector boundary", sector));
|
||||
} else {
|
||||
size_t needed = sizeof(uint32_t) * 2 + len;
|
||||
size_t needed_aligned = MG_ROUND_UP(needed, mg_flash_write_align());
|
||||
while ((n = mg_flash_next(s + ofs, s + ss, NULL, NULL)) > 0) ofs += n;
|
||||
|
||||
// If there is not enough space left, cleanup sector and re-eval ofs
|
||||
if (ofs + needed_aligned > ss) {
|
||||
mg_flash_sector_cleanup(s);
|
||||
ofs = 0;
|
||||
while ((n = mg_flash_next(s + ofs, s + ss, NULL, NULL)) > 0) ofs += n;
|
||||
}
|
||||
|
||||
if (ofs + needed_aligned <= ss) {
|
||||
// Enough space to save this object
|
||||
uint32_t hdr[2] = {(uint32_t) len, key};
|
||||
struct mg_str data[] = {mg_str_n((char *) hdr, sizeof(hdr)),
|
||||
mg_str_n(buf, len)};
|
||||
ok = mg_flash_writev(s + ofs, data, 2);
|
||||
MG_DEBUG(("Saving %lu bytes @ %p, key %x: %d", len, s + ofs, key, ok));
|
||||
MG_DEBUG(("Sector space left: %lu bytes", ss - ofs - needed_aligned));
|
||||
} else {
|
||||
MG_ERROR(("Sector is full"));
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
#else
|
||||
bool mg_flash_save(void *sector, uint32_t key, const void *buf, size_t len) {
|
||||
(void) sector, (void) key, (void) buf, (void) len;
|
||||
return false;
|
||||
}
|
||||
bool mg_flash_load(void *sector, uint32_t key, void *buf, size_t len) {
|
||||
(void) sector, (void) key, (void) buf, (void) len;
|
||||
return false;
|
||||
}
|
||||
#endif
|
@ -1,6 +1,5 @@
|
||||
#include "arch.h"
|
||||
#include "log.h"
|
||||
#include "ota.h"
|
||||
#include "sys.h"
|
||||
|
||||
#if MG_SYS == MG_SYS_STM32H5
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "arch.h"
|
||||
#include "log.h"
|
||||
#include "ota.h"
|
||||
#include "sys.h"
|
||||
|
||||
#if MG_SYS == MG_SYS_STM32H7
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user