Zephyr输入子系统-3输入设备和KSCAN兼容

Creative Commons
本作品采用知识共享署名

本文简要介绍输入设备的实现情况和对Kscan的兼容。

输入设备

在输入子系统导入前,Zephyr的输入设备大部分都放在zephyr/drivers/kscan/下以kscan的接口和应用对接,导入输入子系统同时zephyr也开始添加输入设备驱动在zephyr/drivers/input下面,目前支持

  • ft5336
  • gpio-key
  • npcx-kbd
  • sdl-touch
    这些驱动的设备树绑定在zephyr/dts/bindings/input内,将来kscan的驱动将被逐步转换为输入设备驱动。
    输入设备驱动通过输入子系统的input_report发送输入事件,没有定义其它和应用程序的接口。实现输入设备驱动只需要根据其硬件特性设计设备树绑定文件,并撰写驱动程序,将从硬件获得的输入事件用input_report送到输入子系统即可。

Kscan兼容

现阶段,上层应用和模块(例如lvgl)都还使用的时kscan作为输入,为了让输入系统兼容kscan,在kscan驱动中引入了input。当需要一个input设备驱动兼容kscan时,在其设备树内加入kscan_input即可

1
2
3
4
5
6
7
8
9
ft5336@38 {
compatible = "focaltech,ft5336";
reg = <0x38>;
int-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;

kscan_input: kscan-input {
compatible = "zephyr,kscan-input";
};
};

zephyr/drivers/kscan/kscan_input.c中会查找支持kscan_input的输入设备,并将其发送的event转换为kscan的.
先使用KSCAN_INPUT_INIT遍历所有设kscan_input的设备树节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define KSCAN_INPUT_INIT(index) \
static void kscan_input_cb_0(struct input_event *evt) \
{ \
kscan_input_cb(DEVICE_DT_GET(DT_INST(index, DT_DRV_COMPAT)), \
evt); \
} \
INPUT_LISTENER_CB_DEFINE(DEVICE_DT_GET(DT_INST_PARENT(index)), \
kscan_input_cb_0); \
static const struct kscan_input_config kscan_input_config_0 = { \
.input_dev = DEVICE_DT_GET(DT_INST_PARENT(index)), \
}; \
static struct kscan_input_data kscan_input_data_0; \
DEVICE_DT_INST_DEFINE(index, kscan_input_init, NULL, \
&kscan_input_data_0, \
&kscan_input_config_0, \
POST_KERNEL, CONFIG_KSCAN_INIT_PRIORITY, \
&kscan_input_driver_api);

当遇到下面这个设备树

1
2
3
4
5
6
7
8
9
ft5336@38 {
compatible = "focaltech,ft5336";
reg = <0x38>;
int-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;

kscan_input: kscan-input {
compatible = "zephyr,kscan-input";
};
};

就会被展开为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static void kscan_input_cb_0(struct input_event *evt)            \
{ \
kscan_input_cb(DEVICE_DT_GET(DT_INST(index, DT_DRV_COMPAT)), \
evt); \
} \
INPUT_LISTENER_CB_DEFINE(DEVICE_DT_GET(DT_INST_PARENT(index)), \
kscan_input_cb_0); \
static const struct kscan_input_config kscan_input_config_0 = { \
.input_dev = DEVICE_DT_GET(DT_INST_PARENT(index)), \
}; \
static struct kscan_input_data kscan_input_data_0; \
DEVICE_DT_INST_DEFINE(index, kscan_input_init, NULL, \
&kscan_input_data_0, \
&kscan_input_config_0, \
POST_KERNEL, CONFIG_KSCAN_INIT_PRIORITY, \
&kscan_input_driver_api);

也就是kscan向输入子系统注册一个监听器kscan_input_cb_0当触摸屏有事件发生时,会调用kscan_input_cb_0->kscan_input_cb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

static void kscan_input_cb(const struct device *dev, struct input_event *evt)
{
struct kscan_input_data *data = dev->data;

//按照event code转换为kscan的参数
switch (evt->code) {
case INPUT_ABS_X:
data->col = evt->value;
break;
case INPUT_ABS_Y:
data->row = evt->value;
break;
case INPUT_BTN_TOUCH:
data->pressed = evt->value;
break;
}

//通过kscan的callback通知应用
if (evt->sync) {
LOG_DBG("input event: %3d %3d %d",
data->row, data->col, data->pressed);
if (data->callback) {
data->callback(dev, data->row, data->col, data->pressed);
}
}
}

kscan的三个接口函数的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
static int kscan_input_configure(const struct device *dev,
kscan_callback_t callback)
{
struct kscan_input_data *data = dev->data;

if (!callback) {
LOG_ERR("Invalid callback (NULL)");
return -EINVAL;
}

data->callback = callback;

return 0;
}

static int kscan_input_enable_callback(const struct device *dev)
{
struct kscan_input_data *data = dev->data;

data->enabled = true;

return 0;
}

static int kscan_input_disable_callback(const struct device *dev)
{
struct kscan_input_data *data = dev->data;

data->enabled = false;

return 0;
}

static const struct kscan_driver_api kscan_input_driver_api = {
.config = kscan_input_configure,
.enable_callback = kscan_input_enable_callback,
.disable_callback = kscan_input_disable_callback,
};

kscan-input只是目前输入子系统导入的一个兼容kscan的过渡,当kscan的驱动全部被迁移为输入设备驱动后,kscan将会被废弃

参考

https://docs.zephyrproject.org/3.4.0/services/input/index.html