@ -1,48 +1,158 @@
/*
/*
* Copyright ( c ) 2020 The ZMK Contributors
* Copyright ( c ) 2020 - 2021 The ZMK Contributors
*
*
* SPDX - License - Identifier : MIT
* SPDX - License - Identifier : MIT
*/
*/
# define DT_DRV_COMPAT zmk_kscan_gpio_matrix
# include <device.h>
# include <device.h>
# include <drivers/kscan .h>
# include <devicetree.h>
# include <drivers/gpio.h>
# include <drivers/gpio.h>
# include <drivers/kscan.h>
# include <logging/log.h>
# include <logging/log.h>
# include <sys/__assert.h>
# include <sys/util.h>
LOG_MODULE_DECLARE ( zmk , CONFIG_ZMK_LOG_LEVEL ) ;
LOG_MODULE_DECLARE ( zmk , CONFIG_ZMK_LOG_LEVEL ) ;
# define DT_DRV_COMPAT zmk_kscan_gpio_matrix
# if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
# if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
struct kscan_gpio_item_config {
# define INST_DIODE_DIR(n) DT_ENUM_IDX(DT_DRV_INST(n), diode_direction)
char * label ;
# define COND_DIODE_DIR(n, row2col_code, col2row_code) \
COND_CODE_0 ( INST_DIODE_DIR ( n ) , row2col_code , col2row_code )
# define INST_ROWS_LEN(n) DT_INST_PROP_LEN(n, row_gpios)
# define INST_COLS_LEN(n) DT_INST_PROP_LEN(n, col_gpios)
# define INST_MATRIX_LEN(n) (INST_ROWS_LEN(n) * INST_COLS_LEN(n))
# define INST_INPUTS_LEN(n) COND_DIODE_DIR(n, (INST_COLS_LEN(n)), (INST_ROWS_LEN(n)))
# define USE_POLLING IS_ENABLED(CONFIG_ZMK_KSCAN_MATRIX_POLLING)
# define USE_INTERRUPTS (!USE_POLLING)
# define COND_INTERRUPTS(code) COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (), code)
# define COND_POLL_OR_INTERRUPTS(pollcode, intcode) \
COND_CODE_1 ( CONFIG_ZMK_KSCAN_MATRIX_POLLING , pollcode , intcode )
// TODO (Zephr 2.6): replace the following
// kscan_gpio_dt_spec -> gpio_dt_spec
// KSCAN_GPIO_DT_SPEC_GET_BY_IDX -> GPIO_DT_SPEC_GET_BY_IDX
// gpio_pin_get -> gpio_pin_get_dt
// gpio_pin_set -> gpio_pin_set_dt
// gpio_pin_interrupt_configure -> gpio_pin_interrupt_configure_dt
struct kscan_gpio_dt_spec {
const struct device * port ;
gpio_pin_t pin ;
gpio_pin_t pin ;
gpio_flags_t flags ;
gpio_dt_ flags_t dt_ flags;
} ;
} ;
# define _KSCAN_GPIO_ITEM_CFG_INIT(n, prop, idx) \
# define KSCAN_GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, idx) \
{ \
{ \
. label = DT_INST_GPIO_LABEL_BY_IDX ( n , prop , idx ) , \
. port = DEVICE_DT_GET ( DT_GPIO_CTLR_BY_IDX ( node_id , prop , idx ) ) , \
. pin = DT_INST_GPIO_PIN_BY_IDX ( n , prop , idx ) , \
. pin = DT_GPIO_PIN_BY_IDX ( node_id , prop , idx ) , \
. flags = DT_INST_GPIO_FLAGS_BY_IDX ( n , prop , idx ) , \
. dt_flags = DT_GPIO_FLAGS_BY_IDX ( node_id , prop , idx ) , \
} ,
}
# define KSCAN_GPIO_ROW_CFG_INIT(idx, inst_idx) \
KSCAN_GPIO_DT_SPEC_GET_BY_IDX ( DT_DRV_INST ( inst_idx ) , row_gpios , idx ) ,
# define KSCAN_GPIO_COL_CFG_INIT(idx, inst_idx) \
KSCAN_GPIO_DT_SPEC_GET_BY_IDX ( DT_DRV_INST ( inst_idx ) , col_gpios , idx ) ,
enum kscan_diode_direction {
KSCAN_ROW2COL ,
KSCAN_COL2ROW ,
} ;
struct kscan_matrix_irq_callback {
const struct device * dev ;
struct gpio_callback callback ;
struct k_delayed_work * work ;
} ;
struct kscan_matrix_data {
const struct device * dev ;
kscan_callback_t callback ;
struct k_delayed_work work ;
# if USE_POLLING
struct k_timer poll_timer ;
# else
/** Array of length config->inputs.len */
struct kscan_matrix_irq_callback * irqs ;
# endif
/**
* Current state of the matrix as a flattened 2 D array of length
* ( config - > rows . len * config - > cols . len )
*/
bool * current_state ;
/** Buffer for reading in the next matrix state. Parallel array to current_state. */
bool * next_state ;
} ;
# define _KSCAN_GPIO_ROW_CFG_INIT(idx, n) _KSCAN_GPIO_ITEM_CFG_INIT(n, row_gpios, idx)
struct kscan_gpio_list {
# define _KSCAN_GPIO_COL_CFG_INIT(idx, n) _KSCAN_GPIO_ITEM_CFG_INIT(n, col_gpios, idx)
const struct kscan_gpio_dt_spec * gpios ;
size_t len ;
} ;
/** Define a kscan_gpio_list from a compile-time GPIO array. */
# define KSCAN_GPIO_LIST(gpio_array) \
( ( struct kscan_gpio_list ) { . gpios = gpio_array , . len = ARRAY_SIZE ( gpio_array ) } )
struct kscan_matrix_config {
struct kscan_gpio_list rows ;
struct kscan_gpio_list cols ;
struct kscan_gpio_list inputs ;
struct kscan_gpio_list outputs ;
int32_t debounce_period_ms ;
int32_t poll_period_ms ;
enum kscan_diode_direction diode_direction ;
} ;
/**
* Get the index into a matrix state array from a row and column .
*/
static int state_index_rc ( const struct kscan_matrix_config * config , const int row , const int col ) {
__ASSERT ( row < config - > rows . len , " Invalid row %i " , row ) ;
__ASSERT ( col < config - > cols . len , " Invalid column %i " , col ) ;
return ( col * config - > rows . len ) + row ;
}
/**
* Get the index into a matrix state array from input / output pin indices .
*/
static int state_index_io ( const struct kscan_matrix_config * config , const int input_idx ,
const int output_idx ) {
return ( config - > diode_direction = = KSCAN_ROW2COL )
? state_index_rc ( config , output_idx , input_idx )
: state_index_rc ( config , input_idx , output_idx ) ;
}
static int kscan_matrix_set_all_outputs ( const struct device * dev , const int value ) {
const struct kscan_matrix_config * config = dev - > config ;
for ( int i = 0 ; i < config - > outputs . len ; i + + ) {
const struct kscan_gpio_dt_spec * gpio = & config - > outputs . gpios [ i ] ;
int err = gpio_pin_set ( gpio - > port , gpio - > pin , value ) ;
if ( err ) {
LOG_ERR ( " Failed to set output %i to %i: %i " , i , value , err ) ;
return err ;
}
}
return 0 ;
}
# if !defined(CONFIG_ZMK_KSCAN_MATRIX_POLLING)
# if USE_INTERRUPTS
static int kscan_gpio_config_interrupts ( const struct device * * devices ,
static int kscan_matrix_interrupt_configure ( const struct device * dev , const gpio_flags_t flags ) {
const struct kscan_gpio_item_config * configs , size_t len ,
const struct kscan_matrix_config * config = dev - > config ;
gpio_flags_t flags ) {
for ( int i = 0 ; i < len ; i + + ) {
const struct device * dev = devices [ i ] ;
const struct kscan_gpio_item_config * cfg = & configs [ i ] ;
int err = gpio_pin_interrupt_configure ( dev , cfg - > pin , flags ) ;
for ( int i = 0 ; i < config - > inputs . len ; i + + ) {
const struct kscan_gpio_dt_spec * gpio = & config - > inputs . gpios [ i ] ;
int err = gpio_pin_interrupt_configure ( gpio - > port , gpio - > pin , flags ) ;
if ( err ) {
if ( err ) {
LOG_ERR ( " Unable to enable matrix GPIO interrupt " ) ;
LOG_ERR ( " Unable to configure interrupt for pin %u on %s " , gpio - > pin , gpio - > port - > name ) ;
return err ;
return err ;
}
}
}
}
@ -51,257 +161,299 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
}
}
# endif
# endif
# define COND_POLLING(code) COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (code), ())
# if USE_INTERRUPTS
# define COND_INTERRUPTS(code) COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (), (code))
static int kscan_matrix_interrupt_enable ( const struct device * dev ) {
# define COND_POLL_OR_INTERRUPTS(pollcode, intcode) \
int err = kscan_matrix_interrupt_configure ( dev , GPIO_INT_LEVEL_ACTIVE ) ;
COND_CODE_1 ( CONFIG_ZMK_KSCAN_MATRIX_POLLING , pollcode , intcode )
if ( err ) {
return err ;
}
# define INST_MATRIX_ROWS(n) DT_INST_PROP_LEN(n, row_gpios)
// While interrupts are enabled, set all outputs active so a pressed key
# define INST_MATRIX_COLS(n) DT_INST_PROP_LEN(n, col_gpios)
// will trigger an interrupt.
# define INST_OUTPUT_LEN(n) \
return kscan_matrix_set_all_outputs ( dev , 1 ) ;
COND_CODE_0 ( DT_ENUM_IDX ( DT_DRV_INST ( n ) , diode_direction ) , ( INST_MATRIX_ROWS ( n ) ) , \
}
( INST_MATRIX_COLS ( n ) ) )
# endif
# define INST_INPUT_LEN(n) \
COND_CODE_0 ( DT_ENUM_IDX ( DT_DRV_INST ( n ) , diode_direction ) , ( INST_MATRIX_COLS ( n ) ) , \
# if USE_INTERRUPTS
( INST_MATRIX_ROWS ( n ) ) )
static int kscan_matrix_interrupt_disable ( const struct device * dev ) {
int err = kscan_matrix_interrupt_configure ( dev , GPIO_INT_DISABLE ) ;
# define GPIO_INST_INIT(n) \
if ( err ) {
COND_INTERRUPTS ( \
return err ;
struct kscan_gpio_irq_callback_ # # n { \
}
struct COND_CODE_0 ( DT_INST_PROP ( n , debounce_period ) , ( k_work ) , ( k_delayed_work ) ) * \
work ; \
// While interrupts are disabled, set all outputs inactive so
struct gpio_callback callback ; \
// kscan_matrix_read() can scan them one by one.
const struct device * dev ; \
return kscan_matrix_set_all_outputs ( dev , 0 ) ;
} ; \
}
static struct kscan_gpio_irq_callback_ # # n irq_callbacks_ # # n [ INST_INPUT_LEN ( n ) ] ; ) \
# endif
struct kscan_gpio_config_ # # n { \
struct kscan_gpio_item_config rows [ INST_MATRIX_ROWS ( n ) ] ; \
# if USE_INTERRUPTS
struct kscan_gpio_item_config cols [ INST_MATRIX_COLS ( n ) ] ; \
static void kscan_matrix_irq_callback_handler ( const struct device * port , struct gpio_callback * cb ,
} ; \
const gpio_port_pins_t pin ) {
struct kscan_gpio_data_ # # n { \
struct kscan_matrix_irq_callback * data =
kscan_callback_t callback ; \
CONTAINER_OF ( cb , struct kscan_matrix_irq_callback , callback ) ;
COND_POLLING ( struct k_timer poll_timer ; ) \
const struct kscan_matrix_config * config = data - > dev - > config ;
struct COND_CODE_0 ( DT_INST_PROP ( n , debounce_period ) , ( k_work ) , ( k_delayed_work ) ) work ; \
bool matrix_state [ INST_MATRIX_ROWS ( n ) ] [ INST_MATRIX_COLS ( n ) ] ; \
// Disable our interrupts temporarily to avoid re-entry while we scan.
const struct device * rows [ INST_MATRIX_ROWS ( n ) ] ; \
kscan_matrix_interrupt_disable ( data - > dev ) ;
const struct device * cols [ INST_MATRIX_COLS ( n ) ] ; \
const struct device * dev ; \
// TODO (Zephyr 2.6): use k_work_reschedule()
} ; \
k_delayed_work_cancel ( data - > work ) ;
static const struct device * * kscan_gpio_input_devices_ # # n ( const struct device * dev ) { \
k_delayed_work_submit ( data - > work , K_MSEC ( config - > debounce_period_ms ) ) ;
struct kscan_gpio_data_ # # n * data = dev - > data ; \
}
return ( COND_CODE_0 ( DT_ENUM_IDX ( DT_DRV_INST ( n ) , diode_direction ) , ( data - > cols ) , \
# endif
( data - > rows ) ) ) ; \
} \
static int kscan_matrix_read ( const struct device * dev ) {
static const struct kscan_gpio_item_config * kscan_gpio_input_configs_ # # n ( \
struct kscan_matrix_data * data = dev - > data ;
const struct device * dev ) { \
const struct kscan_matrix_config * config = dev - > config ;
const struct kscan_gpio_config_ # # n * cfg = dev - > config ; \
return ( ( \
// Scan the matrix.
COND_CODE_0 ( DT_ENUM_IDX ( DT_DRV_INST ( n ) , diode_direction ) , ( cfg - > cols ) , ( cfg - > rows ) ) ) ) ; \
for ( int o = 0 ; o < config - > outputs . len ; o + + ) {
} \
const struct kscan_gpio_dt_spec * out_gpio = & config - > outputs . gpios [ o ] ;
static const struct device * * kscan_gpio_output_devices_ # # n ( const struct device * dev ) { \
struct kscan_gpio_data_ # # n * data = dev - > data ; \
int err = gpio_pin_set ( out_gpio - > port , out_gpio - > pin , 1 ) ;
return ( COND_CODE_0 ( DT_ENUM_IDX ( DT_DRV_INST ( n ) , diode_direction ) , ( data - > rows ) , \
if ( err ) {
( data - > cols ) ) ) ; \
LOG_ERR ( " Failed to set output %i active: %i " , o , err ) ;
} \
return err ;
static const struct kscan_gpio_item_config * kscan_gpio_output_configs_ # # n ( \
}
const struct device * dev ) { \
const struct kscan_gpio_config_ # # n * cfg = dev - > config ; \
for ( int i = 0 ; i < config - > inputs . len ; i + + ) {
return ( \
const struct kscan_gpio_dt_spec * in_gpio = & config - > inputs . gpios [ i ] ;
COND_CODE_0 ( DT_ENUM_IDX ( DT_DRV_INST ( n ) , diode_direction ) , ( cfg - > rows ) , ( cfg - > cols ) ) ) ; \
} \
const int index = state_index_io ( config , i , o ) ;
COND_INTERRUPTS ( \
data - > next_state [ index ] = gpio_pin_get ( in_gpio - > port , in_gpio - > pin ) ;
static int kscan_gpio_enable_interrupts_ # # n ( const struct device * dev ) { \
}
return kscan_gpio_config_interrupts ( kscan_gpio_input_devices_ # # n ( dev ) , \
kscan_gpio_input_configs_ # # n ( dev ) , \
err = gpio_pin_set ( out_gpio - > port , out_gpio - > pin , 0 ) ;
INST_INPUT_LEN ( n ) , GPIO_INT_LEVEL_ACTIVE ) ; \
if ( err ) {
} static int kscan_gpio_disable_interrupts_ # # n ( const struct device * dev ) { \
LOG_ERR ( " Failed to set output %i inactive: %i " , o , err ) ;
return kscan_gpio_config_interrupts ( kscan_gpio_input_devices_ # # n ( dev ) , \
return err ;
kscan_gpio_input_configs_ # # n ( dev ) , \
}
INST_INPUT_LEN ( n ) , GPIO_INT_DISABLE ) ; \
}
} ) \
static void kscan_gpio_set_output_state_ # # n ( const struct device * dev , int value ) { \
// Process the new state.
int err ; \
# if USE_INTERRUPTS
for ( int i = 0 ; i < INST_OUTPUT_LEN ( n ) ; i + + ) { \
bool submit_followup_read = false ;
const struct device * in_dev = kscan_gpio_output_devices_ # # n ( dev ) [ i ] ; \
# endif
const struct kscan_gpio_item_config * cfg = & kscan_gpio_output_configs_ # # n ( dev ) [ i ] ; \
if ( ( err = gpio_pin_set ( in_dev , cfg - > pin , value ) ) ) { \
for ( int r = 0 ; r < config - > rows . len ; r + + ) {
LOG_DBG ( " FAILED TO SET OUTPUT %d to %d " , cfg - > pin , err ) ; \
for ( int c = 0 ; c < config - > cols . len ; c + + ) {
} \
const int index = state_index_rc ( config , r , c ) ;
} \
const bool pressed = data - > next_state [ index ] ;
} \
static void kscan_gpio_set_matrix_state_ # # n ( \
// Follow up reads are needed if any key is pressed because further
bool state [ INST_MATRIX_ROWS ( n ) ] [ INST_MATRIX_COLS ( n ) ] , uint32_t input_index , \
// interrupts won't fire on already tripped GPIO pins.
uint32_t output_index , bool value ) { \
# if USE_INTERRUPTS
state [ COND_CODE_0 ( DT_ENUM_IDX ( DT_DRV_INST ( n ) , diode_direction ) , ( output_index ) , \
submit_followup_read = submit_followup_read | | pressed ;
( input_index ) ) ] \
# endif
[ COND_CODE_0 ( DT_ENUM_IDX ( DT_DRV_INST ( n ) , diode_direction ) , ( input_index ) , \
if ( pressed ! = data - > current_state [ index ] ) {
( output_index ) ) ] = value ; \
LOG_DBG ( " Sending event at %i,%i state %s " , r , c , pressed ? " on " : " off " ) ;
} \
data - > current_state [ index ] = pressed ;
static int kscan_gpio_read_ # # n ( const struct device * dev ) { \
data - > callback ( dev , r , c , pressed ) ;
COND_INTERRUPTS ( bool submit_follow_up_read = false ; ) \
}
struct kscan_gpio_data_ # # n * data = dev - > data ; \
}
static bool read_state [ INST_MATRIX_ROWS ( n ) ] [ INST_MATRIX_COLS ( n ) ] ; \
}
int err ; \
/* Disable our interrupts temporarily while we scan, to avoid */ \
# if USE_INTERRUPTS
/* re-entry while we iterate columns and set them active one by one */ \
if ( submit_followup_read ) {
/* to get pressed state for each matrix cell. */ \
// At least one key is pressed. Poll until everything is released.
COND_INTERRUPTS ( kscan_gpio_set_output_state_ # # n ( dev , 0 ) ; ) \
// TODO (Zephyr 2.6): use k_work_reschedule()
for ( int o = 0 ; o < INST_OUTPUT_LEN ( n ) ; o + + ) { \
k_delayed_work_cancel ( & data - > work ) ;
const struct device * out_dev = kscan_gpio_output_devices_ # # n ( dev ) [ o ] ; \
k_delayed_work_submit ( & data - > work , K_MSEC ( config - > debounce_period_ms ) ) ;
const struct kscan_gpio_item_config * out_cfg = & kscan_gpio_output_configs_ # # n ( dev ) [ o ] ; \
} else {
err = gpio_pin_set ( out_dev , out_cfg - > pin , 1 ) ; \
// All keys are released. Return to waiting for an interrupt.
if ( err ) { \
kscan_matrix_interrupt_enable ( dev ) ;
LOG_ERR ( " Failed to set output active (err %d) " , err ) ; \
}
return err ; \
# endif
} \
for ( int i = 0 ; i < INST_INPUT_LEN ( n ) ; i + + ) { \
return 0 ;
const struct device * in_dev = kscan_gpio_input_devices_ # # n ( dev ) [ i ] ; \
}
const struct kscan_gpio_item_config * in_cfg = \
& kscan_gpio_input_configs_ # # n ( dev ) [ i ] ; \
# if USE_POLLING
kscan_gpio_set_matrix_state_ # # n ( read_state , i , o , \
static void kscan_matrix_timer_handler ( struct k_timer * timer ) {
gpio_pin_get ( in_dev , in_cfg - > pin ) > 0 ) ; \
struct kscan_matrix_data * data = CONTAINER_OF ( timer , struct kscan_matrix_data , poll_timer ) ;
} \
k_delayed_work_submit ( & data - > work , K_NO_WAIT ) ;
err = gpio_pin_set ( out_dev , out_cfg - > pin , 0 ) ; \
}
if ( err ) { \
# endif
LOG_ERR ( " Failed to set output inactive (err %d) " , err ) ; \
return err ; \
static void kscan_matrix_work_handler ( struct k_work * work ) {
} \
struct k_delayed_work * dwork = CONTAINER_OF ( work , struct k_delayed_work , work ) ;
} \
struct kscan_matrix_data * data = CONTAINER_OF ( dwork , struct kscan_matrix_data , work ) ;
/* Set all our outputs as active again. */ \
kscan_matrix_read ( data - > dev ) ;
COND_INTERRUPTS ( kscan_gpio_set_output_state_ # # n ( dev , 1 ) ; ) \
}
for ( int r = 0 ; r < INST_MATRIX_ROWS ( n ) ; r + + ) { \
for ( int c = 0 ; c < INST_MATRIX_COLS ( n ) ; c + + ) { \
static int kscan_matrix_configure ( const struct device * dev , const kscan_callback_t callback ) {
bool pressed = read_state [ r ] [ c ] ; \
struct kscan_matrix_data * data = dev - > data ;
/* Follow up reads needed because further interrupts won't fire on already tripped \
* input GPIO pins */ \
if ( ! callback ) {
COND_INTERRUPTS ( submit_follow_up_read = ( submit_follow_up_read | | pressed ) ; ) \
return - EINVAL ;
if ( pressed ! = data - > matrix_state [ r ] [ c ] ) { \
}
LOG_DBG ( " Sending event at %d,%d state %s " , r , c , ( pressed ? " on " : " off " ) ) ; \
data - > matrix_state [ r ] [ c ] = pressed ; \
data - > callback = callback ;
data - > callback ( dev , r , c , pressed ) ; \
return 0 ;
} \
}
} \
} \
static int kscan_matrix_enable ( const struct device * dev ) {
COND_INTERRUPTS ( \
# if USE_POLLING
if ( submit_follow_up_read ) { \
struct kscan_matrix_data * data = dev - > data ;
COND_CODE_0 ( DT_INST_PROP ( n , debounce_period ) , ( { k_work_submit ( & data - > work ) ; } ) , \
const struct kscan_matrix_config * config = dev - > config ;
( { \
k_delayed_work_cancel ( & data - > work ) ; \
k_timer_start ( & data - > poll_timer , K_MSEC ( config - > poll_period_ms ) ,
k_delayed_work_submit ( & data - > work , K_MSEC ( 5 ) ) ; \
K_MSEC ( config - > poll_period_ms ) ) ;
} ) ) \
return 0 ;
} else { kscan_gpio_enable_interrupts_ # # n ( dev ) ; } ) \
# else
return 0 ; \
// Read will automatically enable interrupts once done.
} \
return kscan_matrix_read ( dev ) ;
static void kscan_gpio_work_handler_ # # n ( struct k_work * work ) { \
# endif
struct kscan_gpio_data_ # # n * data = CONTAINER_OF ( work , struct kscan_gpio_data_ # # n , work ) ; \
}
kscan_gpio_read_ # # n ( data - > dev ) ; \
} \
static int kscan_matrix_disable ( const struct device * dev ) {
COND_INTERRUPTS ( static void kscan_gpio_irq_callback_handler_ # # n ( \
# if USE_POLLING
const struct device * dev , struct gpio_callback * cb , gpio_port_pins_t pin ) { \
struct kscan_matrix_data * data = dev - > data ;
struct kscan_gpio_irq_callback_ # # n * data = \
CONTAINER_OF ( cb , struct kscan_gpio_irq_callback_ # # n , callback ) ; \
k_timer_stop ( & data - > poll_timer ) ;
kscan_gpio_disable_interrupts_ # # n ( data - > dev ) ; \
return 0 ;
COND_CODE_0 ( DT_INST_PROP ( n , debounce_period ) , ( { k_work_submit ( data - > work ) ; } ) , ( { \
# else
k_delayed_work_cancel ( data - > work ) ; \
return kscan_matrix_interrupt_disable ( dev ) ;
k_delayed_work_submit ( data - > work , \
# endif
K_MSEC ( DT_INST_PROP ( n , debounce_period ) ) ) ; \
}
} ) ) \
} ) \
static int kscan_matrix_init_input_inst ( const struct device * dev ,
const struct kscan_gpio_dt_spec * gpio , const int index ) {
if ( ! device_is_ready ( gpio - > port ) ) {
LOG_ERR ( " GPIO is not ready: %s " , gpio - > port - > name ) ;
return - ENODEV ;
}
int err = gpio_pin_configure ( gpio - > port , gpio - > pin , GPIO_INPUT | gpio - > dt_flags ) ;
if ( err ) {
LOG_ERR ( " Unable to configure pin %u on %s for input " , gpio - > pin , gpio - > port - > name ) ;
return err ;
}
LOG_DBG ( " Configured pin %u on %s for input " , gpio - > pin , gpio - > port - > name ) ;
# if USE_INTERRUPTS
struct kscan_matrix_data * data = dev - > data ;
struct kscan_matrix_irq_callback * irq = & data - > irqs [ index ] ;
irq - > dev = dev ;
irq - > work = & data - > work ;
gpio_init_callback ( & irq - > callback , kscan_matrix_irq_callback_handler , BIT ( gpio - > pin ) ) ;
err = gpio_add_callback ( gpio - > port , & irq - > callback ) ;
if ( err ) {
LOG_ERR ( " Error adding the callback to the input device: %i " , err ) ;
return err ;
}
# endif
return 0 ;
}
static int kscan_matrix_init_inputs ( const struct device * dev ) {
const struct kscan_matrix_config * config = dev - > config ;
for ( int i = 0 ; i < config - > inputs . len ; i + + ) {
const struct kscan_gpio_dt_spec * gpio = & config - > inputs . gpios [ i ] ;
int err = kscan_matrix_init_input_inst ( dev , gpio , i ) ;
if ( err ) {
return err ;
}
}
return 0 ;
}
static int kscan_matrix_init_output_inst ( const struct device * dev ,
const struct kscan_gpio_dt_spec * gpio ) {
if ( ! device_is_ready ( gpio - > port ) ) {
LOG_ERR ( " GPIO is not ready: %s " , gpio - > port - > name ) ;
return - ENODEV ;
}
int err = gpio_pin_configure ( gpio - > port , gpio - > pin , GPIO_OUTPUT | gpio - > dt_flags ) ;
if ( err ) {
LOG_ERR ( " Unable to configure pin %u on %s for output " , gpio - > pin , gpio - > port - > name ) ;
return err ;
}
LOG_DBG ( " Configured pin %u on %s for output " , gpio - > pin , gpio - > port - > name ) ;
return 0 ;
}
static int kscan_matrix_init_outputs ( const struct device * dev ) {
const struct kscan_matrix_config * config = dev - > config ;
for ( int i = 0 ; i < config - > outputs . len ; i + + ) {
const struct kscan_gpio_dt_spec * gpio = & config - > outputs . gpios [ i ] ;
int err = kscan_matrix_init_output_inst ( dev , gpio ) ;
if ( err ) {
return err ;
}
}
return 0 ;
}
static int kscan_matrix_init ( const struct device * dev ) {
struct kscan_matrix_data * data = dev - > data ;
data - > dev = dev ;
kscan_matrix_init_inputs ( dev ) ;
kscan_matrix_init_outputs ( dev ) ;
kscan_matrix_set_all_outputs ( dev , 0 ) ;
k_delayed_work_init ( & data - > work , kscan_matrix_work_handler ) ;
# if USE_POLLING
k_timer_init ( & data - > poll_timer , kscan_matrix_timer_handler , NULL ) ;
# endif
return 0 ;
}
static const struct kscan_driver_api kscan_matrix_api = {
. config = kscan_matrix_configure ,
. enable_callback = kscan_matrix_enable ,
. disable_callback = kscan_matrix_disable ,
} ;
# define KSCAN_MATRIX_INIT(index) \
static const struct kscan_gpio_dt_spec kscan_matrix_rows_ # # index [ ] = { \
UTIL_LISTIFY ( INST_ROWS_LEN ( index ) , KSCAN_GPIO_ROW_CFG_INIT , index ) } ; \
\
\
static struct kscan_gpio_data_ # # n kscan_gpio_data_ # # n = { \
static const struct kscan_gpio_dt_spec kscan_matrix_cols_ # # index [ ] = { \
. rows = { [ INST_MATRIX_ROWS ( n ) - 1 ] = NULL } , . cols = { [ INST_MATRIX_COLS ( n ) - 1 ] = NULL } } ; \
UTIL_LISTIFY ( INST_COLS_LEN ( index ) , KSCAN_GPIO_COL_CFG_INIT , index ) } ; \
static int kscan_gpio_configure_ # # n ( const struct device * dev , kscan_callback_t callback ) { \
\
struct kscan_gpio_data_ # # n * data = dev - > data ; \
static bool kscan_current_state_ # # index [ INST_MATRIX_LEN ( index ) ] ; \
if ( ! callback ) { \
static bool kscan_next_state_ # # index [ INST_MATRIX_LEN ( index ) ] ; \
return - EINVAL ; \
\
} \
COND_INTERRUPTS ( ( static struct kscan_matrix_irq_callback \
data - > callback = callback ; \
kscan_matrix_irqs_ # # index [ INST_INPUTS_LEN ( index ) ] ; ) ) \
LOG_DBG ( " Configured GPIO %d " , n ) ; \
\
return 0 ; \
static struct kscan_matrix_data kscan_matrix_data_ # # index = { \
} ; \
. current_state = kscan_current_state_ # # index , \
static int kscan_gpio_enable_ # # n ( const struct device * dev ) { \
. next_state = kscan_next_state_ # # index , \
COND_POLL_OR_INTERRUPTS ( ( struct kscan_gpio_data_ # # n * data = dev - > data ; \
COND_INTERRUPTS ( ( . irqs = kscan_matrix_irqs_ # # index , ) ) } ; \
k_timer_start ( & data - > poll_timer , K_MSEC ( 10 ) , K_MSEC ( 10 ) ) ; \
\
return 0 ; ) , \
static struct kscan_matrix_config kscan_matrix_config_ # # index = { \
( int err = kscan_gpio_enable_interrupts_ # # n ( dev ) ; \
. rows = KSCAN_GPIO_LIST ( kscan_matrix_rows_ # # index ) , \
if ( err ) { return err ; } return kscan_gpio_read_ # # n ( dev ) ; ) ) \
. cols = KSCAN_GPIO_LIST ( kscan_matrix_cols_ # # index ) , \
} ; \
. inputs = KSCAN_GPIO_LIST ( \
static int kscan_gpio_disable_ # # n ( const struct device * dev ) { \
COND_DIODE_DIR ( index , ( kscan_matrix_cols_ # # index ) , ( kscan_matrix_rows_ # # index ) ) ) , \
COND_POLL_OR_INTERRUPTS ( ( struct kscan_gpio_data_ # # n * data = dev - > data ; \
. outputs = KSCAN_GPIO_LIST ( \
k_timer_stop ( & data - > poll_timer ) ; return 0 ; ) , \
COND_DIODE_DIR ( index , ( kscan_matrix_rows_ # # index ) , ( kscan_matrix_cols_ # # index ) ) ) , \
( return kscan_gpio_disable_interrupts_ # # n ( dev ) ; ) ) \
. debounce_period_ms = DT_INST_PROP ( index , debounce_period ) , \
} ; \
. poll_period_ms = DT_INST_PROP ( index , poll_period_ms ) , \
COND_POLLING ( static void kscan_gpio_timer_handler_ # # n ( struct k_timer * timer ) { \
. diode_direction = INST_DIODE_DIR ( index ) , \
struct kscan_gpio_data_ # # n * data = \
CONTAINER_OF ( timer , struct kscan_gpio_data_ # # n , poll_timer ) ; \
k_work_submit ( & data - > work . work ) ; \
} ) \
static int kscan_gpio_init_ # # n ( const struct device * dev ) { \
struct kscan_gpio_data_ # # n * data = dev - > data ; \
int err ; \
const struct device * * input_devices = kscan_gpio_input_devices_ # # n ( dev ) ; \
for ( int i = 0 ; i < INST_INPUT_LEN ( n ) ; i + + ) { \
const struct kscan_gpio_item_config * in_cfg = & kscan_gpio_input_configs_ # # n ( dev ) [ i ] ; \
input_devices [ i ] = device_get_binding ( in_cfg - > label ) ; \
if ( ! input_devices [ i ] ) { \
LOG_ERR ( " Unable to find input GPIO device " ) ; \
return - EINVAL ; \
} \
err = gpio_pin_configure ( input_devices [ i ] , in_cfg - > pin , GPIO_INPUT | in_cfg - > flags ) ; \
if ( err ) { \
LOG_ERR ( " Unable to configure pin %d on %s for input " , in_cfg - > pin , in_cfg - > label ) ; \
return err ; \
} else { \
LOG_DBG ( " Configured pin %d on %s for input " , in_cfg - > pin , in_cfg - > label ) ; \
} \
COND_INTERRUPTS ( \
irq_callbacks_ # # n [ i ] . work = & data - > work ; irq_callbacks_ # # n [ i ] . dev = dev ; \
gpio_init_callback ( & irq_callbacks_ # # n [ i ] . callback , \
kscan_gpio_irq_callback_handler_ # # n , BIT ( in_cfg - > pin ) ) ; \
err = gpio_add_callback ( input_devices [ i ] , & irq_callbacks_ # # n [ i ] . callback ) ; \
if ( err ) { \
LOG_ERR ( " Error adding the callback to the input device " ) ; \
return err ; \
} ) \
} \
const struct device * * output_devices = kscan_gpio_output_devices_ # # n ( dev ) ; \
for ( int o = 0 ; o < INST_OUTPUT_LEN ( n ) ; o + + ) { \
const struct kscan_gpio_item_config * out_cfg = & kscan_gpio_output_configs_ # # n ( dev ) [ o ] ; \
output_devices [ o ] = device_get_binding ( out_cfg - > label ) ; \
if ( ! output_devices [ o ] ) { \
LOG_ERR ( " Unable to find output GPIO device " ) ; \
return - EINVAL ; \
} \
err = \
gpio_pin_configure ( output_devices [ o ] , out_cfg - > pin , GPIO_OUTPUT | out_cfg - > flags ) ; \
if ( err ) { \
LOG_ERR ( " Unable to configure pin %d on %s for output " , out_cfg - > pin , \
out_cfg - > label ) ; \
return err ; \
} \
} \
data - > dev = dev ; \
( COND_CODE_0 ( DT_INST_PROP ( n , debounce_period ) , ( k_work_init ) , ( k_delayed_work_init ) ) ) ( \
& data - > work , kscan_gpio_work_handler_ # # n ) ; \
COND_POLL_OR_INTERRUPTS ( \
( k_timer_init ( & data - > poll_timer , kscan_gpio_timer_handler_ # # n , NULL ) ; \
kscan_gpio_set_output_state_ # # n ( dev , 0 ) ; ) , \
( kscan_gpio_set_output_state_ # # n ( dev , 1 ) ; ) ) \
return 0 ; \
} \
static const struct kscan_driver_api gpio_driver_api_ # # n = { \
. config = kscan_gpio_configure_ # # n , \
. enable_callback = kscan_gpio_enable_ # # n , \
. disable_callback = kscan_gpio_disable_ # # n , \
} ; \
static const struct kscan_gpio_config_ # # n kscan_gpio_config_ # # n = { \
. rows = { UTIL_LISTIFY ( INST_MATRIX_ROWS ( n ) , _KSCAN_GPIO_ROW_CFG_INIT , n ) } , \
. cols = { UTIL_LISTIFY ( INST_MATRIX_COLS ( n ) , _KSCAN_GPIO_COL_CFG_INIT , n ) } , \
} ; \
} ; \
DEVICE_DT_INST_DEFINE ( n , kscan_gpio_init_ # # n , device_pm_control_nop , & kscan_gpio_data_ # # n , \
\
& kscan_gpio_config_ # # n , APPLICATION , CONFIG_APPLICATION_INIT_PRIORITY , \
DEVICE_DT_INST_DEFINE ( index , & kscan_matrix_init , device_pm_control_nop , \
& gpio_driver_api_ # # n ) ;
& kscan_matrix_data_ # # index , & kscan_matrix_config_ # # index , APPLICATION , \
CONFIG_APPLICATION_INIT_PRIORITY , & kscan_matrix_api ) ;
DT_INST_FOREACH_STATUS_OKAY ( GPIO_INST_INIT )
DT_INST_FOREACH_STATUS_OKAY ( KSCAN_MATRIX_INIT ) ;
# endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
# endif // DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)