Linux输入子系统(一) _驱动编码
输入设备都有共性:中断驱动+字符IO,基于分层的思想,Linux内核将这些设备的公有的部分提取出来,基于cdev提供接口,设计了输入子系统,所有使用输入子系统构建的设备都使用主设备号13,同时输入子系统也支持自动创建设备文件,这些文件采用阻塞的IO读写方式,被创建在"/dev/input/"下。如下图所示。内核中的输入子系统自底向上分为设备驱动层,输入核心层,事件处理层。由于每种输入的设备上报的事件都各有不同,所以为了应用层能够很好识别上报的事件,内核中也为应用层封装了标准的接口来描述一个事件,这些接口在"/include/upai/linux/input"中。
- 设备驱动层是具体硬件相关的实现,也是驱动开发中主要完成的部分,
- 输入核心层主要提供一些API供设备驱动层调用,通过这些API设备驱动层上报的数据就可以传递到事件处理层,
- 事件处理层负责创建设备文件以及将上报的事件传递到用户空间,
input的使用
input对象描述了一个输入设备,包括它可能上报的事件,这些事件使用位图来描述,内核提供的相应的工具帮助我们构建一个input对象,大家可以参考内核文档"Documentation/input/input-programming.txt",里面对于input子系统的使用有详细的描述。
//input设备对象
121 struct input_dev {
122 const char *name;
129 unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
130 unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
131 unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
132 unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
133 unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
134 unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
135 unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
136 unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
137 unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
155
162 unsigned long key[BITS_TO_LONGS(KEY_CNT)];
163 unsigned long led[BITS_TO_LONGS(LED_CNT)];
164 unsigned long snd[BITS_TO_LONGS(SND_CNT)];
165 unsigned long sw[BITS_TO_LONGS(SW_CNT)];
166
172 struct input_handle __rcu *grab;
179
180 struct device dev;
181
182 struct list_head h_list;
183 struct list_head node;
190 };
struct input_dev
--122--> 这个name不是设备名,input子系统的设备名在子系统源码中指定的,不是这。
--129--> 设备支持的输入事件位图,EV_KEY,EV_REL, etc
--130--> 对于按键事件,设备支持的输入子事件位图
--132--> 对于相对坐标事件,设备支持的相对坐标子事件位图
--133--> 对于绝对坐标事件,设备支持的绝对坐标子事件位图
--134--> 混杂设备的支持的子事件位图
--180-->表示这是一个device。
--182-->h_list是用来链接相关handle的链表
--183-->node用来链接其他input_dev的链表
分配/释放
//drivers/input/input.c
//创建一个input对象
struct input_dev *input_allocate_device(void);
//释放一个input对象
void input_free_device(struct input_dev *dev);
初始化
初始化一个input对象是使用input子系统编写驱动的主要工作,内核在头文件"include/uapi/linux/input.h"中规定了一些常见输入设备的常见的输入事件,这些宏和数组就是我们初始化input对象的工具。这些宏同时用在用户空间的事件解析和驱动的事件注册,可以看作是驱动和用户空间的通信协议,所以理解其中的意义十分重要。在input子系统中,每一个事件的发生都使用事件(type)->子事件(code)->值(value)三级来描述,比如,按键事件->按键F1子事件->按键F1子事件触发的值是高电平1。注意,事件和子事件和值是相辅相成的,只有注册了事件EV_KEY,才可以注册子事件BTN_0,也只有这样做才是有意义的。
下面就是内核约定的事件类型,对应应用层的事件对象的type域
下面这些是按键子事件的类型,可以看到对PC键值的定义
除了对常用的事件进行描述,内核同样提供了工具将这些事件正确的填充到input对象中描述事件的位图中。
//第一种
//这种方式非常适合同时注册多个事件
button_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
button_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |BIT_MASK(BTN_RIGHT) |BIT_MASK(BTN_MIDDLE);
//第二种
//通常用于只注册一个事件
set_bit(EV_KEY,button_dev.evbit);
set_bit(BTN_0,button_dev.keybit);
注册/注销
初始化好了一个input对象,接下来就需要将其注册到内核
//注册input对象到内核
int input_register_device(struct input_dev *dev);
//从内核注销一个input对象
void input_unregister_device(struct input_dev *dev);
驱动层报告事件
在合适的时机(由于输入最终是中断表示的,所以通常在驱动的中断处理函数中)驱动可以将注册好的事件上报,且可以同时上报多个事件,下面是内核提供的API
//上报指定的事件+子事件+值
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_abs(struct input_dev *dev,unsigned int code,int value);
//报告同步事件
void input_report_rel(struct input_dev *dev,unsigned int code,int value);
//同步所有的上报
void input_sync(struct input_dev *dev);
上报事件有2点需要注意:
- report函数们并不会真的上报,只是准备上报,sync才会真的将刚刚report的事件真的上报搭input核心
- input核心会进行裁决再上报的事件处理层,所以对于按键事件,一定要先报1再报0(或者反过来),不能只report 1或0, 这样核心会认为是一个事件被误触发了多次而只上报一次,虽然我们真的按下了多次。
应用层解析
事件处理层最终会将驱动sync一次时所有report的事件组织成一个struct input_value[]的形式上报到应用层,在应用层从相应的设备文件中获取上报的事件的时候,需要注意:
- 收到数组元素的数量会比底层多一个空元素,类似于写of_device_id[]时最后的空元素,这点应用层在解析的时候需要注意。
- 事件处理层并不会缓存收到的事件,如果有新的事件到来,即使旧的事件没有被读取,也会被覆盖,所以应用程序需要及时读取。
前文已经说过,"include/uapi/linux/input.h"中的宏是应用层和驱动层共用的通信协议,所以应用层在解析收到的struct input_value对象的时候,只需要"include <linux/input.h>"即可使用其中的宏。
/*
* The event structure itself
*/
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
input分析
上文已经说过,input子系统使用三层结构来实现驱动事件到应用层的传递。具体的,这三个层次每一个层次都由一条结构体链表组成,在设备驱动层,核心结构体是input_dev;在input核心层,是input_handle;在事件处理层,是input_handler。内核通过链表和指针将三者结合到一起,最终实现了input_dev和input_handler的多对多的映射关系,这种关系可用下图简单描述。
模板
下面的这个模板首先使用input子系统上报按键事件,然后在应用层读取。
input按键设备驱动
/{
key@26{
compatible = "xj4412,key";
interrupt-parent = <&gpx1>;
interrupts = <2 2>;
};
};
static struct input_dev *button_dev;
static int button_irq;
static int irqflags;
static irqreturn_t button_interrupt(int irq, void *dummy)
{
input_report_key(button_dev, BTN_0, 0);
input_report_key(button_dev, BTN_0, 1);
input_sync(button_dev);
return IRQ_HANDLED;
}
static int button_init(void)
{
request_irq(button_irq, button_interrupt,irqflags, "button", NULL)) ;
button_dev = input_allocate_device();
button_dev->name = "button";
button_dev->evbit[0] = BIT_MASK(EV_KEY);
button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
input_register_device(button_dev);
return 0;
}
static int button_exit(void)
{
input_free_device(button_dev);
free_irq(button_irq, button_interrupt);
return 0;
}
static int key_probe(struct platform_device *pdev)
{
struct resource *irq_res;
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if(irq_res){
button_irq = irq_res->start;
irqflags = irq_res->flags & IRQF_TRIGGER_MASK;
}else{
return -EINVAL;
}
return button_init();
}
static int key_remove(struct platform_device *dev)
{
return button_exit();
}
struct of_device_id of_tbl[] = {
{.compatible = "xj4412,key",},
{},
};
MODULE_DEVICE_TABLE(of, of_tbl);
struct platform_driver key_drv = {
.probe = key_probe,
.remove = key_remove,
.driver.name = "keydrv",
.driver.of_match_table = of_tbl,
};
module_platform_driver_register(key_drv);
MODULE_LICENSE("GPL");
应用层获取键值
#include <linux/input.h>
struct input_event {
struct timeval time;
unsigned short type;
unsigned short code;
int value;
};
int main(int argc, char * const argv[])
{
int fd = 0;
struct input_event event[3] = {0}; //3!!!,驱动上传了2个事件,第三个用来装空元素
int ret = 0;
fd = open(argv[1],O_RDONLY);
while(1){
ret = read(fd,&event,sizeof(event));
printf("ret:%d,val0:%d,val1:%d,val12:%d\n",ret,event[0].value,event[1].value,event[2].value); //2!!!,最后一个是空
sleep(1);
}
return 0;
}
Linux输入子系统(一) _驱动编码的更多相关文章
- linux输入子系统之按键驱动
上一节中,我们讲解了Linux input子系统的框架,到内核源码里详细分析了输入子系统的分离分层的框架等. 上一节文章链接:http://blog.csdn.net/lwj103862095/ar ...
- linux输入子系统简述【转】
本文转载自:http://blog.csdn.net/xubin341719/article/details/7678035 1,linux输入子系统简述 其实驱动这部分大多还是转载别人的,linux ...
- Linux输入子系统详解
input输入子系统框架 linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(Input ...
- linux输入子系统(input subsystem)之evdev.c事件处理过程
1.代码 input_subsys.drv.c 在linux输入子系统(input subsystem)之按键输入和LED控制的基础上有小改动,input_subsys_test.c不变. input ...
- Linux输入子系统(转)
Linux输入子系统(Input Subsystem) 1.1.input子系统概述 输入设备(如按键,键盘,触摸屏,鼠标等)是典型的字符设备,其一般的工作机制是低层在按键,触摸等动作发生时产生一个中 ...
- Linux输入子系统框架分析(1)
在Linux下的输入设备键盘.触摸屏.鼠标等都能够用输入子系统来实现驱动.输入子系统分为三层,核心层和设备驱动层.事件层.核心层和事件层由Linux输入子系统本身实现,设备驱动层由我们实现.我们在设备 ...
- linux输入子系统
linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(InputCore)和输入子系统设备驱 ...
- linux输入子系统概念介绍
在此文章之前,我们讲解的都是简单的字符驱动,涉及的内容有字符驱动的框架.自动创建设备节点.linux中断.poll机制.异步通知.同步互斥.非阻塞.定时器去抖动. 上一节文章链接:http://blo ...
- 7.Linux 输入子系统分析
为什么要引入输入子系统? 在前面我们写了一些简单的字符设备的驱动程序,我们是怎么样打开一个设备并操作的呢? 一般都是在执行应用程序时,open一个特定的设备文件,如:/dev/buttons .... ...
随机推荐
- [无关IT]就这样在凌晨写一篇吧~
由于新浪博客广告实在太嚣张,自己也都是转载,故决定搬家至此,一改只转不写的习惯T^T,争取记录一下自己的小成长~日后有时间把脑子里的小东西一点点写出来~(好可怕的说)... 好了,睡了!各位爷早睡~ ...
- tools_list
http://files.cnblogs.com/files/yansc/ExportQingtaoImage.rar
- Mybatis 模糊查询 中文问题
IDE编码:GBK ,换成UTF-8也不行! @Testpublic void testSearchStudents() {logger.info("查询学生(带条件)");Map ...
- unicode转GBK,GNK转unicode,解决FATFS中文码表占用ROM问题(转)
源:unicode转GBK,GNK转unicode,解决FATFS中文码表占用ROM问题 之前一直使用的512KB ROM的STM32,但是最近使用的只有128KB,想用FATFS显示支持长文件名,发 ...
- java制作图片水印
1.创建缓存图片对象 2.创建Java绘图工具对象 3.将原图绘制到缓存图片对象 4.使用工具将水印绘制到缓存图片对象 5.创建图片编码工具类 6.输出缓存图片对象到目标图片文件 BufferedIm ...
- 如何使用Grunt(好文)
Grunt 是什么? Grunt 基于Node.js之上,是一个以任务处理为基础的命令行工具,可以减少优化开发版本为发布版本所需的人力和时间,从而加速开发流程.它的工作原理是把这 些工作整合为不同的任 ...
- 前言《iOS网络高级编程:iPhone和iPad的企业应用开发》(书籍学习)
本书内容: 在客户端设备与服务器之间执行HTTP请求 管理客户端设备与服务器之间的数据负载 处理HTTP请求的错误 保护网络通信 改进网络通信的性能 执行Socket层的通信 实现推送通知 单个设备上 ...
- HTML 脚本
JavaScript 使 HTML 页面具有更强的动态和交互性. 在线实例 插入一段脚本如何将脚本插入 HTML 文档. 使用 <noscript> 标签如何应对不支持脚本或禁用脚本的浏览 ...
- 用SWF来代替传统的帧动画
一般的帧动画是有两大缺点: 1.资源浪费,包大 2.很难实现平滑过渡 特别对于GIF,还会存在噪点问题,但是SWF利用自身的优势,不仅有现成的编辑器,而且还有矢量动画,补间动画等,大大 降低了资源的大 ...
- Bootstrap入门(二十三)JS插件1:模态框
Bootstrap入门(二十三)JS插件1:模态框 1.静态实例 2.动态实例 3.模态框的尺寸和效果 4.包含表单的模态框 模态框经过了优化,更加灵活,以弹出对话框的形式出现,具有最小和最实用的功能 ...