summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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();
}