Files
ZephCore/zephcore/helpers/boot_info.c
T
liquidraver 187a44060d close out the restructure's open items
- boot_info: label the ESP32 reset reasons Zephyr's hwinfo driver maps to 0
  (USB-Serial-JTAG/JTAG reset -> DEBUG, power glitch -> BROWNOUT, CPU lockup,
  eFuse -> HARDWARE); get pwrmgt.bootreason no longer says Unknown after a flash
- mqtt: the uplink credentials struct moves to adapters/mqtt/uplink_creds.h,
  so the adapter no longer includes an app header (layout unchanged: the
  struct is persisted raw)
- gps: one off-levels routine for standby/off and System OFF; the sleep line
  stays asserted in standby and drops at System OFF, as upstream's
  sleep_gps/stop_gps/powerOff; System OFF now asserts reset before cutting
  GPS_EN
- doom: sprites solid with a one-pixel black halo, boss drawn 1.5x on the
  floor; the L2 boss was invisible from the spawn on 1-bit panels
2026-09-26 08:50:30 +02:00

170 lines
4.2 KiB
C

/*
* SPDX-License-Identifier: MIT
* Boot-time reset-cause capture. See boot_info.h.
*/
#include "boot_info.h"
#include <zephyr/init.h>
#include <zephyr/drivers/hwinfo.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/util.h>
#include <stdio.h>
#if defined(CONFIG_SOC_FAMILY_ESPRESSIF_ESP32)
#include <esp_system.h>
#endif
LOG_MODULE_REGISTER(zephcore_boot, CONFIG_ZEPHCORE_MAIN_LOG_LEVEL);
static uint32_t s_reset_cause;
static bool s_reset_cause_valid;
#if defined(CONFIG_SOC_FAMILY_ESPRESSIF_ESP32)
/* Zephyr's ESP32 hwinfo driver reports 0 for the reasons it has no case for,
* the commonest being ESP_RST_USB: esptool's reset over USB-Serial-JTAG, i.e.
* every flash. Map those onto the nearest RESET_* bit so they are labelled
* instead of "Unknown". */
static uint32_t esp_unmapped_reset_cause(void)
{
switch (esp_reset_reason()) {
case ESP_RST_USB:
case ESP_RST_JTAG:
return RESET_DEBUG;
case ESP_RST_PWR_GLITCH:
return RESET_BROWNOUT;
case ESP_RST_CPU_LOCKUP:
return RESET_CPU_LOCKUP;
case ESP_RST_EFUSE:
return RESET_HARDWARE;
default:
return 0;
}
}
#endif
/* POST_KERNEL: before main() on every role. */
static int boot_info_init(void)
{
uint32_t cause = 0;
if (hwinfo_get_reset_cause(&cause) != 0) {
/* Nothing to capture; the accessor reports false. */
return 0;
}
#if defined(CONFIG_SOC_FAMILY_ESPRESSIF_ESP32)
if (cause == 0) {
cause = esp_unmapped_reset_cause();
}
#endif
s_reset_cause = cause;
s_reset_cause_valid = true;
/* Logged here for every role: the clear below would otherwise lose the
* cause on roles that never read it. */
char labels[128];
(void)zephcore_boot_reset_cause_str(labels, sizeof(labels), false);
LOG_INF("Reset cause: 0x%08x%s", cause, labels);
/* Not reported again next boot. -ENOSYS where unimplemented (ESP32). */
(void)hwinfo_clear_reset_cause();
return 0;
}
SYS_INIT(boot_info_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
bool zephcore_boot_reset_cause(uint32_t *out)
{
if (!s_reset_cause_valid) {
return false;
}
*out = s_reset_cause;
return true;
}
/*
* One row per RESET_* bit in <zephyr/drivers/hwinfo.h>. A bit with no row is
* rendered as nothing and is invisible to zephcore_boot_reset_cause_labelled().
* hint is the plain-English suffix for the user-facing notice, NULL where
* the label already says it or no honest one fits. SOFTWARE has none: it is
* a reboot command, a firmware update and a fatal-error reboot alike.
*/
static const struct {
uint32_t bit;
const char *label;
const char *hint;
} s_labels[] = {
{ RESET_PIN, "PIN", "(reset button)" },
{ RESET_SOFTWARE, "SOFTWARE" },
{ RESET_BROWNOUT, "BROWNOUT", "(low voltage)" },
{ RESET_POR, "POR", "(power-on)" },
{ RESET_WATCHDOG, "WATCHDOG", "(hang)" },
{ RESET_DEBUG, "DEBUG", "(debugger)" },
{ RESET_SECURITY, "SECURITY" },
{ RESET_LOW_POWER_WAKE, "LOWPOWER", "(wake from off)" },
{ RESET_CPU_LOCKUP, "LOCKUP", "(crash)" },
{ RESET_PARITY, "PARITY", "(memory error)" },
{ RESET_PLL, "PLL", "(clock fault)" },
{ RESET_CLOCK, "CLOCK" },
{ RESET_HARDWARE, "HARDWARE" },
{ RESET_USER, "USER" },
{ RESET_TEMPERATURE, "TEMPERATURE", "(overheat)" },
{ RESET_BOOTLOADER, "BOOTLOADER" },
{ RESET_FLASH, "FLASH" },
};
int zephcore_boot_reset_cause_str(char *buf, size_t cap, bool hints)
{
if (buf == NULL || cap == 0) {
return 0;
}
buf[0] = '\0';
if (!s_reset_cause_valid) {
return 0;
}
size_t n = 0;
for (size_t i = 0; i < ARRAY_SIZE(s_labels); i++) {
if ((s_reset_cause & s_labels[i].bit) == 0) {
continue;
}
const char *hint = (hints && s_labels[i].hint) ? s_labels[i].hint : "";
int w = snprintf(buf + n, cap - n, " %s%s", s_labels[i].label, hint);
if (w < 0 || (size_t)w >= cap - n) {
/* Stop rather than emit half a label. */
buf[n] = '\0';
break;
}
n += (size_t)w;
}
return (int)n;
}
uint32_t zephcore_boot_reset_cause_labelled(void)
{
uint32_t mask = 0;
if (!s_reset_cause_valid) {
return 0;
}
for (size_t i = 0; i < ARRAY_SIZE(s_labels); i++) {
mask |= s_reset_cause & s_labels[i].bit;
}
return mask;
}