0
点赞
收藏
分享

微信扫一扫

Linux驱动的输入子系统简介以及相关函数与例程分析

输入子系统的简介

Linux内核的输入子系统(Input Subsystem)主要用于处理各种输入设备的报告,并将其转换为通用的相关的事件上传给用户空间。

输入子系统的组成部分:

  1. 输入设备驱动(Input Device Drivers):实现不同输入设备的硬件访问,如键盘、鼠标、触摸屏等。
  2. 输入设备核心(Input Core):实现输入设备驱动和输入处理之间的抽象。它允许不同的事件源使用统一的接口与上层交互。
  3. 输入处理层(Input Handlers):实现事件到键码的转换、不同键状态的跟踪、hotplug事件生成等。比如keycode转换成keysym。
  4. 输入设备模型(Input Device Model):用于维护有关输入设备的信息,如名称、特性等。并向用户空间提供统一的输入设备模型。

输入子系统的工作流程

  1. 输入设备驱动采集输入事件,上报给输入核心。
  2. 输入核心转换为通用的输入事件格式。
  3. 输入处理层处理这些事件,执行映射、跟踪等工作。
  4. 最终转换为键码、坐标等,提交给用户空间。

输入子系统的优势和作用

  1. 抽象化

输入子系统实现了底层输入设备与上层输入处理之间的抽象,通过统一的接口使两者解耦,上层只需要处理通用的输入事件,不关心具体是哪种输入设备。

  1. 统一处理

对各类输入设备的报告进行统一格式的封装,然后由通用的输入核心进行处理,简化上层逻辑。

  1. 热插拔支持

支持输入设备的热插拔,并动态感知新增设备或移除设备,自动处理输入设备的添加和删除。

  1. 输入设备模型

构建输入设备的模型,管理输入设备的属性、capable信息等,通过sysfs接口导出到用户空间。

  1. 支持多种设备

keyboard, mouse, touchscreen, joystick等很多种类的输入设备可以通过输入子系统统一处理。

  1. 灵活性好

通过不同的映射表,一个输入事件可以很容易映射到不同的键值,为不同需求提供支持。

总体来说,输入子系统很好地抽象和封装了底层输入设备,简化了输入处理逻辑,提高了Linux系统的移植性,也使得上层应用程序的输入支持更加灵活方便。输入子系统屏蔽了不同设备的差异,为用户空间提供统一的输入抽象,使得上层应用可以独立于具体设备实现。

设备树相关API函数

注册输入子系统

头文件
    linux/input.h
原型
    int input_register_device(struct input_dev *dev)
参数
    struct input_dev *dev    输入子系统的核心结构体
返回值
    成功 0
    失败 负数
struct input_dev {
    const char *name;    名字 会出现在sys目录下
    
    unsigned long evbit[BITS_TO_LONGS(EV_CNT)];    支持的事件的类型 
    #define EV_SYN (0x00)	//同步事件,用于同步多个输入事件的状态,例如在一次事件序列中标记事件的开始和结束。
    #defineEV_KEY (0x01)	//按键事件,用于表示按键的按下和松开。
    #defineEV_REL (0x02)	//相对事件,用于表示相对输入设备的移动,例如鼠标的相对位移。
    #defineEV_ABS (0x03)	//绝对事件,用于表示绝对输入设备的位置,例如触摸屏的绝对坐标。
    #define EV_MSC (0x04)	//杂项事件,用于表示一些杂项的事件,例如时间戳。
    #define EV_SW (0x05)	//开关事件,用于表示开关类设备的状态变化。
    #define EV_LED (0x11)	//LED事件,用于表示LED灯的状态变化。
    #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)	//事件类型的数量,用于表示事件类型的总数。

    unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];    支持的按键 一一对应 set_bit()
 }
    输入子系统的核心结构体不能直接定义,必须要借助于内核提供的核心结构体的初始化的函数

取消输入子系统的注册

头文件
  linux/input.h
原型
   void input_unregister_device(struct input_dev *dev)
参数
    struct input_dev *dev    输入子系统的核心结构体
