linux-2.6.38 input子系统(用输入子系统实现按键操作)

一、设备驱动程序

  在上一篇随笔中已经分析,linux输入子系统分为设备驱动层、核心层和事件层。要利用linux内核中自带的输入子系统实现一个某个设备的操作,我们一般只需要完成驱动层的程序即可,核心层和事件层内核已经帮我们做好了。因此这篇随笔主要介绍按键操作设备驱动层的代码。

1.1设备驱动入口函数

  在设备驱动入口函数中我们需要做的事:(1)分配一个input_dev 结构体

                    (2)设置这个input_dev 结构体

                    (3)调用input_register_device注册这个input_dev

                    (4)完成硬件相关操作:如注册中断处理函数,添加定时器等

static int button_init(void)
{int i;int err;/* 1. 分配一个 input_dev 结构体*/button_dev = input_allocate_device();/* 2. 设置 *//* 2.1 能产生哪类事件 */set_bit(EV_KEY, button_dev->evbit);/* 2.2 能产生这类事件下的哪些操作: L S  ENTER LEFTSHIT*/set_bit(KEY_L, button_dev->keybit);set_bit(KEY_S, button_dev->keybit);set_bit(KEY_ENTER, button_dev->keybit);set_bit(KEY_LEFTSHIFT, button_dev->keybit);/* 3. 注册 */input_register_device(button_dev);/* 4. 硬件相关操作*/gpkcon = ioremap(GPKCON_PA, 4);  //io口映射gpkdat = ioremap(GPKDAT_PA, 4);gpndat = ioremap(GPNDAT_PA, 4);init_timer(&button_timer);              // 初始化定时器button_timer.function = button_timer_function;// 指定定时器的处理函数add_timer(&button_timer);            // 添加定时器for (i=0; i<4; i++){err = request_irq(button_irqs[i].irq, buttons_interrupt, button_irqs[i].flags,button_irqs[i].name,(void *)&button_irqs[i]);if (err)break;}    return 0;
}

1.1.1在初始化iinput_dev结构体过程

  主要对其中的如下数组做了初始化,从而来确定该设备支持哪些事件,支持哪些操作。

      

  初始化时首先要确定设备能够产生哪一类事件

      事件的类型如下: 

      

      事件类型的设置主要对evbit[]数组中的相应位做设置: set_bit(EV_KEY, button_dev->evbit);

   然后再确定设备支持该类事件下的哪些操作

      例如:在相对坐标事件下可以支持如下操作

      

      在本次按键驱动程序中按键设备产生的事件自然是按键事件,按键事件支持KEY_L、KEY_S、KEY_ENTER、KEY_LEFTSHIFT  4个操作,分别对应 L ,S, enter,shift 

      set_bit(KEY_L, button_dev->keybit);

      set_bit(KEY_S, button_dev->keybit);

      set_bit(KEY_ENTER, button_dev->keybit;

      set_bit(KEY_LEFTSHIFT, button_dev->keybit);

1.1.2 注册输入设备input_register_device()

  这个函数在上一篇博客中已经做了简要分析,这里在提一下input_register_device()中做了哪些事

    (1) 设置同步事件、清除KEY_RESERVED、清除bitmasks中没有提到的位

    (2) 初始化定时器,确定定时器的处理函数。这里定时器与重复上报事件有关,注意在事件类型中有EV_REP事件,设置这个事件在ev_bit中的相应位,就可以重复上报事件。

    (3) 设置getkeycode 和 setkeycode 函数

    (4) device_add, input_dev包含的device结构注册到Linux设备模型中,在sysfs文件系统中可以看到增加了设备input1

        

    (5)list_add_tail  在上一篇博客中已经介绍了

    (6)遍历iinput_handler_list 中的事件处理器 input_handler, 与input_handler 进行匹配 连接操作。 具体的连接操作在下边分析。

1.1.3 硬件相关操作

  定时器、IO映射、注册中断

2. 中断处理函数

static void button_timer_function(unsigned long data)
{int num;int tmp;struct button_irq_desc* pindesc = irq_pdesc;if (! pindesc)return;num = pindesc->number;    tmp = *gpndat;input_event(button_dev, EV_KEY,button_irqs[num].key_val , !(tmp &(1<<num)));input_sync(button_dev);return;
}
static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{/* 10 ms 则 hz/100  100 ms 则 hz/10*/irq_pdesc = (struct button_irq_desc *)dev_id;mod_timer(&button_timer, jiffies+HZ/100);return IRQ_RETVAL(IRQ_HANDLED);
}

 中断处理函数包括两部分:定时中断用于消除按键抖动

             按键中断处理函数主要用来调

整定时器事件

 当有按键中断发生时,我们需要上报事件:

  input_event(button_dev, EV_KEY,button_irqs[num].key_val , !(tmp &(1<<num)));
  input_sync(button_dev);

 2.1.1 input_event 上报事件函数

void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{unsigned long flags;// 判断是否支持这种事件if (is_event_supported(type, dev->evbit, EV_MAX)) {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)
{int disposition = INPUT_IGNORE_EVENT;.....switch (type) {case EV_KEY: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);elseinput_stop_autorepeat(dev);}disposition = INPUT_PASS_TO_HANDLERS;}break;if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)dev->sync = false;if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)dev->event(dev, type, code, value);if (disposition & INPUT_PASS_TO_HANDLERS)input_pass_event(dev, type, code, value);
}

  大概可以分析出事件处理函数要么执行input_dev下的event, 要么执行input_handler下的event函数,至于执行哪一个和disposition这个变量有关。

  需要注意的是如果执行的是input_dev下边的event,那么应该只会执行一个event函数,如果要是执行的是input_handler下的event函数,那么会执行input_pass_event 函数。这个函数中通过input_dev->h_list链表上挂的input_handler结构体,找到与之匹配的input_handler。然后执行input_handler下的event函数。      如果input_dev与多个input_handler事件处理器匹配了,那么当设备驱动上报事件时,并且要执行input_handler中的event函数,那么会依次执行这些匹配好的input_handler的event函数。在接下的实验中可以看到这一点。

