From 8c891e3488c8aaace9ebca1f7e93389e5d4172fb Mon Sep 17 00:00:00 2001 From: Alex Tu <6798052+AlexTu2@users.noreply.github.com> Date: Fri, 11 Sep 2026 03:07:17 -0400 Subject: [PATCH 1/2] fwk: keyboard: restore auto backlight when Fn lock is set board_kblight_init() masks KB_FN_LOCKED off the BBRAM byte before calling kblight_set(), but compares the unmasked byte against KEYBOARD_BL_BRIGHTNESS_AUTO (101). fnkey_shutdown() stores the Fn-lock flag in bit 7 of that same byte, so with Fn lock enabled the stored value is 101 | 0x80 = 229, the compare never matches, and auto keyboard backlight is silently lost on every boot for anyone who leaves Fn lock on. Apply KB_BRIGHTNESS_MASK before the compare, and use the existing macro in place of the literal 0x7F on the line above. Tested on a Framework Laptop 13 Pro (sakura), together with the following commit: with Fn lock on and the keyboard backlight set to auto (Fn+Space), auto is retained across a reboot. Also builds for azalea and marigold, which share this file; not tested on their hardware. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_014dAnHutxgoW4y9K7DAZomY --- .../program/framework/src/keyboard_customization_13.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/zephyr/program/framework/src/keyboard_customization_13.c b/zephyr/program/framework/src/keyboard_customization_13.c index 3299572534..799fd27b2b 100644 --- a/zephyr/program/framework/src/keyboard_customization_13.c +++ b/zephyr/program/framework/src/keyboard_customization_13.c @@ -159,8 +159,14 @@ void board_kblight_init(void) uint8_t current_kblight = 0; if (system_get_bbram(SYSTEM_BBRAM_IDX_KBSTATE, ¤t_kblight) == EC_SUCCESS) { - kblight_set(current_kblight & 0x7F); - if (current_kblight == KEYBOARD_BL_BRIGHTNESS_AUTO) + kblight_set(current_kblight & KB_BRIGHTNESS_MASK); + /* + * Mask off KB_FN_LOCKED before comparing: fnkey_shutdown() + * packs the Fn-lock flag into bit 7 of this same byte, so an + * unmasked compare never matches once Fn lock has been used. + */ + if ((current_kblight & KB_BRIGHTNESS_MASK) == + KEYBOARD_BL_BRIGHTNESS_AUTO) kb_als_auto_brightness = true; } } From d26ed7ae462ca563ca6aaf6bd272ad3dfa35e6cc Mon Sep 17 00:00:00 2001 From: Alex Tu <6798052+AlexTu2@users.noreply.github.com> Date: Fri, 11 Sep 2026 03:07:17 -0400 Subject: [PATCH 2/2] fwk: keyboard: optionally show Fn lock on the Caps Lock LED Fn lock (Fn+Esc) is tracked only inside the EC: there is no host command or HID report for it, so there is no way to tell which state the top row is in without pressing a key. See https://github.com/FrameworkComputer/SoftwareFirmwareIssueTracker/issues/176 Add CONFIG_PLATFORM_EC_FRAMEWORK_FNLOCK_CAPS_LED (default n). When enabled, gpio_cap_led follows Fn_key & FN_LOCKED instead of the host's 8042 Caps Lock state, using the existing lid/S0 blanking in keyboard_caps_led_update(). With the option off, LED behaviour is unchanged. Independently of the option, persist the Fn-lock flag to BBRAM on every toggle rather than only in fnkey_shutdown(), so an unclean power loss does not restore whatever the last clean shutdown stored. fnkey_save_kbstate() is factored out of fnkey_shutdown() for this; Fn_key moves above the LED code so the updater can read it. Boot-time restore relies on HOOK_CHIPSET_RESUME: HOOK_CHIPSET_STARTUP fires while power_get_state() is still POWER_S5S3, which the blanking rejects, so a call from fnkey_startup() would always write 0. A comment says so. Tested on a Framework Laptop 13 Pro (sakura) with the option enabled. That build is byte-identical to the image flashed for testing. - Fn+Esc toggles the LED - the LED goes dark with the lid closed and returns when it is opened - the LED is dark in suspend and returns on wake - Fn lock and the LED state are restored after a reboot Not tested: restoring the Fn-lock flag after an unclean power loss, and any hardware other than sakura. With the option off, sakura, azalea and marigold build, but that configuration was not run on hardware. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_014dAnHutxgoW4y9K7DAZomY --- zephyr/program/framework/Kconfig | 12 ++++ .../framework/src/keyboard_customization_13.c | 62 ++++++++++++++----- 2 files changed, 58 insertions(+), 16 deletions(-) diff --git a/zephyr/program/framework/Kconfig b/zephyr/program/framework/Kconfig index 424943db5c..5e91593c50 100644 --- a/zephyr/program/framework/Kconfig +++ b/zephyr/program/framework/Kconfig @@ -318,3 +318,15 @@ module-str = FRAMEWORK board-specific code source "subsys/logging/Kconfig.template.log_config" source "Kconfig.zephyr" + +config PLATFORM_EC_FRAMEWORK_FNLOCK_CAPS_LED + bool "Show Fn lock on the Caps Lock key LED" + depends on PLATFORM_EC_FRAMEWORK_LAPTOP_13 + help + Drive the Caps Lock key's LED from the Fn-lock state (toggled with + Fn+Esc) instead of from Caps Lock. The EC does not expose Fn-lock + state to the host, so this is the only way to see it without + guessing. Caps Lock has no indicator when this is enabled. + + The LED keeps the stock blanking rules: dark with the lid closed + and outside S0. diff --git a/zephyr/program/framework/src/keyboard_customization_13.c b/zephyr/program/framework/src/keyboard_customization_13.c index 799fd27b2b..220337962c 100644 --- a/zephyr/program/framework/src/keyboard_customization_13.c +++ b/zephyr/program/framework/src/keyboard_customization_13.c @@ -171,6 +171,10 @@ void board_kblight_init(void) } } +#define FN_PRESSED BIT(0) +#define FN_LOCKED BIT(1) +static uint8_t Fn_key; + int caps_status_check(void) { return caps_led_status; @@ -178,32 +182,43 @@ int caps_status_check(void) void board_caps_led_control(int data) { - if (data & CAPS_LED) { - caps_led_status = 1; - gpio_pin_set_dt(GPIO_DT_FROM_NODELABEL(gpio_cap_led), 1); - } else { - caps_led_status = 0; - gpio_pin_set_dt(GPIO_DT_FROM_NODELABEL(gpio_cap_led), 0); - } + /* Host 8042 LED command; see common/keyboard_8042.c. */ + caps_led_status = (data & CAPS_LED) ? 1 : 0; + +#ifndef CONFIG_PLATFORM_EC_FRAMEWORK_FNLOCK_CAPS_LED + /* Boards keeping stock behaviour drive the LED straight from here. */ + gpio_pin_set_dt(GPIO_DT_FROM_NODELABEL(gpio_cap_led), caps_led_status); +#endif } +/* + * With CONFIG_PLATFORM_EC_FRAMEWORK_FNLOCK_CAPS_LED, lit means the F1-F12 row emits F1-F12 directly. + * + * Note this is also what restores the LED on boot: HOOK_CHIPSET_STARTUP runs + * while power_get_state() is still POWER_S5S3, which the gating below rejects, + * so the Fn-lock state recovered from BBRAM only becomes visible when + * HOOK_CHIPSET_RESUME fires at POWER_S3S0. Do not narrow that state set + * without revisiting boot-time restore. + */ static void keyboard_caps_led_update(void) { enum power_state ps = power_get_state(); +#ifdef CONFIG_PLATFORM_EC_FRAMEWORK_FNLOCK_CAPS_LED + int on = !!(Fn_key & FN_LOCKED); +#else + int on = caps_led_status; +#endif if (!lid_is_open() || !(ps == POWER_S0ixS0 || ps == POWER_S0 || ps == POWER_S3S0)) - gpio_pin_set_dt(GPIO_DT_FROM_NODELABEL(gpio_cap_led), 0); - else - gpio_pin_set_dt(GPIO_DT_FROM_NODELABEL(gpio_cap_led), caps_led_status); + on = 0; + + gpio_pin_set_dt(GPIO_DT_FROM_NODELABEL(gpio_cap_led), on); } DECLARE_HOOK(HOOK_LID_CHANGE, keyboard_caps_led_update, HOOK_PRIO_DEFAULT); DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, keyboard_caps_led_update, HOOK_PRIO_DEFAULT); DECLARE_HOOK(HOOK_CHIPSET_RESUME, keyboard_caps_led_update, HOOK_PRIO_DEFAULT); -#define FN_PRESSED BIT(0) -#define FN_LOCKED BIT(1) -static uint8_t Fn_key; static uint32_t fn_key_table_media; static uint32_t fn_key_table; @@ -233,7 +248,13 @@ int fn_table_set(int8_t pressed, uint32_t fn_bit) return false; } -void fnkey_shutdown(void) +/* + * Persist backlight brightness plus the Fn-lock flag into one BBRAM byte. + * Called at shutdown and on every Fn-lock toggle, so the state survives an + * unclean power loss instead of reverting to whatever the last clean shutdown + * stored -- which would leave the indicator confidently wrong. + */ +static void fnkey_save_kbstate(void) { uint8_t current_kb = 0; @@ -242,10 +263,15 @@ void fnkey_shutdown(void) else current_kb |= kblight_get() & KB_BRIGHTNESS_MASK; - if (Fn_key & FN_LOCKED) { + if (Fn_key & FN_LOCKED) current_kb |= KB_FN_LOCKED; - } + system_set_bbram(SYSTEM_BBRAM_IDX_KBSTATE, current_kb); +} + +void fnkey_shutdown(void) +{ + fnkey_save_kbstate(); Fn_key &= ~FN_LOCKED; Fn_key &= ~FN_PRESSED; @@ -405,6 +431,10 @@ int functional_hotkey(uint16_t *key_code, int8_t pressed) Fn_key &= ~FN_LOCKED; else Fn_key |= FN_LOCKED; + fnkey_save_kbstate(); +#ifdef CONFIG_PLATFORM_EC_FRAMEWORK_FNLOCK_CAPS_LED + keyboard_caps_led_update(); +#endif } return EC_ERROR_UNIMPLEMENTED; }