input子系统 事件流程浅析

Qt之 读取 /dev/input/event*设备 并解析 qt 读取/dev/input/event 设备,并解析数据 阅读详情

事件(struct input_event)从设备驱动层 –> 核心层—>事件处理层的经过

 struct input_event {
    struct timeval time; //事件发生的时间
    __u16 type;          //事件的类型
    __u16 code;          //事件的代码
    __s32 value;         //事件的值
};

这里写图片描述


在input.h中有定义:
/** type:
    事件的类型
*/
#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
#define EV_MAX          0x1f
#define EV_CNT          (EV_MAX+1)  //不知道这个设置是为什么了,下面is_event_supported()中会判断事件类型值 <= EV_MAX

/**
    当事件类型是EV_KEY的时候,code为设备键盘码 0~127为键盘上的按键
*/
#define KEY_Q           16
#define KEY_W           17
#define KEY_E           18
#define KEY_R           19
#define KEY_T           20
#define KEY_Y           21
#define KEY_U           22
#define KEY_I           23
#define KEY_O           24
#define KEY_P           25

/*
  code : 事件的代码
         如果事件的类型是EV_KEY
                  该code为设备键盘码, 0 - 127 : 键盘上的按键代码 
         如果事件的类型是EV_REL
                  该code : REL_X  REL_Y
   value : 如果事件的类型是EV_KEY
                          value : 0 松开   1 按下
            如果事件的类型是EV_REL
                          value正负值表示 两个方向上的值 
   详细请参考input.h                                     
*/
在中断处理函数中,调用input_report_key( )向输入子系统报告发生的事件
/**
    向输入子系统发生事件。
    dev  :发生事件的设备
    code : 事件代码
    value:事件的值
    在中断函数中不需要考虑重复按键的点击情况,这个函数可以检查这个问题,并报告一次事件
*/
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
    // !!value ?   
    input_event(dev, EV_KEY, code, !!value);
}
/**
    input子系统中 任何向核心层报告事件的都会经过这个函数。
*/
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
    unsigned long flags; //为什么不初始化就传递过去了?
     /**
         判断设备是否支持该事件
         is_event_supported(){
                  这里的code是上面传递过来的type 
                  EV_KEY : 1
                  EV_CNT : 32
                  max : 31
                  既然位图是0~31位,为什么还要定义EV_CNT?这里还要判断?
                  code <= max : 事件类型不是 EV_CNT
                  test_bit(code, bm) : 判断evbit的第code位是否被置1了,被置1了,表示支持,为0,表示不支持
                 return code <= max && test_bit(code, bm);
         }
      */   
    if (is_event_supported(type, dev->evbit, EV_MAX)) {
        /*
            避免竞态的一种方法:屏蔽本地cpu中断
            那么 如果临界区执行完成需要的时间长,那么这里屏蔽中断有点危险
            保存本地中断状态,
            关闭本地中断,
            获取自旋锁
        */
        spin_lock_irqsave(&dev->event_lock, flags);
        add_input_randomness(type, code, value);
        //调用这个函数来报告事件
        input_handle_event(dev, type, code, value);
        spin_unlock_irqrestore(&dev->event_lock, flags);
    }
}
static void input_handle_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
   /*
     这个disposition变量表示使用什么样的方式处理事件,
     #define INPUT_IGNORE_EVENT   0           //忽略该事件
     #define INPUT_PASS_TO_HANDLERS   1   //将该事件交给handler处理
     #define INPUT_PASS_TO_DEVICE 2   //交给input_dev处理
     #define INPUT_PASS_TO_ALL    (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE) //handler、input_dev共同处理
   */
   int disposition = INPUT_IGNORE_EVENT;

   //下面是一个大的switch结构,截取EV_KEY部分。
   case EV_KEY:
                //is_event_supported 判断是否支持该事件。test_bit 测试按键的状态是否改变
        if (is_event_supported(code, dev->keybit, KEY_MAX) && !!test_bit(code, dev->key) != value) {
            if (value != 2) {
                __change_bit(code, dev->key);
                if (value)
                    input_start_autorepeat(dev, code);
                else
                    input_stop_autorepeat(dev);
            }
            //设置成INPUT_PASS_TO_HANDLERS,表示由handler来处理这个事件
            disposition = INPUT_PASS_TO_HANDLERS;
        }
        break; 
             .......
   if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
        dev->event(dev, type, code, value);

   // 事件交给handler处理,调用input_pass_event函数        
   if (disposition & INPUT_PASS_TO_HANDLERS)
    input_pass_event(dev, type, code, value);         
}
static void input_pass_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
    struct input_handle *handle;
    rcu_read_lock();
    handle = rcu_dereference(dev->grab);
    if (handle)
        handle->handler->event(handle, type, code, value);
    else   
     /*
        遍历input_handle链表,取出input_handle指向的input_handler.
        调用这个input_handler的event函数。     
     */
        list_for_each_entry_rcu(handle, &dev->h_list, d_node)
            if (handle->open)  //如果已经被打开
                /**
                    事件由核心层流转到事件处理层。
                    如果input_handler是evdev的话,那么evdev_event将会被调用
                */
                handle->handler->event(handle,type, code, value);
    rcu_read_unlock();
}
/**
    到这里事件处理到达了 事件处理层,我们知道了事件发生后,input_handler的event函数会被调用。
    下面我们看看 input_handler的event函数
*/
static struct input_handler evdev_handler = {
    .event      = evdev_event,     //事件上报后,这个函数会被调用
    .connect    = evdev_connect,
    .disconnect = evdev_disconnect,
    .fops       = &evdev_fops,
    .minor      = EVDEV_MINOR_BASE,
    .name       = "evdev",
    .id_table   = evdev_ids, //这个handler所能够支持的设备列表
};
static void evdev_event(struct input_handle *handle,unsigned int type, unsigned int code, int value)
{
        /*取出evdev
                在evdev_connect()中设置
        把evdev放入到input_handle的私有数据中,
            evdev->handle.private = evdev;
        */
    struct evdev *evdev = handle->private;
    struct evdev_client *client;
    struct input_event event;

    do_gettimeofday(&event.time);
    //根据传入的值 为input_event赋值
    event.type = type;
    event.code = code;
    event.value = value;

    rcu_read_lock();
    client = rcu_dereference(evdev->grab);
    if (client)
        evdev_pass_event(client, &event);
    else
            //遍历client链表,调用evdev_pass_event函数   
        list_for_each_entry_rcu(client, &evdev->client_list, node)
                //调用这个函数来发送
            evdev_pass_event(client, &event);

    rcu_read_unlock();

    //唤醒等待的进程。  那么是在什么时候被阻塞的呢?  
    wake_up_interruptible(&evdev->wait);
    /**
        static ssize_t evdev_read(struct file *file, char __user *buffer,size_t count, loff_t *ppos)
        {
            /**
                如果没有数据&&以非阻塞的方式打开的话,就返回 
            */  
            if (client->head == client->tail && evdev->exist &&(file->f_flags & O_NONBLOCK))
            {
                return -EAGAIN;
            }
            /**
                不然,就休眠。input_event()函数会唤醒的
            */  
            retval = wait_event_interruptible(evdev->wait,client->head != client->tail ||!evdev->exist);
            if (retval)
            {
                return retval;
            }   
        }     
    */ 
}
static void evdev_pass_event(struct evdev_client *client,
                 struct input_event *event)
{
    /**
        获取自旋锁 --- 访问临界区----释放自旋锁
    */    
    spin_lock(&client->buffer_lock);
    //将事件赋值给客户端的input_event 数组
    client->buffer[client->head++] = *event;
    client->head &= EVDEV_BUFFER_SIZE - 1;
    spin_unlock(&client->buffer_lock);
    /**
        向应用层发送消息,应用层会执行对应的消息处理函数。
    */  
    kill_fasync(&client->fasync, SIGIO, POLL_IN);
}

