blob: 9600b510f0a174b6d47e24920a170f58852e5d39 [file] [log] [blame]
/*
* Copyright 2023 Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/device.h>
#include <zephyr/drivers/gpio/gpio_emul.h>
#include <zephyr/input/input.h>
#include <zephyr/input/input_kbd_matrix.h>
#include <zephyr/kernel.h>
#include <zephyr/ztest.h>
#define INTERRUPT_NODE DT_NODELABEL(kbd_matrix_interrupt)
#define POLL_NODE DT_NODELABEL(kbd_matrix_poll)
#define SCAN_NODE DT_NODELABEL(kbd_matrix_scan)
static const struct device *dev_interrupt = DEVICE_DT_GET_OR_NULL(INTERRUPT_NODE);
static const struct device *dev_poll = DEVICE_DT_GET_OR_NULL(POLL_NODE);
static const struct device *dev_scan = DEVICE_DT_GET_OR_NULL(SCAN_NODE);
#define INTERRUPT_R0_PIN DT_GPIO_PIN_BY_IDX(INTERRUPT_NODE, row_gpios, 0)
#define INTERRUPT_R1_PIN DT_GPIO_PIN_BY_IDX(INTERRUPT_NODE, row_gpios, 1)
#define INTERRUPT_C0_PIN DT_GPIO_PIN_BY_IDX(INTERRUPT_NODE, col_gpios, 0)
#define INTERRUPT_C1_PIN DT_GPIO_PIN_BY_IDX(INTERRUPT_NODE, col_gpios, 1)
#define POLL_R0_PIN DT_GPIO_PIN_BY_IDX(POLL_NODE, row_gpios, 0)
#define POLL_R1_PIN DT_GPIO_PIN_BY_IDX(POLL_NODE, row_gpios, 1)
#define POLL_C0_PIN DT_GPIO_PIN_BY_IDX(POLL_NODE, col_gpios, 0)
#define POLL_C1_PIN DT_GPIO_PIN_BY_IDX(POLL_NODE, col_gpios, 1)
#define SCAN_R0_PIN DT_GPIO_PIN_BY_IDX(SCAN_NODE, row_gpios, 0)
#define SCAN_R1_PIN DT_GPIO_PIN_BY_IDX(SCAN_NODE, row_gpios, 1)
#define SCAN_C0_PIN DT_GPIO_PIN_BY_IDX(SCAN_NODE, col_gpios, 0)
#define SCAN_C1_PIN DT_GPIO_PIN_BY_IDX(SCAN_NODE, col_gpios, 1)
static const struct device *dev_gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0));
enum {
KBD_DEV_INTERRUPT,
KBD_DEV_POLL,
KBD_DEV_SCAN,
};
#define KBD_DEV_COUNT (KBD_DEV_SCAN + 1)
#define COL_COUNT 2
BUILD_ASSERT(DT_PROP_LEN(INTERRUPT_NODE, col_gpios) == COL_COUNT);
BUILD_ASSERT(DT_PROP_LEN(POLL_NODE, col_gpios) == COL_COUNT);
BUILD_ASSERT(DT_PROP_LEN(SCAN_NODE, col_gpios) == COL_COUNT);
static uint8_t test_rows[KBD_DEV_COUNT][COL_COUNT];
static int scan_set_count[KBD_DEV_COUNT];
static void gpio_kbd_scan_set_row(const struct device *dev, uint8_t row)
{
if (dev == dev_interrupt) {
gpio_emul_input_set(dev_gpio, INTERRUPT_R0_PIN, !(row & BIT(0)));
gpio_emul_input_set(dev_gpio, INTERRUPT_R1_PIN, !(row & BIT(1)));
return;
} else if (dev == dev_poll) {
gpio_emul_input_set(dev_gpio, POLL_R0_PIN, !(row & BIT(0)));
gpio_emul_input_set(dev_gpio, POLL_R1_PIN, !(row & BIT(1)));
return;
} else if (dev == dev_scan) {
gpio_emul_input_set(dev_gpio, SCAN_R0_PIN, !(row & BIT(0)));
gpio_emul_input_set(dev_gpio, SCAN_R1_PIN, !(row & BIT(1)));
return;
}
TC_PRINT("unknown device: %s\n", dev->name);
}
void input_kbd_matrix_drive_column_hook(const struct device *dev, int col)
{
gpio_flags_t flags0, flags1;
if (col >= COL_COUNT) {
TC_PRINT("invalid column: %d\n", col);
return;
}
if (dev == dev_interrupt) {
scan_set_count[KBD_DEV_INTERRUPT]++;
gpio_kbd_scan_set_row(dev, test_rows[KBD_DEV_INTERRUPT][col]);
/* Verify that columns are NOT driven. */
gpio_emul_flags_get(dev_gpio, INTERRUPT_C0_PIN, &flags0);
gpio_emul_flags_get(dev_gpio, INTERRUPT_C1_PIN, &flags1);
switch (col) {
case 0:
zassert_equal(flags0 & GPIO_DIR_MASK, GPIO_OUTPUT);
zassert_equal(flags1 & GPIO_DIR_MASK, GPIO_INPUT);
break;
case 1:
zassert_equal(flags0 & GPIO_DIR_MASK, GPIO_INPUT);
zassert_equal(flags1 & GPIO_DIR_MASK, GPIO_OUTPUT);
break;
case INPUT_KBD_MATRIX_COLUMN_DRIVE_NONE:
zassert_equal(flags0 & GPIO_DIR_MASK, GPIO_INPUT);
zassert_equal(flags1 & GPIO_DIR_MASK, GPIO_INPUT);
break;
case INPUT_KBD_MATRIX_COLUMN_DRIVE_ALL:
zassert_equal(flags0 & GPIO_DIR_MASK, GPIO_OUTPUT);
zassert_equal(flags1 & GPIO_DIR_MASK, GPIO_OUTPUT);
break;
}
return;
} else if (dev == dev_poll) {
scan_set_count[KBD_DEV_POLL]++;
gpio_kbd_scan_set_row(dev, test_rows[KBD_DEV_POLL][col]);
/* Verify that columns are always driven */
gpio_emul_flags_get(dev_gpio, POLL_C0_PIN, &flags0);
gpio_emul_flags_get(dev_gpio, POLL_C1_PIN, &flags1);
zassert_equal(flags0 & GPIO_DIR_MASK, GPIO_OUTPUT);
zassert_equal(flags1 & GPIO_DIR_MASK, GPIO_OUTPUT);
switch (col) {
case 0:
zassert_equal(gpio_emul_output_get(dev_gpio, POLL_C0_PIN), 0);
zassert_equal(gpio_emul_output_get(dev_gpio, POLL_C1_PIN), 1);
break;
case 1:
zassert_equal(gpio_emul_output_get(dev_gpio, POLL_C0_PIN), 1);
zassert_equal(gpio_emul_output_get(dev_gpio, POLL_C1_PIN), 0);
break;
case INPUT_KBD_MATRIX_COLUMN_DRIVE_NONE:
zassert_equal(gpio_emul_output_get(dev_gpio, POLL_C0_PIN), 1);
zassert_equal(gpio_emul_output_get(dev_gpio, POLL_C1_PIN), 1);
break;
case INPUT_KBD_MATRIX_COLUMN_DRIVE_ALL:
zassert_equal(gpio_emul_output_get(dev_gpio, POLL_C0_PIN), 0);
zassert_equal(gpio_emul_output_get(dev_gpio, POLL_C1_PIN), 0);
break;
}
return;
} else if (dev == dev_scan) {
scan_set_count[KBD_DEV_SCAN]++;
gpio_kbd_scan_set_row(dev, test_rows[KBD_DEV_SCAN][col]);
/* Verify that columns are always driven */
gpio_emul_flags_get(dev_gpio, SCAN_C0_PIN, &flags0);
gpio_emul_flags_get(dev_gpio, SCAN_C1_PIN, &flags1);
zassert_equal(flags0 & GPIO_DIR_MASK, GPIO_OUTPUT);
zassert_equal(flags1 & GPIO_DIR_MASK, GPIO_OUTPUT);
switch (col) {
case 0:
zassert_equal(gpio_emul_output_get(dev_gpio, SCAN_C0_PIN), 0);
zassert_equal(gpio_emul_output_get(dev_gpio, SCAN_C1_PIN), 1);
break;
case 1:
zassert_equal(gpio_emul_output_get(dev_gpio, SCAN_C0_PIN), 1);
zassert_equal(gpio_emul_output_get(dev_gpio, SCAN_C1_PIN), 0);
break;
case INPUT_KBD_MATRIX_COLUMN_DRIVE_NONE:
zassert_equal(gpio_emul_output_get(dev_gpio, SCAN_C0_PIN), 1);
zassert_equal(gpio_emul_output_get(dev_gpio, SCAN_C1_PIN), 1);
break;
case INPUT_KBD_MATRIX_COLUMN_DRIVE_ALL:
zassert_equal(gpio_emul_output_get(dev_gpio, SCAN_C0_PIN), 0);
zassert_equal(gpio_emul_output_get(dev_gpio, SCAN_C1_PIN), 0);
break;
}
return;
}
TC_PRINT("unknown device: %s\n", dev->name);
}
/* support stuff */
static struct {
int row;
int col;
int val;
int event_count;
} test_event_data;
static int last_checked_event_count;
#define assert_no_new_events() \
zassert_equal(last_checked_event_count, test_event_data.event_count);
#define assert_new_event(_row, _col, _val) { \
last_checked_event_count++; \
zassert_equal(last_checked_event_count, test_event_data.event_count); \
zassert_equal(_row, test_event_data.row); \
zassert_equal(_col, test_event_data.col); \
zassert_equal(_val, test_event_data.val); \
}
static void test_cb(struct input_event *evt)
{
static int row, col, val;
switch (evt->code) {
case INPUT_ABS_X:
col = evt->value;
break;
case INPUT_ABS_Y:
row = evt->value;
break;
case INPUT_BTN_TOUCH:
val = evt->value;
break;
}
if (evt->sync) {
test_event_data.row = row;
test_event_data.col = col;
test_event_data.val = val;
test_event_data.event_count++;
TC_PRINT("input event: count=%d row=%d col=%d val=%d\n",
test_event_data.event_count, row, col, val);
}
}
INPUT_CALLBACK_DEFINE(NULL, test_cb);
/* actual tests */
ZTEST(gpio_kbd_scan, test_gpio_kbd_scan_interrupt)
{
const struct device *dev = dev_interrupt;
if (dev == NULL) {
ztest_test_skip();
return;
}
const struct input_kbd_matrix_common_config *cfg = dev->config;
uint8_t *rows = test_rows[KBD_DEV_INTERRUPT];
int *set_count = &scan_set_count[KBD_DEV_INTERRUPT];
int prev_count;
gpio_flags_t flags;
k_sleep(K_SECONDS(1));
assert_no_new_events();
zassert_equal(*set_count, 1);
/* Verify that interrupts are enabled. */
gpio_emul_flags_get(dev_gpio, INTERRUPT_R0_PIN, &flags);
zassert_equal(flags & GPIO_INT_ENABLE, GPIO_INT_ENABLE);
gpio_emul_flags_get(dev_gpio, INTERRUPT_R1_PIN, &flags);
zassert_equal(flags & GPIO_INT_ENABLE, GPIO_INT_ENABLE);
rows[0] = BIT(0);
gpio_kbd_scan_set_row(dev, 0x01);
k_sleep(K_USEC(cfg->debounce_down_us * 1.5));
assert_new_event(0, 0, 1);
rows[1] = BIT(1);
k_sleep(K_USEC(cfg->debounce_down_us * 1.5));
assert_new_event(1, 1, 1);
rows[0] = 0x00;
k_sleep(K_USEC(cfg->debounce_up_us * 1.5));
assert_new_event(0, 0, 0);
rows[1] = 0x00;
k_sleep(K_USEC(cfg->debounce_up_us * 1.5));
assert_new_event(1, 1, 0);
k_sleep(K_MSEC(cfg->poll_timeout_ms * 1.5));
/* Check that scanning is NOT running */
prev_count = *set_count;
k_sleep(K_MSEC(cfg->poll_timeout_ms * 10));
assert_no_new_events();
TC_PRINT("scan_set_count=%d, prev_count=%d\n", *set_count, prev_count);
zassert_equal(*set_count, prev_count);
/* Verify that interrupts are still enabled. */
gpio_emul_flags_get(dev_gpio, INTERRUPT_R0_PIN, &flags);
zassert_equal(flags & GPIO_INT_ENABLE, GPIO_INT_ENABLE);
gpio_emul_flags_get(dev_gpio, INTERRUPT_R1_PIN, &flags);
zassert_equal(flags & GPIO_INT_ENABLE, GPIO_INT_ENABLE);
}
ZTEST(gpio_kbd_scan, test_gpio_kbd_scan_poll)
{
const struct device *dev = dev_poll;
if (dev == NULL) {
ztest_test_skip();
return;
}
const struct input_kbd_matrix_common_config *cfg = dev->config;
uint8_t *rows = test_rows[KBD_DEV_POLL];
int *set_count = &scan_set_count[KBD_DEV_POLL];
int prev_count;
gpio_flags_t flags;
k_sleep(K_SECONDS(1));
assert_no_new_events();
zassert_equal(*set_count, 0);
/* Verify that interrupts are NOT enabled. */
gpio_emul_flags_get(dev_gpio, POLL_R0_PIN, &flags);
zassert_equal(flags & GPIO_INT_MASK, 0);
gpio_emul_flags_get(dev_gpio, POLL_R1_PIN, &flags);
zassert_equal(flags & GPIO_INT_MASK, 0);
rows[0] = BIT(0);
gpio_kbd_scan_set_row(dev, 0x01);
k_sleep(K_USEC(cfg->debounce_down_us * 1.5));
assert_new_event(0, 0, 1);
rows[1] = BIT(1);
k_sleep(K_USEC(cfg->debounce_down_us * 1.5));
assert_new_event(1, 1, 1);
rows[0] = 0x00;
k_sleep(K_USEC(cfg->debounce_up_us * 1.5));
assert_new_event(0, 0, 0);
rows[1] = 0x00;
k_sleep(K_USEC(cfg->debounce_up_us * 1.5));
assert_new_event(1, 1, 0);
k_sleep(K_MSEC(cfg->poll_timeout_ms * 1.5));
/* Check that scanning is NOT running */
prev_count = *set_count;
k_sleep(K_MSEC(cfg->poll_timeout_ms * 10));
assert_no_new_events();
TC_PRINT("scan_set_count=%d, prev_count=%d\n", *set_count, prev_count);
zassert_equal(*set_count, prev_count);
/* Verify that interrupts are still NOT enabled. */
gpio_emul_flags_get(dev_gpio, POLL_R0_PIN, &flags);
zassert_equal(flags & GPIO_INT_MASK, 0);
gpio_emul_flags_get(dev_gpio, POLL_R1_PIN, &flags);
zassert_equal(flags & GPIO_INT_MASK, 0);
}
ZTEST(gpio_kbd_scan, test_gpio_kbd_scan_scan)
{
const struct device *dev = dev_scan;
if (dev == NULL) {
ztest_test_skip();
return;
}
const struct input_kbd_matrix_common_config *cfg = dev->config;
uint8_t *rows = test_rows[KBD_DEV_SCAN];
int *set_count = &scan_set_count[KBD_DEV_SCAN];
int prev_count;
int delta_count;
gpio_flags_t flags;
/* check that scanning is already running */
prev_count = *set_count;
k_sleep(K_SECONDS(1));
assert_no_new_events();
delta_count = *set_count - prev_count;
TC_PRINT("scan_set_count=%d, delta=%d\n", *set_count, delta_count);
zassert_true(delta_count > 100);
/* Verify that interrupts are NOT enabled. */
gpio_emul_flags_get(dev_gpio, SCAN_R0_PIN, &flags);
zassert_equal(flags & GPIO_INT_MASK, 0);
gpio_emul_flags_get(dev_gpio, SCAN_R1_PIN, &flags);
zassert_equal(flags & GPIO_INT_MASK, 0);
rows[0] = BIT(0);
gpio_kbd_scan_set_row(dev, 0x01);
k_sleep(K_USEC(cfg->debounce_down_us * 1.5));
assert_new_event(0, 0, 1);
rows[1] = BIT(1);
k_sleep(K_USEC(cfg->debounce_down_us * 1.5));
assert_new_event(1, 1, 1);
rows[0] = 0x00;
k_sleep(K_USEC(cfg->debounce_up_us * 1.5));
assert_new_event(0, 0, 0);
rows[1] = 0x00;
k_sleep(K_USEC(cfg->debounce_up_us * 1.5));
assert_new_event(1, 1, 0);
k_sleep(K_MSEC(cfg->poll_timeout_ms * 1.5));
/* Check that scanning is still running */
prev_count = *set_count;
k_sleep(K_SECONDS(1));
assert_no_new_events();
delta_count = *set_count - prev_count;
TC_PRINT("scan_set_count=%d, delta=%d\n", *set_count, delta_count);
zassert_true(delta_count > 100);
/* Verify that interrupts are still NOT enabled. */
gpio_emul_flags_get(dev_gpio, SCAN_R0_PIN, &flags);
zassert_equal(flags & GPIO_INT_MASK, 0);
gpio_emul_flags_get(dev_gpio, SCAN_R1_PIN, &flags);
zassert_equal(flags & GPIO_INT_MASK, 0);
}
static void gpio_kbd_scan_before(void *data)
{
last_checked_event_count = 0;
memset(&test_event_data, 0, sizeof(test_event_data));
memset(&scan_set_count, 0, sizeof(scan_set_count));
}
ZTEST_SUITE(gpio_kbd_scan, NULL, NULL, gpio_kbd_scan_before, NULL, NULL);