summaryrefslogtreecommitdiff
path: root/common/button.c
diff options
context:
space:
mode:
authorFurquan Shaikh <furquan@chromium.org>2017-05-27 20:45:16 -0700
committerchrome-bot <chrome-bot@chromium.org>2017-06-15 01:48:26 -0700
commit5ed0e0f76fda2042236b59de594f8e2cf4e332f2 (patch)
treec620793e8562954f5e246420307ec7dabb239333 /common/button.c
parent60ce79badda9e8bf9e3259d07d8721b9f83ccc26 (diff)
downloadchrome-ec-5ed0e0f76fda2042236b59de594f8e2cf4e332f2.tar.gz
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>
Diffstat (limited to 'common/button.c')
-rw-r--r--common/button.c306
1 files changed, 306 insertions, 0 deletions
diff --git a/common/button.c b/common/button.c
index a536f16a2b..fc1b45167a 100644
--- a/common/button.c
+++ b/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 */