commit 12b41e011d7f3c2a73af8fb996451d3964177c54 Author: Dominic Grimm Date: Wed May 21 16:46:39 2025 +0200 Init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7e9e158 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# This file is for zig-specific build artifacts. +# If you have OS-specific or editor-specific files to ignore, +# such as *.swp or .DS_Store, put those in your global +# ~/.gitignore and put this in your ~/.gitconfig: +# +# [core] +# excludesfile = ~/.gitignore +# +# Cheers! +# -andrewrk + +.zig-cache/ +zig-out/ +/release/ +/debug/ +/build/ +/build-*/ +/docgen_tmp/ + +# Although this was renamed to .zig-cache, let's leave it here for a few +# releases to make it less annoying to work with multiple branches. +zig-cache/ diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..ce451ca --- /dev/null +++ b/build.zig @@ -0,0 +1,41 @@ +const std = @import("std"); + +const kindle_target_query: std.Target.Query = .{ + .cpu_arch = .arm, + .cpu_model = std.Target.Query.CpuModel{ + .explicit = &std.Target.arm.cpu.cortex_a8, + }, + .abi = .gnueabihf, + .os_tag = .linux, + .glibc_version = std.SemanticVersion.parse("2.12.0") catch unreachable, + .dynamic_linker = std.Target.DynamicLinker.init("/lib/ld-linux.so.3"), +}; + +pub fn build(b: *std.Build) void { + const kindle_target = b.resolveTargetQuery(kindle_target_query); + const optimize = b.standardOptimizeOption(.{}); + + const libkindle_sys = buildLibkindleSys(b, kindle_target, optimize); + b.installArtifact(libkindle_sys); + + const lib_mod = b.createModule(.{ + .root_source_file = b.path("src/root.zig"), + .target = kindle_target, + .optimize = optimize, + }); + lib_mod.linkLibrary(libkindle_sys); +} + +fn buildLibkindleSys(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { + const getauxval_backport_dep = b.dependency("getauxval_backport", .{ + .target = target, + .optimize = optimize, + .elf64 = false, + }); + const libgetauxval_backport = getauxval_backport_dep.artifact("getauxval_backport"); + + const libkindle_sys = @import("libs/kindle_sys.zig").create(b, target, optimize); + libkindle_sys.linkLibrary(libgetauxval_backport); + + return libkindle_sys; +} diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..d38009a --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,17 @@ +.{ + .name = .kindle, + .version = "0.0.0", + .fingerprint = 0x276419b66ac24917, + .minimum_zig_version = "0.14.0", + .dependencies = .{ + .getauxval_backport = .{ + .url = "git+https://git.dergrimm.net/dergrimm/getauxval-backport#e1a7e24c5cb9c42a9dbea5997bb03c2565f3f164", + .hash = "getauxval_backport-0.0.0-3FxplIwLAAAnSp0CfvCJmoYOjptZYCuz8d-fTRILQEct", + }, + }, + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + }, +} diff --git a/kindle/backports/fortify.c b/kindle/backports/fortify.c new file mode 100644 index 0000000..e574cf8 --- /dev/null +++ b/kindle/backports/fortify.c @@ -0,0 +1,21 @@ +#include +#include +#include +#include + +void __attribute__((noreturn)) __fortify_fail(const char *msg) { + fprintf(stderr, "*** ***: %s terminated\n", msg); + abort(); +} + +void __attribute__((noreturn)) __chk_fail(void) { + __fortify_fail("buffer overflow detected"); +} + +long int __fdelt_chk(long int d) { + if (d >= FD_SETSIZE) { + __chk_fail(); + } + + return d / __NFDBITS; +} diff --git a/kindle/sysroot/lib/libc.so b/kindle/sysroot/lib/libc.so new file mode 120000 index 0000000..f6190ad --- /dev/null +++ b/kindle/sysroot/lib/libc.so @@ -0,0 +1 @@ +./libc.so.6 \ No newline at end of file diff --git a/kindle/sysroot/lib/libc.so.6 b/kindle/sysroot/lib/libc.so.6 new file mode 100755 index 0000000..2ef46a4 Binary files /dev/null and b/kindle/sysroot/lib/libc.so.6 differ diff --git a/kindle/sysroot/usr/include/einkfb.h b/kindle/sysroot/usr/include/einkfb.h new file mode 100644 index 0000000..d99c053 --- /dev/null +++ b/kindle/sysroot/usr/include/einkfb.h @@ -0,0 +1,489 @@ +// NOTE: Upstream kernels available here: +// https://www.amazon.com/gp/help/customer/display.html?nodeId=200203720 + +#ifndef _EINKFB_H +#define _EINKFB_H + +#include + +#ifndef __KERNEL__ +#include +#endif + +#define EINK_1BPP 1 +#define EINK_2BPP 2 +#define EINK_4BPP 4 +#define EINK_8BPP 8 +#define EINK_BPP_MAX EINK_8BPP + +#define EINK_WHITE 0x00 // For whacking all the pixels in a... +#define EINK_BLACK 0xFF // ...byte (8, 4, 2, or 1) at once. + +// Replace EINK_WHITE & EINK_BLACK with the following macros. +// +#define eink_white(b) EINK_WHITE +#define eink_black(b) EINK_BLACK + +// For pixels (at bytes at a time) other than white/black, the following holds. +// +#define eink_pixels(b, p) (p) + +#define EINK_ORIENT_LANDSCAPE 1 +#define EINK_ORIENT_PORTRAIT 0 + +#define BPP_SIZE(r, b) (((r) * (b)) / 8) +#define BPP_MAX(b) (1 << (b)) + +#define U_IN_RANGE(n, m, M) \ + ((((n) == 0) && ((m) == 0)) || (((n) > (m)) && ((n) <= (M)))) +#define IN_RANGE(n, m, M) (((n) >= (m)) && ((n) <= (M))) + +#define ORIENTATION(x, y) \ + (((y) > (x)) ? EINK_ORIENT_PORTRAIT : EINK_ORIENT_LANDSCAPE) + +struct raw_image_t { + int xres, // image's width, in pixels + yres, // image's height + bpp; // image's pixel (bit) depth + + uint8_t start[]; // actual start of image +}; +typedef struct raw_image_t raw_image_t; + +struct image_t { + int xres, // image's visual width, in pixels + xlen, // image's actual width, used for rowbyte & memory size calculations + yres, // image's height + bpp; // image's pixel (bit) depth + + uint8_t *start; // pointer to start of image +}; +typedef struct image_t image_t; + +#define INIT_IMAGE_T() {0, 0, 0, 0, NULL} + +enum splash_screen_type { + // Simple (non-composite) splash screens. + // + // splash_screen_powering_off = 0, // Deprecated. + // splash_screen_powering_on, // Deprecated. + + // splash_screen_powering_off_wireless, // Deprecated. + // splash_screen_powering_on_wireless, // Deprecated. + + // splash_screen_exit, // Deprecated. + splash_screen_logo = 5, + + // splash_screen_usb_internal, // Deprecated. + // splash_screen_usb_external, // Deprecated. + // splash_screen_usb, // Deprecated. + + // splash_screen_sleep, // Deprecated. + // splash_screen_update, // Deprecated. + + // num_splash_screens, // Deprecated. + + // Composite splash screens & messages. + // + // splash_screen_drivemode_0, // Deprecated. + // splash_screen_drivemode_1, // Deprecated. + // splash_screen_drivemode_2, // Deprecated. + // splash_screen_drivemode_3, // Deprecated. + + splash_screen_power_off_clear_screen = + 16, // Message: clear screen and power down controller. + // splash_screen_screen_saver_picture, // Deprecated. + + splash_screen_shim_picture = 18, // Message: shim wants a picture displayed. + + splash_screen_lowbatt, // Picture: Not composite, post-legacy ordering (Mario + // only). + splash_screen_reboot, // Picture: Composite (not used on Fiona). + + splash_screen_update_initial, // Composite software-update screens. + splash_screen_update_success, // + splash_screen_update_failure, // + splash_screen_update_failure_no_wait, // + + splash_screen_repair_needed, // More composite screens. + splash_screen_boot, // + + splash_screen_invalid = -1 +}; +typedef enum splash_screen_type splash_screen_type; + +// Alias some of the legacy enumerations for Mario. +// +#define splash_screen_usb_recovery_util \ + ((splash_screen_type)8) // splash_screen_usb + +struct power_override_t { + unsigned int cmd; + unsigned long arg; +}; +typedef struct power_override_t power_override_t; + +enum fx_type { + // Deprecated from the HAL, but still supported by the Shim. + // + fx_mask = 11, // Only for use with update_area_t's non-NULL buffer which_fx. + fx_buf_is_mask = 14, // Same as fx_mask, but doesn't require a doubling (i.e., + // the buffer & mask are the same). + + fx_none = -1, // No legacy-FX to apply. + + // Screen-update FX, supported by HAL. + // + fx_flash = + 20, // Only for use with update_area_t (for faking a flashing update). + fx_invert = 21, // Only for use with update_area_t (only inverts output data). + + fx_update_partial = 0, // Higher-speed, lower-fidelity update at bit depth's + // number of grays (aka non-flashing). + fx_update_full = 1, // Higher-fidelity, lower speed update at bit depth's + // number of grays (aka flashing). + + fx_update_fast = + 2, // Sacrifices all fidelity for speed (will be non-flashing). + fx_update_slow = 3, // Sacrifices any speed for fidelity (will be flashing). + + fx_buffer_load = + 99, // Just load the hardware buffer; don't update the display. + fx_buffer_display_partial = + 100, // Display whatever's in the hardware's buffer, non-flashing style. + fx_buffer_display_full = + 101 // Display whatever's in the hardware's buffer, flashing style. +}; +typedef enum fx_type fx_type; + +// The only valid legacy-FX types for area updates are fx_mask and +// fx_buf_is_mask. +// +#define UPDATE_AREA_FX(f) ((fx_mask == (f)) || (fx_buf_is_mask == (f))) + +// Fast page turns consists of both fast updates to begin with, which are low +// fidelity, and then a slow update at the end of the fast-update sequence to +// restore fidelity to the display. +// +#define UPDATE_FAST_PAGE_TURN(f) \ + ((fx_update_fast == (f)) || (fx_update_slow == (f))) + +// The default ("none") for area updates is partial (non-flashing); full +// (flashing) updates are for FX and such (i.e., explicit completion is +// desired). +// +#define UPDATE_AREA_PART(f) \ + ((fx_none == (f)) || (fx_buffer_display_partial == (f)) || \ + (fx_update_partial == (f)) || (fx_update_fast == (f))) + +#define UPDATE_AREA_FULL(f) \ + (UPDATE_AREA_FX(f) || (fx_buffer_display_full == (f)) || \ + (fx_update_full == (f)) || (fx_update_slow == (f))) + +#define UPDATE_AREA_MODE(f) \ + (UPDATE_AREA_FULL(f) ? fx_update_full : fx_update_partial) + +// For use with the FBIO_EINK_UPDATE_DISPLAY ioctl. +// +#define UPDATE_PART(f) \ + ((fx_buffer_display_partial == (f)) || (fx_update_partial == (f)) || \ + (fx_update_fast == (f))) + +#define UPDATE_FULL(f) \ + ((fx_buffer_display_full == (f)) || (fx_update_full == (f)) || \ + (fx_update_slow == (f))) + +#define UPDATE_MODE(f) (UPDATE_FULL(f) ? fx_update_full : fx_update_partial) + +#define UPDATE_MODE_BUFFER(f) \ + ((fx_buffer_load == (f)) || (fx_buffer_display_partial == (f)) || \ + (fx_buffer_display_full == (f))) + +#define SKIP_BUFFERS_EQUAL(f, c) \ + (UPDATE_MODE_BUFFER(f) || (fx_update_slow == (f)) || (contrast_off != (c))) + +struct rect_t { + // Note: The bottom-right (x2, y2) coordinate is actually such that (x2 - x1) + // and (y2 - y1) + // are xres and yres, respectively, when normally xres and yres would + // be (x2 - x1) + 1 and (y2 - y1) + 1, respectively. + // + int x1, y1, x2, y2; +}; +typedef struct rect_t rect_t; + +#define INIT_RECT_T() {0, 0, 0, 0} +#define MAX_EXCLUDE_RECTS 8 + +struct fx_t { + fx_type update_mode, // Screen-update FX: fx_update_full | fx_update_partial. + which_fx; // Shim (legacy) FX. + + int num_exclude_rects; // 0..MAX_EXCLUDE_RECTS. + rect_t exclude_rects[MAX_EXCLUDE_RECTS]; +}; +typedef struct fx_t fx_t; + +#define INIT_FX_T() \ + { \ + fx_update_partial, fx_none, 0, { \ + INIT_RECT_T(), INIT_RECT_T(), INIT_RECT_T(), INIT_RECT_T(), \ + INIT_RECT_T(), INIT_RECT_T(), INIT_RECT_T(), INIT_RECT_T() \ + } \ + } + +struct update_area_t { + // Note: The bottom-right (x2, y2) coordinate is actually such that (x2 - x1) + // and (y2 - y1) + // are xres and yres, respectively, when normally xres and yres would + // be (x2 - x1) + 1 and (y2 - y1) + 1, respectively. + // + int x1, y1, // Top-left... + x2, y2; // ...bottom-right. + + fx_type which_fx; // FX to use. + + uint8_t *buffer; // If NULL, extract from framebuffer, top-left to + // bottom-right, by rowbytes. +}; +typedef struct update_area_t update_area_t; + +#define INIT_UPDATE_AREA_T() {0, 0, 0, 0, fx_none, NULL} + +struct progressbar_xy_t { + int x, y; // Top-left corner of progressbar's position (ignores x for now). +}; +typedef struct progressbar_xy_t progressbar_xy_t; + +enum screen_saver_t { screen_saver_invalid = 0, screen_saver_valid }; +typedef enum screen_saver_t screen_saver_t; + +enum orientation_t { + orientation_portrait, + orientation_portrait_upside_down, + orientation_landscape, + orientation_landscape_upside_down +}; +typedef enum orientation_t orientation_t; + +#define num_orientations (orientation_landscape_upside_down + 1) + +#define ORIENTATION_PORTRAIT(o) \ + ((orientation_portrait == (o)) || (orientation_portrait_upside_down == (o))) + +#define ORIENTATION_LANDSCAPE(o) \ + ((orientation_landscape == (o)) || (orientation_landscape_upside_down == (o))) + +#define ORIENTATION_SAME(o1, o2) \ + ((ORIENTATION_PORTRAIT(o1) && ORIENTATION_PORTRAIT(o2)) || \ + (ORIENTATION_LANDSCAPE(o1) && ORIENTATION_LANDSCAPE(o2))) + +enum einkfb_events_t { + einkfb_event_update_display = 0, // FBIO_EINK_UPDATE_DISPLAY + einkfb_event_update_display_area, // FBIO_EINK_UPDATE_DISPLAY_AREA + + einkfb_event_blank_display, // FBIOBLANK (fb.h) + einkfb_event_rotate_display, // FBIO_EINK_SET_DISPLAY_ORIENTATION + + einkfb_event_null = -1 +}; +typedef enum einkfb_events_t einkfb_events_t; + +struct einkfb_event_t { + einkfb_events_t + event; // Not all einkfb_events_t use all of the einkfb_event_t fields. + + fx_type update_mode; // Screen-update FX: fx_update_full | fx_update_partial. + + // Note: The bottom-right (x2, y2) coordinate is actually such that (x2 - x1) + // and (y2 - y1) + // are xres and yres, respectively, when normally xres and yres would + // be (x2 - x1) + 1 and (y2 - y1) + 1, respectively. + // + int x1, y1, // Top-left... + x2, y2; // ...bottom-right. + + orientation_t orientation; // Display rotated into this orientation. +}; +typedef struct einkfb_event_t einkfb_event_t; + +enum reboot_behavior_t { + reboot_screen_asis, + reboot_screen_clear, + reboot_screen_splash +}; +typedef enum reboot_behavior_t reboot_behavior_t; + +enum progressbar_badge_t { + progressbar_badge_success, + progressbar_badge_failure, + + progressbar_badge_none +}; +typedef enum progressbar_badge_t progressbar_badge_t; + +enum sleep_behavior_t { + sleep_behavior_allow_sleep, + sleep_behavior_prevent_sleep +}; +typedef enum sleep_behavior_t sleep_behavior_t; + +enum contrast_t { + contrast_off, + + contrast_light, + contrast_medium, + contrast_dark, + + contrast_lighter, + contrast_lightest, + + contrast_darker, + contrast_darkest +}; +typedef enum contrast_t contrast_t; + +#define EINK_FRAME_BUFFER "/dev/fb/0" + +#define SIZEOF_EINK_EVENT sizeof(einkfb_event_t) +#define EINK_EVENTS "/dev/misc/eink_events" + +#define EINK_ROTATE_FILE "/sys/devices/platform/eink_fb.0/send_fake_rotate" +#define EINK_ROTATE_FILE_LEN 1 +#define ORIENT_PORTRAIT orientation_portrait +#define ORIENT_PORTRAIT_UPSIDE_DOWN orientation_portrait_upside_down +#define ORIENT_LANDSCAPE orientation_landscape +#define ORIENT_LANDSCAPE_UPSIDE_DOWN orientation_landscape_upside_down +#define ORIENT_ASIS (-1) + +#define EINK_USID_FILE "/var/local/eink/usid" + +#define EINK_CLEAR_SCREEN 0 +#define EINK_CLEAR_BUFFER 1 +#define EINK_CLEAR_SCREEN_NOT_BUFFER 2 + +#define FBIO_EINK_SCREEN_CLEAR FBIO_EINK_CLEAR_SCREEN, EINK_CLEAR_SCREEN +#define FBIO_EINK_BUFFER_CLEAR FBIO_EINK_CLEAR_SCREEN, EINK_CLEAR_BUFFER +#define FBIO_EINK_SCREEN_CLEAR_ONLY \ + FBIO_EINK_CLEAR_SCREEN, EINK_CLEAR_SCREEN_NOT_BUFFER + +#define FBIO_MIN_SCREEN splash_screen_powering_off +#define FBIO_MAX_SCREEN num_splash_screens +#define FBIO_SCREEN_IN_RANGE(s) \ + ((FBIO_MIN_SCREEN <= (s)) && (FBIO_MAX_SCREEN > (s))) + +#define FBIO_MAGIC_NUMBER 'F' + +// Implemented in the eInk HAL. +// +#define FBIO_EINK_UPDATE_DISPLAY \ + _IO(FBIO_MAGIC_NUMBER, 0xdb) // 0x46db (fx_type) +#define FBIO_EINK_UPDATE_DISPLAY_AREA \ + _IO(FBIO_MAGIC_NUMBER, 0xdd) // 0x46dd (update_area_t *) + +#define FBIO_EINK_RESTORE_DISPLAY \ + _IO(FBIO_MAGIC_NUMBER, 0xef) // 0x46ef (UPDATE_MODE(fx_type)) + +#define FBIO_EINK_SET_REBOOT_BEHAVIOR \ + _IO(FBIO_MAGIC_NUMBER, 0xe9) // 0x46e9 (reboot_behavior_t) +#define FBIO_EINK_GET_REBOOT_BEHAVIOR \ + _IO(FBIO_MAGIC_NUMBER, 0xed) // 0x46ed (reboot_behavior_t *) + +#define FBIO_EINK_SET_DISPLAY_ORIENTATION \ + _IO(FBIO_MAGIC_NUMBER, 0xf0) // 0x46f0 (orientation_t) +#define FBIO_EINK_GET_DISPLAY_ORIENTATION \ + _IO(FBIO_MAGIC_NUMBER, 0xf1) // 0x46f1 (orientation_t *) + +#define FBIO_EINK_SET_SLEEP_BEHAVIOR \ + _IO(FBIO_MAGIC_NUMBER, 0xf2) // 0x46f2 (sleep_behavior_t) +#define FBIO_EINK_GET_SLEEP_BEHAVIOR \ + _IO(FBIO_MAGIC_NUMBER, 0xf3) // 0x46f3 (sleep_behavior_t *) + +#define FBIO_EINK_SET_CONTRAST \ + _IO(FBIO_MAGIC_NUMBER, 0xf5) // 0x46f5 (contrast_t) + +// Implemented in the eInk Shim. +// +#define FBIO_EINK_UPDATE_DISPLAY_FX \ + _IO(FBIO_MAGIC_NUMBER, 0xe4) // 0x46e4 (fx_t *) +#define FBIO_EINK_SPLASH_SCREEN \ + _IO(FBIO_MAGIC_NUMBER, 0xdc) // 0x46dc (splash_screen_type) +#define FBIO_EINK_SPLASH_SCREEN_SLEEP \ + _IO(FBIO_MAGIC_NUMBER, 0xe0) // 0x46e0 (splash_screen_type) +#define FBIO_EINK_OFF_CLEAR_SCREEN \ + _IO(FBIO_MAGIC_NUMBER, 0xdf) // 0x46df (no args) +#define FBIO_EINK_CLEAR_SCREEN \ + _IO(FBIO_MAGIC_NUMBER, 0xe1) // 0x46e1 (EINK_CLEAR_SCREEN || EINK_CLEAR_BUFFER + // || EINK_CLEAR_SCREEN_NOT_BUFFER) +#define FBIO_EINK_POWER_OVERRIDE \ + _IO(FBIO_MAGIC_NUMBER, 0xe3) // 0x46e3 (power_override_t *) + +#define FBIO_EINK_PROGRESSBAR \ + _IO(FBIO_MAGIC_NUMBER, 0xea) // 0x46ea (int: 0..100 -> draw progressbar || + // !(0..100) -> clear progressbar) +#define FBIO_EINK_PROGRESSBAR_SET_XY \ + _IO(FBIO_MAGIC_NUMBER, 0xeb) // 0x46eb (progressbar_xy_t *) +#define FBIO_EINK_PROGRESSBAR_BADGE \ + _IO(FBIO_MAGIC_NUMBER, 0xec) // 0x46ec (progressbar_badge_t); +#define FBIO_EINK_PROGRESSBAR_BACKGROUND \ + _IO(FBIO_MAGIC_NUMBER, 0xf4) // 0x46f4 (int: EINKFB_WHITE || EINKFB_BLACK) + +#define IS_UPDATE_DISPLAY_CMD(c) \ + ((FBIO_EINK_UPDATE_DISPLAY == (c)) || \ + (FBIO_EINK_UPDATE_DISPLAY_FX == (c)) || \ + (FBIO_EINK_UPDATE_DISPLAY_AREA == (c)) || \ + (FBIO_EINK_RESTORE_DISPLAY == (c)) || (FBIO_EINK_CLEAR_SCREEN == (c))) + +#define IS_PROGRESSBAR_CMD(c) \ + ((FBIO_EINK_PROGRESSBAR == (c)) || (FBIO_EINK_PROGRESSBAR_SET_XY == (c)) || \ + (FBIO_EINK_PROGRESSBAR_BADGE == (c)) || \ + (FBIO_EINK_PROGRESSBAR_BACKGROUND == (c))) + +// Deprecated from the HAL & Shim. +// +// #define FBIO_EINK_UPDATE_DISPLAY_ASYNC _IO(FBIO_MAGIC_NUMBER, 0xde) // +// 0x46de (fx_type: fx_update_full || fx_update_partial) #define +// FBIO_EINK_FAKE_PNLCD _IO(FBIO_MAGIC_NUMBER, 0xe8) // 0x46e8 +// (char *) + +// For use with /proc/eink_fb/update_display. +// +#define PROC_EINK_UPDATE_DISPLAY_CLS 0 // FBIO_EINK_CLEAR_SCREEN +#define PROC_EINK_UPDATE_DISPLAY_PART \ + 1 // FBIO_EINK_UPDATE_DISPLAY(fx_update_partial) +#define PROC_EINK_UPDATE_DISPLAY_FULL \ + 2 // FBIO_EINK_UPDATE_DISPLAY(fx_update_full) +#define PROC_EINK_UPDATE_DISPLAY_AREA 3 // FBIO_EINK_UPDATE_DISPLAY_AREA +// #define PROC_EINK_UPDATE_DISPLAY_REST 4 // FBIO_EINK_RESTORE_SCREEN +#define PROC_EINK_UPDATE_DISPLAY_SCRN 5 // FBIO_EINK_SPLASH_SCREEN +#define PROC_EINK_UPDATE_DISPLAY_OVRD 6 // FBIO_EINK_FPOW_OVERRIDE +#define PROC_EINK_UPDATE_DISPLAY_FX 7 // FBIO_EINK_UPDATE_DISPLAY_FX +// #define PROC_EINK_UPDATE_DISPLAY_SYNC 8 // FBIO_EINK_SYNC_BUFFERS +// #define PROC_EINK_UPDATE_DISPLAY_PNLCD 9 // FBIO_EINK_FAKE_PNLCD +#define PROC_EINK_SET_REBOOT_BEHAVIOR 10 // FBIO_EINK_SET_REBOOT_BEHAVIOR +#define PROC_EINK_SET_PROGRESSBAR_XY 11 // FBIO_EINK_PROGRESSBAR_SET_XY +#define PROC_EINK_UPDATE_DISPLAY_SCRN_SLP 12 // FBIO_EINK_SPLASH_SCREEN_SLEEP +#define PROC_EINK_PROGRESSBAR_BADGE 13 // FBIO_EINK_PROGRESSBAR_BADGE +#define PROC_EINK_SET_DISPLAY_ORIENTATION \ + 14 // FBIO_EINK_SET_DISPLAY_ORIENTATION +#define PROC_EINK_RESTORE_DISPLAY 15 // FBIO_EINK_RESTORE_DISPLAY +#define PROC_EINK_SET_SLEEP_BEHAVIOR 16 // FBIO_EINK_SET_SLEEP_BEHAVIOR +#define PROC_EINK_PROGRESSBAR_BACKGROUND 17 // FBIO_EINK_PROGRESSBAR_BACKGROUND +#define PROC_EINK_UPDATE_DISPLAY_WHICH 18 // FBIO_EINK_UPDATE_DISPLAY +#define PROC_EINK_SET_CONTRAST 19 // FBIO_EINK_SET_CONTRAST + +// #define PROC_EINK_FAKE_PNLCD_TEST 100 // Programmatically drive +// FBIO_EINK_FAKE_PNLCD (not implemented). +#define PROC_EINK_GRAYSCALE_TEST \ + 101 // Fills display with white-to-black ramp at current bit depth. + +// Inter-module/inter-driver eink ioctl access. +// +extern int fiona_eink_ioctl_stub(unsigned int cmd, unsigned long arg); + +#define eink_sys_ioctl(cmd, arg) \ + (get_fb_ioctl() \ + ? (*get_fb_ioctl())((unsigned int)cmd, (unsigned long)arg) \ + : fiona_eink_ioctl_stub((unsigned int)cmd, (unsigned long)arg)) + +#endif // _EINKFB_H diff --git a/libs/kindle_sys.zig b/libs/kindle_sys.zig new file mode 100644 index 0000000..97e84c1 --- /dev/null +++ b/libs/kindle_sys.zig @@ -0,0 +1,21 @@ +const std = @import("std"); + +pub fn create(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { + const lib = b.addStaticLibrary(.{ + .name = "kindle_sys", + .target = target, + .optimize = optimize, + }); + + lib.addCSourceFile(.{ + .file = b.path("kindle/backports/fortify.c"), + }); + + lib.addLibraryPath(b.path("kindle/sysroot/lib")); + lib.linkSystemLibrary2("c", .{ .preferred_link_mode = .dynamic }); + + lib.addIncludePath(b.path("kindle/sysroot/usr/include")); + lib.installHeadersDirectory(b.path("kindle/sysroot/usr/include"), "", .{}); + + return lib; +} diff --git a/src/eink/fb.zig b/src/eink/fb.zig new file mode 100644 index 0000000..107f564 --- /dev/null +++ b/src/eink/fb.zig @@ -0,0 +1,125 @@ +const std = @import("std"); + +const sys = @import("sys.zig"); + +pub const FramebufferError = error{ + BppUnsupported, +}; + +pub const Framebuffer = struct { + const Self = @This(); + + file: std.fs.File, + data: []align(std.mem.page_size) u8, + + xres: u32, + yres: u32, + bits_per_pixel: u32, + + /// Extracted from `eips` binary + fn checkBppSupported(bpp: c_uint) bool { + return (bpp <= 8) and + ((@as(c_uint, 0x68800000) << @truncate(bpp)) >> ((@sizeOf(c_uint) * 8) - 1)) == 1; + } + + pub fn open(path: []const u8) !Self { + const file = try std.fs.cwd().openFile(path, .{ + .mode = .read_write, + }); + + const vinfo = try sys.vscreeninfo(file.handle); + if (!checkBppSupported(vinfo.bits_per_pixel)) { + return FramebufferError.BppUnsupported; + } + + const data = try sys.mmap_fb(file.handle, vinfo); + + return .{ + .file = file, + .data = data, + .xres = vinfo.xres, + .yres = vinfo.yres, + .bits_per_pixel = vinfo.bits_per_pixel, + }; + } + + pub fn deinit(self: *Self) void { + std.posix.munmap(self.data); + self.file.close(); + } + + pub fn fill(self: *Self, color: u8) void { + @memset(self.data, color); + } + + pub fn fillLine(self: *Self, y: u32, color: u8) void { + if (y >= self.yres) + return; + + const buf = self.data[self.offset(0, y)..self.offset(self.xres, y)]; + @memset(buf, color); + } + + pub fn fillLineVar(self: *Self, y: u32, x_start: u32, x_end: u32, color: u8) void { + if (y >= self.yres or + x_start > x_end or + x_start >= self.xres) + return; + + if (x_start == x_end) { + self.set(x_start, y, color); + return; + } + + const safe_x_end = @min(x_end, self.xres); + const buf = self.data[self.offset(x_start, y)..self.offset(safe_x_end, y)]; + @memset(buf, color); + } + + pub fn fillArea(self: *Self, region: sys.Region, color: u8) void { + if (region.top >= self.yres or + region.left >= self.xres or + region.width == 0 or + region.height == 0) + return; + + const y_end = @min(region.top + region.height + 1, self.yres); + const x_end = @min(region.left + region.width + 1, self.xres); + + for (region.top..y_end) |y| { + const buf = self.data[self.offset(region.left, y)..self.offset(x_end, y)]; + @memset(buf, color); + } + } + + pub fn clear(self: *Self) !void { + try sys.clear_screen(self.file.handle); + self.fill(0x00); + } + + pub fn update(self: *const Self, update_mode: sys.UpdateMode) !void { + try sys.update_display(self.file.handle, update_mode); + } + + pub fn updateArea(self: *const Self, region: sys.Region, update_mode: sys.UpdateMode) !void { + if (region.width == self.xres and region.height == self.yres) { + try sys.update_display(self.file.handle, update_mode); + } else { + try sys.update_display_area(self.file.handle, region, update_mode); + } + } + + inline fn offset(self: *const Self, x: usize, y: usize) usize { + return y * self.xres + x; + } + + pub fn get(self: *const Self, x: usize, y: usize) u8 { + return self.data[self.offset(x, y)]; + } + + pub fn set(self: *Self, x: usize, y: usize, color: u8) void { + if (x < self.xres and y < self.yres) { + self.data[self.offset(x, y)] = color; + } + } +}; diff --git a/src/eink/root.zig b/src/eink/root.zig new file mode 100644 index 0000000..14d9110 --- /dev/null +++ b/src/eink/root.zig @@ -0,0 +1 @@ +pub const fb = @import("fb.zig"); diff --git a/src/eink/sys.zig b/src/eink/sys.zig new file mode 100644 index 0000000..a8ff41f --- /dev/null +++ b/src/eink/sys.zig @@ -0,0 +1,90 @@ +const std = @import("std"); + +const c = @cImport({ + @cInclude("linux/fb.h"); + @cInclude("einkfb.h"); +}); + +fn handleErrno(rc: usize) std.posix.UnexpectedError!void { + const err = std.posix.errno(rc); + if (err != .SUCCESS) + return std.posix.unexpectedErrno(err); +} + +pub fn vscreeninfo(fd: std.posix.fd_t) std.posix.UnexpectedError!c.fb_var_screeninfo { + var vinfo: c.fb_var_screeninfo = undefined; + try handleErrno( + std.os.linux.ioctl(fd, c.FBIOGET_VSCREENINFO, @intFromPtr(&vinfo)), + ); + + return vinfo; +} + +pub fn mmap_fb(fd: std.posix.fd_t, vinfo: c.fb_var_screeninfo) ![]align(std.mem.page_size) u8 { + const data_size: usize = vinfo.xres * vinfo.yres * (vinfo.bits_per_pixel / 8); + const data = try std.posix.mmap( + null, + data_size, + std.posix.PROT.READ | std.posix.PROT.WRITE, + .{ + .TYPE = .SHARED, + .LOCKED = false, + }, + fd, + 0, + ); + + return data; +} + +pub fn clear_screen(fd: std.posix.fd_t) std.posix.UnexpectedError!void { + try handleErrno( + std.os.linux.ioctl(fd, c.FBIO_EINK_CLEAR_SCREEN, c.EINK_CLEAR_SCREEN), + ); +} + +pub const UpdateMode = enum { + full, + partial, + + fn toFxType(self: UpdateMode) c.fx_type { + switch (self) { + .full => return c.fx_update_full, + .partial => return c.fx_update_partial, + } + } +}; + +pub fn update_display(fd: std.posix.fd_t, update_mode: UpdateMode) std.posix.UnexpectedError!void { + const fx = update_mode.toFxType(); + + try handleErrno( + std.os.linux.ioctl(fd, c.FBIO_EINK_UPDATE_DISPLAY, @intCast(fx)), + ); +} + +pub fn Rect(comptime T: type) type { + return struct { + top: T, + left: T, + width: T, + height: T, + }; +} + +pub const Region = Rect(u32); + +pub fn update_display_area(fd: std.posix.fd_t, region: Region, update_mode: UpdateMode) std.posix.UnexpectedError!void { + const area: c.update_area_t = .{ + .x1 = @intCast(region.left), + .y1 = @intCast(region.top), + .x2 = @intCast(region.left + region.width), + .y2 = @intCast(region.top + region.height), + .which_fx = update_mode.toFxType(), + .buffer = 0, + }; + + try handleErrno( + std.os.linux.ioctl(fd, c.FBIO_EINK_UPDATE_DISPLAY_AREA, @intFromPtr(&area)), + ); +} diff --git a/src/root.zig b/src/root.zig new file mode 100644 index 0000000..1f12a2f --- /dev/null +++ b/src/root.zig @@ -0,0 +1 @@ +pub const eink = @import("eink/root.zig");