diff mbox series

[v2] boot: add support for button commands

Message ID 20240109115310.1917139-1-caleb.connolly@linaro.org
State New
Headers show
Series [v2] boot: add support for button commands | expand

Commit Message

Caleb Connolly Jan. 9, 2024, 11:51 a.m. UTC
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>
---
Changes since v1:
 * make get_button_cmd() static.
 * use #if IS_ENABLED(CONFIG_BUTTON_CMD) instead of #ifdef
   CONFIG_BUTTON_CMD.
 * Kconfig fixes
---
 boot/Kconfig              | 15 +++++++
 common/Makefile           |  1 +
 common/button_cmd.c       | 83 +++++++++++++++++++++++++++++++++++++++
 common/main.c             |  3 ++
 doc/usage/environment.rst |  4 ++
 include/button.h          |  9 +++++
 6 files changed, 115 insertions(+)
 create mode 100644 common/button_cmd.c

Comments

Dragan Simic Jan. 10, 2024, 6:08 p.m. UTC | #1
On 2024-01-09 12:51, Caleb Connolly wrote:
> 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.

This is simply awesome, but I see one possible issue -- the need to have 
proper environment variables defined for a particular board or device, 
to make the buttons work as expected.  Obviously, those environment 
variables can be absent or can become missing for numerous reasons.

I think that we should have an additional mechanism in place that 
defines the buttons and the associated commands even if no environment 
variables are found.  Like a set of fallback defaults for a particular 
board or device, built into the U-Boot image.  For example, Rockchip 
boards have those defaults pretty well defined.

> 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>
> ---
> Changes since v1:
>  * make get_button_cmd() static.
>  * use #if IS_ENABLED(CONFIG_BUTTON_CMD) instead of #ifdef
>    CONFIG_BUTTON_CMD.
>  * Kconfig fixes
> ---
>  boot/Kconfig              | 15 +++++++
>  common/Makefile           |  1 +
>  common/button_cmd.c       | 83 +++++++++++++++++++++++++++++++++++++++
>  common/main.c             |  3 ++
>  doc/usage/environment.rst |  4 ++
>  include/button.h          |  9 +++++
>  6 files changed, 115 insertions(+)
>  create mode 100644 common/button_cmd.c
> 
> diff --git a/boot/Kconfig b/boot/Kconfig
> index fbc49c5bca47..882835731ea9 100644
> --- a/boot/Kconfig
> +++ b/boot/Kconfig
> @@ -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
> diff --git a/common/Makefile b/common/Makefile
> index cdeadf72026c..53105a6ce12a 100644
> --- a/common/Makefile
> +++ b/common/Makefile
> @@ -10,6 +10,7 @@ obj-y += main.o
>  obj-y += exports.o
>  obj-$(CONFIG_HUSH_PARSER) += cli_hush.o
>  obj-$(CONFIG_AUTOBOOT) += autoboot.o
> +obj-$(CONFIG_BUTTON_CMD) += button_cmd.o
> 
>  # # boards
>  obj-y += board_f.o
> diff --git a/common/button_cmd.c b/common/button_cmd.c
> new file mode 100644
> index 000000000000..b6a8434d6f29
> --- /dev/null
> +++ b/common/button_cmd.c
> @@ -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;
> +	}
> +}
> diff --git a/common/main.c b/common/main.c
> index 7c70de2e59a8..717e8b7e8bd2 100644
> --- a/common/main.c
> +++ b/common/main.c
> @@ -8,6 +8,7 @@
> 
>  #include <common.h>
>  #include <autoboot.h>
> +#include <button.h>
>  #include <bootstage.h>
>  #include <cli.h>
>  #include <command.h>
> @@ -61,6 +62,8 @@ void main_loop(void)
>  			efi_launch_capsules();
>  	}
> 
> +	process_button_cmds();
> +
>  	s = bootdelay_process();
>  	if (cli_process_fdt(&s))
>  		cli_secure_boot_cmd(s);
> diff --git a/doc/usage/environment.rst b/doc/usage/environment.rst
> index c57b717caaf3..ce5a9627f025 100644
> --- a/doc/usage/environment.rst
> +++ b/doc/usage/environment.rst
> @@ -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
> diff --git a/include/button.h b/include/button.h
> index 207f4a0f4dbd..8d38e521324d 100644
> --- a/include/button.h
> +++ b/include/button.h
> @@ -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
Caleb Connolly Jan. 10, 2024, 8:09 p.m. UTC | #2
On 10/01/2024 18:08, Dragan Simic wrote:
> On 2024-01-09 12:51, Caleb Connolly wrote:
>> 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.
> 
> This is simply awesome, but I see one possible issue -- the need to have
> proper environment variables defined for a particular board or device,
> to make the buttons work as expected.  Obviously, those environment
> variables can be absent or can become missing for numerous reasons.

