From da3c11dcfc35c3fbe11ffecd224aa6377b66ea91 Mon Sep 17 00:00:00 2001 From: Rahix <rahix@rahix.de> Date: Sun, 28 Jul 2019 01:50:19 +0200 Subject: [PATCH] feat(bootloader): Add a boot-splash Signed-off-by: Rahix <rahix@rahix.de> --- bootloader/bootloader-display.c | 30 +++++++++++- bootloader/main.c | 19 +++++--- bootloader/meson.build | 14 ++++++ bootloader/splash-screen.png | Bin 0 -> 6332 bytes tools/bootloader-image.py | 83 ++++++++++++++++++++++++++++++++ 5 files changed, 139 insertions(+), 7 deletions(-) create mode 100644 bootloader/splash-screen.png create mode 100755 tools/bootloader-image.py diff --git a/bootloader/bootloader-display.c b/bootloader/bootloader-display.c index 2c1e34fd..1fe0cae0 100644 --- a/bootloader/bootloader-display.c +++ b/bootloader/bootloader-display.c @@ -1,14 +1,42 @@ #include "bootloader.h" +/* Autogenerated */ +#include "splash-screen.h" #include "GUI_Paint.h" #include "display.h" +/* + * "Decompress" splash-screen image. The algorithm works as follows: + * + * Each byte encodes up to 127 pixels in either white or black. The most + * significant bit determines the color, the remaining 7 bits determine the + * amount. + */ +static void bootloader_display_splash(void) +{ + int idx = 0; + + for (int i = 0; i < sizeof(splash); i++) { + uint16_t color = (splash[i] & 0x80) ? 0xffff : 0x0000; + uint8_t length = splash[i] & 0x7f; + + for (int j = 0; j < length; j++) { + uint16_t x = idx % 160; + uint16_t y = idx / 160; + Paint_SetPixel(x, y, color); + idx++; + } + } + + LCD_Update(); +} + /* * Initialize the display. */ void bootloader_display_init(void) { - ; + bootloader_display_splash(); } /* diff --git a/bootloader/main.c b/bootloader/main.c index 1a5f08e0..d3577542 100644 --- a/bootloader/main.c +++ b/bootloader/main.c @@ -205,10 +205,11 @@ int main(void) */ pmic_set_button_callback(pmic_button); - bootloader_display_header(); + bootloader_display_init(); // If the button is pressed, we go into MSC mode. if (PB_Get(3)) { + bootloader_display_header(); bootloader_display_line(2, "USB activated.", 0xffff); bootloader_display_line(3, "Ready.", 0xffff); run_usbmsc(); @@ -224,36 +225,42 @@ int main(void) int res = check_integrity(); if (res == -ENOENT) { printf("card10.bin not found!\n"); - bootloader_display_line( - 2, "card10.bin not found", 0xffff - ); } else if (res == -EINVAL) { printf("card10.bin CRC is invalid!\n"); + bootloader_display_header(); bootloader_display_line( 2, "Integrity check failed", 0xffff ); + + bootloader_display_line(4, "Trying to boot", 0xffff); } else if (res == 0) { printf("Found valid application image\n"); if (is_update_needed()) { printf("Trying to update firmware from external flash\n"); + bootloader_display_header(); bootloader_display_line( - 4, "Updating ...", 0xffff + 3, "Updating ...", 0xffff ); erase_partition(); flash_partition(); + bootloader_display_line( + 4, "Trying to boot", 0xffff + ); } else { printf("No update needed\n"); } } } else { + bootloader_display_header(); bootloader_display_line( 2, "Failed to mount filesystem", 0xffff ); printf("Failed to mount the external flash\n"); + + bootloader_display_line(4, "Trying to boot", 0xffff); } printf("Trying to boot\n"); - bootloader_display_line(4, "Trying to boot", 0xffff); boot((uintptr_t *)PARTITION_START); diff --git a/bootloader/meson.build b/bootloader/meson.build index 9a863cdf..23c38794 100644 --- a/bootloader/meson.build +++ b/bootloader/meson.build @@ -1,5 +1,18 @@ name = 'bootloader' +splash_screen = custom_target( + 'splash-screen.h', + output: 'splash-screen.h', + input: 'splash-screen.png', + command: [ + python3, + meson.current_source_dir() + '../tools/bootloader-image.py', + '-n', 'splash', + '@INPUT@', + '@OUTPUT@', + ], +) + executable( name + '.elf', 'main.c', @@ -7,6 +20,7 @@ executable( 'bootloader-display.c', 'bootloader-usb.c', 'crc16-ccitt.c', + splash_screen, dependencies: [ libcard10, max32665_startup_boot, diff --git a/bootloader/splash-screen.png b/bootloader/splash-screen.png new file mode 100644 index 0000000000000000000000000000000000000000..02a632049624f5b96e54ac2d4c7d408fd41ae796 GIT binary patch literal 6332 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 literal 0 HcmV?d00001 diff --git a/tools/bootloader-image.py b/tools/bootloader-image.py new file mode 100755 index 00000000..f2ce2bf8 --- /dev/null +++ b/tools/bootloader-image.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +import argparse +import os +from PIL import Image + + +def main() -> None: + parser = argparse.ArgumentParser( + description="""\ +\"Compress\" an image for the boot-splash. +The boot-splash is a two-color image.""" + ) + + parser.add_argument("image", help="Boot-splash image") + parser.add_argument("-n", "--name", help="Name of the data-block") + parser.add_argument("output", help="Output file name") + + args = parser.parse_args() + + im = Image.open(args.image) + + assert im.size[0] == 160, "Image must be 160 pixels wide" + assert im.size[1] == 80, "Image must be 80 pixels high)" + + if args.name is not None: + name = args.name + else: + name = os.path.splitext(os.path.basename(args.image))[0].replace("-", "_") + + with open(args.output, "w") as f: + tmp = """\ +#include <stdint.h> + +/* + * This is the splash-screen image, compressed using a very primitive algorithm: + * + * Each byte encodes up to 127 pixels in either white or black. The most + * significant bit determines the color, the remaining 7 bits determine the + * amount. + */ +const uint8_t {name}[] = {{ +""" + f.write(tmp.format(name=name)) + + total = 0 + start = 0 + previous_white = False + for i in range(160 * 80): + x = i % 160 + y = i // 160 + white = im.getpixel((x, y))[0] > 0 + + if white != previous_white or i - start == 127: + length = i - start + assert length < 128, "Internal error" + value = (length & 0x7F) | (0x80 if previous_white else 0x00) + + tmp = """\ + /* {length} pixels in {color} */ + 0x{value:x}, +""" + f.write( + tmp.format( + length=length, + color="white" if previous_white else "black", + value=value, + ) + ) + + previous_white = white + start = i + total += length + + tmp = """\ +}}; +""" + f.write(tmp.format()) + + assert total < (160 * 80), "Internal error" + + +if __name__ == "__main__": + main() -- GitLab