/* * Copyright 2023 Google LLC * * SPDX-License-Identifier: Apache-2.0 */ #ifndef ZEPHYR_INCLUDE_INPUT_KBD_MATRIX_H_ #define ZEPHYR_INCLUDE_INPUT_KBD_MATRIX_H_ /** * @brief Keyboard Matrix API * @defgroup input_kbd_matrix Keyboard Matrix API * @ingroup io_interfaces * @{ */ #include #include #include #include #include #include #include /** Special drive_column argument for not driving any column */ #define INPUT_KBD_MATRIX_COLUMN_DRIVE_NONE -1 /** Special drive_column argument for driving all the columns */ #define INPUT_KBD_MATRIX_COLUMN_DRIVE_ALL -2 /** Number of tracked scan cycles */ #define INPUT_KBD_MATRIX_SCAN_OCURRENCES 30U /** Row entry data type */ #if CONFIG_INPUT_KBD_MATRIX_16_BIT_ROW typedef uint16_t kbd_row_t; #define PRIkbdrow "04" PRIx16 #else typedef uint8_t kbd_row_t; #define PRIkbdrow "02" PRIx8 #endif #if defined(CONFIG_INPUT_KBD_ACTUAL_KEY_MASK_DYNAMIC) || defined(__DOXYGEN__) #define INPUT_KBD_ACTUAL_KEY_MASK_CONST /** * @brief Enables or disables a specific row, column combination in the actual * key mask. * * This allows enabling or disabling specific row, column combination in the * actual key mask in runtime. It can be useful if some of the keys are not * present in some configuration, and the specific configuration is determined * in runtime. Requires @kconfig{CONFIG_INPUT_KBD_ACTUAL_KEY_MASK_DYNAMIC} to * be enabled. * * @param dev Pointer to the keyboard matrix device. * @param row The matrix row to enable or disable. * @param col The matrix column to enable or disable. * @param enabled Whether the specified row, col has to be enabled or disabled. * * @retval 0 If the change is successful. * @retval -errno Negative errno if row or col are out of range for the device. */ int input_kbd_matrix_actual_key_mask_set(const struct device *dev, uint8_t row, uint8_t col, bool enabled); #else #define INPUT_KBD_ACTUAL_KEY_MASK_CONST const #endif /** Maximum number of rows */ #define INPUT_KBD_MATRIX_ROW_BITS NUM_BITS(kbd_row_t) /** * @brief Keyboard matrix internal APIs. */ struct input_kbd_matrix_api { /** * @brief Request to drive a specific column. * * Request to drive a specific matrix column, or none, or all. * * @param dev Pointer to the keyboard matrix device. * @param col The column to drive, or * @ref INPUT_KBD_MATRIX_COLUMN_DRIVE_NONE or * @ref INPUT_KBD_MATRIX_COLUMN_DRIVE_ALL. */ void (*drive_column)(const struct device *dev, int col); /** * @brief Read the matrix row. * * @param dev Pointer to the keyboard matrix device. */ kbd_row_t (*read_row)(const struct device *dev); /** * @brief Request to put the matrix in detection mode. * * Request to put the driver in detection mode, this is called after a * request to drive all the column and typically involves reenabling * interrupts row pin changes. * * @param dev Pointer to the keyboard matrix device. * @param enable Whether detection mode has to be enabled or disabled. */ void (*set_detect_mode)(const struct device *dev, bool enabled); }; /** * @brief Common keyboard matrix config. * * This structure **must** be placed first in the driver's config structure. */ struct input_kbd_matrix_common_config { const struct input_kbd_matrix_api *api; uint8_t row_size; uint8_t col_size; uint32_t poll_period_us; uint32_t stable_poll_period_us; uint32_t poll_timeout_ms; uint32_t debounce_down_us; uint32_t debounce_up_us; uint32_t settle_time_us; bool ghostkey_check; INPUT_KBD_ACTUAL_KEY_MASK_CONST kbd_row_t *actual_key_mask; /* extra data pointers */ kbd_row_t *matrix_stable_state; kbd_row_t *matrix_unstable_state; kbd_row_t *matrix_previous_state; kbd_row_t *matrix_new_state; uint8_t *scan_cycle_idx; }; #define INPUT_KBD_MATRIX_DATA_NAME(node_id, name) \ _CONCAT(__input_kbd_matrix_, \ _CONCAT(name, DEVICE_DT_NAME_GET(node_id))) /** * @brief Defines the common keyboard matrix support data from devicetree, * specify row and col count. */ #define INPUT_KBD_MATRIX_DT_DEFINE_ROW_COL(node_id, _row_size, _col_size) \ BUILD_ASSERT(IN_RANGE(_row_size, 1, INPUT_KBD_MATRIX_ROW_BITS), "invalid row-size"); \ BUILD_ASSERT(IN_RANGE(_col_size, 1, UINT8_MAX), "invalid col-size"); \ IF_ENABLED(DT_NODE_HAS_PROP(node_id, actual_key_mask), ( \ BUILD_ASSERT(DT_PROP_LEN(node_id, actual_key_mask) == _col_size, \ "actual-key-mask size does not match the number of columns"); \ static INPUT_KBD_ACTUAL_KEY_MASK_CONST kbd_row_t \ INPUT_KBD_MATRIX_DATA_NAME(node_id, actual_key_mask)[_col_size] = \ DT_PROP(node_id, actual_key_mask); \ )) \ static kbd_row_t INPUT_KBD_MATRIX_DATA_NAME(node_id, stable_state)[_col_size]; \ static kbd_row_t INPUT_KBD_MATRIX_DATA_NAME(node_id, unstable_state)[_col_size]; \ static kbd_row_t INPUT_KBD_MATRIX_DATA_NAME(node_id, previous_state)[_col_size]; \ static kbd_row_t INPUT_KBD_MATRIX_DATA_NAME(node_id, new_state)[_col_size]; \ static uint8_t INPUT_KBD_MATRIX_DATA_NAME(node_id, scan_cycle_idx)[_row_size * _col_size]; /** * @brief Defines the common keyboard matrix support data from devicetree. */ #define INPUT_KBD_MATRIX_DT_DEFINE(node_id) \ INPUT_KBD_MATRIX_DT_DEFINE_ROW_COL( \ node_id, DT_PROP(node_id, row_size), DT_PROP(node_id, col_size)) /** * @brief Defines the common keyboard matrix support data from devicetree * instance, specify row and col count. * * @param inst Instance. * @param row_size The matrix row count. * @param col_size The matrix column count. */ #define INPUT_KBD_MATRIX_DT_INST_DEFINE_ROW_COL(inst, row_size, col_size) \ INPUT_KBD_MATRIX_DT_DEFINE_ROW_COL(DT_DRV_INST(inst), row_size, col_size) /** * @brief Defines the common keyboard matrix support data from devicetree instance. * * @param inst Instance. */ #define INPUT_KBD_MATRIX_DT_INST_DEFINE(inst) \ INPUT_KBD_MATRIX_DT_DEFINE(DT_DRV_INST(inst)) /** * @brief Initialize common keyboard matrix config from devicetree, specify row and col count. * * @param node_id The devicetree node identifier. * @param _api Pointer to a @ref input_kbd_matrix_api structure. * @param _row_size The matrix row count. * @param _col_size The matrix column count. */ #define INPUT_KBD_MATRIX_DT_COMMON_CONFIG_INIT_ROW_COL(node_id, _api, _row_size, _col_size) \ { \ .api = _api, \ .row_size = _row_size, \ .col_size = _col_size, \ .poll_period_us = DT_PROP(node_id, poll_period_ms) * USEC_PER_MSEC, \ .stable_poll_period_us = DT_PROP_OR(node_id, stable_poll_period_ms, \ DT_PROP(node_id, poll_period_ms)) * \ USEC_PER_MSEC, \ .poll_timeout_ms = DT_PROP(node_id, poll_timeout_ms), \ .debounce_down_us = DT_PROP(node_id, debounce_down_ms) * USEC_PER_MSEC, \ .debounce_up_us = DT_PROP(node_id, debounce_up_ms) * USEC_PER_MSEC, \ .settle_time_us = DT_PROP(node_id, settle_time_us), \ .ghostkey_check = !DT_PROP(node_id, no_ghostkey_check), \ IF_ENABLED(DT_NODE_HAS_PROP(node_id, actual_key_mask), ( \ .actual_key_mask = INPUT_KBD_MATRIX_DATA_NAME(node_id, actual_key_mask), \ )) \ \ .matrix_stable_state = INPUT_KBD_MATRIX_DATA_NAME(node_id, stable_state), \ .matrix_unstable_state = INPUT_KBD_MATRIX_DATA_NAME(node_id, unstable_state), \ .matrix_previous_state = INPUT_KBD_MATRIX_DATA_NAME(node_id, previous_state), \ .matrix_new_state = INPUT_KBD_MATRIX_DATA_NAME(node_id, new_state), \ .scan_cycle_idx = INPUT_KBD_MATRIX_DATA_NAME(node_id, scan_cycle_idx), \ } /** * @brief Initialize common keyboard matrix config from devicetree. * * @param node_id The devicetree node identifier. * @param api Pointer to a @ref input_kbd_matrix_api structure. */ #define INPUT_KBD_MATRIX_DT_COMMON_CONFIG_INIT(node_id, api) \ INPUT_KBD_MATRIX_DT_COMMON_CONFIG_INIT_ROW_COL( \ node_id, api, DT_PROP(node_id, row_size), DT_PROP(node_id, col_size)) /** * @brief Initialize common keyboard matrix config from devicetree instance, * specify row and col count. * * @param inst Instance. * @param api Pointer to a @ref input_kbd_matrix_api structure. * @param row_size The matrix row count. * @param col_size The matrix column count. */ #define INPUT_KBD_MATRIX_DT_INST_COMMON_CONFIG_INIT_ROW_COL(inst, api, row_size, col_size) \ INPUT_KBD_MATRIX_DT_COMMON_CONFIG_INIT_ROW_COL(DT_DRV_INST(inst), api, row_size, col_size) /** * @brief Initialize common keyboard matrix config from devicetree instance. * * @param inst Instance. * @param api Pointer to a @ref input_kbd_matrix_api structure. */ #define INPUT_KBD_MATRIX_DT_INST_COMMON_CONFIG_INIT(inst, api) \ INPUT_KBD_MATRIX_DT_COMMON_CONFIG_INIT(DT_DRV_INST(inst), api) /** * @brief Common keyboard matrix data. * * This structure **must** be placed first in the driver's data structure. */ struct input_kbd_matrix_common_data { /* Track previous cycles, used for debouncing. */ uint32_t scan_clk_cycle[INPUT_KBD_MATRIX_SCAN_OCURRENCES]; uint8_t scan_cycles_idx; struct k_sem poll_lock; #ifdef CONFIG_PM_DEVICE atomic_t suspended; #endif struct k_thread thread; K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_INPUT_KBD_MATRIX_THREAD_STACK_SIZE); }; /** * @brief Validate the offset of the common data structures. * * @param config Name of the config structure. * @param data Name of the data structure. */ #define INPUT_KBD_STRUCT_CHECK(config, data) \ BUILD_ASSERT(offsetof(config, common) == 0, \ "struct input_kbd_matrix_common_config must be placed first"); \ BUILD_ASSERT(offsetof(data, common) == 0, \ "struct input_kbd_matrix_common_data must be placed first") /** * @brief Start scanning the keyboard matrix * * Starts the keyboard matrix scanning cycle, this should be called in reaction * of a press event, after the device has been put in detect mode. * * @param dev Keyboard matrix device instance. */ void input_kbd_matrix_poll_start(const struct device *dev); #if defined(CONFIG_INPUT_KBD_DRIVE_COLUMN_HOOK) || defined(__DOXYGEN__) /** * @brief Drive column hook * * This can be implemented by the application to handle column selection * quirks. Called after the driver specific drive_column function. Requires * @kconfig{CONFIG_INPUT_KBD_DRIVE_COLUMN_HOOK} to be enabled. * * @param dev Keyboard matrix device instance. * @param col The column to drive, or * @ref INPUT_KBD_MATRIX_COLUMN_DRIVE_NONE or * @ref INPUT_KBD_MATRIX_COLUMN_DRIVE_ALL. */ void input_kbd_matrix_drive_column_hook(const struct device *dev, int col); #endif /** * @brief Common function to initialize a keyboard matrix device at init time. * * This function must be called at the end of the device init function. * * @param dev Keyboard matrix device instance. * * @retval 0 If initialized successfully. * @retval -errno Negative errno in case of failure. */ int input_kbd_matrix_common_init(const struct device *dev); #ifdef CONFIG_PM_DEVICE /** * @brief Common power management action handler. * * This handles PM actions for a keyboard matrix device, meant to be used as * argument of @ref PM_DEVICE_DT_INST_DEFINE. * * @param dev Keyboard matrix device instance. * @param action The power management action to handle. */ int input_kbd_matrix_pm_action(const struct device *dev, enum pm_device_action action); #endif /** @} */ #endif /* ZEPHYR_INCLUDE_INPUT_KBD_MATRIX_H_ */