二、程序执行部分过程分析:

  2.1 input_dev与 input_handler 的连接

  因为我们的程序中已经注册了evdev_handler事件处理器(在上一篇博客中已经分析过了input_register_handler)和 kbd_handler 事件处理器(在keyboard.c 文件中 kbd_init-->input_register_handler(&kbd_handler)) ,当在驱动程序中input_register_device后,设备驱动和这两个事件处理器进行匹配和连接操作。

  2.1.1 button_dev 和 evdev_handler 连接

  匹配完成之后会执行连接操作,连接操作执行的是evdev_handler->connet 函数,这个函数已经在前一篇博客中分析了,这里只简述函数执行过程和效果。

  (1) 分配evdev 结构体

  (2) 设置evdev 结构体下边的 handle 和 dev 

     (3) 注册evdev 下边的handle ,在上一篇博客中已经分析了这个handle是连接input_dev 和 input_handler 的桥梁

   (4) 添加设备device_add(&evdev->dev)

     执行完这个函数应该可以看到在/dev/input/下出现even0 设个设备

      

     可以看到主设备号为13, 次设备号为64, 这里次设备号=64+minor, 因为evdev_handler 还没有和任何设备建立连接,所以在input_table[] 数组中还没存放任何evdev结构体,因此minor的值为0, 故此设备号为64+0=64。

  2.1.2 button_dev 和 kbd_handler 的匹配过程

static int kbd_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
{struct input_handle *handle;int error;handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);if (!handle)return -ENOMEM;handle->dev = dev;handle->handler = handler;handle->name = "kbd";error = input_register_handle(handle);if (error)goto err_free_handle;error = input_open_device(handle);if (error)goto err_unregister_handle;return 0;err_unregister_handle:input_unregister_handle(handle);err_free_handle:kfree(handle);return error;
}

  这里的连接过程比较简单,没有上一个连接过程那么复杂,但是可以看到主要的数据结构input_handle并没有少,因为这个结构体是input_dev和input_handler的联系桥梁,复杂的数据结构之间的关系可以使input_dev 可以访问到input_handle从而访问到input_handler。(在事件上报函数中分析过怎样从input_dev找到input_handler)

2.2  事件上报函数分析

  前文已经提到了button_dev 与 evdev_handler 和 kbd_handler 匹配连接成功,那么在事件上报的时候,就会分别执行这两个事件处理器中的event函数。

  2.2.1 evdev->event 函数

  这个函数已经在上一篇博客中提前分析过了。

  2.2.2 kbd->event 函数

