使用 VSCode 编写轮询按键驱动

工程的展示和驱动的编写

Posted by Jerry Chen on July 14, 2020

先导内容

mknod 命令

用途:创建设备节点或命名管道(FIFO)

使用方法:mknod Name { b | c | p } Major Minor

b 创建块设备,c 创建字符设备,p 创建 FIFO(创建 FIFO 不需要主次设备号);

示例:

1
mknod /dev/misc_demo c 10 255

创建后查看:

第一个 c 表示这是一个字符设备;

1
2
ls /dev/misc_demo -l
crw-r--r-- 1 root root 10, 255 Feb 12 00:00 /dev/misc_demo

解决 printk 无打印

  1. 日志级别不够,提高 printk 时日志等级,或调整显示等级;

    如果打印不及时,往往是 printk 没有 ‘\n’;

  2. ssh 方式无法获取 printk 的输出,用另一个 ssh 持续打印:

    1
    2
    3
    4
    5
    
    while true
    do
        sudo dmesg -c
        sleep 1
    done
    

全志 pinctrl

应用层查找设备树节点属性的函数实现: linux-3.10/drivers/pinctrl/sunxi/pinctrl-sunxi.c(h)

芯片引脚复用功能的描述:linux-3.10/drivers/pinctrl/sunxi/pinctrl-sun8iw11p1.c

设备描述 fex 文件在:tools/pack/chips/sun8iw11p1/configs/scm701-p1/sys_config.fex

分区描述 fex 文件在:

tools/pack/chips/sun8iw11p1/configs/default/sys_partition_linux.fex

最终生成的完整设备树文件在:out/sun8iw11p1/linux/common/.sunxi.dts

全志设备树 gpio 应用开发

设备树中关于 GPIO 的描述:

1
2
3
4
5
6
7
8
9
mypio = <&pio   1   1   1   1   1  0>;
    |      |    |   |   |   |   |  |-------------------表示有效电平
    |      |    |   |   |   |   |----------------------上下拉, 0关闭功能, 1上拉, 2下拉, 3保留
    |      |    |   |   |   |-------------------------驱动力,电流等级(0 - 3),级别越高,输出电流越大
    |      |    |   |   |----------------------------gpio功能类型,0输入, 1输出, 6和外部中断,7关闭功能(具体查手册)
    |      |    |   |------------------------------pin bank 内偏移(即组内第几个io口).
    |      |    |---------------------------------哪组gpio, PA(0),PB(1),PC(2),PD(3),PE(4),PF(5),PG(6),PH(7),PI(8),PJ(9),PK(10),PL(11)
    |      |--------------------------------------指向哪个gpio控制器,  pio / r_pio(PL组)
    |-----------------------------------------------------属性名字(随便命名)

应用层的获取属性的函数 of_get_named_gpio_flags:

1
2
3
4
#include <linux/of_gpio.h>

int of_get_named_gpio_flags(struct device_node *np, const char *propname,
    int index, enum of_gpio_flags *flags);
  • 返回值为 int 类型的 gpio 口;
  • np 为设备或设备子节点对象;
  • propname 为指定的属性名字,如 mypio;
  • index 表示获取属性里的第几个值;
  • flags 的参数应为 struct gpio_config 类型;

其中 struct gpio_config 类型如下:

1
2
3
4
5
6
7
8
#include <linux/sys_config.h>
struct gpio_config {
     u32 gpio;       /* gpio global index, must be unique */
     u32 mul_sel;    /* multi sel val: 0 - input, 1 - output... */
     u32 pull;       /* pull val: 0 - pull up/down disable, 1 - pull up... */
     u32 drv_level;  /* driver level val: 0 - level 0, 1 - level 1... */
     u32 data;       /* data val: 0 - low, 1 - high */
 };

接着就可以使用 GPIO 通用操作:

1
2
3
4
5
6
7
8
9
10
11
12
#include <linux/gpio.h> //里面声明io口的操作函数

int gpio_request(unsigned gpio, const char *label);//每个io只能被请求一次,可防止多个驱动来控制同一个IO口
void gpio_free(unsigned gpio); //释放已请求的io口

int gpio_direction_input(unsigned gpio); //把指定的IO口作输入功能, gpio用于指定具体哪个io口
int gpio_direction_output(unsigned gpio, int value); //作输出功能,并根据value的值输出高低电平

