1 /* 2 * Copyright 2023 Google LLC 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7 #ifndef ZEPHYR_INCLUDE_INPUT_KBD_MATRIX_H_ 8 #define ZEPHYR_INCLUDE_INPUT_KBD_MATRIX_H_ 9 10 /** 11 * @brief Keyboard Matrix API 12 * @defgroup input_kbd_matrix Keyboard Matrix API 13 * @ingroup io_interfaces 14 * @{ 15 */ 16 17 #include <zephyr/device.h> 18 #include <zephyr/kernel.h> 19 #include <zephyr/sys/util.h> 20 #include <zephyr/sys_clock.h> 21 #include <zephyr/toolchain.h> 22 23 /** Special drive_column argument for not driving any column */ 24 #define INPUT_KBD_MATRIX_COLUMN_DRIVE_NONE -1 25 26 /** Special drive_column argument for driving all the columns */ 27 #define INPUT_KBD_MATRIX_COLUMN_DRIVE_ALL -2 28 29 /** Number of tracked scan cycles */ 30 #define INPUT_KBD_MATRIX_SCAN_OCURRENCES 30U 31 32 /** Row entry data type */ 33 #if CONFIG_INPUT_KBD_MATRIX_16_BIT_ROW 34 typedef uint16_t kbd_row_t; 35 #define PRIkbdrow "%04x" 36 #else 37 typedef uint8_t kbd_row_t; 38 #define PRIkbdrow "%02x" 39 #endif 40 41 #if defined(CONFIG_INPUT_KBD_ACTUAL_KEY_MASK_DYNAMIC) || defined(__DOXYGEN__) 42 #define INPUT_KBD_ACTUAL_KEY_MASK_CONST 43 /** 44 * @brief Enables or disables a specific row, column combination in the actual 45 * key mask. 46 * 47 * This allows enabling or disabling spcific row, column combination in the 48 * actual key mask in runtime. It can be useful if some of the keys are not 49 * present in some configuration, and the specific configuration is determined 50 * in runtime. Requires @kconfig{CONFIG_INPUT_KBD_ACTUAL_KEY_MASK_DYNAMIC} to 51 * be enabled. 52 * 53 * @param dev Pointer to the keyboard matrix device. 54 * @param row The matrix row to enable or disable. 55 * @param col The matrix column to enable or disable. 56 * @param enabled Whether the specificied row, col has to be enabled or disabled. 57 * 58 * @retval 0 If the change is successful. 59 * @retval -errno Negative errno if row or col are out of range for the device. 60 */ 61 int input_kbd_matrix_actual_key_mask_set(const struct device *dev, 62 uint8_t row, uint8_t col, bool enabled); 63 #else 64 #define INPUT_KBD_ACTUAL_KEY_MASK_CONST const 65 #endif 66 67 /** Maximum number of rows */ 68 #define INPUT_KBD_MATRIX_ROW_BITS NUM_BITS(kbd_row_t) 69 70 /** 71 * @brief Keyboard matrix internal APIs. 72 */ 73 struct input_kbd_matrix_api { 74 /** 75 * @brief Request to drive a specific column. 76 * 77 * Request to drive a specific matrix column, or none, or all. 78 * 79 * @param dev Pointer to the keyboard matrix device. 80 * @param col The column to drive, or 81 * @ref INPUT_KBD_MATRIX_COLUMN_DRIVE_NONE or 82 * @ref INPUT_KBD_MATRIX_COLUMN_DRIVE_ALL. 83 */ 84 void (*drive_column)(const struct device *dev, int col); 85 /** 86 * @brief Read the matrix row. 87 * 88 * @param dev Pointer to the keyboard matrix device. 89 */ 90 kbd_row_t (*read_row)(const struct device *dev); 91 /** 92 * @brief Request to put the matrix in detection mode. 93 * 94 * Request to put the driver in detection mode, this is called after a 95 * request to drive all the column and typically involves reenabling 96 * interrupts row pin changes. 97 * 98 * @param dev Pointer to the keyboard matrix device. 99 * @param enable Whether detection mode has to be enabled or disabled. 100 */ 101 void (*set_detect_mode)(const struct device *dev, bool enabled); 102 }; 103 104 /** 105 * @brief Common keyboard matrix config. 106 * 107 * This structure **must** be placed first in the driver's config structure. 108 */ 109 struct input_kbd_matrix_common_config { 110 const struct input_kbd_matrix_api *api; 111 uint8_t row_size; 112 uint8_t col_size; 113 uint32_t poll_period_us; 114 uint32_t poll_timeout_ms; 115 uint32_t debounce_down_us; 116 uint32_t debounce_up_us; 117 uint32_t settle_time_us; 118 bool ghostkey_check; 119 INPUT_KBD_ACTUAL_KEY_MASK_CONST kbd_row_t *actual_key_mask; 120 121 /* extra data pointers */ 122 kbd_row_t *matrix_stable_state; 123 kbd_row_t *matrix_unstable_state; 124 kbd_row_t *matrix_previous_state; 125 kbd_row_t *matrix_new_state; 126 uint8_t *scan_cycle_idx; 127 }; 128 129 #define INPUT_KBD_MATRIX_DATA_NAME(node_id, name) \ 130 _CONCAT(__input_kbd_matrix_, \ 131 _CONCAT(name, DEVICE_DT_NAME_GET(node_id))) 132 133 /** 134 * @brief Defines the common keyboard matrix support data from devicetree, 135 * specify row and col count. 136 */ 137 #define INPUT_KBD_MATRIX_DT_DEFINE_ROW_COL(node_id, _row_size, _col_size) \ 138 BUILD_ASSERT(IN_RANGE(_row_size, 1, INPUT_KBD_MATRIX_ROW_BITS), "invalid row-size"); \ 139 BUILD_ASSERT(IN_RANGE(_col_size, 1, UINT8_MAX), "invalid col-size"); \ 140 IF_ENABLED(DT_NODE_HAS_PROP(node_id, actual_key_mask), ( \ 141 BUILD_ASSERT(DT_PROP_LEN(node_id, actual_key_mask) == _col_size, \ 142 "actual-key-mask size does not match the number of columns"); \ 143 static INPUT_KBD_ACTUAL_KEY_MASK_CONST kbd_row_t \ 144 INPUT_KBD_MATRIX_DATA_NAME(node_id, actual_key_mask)[_col_size] = \ 145 DT_PROP(node_id, actual_key_mask); \ 146 )) \ 147 static kbd_row_t INPUT_KBD_MATRIX_DATA_NAME(node_id, stable_state)[_col_size]; \ 148 static kbd_row_t INPUT_KBD_MATRIX_DATA_NAME(node_id, unstable_state)[_col_size]; \ 149 static kbd_row_t INPUT_KBD_MATRIX_DATA_NAME(node_id, previous_state)[_col_size]; \ 150 static kbd_row_t INPUT_KBD_MATRIX_DATA_NAME(node_id, new_state)[_col_size]; \ 151 static uint8_t INPUT_KBD_MATRIX_DATA_NAME(node_id, scan_cycle_idx)[_row_size * _col_size]; 152 153 /** 154 * @brief Defines the common keyboard matrix support data from devicetree. 155 */ 156 #define INPUT_KBD_MATRIX_DT_DEFINE(node_id) \ 157 INPUT_KBD_MATRIX_DT_DEFINE_ROW_COL( \ 158 node_id, DT_PROP(node_id, row_size), DT_PROP(node_id, col_size)) 159 160 /** 161 * @brief Defines the common keyboard matrix support data from devicetree 162 * instance, specify row and col count. 163 * 164 * @param inst Instance. 165 * @param row_size The matrix row count. 166 * @param col_size The matrix column count. 167 */ 168 #define INPUT_KBD_MATRIX_DT_INST_DEFINE_ROW_COL(inst, row_size, col_size) \ 169 INPUT_KBD_MATRIX_DT_DEFINE_ROW_COL(DT_DRV_INST(inst), row_size, col_size) 170 171 /** 172 * @brief Defines the common keyboard matrix support data from devicetree instance. 173 * 174 * @param inst Instance. 175 */ 176 #define INPUT_KBD_MATRIX_DT_INST_DEFINE(inst) \ 177 INPUT_KBD_MATRIX_DT_DEFINE(DT_DRV_INST(inst)) 178 179 /** 180 * @brief Initialize common keyboard matrix config from devicetree, specify row and col count. 181 * 182 * @param node_id The devicetree node identifier. 183 * @param _api Pointer to a @ref input_kbd_matrix_api structure. 184 * @param _row_size The matrix row count. 185 * @param _col_size The matrix column count. 186 */ 187 #define INPUT_KBD_MATRIX_DT_COMMON_CONFIG_INIT_ROW_COL(node_id, _api, _row_size, _col_size) \ 188 { \ 189 .api = _api, \ 190 .row_size = _row_size, \ 191 .col_size = _col_size, \ 192 .poll_period_us = DT_PROP(node_id, poll_period_ms) * USEC_PER_MSEC, \ 193 .poll_timeout_ms = DT_PROP(node_id, poll_timeout_ms), \ 194 .debounce_down_us = DT_PROP(node_id, debounce_down_ms) * USEC_PER_MSEC, \ 195 .debounce_up_us = DT_PROP(node_id, debounce_up_ms) * USEC_PER_MSEC, \ 196 .settle_time_us = DT_PROP(node_id, settle_time_us), \ 197 .ghostkey_check = !DT_PROP(node_id, no_ghostkey_check), \ 198 IF_ENABLED(DT_NODE_HAS_PROP(node_id, actual_key_mask), ( \ 199 .actual_key_mask = INPUT_KBD_MATRIX_DATA_NAME(node_id, actual_key_mask), \ 200 )) \ 201 \ 202 .matrix_stable_state = INPUT_KBD_MATRIX_DATA_NAME(node_id, stable_state), \ 203 .matrix_unstable_state = INPUT_KBD_MATRIX_DATA_NAME(node_id, unstable_state), \ 204 .matrix_previous_state = INPUT_KBD_MATRIX_DATA_NAME(node_id, previous_state), \ 205 .matrix_new_state = INPUT_KBD_MATRIX_DATA_NAME(node_id, new_state), \ 206 .scan_cycle_idx = INPUT_KBD_MATRIX_DATA_NAME(node_id, scan_cycle_idx), \ 207 } 208 209 /** 210 * @brief Initialize common keyboard matrix config from devicetree. 211 * 212 * @param node_id The devicetree node identifier. 213 * @param api Pointer to a @ref input_kbd_matrix_api structure. 214 */ 215 #define INPUT_KBD_MATRIX_DT_COMMON_CONFIG_INIT(node_id, api) \ 216 INPUT_KBD_MATRIX_DT_COMMON_CONFIG_INIT_ROW_COL( \ 217 node_id, api, DT_PROP(node_id, row_size), DT_PROP(node_id, col_size)) 218 219 /** 220 * @brief Initialize common keyboard matrix config from devicetree instance, 221 * specify row and col count. 222 * 223 * @param inst Instance. 224 * @param api Pointer to a @ref input_kbd_matrix_api structure. 225 * @param row_size The matrix row count. 226 * @param col_size The matrix column count. 227 */ 228 #define INPUT_KBD_MATRIX_DT_INST_COMMON_CONFIG_INIT_ROW_COL(inst, api, row_size, col_size) \ 229 INPUT_KBD_MATRIX_DT_COMMON_CONFIG_INIT_ROW_COL(DT_DRV_INST(inst), api, row_size, col_size) 230 231 /** 232 * @brief Initialize common keyboard matrix config from devicetree instance. 233 * 234 * @param inst Instance. 235 * @param api Pointer to a @ref input_kbd_matrix_api structure. 236 */ 237 #define INPUT_KBD_MATRIX_DT_INST_COMMON_CONFIG_INIT(inst, api) \ 238 INPUT_KBD_MATRIX_DT_COMMON_CONFIG_INIT(DT_DRV_INST(inst), api) 239 240 /** 241 * @brief Common keyboard matrix data. 242 * 243 * This structure **must** be placed first in the driver's data structure. 244 */ 245 struct input_kbd_matrix_common_data { 246 /* Track previous cycles, used for debouncing. */ 247 uint32_t scan_clk_cycle[INPUT_KBD_MATRIX_SCAN_OCURRENCES]; 248 uint8_t scan_cycles_idx; 249 250 struct k_sem poll_lock; 251 252 struct k_thread thread; 253 254 K_KERNEL_STACK_MEMBER(thread_stack, 255 CONFIG_INPUT_KBD_MATRIX_THREAD_STACK_SIZE); 256 }; 257 258 /** 259 * @brief Validate the offset of the common data structures. 260 * 261 * @param config Name of the config structure. 262 * @param data Name of the data structure. 263 */ 264 #define INPUT_KBD_STRUCT_CHECK(config, data) \ 265 BUILD_ASSERT(offsetof(config, common) == 0, \ 266 "struct input_kbd_matrix_common_config must be placed first"); \ 267 BUILD_ASSERT(offsetof(data, common) == 0, \ 268 "struct input_kbd_matrix_common_data must be placed first") 269 270 /** 271 * @brief Start scanning the keyboard matrix 272 * 273 * Starts the keyboard matrix scanning cycle, this should be called in reaction 274 * of a press event, after the device has been put in detect mode. 275 * 276 * @param dev Keyboard matrix device instance. 277 */ 278 void input_kbd_matrix_poll_start(const struct device *dev); 279 280 #if defined(CONFIG_INPUT_KBD_DRIVE_COLUMN_HOOK) || defined(__DOXYGEN__) 281 /** 282 * @brief Drive column hook 283 * 284 * This can be implemented by the application to handle column selection 285 * quirks. Called after the driver specific drive_column function. Requires 286 * @kconfig{CONFIG_INPUT_KBD_DRIVE_COLUMN_HOOK} to be enabled. 287 * 288 * @param dev Keyboard matrix device instance. 289 * @param col The column to drive, or 290 * @ref INPUT_KBD_MATRIX_COLUMN_DRIVE_NONE or 291 * @ref INPUT_KBD_MATRIX_COLUMN_DRIVE_ALL. 292 */ 293 void input_kbd_matrix_drive_column_hook(const struct device *dev, int col); 294 #endif 295 296 /** 297 * @brief Common function to initialize a keyboard matrix device at init time. 298 * 299 * This function must be called at the end of the device init function. 300 * 301 * @param dev Keyboard matrix device instance. 302 * 303 * @retval 0 If initialized successfully. 304 * @retval -errno Negative errno in case of failure. 305 */ 306 int input_kbd_matrix_common_init(const struct device *dev); 307 308 /** @} */ 309 310 #endif /* ZEPHYR_INCLUDE_INPUT_KBD_MATRIX_H_ */ 311