summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@chromium.org>2013-08-07 15:36:21 -0700
committerDave Parker <dparker@chromium.org>2013-08-09 23:39:49 -0700
commit015f47901095c9da21c141c2c69db5e4f89c827a (patch)
treecc554829af4941249fc164db8a416f82f949ebd2
parent941245269b45cda57831e144caf62ba1fe1c310d (diff)
downloadchrome-ec-015f47901095c9da21c141c2c69db5e4f89c827a.tar.gz
CHERRY-PICK:Add abstract "cond_t" type to detect state transitions.
We often need to watch for transitions between one state and another, so that we can issue warnings or take action ONCE. This abstracts that "have I already reacted to this" stuff into a single set of functions. For example, this code reads a GPIO every time through the loop, but it only generates an event when the GPIO value changes from 0 to 1: cond_t c; cond_init_false(&c); while(1) { int val = read_some_gpio(); cond_set(&c, val); if (cond_went_true(&c)) host_event(SOMETHING_HAPPENED); sleep(1); } BUG=none BRANCH=falco,peppy TEST=manual make BOARD=falco runtests Change-Id: I301d97b0e96cc3e2bfd3283db308b2cd900fd4c1 Original-Change-Id: I42393fcf3c4eb71b9551118a0f442d55c0691315 Signed-off-by: Bill Richardson <wfrichar@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/65071 Reviewed-on: https://gerrit.chromium.org/gerrit/65456 Commit-Queue: Dave Parker <dparker@chromium.org> Reviewed-by: Dave Parker <dparker@chromium.org> Tested-by: Dave Parker <dparker@chromium.org>
-rw-r--r--common/util.c58
-rw-r--r--include/util.h47
-rw-r--r--test/utils.c98
3 files changed, 202 insertions, 1 deletions
diff --git a/common/util.c b/common/util.c
index 5ccebeb063..0d7bff2710 100644
--- a/common/util.c
+++ b/common/util.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 2013 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.
*/
@@ -272,3 +272,59 @@ int uint64divmod(uint64_t *n, int d)
*n = q;
return r;
}
+
+
+/****************************************************************************/
+/* stateful conditional stuff */
+
+enum cond_internal_bits {
+ COND_CURR_MASK = (1 << 0), /* current value */
+ COND_RISE_MASK = (1 << 1), /* set if 0->1 */
+ COND_FALL_MASK = (1 << 2), /* set if 1->0 */
+};
+
+void cond_init(cond_t *c, int val)
+{
+ if (val)
+ *c = COND_CURR_MASK;
+ else
+ *c = 0;
+}
+
+int cond_is(cond_t *c, int val)
+{
+ if (val)
+ return *c & COND_CURR_MASK;
+ else
+ return !(*c & COND_CURR_MASK);
+}
+
+
+void cond_set(cond_t *c, int val)
+{
+ if (val && cond_is(c, 0))
+ *c |= COND_RISE_MASK;
+ else if (!val && cond_is(c, 1))
+ *c |= COND_FALL_MASK;
+ if (val)
+ *c |= COND_CURR_MASK;
+ else
+ *c &= ~COND_CURR_MASK;
+}
+
+
+int cond_went(cond_t *c, int val)
+{
+ int ret;
+
+ if (val) {
+ ret = *c & COND_RISE_MASK;
+ *c &= ~COND_RISE_MASK;
+ }
+ else {
+ ret = *c & COND_FALL_MASK;
+ *c &= ~COND_FALL_MASK;
+ }
+
+ return ret;
+}
diff --git a/include/util.h b/include/util.h
index ed30ed4e46..0408940b35 100644
--- a/include/util.h
+++ b/include/util.h
@@ -101,4 +101,51 @@ int tolower(int c);
*/
int uint64divmod(uint64_t *v, int by);
+
+/****************************************************************************/
+/* Conditional stuff.
+ *
+ * We often need to watch for transitions between one state and another, so
+ * that we can issue warnings or take action ONCE. This abstracts that "have I
+ * already reacted to this" stuff into a single set of functions.
+ *
+ * For example:
+ *
+ * cond_t c;
+ *
+ * cond_init_false(&c);
+ *
+ * while(1) {
+ * int val = read_some_gpio();
+ * cond_set(&c, val);
+ *
+ * if (cond_went_true(&c))
+ * host_event(SOMETHING_HAPPENED);
+ * }
+ *
+ */
+typedef uint8_t cond_t;
+
+/* Initialize a conditional to a specific state. Do this first. */
+void cond_init(cond_t *c, int boolean);
+static inline void cond_init_false(cond_t *c) { cond_init(c, 0); }
+static inline void cond_init_true(cond_t *c) { cond_init(c, 1); }
+
+/* Set the current state. Do this as often as you like. */
+void cond_set(cond_t *c, int boolean);
+static inline void cond_set_false(cond_t *c) { cond_set(c, 0); }
+static inline void cond_set_true(cond_t *c) { cond_set(c, 1); }
+
+/* Get the current state. Do this as often as you like. */
+int cond_is(cond_t *c, int boolean);
+static inline int cond_is_false(cond_t *c) { return cond_is(c, 0); }
+static inline int cond_is_true(cond_t *c) { return cond_is(c, 1); }
+
+/* See if the state has transitioned. If it has, the corresponding function
+ * will return true ONCE only, until it's changed back.
+ */
+int cond_went(cond_t *c, int boolean);
+static inline int cond_went_false(cond_t *c) { return cond_went(c, 0); }
+static inline int cond_went_true(cond_t *c) { return cond_went(c, 1); }
+
#endif /* __CROS_EC_UTIL_H */
diff --git a/test/utils.c b/test/utils.c
index 3c92846417..e2230bfd44 100644
--- a/test/utils.c
+++ b/test/utils.c
@@ -185,6 +185,103 @@ static int test_scratchpad(void)
return EC_SUCCESS;
}
+static int test_cond_t(void)
+{
+ cond_t c;
+
+ /* one-shot? */
+ cond_init_false(&c);
+ cond_set_true(&c);
+ TEST_ASSERT(cond_went_true(&c));
+ TEST_ASSERT(!cond_went_true(&c));
+ TEST_ASSERT(!cond_went_true(&c));
+ cond_set_false(&c);
+ TEST_ASSERT(cond_went_false(&c));
+ TEST_ASSERT(!cond_went_false(&c));
+ TEST_ASSERT(!cond_went_false(&c));
+
+ /* one-shot when initially true? */
+ cond_init_true(&c);
+ cond_set_false(&c);
+ TEST_ASSERT(cond_went_false(&c));
+ TEST_ASSERT(!cond_went_false(&c));
+ TEST_ASSERT(!cond_went_false(&c));
+ cond_set_true(&c);
+ TEST_ASSERT(cond_went_true(&c));
+ TEST_ASSERT(!cond_went_true(&c));
+ TEST_ASSERT(!cond_went_true(&c));
+
+ /* still one-shot even if set multiple times? */
+ cond_init_false(&c);
+ cond_set_true(&c);
+ cond_set_true(&c);
+ cond_set_true(&c);
+ cond_set_true(&c);
+ cond_set_true(&c);
+ cond_set_true(&c);
+ TEST_ASSERT(cond_went_true(&c));
+ TEST_ASSERT(!cond_went_true(&c));
+ TEST_ASSERT(!cond_went_true(&c));
+ cond_set_true(&c);
+ cond_set_false(&c);
+ cond_set_false(&c);
+ cond_set_false(&c);
+ cond_set_false(&c);
+ cond_set_false(&c);
+ TEST_ASSERT(cond_went_false(&c));
+ TEST_ASSERT(!cond_went_false(&c));
+ TEST_ASSERT(!cond_went_false(&c));
+
+ /* only the detected transition direction resets it */
+ cond_set_true(&c);
+ TEST_ASSERT(!cond_went_false(&c));
+ TEST_ASSERT(cond_went_true(&c));
+ TEST_ASSERT(!cond_went_false(&c));
+ TEST_ASSERT(!cond_went_true(&c));
+ cond_set_false(&c);
+ TEST_ASSERT(!cond_went_true(&c));
+ TEST_ASSERT(!cond_went_true(&c));
+ TEST_ASSERT(cond_went_false(&c));
+ TEST_ASSERT(!cond_went_false(&c));
+ TEST_ASSERT(!cond_went_false(&c));
+ TEST_ASSERT(!cond_went_true(&c));
+ TEST_ASSERT(!cond_went_true(&c));
+
+ /* multiple transitions between checks should notice both edges */
+ cond_set_true(&c);
+ cond_set_false(&c);
+ cond_set_true(&c);
+ cond_set_false(&c);
+ cond_set_true(&c);
+ cond_set_false(&c);
+ TEST_ASSERT(cond_went_true(&c));
+ TEST_ASSERT(!cond_went_true(&c));
+ TEST_ASSERT(!cond_went_true(&c));
+ TEST_ASSERT(!cond_went_true(&c));
+ TEST_ASSERT(cond_went_false(&c));
+ TEST_ASSERT(!cond_went_false(&c));
+ TEST_ASSERT(!cond_went_false(&c));
+ TEST_ASSERT(!cond_went_false(&c));
+ TEST_ASSERT(!cond_went_true(&c));
+ TEST_ASSERT(!cond_went_true(&c));
+ TEST_ASSERT(!cond_went_false(&c));
+
+ /* Still has last value? */
+ cond_set_true(&c);
+ cond_set_false(&c);
+ cond_set_true(&c);
+ cond_set_false(&c);
+ TEST_ASSERT(cond_is_false(&c));
+ cond_set_false(&c);
+ cond_set_true(&c);
+ cond_set_false(&c);
+ cond_set_true(&c);
+ TEST_ASSERT(cond_is_true(&c));
+
+ /* well okay then */
+ return EC_SUCCESS;
+}
+
void run_test(void)
{
test_reset();
@@ -204,6 +301,7 @@ void run_test(void)
RUN_TEST(test_uint64divmod_2);
RUN_TEST(test_shared_mem);
RUN_TEST(test_scratchpad);
+ RUN_TEST(test_cond_t);
test_print_result();
}