int gpio_get_value(unsigned gpio); //获取指定IO口的电平
void gpio_set_value(unsigned gpio, int value); //设置IO口的电平为value(0/1)

int gpio_to_irq(unsigned gpio);  //根据io口,获取到它对应的中断号(io口大都有外部中断功能)

终端文件系统中 dd 命令烧写

我在全志终端上 dd 烧写测试无效,大家可自行尝试;

uboot 下 printenv 指令,可以看到如下环境变量(boot 放在 mmcblk0p6):

1
boot-res@mmcblk0p2:env@mmcblk0p5:boot@mmcblk0p6:rootfs@mmcblk0p7:klog@mmcblk0p8:data@mmcblk0p9:secret@mmcblk0p10:reserve@mmcblk0p11:reserve2@mmcblk0p12:UDISK@mmcblk0p1

我们需要烧写的文件:out/sun8iw11p1/linux/common/boot.img,其大小为 14606336 bytes,count 为 28528;

进入终端文件系统后使用 dd 命令:

1
2
dd if=boot.img of=/dev/block/mmcblk6 bs=512 count=28528 
sync

VSCode 工程模板

文件清单

.vscode 目录

c_cpp_properties.json

demo 目录下是我的模板驱动,故加入了头路径;

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
39
40
41
42
43
{
    "env": {
        "myLinuxKernelPath": "/share/active/scm701_sdk/linux-3.10",
        "myDefaultIncludePath": [
            "${workspaceFolder}/demo/**",
            "${myLinuxKernelPath}/include",
            "${myLinuxKernelPath}/arch/alpha/include",
            "${myLinuxKernelPath}/arch/alpha/include/uapi",
            "${myLinuxKernelPath}/arch/arm/include"
        ],
        "myDefines": [
            "__KERNEL__"
        ],
        "myCompilerPath": "/opt/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc"
    },
    "configurations": [
        {
            "name": "Linux-Arm",
            "intelliSenseMode": "gcc-arm",
            "includePath": [
                "${myDefaultIncludePath}"
            ],
            "defines": [
                "${myDefines}"
            ],
            "compilerPath": "${myCompilerPath}",
            "cStandard": "c99",
            "cppStandard": "c++11",
            "browse": {
                "path": [
                    "${myLinuxKernelPath}/include",
                    "${myLinuxKernelPath}/arch/alpha/include",
                    "${myLinuxKernelPath}/arch/alpha/include/uapi",
                    "${myLinuxKernelPath}/arch/arm/include",
                    "${workspaceFolder}/demo"
                ],
                "limitSymbolsToIncludedHeaders": true,
                "databaseFilename": ""
            }
        }
    ],
    "version": 4
}
settings.json
1
2
3
4
5
6
7
8
9
10
11
12
{
    "editor.formatOnType": true,                // 输入分号后触发格式化编辑的段
    "editor.snippetSuggestions": "top",         // 代码段(如输入for tab)优先补全
    "C_Cpp.clang_format_sortIncludes": true,    // 右键格式化文档时调整include的顺序
    "files.associations": {
        "cstring": "cpp",
        "iostream": "cpp",
        "mutex": "cpp",
        "thread": "cpp",
        "sysinfo.h": "c"
    },
}
tasks.json
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "make",
            "type": "shell",
            "command": "cd ${fileDirname};make",
            "problemMatcher": [],
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },
        {
            "label": "make clean",
            "type": "shell",
            "command": "cd ${fileDirname};make clean",
        },
        {
            "label": "trans",
            "type": "shell",
            "command": "${workspaceFolder}/.x86_tools/file_send_client",
            "args": [
                "192.168.1.101",
                "10069",
                "${fileDirname}/${fileBasenameNoExtension}.ko",
                "/usr/local/extapps/drivers",
                "${fileBasenameNoExtension}.ko",
                "0644"
            ],
        },
        {
            "label": "trans activity file",
            "type": "shell",
            "command": "${workspaceFolder}/.x86_tools/file_send_client",
            "args": [
                "192.168.1.101",
                "10069",
                "${file}",
                "/usr/local/extapps/drivers",
                "${fileBasename}",
                "0644"
            ],
        },
        {
            "label": "run activity sh script",
            "type": "shell",
            "command": "chmod +x ${fileDirname}/${fileBasenameNoExtension}.sh;${fileDirname}/${fileBasenameNoExtension}.sh",
        }
    ]
}

.x86_tools 目录

