boot: add support for button commands

With the relatively new button API in U-Boot, it's now much easier to
model the common usecase of mapping arbitrary actions to different
buttons during boot - for example entering fastboot mode, setting some
additional kernel cmdline arguments, or booting with a custom recovery
ramdisk, to name a few.

Historically, this functionality has been implemented in board code,
making it fixed for a given U-Boot binary and requiring the code be
duplicated and modified for every board.

Implement a generic abstraction to run an arbitrary command during boot
when a specific button is pressed. The button -> command mapping is
configured via environment variables with the following format:

  button_cmd_N_name=<button label>
  button_cmd_N=<command to run>

Where N is the mapping number starting from 0. For example:

  button_cmd_0_name=vol_down
  button_cmd_0=fastboot usb 0

This will cause the device to enter fastboot mode if volume down is held
during boot.

After we enter the cli loop the button commands are no longer valid,
this allows the buttons to additionally be used for navigating a boot
menu.

Tested-by: Svyatoslav Ryhel <clamor95@gmail.com> # Tegra30
Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
This commit is contained in:
Caleb Connolly 2024-01-09 11:51:09 +00:00 committed by Tom Rini
parent 348ea87850
commit e761035b64
6 changed files with 115 additions and 0 deletions

View file

@ -20,6 +20,21 @@ config TIMESTAMP
loaded that does not, the message 'Wrong FIT format: no timestamp'
is shown.
config BUTTON_CMD
bool "Support for running a command if a button is held during boot"
depends on CMDLINE
depends on BUTTON
help
For many embedded devices it's useful to enter a special flashing mode
such as fastboot mode when a button is held during boot. This option
allows arbitrary commands to be assigned to specific buttons. These will
be run after "preboot" if the button is held. Configuration is done via
the environment variables "button_cmd_N_name" and "button_cmd_N" where n is
the button number (starting from 0). e.g:
"button_cmd_0_name=vol_down"
"button_cmd_0=fastboot usb 0"
menuconfig FIT
bool "Flattened Image Tree (FIT)"
select HASH

View file

@ -12,6 +12,7 @@ obj-y += cli_getch.o cli_simple.o cli_readline.o
obj-$(CONFIG_HUSH_OLD_PARSER) += cli_hush.o
obj-$(CONFIG_HUSH_MODERN_PARSER) += cli_hush_modern.o
obj-$(CONFIG_AUTOBOOT) += autoboot.o
obj-$(CONFIG_BUTTON_CMD) += button_cmd.o
obj-y += version.o
# # boards

83
common/button_cmd.c Normal file
View file

@ -0,0 +1,83 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2023 Linaro Ltd.
* Author: Caleb Connolly <caleb.connolly@linaro.org>
*/
#include <button.h>
#include <command.h>
#include <env.h>
#include <log.h>
#include <vsprintf.h>
/* Some sane limit "just in case" */
#define MAX_BTN_CMDS 32
struct button_cmd {
bool pressed;
const char *btn_name;
const char *cmd;
};
/*
* Button commands are set via environment variables, e.g.:
* button_cmd_N_name=Volume Up
* button_cmd_N=fastboot usb 0
*
* This function will retrieve the command for the given button N
* and populate the cmd struct with the command string and pressed
* state of the button.
*
* Returns 1 if a command was found, 0 otherwise.
*/
static int get_button_cmd(int n, struct button_cmd *cmd)
{
const char *cmd_str;
struct udevice *btn;
char buf[24];
snprintf(buf, sizeof(buf), "button_cmd_%d_name", n);
cmd->btn_name = env_get(buf);
if (!cmd->btn_name)
return 0;
button_get_by_label(cmd->btn_name, &btn);
if (!btn) {
log_err("No button labelled '%s'\n", cmd->btn_name);
return 0;
}
cmd->pressed = button_get_state(btn) == BUTTON_ON;
/* If the button isn't pressed then cmd->cmd will be unused so don't waste
* cycles reading it
*/
if (!cmd->pressed)
return 1;
snprintf(buf, sizeof(buf), "button_cmd_%d", n);
cmd_str = env_get(buf);
if (!cmd_str) {
log_err("No command set for button '%s'\n", cmd->btn_name);
return 0;
}
cmd->cmd = cmd_str;
return 1;
}
void process_button_cmds(void)
{
struct button_cmd cmd = {0};
int i = 0;
while (get_button_cmd(i++, &cmd) && i < MAX_BTN_CMDS) {
if (!cmd.pressed)
continue;
log_info("BTN '%s'> %s\n", cmd.btn_name, cmd.cmd);
run_command(cmd.cmd, CMD_FLAG_ENV);
/* Don't run commands for multiple buttons */
return;
}
}

View file

@ -8,6 +8,7 @@
#include <common.h>
#include <autoboot.h>
#include <button.h>
#include <bootstage.h>
#include <bootstd.h>
#include <cli.h>
@ -62,6 +63,8 @@ void main_loop(void)
efi_launch_capsules();
}
process_button_cmds();
s = bootdelay_process();
if (cli_process_fdt(&s))
cli_secure_boot_cmd(s);

View file

@ -190,6 +190,10 @@ bootm_size
bootstopkeysha256, bootdelaykey, bootstopkey
See README.autoboot
button_cmd_0, button_cmd_0_name ... button_cmd_N, button_cmd_N_name
Used to map commands to run when a button is held during boot.
See CONFIG_BUTTON_CMD.
updatefile
Location of the software update file on a TFTP server, used
by the automatic software update feature. Please refer to

View file

@ -74,4 +74,13 @@ enum button_state_t button_get_state(struct udevice *dev);
*/
int button_get_code(struct udevice *dev);
#if IS_ENABLED(CONFIG_BUTTON_CMD)
/* Process button command mappings specified in the environment,
* running the commands for buttons which are pressed
*/
void process_button_cmds(void);
#else
static inline void process_button_cmds(void) {}
#endif /* CONFIG_BUTTON_CMD */
#endif