Skip to content

Commit d26ed7a

Browse files
AlexTu2claude
andcommitted
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 FrameworkComputer/SoftwareFirmwareIssueTracker#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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014dAnHutxgoW4y9K7DAZomY
1 parent 8c891e3 commit d26ed7a

2 files changed

Lines changed: 58 additions & 16 deletions

File tree

‎zephyr/program/framework/Kconfig‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,3 +318,15 @@ module-str = FRAMEWORK board-specific code
318318
source "subsys/logging/Kconfig.template.log_config"
319319

320320
source "Kconfig.zephyr"
321+
322+
config PLATFORM_EC_FRAMEWORK_FNLOCK_CAPS_LED
323+
bool "Show Fn lock on the Caps Lock key LED"
324+
depends on PLATFORM_EC_FRAMEWORK_LAPTOP_13
325+
help
326+
Drive the Caps Lock key's LED from the Fn-lock state (toggled with
327+
Fn+Esc) instead of from Caps Lock. The EC does not expose Fn-lock
328+
state to the host, so this is the only way to see it without
329+
guessing. Caps Lock has no indicator when this is enabled.
330+
331+
The LED keeps the stock blanking rules: dark with the lid closed
332+
and outside S0.

‎zephyr/program/framework/src/keyboard_customization_13.c‎

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -171,39 +171,54 @@ void board_kblight_init(void)
171171
}
172172
}
173173

174+
#define FN_PRESSED BIT(0)
175+
#define FN_LOCKED BIT(1)
176+
static uint8_t Fn_key;
177+
174178
int caps_status_check(void)
175179
{
176180
return caps_led_status;
177181
}
178182

179183
void board_caps_led_control(int data)
180184
{
181-
if (data & CAPS_LED) {
182-
caps_led_status = 1;
183-
gpio_pin_set_dt(GPIO_DT_FROM_NODELABEL(gpio_cap_led), 1);
184-
} else {
185-
caps_led_status = 0;
186-
gpio_pin_set_dt(GPIO_DT_FROM_NODELABEL(gpio_cap_led), 0);
187-
}
185+
/* Host 8042 LED command; see common/keyboard_8042.c. */
186+
caps_led_status = (data & CAPS_LED) ? 1 : 0;
187+
188+
#ifndef CONFIG_PLATFORM_EC_FRAMEWORK_FNLOCK_CAPS_LED
189+
/* Boards keeping stock behaviour drive the LED straight from here. */
190+
gpio_pin_set_dt(GPIO_DT_FROM_NODELABEL(gpio_cap_led), caps_led_status);
191+
#endif
188192
}
189193

194+
/*
195+
* With CONFIG_PLATFORM_EC_FRAMEWORK_FNLOCK_CAPS_LED, lit means the F1-F12 row emits F1-F12 directly.
196+
*
197+
* Note this is also what restores the LED on boot: HOOK_CHIPSET_STARTUP runs
198+
* while power_get_state() is still POWER_S5S3, which the gating below rejects,
199+
* so the Fn-lock state recovered from BBRAM only becomes visible when
200+
* HOOK_CHIPSET_RESUME fires at POWER_S3S0. Do not narrow that state set
201+
* without revisiting boot-time restore.
202+
*/
190203
static void keyboard_caps_led_update(void)
191204
{
192205
enum power_state ps = power_get_state();
206+
#ifdef CONFIG_PLATFORM_EC_FRAMEWORK_FNLOCK_CAPS_LED
207+
int on = !!(Fn_key & FN_LOCKED);
208+
#else
209+
int on = caps_led_status;
210+
#endif
193211

194212
if (!lid_is_open() || !(ps == POWER_S0ixS0 || ps == POWER_S0 || ps == POWER_S3S0))
195-
gpio_pin_set_dt(GPIO_DT_FROM_NODELABEL(gpio_cap_led), 0);
196-
else
197-
gpio_pin_set_dt(GPIO_DT_FROM_NODELABEL(gpio_cap_led), caps_led_status);
213+
on = 0;
214+
215+
gpio_pin_set_dt(GPIO_DT_FROM_NODELABEL(gpio_cap_led), on);
198216
}
199217
DECLARE_HOOK(HOOK_LID_CHANGE, keyboard_caps_led_update, HOOK_PRIO_DEFAULT);
200218
DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, keyboard_caps_led_update, HOOK_PRIO_DEFAULT);
201219
DECLARE_HOOK(HOOK_CHIPSET_RESUME, keyboard_caps_led_update, HOOK_PRIO_DEFAULT);
202220

203221

204-
#define FN_PRESSED BIT(0)
205-
#define FN_LOCKED BIT(1)
206-
static uint8_t Fn_key;
207222
static uint32_t fn_key_table_media;
208223
static uint32_t fn_key_table;
209224

@@ -233,7 +248,13 @@ int fn_table_set(int8_t pressed, uint32_t fn_bit)
233248
return false;
234249
}
235250

236-
void fnkey_shutdown(void)
251+
/*
252+
* Persist backlight brightness plus the Fn-lock flag into one BBRAM byte.
253+
* Called at shutdown and on every Fn-lock toggle, so the state survives an
254+
* unclean power loss instead of reverting to whatever the last clean shutdown
255+
* stored -- which would leave the indicator confidently wrong.
256+
*/
257+
static void fnkey_save_kbstate(void)
237258
{
238259
uint8_t current_kb = 0;
239260

@@ -242,10 +263,15 @@ void fnkey_shutdown(void)
242263
else
243264
current_kb |= kblight_get() & KB_BRIGHTNESS_MASK;
244265

245-
if (Fn_key & FN_LOCKED) {
266+
if (Fn_key & FN_LOCKED)
246267
current_kb |= KB_FN_LOCKED;
247-
}
268+
248269
system_set_bbram(SYSTEM_BBRAM_IDX_KBSTATE, current_kb);
270+
}
271+
272+
void fnkey_shutdown(void)
273+
{
274+
fnkey_save_kbstate();
249275

250276
Fn_key &= ~FN_LOCKED;
251277
Fn_key &= ~FN_PRESSED;
@@ -405,6 +431,10 @@ int functional_hotkey(uint16_t *key_code, int8_t pressed)
405431
Fn_key &= ~FN_LOCKED;
406432
else
407433
Fn_key |= FN_LOCKED;
434+
fnkey_save_kbstate();
435+
#ifdef CONFIG_PLATFORM_EC_FRAMEWORK_FNLOCK_CAPS_LED
436+
keyboard_caps_led_update();
437+
#endif
408438
}
409439
return EC_ERROR_UNIMPLEMENTED;
410440
}

0 commit comments

Comments
 (0)