从上面可以看出  事件最终被放入到了客户端的input_event[]数组中了,
只需要将这个input_event[]数组复制给用户空间即可。

看看用户空间把,用户空间调用read,evdev_read会被调用,
static ssize_t evdev_read(struct file *file, char __user *buffer,size_t count, loff_t *ppos)
{
    ....
    while (retval + input_event_size() <= count &&evdev_fetch_next_event(client, &event)) {
                //调用了这个函数来获取事件
        if (input_event_to_user(buffer + retval, &event))
            return -EFAULT;
        retval += input_event_size();
    }

    return retval;
}
int input_event_to_user(char __user *buffer,const struct input_event *event)
{
        //哈哈,copy_to_user 将input_event拷贝到用户空间中
    if (copy_to_user(buffer, event, sizeof(struct input_event)))
    {
        return -EFAULT;
    }    
    return 0;
}
内核input子系统之input_event数据上报过程 我们数据上报是在device层,其中上报的一个入口是调用input_event(dev, EV_KEY, code, !!value);那么,那么首先在在其中做的判断是是否支持evbit,什么意思呢?我们还记得在module_init中会做一个初始化设置:设置了当前设备能够产生按键数据--将某个bit置1,和当前设备能够产生的输入按键类型,比如power键,音量up键等。设置方法如下:__set_... 阅读详情