Is CFG_EXTRA_ENV_SETTINGS not persistent enough?
> 
> I think that we should have an additional mechanism in place that
> defines the buttons and the associated commands even if no environment
> variables are found.  Like a set of fallback defaults for a particular
> board or device, built into the U-Boot image.  For example, Rockchip
> boards have those defaults pretty well defined.

A programmatic API for register button/cmd mapping from
board_late_init() (for example) sounds sensible to me.

Is this really an issue that invalidates the implementation proposed
here though? It feels much more like a nice-to-have addition that maybe
we could leave out for now?

It also has a MUCH wider scope imo - should board override env or vice
versa? What about triggering default AND custom actions for one button
press? What if a board wants to register a callback function instead of
running a command?) - these are questions I don't want to answer with
this patch.
> 
>> 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>
>> ---
>> Changes since v1:
>>  * make get_button_cmd() static.
>>  * use #if IS_ENABLED(CONFIG_BUTTON_CMD) instead of #ifdef
>>    CONFIG_BUTTON_CMD.
>>  * Kconfig fixes
>> ---
>>  boot/Kconfig              | 15 +++++++
>>  common/Makefile           |  1 +
>>  common/button_cmd.c       | 83 +++++++++++++++++++++++++++++++++++++++
>>  common/main.c             |  3 ++
>>  doc/usage/environment.rst |  4 ++
>>  include/button.h          |  9 +++++
>>  6 files changed, 115 insertions(+)
>>  create mode 100644 common/button_cmd.c
>>
>> diff --git a/boot/Kconfig b/boot/Kconfig
>> index fbc49c5bca47..882835731ea9 100644
>> --- a/boot/Kconfig
>> +++ b/boot/Kconfig
>> @@ -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
>> diff --git a/common/Makefile b/common/Makefile
>> index cdeadf72026c..53105a6ce12a 100644
>> --- a/common/Makefile
>> +++ b/common/Makefile
>> @@ -10,6 +10,7 @@ obj-y += main.o
>>  obj-y += exports.o
>>  obj-$(CONFIG_HUSH_PARSER) += cli_hush.o
>>  obj-$(CONFIG_AUTOBOOT) += autoboot.o
>> +obj-$(CONFIG_BUTTON_CMD) += button_cmd.o
>>
>>  # # boards
>>  obj-y += board_f.o
>> diff --git a/common/button_cmd.c b/common/button_cmd.c
>> new file mode 100644
>> index 000000000000..b6a8434d6f29
>> --- /dev/null
>> +++ b/common/button_cmd.c
>> @@ -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;
>> +    }
>> +}
>> diff --git a/common/main.c b/common/main.c
>> index 7c70de2e59a8..717e8b7e8bd2 100644
>> --- a/common/main.c
>> +++ b/common/main.c
>> @@ -8,6 +8,7 @@
>>
>>  #include <common.h>
>>  #include <autoboot.h>
>> +#include <button.h>
>>  #include <bootstage.h>
>>  #include <cli.h>
>>  #include <command.h>
>> @@ -61,6 +62,8 @@ void main_loop(void)
>>              efi_launch_capsules();
>>      }
>>
>> +    process_button_cmds();
>> +
>>      s = bootdelay_process();
>>      if (cli_process_fdt(&s))
>>          cli_secure_boot_cmd(s);
>> diff --git a/doc/usage/environment.rst b/doc/usage/environment.rst
>> index c57b717caaf3..ce5a9627f025 100644
>> --- a/doc/usage/environment.rst
>> +++ b/doc/usage/environment.rst
>> @@ -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
>> diff --git a/include/button.h b/include/button.h
>> index 207f4a0f4dbd..8d38e521324d 100644
>> --- a/include/button.h
>> +++ b/include/button.h
>> @@ -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
Michael Walle Jan. 11, 2024, 9:38 a.m. UTC | #3
>> This is simply awesome, but I see one possible issue -- the need to have
>> proper environment variables defined for a particular board or device,
>> to make the buttons work as expected.  Obviously, those environment
>> variables can be absent or can become missing for numerous reasons.
> 
> Is CFG_EXTRA_ENV_SETTINGS not persistent enough?

