fa6eca312f
Change the input callback function name to make it easier to identify what it belongs to. This clears some ambiguity when observing the symbol names corresponding to the function pointers in the callback section. Signed-off-by: Fabio Baltieri <fabiobaltieri@google.com>
290 lines
6.8 KiB
C
290 lines
6.8 KiB
C
/*
|
|
* Copyright 2023 Google LLC
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <zephyr/input/input.h>
|
|
#ifdef CONFIG_INPUT_SHELL_KBD_MATRIX_STATE
|
|
#include <zephyr/input/input_kbd_matrix.h>
|
|
#endif
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/shell/shell.h>
|
|
#include <zephyr/sys/atomic.h>
|
|
|
|
LOG_MODULE_DECLARE(input);
|
|
|
|
#ifdef CONFIG_INPUT_EVENT_DUMP
|
|
#ifdef CONFIG_INPUT_SHELL
|
|
static atomic_t dump_enable;
|
|
|
|
static bool input_dump_enabled(void)
|
|
{
|
|
return atomic_get(&dump_enable);
|
|
}
|
|
|
|
static int input_cmd_dump(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
bool enabled;
|
|
int err = 0;
|
|
|
|
enabled = shell_strtobool(argv[1], 0, &err);
|
|
if (err) {
|
|
shell_error(sh, "Invalid argument: %s", argv[1]);
|
|
return err;
|
|
}
|
|
|
|
if (enabled) {
|
|
shell_info(sh, "Input event dumping enabled");
|
|
atomic_set(&dump_enable, 1);
|
|
} else {
|
|
atomic_set(&dump_enable, 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static bool input_dump_enabled(void)
|
|
{
|
|
return true;
|
|
}
|
|
#endif /* CONFIG_INPUT_SHELL */
|
|
|
|
static void input_dump_cb(struct input_event *evt)
|
|
{
|
|
if (!input_dump_enabled()) {
|
|
return;
|
|
}
|
|
|
|
LOG_INF("input event: dev=%-16s %3s type=%2x code=%3d value=%d",
|
|
evt->dev ? evt->dev->name : "NULL",
|
|
evt->sync ? "SYN" : "",
|
|
evt->type,
|
|
evt->code,
|
|
evt->value);
|
|
}
|
|
INPUT_CALLBACK_DEFINE(NULL, input_dump_cb);
|
|
#endif /* CONFIG_INPUT_EVENT_DUMP */
|
|
|
|
#ifdef CONFIG_INPUT_SHELL
|
|
static int input_cmd_report(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
bool sync;
|
|
int err = 0;
|
|
uint32_t type, code, value;
|
|
|
|
if (argc == 5) {
|
|
sync = shell_strtobool(argv[4], 0, &err);
|
|
if (err) {
|
|
shell_error(sh, "Invalid argument: %s", argv[4]);
|
|
return err;
|
|
}
|
|
} else {
|
|
sync = true;
|
|
}
|
|
|
|
type = shell_strtoul(argv[1], 0, &err);
|
|
if (err) {
|
|
shell_error(sh, "Invalid argument: %s", argv[1]);
|
|
return err;
|
|
}
|
|
if (type > UINT8_MAX) {
|
|
shell_error(sh, "Out of range: %s", argv[1]);
|
|
return -EINVAL;
|
|
}
|
|
|
|
code = shell_strtoul(argv[2], 0, &err);
|
|
if (err) {
|
|
shell_error(sh, "Invalid argument: %s", argv[2]);
|
|
return err;
|
|
}
|
|
if (code > UINT16_MAX) {
|
|
shell_error(sh, "Out of range: %s", argv[2]);
|
|
return -EINVAL;
|
|
}
|
|
|
|
value = shell_strtoul(argv[3], 0, &err);
|
|
if (err) {
|
|
shell_error(sh, "Invalid argument: %s", argv[3]);
|
|
return err;
|
|
}
|
|
|
|
input_report(NULL, type, code, value, sync, K_FOREVER);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_INPUT_SHELL_KBD_MATRIX_STATE
|
|
static const struct device *kbd_matrix_state_dev;
|
|
static kbd_row_t kbd_matrix_state[CONFIG_INPUT_SHELL_KBD_MATRIX_STATE_MAX_COLS];
|
|
static kbd_row_t kbd_matrix_key_mask[CONFIG_INPUT_SHELL_KBD_MATRIX_STATE_MAX_COLS];
|
|
|
|
/* Keep space for each column value, 2 char per byte + space. */
|
|
#define KEY_MATRIX_ENTRY_LEN (sizeof(kbd_row_t) * 2 + 1)
|
|
#define KEY_MATRIX_BUF_SZ (CONFIG_INPUT_SHELL_KBD_MATRIX_STATE_MAX_COLS * \
|
|
KEY_MATRIX_ENTRY_LEN)
|
|
static char kbd_matrix_buf[KEY_MATRIX_BUF_SZ];
|
|
|
|
static void kbd_matrix_state_log_entry(char *header, kbd_row_t *data)
|
|
{
|
|
const struct input_kbd_matrix_common_config *cfg = kbd_matrix_state_dev->config;
|
|
char *buf = kbd_matrix_buf;
|
|
int size = sizeof(kbd_matrix_buf);
|
|
int ret;
|
|
char blank[KEY_MATRIX_ENTRY_LEN];
|
|
int count = 0;
|
|
|
|
memset(blank, '-', sizeof(blank) - 1);
|
|
blank[sizeof(blank) - 1] = '\0';
|
|
|
|
for (int i = 0; i < cfg->col_size; i++) {
|
|
char *sep = (i + 1) < cfg->col_size ? " " : "";
|
|
|
|
if (data[i] != 0) {
|
|
ret = snprintf(buf, size, "%" PRIkbdrow "%s", data[i], sep);
|
|
} else {
|
|
ret = snprintf(buf, size, "%s%s", blank, sep);
|
|
}
|
|
size -= ret;
|
|
buf += ret;
|
|
|
|
count += POPCOUNT(data[i]);
|
|
|
|
/* Last byte is for the string termination */
|
|
if (size < 1) {
|
|
LOG_ERR("kbd_matrix_buf too small");
|
|
return;
|
|
}
|
|
}
|
|
|
|
LOG_INF("%s %s [%s] (%d)",
|
|
kbd_matrix_state_dev->name, header, kbd_matrix_buf, count);
|
|
}
|
|
|
|
static void kbd_matrix_state_log(struct input_event *evt)
|
|
{
|
|
const struct input_kbd_matrix_common_config *cfg;
|
|
static uint32_t row, col;
|
|
static bool val;
|
|
|
|
if (kbd_matrix_state_dev == NULL || kbd_matrix_state_dev != evt->dev) {
|
|
return;
|
|
}
|
|
|
|
cfg = kbd_matrix_state_dev->config;
|
|
|
|
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) {
|
|
return;
|
|
}
|
|
|
|
if (col > (CONFIG_INPUT_SHELL_KBD_MATRIX_STATE_MAX_COLS - 1)) {
|
|
LOG_ERR("column index too large for the state buffer: %d", col);
|
|
return;
|
|
}
|
|
|
|
if (col > (cfg->col_size - 1)) {
|
|
LOG_ERR("invalid column index: %d", col);
|
|
return;
|
|
}
|
|
|
|
if (row > (cfg->row_size - 1)) {
|
|
LOG_ERR("invalid row index: %d", row);
|
|
return;
|
|
}
|
|
|
|
WRITE_BIT(kbd_matrix_state[col], row, val);
|
|
if (val != 0) {
|
|
WRITE_BIT(kbd_matrix_key_mask[col], row, 1);
|
|
}
|
|
|
|
kbd_matrix_state_log_entry("state", kbd_matrix_state);
|
|
}
|
|
INPUT_CALLBACK_DEFINE(NULL, kbd_matrix_state_log);
|
|
|
|
static int input_cmd_kbd_matrix_state_dump(const struct shell *sh,
|
|
size_t argc, char *argv[])
|
|
{
|
|
const struct device *dev;
|
|
|
|
if (!strcmp(argv[1], "off")) {
|
|
if (kbd_matrix_state_dev != NULL) {
|
|
kbd_matrix_state_log_entry("key-mask",
|
|
kbd_matrix_key_mask);
|
|
}
|
|
|
|
kbd_matrix_state_dev = NULL;
|
|
shell_info(sh, "Keyboard state logging disabled");
|
|
return 0;
|
|
}
|
|
|
|
dev = device_get_binding(argv[1]);
|
|
if (dev == NULL) {
|
|
shell_error(sh, "Invalid device: %s", argv[1]);
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (kbd_matrix_state_dev != NULL && kbd_matrix_state_dev != dev) {
|
|
shell_error(sh, "Already logging for %s, disable logging first",
|
|
kbd_matrix_state_dev->name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
memset(kbd_matrix_state, 0, sizeof(kbd_matrix_state));
|
|
memset(kbd_matrix_key_mask, 0, sizeof(kbd_matrix_state));
|
|
kbd_matrix_state_dev = dev;
|
|
|
|
shell_info(sh, "Keyboard state logging enabled for %s", dev->name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void device_name_get(size_t idx, struct shell_static_entry *entry)
|
|
{
|
|
const struct device *dev = shell_device_lookup(idx, NULL);
|
|
|
|
entry->syntax = (dev != NULL) ? dev->name : NULL;
|
|
entry->handler = NULL;
|
|
entry->help = NULL;
|
|
entry->subcmd = NULL;
|
|
}
|
|
|
|
SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
|
|
#endif /* CONFIG_INPUT_SHELL_KBD_MATRIX_STATE */
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(
|
|
sub_input_cmds,
|
|
#ifdef CONFIG_INPUT_EVENT_DUMP
|
|
SHELL_CMD_ARG(dump, NULL,
|
|
"Enable event dumping\n"
|
|
"usage: dump <on|off>",
|
|
input_cmd_dump, 2, 0),
|
|
#endif /* CONFIG_INPUT_EVENT_DUMP */
|
|
#ifdef CONFIG_INPUT_SHELL_KBD_MATRIX_STATE
|
|
SHELL_CMD_ARG(kbd_matrix_state_dump, &dsub_device_name,
|
|
"Print the state of a keyboard matrix device each time a "
|
|
"key is pressed or released\n"
|
|
"usage: kbd_matrix_state_dump <device>|off",
|
|
input_cmd_kbd_matrix_state_dump, 2, 0),
|
|
#endif /* CONFIG_INPUT_SHELL_KBD_MATRIX_STATE */
|
|
SHELL_CMD_ARG(report, NULL,
|
|
"Trigger an input report event\n"
|
|
"usage: report <type> <code> <value> [<sync>]",
|
|
input_cmd_report, 4, 1),
|
|
SHELL_SUBCMD_SET_END);
|
|
|
|
SHELL_CMD_REGISTER(input, &sub_input_cmds, "Input commands", NULL);
|
|
#endif /* CONFIG_INPUT_SHELL */
|