file_send_client 是我的传文件二进制,和任务配合使用;

demo 目录

这是我的 demo 驱动编写目录;

hello_module.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

static int __init demo_init(void){
    printk("demo module init!\n");
    return 0;
}

static void __exit demo_exit(void){
    printk("demo module exit!\n");
}

module_init(demo_init);
module_exit(demo_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("jerry CLOU");
Makefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
### Makefile

# 环境变量
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-

# 内核目录
KDIR?=/share/active/scm701_sdk/linux-3.10

# 模块
obj-m+=hello_module.o

# 跳转执行 KDIR 目录的 Makefile 文件
all:
	make -C $(KDIR) modules M=$(PWD)
clean:
	make -C $(KDIR) clean M=$(PWD)

GPIO 按键轮询驱动

驱动

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/input-polldev.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/gpio_keys.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/sys_config.h>

#define DRV_NAME	"disp-keys-polled"

static struct gpio_keys_button buttons[]={
    {
        .code = KEY_LEFT,
        .desc = "left_key",
        .active_low = 1,
    },
    {
        .code = KEY_DOWN,
        .desc = "down_key",
        .active_low = 1,
    },
    {
        .code = KEY_ENTER,
        .desc = "enter_key",
        .active_low = 1,
    },
    {
        .code = KEY_UP,
        .desc = "up_key",
        .active_low = 1,
    },
    {
        .code = KEY_RIGHT,
        .desc = "right_key",
        .active_low = 1,
    },
    {
        .code = KEY_SPACE, //实际未用
        .desc = "unuse_key",
        .active_low = 1,
    },
};

struct gpio_keys_button_data {
	int last_state;
	int count;
	int threshold;
	int can_sleep;
};

struct gpio_keys_polled_dev {
	struct input_polled_dev *poll_dev;
	struct device *dev;
	const struct gpio_keys_platform_data *pdata;
	struct gpio_keys_button_data data[0];
};

static void gpio_keys_polled_check_state(struct input_dev *input,
					 struct gpio_keys_button *button,
					 struct gpio_keys_button_data *bdata)
{
	int state;

	if (bdata->can_sleep)
		state = !!gpio_get_value_cansleep(button->gpio);
	else
		state = !!gpio_get_value(button->gpio);

	if (state != bdata->last_state) {
		unsigned int type = button->type ?: EV_KEY;

		input_event(input, type, button->code,
			    !!(state ^ button->active_low));
		input_sync(input);
		bdata->count = 0;
		bdata->last_state = state;
	}
}

static void gpio_keys_polled_poll(struct input_polled_dev *dev)
{
	struct gpio_keys_polled_dev *bdev = dev->private;
	const struct gpio_keys_platform_data *pdata = bdev->pdata;
	struct input_dev *input = dev->input;
	int i;

	for (i = 0; i < pdata->nbuttons; i++) {
		struct gpio_keys_button_data *bdata = &bdev->data[i];

		if (bdata->count < bdata->threshold)
			bdata->count++;
		else
			gpio_keys_polled_check_state(input, &pdata->buttons[i],
						     bdata);
	}
}

static void gpio_keys_polled_open(struct input_polled_dev *dev)
{
	struct gpio_keys_polled_dev *bdev = dev->private;
	const struct gpio_keys_platform_data *pdata = bdev->pdata;

	if (pdata->enable)
		pdata->enable(bdev->dev);
}

static void gpio_keys_polled_close(struct input_polled_dev *dev)
{
	struct gpio_keys_polled_dev *bdev = dev->private;
	const struct gpio_keys_platform_data *pdata = bdev->pdata;

	if (pdata->disable)
		pdata->disable(bdev->dev);
}

static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct device *dev)
{
	struct device_node *node, *pp;
	struct gpio_keys_platform_data *pdata;
	struct gpio_keys_button *button;
	int error;
	int nbuttons;
	int i;
    struct gpio_config config;
    char gpio_name[32];

    node = dev->of_node;
	if (!node)
		return NULL;

	//nbuttons = of_get_child_count(node);
    of_property_read_u32(node, "keys_num", &nbuttons);
	if (nbuttons == 0)
		return NULL;

	pdata = kzalloc(sizeof(*pdata) + nbuttons * (sizeof *button),
			GFP_KERNEL);
	if (!pdata) {
		error = -ENOMEM;
		goto err_out;
	}

	pdata->buttons = (struct gpio_keys_button *)(pdata + 1);
	pdata->nbuttons = nbuttons;

	//pdata->rep = !!of_get_property(node, "autorepeat", NULL);
	//of_property_read_u32(node, "poll-interval", &pdata->poll_interval);
    pdata->poll_interval = 50;//50ms

	i = 0;
    #if 0
	for_each_child_of_node(node, pp) {
		int gpio;
		enum of_gpio_flags flags;

		if (!of_find_property(pp, "gpios", NULL)) {
			pdata->nbuttons--;
			dev_warn(dev, "Found button without gpios\n");
			continue;
		}

		gpio = of_get_gpio_flags(pp, 0, &flags);
		if (gpio < 0) {
			error = gpio;
			if (error != -EPROBE_DEFER)
				dev_err(dev,
					"Failed to get gpio flags, error: %d\n",
					error);
			goto err_free_pdata;
		}

		button = &pdata->buttons[i++];

		button->gpio = gpio;
		button->active_low = flags & OF_GPIO_ACTIVE_LOW;

		if (of_property_read_u32(pp, "linux,code", &button->code)) {
			dev_err(dev, "Button without keycode: 0x%x\n",
				button->gpio);
			error = -EINVAL;
			goto err_free_pdata;
		}

		button->desc = of_get_property(pp, "label", NULL);

		if (of_property_read_u32(pp, "linux,input-type", &button->type))
			button->type = EV_KEY;

		button->wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL);

		if (of_property_read_u32(pp, "debounce-interval",
					 &button->debounce_interval))
			button->debounce_interval = 5;
	}
    #else
    for(i=0; i<nbuttons; i++)
	{
		button = &pdata->buttons[i];	
		//button->gpio = buttons[i].gpio;
        sprintf(gpio_name, "keys_pin_%d", i + 1);
        button->gpio = of_get_named_gpio_flags(node, gpio_name, 0, (enum of_gpio_flags *)&config);
		button->active_low = buttons[i].active_low;
		button->type = EV_KEY;
		button->code = buttons[i].code;	
	}
    #endif

	if (pdata->nbuttons == 0) {
		error = -EINVAL;
		goto err_free_pdata;
	}

	return pdata;