相关推荐

input子系统四:输入事件是如何上报的

我们在输入设备驱动中利用函数input_event()函数来上报事件,接下来我们就来看看,内核拿到我们上报的数据之后是如何继续上报到用户空间的 /** * input_event() - report new input event * @dev: device that generated the event * @type: type of the event * @code: ev...

weixin_43790707的博客 1765

Linux系统struct input_event结构体分类型(鼠标、键盘、触屏)详解与例子

Linux系统是通过输入子系统来管理输入设备(如鼠标、键盘、触摸屏、游戏摇杆)的。配置了内核支持输入设备且有对应驱动的情况下,当系统接入输入设备,会在/dev目录或/dev/input下生成对应设备文件,下图是鼠标、键盘在不同情况下/dev/input的设备文件。每当输入设备有事件产生时,内核就会将事件上报到设备文件,事件数据以为单位存入设备文件,所以读取这些输入设备文件时使用结构体,这个结构体定义在input-event-codes.h。

wkd_007的博客 1万+

Linux input子系统(二)input子系统驱动

Linux input子系统 Linux input子系统(一)input设备应用编程 Linux input子系统(二)input子系统驱动 Linux input子系统(二)input子系统驱动 文章目录Linux input子系统(二)input子系统驱动一、input子系统驱动框架1.1 input子系统的主要对象1.2 input子系统的驱动框架二、input设备驱动编写2.1 分配一个...

JT同学的博客 2321

input_event结构体详解

关键结构体input_event信息: struct input_event { struct timeval time; __u16 type; __u16 code; __s32 value; }; type: 设备类型。可以设置为: #define EV_SYN          0x00    表示设备支持所有的事件 #define EV_KEY         

liwei405499的专栏 1万+

Linux input子系统(一)input设备应用编程

Linux input子系统 Linux input子系统(一)input设备应用编程 Linux input子系统(二)input子系统驱动 Linux input子系统(一)input设备应用编程 文章目录Linux input子系统(一)input设备应用编程一、使用hexdump读取input设备二、input事件分析三、input设备应用编程 Linux内核提供input子系统给输入设备...

JT同学的博客 3406

input子系统——kernel中input设备介绍

一、输入子系统驱动层分析    在键盘驱动代码分析的笔记中,接触到了input子系统,键盘驱动,键盘驱动将检测到的所有按键都上报给了input子系统。Input子系统是所有I/O设备驱动的中间层,为上层提供了一个统一的界面。例如,在终端系统中,我们不需要去管有多少个键盘,多少个鼠标。它只要从input子系统中去取对应的事件(按键,鼠标移位等)就可以了。今天就对input子系统做一个详尽的

frank_zyp的博客 9370

Linux之Input子系统

Input 子系统是管理输入的子系统, 和 pinctrl 和 gpio 子系统一样, 都是 Linux 内核针对某一类设备而创建的框架。input 子系统处理输入事务, 任何输入设备的驱动程序都可以通过 input 输入子系统提供的接口注册到内核, 利用子系统提供的功能来与用户空间交互。输入设备总类繁杂, 包括按键, 键盘, 触摸屏, 鼠标, 摇杆等, 它们本身是字符设备, 不过内核为了能将这些设备的共性抽象出来, 简化驱动的开发, 建立了一个 Input 子系统。

风间琉璃的博客 6905

Input子系统

linux子系统--Input, 框架学习, 源码分析

m0_74976404的博客 1133

【Linux驱动】Input子系统

输入子系统由驱动层(input_driver)、核心层(input_core)以及事件层(input_event_handle)组成,如下图所示: Note:Input子系统中所有输入设备的主设备号都是13,核心层通过次设备号来将输入设备分类,例如如0-31是游戏杆,32-63是鼠标(对应Mouse Handler)、64-95是事件设备(如触摸屏,对应Event Handler)。在使用input系统时,不需要去注册字符设备,只需要向系统申请一个input_device即可。在Linux内核中,使用inp

weixin_46334900的博客 2300

【Cadence】Virtuoso@LayoutXL 差分电路版图同步修改

对于差分电路,器件和部分走线通常是对称的,只需要画一半,另一半通过镜像操作完成。因此,需要对差分电路进行同步的修改,保证电路的差分对称性和正确的连接关系。然而,通常版图会经过多次修改,修改的部分可能在镜像的时候被遗漏。左边画了金属块,右边也出现了同步出现了金属块。进入group会有同步复制的提示。

乾巽的博客 3046

robomimic实验复现全流程指南:从数据集下载到训练视频生成

本文提供了RoboMimic实验复现的完整实战指南,重点解析了从数据集下载、配置文件定制到模型训练与评估的全流程。以tool_hang任务为例,详细说明了环境准备、数据获取、训练监控及视频生成等关键步骤,并针对常见问题给出了排查方案,帮助研究者高效完成机器人模仿学习实验的闭环验证。

2w3e4r5t6y的博客 1031

input子系统中读取流程解析

对Input子系统中读取流程进行解析,以内核提供的evdev.c(input_handler)和gpio-keys.c(input_dev)为例,详解发生中断时,Input device层是如何上报数据,将事件传递给input handler层去进行处理,唤醒休眠开始读取数据。包括 input_handler、evdev、evdev_client 等结构体的实现细节,尤其是 evdev 设备驱动的接口和数据传递的工作机制

caiji0169的博客 1925

基于DE2-115红外解码实验

基于DE2-115红外解码实验   一、   功能描述 在同一个遥控电路中通常要使用实现不同的遥控功能或区分不同的机器类型, 这样就要求信号按一定的编码传送,编码则会由编码芯片或电路完成。对应于编码芯片通常会有相配对的解码芯片或包含解码模块的应用芯片。在实际的产品设计或业余电子制作中,编码芯片并一定能完成我们要求的功能,这时我们就需要了解所使用的编码芯片到底是如何编码的。只有知道编码方式,我...

乘风的博客 2513

十五、(正点原子)Linux INPUT子系统

input就是输入的意思,因此input子系统就是管理输入的子系统,和pinctrlgpio子系统一样,都是 Linux 内核针对某一类设备而创建的框架。比如按键输入、键盘、鼠标、触摸屏等等这些都属于输入设备,不同的输入设备所代表的含义不同,按键和键盘就是代表按键信息,鼠标和触摸屏代表坐标信息,因此在应用层的处理就不同,对于驱动编写者而言不需要去关心应用层的事情,我们只需要按照要求上报这些输入事件即可。为此input子系统分为input驱动层、input核心层、input。

Tofu_Cabbage的博客 1988

【中国大学生计算机设计大赛信息可视化赛道】国奖项目深入剖析—中国古代奇异怪兽移动端可视化应用项目

【中国大学生计算机设计大赛信息可视化赛道】国奖项目深入剖析—中国古代奇异怪兽移动端可视化应用项目 注:本文主要目的是对于往年国奖项目案例-可视化项目的深入剖析讲解,对源码、项目特点、要点、特色继续深入分析。帮助大家学习技术,掌握规律。

一键难忘的博客 3727

27、物联网中的量子密钥分发:安全通信新范式

本文探讨了物联网(IoT)快速发展所带来的安全挑战,并提出量子密钥分发(QKD)作为应对新兴威胁的新范式。文章详细介绍了QKD的基础原理、BB84协议的实现流程以及其在物联网中的应用前景,同时强调了安全专业人员提升技能、制定标准化指南以及跨学科合作的重要性。通过利用量子力学特性,QKD能够提供更高的通信安全性,抵御量子计算等未来威胁,为物联网的安全发展提供保障。

tequila的博客 156

Linux中的input子系统浅析

系统框架主要分为三部分:Input driver:该部分是对应实际的输入设备驱动,用于处理硬件设备,将数据转化成统一的数据,方便和handler层交互。input core:字面意思就是input系统的核心层,提供各种处理函数input_register_device、input_register_handler、input_allocate_device、input_event等。Event handler:对应某个输入事件的处理,将数据发送到上层。

qq_54089476的博客 1133

CATIA二次开发核心技术:多实体零件自动化拆解与装配生成深度解析(零件转产品)

CATIA二次开发核心技术:多实体零件自动化拆解与装配生成深度解析

KE17RS的博客 2143

Linux驱动学习之input子系统

input子系统框架编写input驱动我们只关心内核层,在内核层注册input子系统,然后进行事件上报就可以了。

m0_57485149的博客 2335

SMART200如何实现多次调用子程序带定时器功能

1、西门子200PLC的子程序由于很多限制条件,如果希望建立一个可以多次调用的子程序,那么这个子程序里是不能含有定时器指令的,所以我们只能退而求其次了。建议将沿指令(上升沿、下降沿)放在多次调用子程序的外面。 2、可以提供不同的时基,提供不同时间的定时 3、实现秒定时 5、子程序内部: STL代码: 1、时基信号由外部提供、 可以提供不同的时基信号 2、实现循环定时开关 LD #Start:L0....

RXXW_Dor的博客 1万+

从零开始跑通3DGS教程:(三)坐标系与尺度编辑(CloudCompare)

本文所属《从零开始跑通3DGS教程》系列文章; sfm重建的点云已经丢掉了尺度信息,并且坐标系跟图像数据有关(SFM初始化选择的图像),所以如果想恢复物理真实尺度,以及在想要的视角下渲染,那么需要对尺度和坐标系进行编辑修正,下文介绍如何使用CloudCompare修改坐标系和尺度

诺有缸的高飞鸟的博客 2870

浅析 Linux input 输入子系统

input子系统简介

安迪西_嵌入式攻城狮 1733

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

驱动开发之 input 子系统

input 就是输入的意思,input 子系统就是管理输入的子系统,和 pinctrl、gpio 子系统 一样,都是 Linux 内核针对某一类设备而创建的框架。比如按键输入、键盘、鼠标、触摸屏等 等这些都属于输入设备,不同的输入设备所代表的含义不同,按键和键盘就是代表按键信息, 鼠标和触摸屏代表坐标信息,因此在应用层的处理就不同,。为此 input 子系统分为 input 驱动 层、input 核心层、input 事件处理层,最终给用户空间提供可访问的设备节点,input 子系统框。

m0_46392035的博客 1579

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

【Linux】浅析Input子系统

文章目录1. 框架1.1 数据结构1.2 evdev_handler1.3 evdev_init1.4 input_register_handler2. 应用如何打开节点并读取到事件数据2.1 evdev_fops2.2 evdev_open2.3 evdev_release2.4 evdev_read2.5 evdev_write2.6 evdev_poll2.7 evdev_fasync2.8 evdev_ioctl2.9 evdev_ioctl_compat2.10 总结3. Driver 如何注册i

永远年轻 永远热泪盈眶 1743

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Linux驱动(input子系统)

input 就是输入的意思,因此 input 子系统就是管理输入的子系统,和 pinctrl、 gpio 子系统一样,都是 Linux 内核针对某一类设备而创建的框架。比如按键输入、键盘、鼠标、触摸屏等等这些都属于输入设备,不同的输入设备所代表的含义不同,按键和键盘就是代表按键信息,鼠标和触摸屏代表坐标信息,因此在应用层的处理就不同,对于驱动编写者而言不需要去关心应用层的事情,我们只需要按照要求上报这些输入事件即可。

2302_80784410的博客 1339

Linux Input 子系统

一、输入子系统的基本框架 输入子系统主要由三部分构成: 核心层(input.c,linux提供的中间核心层,向事件处理曾和设备驱动层提供接口函数)、事件处理层(evdev.c, keyboard.c, mousedev.c 等等)、设备驱动层(具体的设备驱动程序,比如按键驱动,触摸屏驱动等等, 具体硬件设备相关的驱动) 1、核心层 - input.c 这一层是linux内核实现的的一些通用的接口,可以向事件处理层和设备驱动层提供一些公用函数 input_register_handler /inpu.

db_linux_driver的博客 2670
上一篇: lcd probe函数 赏析
下一篇: linux 内核多线程编程
leesagacious
博客等级 码龄12年 146粉丝 179原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值