summaryrefslogtreecommitdiff
path: root/common/physical_presence.c
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2017-07-12 12:56:48 -0700
committerchrome-bot <chrome-bot@chromium.org>2017-07-19 14:41:21 -0700
commita6060f53ee2c2736031b5fea80884886c6e08816 (patch)
treec412e882d29a0e65fb90ad29e66ac98798fc69e2 /common/physical_presence.c
parent1082d99ca629c88a46996f3911f0a5af98dd3ba8 (diff)
downloadchrome-ec-a6060f53ee2c2736031b5fea80884886c6e08816.tar.gz
cr50: Add physical presence state machine
This will be used as part of case closed debugging configuration. Currently, this only adds the state machine, and a test command which is available when CR50_DEV=1. It also adds a new sleep mask flag for physical presence detect, instead of oveloading FORCE_NO_DSLEEP. The physical presence state machine supports both short (tap 5 times in 15 seconds) and long (short, followed by 4 more taps 1-5 minutes apart) sequences. It will call a callback function at the end of the sequence, so that multiple things can each request physical presence in turn. This will be used by ccdopen / ccdunlock / testlab commands for CCD V1. Eventually, CCD config will replace the unlock sequence handling in wp.c. But for now, we don't touch that. See go/cr50-ccd-wp for more information. BUG=b:62537474 BRANCH=none TEST=manual Short sequence: pp short pp pp pp # See "Physical presence good" Timeout pp short pp # 15 sec later, get timeout error Long sequence: pp long pp pp pp (wait 2 sec) pp (wait 2 sec) pp # See "PP good" Long sequence, presses too fast: pp long pp pp pp pp # See "PP L too soon" (wait 2 sec) pp (wait 2 sec) pp # See "Physical presence good" Abort: pp short pp abort # See "Physical presence aborted" Change-Id: I09da81ad11d328e7d7740c3fe838a5f67d7b8708 Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/568796 Reviewed-by: Aseda Aboagye <aaboagye@chromium.org> Reviewed-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-by: Mary Ruthven <mruthven@chromium.org>
Diffstat (limited to 'common/physical_presence.c')
-rw-r--r--common/physical_presence.c303
1 files changed, 303 insertions, 0 deletions
diff --git a/common/physical_presence.c b/common/physical_presence.c
new file mode 100644
index 0000000000..0f4bbeb0f3
--- /dev/null
+++ b/common/physical_presence.c
@@ -0,0 +1,303 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Physical presence detect state machine
+ */
+
+#include "common.h"
+#include "console.h"
+#include "hooks.h"
+#include "physical_presence.h"
+#include "task.h"
+#include "timer.h"
+#include "util.h"
+
+#define CPRINTS(format, args...) cprints(CC_CCD, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_CCD, format, ## args)
+
+#ifdef CONFIG_PHYSICAL_PRESENCE_DEBUG_UNSAFE
+/* More lenient physical presence for dev builds */
+#define PP_SHORT_PRESS_COUNT 3
+#define PP_SHORT_PRESS_MIN_INTERVAL_US (100 * MSEC)
+#define PP_SHORT_PRESS_MAX_INTERVAL_US (15 * SECOND)
+#define PP_LONG_PRESS_COUNT (PP_SHORT_PRESS_COUNT + 2)
+#define PP_LONG_PRESS_MIN_INTERVAL_US (2 * SECOND)
+#define PP_LONG_PRESS_MAX_INTERVAL_US (300 * SECOND)
+#else
+/* Stricter physical presence for non-dev builds */
+#define PP_SHORT_PRESS_COUNT 5
+#define PP_SHORT_PRESS_MIN_INTERVAL_US (100 * MSEC)
+#define PP_SHORT_PRESS_MAX_INTERVAL_US (5 * SECOND)
+#define PP_LONG_PRESS_COUNT (PP_SHORT_PRESS_COUNT + 4)
+#define PP_LONG_PRESS_MIN_INTERVAL_US (60 * SECOND)
+#define PP_LONG_PRESS_MAX_INTERVAL_US (300 * SECOND)
+#endif
+
+enum pp_detect_state {
+ PP_DETECT_IDLE = 0,
+ PP_DETECT_IN_PROGRESS,
+ PP_DETECT_FINISHING,
+ PP_DETECT_ABORT
+};
+
+/* Physical presence state machine data */
+static enum pp_detect_state pp_detect_state;
+static void (*pp_detect_callback)(void);
+static uint8_t pp_press_count;
+static uint8_t pp_press_count_needed;
+static uint64_t pp_last_press; /* Time of last press */
+
+/*
+ * We need a mutex because physical_detect_start() and physical_detect_abort()
+ * could be called from multiple threads (TPM or console). And either of those
+ * could preempt the deferred functions for the state machine which run in the
+ * hook task.
+ */
+static struct mutex pp_mutex;
+
+/******************************************************************************/
+/*
+ * Deferred functions
+ *
+ * These are called by the hook task, so can't preempt each other. But they
+ * could be preempted by calls to physical_presence_start() or
+ * physical_presence_abort().
+ */
+
+/**
+ * Clean up at end of physical detect sequence.
+ */
+static void physical_detect_done(void)
+{
+ /*
+ * Note that calling physical_detect_abort() from another thread after
+ * the start of physical_detect_done() but before mutex_lock() will
+ * result in another call to physical_detect_done() being queued up.
+ * That's harmless, because we go back to PP_DETECT_IDLE at the end of
+ * this call, so the second call will simply drop through without
+ * calling pp_detect_callback().
+ */
+ mutex_lock(&pp_mutex);
+
+ if (pp_detect_state != PP_DETECT_IN_PROGRESS) {
+ CPRINTF("\nPhysical presence check aborted.\n");
+ pp_detect_callback = NULL;
+ } else if (pp_press_count < pp_press_count_needed) {
+ CPRINTF("\nPhysical presence check timeout.\n");
+ pp_detect_callback = NULL;
+ }
+
+ pp_detect_state = PP_DETECT_FINISHING;
+ mutex_unlock(&pp_mutex);
+
+ /* No longer care about button presses */
+ board_physical_presence_enable(0);
+
+ /*
+ * Call the callback function. Do this outside the mutex, because the
+ * callback may take a while. If we kept holding the mutex, then calls
+ * to physical_detect_abort() or physical_detect_start() during the
+ * callback would block instead of simply failing.
+ */
+ if (pp_detect_callback) {
+ CPRINTS("PP callback");
+ pp_detect_callback();
+ pp_detect_callback = NULL;
+ }
+
+ /* Now go to idle */
+ mutex_lock(&pp_mutex);
+ pp_detect_state = PP_DETECT_IDLE;
+ mutex_unlock(&pp_mutex);
+}
+DECLARE_DEFERRED(physical_detect_done);
+
+/**
+ * Print a prompt when we've hit the minimum wait time
+ */
+static void physical_detect_prompt(void)
+{
+ CPRINTF("\n\nPress the physical button now!\n\n");
+}
+DECLARE_DEFERRED(physical_detect_prompt);
+
+/**
+ * Handle a physical present button press
+ *
+ * This is implemented as a deferred function so it can use the mutex.
+ */
+static void physical_detect_check_press(void)
+{
+ uint64_t now = get_time().val;
+ uint64_t dt = now - pp_last_press;
+
+ mutex_lock(&pp_mutex);
+
+ CPRINTS("PP press dt=%.6ld\n", dt);
+
+ /* If we no longer care about presses, ignore them */
+ if (pp_detect_state != PP_DETECT_IN_PROGRESS)
+ goto pdpress_exit;
+
+ /* Ignore extra presses we don't need */
+ if (pp_press_count >= pp_press_count_needed)
+ goto pdpress_exit;
+
+ /* Ignore presses outside the expected interval */
+ if (pp_press_count < PP_SHORT_PRESS_COUNT) {
+ if (dt < PP_SHORT_PRESS_MIN_INTERVAL_US) {
+ CPRINTS("PP S too soon");
+ goto pdpress_exit;
+ }
+ if (dt > PP_SHORT_PRESS_MAX_INTERVAL_US) {
+ CPRINTS("PP S too late");
+ goto pdpress_exit;
+ }
+ } else {
+ if (dt < PP_LONG_PRESS_MIN_INTERVAL_US) {
+ CPRINTS("PP L too soon");
+ goto pdpress_exit;
+ }
+ if (dt > PP_LONG_PRESS_MAX_INTERVAL_US) {
+ CPRINTS("PP L too late");
+ goto pdpress_exit;
+ }
+ }
+
+ /* Ok, we need this press */
+ CPRINTS("PP press counted!");
+ pp_last_press = now;
+ pp_press_count++;
+
+ /* Set up call to done handler for timeout or actually done */
+ if (pp_press_count == pp_press_count_needed) {
+ /* Done, so call right away */
+ hook_call_deferred(&physical_detect_done_data, 0);
+ } else if (pp_press_count < PP_SHORT_PRESS_COUNT) {
+ hook_call_deferred(&physical_detect_prompt_data,
+ PP_SHORT_PRESS_MIN_INTERVAL_US);
+ hook_call_deferred(&physical_detect_done_data,
+ PP_SHORT_PRESS_MAX_INTERVAL_US);
+ } else {
+ CPRINTF("Another press will be required soon.\n");
+ dt = PP_LONG_PRESS_MAX_INTERVAL_US;
+ hook_call_deferred(&physical_detect_prompt_data,
+ PP_LONG_PRESS_MIN_INTERVAL_US);
+ hook_call_deferred(&physical_detect_done_data,
+ PP_LONG_PRESS_MAX_INTERVAL_US);
+ }
+
+pdpress_exit:
+ mutex_unlock(&pp_mutex);
+}
+DECLARE_DEFERRED(physical_detect_check_press);
+
+/******************************************************************************/
+/* Interface */
+
+int physical_detect_start(int is_long, void (*callback)(void))
+{
+ mutex_lock(&pp_mutex);
+
+ /* Fail if detection is already in progress */
+ if (pp_detect_state != PP_DETECT_IDLE) {
+ mutex_unlock(&pp_mutex);
+ return EC_ERROR_BUSY;
+ }
+
+ pp_press_count_needed = is_long ? PP_LONG_PRESS_COUNT :
+ PP_SHORT_PRESS_COUNT;
+ pp_press_count = 0;
+ pp_last_press = get_time().val;
+ pp_detect_callback = callback;
+ pp_detect_state = PP_DETECT_IN_PROGRESS;
+ mutex_unlock(&pp_mutex);
+
+ /* Start capturing button presses */
+ hook_call_deferred(&physical_detect_check_press_data, -1);
+ board_physical_presence_enable(1);
+
+ CPRINTS("PP start %s", is_long ? "long" : "short");
+
+ /* Initial timeout is for a short press */
+ hook_call_deferred(&physical_detect_prompt_data,
+ PP_SHORT_PRESS_MIN_INTERVAL_US);
+ hook_call_deferred(&physical_detect_done_data,
+ PP_SHORT_PRESS_MAX_INTERVAL_US);
+
+ return EC_SUCCESS;
+}
+
+int physical_detect_busy(void)
+{
+ return pp_detect_state != PP_DETECT_IDLE;
+}
+
+void physical_detect_abort(void)
+{
+ mutex_lock(&pp_mutex);
+ if (pp_detect_state == PP_DETECT_IN_PROGRESS) {
+ CPRINTS("PP abort");
+ pp_detect_state = PP_DETECT_ABORT;
+ /* Speed up call to done */
+ hook_call_deferred(&physical_detect_prompt_data, -1);
+ hook_call_deferred(&physical_detect_check_press_data, -1);
+ hook_call_deferred(&physical_detect_done_data, 0);
+ }
+ mutex_unlock(&pp_mutex);
+}
+
+int physical_detect_press(void)
+{
+ /* Ignore presses if we're idle */
+ if (pp_detect_state == PP_DETECT_IDLE)
+ return EC_ERROR_NOT_HANDLED;
+
+ /* Call the deferred function to do the work */
+ hook_call_deferred(&physical_detect_check_press_data, 0);
+ return EC_SUCCESS;
+}
+
+#ifdef CONFIG_PHYSICAL_PRESENCE_DEBUG_UNSAFE
+
+/**
+ * Test callback function
+ */
+static void pp_test_callback(void)
+{
+ ccprintf("\nPhysical presence good\n");
+}
+
+/**
+ * Test physical presence.
+ */
+static int command_ppresence(int argc, char **argv)
+{
+ /* Print current status */
+ ccprintf("PP state: %d, %d/%d, dt=%.6ld\n",
+ pp_detect_state, pp_press_count, pp_press_count_needed,
+ get_time().val - pp_last_press);
+
+ /* With no args, simulate a button press */
+ if (argc < 2) {
+ physical_detect_press();
+ return EC_SUCCESS;
+ }
+
+ if (!strcasecmp(argv[1], "short")) {
+ return physical_detect_start(0, pp_test_callback);
+ } else if (!strcasecmp(argv[1], "long")) {
+ return physical_detect_start(1, pp_test_callback);
+ } else if (!strcasecmp(argv[1], "abort")) {
+ physical_detect_abort();
+ return EC_SUCCESS;
+ } else {
+ return EC_ERROR_PARAM1;
+ }
+}
+DECLARE_SAFE_CONSOLE_COMMAND(ppresence, command_ppresence,
+ "[short | long | abort]",
+ "Test physical presence press or sequence");
+
+#endif