err_free_pdata:
	kfree(pdata);
err_out:
	return ERR_PTR(error);
}

static struct of_device_id gpio_keys_polled_of_match[] = {
	{ .compatible = "SMARTCHIP,disp-keys-gpio", },
	{ },
};
MODULE_DEVICE_TABLE(of, gpio_keys_polled_of_match);

static int gpio_keys_polled_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
	struct gpio_keys_polled_dev *bdev;
	struct input_polled_dev *poll_dev;
	struct input_dev *input;
	int error;
	int i;

	//if (!pdata) 
    {
		pdata = gpio_keys_polled_get_devtree_pdata(dev);
		if (IS_ERR(pdata))
			return PTR_ERR(pdata);
		if (!pdata) {
			dev_err(dev, "missing platform data\n");
			return -EINVAL;
		}
	}

	if (!pdata->poll_interval) {
		dev_err(dev, "missing poll_interval value\n");
		error = -EINVAL;
		goto err_free_pdata;
	}

	bdev = kzalloc(sizeof(struct gpio_keys_polled_dev) +
		       pdata->nbuttons * sizeof(struct gpio_keys_button_data),
		       GFP_KERNEL);
	if (!bdev) {
		dev_err(dev, "no memory for private data\n");
		error = -ENOMEM;
		goto err_free_pdata;
	}

	poll_dev = input_allocate_polled_device();
	if (!poll_dev) {
		dev_err(dev, "no memory for polled device\n");
		error = -ENOMEM;
		goto err_free_bdev;
	}

	poll_dev->private = bdev;
	poll_dev->poll = gpio_keys_polled_poll;
	poll_dev->poll_interval = pdata->poll_interval;
	poll_dev->open = gpio_keys_polled_open;
	poll_dev->close = gpio_keys_polled_close;

	input = poll_dev->input;

	input->name = pdev->name;
	input->phys = DRV_NAME"/input0";
	input->dev.parent = &pdev->dev;

	input->id.bustype = BUS_HOST;
	input->id.vendor = 0x0001;
	input->id.product = 0x0001;
	input->id.version = 0x0100;

	__set_bit(EV_KEY, input->evbit);
	if (pdata->rep)
		__set_bit(EV_REP, input->evbit);

	for (i = 0; i < pdata->nbuttons; i++) {
		struct gpio_keys_button *button = &pdata->buttons[i];
		struct gpio_keys_button_data *bdata = &bdev->data[i];
		unsigned int gpio = button->gpio;
		unsigned int type = button->type ?: EV_KEY;

		if (button->wakeup) {
			dev_err(dev, DRV_NAME " does not support wakeup\n");
			error = -EINVAL;
			goto err_free_gpio;
		}

		error = gpio_request_one(gpio, GPIOF_IN,
					 button->desc ?: DRV_NAME);
		if (error) {
			dev_err(dev, "unable to claim gpio %u, err=%d\n",
				gpio, error);
			goto err_free_gpio;
		}

		bdata->can_sleep = gpio_cansleep(gpio);
		bdata->last_state = -1;
		bdata->threshold = DIV_ROUND_UP(button->debounce_interval,
						pdata->poll_interval);

		input_set_capability(input, type, button->code);
	}

	bdev->poll_dev = poll_dev;
	bdev->dev = dev;
	bdev->pdata = pdata;
	platform_set_drvdata(pdev, bdev);

	error = input_register_polled_device(poll_dev);
	if (error) {
		dev_err(dev, "unable to register polled device, err=%d\n",
			error);
		goto err_free_gpio;
	}

	/* report initial state of the buttons */
	for (i = 0; i < pdata->nbuttons; i++)
		gpio_keys_polled_check_state(input, &pdata->buttons[i],
					     &bdev->data[i]);

	return 0;

