mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-29 18:11:05 +00:00
button: Implement emulated debug mode using buttons for detachables
BUG=b:36394093 BRANCH=None TEST=make -j buildall. Verified following actions: Vup+Vdn (10 seconds) --> Vdn --> Vup : Warm reset AP Vup+Vdn (10 seconds) --> Vdn -> Power: Exit debug state Vup+Vdn (10 seconds) --> Vup --> Vdn : Restart chrome Vup+Vdn (10 seconds) --> Vup --> Power : Exit debug state Vup+Vdn (10 seconds) --> Vup --> Vup --> Vdn : No action defined Vup+Vdn (10 seconds) --> Vup --> Vup --> Power: Exit debug state Vup+Vdn (10 seconds) --> Vup --> Vup --> Vup --> Vdn : Kernel panic Vup+Vdn (10 seconds) --> Vup --> Vup --> Vup --> Power: Exit debug state Vup+Vdn (10 seconds) --> Vup --> Vup --> Vup --> Vup: Exit debug state Vup+Vdn (10 seconds) --> Vdn --> Vdn : Exit debug state Change-Id: Ic49cc7463f6d8a00f3b4586754feeb3a7d23c371 Signed-off-by: Furquan Shaikh <furquan@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/520564 Reviewed-by: Nicolas Boichat <drinkcat@chromium.org>
This commit is contained in:
committed by
chrome-bot
parent
60ce79badd
commit
5ed0e0f76f
306
common/button.c
306
common/button.c
@@ -6,6 +6,7 @@
|
||||
/* Button module for Chrome EC */
|
||||
|
||||
#include "button.h"
|
||||
#include "chipset.h"
|
||||
#include "common.h"
|
||||
#include "console.h"
|
||||
#include "gpio.h"
|
||||
@@ -175,6 +176,12 @@ void button_init(void)
|
||||
static void button_change_deferred(void);
|
||||
DECLARE_DEFERRED(button_change_deferred);
|
||||
|
||||
#ifdef CONFIG_EMULATED_SYSRQ
|
||||
static void debug_mode_handle(void);
|
||||
DECLARE_DEFERRED(debug_mode_handle);
|
||||
DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, debug_mode_handle, HOOK_PRIO_LAST);
|
||||
#endif
|
||||
|
||||
static void button_change_deferred(void)
|
||||
{
|
||||
int i;
|
||||
@@ -192,6 +199,14 @@ static void button_change_deferred(void)
|
||||
new_pressed = raw_button_pressed(&buttons[i]);
|
||||
if (state[i].debounced_pressed != new_pressed) {
|
||||
state[i].debounced_pressed = new_pressed;
|
||||
#ifdef CONFIG_EMULATED_SYSRQ
|
||||
/*
|
||||
* Calling deferred function for handling debug
|
||||
* mode so that button change processing is not
|
||||
* delayed.
|
||||
*/
|
||||
hook_call_deferred(&debug_mode_handle_data, 0);
|
||||
#endif
|
||||
CPRINTS("Button '%s' was %s",
|
||||
buttons[i].name, new_pressed ?
|
||||
"pressed" : "released");
|
||||
@@ -311,3 +326,294 @@ DECLARE_CONSOLE_COMMAND(button, console_command_button,
|
||||
"vup|vdown msec",
|
||||
"Simulate button press");
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_EMULATED_SYSRQ
|
||||
|
||||
enum debug_state {
|
||||
STATE_DEBUG_NONE,
|
||||
STATE_DEBUG_CHECK,
|
||||
STATE_STAGING,
|
||||
STATE_DEBUG_MODE_ACTIVE,
|
||||
STATE_SYSRQ_PATH,
|
||||
STATE_WARM_RESET_PATH,
|
||||
STATE_SYSRQ_EXEC,
|
||||
STATE_WARM_RESET_EXEC,
|
||||
};
|
||||
|
||||
#define DEBUG_BTN_POWER (1 << 0)
|
||||
#define DEBUG_BTN_VOL_UP (1 << 1)
|
||||
#define DEBUG_BTN_VOL_DN (1 << 2)
|
||||
#define DEBUG_TIMEOUT (10 * SECOND)
|
||||
|
||||
static enum debug_state curr_debug_state = STATE_DEBUG_NONE;
|
||||
static enum debug_state next_debug_state = STATE_DEBUG_NONE;
|
||||
static timestamp_t debug_state_deadline;
|
||||
static int debug_button_hit_count;
|
||||
|
||||
static int debug_button_mask(void)
|
||||
{
|
||||
int mask = 0;
|
||||
|
||||
/* Get power button state */
|
||||
if (power_button_is_pressed())
|
||||
mask |= DEBUG_BTN_POWER;
|
||||
|
||||
/* Get volume up state */
|
||||
if (state[BUTTON_VOLUME_UP].debounced_pressed)
|
||||
mask |= DEBUG_BTN_VOL_UP;
|
||||
|
||||
/* Get volume down state */
|
||||
if (state[BUTTON_VOLUME_DOWN].debounced_pressed)
|
||||
mask |= DEBUG_BTN_VOL_DN;
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static int debug_button_pressed(int mask)
|
||||
{
|
||||
return debug_button_mask() == mask;
|
||||
}
|
||||
|
||||
static int debug_mode_blink_led(void)
|
||||
{
|
||||
return ((curr_debug_state != STATE_DEBUG_NONE) &&
|
||||
(curr_debug_state != STATE_DEBUG_CHECK));
|
||||
}
|
||||
|
||||
static void debug_mode_transition(enum debug_state next_state)
|
||||
{
|
||||
timestamp_t now = get_time();
|
||||
#ifdef CONFIG_LED_COMMON
|
||||
int curr_blink_state = debug_mode_blink_led();
|
||||
#endif
|
||||
|
||||
/* Cancel any deferred calls. */
|
||||
hook_call_deferred(&debug_mode_handle_data, -1);
|
||||
|
||||
/* Update current debug mode state. */
|
||||
curr_debug_state = next_state;
|
||||
|
||||
/* Set deadline to 10seconds from current time. */
|
||||
debug_state_deadline.val = now.val + DEBUG_TIMEOUT;
|
||||
|
||||
switch (curr_debug_state) {
|
||||
case STATE_DEBUG_NONE:
|
||||
/*
|
||||
* Nothing is done here since some states can transition to
|
||||
* STATE_DEBUG_NONE in this function. Wait until all other
|
||||
* states are evaluated to take the action for STATE_NONE.
|
||||
*/
|
||||
break;
|
||||
case STATE_DEBUG_CHECK:
|
||||
case STATE_STAGING:
|
||||
/*
|
||||
* Schedule a deferred call after DEBUG_TIMEOUT to check for
|
||||
* button state if it does not change during the timeout
|
||||
* duration.
|
||||
*/
|
||||
hook_call_deferred(&debug_mode_handle_data, DEBUG_TIMEOUT);
|
||||
break;
|
||||
case STATE_DEBUG_MODE_ACTIVE:
|
||||
debug_button_hit_count = 0;
|
||||
break;
|
||||
case STATE_SYSRQ_PATH:
|
||||
/*
|
||||
* Increment debug_button_hit_count and ensure it does not go
|
||||
* past 3. If it exceeds the limit transition to STATE_NONE.
|
||||
*/
|
||||
debug_button_hit_count++;
|
||||
if (debug_button_hit_count == 4)
|
||||
curr_debug_state = STATE_DEBUG_NONE;
|
||||
break;
|
||||
case STATE_WARM_RESET_PATH:
|
||||
break;
|
||||
case STATE_SYSRQ_EXEC:
|
||||
/*
|
||||
* Depending upon debug_button_hit_count, send appropriate
|
||||
* number of sysrq events to host and transition to STATE_NONE.
|
||||
*/
|
||||
while (debug_button_hit_count) {
|
||||
host_send_sysrq('x');
|
||||
CPRINTS("DEBUG MODE: sysrq-x sent");
|
||||
debug_button_hit_count--;
|
||||
}
|
||||
curr_debug_state = STATE_DEBUG_NONE;
|
||||
break;
|
||||
case STATE_WARM_RESET_EXEC:
|
||||
/* Warm reset the host and transition to STATE_NONE. */
|
||||
chipset_reset(0);
|
||||
CPRINTS("DEBUG MODE: Warm reset triggered");
|
||||
curr_debug_state = STATE_DEBUG_NONE;
|
||||
break;
|
||||
default:
|
||||
curr_debug_state = STATE_DEBUG_NONE;
|
||||
}
|
||||
|
||||
if (curr_debug_state != STATE_DEBUG_NONE)
|
||||
return;
|
||||
|
||||
/* If state machine reached initial state, reset all variables. */
|
||||
CPRINTS("DEBUG MODE: Exit!");
|
||||
next_debug_state = STATE_DEBUG_NONE;
|
||||
debug_state_deadline.val = 0;
|
||||
debug_button_hit_count = 0;
|
||||
#ifdef CONFIG_LED_COMMON
|
||||
if (curr_blink_state)
|
||||
led_control(EC_LED_ID_SYSRQ_DEBUG_LED, LED_STATE_RESET);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void debug_mode_handle(void)
|
||||
{
|
||||
int mask;
|
||||
|
||||
switch (curr_debug_state) {
|
||||
case STATE_DEBUG_NONE:
|
||||
/*
|
||||
* If user pressed Vup+Vdn, check for next 10 seconds to see if
|
||||
* user keeps holding the keys.
|
||||
*/
|
||||
if (debug_button_pressed(DEBUG_BTN_VOL_UP | DEBUG_BTN_VOL_DN))
|
||||
debug_mode_transition(STATE_DEBUG_CHECK);
|
||||
break;
|
||||
case STATE_DEBUG_CHECK:
|
||||
/*
|
||||
* If no key is pressed or any key combo other than Vup+Vdn is
|
||||
* held, then quit debug check mode.
|
||||
*/
|
||||
if (!debug_button_pressed(DEBUG_BTN_VOL_UP | DEBUG_BTN_VOL_DN))
|
||||
debug_mode_transition(STATE_DEBUG_NONE);
|
||||
else if (timestamp_expired(debug_state_deadline, NULL)) {
|
||||
/*
|
||||
* If Vup+Vdn are held down for 10 seconds, then its
|
||||
* time to enter debug mode.
|
||||
*/
|
||||
CPRINTS("DEBUG MODE: Active!");
|
||||
next_debug_state = STATE_DEBUG_MODE_ACTIVE;
|
||||
debug_mode_transition(STATE_STAGING);
|
||||
}
|
||||
break;
|
||||
case STATE_STAGING:
|
||||
mask = debug_button_mask();
|
||||
|
||||
/* If no button is pressed, transition to next state. */
|
||||
if (!mask) {
|
||||
debug_mode_transition(next_debug_state);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Exit debug mode if keys are stuck for > 10 seconds. */
|
||||
if (timestamp_expired(debug_state_deadline, NULL))
|
||||
debug_mode_transition(STATE_DEBUG_NONE);
|
||||
else {
|
||||
timestamp_t now = get_time();
|
||||
|
||||
/*
|
||||
* Schedule a deferred call in case timeout hasn't
|
||||
* occurred yet.
|
||||
*/
|
||||
hook_call_deferred(&debug_mode_handle_data,
|
||||
(debug_state_deadline.val - now.val));
|
||||
}
|
||||
|
||||
break;
|
||||
case STATE_DEBUG_MODE_ACTIVE:
|
||||
mask = debug_button_mask();
|
||||
|
||||
/*
|
||||
* Continue in this state if button is not pressed and timeout
|
||||
* has not occurred.
|
||||
*/
|
||||
if (!mask && !timestamp_expired(debug_state_deadline, NULL))
|
||||
return;
|
||||
|
||||
/* Exit debug mode if valid buttons are not pressed. */
|
||||
if ((mask != DEBUG_BTN_VOL_UP) && (mask != DEBUG_BTN_VOL_DN)) {
|
||||
debug_mode_transition(STATE_DEBUG_NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transition to STAGING state with next state set to:
|
||||
* 1. SYSRQ_PATH : If Vup was pressed.
|
||||
* 2. WARM_RESET_PATH: If Vdn was pressed.
|
||||
*/
|
||||
if (mask == DEBUG_BTN_VOL_UP)
|
||||
next_debug_state = STATE_SYSRQ_PATH;
|
||||
else
|
||||
next_debug_state = STATE_WARM_RESET_PATH;
|
||||
|
||||
debug_mode_transition(STATE_STAGING);
|
||||
break;
|
||||
case STATE_SYSRQ_PATH:
|
||||
mask = debug_button_mask();
|
||||
|
||||
/*
|
||||
* Continue in this state if button is not pressed and timeout
|
||||
* has not occurred.
|
||||
*/
|
||||
if (!mask && !timestamp_expired(debug_state_deadline, NULL))
|
||||
return;
|
||||
|
||||
/* Exit debug mode if valid buttons are not pressed. */
|
||||
if ((mask != DEBUG_BTN_VOL_UP) && (mask != DEBUG_BTN_VOL_DN)) {
|
||||
debug_mode_transition(STATE_DEBUG_NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mask == DEBUG_BTN_VOL_UP) {
|
||||
/*
|
||||
* Else transition to STAGING state with next state set
|
||||
* to SYSRQ_PATH.
|
||||
*/
|
||||
next_debug_state = STATE_SYSRQ_PATH;
|
||||
} else {
|
||||
/*
|
||||
* Else if Vdn is pressed, transition to STAGING with
|
||||
* next state set to SYSRQ_EXEC.
|
||||
*/
|
||||
next_debug_state = STATE_SYSRQ_EXEC;
|
||||
}
|
||||
debug_mode_transition(STATE_STAGING);
|
||||
break;
|
||||
case STATE_WARM_RESET_PATH:
|
||||
mask = debug_button_mask();
|
||||
|
||||
/*
|
||||
* Continue in this state if button is not pressed and timeout
|
||||
* has not occurred.
|
||||
*/
|
||||
if (!mask && !timestamp_expired(debug_state_deadline, NULL))
|
||||
return;
|
||||
|
||||
/* Exit debug mode if valid buttons are not pressed. */
|
||||
if (mask != DEBUG_BTN_VOL_UP) {
|
||||
debug_mode_transition(STATE_DEBUG_NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
next_debug_state = STATE_WARM_RESET_EXEC;
|
||||
debug_mode_transition(STATE_STAGING);
|
||||
break;
|
||||
case STATE_SYSRQ_EXEC:
|
||||
case STATE_WARM_RESET_EXEC:
|
||||
default:
|
||||
debug_mode_transition(STATE_DEBUG_NONE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LED_COMMON
|
||||
static void debug_led_tick(void)
|
||||
{
|
||||
static int led_state = LED_STATE_OFF;
|
||||
|
||||
if (debug_mode_blink_led()) {
|
||||
led_state = !led_state;
|
||||
led_control(EC_LED_ID_SYSRQ_DEBUG_LED, led_state);
|
||||
}
|
||||
}
|
||||
DECLARE_HOOK(HOOK_TICK, debug_led_tick, HOOK_PRIO_DEFAULT);
|
||||
#endif /* CONFIG_LED_COMMON */
|
||||
|
||||
#endif /* CONFIG_EMULATED_SYSRQ */
|
||||
|
||||
@@ -1805,6 +1805,8 @@ enum ec_led_id {
|
||||
EC_LED_ID_RIGHT_LED,
|
||||
/* LED to indicate recovery mode with HW_REINIT */
|
||||
EC_LED_ID_RECOVERY_HW_REINIT_LED,
|
||||
/* LED to indicate sysrq debug mode. */
|
||||
EC_LED_ID_SYSRQ_DEBUG_LED,
|
||||
|
||||
EC_LED_ID_COUNT
|
||||
};
|
||||
|
||||
@@ -266,7 +266,8 @@ BUILD_ASSERT(ARRAY_SIZE(led_color_names) == EC_LED_COLOR_COUNT);
|
||||
|
||||
/* Note: depends on enum ec_led_id */
|
||||
static const char * const led_names[] = {
|
||||
"battery", "power", "adapter", "left", "right", "recovery_hwreinit"};
|
||||
"battery", "power", "adapter", "left", "right", "recovery_hwreinit",
|
||||
"sysrq debug" };
|
||||
BUILD_ASSERT(ARRAY_SIZE(led_names) == EC_LED_ID_COUNT);
|
||||
|
||||
/* Check SBS numerical value range */
|
||||
|
||||
Reference in New Issue
Block a user