一、顶级架构一句话总结
input_dev(设备) → Input核心层 → input_handler(事件处理) → 用户空间(/dev/input/eventX)
输入子系统统一管理键盘、鼠标、触摸屏、游戏手柄等输入设备。
二、输入子系统架构
架构层次
┌─────────────────────────────────────────────────────────┐
│ 输入子系统架构 │
├─────────────────────────────────────────────────────────┤
│ │
│ 用户空间 /dev/input/eventX, evtest, libinput │
│ ↓ │
│ 事件处理 input_handler (evdev, keyboard, mousedev) │
│ ↓ │
│ Input核心 input.c (统一抽象层) │
│ ↓ │
│ 设备驱动 input_dev (按键、触摸屏、鼠标驱动) │
│ ↓ │
│ 硬件层 GPIO、I2C、SPI设备 │
│ │
└─────────────────────────────────────────────────────────┘
核心数据结构
// 输入设备
struct input_dev {
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; // 支持的事件类型
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; // 支持的按键
unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; // 支持的相对坐标
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; // 支持的绝对坐标
unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
struct ff_device *ff; // 力反馈
unsigned int repeat_key;
struct timer_list timer;
int sync;
struct device dev;
struct list_head h_list;
struct list_head node;
};
// 输入事件
struct input_event {
struct timeval time;
__u16 type; // 事件类型
__u16 code; // 事件代码
__s32 value; // 事件值
};
事件类型
#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 // LED事件
#define EV_SND 0x12 // 声音事件
#define EV_REP 0x14 // 重复事件
#define EV_FF 0x15 // 力反馈
#define EV_PWR 0x16 // 电源事件
#define EV_FF_STATUS 0x17 // 力反馈状态
三、输入设备驱动开发
API函数
#include <linux/input.h>
// 分配输入设备
struct input_dev *input_allocate_device(void);
struct input_dev *devm_input_allocate_device(struct device *dev);
// 释放输入设备
void input_free_device(struct input_dev *dev);
// 注册/注销
int input_register_device(struct input_dev *dev);
void input_unregister_device(struct input_dev *dev);
// 设置能力
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code);
void input_set_abs_params(struct input_dev *dev, unsigned int axis, int min, int max, int fuzz, int flat);
// 上报事件
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
void input_report_key(struct input_dev *dev, unsigned int code, int value);
void input_report_rel(struct input_dev *dev, unsigned int code, int value);
void input_report_abs(struct input_dev *dev, unsigned int code, int value);
void input_sync(struct input_dev *dev);
按键驱动示例
#include <linux/module.h>
#include <linux/input.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
struct button_data {
struct input_dev *input;
struct gpio_desc *gpio;
int irq;
};
static irqreturn_t button_irq_handler(int irq, void *dev_id)
{
struct button_data *data = dev_id;
int state;
// 读取GPIO状态
state = gpiod_get_value(data->gpio);
// 上报按键事件
input_report_key(data->input, KEY_ENTER, !state);
input_sync(data->input);
return IRQ_HANDLED;
}
static int button_probe(struct platform_device *pdev)
{
struct button_data *data;
struct device *dev = &pdev->dev;
int ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
// 获取GPIO
data->gpio = devm_gpiod_get(dev, "button", GPIOD_IN);
if (IS_ERR(data->gpio))
return PTR_ERR(data->gpio);
// 获取中断号
data->irq = gpiod_to_irq(data->gpio);
if (data->irq < 0)
return data->irq;
// 分配输入设备
data->input = devm_input_allocate_device(dev);
if (!data->input)
return -ENOMEM;
// 设置设备名称
data->input->name = "my-button";
data->input->id.bustype = BUS_HOST;
// 设置支持的事件
set_bit(EV_KEY, data->input->evbit);
set_bit(KEY_ENTER, data->input->keybit);
// 或使用input_set_capability
input_set_capability(data->input, EV_KEY, KEY_ENTER);
// 注册输入设备
ret = input_register_device(data->input);
if (ret)
return ret;
// 注册中断
ret = devm_request_irq(dev, data->irq, button_irq_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
"button_irq", data);
if (ret)
return ret;
platform_set_drvdata(pdev, data);
dev_info(dev, "Button input device registered\n");
return 0;
}
static const struct of_device_id button_of_match[] = {
{ .compatible = "mycompany,button", },
{ }
};
MODULE_DEVICE_TABLE(of, button_of_match);
static struct platform_driver button_driver = {
.probe = button_probe,
.driver = {
.name = "button_input",
.of_match_table = button_of_match,
},
};
module_platform_driver(button_driver);
MODULE_LICENSE("GPL");
四、触摸屏驱动示例
#include <linux/module.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/gpio/consumer.h>
struct touchscreen_data {
struct i2c_client *client;
struct input_dev *input;
struct gpio_desc *irq_gpio;
int max_x;
int max_y;
};
static irqreturn_t touchscreen_irq_handler(int irq, void *dev_id)
{
struct touchscreen_data *data = dev_id;
u8 buf[8];
int x, y, pressed;
// 读取触摸数据(假设I2C协议)
i2c_smbus_read_i2c_block_data(data->client, 0x00, 8, buf);
x = (buf[0] << 8) | buf[1];
y = (buf[2] << 8) | buf[3];
pressed = buf[4] & 0x01;
// 上报触摸事件
if (pressed) {
input_report_key(data->input, BTN_TOUCH, 1);
input_report_abs(data->input, ABS_X, x);
input_report_abs(data->input, ABS_Y, y);
input_report_abs(data->input, ABS_PRESSURE, 255);
} else {
input_report_key(data->input, BTN_TOUCH, 0);
input_report_abs(data->input, ABS_PRESSURE, 0);
}
input_sync(data->input);
return IRQ_HANDLED;
}
static int touchscreen_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct touchscreen_data *data;
struct device *dev = &client->dev;
int ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->client = client;
// 从设备树获取分辨率
of_property_read_u32(dev->of_node, "touchscreen-size-x", &data->max_x);
of_property_read_u32(dev->of_node, "touchscreen-size-y", &data->max_y);
// 分配输入设备
data->input = devm_input_allocate_device(dev);
if (!data->input)
return -ENOMEM;
data->input->name = "my-touchscreen";
data->input->id.bustype = BUS_I2C;
// 设置支持的事件类型
set_bit(EV_KEY, data->input->evbit);
set_bit(EV_ABS, data->input->evbit);
// 设置触摸按键
input_set_capability(data->input, EV_KEY, BTN_TOUCH);
// 设置绝对坐标参数
input_set_abs_params(data->input, ABS_X, 0, data->max_x, 0, 0);
input_set_abs_params(data->input, ABS_Y, 0, data->max_y, 0, 0);
input_set_abs_params(data->input, ABS_PRESSURE, 0, 255, 0, 0);
// 设置多点触摸(可选)
set_bit(INPUT_PROP_DIRECT, data->input->propbit);
// 注册输入设备
ret = input_register_device(data->input);
if (ret)
return ret;
// 注册中断
ret = devm_request_threaded_irq(dev, client->irq, NULL,
touchscreen_irq_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"touchscreen_irq", data);
if (ret)
return ret;
i2c_set_clientdata(client, data);
dev_info(dev, "Touchscreen registered\n");
return 0;
}
static const struct of_device_id touchscreen_of_match[] = {
{ .compatible = "mycompany,touchscreen", },
{ }
};
MODULE_DEVICE_TABLE(of, touchscreen_of_match);
static struct i2c_driver touchscreen_driver = {
.probe = touchscreen_probe,
.driver = {
.name = "touchscreen",
.of_match_table = touchscreen_of_match,
},
};
module_i2c_driver(touchscreen_driver);
MODULE_LICENSE("GPL");
五、鼠标驱动示例
static int mouse_probe(struct platform_device *pdev)
{
struct input_dev *input;
input = devm_input_allocate_device(&pdev->dev);
if (!input)
return -ENOMEM;
input->name = "my-mouse";
input->id.bustype = BUS_USB;
// 设置支持的事件
set_bit(EV_KEY, input->evbit);
set_bit(EV_REL, input->evbit);
// 设置按键
set_bit(BTN_LEFT, input->keybit);
set_bit(BTN_RIGHT, input->keybit);
set_bit(BTN_MIDDLE, input->keybit);
// 设置相对坐标
set_bit(REL_X, input->relbit);
set_bit(REL_Y, input->relbit);
set_bit(REL_WHEEL, input->relbit);
return input_register_device(input);
}
// 上报鼠标移动
void mouse_report_move(struct input_dev *input, int dx, int dy)
{
input_report_rel(input, REL_X, dx);
input_report_rel(input, REL_Y, dy);
input_sync(input);
}
// 上报鼠标按键
void mouse_report_button(struct input_dev *input, int button, int pressed)
{
input_report_key(input, button, pressed);
input_sync(input);
}
// 上报滚轮
void mouse_report_wheel(struct input_dev *input, int delta)
{
input_report_rel(input, REL_WHEEL, delta);
input_sync(input);
}
六、用户空间测试
evtest工具
# 安装工具
apt install evtest
# 查看输入设备
ls /dev/input/
cat /proc/bus/input/devices
# 测试输入设备
evtest /dev/input/event0
# 实时监控
evtest --grab /dev/input/event0
读取输入事件
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
int main()
{
int fd;
struct input_event ev;
fd = open("/dev/input/event0", O_RDONLY);
if (fd < 0) {
perror("open");
return -1;
}
while (read(fd, &ev, sizeof(ev)) == sizeof(ev)) {
printf("Type: %d, Code: %d, Value: %d\n",
ev.type, ev.code, ev.value);
}
close(fd);
return 0;
}
七、调试技巧
查看设备信息
# 查看所有输入设备
cat /proc/bus/input/devices
# 查看设备能力
ls /sys/class/input/input0/capabilities/
# 查看设备属性
cat /sys/class/input/input0/name
cat /sys/class/input/input0/phys
内核调试
# 查看输入子系统日志
dmesg | grep -i input
# 动态调试
echo "module input +p" > /sys/kernel/debug/dynamic_debug/control
八、终极总结
输入子系统 = 统一的输入设备管理框架
- 核心结构:input_dev、input_event
- 事件类型:EV_KEY(按键)、EV_REL(相对)、EV_ABS(绝对)
- 上报流程:input_report_* → input_sync
- 用户接口:/dev/input/eventX
掌握输入子系统,就能开发各类输入设备驱动!
9738



被折叠的 条评论
为什么被折叠?