err_free_gpio:
	while (--i >= 0)
		gpio_free(pdata->buttons[i].gpio);

	input_free_polled_device(poll_dev);

err_free_bdev:
	kfree(bdev);
	platform_set_drvdata(pdev, NULL);

err_free_pdata:
	/* If we have no platform_data, we allocated pdata dynamically.  */
	if (!dev_get_platdata(&pdev->dev))
		kfree(pdata);

	return error;
}

static int gpio_keys_polled_remove(struct platform_device *pdev)
{
	struct gpio_keys_polled_dev *bdev = platform_get_drvdata(pdev);
	const struct gpio_keys_platform_data *pdata = bdev->pdata;
	int i;

	input_unregister_polled_device(bdev->poll_dev);

	for (i = 0; i < pdata->nbuttons; i++)
		gpio_free(pdata->buttons[i].gpio);

	input_free_polled_device(bdev->poll_dev);

	/*
	 * If we had no platform_data, we allocated pdata dynamically and
	 * must free it here.
	 */
	if (!dev_get_platdata(&pdev->dev))
		kfree(pdata);

	kfree(bdev);
	platform_set_drvdata(pdev, NULL);

	return 0;
}

static struct platform_driver gpio_keys_polled_driver = {
	.probe	= gpio_keys_polled_probe,
	.remove	= gpio_keys_polled_remove,
	.driver	= {
		.name	= DRV_NAME,
		.owner	= THIS_MODULE,
		.of_match_table = of_match_ptr(gpio_keys_polled_of_match),
	},
};
module_platform_driver(gpio_keys_polled_driver);

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Polled GPIO Buttons driver");
MODULE_ALIAS("platform:" DRV_NAME);

应用测试

加载驱动后生成了 /dev/input/event4

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
#include <stdio.h>
#include <linux/input.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
#define DEV_PATH "/dev/input/event4"   //difference is possible
 
int main()
{
	int keys_fd;
	char ret[2];
	struct input_event t;
	keys_fd=open(DEV_PATH, O_RDONLY);
	if(keys_fd <= 0)
	{
		printf("open %s device error!\n", DEV_PATH);
		return -1;
	}
	while(1)
	{
		if(read(keys_fd, &t, sizeof(t)) == sizeof(t))
		{
			if(t.type==EV_KEY)
				if(t.value==0 || t.value==1)
				{
					printf("key %d %s\n", t.code, (t.value) ? "Pressed" : "Released");
					if(t.code == KEY_ESC)
						break;
				}
		}
	}
	close(keys_fd);
	return 0;
}

其他说明

实际上你只要知道 GPIO 号,你都可以不需要设备树;如果这样,你需要在 ko 模块中注册设备以及驱动;

上面的驱动参考:https://blog.csdn.net/xb1667/article/details/88244184