diff options
-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(); } |