IMHO no. Because a user might accidentially mess up the environment
variables.

>> I think that we should have an additional mechanism in place that
>> defines the buttons and the associated commands even if no environment
>> variables are found.  Like a set of fallback defaults for a particular
>> board or device, built into the U-Boot image.  For example, Rockchip
>> boards have those defaults pretty well defined.
> 
> A programmatic API for register button/cmd mapping from
> board_late_init() (for example) sounds sensible to me.

I don't know if it has to be that complex, or if it will be enough
to just have some compile-time constants like CONFIG_BUTTON_CMD_N.

> Is this really an issue that invalidates the implementation proposed
> here though? It feels much more like a nice-to-have addition that maybe
> we could leave out for now?

Agreed. Looks like one can add it to get_button_cmd() later.

> It also has a MUCH wider scope imo - should board override env or vice
> versa? What about triggering default AND custom actions for one button
> press? What if a board wants to register a callback function instead of
> running a command?) - these are questions I don't want to answer with
> this patch.

One use case I have is restoring the default environment *in any case*;
regardless what the state of the board is. In this case, the environment
must not override the button settings. This can be a compiled-in
command, which always takes precedence.

If you want to be able to overwrite a button command, then I guess you
can already do that with the environment setting. Provide sane defaults
via CFG_EXTRA_ENV_SETTINGS and a user can then overwrite it.

In summary, the registered (compiled-in) command should always take
precedence. If one wants to supply a default command which can be
changed later, that can go via the (compiled-in) default environment.

-michael
Dragan Simic Jan. 18, 2024, 6:57 a.m. UTC | #4
Hello Caleb and Michael,

On 2024-01-11 10:38, Michael Walle wrote:
>>> This is simply awesome, but I see one possible issue -- the need to 
>>> have
>>> proper environment variables defined for a particular board or 
>>> device,
>>> to make the buttons work as expected.  Obviously, those environment
>>> variables can be absent or can become missing for numerous reasons.
>> 
>> Is CFG_EXTRA_ENV_SETTINGS not persistent enough?
> 
> IMHO no. Because a user might accidentially mess up the environment
> variables.

I apologize for my delayed response.

Using CONFIG_EXTRA_ENV_SETTINGS should be good enough to provide
the fallback defaults.  However, the users can still mess the things up,
but again, they can do that already in many places.

>>> I think that we should have an additional mechanism in place that
>>> defines the buttons and the associated commands even if no 
>>> environment
>>> variables are found.  Like a set of fallback defaults for a 
>>> particular
>>> board or device, built into the U-Boot image.  For example, Rockchip
>>> boards have those defaults pretty well defined.
>> 
>> A programmatic API for register button/cmd mapping from
>> board_late_init() (for example) sounds sensible to me.
> 
> I don't know if it has to be that complex, or if it will be enough
> to just have some compile-time constants like CONFIG_BUTTON_CMD_N.

Having compile-time constants is also a viable option, IMHO.  We should
just need some fallback mechanism, and the simpler the better.

>> Is this really an issue that invalidates the implementation proposed
>> here though? It feels much more like a nice-to-have addition that 
>> maybe
>> we could leave out for now?
> 
> Agreed. Looks like one can add it to get_button_cmd() later.

I also agree, but it should be added eventually.

>> It also has a MUCH wider scope imo - should board override env or vice
>> versa? What about triggering default AND custom actions for one button
>> press? What if a board wants to register a callback function instead 
>> of
>> running a command?) - these are questions I don't want to answer with
>> this patch.

Yes, all this opens quite a few questions.  IMHO, we should simply have
some fallback mechanism, for which using CONFIG_EXTRA_ENV_SETTINGS seems
fine to me, and leave everything else to as it already is.  Sure, the
users could mess it up, but that can already happen in many places.

> One use case I have is restoring the default environment *in any case*;
> regardless what the state of the board is. In this case, the 
> environment
> must not override the button settings. This can be a compiled-in
> command, which always takes precedence.

To me, having an option to restore the default environment isn't a bad
idea, but not automatically and always.  Perhaps some other mechanism 
that
triggers the restoration should be devised.

> If you want to be able to overwrite a button command, then I guess you
> can already do that with the environment setting. Provide sane defaults
> via CFG_EXTRA_ENV_SETTINGS and a user can then overwrite it.

I agree.

> In summary, the registered (compiled-in) command should always take
> precedence. If one wants to supply a default command which can be
> changed later, that can go via the (compiled-in) default environment.

