Linux驱动开发之输入子系统的简介以及相关API与例程

目录

输入子系统的简介

输入子系统的组成部分:

输入子系统的工作流程

输入子系统的优势和作用

设备树相关API函数

注册输入子系统

取消输入子系统的注册

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

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

输入子系统事件上报

按键事件的上报

上报事件的同步

相关例程

例程简介

例程分享


输入子系统的简介


        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. 抽象化
        输入子系统实现了底层输入设备与上层输入处理之间的抽象,通过统一的接口使两者解耦,上层只需要处理通用的输入事件,不关心具体是哪种输入设备。
2. 统一处理
        对各类输入设备的报告进行统一格式的封装,然后由通用的输入核心进行处理,简化上层逻辑。
3. 热插拔支持
        支持输入设备的热插拔,并动态感知新增设备或移除设备,自动处理输入设备的添加和删除。
4. 输入设备模型
        构建输入设备的模型,管理输入设备的属性、capable信息等,通过sysfs接口导出到用户空间。
5. 支持多种设备
        keyboard, mouse, touchscreen, joystick等很多种类的输入设备可以通过输入子系统统一处理。
6. 灵活性好
        通过不同的映射表,一个输入事件可以很容易映射到不同的键值,为不同需求提供支持。


        总体来说,输入子系统很好地抽象和封装了底层输入设备,简化了输入处理逻辑,提高了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)
参数
        无
返回值
    成功 核心结构体的指针
    失败 NULL

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

头文件
        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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


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");

你可能感兴趣的:(Linux驱动开发,驱动开发,linux,c语言,mcu)