diff --git a/mongoose.c b/mongoose.c index 6c060435..5805752d 100644 --- a/mongoose.c +++ b/mongoose.c @@ -5004,6 +5004,284 @@ bool mg_l2_pppoe_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto, #endif +#ifdef MG_ENABLE_LINES +#line 1 "src/lfs.c" +#endif +// Copyright (c) 2023 Cesanta Software Limited +// SPDX-License-Identifier: GPL-2.0-only or commercial + + + + +#if MG_ENABLE_LFS + +#include +#include +#include +#include +#include + +#ifndef LFS_USE_RAM +#define LFS_USE_RAM 0 // If 1, use RAM FS. If 0, use flash +#endif + +#ifndef MG_LFS_BUF_SIZE +#define MG_LFS_BUF_SIZE 64 // Buffer size used for reads, writes, and cache +#endif + +#ifndef MG_LFS_FD_BASE +#define MG_LFS_FD_BASE 12000 // Starting file descriptor number +#endif + +#if LFS_USE_RAM && !defined(MG_LFS_BLOCK_SIZE) +#define MG_LFS_BLOCK_SIZE 8192 +#endif + +#ifndef DT_DIR +#define DT_DIR 4 +#endif +#ifndef DT_REG +#define DT_REG 8 +#endif + +typedef struct mg_lfs_fd DIR; +struct dirent { + char d_name[LFS_NAME_MAX + 1]; + unsigned char d_type; +}; + +struct mg_lfs_fd { + struct mg_lfs_fd *next; + bool isopen; + lfs_file_t file; + lfs_dir_t dir; + int fd; +}; + +static lfs_t s_lfs; +static struct mg_lfs_fd *s_fds; +static uint8_t *s_fs; +static int s_next_fd = MG_LFS_FD_BASE; +static bool s_lfs_ready; +#if LFS_USE_RAM +static uint8_t *s_ram_fs; +#endif + +static int lfs_driver_read(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, void *buf, lfs_size_t len); +static int lfs_driver_prog(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, const void *buf, lfs_size_t len); +static int lfs_driver_erase(const struct lfs_config *cfg, lfs_block_t block); +static int lfs_driver_sync(const struct lfs_config *cfg); + +static struct lfs_config s_cfg = { + .read = lfs_driver_read, + .prog = lfs_driver_prog, + .erase = lfs_driver_erase, + .sync = lfs_driver_sync, + .block_cycles = 200, + .cache_size = MG_LFS_BUF_SIZE, + .read_size = MG_LFS_BUF_SIZE, + .prog_size = MG_LFS_BUF_SIZE, + .lookahead_size = MG_LFS_BUF_SIZE / 8, +}; + +static bool flash_write_buf(void *addr, const void *buf, size_t len) { +#if LFS_USE_RAM + memmove(addr, buf, len); + return true; +#else + return mg_flash != NULL && mg_flash->write_fn(addr, buf, len); +#endif +} + +static int lfs_driver_read(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, void *buf, lfs_size_t len) { + memmove(buf, &s_fs[block * cfg->block_size + off], len); + return 0; +} + +static int lfs_driver_prog(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, const void *buf, lfs_size_t len) { + uint8_t *dst = &s_fs[block * cfg->block_size + off]; + if (!flash_write_buf(dst, buf, len)) return LFS_ERR_IO; + return 0; +} + +static int lfs_driver_erase(const struct lfs_config *cfg, lfs_block_t block) { + (void) cfg, (void) block; + return 0; +} + +static int lfs_driver_sync(const struct lfs_config *cfg) { + (void) cfg; + return 0; +} + +bool mg_lfs_init(size_t size) { + int result = 0; + if (s_lfs_ready) return true; + if (size == 0) return false; +#if LFS_USE_RAM + s_cfg.block_size = MG_LFS_BLOCK_SIZE; + if (s_ram_fs == NULL) s_ram_fs = (uint8_t *) mg_calloc(1, size); + s_fs = s_ram_fs; +#else + if (mg_flash == NULL || mg_flash->secsz == 0 || mg_flash->size < size) return false; + if (mg_flash->write_fn == NULL) return false; + s_cfg.block_size = mg_flash->secsz; + s_fs = (uint8_t *) mg_flash->start + mg_flash->size - size; +#endif + if (s_fs == NULL || s_cfg.block_size == 0 || size % s_cfg.block_size != 0) { + return false; + } + s_cfg.block_count = (lfs_size_t) (size / s_cfg.block_size); + if (s_cfg.block_count == 0) return false; + if (lfs_mount(&s_lfs, &s_cfg) != 0) { + lfs_format(&s_lfs, &s_cfg); + if (lfs_mount(&s_lfs, &s_cfg) != 0) result = -1; + } + s_lfs_ready = result == 0; + return s_lfs_ready; +} + +static struct mg_lfs_fd *find_fd(int fd) { + struct mg_lfs_fd *f; + for (f = s_fds; f != NULL; f = f->next) { + if (f->isopen && f->fd == fd) return f; + } + return NULL; +} + +static struct mg_lfs_fd *open_fd(void) { + struct mg_lfs_fd *f = NULL; + if (s_lfs_ready) { + f = (struct mg_lfs_fd *) mg_calloc(1, sizeof(*f)); + if (f != NULL) { + f->isopen = true; + f->fd = s_next_fd++; + f->next = s_fds; + s_fds = f; + if (s_next_fd < MG_LFS_FD_BASE) s_next_fd = MG_LFS_FD_BASE; + } + } + return f; +} + +static int close_fd(struct mg_lfs_fd *fd) { + struct mg_lfs_fd **f = &s_fds; + while (*f != NULL && *f != fd) f = &(*f)->next; + if (*f == NULL) return -1; + *f = fd->next; + fd->isopen = false; + mg_free(fd); + return 0; +} + +int _open(const char *path, int flags, mode_t mode) { + int err, lfs_flags = 0, fd = -1; + struct mg_lfs_fd *f = open_fd(); + (void) mode; + if (f == NULL) return -1; + fd = f->fd; + if ((flags & 3) == O_RDONLY) lfs_flags |= LFS_O_RDONLY; + if ((flags & 3) == O_WRONLY) lfs_flags |= LFS_O_WRONLY; + if ((flags & 3) == O_RDWR) lfs_flags |= LFS_O_RDWR; + if (flags & O_CREAT) lfs_flags |= LFS_O_CREAT; + if (flags & O_TRUNC) lfs_flags |= LFS_O_TRUNC; + if (flags & O_APPEND) lfs_flags |= LFS_O_APPEND; + err = lfs_file_open(&s_lfs, &f->file, path, lfs_flags); + if (err < 0) close_fd(f), fd = -1; + return fd; +} + +int _close(int fd) { + struct mg_lfs_fd *f = find_fd(fd); + if (fd < 3) return 0; + if (f == NULL) return -1; + lfs_file_close(&s_lfs, &f->file); + close_fd(f); + return 0; +} + +int _write(int fd, char *ptr, int len) { + struct mg_lfs_fd *f = find_fd(fd); + return fd < 3 ? len + : f == NULL ? -1 : lfs_file_write(&s_lfs, &f->file, ptr, len); +} + +int _read(int fd, char *ptr, int len) { + struct mg_lfs_fd *f = find_fd(fd); + return fd < 3 ? 0 : f == NULL ? -1 : lfs_file_read(&s_lfs, &f->file, ptr, len); +} + +int _lseek(int fd, int offset, int whence) { + struct mg_lfs_fd *f = find_fd(fd); + return fd < 3 ? 0 + : f == NULL ? -1 : lfs_file_seek(&s_lfs, &f->file, offset, + whence); +} + +int _rename(const char *oldname, const char *newname) { + return s_lfs_ready ? lfs_rename(&s_lfs, oldname, newname) : -1; +} + +int _unlink_r(void *r, const char *a) { + (void) r; + return s_lfs_ready ? lfs_remove(&s_lfs, a) : -1; +} + +DIR *opendir(const char *name) { + struct mg_lfs_fd *f = open_fd(); + if (f == NULL) return NULL; + if (lfs_dir_open(&s_lfs, &f->dir, name) != 0) { + close_fd(f); + return NULL; + } + return (DIR *) f; +} + +int closedir(DIR *dir) { + struct mg_lfs_fd *f = (struct mg_lfs_fd *) dir; + if (f == NULL || find_fd(f->fd) != f) return -1; + lfs_dir_close(&s_lfs, &f->dir); + return close_fd(f); +} + +struct dirent *readdir(DIR *dir) { + static struct dirent dirent; + struct mg_lfs_fd *f = (struct mg_lfs_fd *) dir; + struct lfs_info info; + if (f == NULL || !f->isopen) return NULL; + if (lfs_dir_read(&s_lfs, &f->dir, &info) < 1) return NULL; + memset(&dirent, 0, sizeof(dirent)); + strncpy(dirent.d_name, info.name, sizeof(dirent.d_name) - 1); + if (info.type == LFS_TYPE_DIR) dirent.d_type |= DT_DIR; + if (info.type == LFS_TYPE_REG) dirent.d_type |= DT_REG; + return &dirent; +} + +int _stat(const char *path, struct stat *st) { + struct lfs_info info; + if (!s_lfs_ready || lfs_stat(&s_lfs, path, &info) != 0) return -1; + st->st_mode = info.type == LFS_TYPE_DIR ? S_IFDIR : S_IFREG; + st->st_size = info.size; + return 0; +} + +int _fstat(int fd, struct stat *st) { + if (fd >= 3 && find_fd(fd) == NULL) return -1; + st->st_mode = S_IFCHR; + return 0; +} + +int mkdir(const char *path, mode_t mode) { + (void) mode; + return s_lfs_ready ? lfs_mkdir(&s_lfs, path) : -1; +} + +#endif + #ifdef MG_ENABLE_LINES #line 1 "src/log.c" #endif @@ -9037,6 +9315,7 @@ bool mg_ota_end(void) { } return false; } +struct mg_flash *mg_flash = &s_mg_flash_ch32v307; #endif #ifdef MG_ENABLE_LINES @@ -9709,7 +9988,7 @@ bool mg_ota_end(void) { } return false; } - +struct mg_flash *mg_flash = &s_mg_flash_imxrt; #endif #ifdef MG_ENABLE_LINES @@ -9923,6 +10202,7 @@ bool mg_ota_end(void) { } return false; } +struct mg_flash *mg_flash = &s_mg_flash_mcxn; #endif #ifdef MG_ENABLE_LINES @@ -10095,6 +10375,7 @@ bool mg_ota_end(void) { } return false; } +struct mg_flash *mg_flash = &s_mg_flash_picosdk; #endif #ifdef MG_ENABLE_LINES @@ -10462,7 +10743,7 @@ bool mg_ota_end(void) { } return false; } - +struct mg_flash *mg_flash = &s_mg_flash_frdm; #endif #ifdef MG_ENABLE_LINES @@ -10697,6 +10978,7 @@ bool mg_ota_end(void) { } return false; } +struct mg_flash *mg_flash = &s_mg_flash_stm32f; #endif #ifdef MG_ENABLE_LINES @@ -10856,6 +11138,7 @@ bool mg_ota_end(void) { *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; return true; } +struct mg_flash *mg_flash = &s_mg_flash_stm32h5; #endif #ifdef MG_ENABLE_LINES @@ -11080,6 +11363,7 @@ bool mg_ota_end(void) { } return false; } +struct mg_flash *mg_flash = &s_mg_flash_stm32h7; #endif #ifdef MG_ENABLE_LINES diff --git a/mongoose.h b/mongoose.h index 1684c72a..9a499ded 100644 --- a/mongoose.h +++ b/mongoose.h @@ -933,6 +933,10 @@ struct timeval { #define MG_ENABLE_FATFS 0 #endif +#ifndef MG_ENABLE_LFS +#define MG_ENABLE_LFS 0 +#endif + #ifndef MG_ENABLE_SSI #define MG_ENABLE_SSI 0 #endif @@ -3283,7 +3287,8 @@ void mg_ota_url_check(struct mg_mgr *mgr, -#if MG_OTA != MG_OTA_NONE && MG_OTA != MG_OTA_CUSTOM + +#if (MG_OTA != MG_OTA_NONE && MG_OTA != MG_OTA_CUSTOM) || MG_ENABLE_LFS struct mg_flash { void *start; // Address at which flash starts @@ -3294,6 +3299,9 @@ struct mg_flash { bool (*swap_fn)(void); // Swap partitions }; +extern struct mg_flash *mg_flash; +bool mg_lfs_init(size_t size); + bool mg_ota_flash_begin(size_t new_firmware_size, struct mg_flash *flash); bool mg_ota_flash_write(const void *buf, size_t len, struct mg_flash *flash); bool mg_ota_flash_end(struct mg_flash *flash); diff --git a/src/config.h b/src/config.h index b66688a4..b38a4b70 100644 --- a/src/config.h +++ b/src/config.h @@ -48,6 +48,10 @@ #define MG_ENABLE_FATFS 0 #endif +#ifndef MG_ENABLE_LFS +#define MG_ENABLE_LFS 0 +#endif + #ifndef MG_ENABLE_SSI #define MG_ENABLE_SSI 0 #endif diff --git a/src/flash.h b/src/flash.h index 9fa04a4c..8b42bf73 100644 --- a/src/flash.h +++ b/src/flash.h @@ -1,7 +1,8 @@ #include "arch.h" +#include "config.h" #include "ota.h" -#if MG_OTA != MG_OTA_NONE && MG_OTA != MG_OTA_CUSTOM +#if (MG_OTA != MG_OTA_NONE && MG_OTA != MG_OTA_CUSTOM) || MG_ENABLE_LFS struct mg_flash { void *start; // Address at which flash starts @@ -12,6 +13,9 @@ struct mg_flash { bool (*swap_fn)(void); // Swap partitions }; +extern struct mg_flash *mg_flash; +bool mg_lfs_init(size_t size); + bool mg_ota_flash_begin(size_t new_firmware_size, struct mg_flash *flash); bool mg_ota_flash_write(const void *buf, size_t len, struct mg_flash *flash); bool mg_ota_flash_end(struct mg_flash *flash); diff --git a/src/lfs.c b/src/lfs.c new file mode 100644 index 00000000..e4b2c44f --- /dev/null +++ b/src/lfs.c @@ -0,0 +1,274 @@ +// Copyright (c) 2023 Cesanta Software Limited +// SPDX-License-Identifier: GPL-2.0-only or commercial + +#include "flash.h" +#include "util.h" + +#if MG_ENABLE_LFS + +#include +#include +#include +#include +#include + +#ifndef LFS_USE_RAM +#define LFS_USE_RAM 0 // If 1, use RAM FS. If 0, use flash +#endif + +#ifndef MG_LFS_BUF_SIZE +#define MG_LFS_BUF_SIZE 64 // Buffer size used for reads, writes, and cache +#endif + +#ifndef MG_LFS_FD_BASE +#define MG_LFS_FD_BASE 12000 // Starting file descriptor number +#endif + +#if LFS_USE_RAM && !defined(MG_LFS_BLOCK_SIZE) +#define MG_LFS_BLOCK_SIZE 8192 +#endif + +#ifndef DT_DIR +#define DT_DIR 4 +#endif +#ifndef DT_REG +#define DT_REG 8 +#endif + +typedef struct mg_lfs_fd DIR; +struct dirent { + char d_name[LFS_NAME_MAX + 1]; + unsigned char d_type; +}; + +struct mg_lfs_fd { + struct mg_lfs_fd *next; + bool isopen; + lfs_file_t file; + lfs_dir_t dir; + int fd; +}; + +static lfs_t s_lfs; +static struct mg_lfs_fd *s_fds; +static uint8_t *s_fs; +static int s_next_fd = MG_LFS_FD_BASE; +static bool s_lfs_ready; +#if LFS_USE_RAM +static uint8_t *s_ram_fs; +#endif + +static int lfs_driver_read(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, void *buf, lfs_size_t len); +static int lfs_driver_prog(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, const void *buf, lfs_size_t len); +static int lfs_driver_erase(const struct lfs_config *cfg, lfs_block_t block); +static int lfs_driver_sync(const struct lfs_config *cfg); + +static struct lfs_config s_cfg = { + .read = lfs_driver_read, + .prog = lfs_driver_prog, + .erase = lfs_driver_erase, + .sync = lfs_driver_sync, + .block_cycles = 200, + .cache_size = MG_LFS_BUF_SIZE, + .read_size = MG_LFS_BUF_SIZE, + .prog_size = MG_LFS_BUF_SIZE, + .lookahead_size = MG_LFS_BUF_SIZE / 8, +}; + +static bool flash_write_buf(void *addr, const void *buf, size_t len) { +#if LFS_USE_RAM + memmove(addr, buf, len); + return true; +#else + return mg_flash != NULL && mg_flash->write_fn(addr, buf, len); +#endif +} + +static int lfs_driver_read(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, void *buf, lfs_size_t len) { + memmove(buf, &s_fs[block * cfg->block_size + off], len); + return 0; +} + +static int lfs_driver_prog(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, const void *buf, lfs_size_t len) { + uint8_t *dst = &s_fs[block * cfg->block_size + off]; + if (!flash_write_buf(dst, buf, len)) return LFS_ERR_IO; + return 0; +} + +static int lfs_driver_erase(const struct lfs_config *cfg, lfs_block_t block) { + (void) cfg, (void) block; + return 0; +} + +static int lfs_driver_sync(const struct lfs_config *cfg) { + (void) cfg; + return 0; +} + +bool mg_lfs_init(size_t size) { + int result = 0; + if (s_lfs_ready) return true; + if (size == 0) return false; +#if LFS_USE_RAM + s_cfg.block_size = MG_LFS_BLOCK_SIZE; + if (s_ram_fs == NULL) s_ram_fs = (uint8_t *) mg_calloc(1, size); + s_fs = s_ram_fs; +#else + if (mg_flash == NULL || mg_flash->secsz == 0 || mg_flash->size < size) return false; + if (mg_flash->write_fn == NULL) return false; + s_cfg.block_size = mg_flash->secsz; + s_fs = (uint8_t *) mg_flash->start + mg_flash->size - size; +#endif + if (s_fs == NULL || s_cfg.block_size == 0 || size % s_cfg.block_size != 0) { + return false; + } + s_cfg.block_count = (lfs_size_t) (size / s_cfg.block_size); + if (s_cfg.block_count == 0) return false; + if (lfs_mount(&s_lfs, &s_cfg) != 0) { + lfs_format(&s_lfs, &s_cfg); + if (lfs_mount(&s_lfs, &s_cfg) != 0) result = -1; + } + s_lfs_ready = result == 0; + return s_lfs_ready; +} + +static struct mg_lfs_fd *find_fd(int fd) { + struct mg_lfs_fd *f; + for (f = s_fds; f != NULL; f = f->next) { + if (f->isopen && f->fd == fd) return f; + } + return NULL; +} + +static struct mg_lfs_fd *open_fd(void) { + struct mg_lfs_fd *f = NULL; + if (s_lfs_ready) { + f = (struct mg_lfs_fd *) mg_calloc(1, sizeof(*f)); + if (f != NULL) { + f->isopen = true; + f->fd = s_next_fd++; + f->next = s_fds; + s_fds = f; + if (s_next_fd < MG_LFS_FD_BASE) s_next_fd = MG_LFS_FD_BASE; + } + } + return f; +} + +static int close_fd(struct mg_lfs_fd *fd) { + struct mg_lfs_fd **f = &s_fds; + while (*f != NULL && *f != fd) f = &(*f)->next; + if (*f == NULL) return -1; + *f = fd->next; + fd->isopen = false; + mg_free(fd); + return 0; +} + +int _open(const char *path, int flags, mode_t mode) { + int err, lfs_flags = 0, fd = -1; + struct mg_lfs_fd *f = open_fd(); + (void) mode; + if (f == NULL) return -1; + fd = f->fd; + if ((flags & 3) == O_RDONLY) lfs_flags |= LFS_O_RDONLY; + if ((flags & 3) == O_WRONLY) lfs_flags |= LFS_O_WRONLY; + if ((flags & 3) == O_RDWR) lfs_flags |= LFS_O_RDWR; + if (flags & O_CREAT) lfs_flags |= LFS_O_CREAT; + if (flags & O_TRUNC) lfs_flags |= LFS_O_TRUNC; + if (flags & O_APPEND) lfs_flags |= LFS_O_APPEND; + err = lfs_file_open(&s_lfs, &f->file, path, lfs_flags); + if (err < 0) close_fd(f), fd = -1; + return fd; +} + +int _close(int fd) { + struct mg_lfs_fd *f = find_fd(fd); + if (fd < 3) return 0; + if (f == NULL) return -1; + lfs_file_close(&s_lfs, &f->file); + close_fd(f); + return 0; +} + +int _write(int fd, char *ptr, int len) { + struct mg_lfs_fd *f = find_fd(fd); + return fd < 3 ? len + : f == NULL ? -1 : lfs_file_write(&s_lfs, &f->file, ptr, len); +} + +int _read(int fd, char *ptr, int len) { + struct mg_lfs_fd *f = find_fd(fd); + return fd < 3 ? 0 : f == NULL ? -1 : lfs_file_read(&s_lfs, &f->file, ptr, len); +} + +int _lseek(int fd, int offset, int whence) { + struct mg_lfs_fd *f = find_fd(fd); + return fd < 3 ? 0 + : f == NULL ? -1 : lfs_file_seek(&s_lfs, &f->file, offset, + whence); +} + +int _rename(const char *oldname, const char *newname) { + return s_lfs_ready ? lfs_rename(&s_lfs, oldname, newname) : -1; +} + +int _unlink_r(void *r, const char *a) { + (void) r; + return s_lfs_ready ? lfs_remove(&s_lfs, a) : -1; +} + +DIR *opendir(const char *name) { + struct mg_lfs_fd *f = open_fd(); + if (f == NULL) return NULL; + if (lfs_dir_open(&s_lfs, &f->dir, name) != 0) { + close_fd(f); + return NULL; + } + return (DIR *) f; +} + +int closedir(DIR *dir) { + struct mg_lfs_fd *f = (struct mg_lfs_fd *) dir; + if (f == NULL || find_fd(f->fd) != f) return -1; + lfs_dir_close(&s_lfs, &f->dir); + return close_fd(f); +} + +struct dirent *readdir(DIR *dir) { + static struct dirent dirent; + struct mg_lfs_fd *f = (struct mg_lfs_fd *) dir; + struct lfs_info info; + if (f == NULL || !f->isopen) return NULL; + if (lfs_dir_read(&s_lfs, &f->dir, &info) < 1) return NULL; + memset(&dirent, 0, sizeof(dirent)); + strncpy(dirent.d_name, info.name, sizeof(dirent.d_name) - 1); + if (info.type == LFS_TYPE_DIR) dirent.d_type |= DT_DIR; + if (info.type == LFS_TYPE_REG) dirent.d_type |= DT_REG; + return &dirent; +} + +int _stat(const char *path, struct stat *st) { + struct lfs_info info; + if (!s_lfs_ready || lfs_stat(&s_lfs, path, &info) != 0) return -1; + st->st_mode = info.type == LFS_TYPE_DIR ? S_IFDIR : S_IFREG; + st->st_size = info.size; + return 0; +} + +int _fstat(int fd, struct stat *st) { + if (fd >= 3 && find_fd(fd) == NULL) return -1; + st->st_mode = S_IFCHR; + return 0; +} + +int mkdir(const char *path, mode_t mode) { + (void) mode; + return s_lfs_ready ? lfs_mkdir(&s_lfs, path) : -1; +} + +#endif diff --git a/src/ota_ch32v307.c b/src/ota_ch32v307.c index d9347ac4..0093c680 100644 --- a/src/ota_ch32v307.c +++ b/src/ota_ch32v307.c @@ -109,4 +109,5 @@ bool mg_ota_end(void) { } return false; } +struct mg_flash *mg_flash = &s_mg_flash_ch32v307; #endif diff --git a/src/ota_imxrt.c b/src/ota_imxrt.c index a1ec3d9a..49db3e37 100644 --- a/src/ota_imxrt.c +++ b/src/ota_imxrt.c @@ -589,5 +589,5 @@ bool mg_ota_end(void) { } return false; } - +struct mg_flash *mg_flash = &s_mg_flash_imxrt; #endif diff --git a/src/ota_mcxn.c b/src/ota_mcxn.c index acd041bb..110f2718 100644 --- a/src/ota_mcxn.c +++ b/src/ota_mcxn.c @@ -206,4 +206,5 @@ bool mg_ota_end(void) { } return false; } +struct mg_flash *mg_flash = &s_mg_flash_mcxn; #endif diff --git a/src/ota_picosdk.c b/src/ota_picosdk.c index b6900ccf..2460b646 100644 --- a/src/ota_picosdk.c +++ b/src/ota_picosdk.c @@ -165,4 +165,5 @@ bool mg_ota_end(void) { } return false; } +struct mg_flash *mg_flash = &s_mg_flash_picosdk; #endif diff --git a/src/ota_rw612.c b/src/ota_rw612.c index ee441d30..b0bae478 100644 --- a/src/ota_rw612.c +++ b/src/ota_rw612.c @@ -360,5 +360,5 @@ bool mg_ota_end(void) { } return false; } - +struct mg_flash *mg_flash = &s_mg_flash_frdm; #endif diff --git a/src/ota_stm32f.c b/src/ota_stm32f.c index 64025091..2e1aacd5 100644 --- a/src/ota_stm32f.c +++ b/src/ota_stm32f.c @@ -227,4 +227,5 @@ bool mg_ota_end(void) { } return false; } +struct mg_flash *mg_flash = &s_mg_flash_stm32f; #endif diff --git a/src/ota_stm32h5.c b/src/ota_stm32h5.c index f03a2e4e..6977293a 100644 --- a/src/ota_stm32h5.c +++ b/src/ota_stm32h5.c @@ -152,4 +152,5 @@ bool mg_ota_end(void) { *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; return true; } +struct mg_flash *mg_flash = &s_mg_flash_stm32h5; #endif diff --git a/src/ota_stm32h7.c b/src/ota_stm32h7.c index d70428c2..6ea5c28c 100644 --- a/src/ota_stm32h7.c +++ b/src/ota_stm32h7.c @@ -217,4 +217,5 @@ bool mg_ota_end(void) { } return false; } +struct mg_flash *mg_flash = &s_mg_flash_stm32h7; #endif diff --git a/tutorials/stm32/nucleo-h563zi/minimal-lfs/Makefile b/tutorials/stm32/nucleo-h563zi/minimal-lfs/Makefile new file mode 100644 index 00000000..2aa83daa --- /dev/null +++ b/tutorials/stm32/nucleo-h563zi/minimal-lfs/Makefile @@ -0,0 +1,66 @@ +# Copyright (c) 2026 Cesanta Software Limited +# Environment setup: https://mongoose.ws/docs/getting-started/build-environment/ + +CFLAGS = -W -Wall -Wextra -Wundef -Wshadow -Wdouble-promotion +CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion +CFLAGS += -g3 -Os -ffunction-sections -fdata-sections +CFLAGS += -I. -Icmsis_core/CMSIS/Core/Include -Icmsis_h5/Include -Imongoose +CFLAGS += -mcpu=cortex-m33 -mthumb -mfpu=fpv5-sp-d16 -mfloat-abi=hard $(CFLAGS_EXTRA) +LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs nosys.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map + +SOURCES = main.c hal.c +SOURCES += littlefs/lfs.c littlefs/lfs_util.c +SOURCES += cmsis_h5/Source/Templates/gcc/startup_stm32h563xx.s +SOURCES += mongoose/mongoose.c + +# Disabled for littlefs/lfs.c +CFLAGS += -Wno-conversion -Wno-shadow +CFLAGS += -DMG_ENABLE_LFS=1 + +all build example: firmware.bin + +mongoose/mongoose.c mongoose/mongoose.h: + cp ../../../../mongoose.[ch] mongoose/ + +firmware.elf: cmsis_core cmsis_h5 littlefs hal.h link.ld Makefile $(SOURCES) mongoose/mongoose.h + arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@ + +firmware.bin: firmware.elf + arm-none-eabi-objcopy -O binary $< $@ + @echo + @echo "To flash, run 'make flash', or use STM32CubeProgrammer" + +flash: firmware.bin + STM32_Programmer_CLI -c port=swd mode=UR -w firmware.elf -hardRst + +gdb: firmware.elf + arm-none-eabi-gdb -q $^ -ex='set confirm off' -ex 'target remote :61234' + +# Directory where STM32_Programmer_CLI binary lives +CUBEPROG_DIR ?= $(dir $(shell which STM32_Programmer_CLI)) +gdb_server: + ST-LINK_gdbserver --port-number 61234 --log-level 1 --swd --verify --stm32cubeprogrammer-path $(CUBEPROG_DIR) --apid 1 --initialize-reset + + +cmsis_core: + git clone -q -c advice.detachedHead=false --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@ + +cmsis_h5: + git clone -q -c advice.detachedHead=false --depth 1 -b v1.6.0 https://github.com/STMicroelectronics/cmsis-device-h5 $@ + +littlefs/lfs.c littlefs/lfs_util.c: littlefs +littlefs: + git clone -q -c advice.detachedHead=false --depth 1 -b v2.11.3 https://github.com/littlefs-project/littlefs $@ + +clean: + rm -rf firmware.* cmsis_* littlefs/ mongoose/mongoose.* + +# Automated remote test. Requires env variable VCON_API_KEY set. See https://vcon.io/automated-firmware-tests/ +DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/11 +update: firmware.bin + curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/ota --data-binary @$< + +test update: CFLAGS_EXTRA = -DUART_DEBUG=USART1 +test: update + curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=15 | tee /tmp/output.txt + grep 'READY, IP:' /tmp/output.txt # Check for network init diff --git a/tutorials/stm32/nucleo-h563zi/minimal-lfs/hal.c b/tutorials/stm32/nucleo-h563zi/minimal-lfs/hal.c new file mode 100644 index 00000000..e519bff3 --- /dev/null +++ b/tutorials/stm32/nucleo-h563zi/minimal-lfs/hal.c @@ -0,0 +1,125 @@ +// Copyright (c) 2026 Cesanta Software Limited +// All rights reserved + +#include "hal.h" + +bool hal_timer_expired(volatile uint64_t *t, uint64_t period, uint64_t now) { + uint64_t diff = now - *t; // Wrap-safe elapsed time since last expiry + if (period == 0) return false; // Avoid division by zero + if (diff < period) return false; // Period has not elapsed yet + *t += (diff / period) * period; // Preserve cadence, skip missed periods + return true; +} + +static volatile uint64_t s_ticks; // Milliseconds since boot +void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms + s_ticks++; +} + +uint64_t hal_get_tick(void) { + return s_ticks; +}; + +uint32_t SystemCoreClock = 160000000; +void SystemInit(void) { // Called automatically by startup code + hal_system_init(); // Enable FPU + hal_clock_init(); + SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms +} + +void ExitRun0Mode(void) { +} + +struct stat; +__attribute__((weak)) int _fstat(int fd, struct stat *st) { + (void) fd, (void) st; + return -1; +} + +extern unsigned char _end[]; // End of data section, start of heap. See link.ld +static unsigned char *s_current_heap_end = _end; + +size_t hal_ram_used(void) { + return (size_t) (s_current_heap_end - _end); +} + +size_t hal_ram_free(void) { + unsigned char endofstack; + return (size_t) (&endofstack - s_current_heap_end); +} + +void *_sbrk(int incr) { + unsigned char *prev_heap; + unsigned char *heap_end = (unsigned char *) ((size_t) &heap_end - 256); + prev_heap = s_current_heap_end; + // Check how much space we got from the heap end to the stack end + if (s_current_heap_end + incr > heap_end) return (void *) -1; + s_current_heap_end += incr; + return prev_heap; +} + +__attribute__((weak)) int _open(const char *path) { + (void) path; + return -1; +} + +__attribute__((weak)) int _close(int fd) { + (void) fd; + return -1; +} + +__attribute__((weak)) int _isatty(int fd) { + (void) fd; + return 1; +} + +__attribute__((weak)) int _lseek(int fd, int ptr, int dir) { + (void) fd, (void) ptr, (void) dir; + return 0; +} + +__attribute__((weak)) void _exit(int status) { + (void) status; + for (;;) asm volatile("BKPT #0"); +} + +__attribute__((weak)) void _kill(int pid, int sig) { + (void) pid, (void) sig; +} + +__attribute__((weak)) int _getpid(void) { + return -1; +} + +__attribute__((weak)) int _write(int fd, char *ptr, int len) { + (void) fd, (void) ptr, (void) len; + return -1; +} + +__attribute__((weak)) int _read(int fd, char *ptr, int len) { + (void) fd, (void) ptr, (void) len; + return -1; +} + +__attribute__((weak)) int _link(const char *a, const char *b) { + (void) a, (void) b; + return -1; +} + +__attribute__((weak)) int _unlink(const char *a) { + (void) a; + return -1; +} + +__attribute__((weak)) int _stat(const char *path, struct stat *st) { + (void) path, (void) st; + return -1; +} + +__attribute__((weak)) int mkdir(const char *path, int mode) { + (void) path, (void) mode; + return -1; +} + +__attribute__((weak)) void _init(void) { +} diff --git a/tutorials/stm32/nucleo-h563zi/minimal-lfs/hal.h b/tutorials/stm32/nucleo-h563zi/minimal-lfs/hal.h new file mode 100644 index 00000000..d66a7620 --- /dev/null +++ b/tutorials/stm32/nucleo-h563zi/minimal-lfs/hal.h @@ -0,0 +1,212 @@ +// Copyright (c) 2022-2026 Cesanta Software Limited +// All rights reserved +// +// Datasheet: RM0481, devboard manual: UM3115 +// https://www.st.com/resource/en/reference_manual/rm0481-stm32h563h573-and-stm32h562-armbased-32bit-mcus-stmicroelectronics.pdf +// Alternate functions: https://www.st.com/resource/en/datasheet/stm32h563vi.pdf + +#pragma once + +#include + +#include +#include +#include +#include +#include + +void hal_init(void); +size_t hal_ram_free(void); +size_t hal_ram_used(void); +bool hal_timer_expired(volatile uint64_t *t, uint64_t prd, uint64_t now); +uint64_t hal_get_tick(void); + +#define BIT(x) (1UL << (x)) +#define CLRSET(R, CLEARMASK, SETMASK) (R) = ((R) & ~(CLEARMASK)) | (SETMASK) +#define PIN(bank, num) ((((bank) - 'A') << 8) | (num)) +#define PINNO(pin) (pin & 255) +#define PINBANK(pin) (pin >> 8) + +#define HAL_ETH_PINS \ + PIN('A', 1), /* ETH_REF_CLK */ \ + PIN('A', 2), /* ETH_MDIO */ \ + PIN('A', 7), /* ETH_CRS_DV */ \ + PIN('B', 15), /* ETH_TXD1 */ \ + PIN('C', 1), /* ETH_MDC */ \ + PIN('C', 4), /* ETH_RXD0 */ \ + PIN('C', 5), /* ETH_RXD1 */ \ + PIN('G', 11), /* ETH_TX_EN */ \ + PIN('G', 13) /* ETH_TXD0 */ + +// System clock (11.4, Figure 48; 11.4.5, Figure 51; 11.4.8 +// SYS_FREQUENCY <= 250 MHz; (CLOCK_FREQUENCY / HPRE) ; hclk = SYS_FREQUENCY +// APB clocks <= 250 MHz. Configure flash latency (WS) in accordance to hclk +// freq (7.3.4, Table 37) +enum { + HAL_HPRE = 7, // register value, divisor value = BIT(value - 7) = / 1 + HAL_PPRE1 = 4, // register values, divisor value = BIT(value - 3) = / 2 + HAL_PPRE2 = 4, + HAL_PPRE3 = 4, +}; +// Make sure your chip package uses the internal LDO, otherwise set PLL1_N = 200 +enum { PLL1_HSI = 64, PLL1_M = 32, PLL1_N = 250, PLL1_P = 2 }; +#define SYS_FREQUENCY \ + ((PLL1_HSI * PLL1_N / PLL1_M / PLL1_P / (BIT(HAL_HPRE - 7))) * 1000000) +#define AHB_FREQUENCY SYS_FREQUENCY +#define APB2_FREQUENCY (AHB_FREQUENCY / (BIT(HAL_PPRE2 - 3))) +#define APB1_FREQUENCY (AHB_FREQUENCY / (BIT(HAL_PPRE1 - 3))) + +static inline void spin(volatile uint32_t n) { + while (n--) (void) 0; +} + +enum { HAL_GPIO_MODE_INPUT, HAL_GPIO_MODE_OUTPUT, HAL_GPIO_MODE_AF, HAL_GPIO_MODE_ANALOG }; +enum { HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_OTYPE_OPEN_DRAIN }; +enum { HAL_GPIO_SPEED_LOW, HAL_GPIO_SPEED_MEDIUM, HAL_GPIO_SPEED_HIGH, HAL_GPIO_SPEED_INSANE }; +enum { HAL_GPIO_PULL_NONE, HAL_GPIO_PULL_UP, HAL_GPIO_PULL_DOWN }; + +#define HAL_GPIO(N) ((GPIO_TypeDef *) ((GPIOA_BASE_NS) + 0x400 * (N))) + +static GPIO_TypeDef *hal_gpio_bank(uint16_t pin) { + return HAL_GPIO(PINBANK(pin)); +} +static inline void hal_gpio_toggle(uint16_t pin) { + GPIO_TypeDef *gpio = hal_gpio_bank(pin); + uint32_t mask = BIT(PINNO(pin)); + gpio->BSRR = mask << (gpio->ODR & mask ? 16 : 0); +} +static inline int hal_gpio_read(uint16_t pin) { + return hal_gpio_bank(pin)->IDR & BIT(PINNO(pin)) ? 1 : 0; +} +static inline void hal_gpio_write(uint16_t pin, bool val) { + GPIO_TypeDef *gpio = hal_gpio_bank(pin); + gpio->BSRR = BIT(PINNO(pin)) << (val ? 0 : 16); +} +static inline void hal_gpio_init(uint16_t pin, uint8_t mode, uint8_t type, + uint8_t speed, uint8_t pull, uint8_t af) { + GPIO_TypeDef *gpio = hal_gpio_bank(pin); + uint8_t n = (uint8_t) (PINNO(pin)); + RCC->AHB2ENR |= BIT(PINBANK(pin)); // Enable GPIO clock + CLRSET(gpio->OTYPER, 1UL << n, ((uint32_t) type) << n); + CLRSET(gpio->OSPEEDR, 3UL << (n * 2), ((uint32_t) speed) << (n * 2)); + CLRSET(gpio->PUPDR, 3UL << (n * 2), ((uint32_t) pull) << (n * 2)); + CLRSET(gpio->AFR[n >> 3], 15UL << ((n & 7) * 4), + ((uint32_t) af) << ((n & 7) * 4)); + CLRSET(gpio->MODER, 3UL << (n * 2), ((uint32_t) mode) << (n * 2)); +} +static inline void hal_gpio_input(uint16_t pin) { + hal_gpio_init(pin, HAL_GPIO_MODE_INPUT, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH, + HAL_GPIO_PULL_NONE, 0); +} +static inline void hal_gpio_output(uint16_t pin) { + hal_gpio_init(pin, HAL_GPIO_MODE_OUTPUT, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH, + HAL_GPIO_PULL_NONE, 0); +} + +static inline bool hal_uart_init(USART_TypeDef *uart, uint16_t tx_pin, + uint16_t rx_pin, unsigned long baud) { + uint32_t freq = 0; // Bus frequency. UART1 is on APB2, rest on APB1 + + if (uart == USART1) { + freq = APB2_FREQUENCY, RCC->APB2ENR |= RCC_APB2ENR_USART1EN; + } else if (uart == USART2) { + freq = APB1_FREQUENCY, RCC->APB1LENR |= RCC_APB1LENR_USART2EN; + } else if (uart == USART3) { + freq = APB1_FREQUENCY, RCC->APB1LENR |= RCC_APB1LENR_USART3EN; + } else { + return false; + } + hal_gpio_init(tx_pin, HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH, 0, 7U); + hal_gpio_init(rx_pin, HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH, 0, 7U); + uart->CR1 = 0; // Disable UART + uart->BRR = freq / baud; // Set baud rate + uart->CR1 = USART_CR1_RE | USART_CR1_TE; // Set mode to TX & RX + uart->CR1 |= USART_CR1_UE; // Enable UART + return true; +} +static inline void hal_uart_write_byte(USART_TypeDef *uart, uint8_t byte) { + uart->TDR = byte; + while ((uart->ISR & BIT(7)) == 0) spin(1); +} +static inline void hal_uart_write_buf(USART_TypeDef *uart, char *buf, size_t len) { + while (len-- > 0) hal_uart_write_byte(uart, *(uint8_t *) buf++); +} +static inline int hal_uart_read_ready(USART_TypeDef *uart) { + return uart->ISR & BIT(5); // If RXNE bit is set, data is ready +} +static inline uint8_t hal_uart_read_byte(USART_TypeDef *uart) { + return (uint8_t) (uart->RDR & 255); +} + +static inline void hal_rng_init(void) { + RCC->CCIPR5 |= RCC_CCIPR5_RNGSEL_0; // RNG clock source pll1_q_ck + RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; // Enable RNG clock + RNG->CR |= RNG_CR_RNGEN; // Enable RNG +} +static inline uint32_t hal_rng_read(void) { + while ((RNG->SR & RNG_SR_DRDY) == 0) spin(1); + return RNG->DR; +} + +static inline bool hal_ldo_is_on(void) { + return (PWR->SCCR & PWR_SCCR_LDOEN) == PWR_SCCR_LDOEN; +} + +// Hw pull-ups on PHY RXD0,1,DV to enable autonegotiation +static inline void hal_ethernet_init(void) { + // Initialise Ethernet. Enable MAC GPIO pins, see UM3115 section 10.7 + uint16_t pins[] = {HAL_ETH_PINS}; + for (size_t i = 0; i < sizeof(pins) / sizeof(pins[0]); i++) { + hal_gpio_init(pins[i], HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_INSANE, + HAL_GPIO_PULL_NONE, 11); // 11 is the Ethernet function + } + NVIC_EnableIRQ(ETH_IRQn); // Setup Ethernet IRQ handler + RCC->APB3ENR |= RCC_APB3ENR_SBSEN; // Enable SBS clock + CLRSET(SBS->PMCR, SBS_PMCR_ETH_SEL_PHY, SBS_PMCR_ETH_SEL_PHY_2); // RMII + RCC->AHB1ENR |= RCC_AHB1ENR_ETHEN | RCC_AHB1ENR_ETHRXEN | RCC_AHB1ENR_ETHTXEN; +} + +#define UUID ((uint32_t *) UID_BASE) // Unique 96-bit chip ID. TRM 59.1 + +// Helper macro for MAC generation, byte reads not allowed +#define GENERATE_LOCALLY_ADMINISTERED_MAC() \ + { \ + 2, UUID[0] & 255, (UUID[0] >> 10) & 255, (UUID[0] >> 19) & 255, \ + UUID[1] & 255, UUID[2] & 255 \ + } + +static inline void hal_system_init(void) { + SCB->CPACR |= ((3UL << 20U) | (3UL << 22U)); // Enable FPU + __DSB(); + __ISB(); +} + +static inline void hal_clock_init(void) { + // Set flash latency. RM0481, section 7.11.1, section 7.3.4 table 37 + CLRSET(FLASH->ACR, (FLASH_ACR_WRHIGHFREQ_Msk | FLASH_ACR_LATENCY_Msk), + FLASH_ACR_LATENCY_5WS | FLASH_ACR_WRHIGHFREQ_1); + + if (hal_ldo_is_on()) { + PWR->VOSCR = PWR_VOSCR_VOS_0 | PWR_VOSCR_VOS_1; // Select VOS0 + } else { + PWR->VOSCR = PWR_VOSCR_VOS_1; // Select VOS1 + } + uint32_t f = PWR->VOSCR; // fake read to wait for bus clocking + while ((PWR->VOSSR & PWR_VOSSR_ACTVOSRDY) == 0) spin(1); + (void) f; + RCC->CR = RCC_CR_HSION; // Clear HSI clock divisor + while ((RCC->CR & RCC_CR_HSIRDY) == 0) spin(1); // Wait until done + RCC->CFGR2 = (HAL_PPRE3 << 12) | (HAL_PPRE2 << 8) | (HAL_PPRE1 << 4) | (HAL_HPRE << 0); + RCC->PLL1DIVR = + ((PLL1_P - 1) << 9) | ((PLL1_N - 1) << 0); // Set PLL1_P PLL1_N + // Enable P and Q divider outputs; set PLL1_M, select HSI as source, + // !PLL1VCOSEL, PLL1RGE=0 + RCC->PLL1CFGR = + RCC_PLL1CFGR_PLL1QEN | RCC_PLL1CFGR_PLL1PEN | (PLL1_M << 8) | (1 << 0); + RCC->CR |= RCC_CR_PLL1ON; // Enable PLL1 + while ((RCC->CR & RCC_CR_PLL1RDY) == 0) spin(1); // Wait until done + RCC->CFGR1 |= (3 << 0); // Set clock source to PLL1 + while ((RCC->CFGR1 & (7 << 3)) != (3 << 3)) spin(1); // Wait until done + + SystemCoreClock = SYS_FREQUENCY; +} diff --git a/tutorials/stm32/nucleo-h563zi/minimal-lfs/link.ld b/tutorials/stm32/nucleo-h563zi/minimal-lfs/link.ld new file mode 100644 index 00000000..fac73050 --- /dev/null +++ b/tutorials/stm32/nucleo-h563zi/minimal-lfs/link.ld @@ -0,0 +1,31 @@ +ENTRY(Reset_Handler); +MEMORY { + flash(rx) : ORIGIN = 0x08000000, LENGTH = 2048k + sram(rwx) : ORIGIN = 0x20000000, LENGTH = 640k +} +_estack = ORIGIN(sram) + LENGTH(sram); /* End of RAM. stack points here */ +_sstack = _estack - 4098; + +SECTIONS { + .vectors : { KEEP(*(.isr_vector)) } > flash + .text : { *(.text* .text.*) } > flash + .rodata : { *(.rodata*) } > flash + + .data : { + _sdata = .; + *(.first_data) + *(.ram) + *(.data SORT(.data.*)) + _edata = .; + } > sram AT > flash + _sidata = LOADADDR(.data); + + .bss : { + _sbss = .; + *(.bss SORT(.bss.*) COMMON) + _ebss = .; + } > sram + + . = ALIGN(8); + _end = .; +} diff --git a/tutorials/stm32/nucleo-h563zi/minimal-lfs/main.c b/tutorials/stm32/nucleo-h563zi/minimal-lfs/main.c new file mode 100644 index 00000000..8a810807 --- /dev/null +++ b/tutorials/stm32/nucleo-h563zi/minimal-lfs/main.c @@ -0,0 +1,89 @@ +// Copyright (c) 2026 Cesanta Software Limited +// All rights reserved + +#include "hal.h" +#include "mongoose.h" + +#ifndef UART_DEBUG +#define UART_DEBUG USART3 +#define UART_DEBUG_TX_PIN PIN('D', 8) +#define UART_DEBUG_RX_PIN PIN('D', 9) +#else +#define UART_DEBUG_TX_PIN PIN('A', 9) +#define UART_DEBUG_RX_PIN PIN('A', 10) +#endif + +#define LED_1 PIN('B', 0) // On-board LED pin (green) +#define LED_2 PIN('F', 4) // On-board LED pin (yellow) +#define LED_3 PIN('G', 4) // On-board LED pin (red) + +#define LFS_SIZE (64 * 1024) + +static void blink_task(void) { + static uint64_t blink_timer = 0; + if (hal_timer_expired(&blink_timer, 500, hal_get_tick())) { + hal_gpio_toggle(LED_1); + } +} + +uint64_t mg_millis(void) { + return hal_get_tick(); +} + +bool mg_random(void *buf, size_t len) { + for (size_t n = 0; n < len; n += sizeof(uint32_t)) { + uint32_t r = hal_rng_read(); + memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r)); + } + return true; +} + +static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { + if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { + mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); + } else { + mg_http_reply(c, 200, "", "Hi from Mongoose, tick %llu\n", hal_get_tick()); + } + } +} + +static void log_fn(char ch, void *param) { + hal_uart_write_buf(param, &ch, 1); +} + +int main(void) { + hal_uart_init(UART_DEBUG, UART_DEBUG_TX_PIN, UART_DEBUG_RX_PIN, 115200); + mg_log_set_fn(log_fn, UART_DEBUG); + hal_rng_init(); + hal_gpio_output(LED_1); + hal_gpio_output(LED_2); + hal_gpio_output(LED_3); + + hal_ethernet_init(); + + // Start a minimal web server + struct mg_mgr mgr; + mg_mgr_init(&mgr); + mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + + // Initialise LFS, which enables stdio fopen/fwrite/... + if (mg_lfs_init(LFS_SIZE)) { + char buf[20] = ""; + FILE *fp = fopen("a.txt", "r"); + if (fp != NULL) fread(buf, 1, sizeof(buf) - 1, fp), fclose(fp); + MG_INFO(("BOOT COUNT: %s", buf)); + fp = fopen("a.txt", "w+"); + if (fp != NULL) fprintf(fp, "%d", atoi(buf) + 1), fclose(fp); + } else { + MG_ERROR(("LFS init failed")); + } + + for (;;) { + mg_mgr_poll(&mgr, 0); + blink_task(); + } + + return 0; +} diff --git a/tutorials/stm32/nucleo-h563zi/minimal-lfs/mongoose/mongoose_config.h b/tutorials/stm32/nucleo-h563zi/minimal-lfs/mongoose/mongoose_config.h new file mode 100644 index 00000000..64bb90c6 --- /dev/null +++ b/tutorials/stm32/nucleo-h563zi/minimal-lfs/mongoose/mongoose_config.h @@ -0,0 +1,45 @@ +#pragma once + +// See https://mongoose.ws/documentation/#build-options +#define MG_ARCH MG_ARCH_ARMGCC +#define MG_OTA MG_OTA_STM32H5 +#define MG_TLS MG_TLS_BUILTIN +#define MG_IO_SIZE 2048 + +#define MG_ENABLE_POSIX_FS 1 +#define MG_ENABLE_TCPIP 1 +#define MG_ENABLE_CUSTOM_MILLIS 1 +#define MG_ENABLE_CUSTOM_RANDOM 1 +#define MG_ENABLE_PACKED_FS 1 +#define MG_ENABLE_DRIVER_STM32H 1 + +// #define MG_DRIVER_MDC_CR 4 // RMII MDC clock divider, from 0 to 4 +// #define MG_TCPIP_PHY_ADDR 0 // PHY address + +// For static IP configuration, define MG_TCPIP_{IP,MASK,GW} +// By default, those are set to zero, meaning that DHCP is used +// +// #define MG_TCPIP_IP MG_IPV4(192, 168, 0, 10) +// #define MG_TCPIP_GW MG_IPV4(192, 168, 0, 1) +// #define MG_TCPIP_MASK MG_IPV4(255, 255, 255, 0) + +// Set custom MAC address. By default, it is randomly generated +// Using a build-time constant: +// #define MG_SET_MAC_ADDRESS(mac) do { mac[0] = 2; mac[1] = 3; mac[2] = 4; mac[3] = 5; mac[4] = 6; mac[5] = 7; } while (0) +// +// Using custom function: +// extern void my_function(unsigned char *mac); +// #define MG_SET_MAC_ADDRESS(mac) my_function(mac) + +// Construct MAC address from the MCU unique ID. It is defined in the +// ST CMSIS header as UID_BASE +#define MGUID ((uint32_t *) 0x08fff800) // Unique 96-bit chip ID +#define MG_SET_MAC_ADDRESS(mac) \ + do { \ + mac[0] = 2; \ + mac[1] = MGUID[0] & 255; \ + mac[2] = (MGUID[0] >> 10) & 255; \ + mac[3] = (MGUID[0] >> 19) & 255; \ + mac[4] = MGUID[1] & 255; \ + mac[5] = MGUID[2] & 255; \ + } while (0)