/* 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; }