设备树(按键节点)
arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts
/* 节点 */r_gpio_keys { /* 节点名称 */compatible = "gpio-keys"; /* 节点属性 *//* 子节点 */sw4 { /* 子节点名称 *//* 子节点的属性 */label = "sw4";linux,code = <KEY_RESTART>;gpios = <&r_pio 0 3 GPIO_ACTIVE_LOW>;};};
驱动程序(按键驱动)
drivers/input/keyboard/gpio_keys.c
static const struct of_device_id gpio_keys_of_match[] = {{ .compatible = "gpio-keys", },{ },};static struct platform_driver gpio_keys_device_driver = {.probe = gpio_keys_probe,.shutdown = gpio_keys_shutdown,.driver = {.name = "gpio-keys",.pm = &gpio_keys_pm_ops,.of_match_table = gpio_keys_of_match,.dev_groups = gpio_keys_groups,}};
设备树节点与驱动程序的匹配
设备树和驱动既独立又联系。独立体现在他俩可以各自单独编译,独立存在;联系体现在他俩在运行时相互配合。那他俩是怎样配合的呢?
首先,内核需要知道 dtb 文件的位置,这个由 uboot 来告诉内核,只要内核知道了 dtb 文件的位置,驱动就可以通过一些 API 获取设备树的内部信息。
接下来就是匹配,驱动程序通过设备树节点中的 compatible(兼容性)来与设备节点进行配对:
-
设备树
r_gpio_keys
节点的
compatible
属性值为 “gpio-keys”,
-
驱动文件 gpio_keys.c 中 gpio_keys_device_driver.driver.of_match_table 中的 compatible 值也为 “gpio-keys”,
他俩就配对上了,👏👏👏。
配对发生在
late_initcall(gpio_keys_init)
后,
配对上后,对应的 probe 函数就会被触发。
probe
drivers/input/keyboard/gpio_keys.c
static int gpio_keys_probe(struct platform_device *pdev){struct device *dev = &pdev->dev;const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);struct fwnode_handle *child = NULL;struct gpio_keys_drvdata *ddata;struct input_dev *input;int i, error;int wakeup = 0;...input->name = pdata->name ? : pdev->name;input->keycode = ddata->keymap;...error = input_register_device(input); // input: r_gpio_keys as ...device_init_wakeup(dev, wakeup);return 0;}
在 probe 函数中会赋能具体的属性和操作接口,然后调用
input_register_device()
注册一个输入设备:
- 将
r_gpio_keys
设备注册为
/devices/platform/r_gpio_keys/input/input1
可以通过
cat /proc/bus/input/devices
查看
# cat /proc/bus/input/devices...I: Bus=0019 Vendor=0001 Product=0001 Version=0100N: Name="r_gpio_keys"P: Phys=gpio-keys/input0S: Sysfs=/devices/platform/r_gpio_keys/input/input1U: Uniq=H: Handlers=event1 evbugB: PROP=0B: EV=3B: KEY=1000000 0 0 0 0 0 0 0 0 0 0 0 0
Name=“r_gpio_keys”
Sysfs=/devices/platform/r_gpio_keys/input/input1
符合预期
应用层读取按键状态
同时看到 r_gpio_keys 对应 event1 设备。于是,我们就可以很方便地在应用层读取 /dev/input/event1 文件来获取按键事件了
# hexdump /dev/input/event10000000 1dd8 0000 5f2b 000a 0001 0198 0001 00000000010 1dd8 0000 5f2b 000a 0000 0000 0000 00000000020 1dd8 0000 934e 000d 0001 0198 0000 00000000030 1dd8 0000 934e 000d 0000 0000 0000 0000
简单介绍下上述数值含义:
以第一行为例,
- 1dd8 0000 表示按键时间(秒);
- 5f2b 000a 表示按键时间(微秒);
- 0001 表示事件类型是键盘;
- 0198 为 code,键盘上的一个按键对应一个 code,0x198 为 KEY_RESTART,表示重启键;
- 0001 0000 为事件值,如果事件类型为 EV_KEY,按键按下时值为 1,松开时值为 0。
综上,第一行表示在 7640.679723 秒时重启按键被按下了。
附录(事件类型):
/** Event types*/#define EV_SYN 0x00 // 用于事件间的分割标志。事件可能按时间或空间进行分割,就像在多点触摸协议中的例子。#define EV_KEY 0x01 // 键盘#define EV_REL 0x02#define EV_ABS 0x03#define EV_MSC 0x04#define EV_SW 0x05#define EV_LED 0x11#define EV_SND 0x12#define EV_REP 0x14#define EV_FF 0x15#define EV_PWR 0x16#define EV_FF_STATUS 0x17#define EV_MAX 0x1f#define EV_CNT (EV_MAX+1)