Linux 按键驱动开发指南
1、按键驱动开发基础
1.1. 按键驱动类型
Linux下的按键驱动主要有两种实现方式:
输入子系统驱动:最常用,通过input子系统上报按键事件
字符设备驱动:较少用,需要自己实现文件操作接口
1.2. 输入子系统框架
推荐使用input子系统实现按键驱动,主要组件:
input_register_device() - 注册输入设备
input_report_key() - 上报按键事件
input_sync() - 同步事件
2、设备树配置
2.1. 基本按键节点配置
/ {
gpio-keys {
compatible = "gpio-keys";
#address-cells = <1>;
#size-cells = <0>;
button@1 {
label = "Power Button";
linux,code = <KEY_POWER>; // 按键编码,定义在include/uapi/linux/input-event-codes.h
gpios = <&gpio0 5 GPIO_ACTIVE_LOW>; // 使用的GPIO,低电平有效
debounce-interval = <20>; // 消抖时间(ms)
};
};
};
2.2. 关键属性说明
- compatible: 必须包含"gpio-keys"
- linux,code: 按键键值,如KEY_POWER、KEY_VOLUMEUP等
- gpios: 指定GPIO控制器、引脚和有效电平
- debounce-interval: 硬件消抖时间
3、驱动代码实现
3.1. 基本驱动框架
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
struct gpio_key_data {
struct gpio_desc *gpiod;
int code;
int irq;
};
struct gpio_key_drvdata {
struct input_dev *input;
struct gpio_key_data *data;
int n_buttons;
};
static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{
struct gpio_key_data *key_data = dev_id;
struct gpio_key_drvdata *ddata = container_of(key_data, struct gpio_key_drvdata, data);
int state = gpiod_get_value(key_data->gpiod);
input_report_key(ddata->input, key_data->code, !state);
input_sync(ddata->input);
return IRQ_HANDLED;
}
static int gpio_key_probe(struct platform_device *pdev)
{
// 驱动初始化代码
// 1. 分配input设备
// 2. 解析设备树获取按键信息
// 3. 申请GPIO和中断
// 4. 注册input设备
// ...
}
static const struct of_device_id gpio_key_of_match[] = {
{ .compatible = "gpio-keys", },
{ },
};
MODULE_DEVICE_TABLE(of, gpio_key_of_match);
static struct platform_driver gpio_key_driver = {
.probe = gpio_key_probe,
.driver = {
.name = "gpio-keys",
.of_match_table = gpio_key_of_match,
},
};
module_platform_driver(gpio_key_driver);
4、应用层使用按键驱动
4.1. 通过input子系统访问
按键驱动注册后会在/dev/input/下生成设备节点,如event0
4.2. 读取按键事件示例代码
#include <stdio.h>
#include <linux/input.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd = open("/dev/input/event0", O_RDONLY);
struct input_event ev;
while(1) {
read(fd, &ev, sizeof(ev));
if(ev.type == EV_KEY && ev.value >= 0 && ev.value <= 2) {
printf("Key %d %s\n", ev.code,
ev.value ? "pressed" : "released");
}
}
close(fd);
return 0;
}
4.3. 使用工具测试
- evtest: 通用输入设备测试工具
- cat /proc/bus/input/devices: 查看已注册的输入设备
5、调试与问题排查
5.1 检查设备树是否正确加载:
ls /proc/device-tree/gpio-keys/
5.2 查看GPIO状态:
cat /sys/kernel/debug/gpio
5.3 检查中断是否注册:
cat /proc/interrupts
5.4 查看input设备信息:
cat /proc/bus/input/devices