static void kbd_event(struct input_handle *handle, unsigned int event_type,unsigned int event_code, int value)
{/* We are called with interrupts disabled, just take the lock */spin_lock(&kbd_event_lock);if (event_type == EV_MSC && event_code == MSC_RAW && HW_RAW(handle->dev))kbd_rawcode(value);if (event_type == EV_KEY)kbd_keycode(event_code, value, HW_RAW(handle->dev));spin_unlock(&kbd_event_lock);tasklet_schedule(&keyboard_tasklet);do_poke_blanked_console = 1;schedule_console_callback();
}

三、实验效果

  3.1 执行hexdump /dev/input/event0

     依次按下 l   s   enter 对应的按键:l 对应0x26  s 对应0x1f  enter对应0x1c  

      

  3.2  执行cat /dev/tty1 

     并且将标准输入重定向到/dev/tty1  依次按下 l   s   enter 对应的按键

      

     可以看到依次按下l s enter 后执行了ls 命令

     同时需要强调的是:上边的两个命令的执行在不同的shell 下,但是只按下了一次l s enter后分别在两个shell中打印了不同的结果。这就证明了之前所说的一个button_dev 与 evdev_handler 和 kbd_handler 建立了连接,上报一次事件,会分别调用这两个事件处理器的event 函数。

 

    

   

  

  

转载于:https://www.cnblogs.com/zf1-2/p/10859535.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/424780.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LinkedList专题2

203 Remove Linked List Elements 思路&#xff1a;考虑1 &#xff1a; 可能有多个节点符合&#xff1b;考虑2&#xff1a;命中节点是head&#xff1b;考虑3&#xff1a;命中节点是尾节点&#xff1b;考虑4&#xff1a;命中节点是中间的普通节点。 学习1&#xff1a;在linked…

第四十一期:一道经典的MySQL面试题,答案出现三次反转

前几天偶然看到大家在讨论一道面试题&#xff0c;而且答案也不够统一&#xff0c;我感觉蛮有意思&#xff0c;在此就做一个解读&#xff0c;整个过程中确实会有几处反转。 作者&#xff1a;杨建荣的学习笔记来源&#xff1a;今日头条 前几天偶然看到大家在讨论一道面试题&…

java中资源获取

安装java sudo apt-get install default-jdk 获取doc $dpkg -l | grep jdk ii default-jdk 2:1.8-56ubuntu2 amd64 Standard Java or Java compatible Development Kit ii default-jdk-headless …

LinkedList专题3

148. Sort List 问题&#xff1a;要求时间度O(NlogN)O(NlogN)&#xff0c;空间复杂度是常数&#xff0c;实现数组排序。 思路&#xff1a;之前做过linkedList的插入排序&#xff0c;时间复杂度应该是O(n2)O(n2)。时间复杂度控制在nlogn 一定有二分。二分我只在数组版的list中…

java面试题1 牛客:A派生出子类B,B派生出子类C,并且在java源代码中有如下声明:

懵逼树上懵逼果&#xff0c;懵逼树下你和我 第一题 [单选题] A派生出子类B&#xff0c;B派生出子类C&#xff0c;并且在java源代码中有如下声明&#xff1a; 1 2 3 A a0new A(); A a1new B(); A a2new C(); 以哪个说法是正确的 A第1行&#xff0c;第2行和第3行的声明都是正…

[Leetcode][第81题][JAVA][N皇后问题][回溯算法]

