/* * Copyright 2023 Google LLC * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #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, void *user_data) { 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, NULL); /* 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);