diff options
author | Bill Richardson <wfrichar@chromium.org> | 2013-08-07 15:36:21 -0700 |
---|---|---|
committer | ChromeBot <chrome-bot@google.com> | 2013-08-09 15:44:09 -0700 |
commit | 0c8c2e453ab3960f315050fbb9808f438398624f (patch) | |
tree | 472f7677ac3738338bea389a42d9c1b7f6703bda | |
parent | 71a3bb0c0746d17e55e1e08322258e555ac80682 (diff) | |
download | chrome-ec-0c8c2e453ab3960f315050fbb9808f438398624f.tar.gz |
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: I42393fcf3c4eb71b9551118a0f442d55c0691315
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/65071
-rw-r--r-- | common/util.c | 58 | ||||
-rw-r--r-- | include/util.h | 47 | ||||
-rw-r--r-- | test/utils.c | 98 |
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(); } |