Sorry, this is a bit confusing to me.  Didn't you write above that
the users should be able to change the associated commands through
the environment variables?
Michael Walle Jan. 18, 2024, 7:59 a.m. UTC | #5
Hi,

> Using CONFIG_EXTRA_ENV_SETTINGS should be good enough to provide
> the fallback defaults.  However, the users can still mess the things 
> up,
> but again, they can do that already in many places.

I disagree. In my case that is a last resort recovery. And it should
work in any case. Even if the user has messed up anything (except
from erasing the bootloader in the SPI flash ;)).

>> In summary, the registered (compiled-in) command should always take
>> precedence. If one wants to supply a default command which can be
>> changed later, that can go via the (compiled-in) default environment.
> 
> Sorry, this is a bit confusing to me.  Didn't you write above that
> the users should be able to change the associated commands through
> the environment variables?

I had two kinds of button commands in mind: immutable ones and mutable
ones. The first can be achieved with compiled-in commands, the second
with a default environment and environment variables.

Also, whether a command is a mutable one or not is the decision of
the developer (or the one who's compiling/configuring u-boot),
not the user.

-michael
Dragan Simic Jan. 18, 2024, 2:33 p.m. UTC | #6
On 2024-01-18 08:59, Michael Walle wrote:
>> Using CONFIG_EXTRA_ENV_SETTINGS should be good enough to provide
>> the fallback defaults.  However, the users can still mess the things 
>> up,
>> but again, they can do that already in many places.
> 
> I disagree. In my case that is a last resort recovery. And it should
> work in any case. Even if the user has messed up anything (except
> from erasing the bootloader in the SPI flash ;)).

Maybe the solution could be another compile-time option to "lock down" 
the built-in defaults provided through CONFIG_EXTRA_ENV_SETTINGS?  If 
that new option is selected, changes to the environment would make no 
changes to the built-in defaults, i.e. those parts of the environment 
would actually be ignored.

>>> In summary, the registered (compiled-in) command should always take
>>> precedence. If one wants to supply a default command which can be
>>> changed later, that can go via the (compiled-in) default environment.
>> 
>> Sorry, this is a bit confusing to me.  Didn't you write above that
>> the users should be able to change the associated commands through
>> the environment variables?
> 
> I had two kinds of button commands in mind: immutable ones and mutable
> ones. The first can be achieved with compiled-in commands, the second
> with a default environment and environment variables.
> 
> Also, whether a command is a mutable one or not is the decision of
> the developer (or the one who's compiling/configuring u-boot),
> not the user.

I believe that the additional compile-time option, which I proposed 
above, could be extended to specify which of the built-in default 
button-command associations are immutable, and which are allowed to be 
modified through the environment variables.
Michael Walle Jan. 18, 2024, 3:11 p.m. UTC | #7
>>> Using CONFIG_EXTRA_ENV_SETTINGS should be good enough to provide
>>> the fallback defaults.  However, the users can still mess the things 
>>> up,
>>> but again, they can do that already in many places.
>> 
>> I disagree. In my case that is a last resort recovery. And it should
>> work in any case. Even if the user has messed up anything (except
>> from erasing the bootloader in the SPI flash ;)).
> 
> Maybe the solution could be another compile-time option to "lock down"
> the built-in defaults provided through CONFIG_EXTRA_ENV_SETTINGS?  If
> that new option is selected, changes to the environment would make no
> changes to the built-in defaults, i.e. those parts of the environment
> would actually be ignored.

Not sure locking down the whole environment is a good idea.

>>>> In summary, the registered (compiled-in) command should always take
>>>> precedence. If one wants to supply a default command which can be
>>>> changed later, that can go via the (compiled-in) default 
>>>> environment.
>>> 
>>> Sorry, this is a bit confusing to me.  Didn't you write above that
>>> the users should be able to change the associated commands through
>>> the environment variables?
>> 
>> I had two kinds of button commands in mind: immutable ones and mutable
>> ones. The first can be achieved with compiled-in commands, the second
>> with a default environment and environment variables.
>> 
>> Also, whether a command is a mutable one or not is the decision of
>> the developer (or the one who's compiling/configuring u-boot),
>> not the user.
> 
> I believe that the additional compile-time option, which I proposed
> above, could be extended to specify which of the built-in default
> button-command associations are immutable, and which are allowed to
> be modified through the environment variables.

IIRC there is already a mechanism for that. Environment hooks
or something like that. But I'm not sure that has other implications
and qualify as simple and lightweight for this use-case.

Anyway, we digress. I just wanted to make you aware of another
use-case, which btw. is already done today in the lsxl board for
example.

-michael
Dragan Simic Jan. 18, 2024, 3:14 p.m. UTC | #8
On 2024-01-18 16:11, Michael Walle wrote:
>>>> Using CONFIG_EXTRA_ENV_SETTINGS should be good enough to provide
>>>> the fallback defaults.  However, the users can still mess the things 
>>>> up,
>>>> but again, they can do that already in many places.
>>> 
>>> I disagree. In my case that is a last resort recovery. And it should
>>> work in any case. Even if the user has messed up anything (except
>>> from erasing the bootloader in the SPI flash ;)).
>> 
>> Maybe the solution could be another compile-time option to "lock down"
>> the built-in defaults provided through CONFIG_EXTRA_ENV_SETTINGS?  If
>> that new option is selected, changes to the environment would make no
>> changes to the built-in defaults, i.e. those parts of the environment
>> would actually be ignored.
> 
> Not sure locking down the whole environment is a good idea.