返回值
    无

输入子系统核心结构体的初始化

头文件
		linux/input.h
原型
		struct input_dev *input_allocate_device(void)
参数
		无
返回值
		成功 核心结构体的指针
    失败

释放输入子系统的核心结构体

头文件
		linux/input.h
原型
		void input_free_device(struct input_dev *dev);
参数
		struct input_dev *dev		输入子系统的核心结构体
返回值
		无

输入子系统事件上报

头文件
    linux/input.h
原型
    void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
参数
    struct input_dev *dev    核心结构体
    unsigned int type    事件的类型
    unsigned int code    具体哪一个设备
    int value    上报的真实的值
返回值
    无

按键事件的上报

头文件
    linux/input.h
原型
    void input_report_key(struct input_dev *dev, unsigned int code, int value)
参数
    struct input_dev *dev    输入子系统的核心结构体
    unsigned int code     哪一个按键
    int value     1 按下  0松开
    无

上报事件的同步

头文件
    linux/input.h
原型
    void input_sync(struct input_dev *dev)
参数
    struct input_dev *dev    输入子系统
返回值
    无

相关例程

例程简介

这个输入子系统的例程实现了对一个按键的支持,主要功能包括:

  1. 定义input_dev核心结构体myinput,并做初始化。指定设备名,支持的事件类型为EV_KEY(按键事件)和EV_REP(重复事件)。支持的按键为KEY_1。
  2. 注册该输入设备。
  3. 获取按键所使用的中断号,并初始化定时器(用于按键消抖)。注册中断处理函数myirq_func。
  4. 在中断处理函数中启动定时器,延时一段时间后执行判断按键状态的函数mytimer_func。
  5. mytimer_func中读取按键电平状态,根据状态上报KEY_1的按下或释放事件。并调用input_sync同步事件。
  6. 初始化时注册设备,退出时注销设备。

通过中断检测按键动作,在经过过滤消抖后,上报按键事件到输入子系统。输入子系统会负责发送这些事件到用户空间,交给相关任务处理。

例程分享

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/gpio.h>


struct input_dev *myinput = NULL;
int irq_num;
struct timer_list mytimer;

/*定时器的回调函数,用于判断按键是否真的按下*/
void mytimer_func(unsigned long data){
	if(gpio_get_value(EXYNOS4_GPX3(2)) == 0){	//获取按键状态
		input_report_key(myinput, KEY_1, 1);	//按键事件上报
	}
	else{
		input_report_key(myinput, KEY_1, 0);
	}
	//上报事件的同步
	input_sync(myinput);
}


/*中断回调函数*/
irqreturn_t myirq_func(int num, void * data){
	//启动定时器,延时15ms,用于消抖
	mod_timer(&mytimer, jiffies + msecs_to_jiffies(15));
	
	return 0;
}

/*驱动设备的加载函数*/
static int __init myinput_init(void){
	int ret;

	//初始化输入子系统的核心结构体
	myinput = input_allocate_device();
	myinput->name = "yyy";

	//向内核注册输入子系统
	set_bit(EV_KEY, myinput->evbit);	//按键事件
	set_bit(EV_REP, myinput->evbit);	//重复事件
	
	set_bit(KEY_1, myinput->keybit);
	ret = input_register_device(myinput);

	//获取中断号
	irq_num = gpio_to_irq(EXYNOS4_GPX3(2));

	//使能中断号
	enable_irq(irq_num);

	//向内核注册中断号
	ret = request_irq(irq_num, myirq_func, IRQ_TYPE_EDGE_BOTH, "my_input", NULL);

	//初始化定时器
	mytimer.expires = jiffies + 1 * HZ;
	mytimer.function = mytimer_func;
	init_timer(&mytimer);
	
	return 0;
}

/*驱动设备的卸载*/
static void __exit myinput_exit(void){
	//取消注册
	input_unregister_device(myinput);
}

module_init(myinput_init);
module_exit(myinput_exit);
MODULE_LICENSE("GPL");










举报

相关推荐

0 条评论