【问题描述】[困难] 【解答思路】 1. 主副对角线列 标记 复杂度 import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.List;public class Solution {private int n;// 记录某一列是否放置了皇后private boolean[] col;// 记…

编码解码错误

python读取文件时提示UnicodeDecodeError错误&#xff1a; "UnicodeDecodeError: gbk codec cant decode byte 0x80 in position 205: illegal multibyte sequence" “UnicodeDecodeError: cp950 codes cant decode byte oxc3 in position 25402: illegal multibyte …

java面试题2 牛客:定义类中成员变量时不可能用到的修饰是

懵逼树上懵逼果&#xff0c;懵逼树下你和我 第二题 定义类中成员变量时不可能用到的修饰是&#xff08;&#xff09; A final B void C protected D static 一看到这道题&#xff0c;有点慌了&#xff0c;直接选c&#xff0c;很显然&#xff0c;我又错了&#xff0c;难题…

[Leetcode][第257题][JAVA][二叉树的所有路径][BFS][DFS]

【问题描述】[简单] 【解答思路】 1. DFS 时间复杂度&#xff1a;O(N^2) 空间复杂度&#xff1a;O(N^2) class Solution {public List<String> binaryTreePaths(TreeNode root) {List<String> paths new ArrayList<String>();constructPaths(root, "…

Depth-first Search深度优先搜索专题1

104. Maximum Depth of Binary Tree 思路&#xff1a;顺着树的一个分支一直数层数直到叶子节点。DFS的思路。这个题目可以练习的是递归转迭代。 代码 695. Max Area of Island 思路&#xff1a;遇到点i,j;如果grid[i][j]没有被访问过&#xff0c;并且等于1&#xff0c;那么…

作用域和数组

作用域 就是一对大括号{} 作用域的的特点在作用域内部声明的变量,只能在这个作用域使用 数组 array 一组类型相同的数(值)1.数组一旦定义好长度就不能改变了2.数组访问摸个数据的方式 数组以下标(index)的方式获取数据 数组的声明 定义10个学员的成绩/两只之间以,隔开…

java面试题3 牛客:下面有关jdbc statement的说法错误的是

下面有关jdbc statement的说法错误的是&#xff1f; A JDBC提供了Statement、PreparedStatement 和 CallableStatement三种方式来执行查询语句&#xff0c; 其中 Statement 用于通用查询&#xff0c; PreparedStatement 用于执行参数化查询&#xff0c;而 CallableStatement则…

Depth-first Search深度优先搜索专题2

199. Binary Tree Right Side View 思路&#xff1a;想要得到树的每一层最右侧元素值&#xff0c;用BFS最方便。先访问左节点再访问右节点&#xff0c;最后访问的一个值就是留下的值。 想要DFS的思路也可以。只是一定要访问所有节点。 代码 491 Increasing Subsequences …

[Leetcode][第60题][JAVA][第k个排列][回溯][DFS][剪枝]

【问题描述】[中等] 【解答思路】 1. 回溯搜索算法 剪枝 &#xff0c;直接来到叶子结点 时间复杂度&#xff1a;O(N^2) 空间复杂度&#xff1a;O(N) import java.util.Arrays;public class Solution {/*** 记录数字是否使用过*/private boolean[] used;/*** 阶乘数组*/priv…

java面试题4 牛客:运行下列代码,运行结果为

第四题&#xff1a;运行下列代码&#xff0c;运行结果为&#xff08;&#xff09; class A {public A() {System.out.println("class A");}{ System.out.println("Im A class"); }static { System.out.println("class A static"); }}public cla…

蓝桥杯-每日刷题-026

奖金提成 一、题目要求 题目描述 企业发放的奖金根据利润提成。 利润I低于100000元的奖金可提10%&#xff1b; 利润高于100000元&#xff0c;低于200000元的&#xff08;100000<I<200000&#xff09;时,低于100000元的部分按10%提成&#xff0c;高于100000元的部分&…

优秀技术人的管理陷阱

写在前面 几乎每一位做软件开发的技术人&#xff0c;都听过类似这样的话&#xff0c;“30 岁以后&#xff0c;身体比不过 20 几岁的年轻人&#xff0c;需要转型做管理了”。这句话理解起来是没有问题的。的确年龄越大身体就越比不过自己 20 多岁的时候&#xff0c;拼体力不够&a…

机器学习中的数学--数学知识复习

机器学习 机器学习三个部分&#xff1a;编程能力数学统计知识业务知识 机器学习分类 1 监督学习&#xff1a;例如分类、房价预测 2 无监督学习&#xff1a;例如聚类 3 强化学习&#xff1a;例如动态系统、机器人控制系统 机器学习算法 是否连续无监督有监督连续聚类 &am…

【数据结构与算法】【算法思想】拓扑排序

一、拓扑排序 拓扑排序是基于依赖关系的节点&#xff0c;根据依赖关系而生成的序列。节点和依赖关系往往要生成有向无环图。类似的问题有&#xff1a;穿衣服裤子的先后关系&#xff0c;生成穿衣序列/专业课程与前置课程形成的课程学习序列/代码编译依赖关系形成的编译顺序序列…

java面试题5 牛客:下列关于JavaBean的说法正确的是:

下列关于JavaBean的说法正确的是&#xff1a; A:Java文件与Bean所定义的类名可以不同&#xff0c;但一定要注意区分字母的大小写 B:在JSP文件中引用Bean&#xff0c;其实就是用语句 C&#xff1a;被引用的Bean文件的文件名后缀为.java D:Bean文件放在任何目录下都可以被引用…