diff options
Diffstat (limited to '0016-bootsplash.patch')
-rw-r--r--[-rwxr-xr-x] | 0016-bootsplash.patch | 818 |
1 files changed, 504 insertions, 314 deletions
diff --git a/0016-bootsplash.patch b/0016-bootsplash.patch index e5c1fd0c8604..5d8ea1fe295c 100755..100644 --- a/0016-bootsplash.patch +++ b/0016-bootsplash.patch @@ -1,321 +1,511 @@ -diff --git a/Documentation/ABI/testing/sysfs-platform-bootsplash b/Documentation/ABI/testing/sysfs-platform-bootsplash +diff --git a/MAINTAINERS b/MAINTAINERS +index 7ffac272434e..ddff07cd794c 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -2715,6 +2715,7 @@ F: drivers/video/fbdev/core/bootsplash*.* + F: drivers/video/fbdev/core/dummycon.c + F: include/linux/bootsplash.h + F: include/uapi/linux/bootsplash_file.h ++F: tools/bootsplash/* + + BPF (Safe dynamic programs and tools) + M: Alexei Starovoitov <ast@kernel.org> +diff --git a/tools/bootsplash/.gitignore b/tools/bootsplash/.gitignore new file mode 100644 -index 000000000000..742c7b035ded +index 000000000000..091b99a17567 --- /dev/null -+++ b/Documentation/ABI/testing/sysfs-platform-bootsplash -@@ -0,0 +1,11 @@ -+What: /sys/devices/platform/bootsplash.0/enabled -+Date: Oct 2017 -+KernelVersion: 4.14 -+Contact: Max Staudt <mstaudt@suse.de> -+Description: -+ Can be set and read. -+ -+ 0: Splash is disabled. -+ 1: Splash is shown whenever fbcon would show a text console -+ (i.e. no graphical application is running), and a splash -+ file is loaded. -diff --git a/Documentation/bootsplash.rst b/Documentation/bootsplash.rst ++++ b/tools/bootsplash/.gitignore +@@ -0,0 +1 @@ ++bootsplash-packer +diff --git a/tools/bootsplash/Makefile b/tools/bootsplash/Makefile new file mode 100644 -index 000000000000..611f0c558925 +index 000000000000..0ad8e8a84942 --- /dev/null -+++ b/Documentation/bootsplash.rst -@@ -0,0 +1,285 @@ -+==================== -+The Linux bootsplash -+==================== -+ -+:Date: November, 2017 -+:Author: Max Staudt <mstaudt@suse.de> -+ -+ -+The Linux bootsplash is a graphical replacement for the '``quiet``' boot -+option, typically showing a logo and a spinner animation as the system starts. -+ -+Currently, it is a part of the Framebuffer Console support, and can be found -+as ``CONFIG_BOOTSPLASH`` in the kernel configuration. This means that as long -+as it is enabled, it hijacks fbcon's output and draws a splash screen instead. -+ -+Purely compiling in the bootsplash will not render it functional - to actually -+render a splash, you will also need a splash theme file. See the example -+utility and script in ``tools/bootsplash`` for a live demo. -+ -+ -+ -+Motivation -+========== -+ -+- The '``quiet``' boot option only suppresses most messages during boot, but -+ errors are still shown. -+ -+- A user space implementation can only show a logo once user space has been -+ initialized far enough to allow this. A kernel splash can display a splash -+ immediately as soon as fbcon can be displayed. -+ -+- Implementing a splash screen in user space (e.g. Plymouth) is problematic -+ due to resource conflicts. -+ -+ For example, if Plymouth is keeping ``/dev/fb0`` (provided via vesafb/efifb) -+ open, then most DRM drivers can't replace it because the address space is -+ still busy - thus leading to a VRAM reservation error. -+ -+ See: https://bugzilla.opensuse.org/show_bug.cgi?id=980750 -+ -+ -+ -+Command line arguments -+====================== -+ -+``bootsplash.bootfile`` -+ Which file in the initramfs to load. -+ -+ The splash theme is loaded via request_firmware(), thus to load -+ ``/lib/firmware/bootsplash/mytheme`` pass the command line: -+ -+ ``bootsplash.bootfile=bootsplash/mytheme`` -+ -+ Note: The splash file *has to be* in the initramfs, as it needs to be -+ available when the splash is initialized early on. -+ -+ Default: none, i.e. a non-functional splash, falling back to showing text. -+ -+ -+ -+sysfs run-time configuration -+============================ -+ -+``/sys/devices/platform/bootsplash.0/enabled`` -+ Enable/disable the bootsplash. -+ The system boots with this set to 1, but will not show a splash unless -+ a splash theme file is also loaded. -+ -+ -+ -+Kconfig -+======= -+ -+``BOOTSPLASH`` -+ Whether to compile in bootsplash support -+ (depends on fbcon compiled in, i.e. ``FRAMEBUFFER_CONSOLE=y``) -+ -+ -+ -+Bootsplash file format -+====================== -+ -+A file specified in the kernel configuration as ``CONFIG_BOOTSPLASH_FILE`` -+or specified on the command line as ``bootsplash.bootfile`` will be loaded -+and displayed as soon as fbcon is initialized. -+ -+ -+Main blocks -+----------- -+ -+There are 3 main blocks in each file: -+ -+ - one File header -+ - n Picture headers -+ - m (Blob header + payload) blocks -+ -+ -+Structures -+---------- -+ -+The on-disk structures are defined in -+``drivers/video/fbdev/core/bootsplash_file.h`` and represent these blocks: -+ -+ - ``struct splash_file_header`` -+ -+ Represents the file header, with splash-wide information including: -+ -+ - The magic string "``Linux bootsplash``" on big-endian platforms -+ (the reverse on little endian) -+ - The file format version (for incompatible updates, hopefully never) -+ - The background color -+ - Number of picture and blob blocks -+ - Animation speed (we only allow one delay for all animations) -+ -+ The file header is followed by the first picture header. -+ -+ -+ - ``struct splash_picture_header`` -+ -+ Represents an object (picture) drawn on screen, including its immutable -+ properties: -+ - Width, height -+ - Positioning relative to screen corners or in the center -+ - Animation, if any -+ - Animation type -+ - Number of blobs -+ -+ The picture header is followed by another picture header, up until n -+ picture headers (as defined in the file header) have been read. Then, -+ the (blob header, payload) pairs follow. -+ ++++ b/tools/bootsplash/Makefile +@@ -0,0 +1,9 @@ ++CC := $(CROSS_COMPILE)gcc ++CFLAGS := -I../../usr/include + -+ - ``struct splash_blob_header`` -+ (followed by payload) ++PROGS := bootsplash-packer + -+ Represents one raw data stream. So far, only picture data is defined. ++all: $(PROGS) + -+ The blob header is followed by a payload, then padding to n*16 bytes, -+ then (if further blobs are defined in the file header) a further blob -+ header. -+ -+ -+Alignment -+--------- -+ -+The bootsplash file is designed to be loaded into memory as-is. -+ -+All structures are a multiple of 16 bytes long, all elements therein are -+aligned to multiples of their length, and the payloads are always padded -+up to multiples of 16 bytes. This is to allow aligned accesses in all -+cases while still simply mapping the structures over an in-memory copy of -+the bootsplash file. -+ -+ -+Further information -+------------------- -+ -+Please see ``drivers/video/fbdev/core/bootsplash_file.h`` for further -+details and possible values in the file. -+ -+ -+ -+Hooks - how the bootsplash is integrated -+======================================== -+ -+``drivers/video/fbdev/core/fbcon.c`` -+ ``fbcon_init()`` calls ``bootsplash_init()``, which loads the default -+ bootsplash file or the one specified on the kernel command line. -+ -+ ``fbcon_switch()`` draws the bootsplash when it's active, and is also -+ one of the callers of ``set_blitting_type()``. -+ -+ ``set_blitting_type()`` calls ``fbcon_set_dummyops()`` when the -+ bootsplash is active, overriding the text rendering functions. -+ -+ ``fbcon_cursor()`` will call ``bootsplash_disable()`` when an oops is -+ being printed in order to make a kernel panic visible. -+ -+``drivers/video/fbdev/core/dummyblit.c`` -+ This contains the dummy text rendering functions used to suppress text -+ output while the bootsplash is shown. -+ -+``drivers/tty/vt/keyboard.c`` -+ ``kbd_keycode()`` can call ``bootsplash_disable()`` when the user -+ presses ESC or F1-F12 (changing VT). This is to provide a built-in way -+ of disabling the splash manually at any time. -+ -+ -+ -+FAQ: Frequently Asked Questions -+=============================== -+ -+I want to see the log! How do I show the log? -+--------------------------------------------- -+ -+Press ESC while the splash is shown, or remove the ``bootsplash.bootfile`` -+parameter from the kernel cmdline. Without that parameter, the bootsplash -+will boot disabled. -+ -+ -+Why use FB instead of modern DRM/KMS? -+------------------------------------- -+ -+This is a semantic problem: -+ - What memory to draw the splash to? -+ - And what mode will the screen be set to? -+ -+Using the fbdev emulation solves these issues. -+ -+Let's start from a bare KMS system, without fbcon, and without fbdev -+emulation. In this case, as long as userspace doesn't open the KMS -+device, the state of the screen is undefined. No framebuffer is -+allocated in video RAM, and no particular mode is set. -+ -+In this case, we'd have to allocate a framebuffer to show the splash, -+and set our mode ourselves. This either wastes a screenful of video RAM -+if the splash is to co-exist with the userspace program's own allocated -+framebuffer, or there is a flicker as we deactivate and delete the -+bootsplash's framebuffer and hand control over to userspace. Since we -+may set a different mode than userspace, we'd also have flicker due -+to mode switching. -+ -+This logic is already contained in every KMS driver that performs fbdev -+emulation. So we might as well use that. And the correct API to do so is -+fbdev. Plus, we get compatibility with old, pure fbdev drivers for free. -+With the fbdev emulation, there is *always* a well-defined framebuffer -+to draw on. And the selection of mode has already been done by the -+graphics driver, so we don't need to reinvent that wheel, either. -+Finally, if userspace decides to use /dev/fbX, we don't have to worry -+about wasting video RAM, either. -+ -+ -+Why is the bootsplash integrated in fbcon? -+------------------------------------------ -+ -+Right now, the bootsplash is drawn from within fbcon, as this allows us -+to easily know *when* to draw - i.e. when we're safe from fbcon and -+userspace drawing all over our beautiful splash logo. -+ -+Separating them is not easy - see the to-do list below. -+ -+ -+ -+TO DO list for future development -+================================= -+ -+Second enable/disable switch for the system -+------------------------------------------- -+ -+It may be helpful to differentiate between the system and the user -+switching off the bootsplash. Thus, the system may make it disappear and -+reappear e.g. for a password prompt, yet once the user has pressed ESC, -+it could stay gone. -+ -+ -+Fix buggy DRM/KMS drivers -+------------------------- -+ -+Currently, the splash code manually checks for fbdev emulation provided by -+the ast, cirrus, and mgag200 DRM/KMS drivers. -+These drivers use a manual mechanism similar to deferred I/O for their FB -+emulation, and thus need to be manually flushed onto the screen in the same -+way. -+ -+This may be improved upon in several ways: -+ -+1. Changing these drivers to expose the fbdev BO's memory directly, like -+ bochsdrmfb does. -+2. Creating a new fb_ops->fb_flush() API to allow the kernel to flush the -+ framebuffer once the bootsplash has been drawn into it. -+ -+ -+Separating from fbcon -+--------------------- -+ -+Separating these two components would yield independence from fbcon being -+compiled into the kernel, and thus lowering code size in embedded -+applications. -+ -+To do this cleanly will involve a clean separation of users of an FB device -+within the kernel, i.e. fbcon, bootsplash, and userspace. Right now, the -+legacy fbcon code and VT code co-operate to switch between fbcon and -+userspace (by setting the VT into KD_GRAPHICS mode). Installing a muxer -+between these components ensues refactoring of old code and checking for -+correct locking. -diff --git a/MAINTAINERS b/MAINTAINERS -index 5c237445761e..7ffac272434e 100644 ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -2709,6 +2709,8 @@ BOOTSPLASH - M: Max Staudt <mstaudt@suse.de> - L: linux-fbdev@vger.kernel.org - S: Maintained -+F: Documentation/ABI/testing/sysfs-platform-bootsplash -+F: Documentation/bootsplash.rst - F: drivers/video/fbdev/core/bootsplash*.* - F: drivers/video/fbdev/core/dummycon.c - F: include/linux/bootsplash.h ++clean: ++ rm -fr $(PROGS) +diff --git a/tools/bootsplash/bootsplash-packer.c b/tools/bootsplash/bootsplash-packer.c +new file mode 100644 +index 000000000000..ffb6a8b69885 +--- /dev/null ++++ b/tools/bootsplash/bootsplash-packer.c +@@ -0,0 +1,471 @@ ++/* ++ * Kernel based bootsplash. ++ * ++ * (Splash file packer tool) ++ * ++ * Authors: ++ * Max Staudt <mstaudt@suse.de> ++ * ++ * SPDX-License-Identifier: GPL-2.0 ++ */ ++ ++#include <endian.h> ++#include <getopt.h> ++#include <stdint.h> ++#include <stdio.h> ++#include <stdlib.h> ++#include <string.h> ++ ++#include <linux/bootsplash_file.h> ++ ++ ++static void print_help(char *progname) ++{ ++ printf("Usage: %s [OPTIONS] outfile\n", progname); ++ printf("\n" ++ "Options, executed in order given:\n" ++ " -h, --help Print this help message\n" ++ "\n" ++ " --bg_red <u8> Background color (red part)\n" ++ " --bg_green <u8> Background color (green part)\n" ++ " --bg_blue <u8> Background color (blue part)\n" ++ " --bg_reserved <u8> (do not use)\n" ++ " --frame_ms <u16> Minimum milliseconds between animation steps\n" ++ "\n" ++ " --picture Start describing the next picture\n" ++ " --pic_width <u16> Picture width in pixels\n" ++ " --pic_height <u16> Picture height in pixels\n" ++ " --pic_position <u8> Coarse picture placement:\n" ++ " 0x00 - Top left\n" ++ " 0x01 - Top\n" ++ " 0x02 - Top right\n" ++ " 0x03 - Right\n" ++ " 0x04 - Bottom right\n" ++ " 0x05 - Bottom\n" ++ " 0x06 - Bottom left\n" ++ " 0x07 - Left\n" ++ "\n" ++ " Flags:\n" ++ " 0x10 - Calculate offset from corner towards center,\n" ++ " rather than from center towards corner\n" ++ " --pic_position_offset <u16> Distance from base position in pixels\n" ++ " --pic_anim_type <u8> Animation type:\n" ++ " 0 - None\n" ++ " 1 - Forward loop\n" ++ " --pic_anim_loop <u8> Loop point for animation\n" ++ "\n" ++ " --blob <filename> Include next data stream\n" ++ " --blob_type <u16> Type of data\n" ++ " --blob_picture_id <u8> Picture to associate this blob with, starting at 0\n" ++ " (default: number of last --picture)\n" ++ "\n"); ++ printf("This tool will write %s files.\n\n", ++#if __BYTE_ORDER == __BIG_ENDIAN ++ "Big Endian (BE)"); ++#elif __BYTE_ORDER == __LITTLE_ENDIAN ++ "Little Endian (LE)"); ++#else ++#error ++#endif ++} ++ ++ ++struct blob_entry { ++ struct blob_entry *next; ++ ++ char *fn; ++ ++ struct splash_blob_header header; ++}; ++ ++ ++static void dump_file_header(struct splash_file_header *h) ++{ ++ printf(" --- File header ---\n"); ++ printf("\n"); ++ printf(" version: %5u\n", h->version); ++ printf("\n"); ++ printf(" bg_red: %5u\n", h->bg_red); ++ printf(" bg_green: %5u\n", h->bg_green); ++ printf(" bg_blue: %5u\n", h->bg_blue); ++ printf(" bg_reserved: %5u\n", h->bg_reserved); ++ printf("\n"); ++ printf(" num_blobs: %5u\n", h->num_blobs); ++ printf(" num_pics: %5u\n", h->num_pics); ++ printf("\n"); ++ printf(" frame_ms: %5u\n", h->frame_ms); ++ printf("\n"); ++} ++ ++static void dump_pic_header(struct splash_pic_header *ph) ++{ ++ printf(" --- Picture header ---\n"); ++ printf("\n"); ++ printf(" width: %5u\n", ph->width); ++ printf(" height: %5u\n", ph->height); ++ printf("\n"); ++ printf(" num_blobs: %5u\n", ph->num_blobs); ++ printf("\n"); ++ printf(" position: %0x3x\n", ph->position); ++ printf(" position_offset: %5u\n", ph->position_offset); ++ printf("\n"); ++ printf(" anim_type: %5u\n", ph->anim_type); ++ printf(" anim_loop: %5u\n", ph->anim_loop); ++ printf("\n"); ++} ++ ++static void dump_blob(struct blob_entry *b) ++{ ++ printf(" --- Blob header ---\n"); ++ printf("\n"); ++ printf(" length: %7u\n", b->header.length); ++ printf(" type: %7u\n", b->header.type); ++ printf("\n"); ++ printf(" picture_id: %7u\n", b->header.picture_id); ++ printf("\n"); ++} ++ ++ ++#define OPT_MAX(var, max) \ ++ do { \ ++ if ((var) > max) { \ ++ fprintf(stderr, "--%s: Invalid value\n", \ ++ long_options[option_index].name); \ ++ break; \ ++ } \ ++ } while (0) ++ ++static struct option long_options[] = { ++ {"help", 0, 0, 'h'}, ++ {"bg_red", 1, 0, 10001}, ++ {"bg_green", 1, 0, 10002}, ++ {"bg_blue", 1, 0, 10003}, ++ {"bg_reserved", 1, 0, 10004}, ++ {"frame_ms", 1, 0, 10005}, ++ {"picture", 0, 0, 20000}, ++ {"pic_width", 1, 0, 20001}, ++ {"pic_height", 1, 0, 20002}, ++ {"pic_position", 1, 0, 20003}, ++ {"pic_position_offset", 1, 0, 20004}, ++ {"pic_anim_type", 1, 0, 20005}, ++ {"pic_anim_loop", 1, 0, 20006}, ++ {"blob", 1, 0, 30000}, ++ {"blob_type", 1, 0, 30001}, ++ {"blob_picture_id", 1, 0, 30002}, ++ {NULL, 0, NULL, 0} ++}; ++ ++ ++int main(int argc, char **argv) ++{ ++ FILE *of; ++ char *ofn; ++ int c; ++ int option_index = 0; ++ ++ unsigned long ul; ++ struct splash_file_header fh = {}; ++ struct splash_pic_header ph[255]; ++ struct blob_entry *blob_first = NULL; ++ struct blob_entry *blob_last = NULL; ++ struct blob_entry *blob_cur = NULL; ++ ++ if (argc < 2) { ++ print_help(argv[0]); ++ return EXIT_FAILURE; ++ } ++ ++ ++ /* Parse and and execute user commands */ ++ while ((c = getopt_long(argc, argv, "h", ++ long_options, &option_index)) != -1) { ++ switch (c) { ++ case 10001: /* bg_red */ ++ ul = strtoul(optarg, NULL, 0); ++ OPT_MAX(ul, 255); ++ fh.bg_red = ul; ++ break; ++ case 10002: /* bg_green */ ++ ul = strtoul(optarg, NULL, 0); ++ OPT_MAX(ul, 255); ++ fh.bg_green = ul; ++ break; ++ case 10003: /* bg_blue */ ++ ul = strtoul(optarg, NULL, 0); ++ OPT_MAX(ul, 255); ++ fh.bg_blue = ul; ++ break; ++ case 10004: /* bg_reserved */ ++ ul = strtoul(optarg, NULL, 0); ++ OPT_MAX(ul, 255); ++ fh.bg_reserved = ul; ++ break; ++ case 10005: /* frame_ms */ ++ ul = strtoul(optarg, NULL, 0); ++ OPT_MAX(ul, 65535); ++ fh.frame_ms = ul; ++ break; ++ ++ ++ case 20000: /* picture */ ++ if (fh.num_pics >= 255) { ++ fprintf(stderr, "--%s: Picture array full\n", ++ long_options[option_index].name); ++ break; ++ } ++ ++ fh.num_pics++; ++ break; ++ ++ case 20001: /* pic_width */ ++ ul = strtoul(optarg, NULL, 0); ++ OPT_MAX(ul, 65535); ++ ph[fh.num_pics - 1].width = ul; ++ break; ++ ++ case 20002: /* pic_height */ ++ ul = strtoul(optarg, NULL, 0); ++ OPT_MAX(ul, 65535); ++ ph[fh.num_pics - 1].height = ul; ++ break; ++ ++ case 20003: /* pic_position */ ++ ul = strtoul(optarg, NULL, 0); ++ OPT_MAX(ul, 255); ++ ph[fh.num_pics - 1].position = ul; ++ break; ++ ++ case 20004: /* pic_position_offset */ ++ ul = strtoul(optarg, NULL, 0); ++ OPT_MAX(ul, 255); ++ ph[fh.num_pics - 1].position_offset = ul; ++ break; ++ ++ case 20005: /* pic_anim_type */ ++ ul = strtoul(optarg, NULL, 0); ++ OPT_MAX(ul, 255); ++ ph[fh.num_pics - 1].anim_type = ul; ++ break; ++ ++ case 20006: /* pic_anim_loop */ ++ ul = strtoul(optarg, NULL, 0); ++ OPT_MAX(ul, 255); ++ ph[fh.num_pics - 1].anim_loop = ul; ++ break; ++ ++ ++ case 30000: /* blob */ ++ if (fh.num_blobs >= 65535) { ++ fprintf(stderr, "--%s: Blob array full\n", ++ long_options[option_index].name); ++ break; ++ } ++ ++ blob_cur = calloc(1, sizeof(struct blob_entry)); ++ if (!blob_cur) { ++ fprintf(stderr, "--%s: Out of memory\n", ++ long_options[option_index].name); ++ break; ++ } ++ ++ blob_cur->fn = optarg; ++ if (fh.num_pics) ++ blob_cur->header.picture_id = fh.num_pics - 1; ++ ++ if (!blob_first) ++ blob_first = blob_cur; ++ if (blob_last) ++ blob_last->next = blob_cur; ++ blob_last = blob_cur; ++ fh.num_blobs++; ++ break; ++ ++ case 30001: /* blob_type */ ++ if (!blob_cur) { ++ fprintf(stderr, "--%s: No blob selected\n", ++ long_options[option_index].name); ++ break; ++ } ++ ++ ul = strtoul(optarg, NULL, 0); ++ OPT_MAX(ul, 255); ++ blob_cur->header.type = ul; ++ break; ++ ++ case 30002: /* blob_picture_id */ ++ if (!blob_cur) { ++ fprintf(stderr, "--%s: No blob selected\n", ++ long_options[option_index].name); ++ break; ++ } ++ ++ ul = strtoul(optarg, NULL, 0); ++ OPT_MAX(ul, 255); ++ blob_cur->header.picture_id = ul; ++ break; ++ ++ ++ ++ case 'h': ++ case '?': ++ default: ++ print_help(argv[0]); ++ goto EXIT; ++ } /* switch (c) */ ++ } /* while ((c = getopt_long(...)) != -1) */ ++ ++ /* Consume and drop lone arguments */ ++ while (optind < argc) { ++ ofn = argv[optind]; ++ optind++; ++ } ++ ++ ++ /* Read file lengths */ ++ for (blob_cur = blob_first; blob_cur; blob_cur = blob_cur->next) { ++ FILE *f; ++ long pos; ++ int i; ++ ++ if (!blob_cur->fn) ++ continue; ++ ++ f = fopen(blob_cur->fn, "rb"); ++ if (!f) ++ goto ERR_FILE_LEN; ++ ++ if (fseek(f, 0, SEEK_END)) ++ goto ERR_FILE_LEN; ++ ++ pos = ftell(f); ++ if (pos < 0 || pos > (1 << 30)) ++ goto ERR_FILE_LEN; ++ ++ blob_cur->header.length = pos; ++ ++ fclose(f); ++ continue; ++ ++ERR_FILE_LEN: ++ fprintf(stderr, "Error getting file length (or too long): %s\n", ++ blob_cur->fn); ++ if (f) ++ fclose(f); ++ continue; ++ } ++ ++ ++ /* Set magic headers */ ++#if __BYTE_ORDER == __BIG_ENDIAN ++ memcpy(&fh.id[0], BOOTSPLASH_MAGIC_BE, 16); ++#elif __BYTE_ORDER == __LITTLE_ENDIAN ++ memcpy(&fh.id[0], BOOTSPLASH_MAGIC_LE, 16); ++#else ++#error ++#endif ++ fh.version = BOOTSPLASH_VERSION; ++ ++ /* Set blob counts */ ++ for (blob_cur = blob_first; blob_cur; blob_cur = blob_cur->next) { ++ if (blob_cur->header.picture_id < fh.num_pics) ++ ph[blob_cur->header.picture_id].num_blobs++; ++ } ++ ++ ++ /* Dump structs */ ++ dump_file_header(&fh); ++ ++ for (ul = 0; ul < fh.num_pics; ul++) ++ dump_pic_header(&ph[ul]); ++ ++ for (blob_cur = blob_first; blob_cur; blob_cur = blob_cur->next) ++ dump_blob(blob_cur); ++ ++ ++ /* Write to file */ ++ printf("Writing splash to file: %s\n", ofn); ++ of = fopen(ofn, "wb"); ++ if (!of) ++ goto ERR_WRITING; ++ ++ if (fwrite(&fh, sizeof(struct splash_file_header), 1, of) != 1) ++ goto ERR_WRITING; ++ ++ for (ul = 0; ul < fh.num_pics; ul++) { ++ if (fwrite(&ph[ul], sizeof(struct splash_pic_header), 1, of) ++ != 1) ++ goto ERR_WRITING; ++ } ++ ++ blob_cur = blob_first; ++ while (blob_cur) { ++ struct blob_entry *blob_old = blob_cur; ++ FILE *f; ++ char *buf[256]; ++ uint32_t left; ++ ++ if (fwrite(&blob_cur->header, ++ sizeof(struct splash_blob_header), 1, of) != 1) ++ goto ERR_WRITING; ++ ++ if (!blob_cur->header.length || !blob_cur->fn) ++ continue; ++ ++ f = fopen(blob_cur->fn, "rb"); ++ if (!f) ++ goto ERR_FILE_COPY; ++ ++ left = blob_cur->header.length; ++ while (left >= sizeof(buf)) { ++ if (fread(buf, sizeof(buf), 1, f) != 1) ++ goto ERR_FILE_COPY; ++ if (fwrite(buf, sizeof(buf), 1, of) != 1) ++ goto ERR_FILE_COPY; ++ left -= sizeof(buf); ++ } ++ if (left) { ++ if (fread(buf, left, 1, f) != 1) ++ goto ERR_FILE_COPY; ++ if (fwrite(buf, left, 1, of) != 1) ++ goto ERR_FILE_COPY; ++ } ++ ++ /* Pad data stream to 16 bytes */ ++ if (left % 16) { ++ if (fwrite("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", ++ 16 - (left % 16), 1, of) != 1) ++ goto ERR_FILE_COPY; ++ } ++ ++ fclose(f); ++ blob_cur = blob_cur->next; ++ free(blob_old); ++ continue; ++ ++ERR_FILE_COPY: ++ if (f) ++ fclose(f); ++ goto ERR_WRITING; ++ } ++ ++ fclose(of); ++ ++EXIT: ++ return EXIT_SUCCESS; ++ ++ ++ERR_WRITING: ++ fprintf(stderr, "Error writing splash.\n"); ++ fprintf(stderr, "The output file is probably corrupt.\n"); ++ if (of) ++ fclose(of); ++ ++ while (blob_cur) { ++ struct blob_entry *blob_old = blob_cur; ++ ++ blob_cur = blob_cur->next; ++ free(blob_old); ++ } ++ ++ return EXIT_FAILURE; ++} |