mirror of
https://github.com/armel/uv-k1-k5v3-firmware-custom.git
synced 2026-10-02 03:15:37 +00:00
Improve mb
This commit is contained in:
1 parent
a25d2fc75e
commit
a2551d97dc
5 files changed
+67
-314
No files matched your search
-108
@@ -881,114 +881,6 @@ void UART_HandleCommand(uint32_t Port)
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_MULTIBOOT
|
||||
// Multiboot self-test (developer only, gated build).
|
||||
case 0x0710: // backup: internal application -> external flash slot 0
|
||||
{
|
||||
uint32_t size = 0, crc = 0;
|
||||
MB_BackupToSlot0(&size, &crc);
|
||||
struct __attribute__((packed)) {
|
||||
Header_t Header;
|
||||
uint32_t Size;
|
||||
uint32_t Crc32;
|
||||
} Reply;
|
||||
Reply.Header.ID = 0x0711;
|
||||
Reply.Header.Size = 8;
|
||||
Reply.Size = size;
|
||||
Reply.Crc32 = crc;
|
||||
SendReply(Port, &Reply, sizeof(Reply)); // ack once the backup completed
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0712: // restore: external flash slot 0 -> internal application + reset
|
||||
{
|
||||
// Returns only if validation failed (internal flash untouched);
|
||||
// on success it reflashes and resets, so control never comes back.
|
||||
uint8_t err = MB_RestoreSlot0();
|
||||
struct __attribute__((packed)) {
|
||||
Header_t Header;
|
||||
uint8_t Code;
|
||||
} Reply;
|
||||
Reply.Header.ID = 0x0713;
|
||||
Reply.Header.Size = 1;
|
||||
Reply.Code = err;
|
||||
SendReply(Port, &Reply, sizeof(Reply)); // restore refused, report why
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0714: // test: corrupt slot 0 image (exercise the CRC-refusal path)
|
||||
{
|
||||
uint16_t n = pUART_Command->Header.Size;
|
||||
if (n > 128)
|
||||
n = 128;
|
||||
MB_CorruptSlot0(pUART_Command->Data, n);
|
||||
struct __attribute__((packed)) {
|
||||
Header_t Header;
|
||||
uint16_t Count;
|
||||
} Reply;
|
||||
Reply.Header.ID = 0x0715;
|
||||
Reply.Header.Size = 2;
|
||||
Reply.Count = n;
|
||||
SendReply(Port, &Reply, sizeof(Reply)); // ack: bytes corrupted
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0716: // validate slot 0 (no reflash): report result code + computed CRC
|
||||
{
|
||||
uint32_t crc = 0;
|
||||
uint8_t code = MB_ValidateSlot0(&crc);
|
||||
struct __attribute__((packed)) {
|
||||
Header_t Header;
|
||||
uint32_t Crc32; // 4-byte aligned (offset 4): no unaligned write
|
||||
uint8_t Code;
|
||||
} Reply;
|
||||
Reply.Header.ID = 0x0717;
|
||||
Reply.Header.Size = 5;
|
||||
Reply.Crc32 = crc;
|
||||
Reply.Code = code;
|
||||
SendReply(Port, &Reply, sizeof(Reply));
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0718: // ground-truth dump of external flash via the DMA driver
|
||||
{
|
||||
uint32_t addr = (uint32_t)pUART_Command->Data[0]
|
||||
| ((uint32_t)pUART_Command->Data[1] << 8)
|
||||
| ((uint32_t)pUART_Command->Data[2] << 16)
|
||||
| ((uint32_t)pUART_Command->Data[3] << 24);
|
||||
uint8_t len = pUART_Command->Data[4];
|
||||
if (len > 64)
|
||||
len = 64;
|
||||
struct __attribute__((packed)) {
|
||||
Header_t Header;
|
||||
uint32_t Addr;
|
||||
uint8_t Len;
|
||||
uint8_t Data[64];
|
||||
} Reply;
|
||||
MB_DumpExt(addr, Reply.Data, len);
|
||||
Reply.Header.ID = 0x0719;
|
||||
Reply.Header.Size = 5 + len; // Addr(4) + Len(1) + data(len)
|
||||
Reply.Addr = addr;
|
||||
Reply.Len = len;
|
||||
SendReply(Port, &Reply, sizeof(Header_t) + 5 + len);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x071C: // diagnostic: snapshot SPI2 / DMA state (no flash access)
|
||||
{
|
||||
uint32_t st[4];
|
||||
MB_SpiState(st);
|
||||
struct __attribute__((packed)) {
|
||||
Header_t Header;
|
||||
uint32_t V[4];
|
||||
} Reply;
|
||||
Reply.Header.ID = 0x071D;
|
||||
Reply.Header.Size = 16;
|
||||
Reply.V[0] = st[0]; Reply.V[1] = st[1];
|
||||
Reply.V[2] = st[2]; Reply.V[3] = st[3];
|
||||
SendReply(Port, &Reply, sizeof(Reply));
|
||||
break;
|
||||
}
|
||||
|
||||
// ---- M4 slot management ("Firmware Slots") ------------------------
|
||||
case 0x0720: // slot info: read the 64-byte header only (fast, no CRC)
|
||||
{
|
||||
|
||||
+1
-121
@@ -13,15 +13,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "driver/mb_flash.h"
|
||||
|
||||
#include "py32f0xx.h"
|
||||
#include "driver/py25q16.h"
|
||||
#include "driver/st7565.h"
|
||||
#include "ui/helper.h"
|
||||
#include "version.h"
|
||||
|
||||
/* Internal-flash program/erase keys (FLASH_KEY1 / FLASH_KEY2). */
|
||||
#define MB_FLASH_KEY1 0x45670123u
|
||||
@@ -62,33 +58,6 @@ static const uint32_t mb_flash_timing[8] = {
|
||||
0x1FFF32D8, 0x1FFF3238, 0x1FFF3238, 0x1FFF3238
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Helpers (flash-resident). */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/* zlib/PNG CRC-32 (poly 0xEDB88320), streaming. Seed 'crc' with 0xFFFFFFFF and
|
||||
* XOR the final result with 0xFFFFFFFF. No lookup table (saves flash). */
|
||||
static uint32_t mb_crc32_update(uint32_t crc, const uint8_t *data, uint32_t len)
|
||||
{
|
||||
while (len--)
|
||||
{
|
||||
crc ^= *data++;
|
||||
for (int k = 0; k < 8; k++)
|
||||
crc = (crc >> 1) ^ (0xEDB88320u & (0u - (crc & 1u)));
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
static void mb_copy_str(char *dst, uint32_t cap, const char *src)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
if (src)
|
||||
for (; i + 1 < cap && src[i]; i++)
|
||||
dst[i] = src[i];
|
||||
for (; i < cap; i++)
|
||||
dst[i] = 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Flash-resident preparation (runs while the flash is still readable). */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
@@ -378,39 +347,6 @@ fatal_reset:
|
||||
/* Public API. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
void MB_BackupToSlot0(uint32_t *out_size, uint32_t *out_crc32)
|
||||
{
|
||||
const uint32_t imgBase = MB_SLOT0_EXT_BASE + MB_SLOT_IMG_OFFSET;
|
||||
const uint32_t size = MB_INT_APP_SIZE; /* full region (self-test) */
|
||||
|
||||
/* CRC-32 of the internal image (memory-mapped, contiguous read). */
|
||||
uint32_t crc = mb_crc32_update(0xFFFFFFFFu, (const uint8_t *)MB_INT_APP_BASE, size)
|
||||
^ 0xFFFFFFFFu;
|
||||
|
||||
/* Write the image first ... */
|
||||
PY25Q16_WriteBuffer(imgBase, (const void *)MB_INT_APP_BASE, size, false);
|
||||
|
||||
/* ... then a valid header (COMMITTED) last, so a committed header always
|
||||
* implies a fully written image. */
|
||||
mb_slot_header_t hdr;
|
||||
memset(&hdr, 0, sizeof(hdr));
|
||||
hdr.magic = MB_SLOT_MAGIC;
|
||||
hdr.hdr_version = MB_HDR_VERSION;
|
||||
hdr.flags = MB_FLAG_COMMITTED;
|
||||
hdr.image_size = size;
|
||||
hdr.image_crc32 = crc;
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
mb_copy_str(hdr.name, MB_NAME_LEN, Edition);
|
||||
#else
|
||||
mb_copy_str(hdr.name, MB_NAME_LEN, "slot0");
|
||||
#endif
|
||||
mb_copy_str(hdr.fw_version, MB_VERSION_LEN, Version);
|
||||
PY25Q16_WriteBuffer(MB_SLOT0_EXT_BASE, &hdr, sizeof(hdr), false);
|
||||
|
||||
if (out_size) *out_size = size;
|
||||
if (out_crc32) *out_crc32 = crc;
|
||||
}
|
||||
|
||||
/* Set when a polled SPI wait below times out (external flash unresponsive). */
|
||||
static volatile int mb_spi_err;
|
||||
|
||||
@@ -674,11 +610,6 @@ uint8_t MB_ValidateSlot(uint8_t slot, mb_slot_header_t *out_header, uint32_t *ou
|
||||
return mb_validate(slot, out_header ? out_header : &local, out_crc);
|
||||
}
|
||||
|
||||
uint8_t MB_ValidateSlot0(uint32_t *out_crc)
|
||||
{
|
||||
return MB_ValidateSlot(0, NULL, out_crc);
|
||||
}
|
||||
|
||||
uint8_t MB_RestoreSlot(uint8_t slot, uint8_t *progress_line)
|
||||
{
|
||||
mb_slot_header_t hdr;
|
||||
@@ -701,11 +632,6 @@ uint8_t MB_RestoreSlot(uint8_t slot, uint8_t *progress_line)
|
||||
return MB_OK; /* not reached */
|
||||
}
|
||||
|
||||
uint8_t MB_RestoreSlot0(void)
|
||||
{
|
||||
return MB_RestoreSlot(0, NULL);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* M4 slot management (host tool). External flash only - never brick-critical.*/
|
||||
/* -------------------------------------------------------------------------- */
|
||||
@@ -750,49 +676,3 @@ uint8_t MB_SlotWrite(uint8_t slot, uint32_t offset, const uint8_t *data, uint32_
|
||||
|
||||
return MB_OK;
|
||||
}
|
||||
|
||||
/* Middle of slot 0's image (32 KiB in, well within the 118 KiB image body). */
|
||||
#define MB_CORRUPT_OFFSET 0x8000u
|
||||
#define MB_CORRUPT_MAX 128u
|
||||
|
||||
void MB_CorruptSlot0(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (!data || len == 0)
|
||||
return;
|
||||
if (len > MB_CORRUPT_MAX)
|
||||
len = MB_CORRUPT_MAX;
|
||||
|
||||
/* Constrained to slot 0's image body: this address range cannot reach the
|
||||
* slot header, nor calibration / EEPROM / RF log / voice regions. */
|
||||
PY25Q16_WriteBuffer(MB_SLOT0_EXT_BASE + MB_SLOT_IMG_OFFSET + MB_CORRUPT_OFFSET,
|
||||
data, len, false);
|
||||
}
|
||||
|
||||
volatile uint8_t mb_mark_on = 0;
|
||||
|
||||
void MB_Mark(const char *s)
|
||||
{
|
||||
if (!mb_mark_on)
|
||||
return;
|
||||
memset(gFrameBuffer, 0, sizeof(gFrameBuffer));
|
||||
UI_PrintStringSmallNormal(s, 10, 0, 3);
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
|
||||
void MB_DumpExt(uint32_t addr, uint8_t *buf, uint32_t len)
|
||||
{
|
||||
/* Trace the driver read step by step on the LCD so a freeze reveals where. */
|
||||
mb_mark_on = 1;
|
||||
MB_Mark("DUMP begin");
|
||||
PY25Q16_ReadBufferSafe(addr, buf, len);
|
||||
MB_Mark("DUMP done");
|
||||
mb_mark_on = 0;
|
||||
}
|
||||
|
||||
void MB_SpiState(uint32_t out[4])
|
||||
{
|
||||
out[0] = SPI2->CR1; /* bit 6 (SPE) = SPI enabled */
|
||||
out[1] = SPI2->CR2; /* bit0 RXDMAEN, bit1 TXDMAEN */
|
||||
out[2] = SPI2->SR; /* bit0 RXNE, bit1 TXE, bit7 BSY */
|
||||
out[3] = DMA1_Channel4->CCR; /* SPI2 RX DMA channel (bit0 EN) */
|
||||
}
|
||||
@@ -80,25 +80,6 @@ enum {
|
||||
MB_ERR_AUTH /* write refused: timestamp mismatch */
|
||||
};
|
||||
|
||||
/*
|
||||
* Copy the live internal application image into external flash slot 0, writing
|
||||
* the image first and then a valid header (COMMITTED) last. Runs entirely from
|
||||
* flash and only writes the external SPI flash, so it is safe (no brick risk).
|
||||
* Reports the stored image size and CRC-32 through the (optional) out params.
|
||||
*/
|
||||
void MB_BackupToSlot0(uint32_t *out_size, uint32_t *out_crc32);
|
||||
|
||||
/*
|
||||
* Validate slot 0 and, if valid, reflash the internal application from it and
|
||||
* reset. Validation (magic, version, COMMITTED flag, size, image CRC-32) runs
|
||||
* from flash *before* any erase: on failure it returns an MB_ERR_* code and the
|
||||
* internal flash is left untouched. On success it NEVER RETURNS (the RAM-resident
|
||||
* copier reflashes the internal application and triggers a system reset). The
|
||||
* factory bootloader is never touched, so an interrupted copy is recoverable
|
||||
* over USB/DFU.
|
||||
*/
|
||||
uint8_t MB_RestoreSlot0(void);
|
||||
|
||||
/* Multi-slot API used by the boot selector. Validation always covers the full
|
||||
* image CRC before restore. progress_line may point to a 128-byte LCD page; the
|
||||
* RAM copier then fills it while reflashing. Pass NULL to disable LCD updates. */
|
||||
@@ -123,46 +104,4 @@ uint8_t MB_SlotInfo(uint8_t slot, mb_slot_header_t *out_header);
|
||||
uint8_t MB_SlotErase(uint8_t slot);
|
||||
uint8_t MB_SlotWrite(uint8_t slot, uint32_t offset, const uint8_t *data, uint32_t len);
|
||||
|
||||
|
||||
/*
|
||||
* Test helper: overwrite up to `len` (capped) bytes in the MIDDLE of slot 0's
|
||||
* image with the supplied data, so the next MB_RestoreSlot0() fails its CRC-32
|
||||
* check and refuses. It writes ONLY inside slot 0's image body - never the
|
||||
* header, and never anything outside the slot (calibration, EEPROM, RF log...).
|
||||
* Developer aid to exercise the safe-refusal path without external tooling.
|
||||
*/
|
||||
void MB_CorruptSlot0(const uint8_t *data, uint32_t len);
|
||||
|
||||
/*
|
||||
* Validate slot 0 (header + image CRC-32) WITHOUT reflashing. Same checks as the
|
||||
* restore path, entirely via polled reads (cannot hang). Returns MB_OK or an
|
||||
* MB_ERR_* code, and reports the computed image CRC-32 through out_crc.
|
||||
* Useful as a safe diagnostic from the host tool.
|
||||
*/
|
||||
uint8_t MB_ValidateSlot0(uint32_t *out_crc);
|
||||
|
||||
/*
|
||||
* Read `len` bytes of external flash at `addr` into `buf`, via the DMA driver
|
||||
* (PY25Q16_ReadBuffer) - the same proven read path the backup uses internally.
|
||||
* Ground-truth diagnostic to check what is actually stored in a slot.
|
||||
*/
|
||||
void MB_DumpExt(uint32_t addr, uint8_t *buf, uint32_t len);
|
||||
|
||||
/*
|
||||
* Diagnostic: snapshot the SPI2 / DMA hardware state WITHOUT any flash access
|
||||
* (so it cannot hang). out[0]=SPI2->CR1, [1]=SPI2->SR, [2]=DMA RD chan CCR,
|
||||
* [3]=DMA WR chan CCR. Reveals whether SPI2 is disabled/busy or a DMA channel
|
||||
* is left armed when a read command runs.
|
||||
*/
|
||||
void MB_SpiState(uint32_t out[4]);
|
||||
|
||||
/*
|
||||
* On-screen trace marker (debug). Draws `s` on the LCD (SPI1, independent of the
|
||||
* flash SPI2) so that when a flash read freezes the CPU, the frozen screen shows
|
||||
* the last step reached. Only draws while mb_mark_on is set (i.e. during a Dump),
|
||||
* so it does not flash the screen during normal writes.
|
||||
*/
|
||||
extern volatile uint8_t mb_mark_on;
|
||||
void MB_Mark(const char *s);
|
||||
|
||||
#endif /* DRIVER_MB_FLASH_H */
|
||||
@@ -27,12 +27,9 @@
|
||||
#include "external/printf/printf.h"
|
||||
#include "misc.h"
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_MULTIBOOT
|
||||
#include "driver/mb_flash.h"
|
||||
#define MBMARK(s) MB_Mark(s)
|
||||
#else
|
||||
#define MBMARK(s)
|
||||
#endif
|
||||
/* MBMARK was an on-screen SPI trace used while bringing up multiboot (M1/M2).
|
||||
* The tracer is gone; keep the call sites as no-ops. */
|
||||
#define MBMARK(s)
|
||||
|
||||
// #define DEBUG
|
||||
|
||||
|
||||
+63
-18
@@ -89,11 +89,37 @@ static void mb_format_slot_label(char *dst, uint8_t cap, const mb_slot_header_t
|
||||
dst[n] = 0;
|
||||
}
|
||||
|
||||
/* "MULTIBOOT" mode banner in the top status bar, shown on every screen - the
|
||||
* same way the firmware puts mode labels there (inverse 3x5 capsule). */
|
||||
static void mb_status_bar(void)
|
||||
{
|
||||
UI_StatusClear();
|
||||
GUI_DisplaySmallestInverse("MULTIBOOT", 47, 0, true, true, 83);
|
||||
}
|
||||
|
||||
/* Bottom key-hint line: each key name as an inverse 3x5 capsule label, its
|
||||
* action in plain 3x5 text beside it. Drawn on the bottom line
|
||||
* (gFrameBuffer[6] -> y = 6*8+1 = 49). MENU is pinned to the left and EXIT to
|
||||
* the right, leaving an airy gap in the middle. "MENU"/"EXIT" are 4 chars
|
||||
* (16 px); their capsule spans [x-2 .. x+16]. */
|
||||
static void mb_key_hints(const char *act_menu, const char *act_exit)
|
||||
{
|
||||
const uint8_t sp = 6u; /* label <-> action gap */
|
||||
const uint8_t ae = (uint8_t)strlen(act_exit);
|
||||
const uint8_t xm = 4u; /* MENU text; capsule at x=2 */
|
||||
const uint8_t xe = (uint8_t)(124u - ae * 4u - sp - 16u); /* EXIT action ends at x=124 */
|
||||
|
||||
GUI_DisplaySmallestInverse("MENU", xm, 6, false, true, (uint8_t)(xm + 16u));
|
||||
GUI_DisplaySmallest(act_menu, (uint8_t)(xm + 16u + sp), 49, false, true);
|
||||
|
||||
GUI_DisplaySmallestInverse("EXIT", xe, 6, false, true, (uint8_t)(xe + 16u));
|
||||
GUI_DisplaySmallest(act_exit, (uint8_t)(xe + 16u + sp), 49, false, true);
|
||||
}
|
||||
|
||||
static void mb_show_message(const char *line1, const char *line2, const char *line3)
|
||||
{
|
||||
UI_DisplayClear();
|
||||
UI_StatusClear();
|
||||
UI_PrintStringSmallNormal("MultiBoot", 2, 126, 0);
|
||||
mb_status_bar();
|
||||
if (line1) UI_PrintStringSmallNormal(line1, 2, 126, 2);
|
||||
if (line2) UI_PrintStringSmallNormal(line2, 2, 126, 4);
|
||||
if (line3) UI_PrintStringSmallNormal(line3, 2, 126, 6);
|
||||
@@ -147,14 +173,15 @@ static void mb_render_slots(uint8_t selected,
|
||||
char line[19]; /* 18 glyphs max: 18 * 7 px fits from x=2 to x=126. */
|
||||
|
||||
UI_DisplayClear();
|
||||
UI_StatusClear();
|
||||
UI_PrintStringSmallNormal("MultiBoot", 2, 126, 0);
|
||||
mb_status_bar();
|
||||
|
||||
for (uint8_t slot = 0; slot < MB_SLOT_COUNT; slot++)
|
||||
{
|
||||
const uint8_t fbLine = (uint8_t)(slot + 1u); /* page 1 stays blank */
|
||||
|
||||
memset(line, 0, sizeof(line));
|
||||
line[0] = (slot == selected) ? '>' : ' ';
|
||||
line[1] = (char)('0' + slot);
|
||||
line[0] = (char)('0' + slot);
|
||||
line[1] = ' ';
|
||||
line[2] = ' ';
|
||||
|
||||
if (status[slot] == MB_OK)
|
||||
@@ -162,25 +189,30 @@ static void mb_render_slots(uint8_t selected,
|
||||
else
|
||||
mb_copy_label(&line[3], sizeof(line) - 3u, mb_error_text(status[slot]), 20u);
|
||||
|
||||
UI_PrintStringSmallNormal(line, 2, 0, (uint8_t)(slot + 1u));
|
||||
UI_PrintStringSmallNormal(line, 2, 0, fbLine);
|
||||
|
||||
/* Selected row: full-width inverse bar, like the firmware menu list. */
|
||||
if (slot == selected)
|
||||
for (uint8_t x = 0; x < LCD_WIDTH; x++)
|
||||
gFrameBuffer[fbLine][x] ^= 0xFFu;
|
||||
}
|
||||
|
||||
UI_PrintStringSmallNormal("MENU to select", 2, 126, 5);
|
||||
UI_PrintStringSmallNormal("EXIT to go back", 2, 126, 6);
|
||||
mb_key_hints("SELECT", "QUIT");
|
||||
|
||||
ST7565_BlitStatusLine();
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
|
||||
static void mb_prepare_progress(uint8_t slot)
|
||||
{
|
||||
char title[] = "RESTORE SLOT 0";
|
||||
char title[] = "Restore slot 0";
|
||||
title[13] = (char)('0' + slot);
|
||||
|
||||
UI_DisplayClear();
|
||||
UI_StatusClear();
|
||||
UI_PrintStringSmallNormal(title, 2, 126, 0);
|
||||
UI_PrintStringSmallNormal("DO NOT POWER OFF", 2, 126, 2);
|
||||
UI_PrintStringSmallNormal("Writing & Verify", 2, 126, 4);
|
||||
mb_status_bar();
|
||||
UI_PrintStringSmallNormal(title, 2, 126, 1);
|
||||
UI_PrintStringSmallNormal("DO NOT POWER OFF", 2, 126, 3);
|
||||
UI_PrintStringSmallNormal("Writing & Verify", 2, 126, 5);
|
||||
|
||||
/* Same rounded outline and hatch pattern as the scan progress gauge. */
|
||||
gFrameBuffer[6][3] = 0x0Cu;
|
||||
@@ -193,14 +225,29 @@ static void mb_prepare_progress(uint8_t slot)
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
|
||||
static void mb_confirm_screen(uint8_t slot)
|
||||
{
|
||||
char title[] = "Restore slot 0?";
|
||||
title[13] = (char)('0' + slot);
|
||||
|
||||
UI_DisplayClear();
|
||||
mb_status_bar();
|
||||
UI_PrintStringSmallNormal(title, 2, 126, 3);
|
||||
mb_key_hints("CONFIRM", "BACK");
|
||||
ST7565_BlitStatusLine();
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
|
||||
void UI_MultibootSelector(void)
|
||||
{
|
||||
mb_slot_header_t headers[MB_SLOT_COUNT];
|
||||
uint8_t status[MB_SLOT_COUNT];
|
||||
uint8_t selected = 0;
|
||||
|
||||
BACKLIGHT_TurnOn();
|
||||
/* Clear + blit the LCD BEFORE the backlight comes on, otherwise it reveals
|
||||
* the random power-on contents of the display RAM for a moment. */
|
||||
mb_show_message("Release keys", NULL, NULL);
|
||||
BACKLIGHT_TurnOn();
|
||||
mb_wait_release();
|
||||
mb_scan_slots(headers, status);
|
||||
|
||||
@@ -247,9 +294,7 @@ void UI_MultibootSelector(void)
|
||||
continue;
|
||||
}
|
||||
|
||||
char confirm[] = "Restore slot 0?";
|
||||
confirm[13] = (char)('0' + selected);
|
||||
mb_show_message(confirm, "MENU to confirm", "EXIT to cancel");
|
||||
mb_confirm_screen(selected);
|
||||
key = mb_get_key();
|
||||
if (key != KEY_MENU)
|
||||
continue;
|
||||
|
||||
Reference in new issue
Block a user