diff options
author | Boris Mittelberg <bmbm@google.com> | 2021-03-29 23:35:03 +0000 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-04-27 01:45:46 +0000 |
commit | 7307d9e294a9309f152fe5e4adf12854116c0727 (patch) | |
tree | 6c34ceecab764ebeb08e789c926cdc95a5585cd0 /common/mkbp_fifo.c | |
parent | 1221d6fbe3d38141604c44f120c307d5b1f12bda (diff) | |
download | chrome-ec-7307d9e294a9309f152fe5e4adf12854116c0727.tar.gz |
mkbp: Separate FIFO from the keyboard driver
Move protocol-related functionality out from the keyboard driver. This
change is required to allow passing button events via MKBP on devices with
non-MKBP keyboards. It reorganizes the code without changing the logic.
BUG=b:170966461
BRANCH=main,firmware-dedede-13606.B,firmware-volteer-13672.B-main
TEST=None
Signed-off-by: Boris Mittelberg <bmbm@google.com>
Change-Id: Ifb5b9d8e605f491313ee1dfe2c9950eb52152aa8
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2669355
Diffstat (limited to 'common/mkbp_fifo.c')
-rw-r--r-- | common/mkbp_fifo.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/common/mkbp_fifo.c b/common/mkbp_fifo.c new file mode 100644 index 0000000000..428d6412fc --- /dev/null +++ b/common/mkbp_fifo.c @@ -0,0 +1,241 @@ +/* Copyright 2021 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. + * + * Matrix KeyBoard Protocol FIFO buffer implementation + */ + +#include "atomic.h" +#include "common.h" +#include "keyboard_config.h" +#include "mkbp_event.h" +#include "mkbp_fifo.h" +#include "system.h" +#include "task.h" +#include "util.h" + +/* Console output macros */ +#define CPRINTS(format, args...) cprints(CC_KEYBOARD, format, ## args) + +/* + * Common FIFO depth. This needs to be big enough not to overflow if a + * series of keys is pressed in rapid succession and the kernel is too busy + * to read them out right away. + * + * RAM usage is (depth * #cols); A 16-entry FIFO will consume 16x13=208 bytes, + * which is non-trivial but not horrible. + */ + +static uint32_t fifo_start; /* first entry */ +static uint32_t fifo_end; /* last entry */ +static uint32_t fifo_entries; /* number of existing entries */ +static uint8_t fifo_max_depth = FIFO_DEPTH; +static struct ec_response_get_next_event fifo[FIFO_DEPTH]; + +/* + * Mutex for critical sections of mkbp_fifo_add(), which is called + * from various tasks. + */ +K_MUTEX_DEFINE(fifo_add_mutex); +/* + * Mutex for critical sections of fifo_remove(), which is called from the + * hostcmd task and from keyboard_clear_buffer(). + */ +K_MUTEX_DEFINE(fifo_remove_mutex); + +static int get_data_size(enum ec_mkbp_event e) +{ + switch (e) { + case EC_MKBP_EVENT_KEY_MATRIX: + return KEYBOARD_COLS_MAX; + +#ifdef CONFIG_HOST_EVENT64 + case EC_MKBP_EVENT_HOST_EVENT64: + return sizeof(uint64_t); +#endif + + case EC_MKBP_EVENT_HOST_EVENT: + case EC_MKBP_EVENT_BUTTON: + case EC_MKBP_EVENT_SWITCH: + case EC_MKBP_EVENT_SYSRQ: + return sizeof(uint32_t); + default: + /* For unknown types, say it's 0. */ + return 0; + } +} + +/** + * Pop MKBP event data from FIFO + * + * @return EC_SUCCESS if entry popped, EC_ERROR_UNKNOWN if FIFO is empty + */ +static int fifo_remove(uint8_t *buffp) +{ + int size; + + mutex_lock(&fifo_remove_mutex); + if (!fifo_entries) { + /* no entry remaining in FIFO : return last known state */ + int last = (fifo_start + FIFO_DEPTH - 1) % FIFO_DEPTH; + + size = get_data_size(fifo[last].event_type); + + memcpy(buffp, &fifo[last].data, size); + mutex_unlock(&fifo_remove_mutex); + + /* + * Bail out without changing any FIFO indices and let the + * caller know something strange happened. The buffer will + * will contain the last known state of the keyboard. + */ + return EC_ERROR_UNKNOWN; + } + + /* Return just the event data. */ + if (buffp) { + size = get_data_size(fifo[fifo_start].event_type); + /* skip over event_type. */ + memcpy(buffp, &fifo[fifo_start].data, size); + } + + fifo_start = (fifo_start + 1) % FIFO_DEPTH; + atomic_sub(&fifo_entries, 1); + mutex_unlock(&fifo_remove_mutex); + + return EC_SUCCESS; +} + +/*****************************************************************************/ +/* Interface */ + +void mkbp_fifo_depth_update(uint8_t new_max_depth) +{ + fifo_max_depth = new_max_depth; +} + + +void mkbp_fifo_clear_keyboard(void) +{ + int i, new_fifo_entries = 0; + + CPRINTS("clear keyboard MKBP fifo"); + + /* + * Order of these locks is important to prevent deadlock since + * mkbp_fifo_add() may call fifo_remove(). + */ + mutex_lock(&fifo_add_mutex); + mutex_lock(&fifo_remove_mutex); + + /* Reset the end position */ + fifo_end = fifo_start; + + for (i = 0; i < fifo_entries; i++) { + int cur = (fifo_start + i) % FIFO_DEPTH; + + /* Drop keyboard events */ + if (fifo[cur].event_type == EC_MKBP_EVENT_KEY_MATRIX) + continue; + + /* And move other events to the front */ + memmove(&fifo[fifo_end], &fifo[cur], sizeof(fifo[cur])); + fifo_end = (fifo_end + 1) % FIFO_DEPTH; + ++new_fifo_entries; + } + fifo_entries = new_fifo_entries; + + mutex_unlock(&fifo_remove_mutex); + mutex_unlock(&fifo_add_mutex); +} + +void mkbp_clear_fifo(void) +{ + int i; + + CPRINTS("clear MKBP fifo"); + + /* + * Order of these locks is important to prevent deadlock since + * mkbp_fifo_add() may call fifo_remove(). + */ + mutex_lock(&fifo_add_mutex); + mutex_lock(&fifo_remove_mutex); + + fifo_start = 0; + fifo_end = 0; + /* This assignment is safe since both mutexes are held. */ + fifo_entries = 0; + for (i = 0; i < FIFO_DEPTH; i++) + memset(&fifo[i], 0, sizeof(struct ec_response_get_next_event)); + + mutex_unlock(&fifo_remove_mutex); + mutex_unlock(&fifo_add_mutex); +} + +test_mockable int mkbp_fifo_add(uint8_t event_type, const uint8_t *buffp) +{ + uint8_t size; + + mutex_lock(&fifo_add_mutex); + if (fifo_entries >= fifo_max_depth) { + mutex_unlock(&fifo_add_mutex); + CPRINTS("MKBP common FIFO depth %d reached", + fifo_max_depth); + + return EC_ERROR_OVERFLOW; + } + + size = get_data_size(event_type); + fifo[fifo_end].event_type = event_type; + memcpy(&fifo[fifo_end].data, buffp, size); + fifo_end = (fifo_end + 1) % FIFO_DEPTH; + atomic_add(&fifo_entries, 1); + + /* + * If our event didn't generate an interrupt then the host is still + * asleep. In this case, we don't want to queue our event, except if + * another event just woke the host (and wake is already in progress). + */ + if (!mkbp_send_event(event_type) && fifo_entries == 1) + fifo_remove(NULL); + + mutex_unlock(&fifo_add_mutex); + return EC_SUCCESS; +} + +int mkbp_fifo_get_next_event(uint8_t *out, enum ec_mkbp_event evt) +{ + uint8_t t = fifo[fifo_start].event_type; + uint8_t size; + + if (!fifo_entries) + return -1; + + /* + * We need to peek at the next event to check that we were called with + * the correct event. + */ + if (t != (uint8_t)evt) { + /* + * We were called with the wrong event. The next element in the + * FIFO's event type doesn't match with what we were called + * with. Return an error that we're busy. The caller will need + * to call us with the correct event first. + */ + return -EC_ERROR_BUSY; + } + + fifo_remove(out); + + /* Keep sending events if FIFO is not empty */ + if (fifo_entries) + mkbp_send_event(fifo[fifo_start].event_type); + + /* Return the correct size of the data. */ + size = get_data_size(t); + if (size) + return size; + else + return -EC_ERROR_UNKNOWN; +} |