Not the entire environment, just the default button-command associations
supplied through CONFIG_EXTRA_ENV_SETTINGS.  I'm sorry if I didn't write
it clearly before.

>>>>> In summary, the registered (compiled-in) command should always take
>>>>> precedence. If one wants to supply a default command which can be
>>>>> changed later, that can go via the (compiled-in) default 
>>>>> environment.
>>>> 
>>>> Sorry, this is a bit confusing to me.  Didn't you write above that
>>>> the users should be able to change the associated commands through
>>>> the environment variables?
>>> 
>>> I had two kinds of button commands in mind: immutable ones and 
>>> mutable
>>> ones. The first can be achieved with compiled-in commands, the second
>>> with a default environment and environment variables.
>>> 
>>> Also, whether a command is a mutable one or not is the decision of
>>> the developer (or the one who's compiling/configuring u-boot),
>>> not the user.
>> 
>> I believe that the additional compile-time option, which I proposed
>> above, could be extended to specify which of the built-in default
>> button-command associations are immutable, and which are allowed to
>> be modified through the environment variables.
> 
> IIRC there is already a mechanism for that. Environment hooks
> or something like that. But I'm not sure that has other implications
> and qualify as simple and lightweight for this use-case.
> 
> Anyway, we digress. I just wanted to make you aware of another
> use-case, which btw. is already done today in the lsxl board for
> example.
Tom Rini Feb. 13, 2024, 10:32 p.m. UTC | #9
On Tue, Jan 09, 2024 at 11:51:09AM +0000, Caleb Connolly wrote:

> 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>

Applied to u-boot/master, thanks!
diff mbox series

Patch

diff --git a/boot/Kconfig b/boot/Kconfig
index fbc49c5bca47..882835731ea9 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -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
diff --git a/common/Makefile b/common/Makefile
index cdeadf72026c..53105a6ce12a 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -10,6 +10,7 @@  obj-y += main.o
 obj-y += exports.o
 obj-$(CONFIG_HUSH_PARSER) += cli_hush.o
 obj-$(CONFIG_AUTOBOOT) += autoboot.o
+obj-$(CONFIG_BUTTON_CMD) += button_cmd.o
 
 # # boards
 obj-y += board_f.o
diff --git a/common/button_cmd.c b/common/button_cmd.c
new file mode 100644
index 000000000000..b6a8434d6f29
--- /dev/null
+++ b/common/button_cmd.c
@@ -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;
+	}
+}
diff --git a/common/main.c b/common/main.c
index 7c70de2e59a8..717e8b7e8bd2 100644
--- a/common/main.c
+++ b/common/main.c
@@ -8,6 +8,7 @@ 
 
 #include <common.h>
 #include <autoboot.h>
+#include <button.h>
 #include <bootstage.h>
 #include <cli.h>
 #include <command.h>
@@ -61,6 +62,8 @@  void main_loop(void)
 			efi_launch_capsules();
 	}
 
+	process_button_cmds();
+
 	s = bootdelay_process();
 	if (cli_process_fdt(&s))
 		cli_secure_boot_cmd(s);
diff --git a/doc/usage/environment.rst b/doc/usage/environment.rst
index c57b717caaf3..ce5a9627f025 100644
--- a/doc/usage/environment.rst
+++ b/doc/usage/environment.rst
@@ -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
diff --git a/include/button.h b/include/button.h
index 207f4a0f4dbd..8d38e521324d 100644
--- a/include/button.h
+++ b/include/button.h
@@ -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