有时候想给输入系统的驱动写个应用也会发现无从下手,今天就对触摸屏写应用进行分析

输入系统调用流程

调用过程

有各种输入设备,就拿触摸屏举例,各种触摸屏经过驱动适配后,会给上层提供一个统一的接口
就便于app直接调用

1.开始运行app open设备节点,暂时没有数据,app进行休眠
2.按下屏幕产生中断,驱动直接获得数据,变成标准的输入事件,发送给输入核心层
3.核心层把数据变成标准格式,传递给事件层
4.事件层唤醒app,把之前标准格式的数据发送给app

传输的数据格式

根据上面所说,在核心层把各种设备传递数据变成统一的格式
把整理过的数据,在交给app处理----> 所以是什么样格式的数据

对于驱动

在驱动里面我们通常用一个 input_dev 结构体来表示输入设备
我们在 include/linux/input.h
下面的数组表示一个大类的事件,比如绝对位移事件里面的会分为 x位移,y位移,z位移等
用这个数组表明这些驱动支持什么事件

app接收到的转换后数据的格式

得到的是一个个输入事件,也就是一个个 struct input_event 结构体
我们在 /include/uapi/linux/input.h

我们在该文件上 找到 #include “input-event-codes.h” ,所以这些事件值的定义就在这个头文件里

type 表示对应的是哪个事件

比如 EV_KEY 表示按键类、EV_REL 表示相对位移(比如鼠标),EV_ABS 表示绝对位置(比如触摸屏)

/** Event types*/#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
#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)
code:表示该类事件下的哪一个事件

比如对于 EV_KEY(按键)类事件,它表示键盘。键盘上有很多按键,比如数字键 1、2、3,字母键 A、B、 C 里等

value:表示事件值

对于按键,它的 value 可以是 0(表示按键被按下)、1(表示按键被松开)、2(表示长按);
对于触摸屏,它的 value 就是坐标值、压力值。

最后同步

当上报完成的时候,驱动会上报一个同步事件,表明自己已经上报完成
同步事件也是一个 input_event 结构体,它的 type、code、value 三项都是 0。

查看一个驱动对app发送的信息

cat /proc/bus/input/devices

驱动信息对照

查看这个驱动对应的信息,方便我们app编写


最左边的意思

I:id of the device(设备 ID)

该参数由结构体 struct input_id 来进行描述,驱动程序中会定义这样的结构体:

N:name of the device

设备名称

P:physical path to the device in the system hierarchy

系统层次结构中设备的物理路径。

S:sysfs path

位于 sys 文件系统的路径

U:unique identification code for the device(if device has it)

设备的唯一标识码

H:list of input handles associated with the device.

与设备关联的输入句柄列表。
比如 H: Handlers=kbd event1 evbug
对应的设备节点就是 /dev/input/event1

B:bitmaps(位图)

PROP:device properties and quirks(设备属性)
EV:types of events supported by the device(设备支持的事件类型)
KEY:keys/buttons this device has(此设备具有的键/按钮)
MSC:miscellaneous events supported by the device(设备支持的其他事件)
LED:leds present on the device(设备上的指示灯)

EV 表示支持什么样的输入事件

从图上看EV==b 翻译过b的二进制是0x1011 也就是0,1,3位为1,支持下表里面的
0,1,3事件 —> EV_SYN,EV_KEY,EV_ABS事件

B: ABS=2658000 3
它表示该设备支持 EV_ABS 这一类事件中的哪一些事件。这是 2 个 32 位的数字:0x2658000、0x3,高位
在前低位在后,组成一个 64 位的数字:“0x2658000,00000003”,数值为 1 的位有:0、1、47、48、50、53、 54,即:0、1、0x2f、0x30、0x32、0x35、0x36

app接收的数据格式

以触摸屏收到的数据为例子

上图中的 type 为 3,对应 EV_ABS;code 为 0x35 对应 ABS_MT_POSITION_X;code 为 0x36 对应
ABS_MT_POSITION_Y。
上图中还发现有 2 个同步事件:它的 type、code、value 都为 0。表示电容屏上报了 2 次完整的数据。

app编写

从上面的分析开始,现在可以写一个app来获取信息了

app读取设参数和信息

想操控一个输入系统驱动 肯定要用到 ioctl 函数 所以具体要传入什么信息呢
一般我们用到的是 evdev.c 这个文件,在里面寻找ioctl函数调用方法

//把函数截取一部分
//根据这个函数,我们就知道ioctl函数要传入的参数是什么
//比如cmd传入EVIOCGID,和一个struct input_id,input_id就能收到参数
static long evdev_do_ioctl(struct file *file, unsigned int cmd,void __user *p, int compat_mode)
{struct evdev_client *client = file->private_data;struct evdev *evdev = client->evdev;struct input_dev *dev = evdev->handle.dev;struct input_absinfo abs;struct input_mask mask;struct ff_effect effect;int __user *ip = (int __user *)p;unsigned int i, t, u, v;unsigned int size;int error;/* First we check for fixed-length commands */switch (cmd) {case EVIOCGVERSION:return put_user(EV_VERSION, ip);case EVIOCGID:if (copy_to_user(p, &dev->id, sizeof(struct input_id)))return -EFAULT;return 0;case EVIOCGREP:if (!test_bit(EV_REP, dev->evbit))return -ENOSYS;if (put_user(dev->rep[REP_DELAY], ip))return -EFAULT;if (put_user(dev->rep[REP_PERIOD], ip + 1))return -EFAULT;return 0;

APP非阻塞(NO_BLOCK)方式读取数据(查询)

APP 调用 open 函数时,传入“O_NONBLOCK”表示“非阻塞”。 APP 调用 read 函数读取数据时,如果驱动程序中有数据,那么 APP 的 read 函数会返回数据,否则也会
立刻返回错误。

APP阻塞方式读取数据(休眠-唤醒方式)

APP 调用 open 函数时,不要传入“O_NONBLOCK”。
APP 调用 read 函数读取数据时,如果驱动程序中有数据,那么 APP 的 read 函数会返回数据;否则 APP
就会在内核态休眠,当有数据时驱动程序会把 APP 唤醒,read 函数恢复执行并返回数据给 APP。

APP的 POLL/SELECT 方式

简单地说,它们就是“定个闹钟”:在调用 poll、select 函数时可以传入“超时时间”。在这段时间内,
条件合适时(比如有数据可读、有空间可写)就会立刻返回,否则等到“超时时间”结束时返回错误。
APP 先调用 open 函数时。
APP 不是直接调用 read 函数,而是先调用 poll 或 select 函数,这 2 个函数中可以传入“超时时间”。
它们的作用是:如果驱动程序中有数据,则立刻返回;否则就休眠。在休眠期间,如果有人操作了硬件,驱
动程序获得数据后就会把 APP 唤醒,导致 poll 或 select 立刻返回;如果在“超时时间”内无人操作硬件,
则时间到后 poll 或 select 函数也会返回。APP 可以根据函数的返回值判断返回原因:有数据?无数据超时
返回?
APP 根据 poll 或 select 的返回值判断有数据之后,就调用 read 函数读取数据时,这时就会立刻获得
数据

APP的异步通知

所谓同步,就是“你慢我等你”。
那么异步就是:你慢那你就自己玩,我做自己的事去了,有情况再通知我。
所谓异步通知,就是 APP 可以忙自己的事,当驱动程序用数据时它会主动给 APP 发信号,这会导致 APP
执行信号处理函数。
APP处理流程是
① 谁发:驱动程序发
② 发什么:信号
③ 发什么信号:SIGIO 表示有驱动的io消息信号
④ 怎么发:内核里提供有函数
⑤ 发给谁:APP,APP 要把自己告诉驱动
⑥ APP 收到后做什么:执行信号处理函数
⑦ 信号处理函数和信号,之间怎么挂钩:APP 注册信号处理函数
想知道获得到什么信号 —> 在signal.h 里面进行分析

驱动程序通知 APP 时,它会发出“SIGIO”这个信号,表示有“IO 事件”要处理。
就 APP 而言,你想处理 SIGIO 信息,那么需要提供信号处理函数,并且要跟 SIGIO 挂钩。这可以通过一
个 signal 函数来“给某个信号注册处理函数”,用法如下:

#include <signal.h>
typedef void (*sighandle_t)(int); //handle处理函数
sighandler_t signal(int signum,sighandler_t handler)// 哪一个信号   信号处理函数

除了注册 SIGIO 的处理函数,APP 还要做什么事?想想这几个问题:
① 内核里有那么多驱动,你想让哪一个驱动给你发 SIGIO 信号?
APP 要打开驱动程序的设备节点。
② 驱动程序怎么知道要发信号给你而不是别人?
APP 要把自己的进程 ID 告诉驱动程序。
③ APP 有时候想收到信号,有时候又不想收到信号:
应该可以把 APP 的意愿告诉驱动:设置 Flag 里面的 FASYNC 位为 1,使能“异步通知”

app编写

app–>ioctl 读取输入设备的数据


#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>/* ./01_get_input_info /dev/input/event0 */
int main(int argc, char **argv)
{int fd;int err;int len;int i;unsigned char byte;int bit;struct input_id id;unsigned int evbit[2];char *ev_names[] = {"EV_SYN ","EV_KEY ","EV_REL ","EV_ABS ","EV_MSC ","EV_SW  ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","EV_LED ","EV_SND ","NULL ","EV_REP ","EV_FF  ","EV_PWR ",};if (argc != 2){printf("Usage: %s <dev>\n", argv[0]);return -1;}fd = open(argv[1], O_RDWR);if (fd < 0){printf("open %s err\n", argv[1]);return -1;}err = ioctl(fd, EVIOCGID, &id);if (err == 0){printf("bustype = 0x%x\n", id.bustype );printf("vendor    = 0x%x\n", id.vendor  );printf("product = 0x%x\n", id.product );printf("version = 0x%x\n", id.version );}len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);if (len > 0 && len <= sizeof(evbit)){printf("support ev type: ");for (i = 0; i < len; i++){byte = ((unsigned char *)evbit)[i];for (bit = 0; bit < 8; bit++){if (byte & (1<<bit)) {printf("%s ", ev_names[i*8 + bit]);}}}printf("\n");}return 0;
}

app的阻塞和非阻塞编写


#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>/* ./01_get_input_info /dev/input/event0 noblock */
int main(int argc, char **argv)
{int fd;int err;int len;int i;unsigned char byte;int bit;struct input_id id;unsigned int evbit[2];struct input_event event;char *ev_names[] = {"EV_SYN ","EV_KEY ","EV_REL ","EV_ABS ","EV_MSC ","EV_SW ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","EV_LED ","EV_SND ","NULL ","EV_REP ","EV_FF  ","EV_PWR ",};if (argc < 2){printf("Usage: %s <dev> [noblock]\n", argv[0]);return -1;}if (argc == 3 && !strcmp(argv[2], "noblock")){fd = open(argv[1], O_RDWR | O_NONBLOCK);}else{fd = open(argv[1], O_RDWR);}if (fd < 0){printf("open %s err\n", argv[1]);return -1;}err = ioctl(fd, EVIOCGID, &id);if (err == 0){printf("bustype = 0x%x\n", id.bustype );printf("vendor   = 0x%x\n", id.vendor  );printf("product = 0x%x\n", id.product );printf("version = 0x%x\n", id.version );}len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);if (len > 0 && len <= sizeof(evbit)){printf("support ev type: ");for (i = 0; i < len; i++){byte = ((unsigned char *)evbit)[i];for (bit = 0; bit < 8; bit++){if (byte & (1<<bit)) {printf("%s ", ev_names[i*8 + bit]);}}}printf("\n");}while (1){len = read(fd, &event, sizeof(event));if (len == sizeof(event)){printf("get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);}else{printf("read err %d\n", len);}}return 0;
}

app异步编写


#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>/* ./01_get_input_info /dev/input/event0 */
int main(int argc, char **argv)
{int fd;int err;int len;int ret;int i;unsigned char byte;int bit;struct input_id id;unsigned int evbit[2];struct input_event event;struct pollfd fds[1];  //设置poll数组的大小(控制几个设备?)nfds_t nfds = 1; //再次表明设备的个数char *ev_names[] = {"EV_SYN ","EV_KEY ","EV_REL ","EV_ABS ","EV_MSC ","EV_SW   ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","EV_LED ","EV_SND ","NULL ","EV_REP ","EV_FF  ","EV_PWR ",};if (argc != 2){printf("Usage: %s <dev>\n", argv[0]);return -1;}fd = open(argv[1], O_RDWR | O_NONBLOCK);if (fd < 0){printf("open %s err\n", argv[1]);return -1;}err = ioctl(fd, EVIOCGID, &id);if (err == 0){printf("bustype = 0x%x\n", id.bustype );printf("vendor   = 0x%x\n", id.vendor  );printf("product = 0x%x\n", id.product );printf("version = 0x%x\n", id.version );}len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);if (len > 0 && len <= sizeof(evbit)){printf("support ev type: ");for (i = 0; i < len; i++){byte = ((unsigned char *)evbit)[i];for (bit = 0; bit < 8; bit++){if (byte & (1<<bit)) {printf("%s ", ev_names[i*8 + bit]);}}}printf("\n");}while (1){fds[0].fd = fd; //监测的文件fds[0].events  = POLLIN; //触发的事件fds[0].revents = 0;   //清零ret = poll(fds, nfds, 5000);if (ret > 0){if (fds[0].revents == POLLIN){while (read(fd, &event, sizeof(event)) == sizeof(event)){printf("get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);}}}else if (ret == 0){printf("time out\n");}else{printf("poll err\n");}}return 0;
}

基于触摸屏的 输入系统应用分析相关推荐

  1. Java、JSP基于JavaEE的家庭影院系统

    随着科技的发展与进步,人们生活水平开始逐步提高,对电影的有了更高的需求.在传统的电视中,观众完全是被动的,观看什么电影完全看电影院的安排,无法体现信息时代人性化的选择,加上现在城市人们的生活节奏逐渐加 ...

  2. 计算机毕设之基于java ee家庭影院系统

    「代码+论文+PPT」免费下载链接: http://106.55.47.97/host-pictures/java.png 概述 随着科技的发展与进步,人们生活水平开始逐步提高,对电影的有了更高的需求 ...

  3. 基于matlab的语音信号基本处理系统,基于matlab的语音信号处理及分析

    内容简介: 毕业设计 基于matlab的语音信号处理及分析(共19页,8147字) 引言 数字信号处理的主要研究对象是数字信号,且是采用运算的方法达到处理的目的的,因此,其实现方法,基本上分成两种实现 ...

  4. 基于大数据的网站日志分析系统

    本文没有任何代码,只有各个模块工作的大体机制和整体流程.算是一个科普文吧,我也对原理一知半解. 基于大数据的网站日志分析系统 1. 日志数据格式 1.1 访问日志 1.1.1 log_format 1 ...

  5. matlab为什么要升维数,基于MATLAB和升维投影法的手写字符识别输入系统

    256 现代交际·2019年6期 作者简介:王思达,长春师范大学学生,研究方向:计算机科学与技术:刘勇,长春师范大学学生,研究方向:计算机科学与技术:袁汇灵, 长春师范大学学生,研究方向:计算机科学与 ...

  6. 基于matlab的音频信号处理系统,毕业设计-基于matlab的语音信号处理及分析

    资料简介 毕业设计 基于matlab的语音信号处理及分析(共19页,8147字) 引言 数字信号处理的主要研究对象是数字信号,且是采用运算的方法达到处理的目的的,因此,其实现方法,基本上分成两种实现方 ...

  7. 基于flask徐州市天气信息可视化分析系统-计算机毕设 附源码 04600

    基于flask徐州市天气信息可视化分析系统 摘 要 信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最 ...

  8. 基于matlab的手写输入板,基于MATLAB和升维投影法的手写字符识别输入系统

    王思达 刘勇 袁汇灵 摘要:互动式电子白板最早由加拿大SMART Technologies Inc在1331年发明.其最早的用途是会议与培训,并在随后进军到了教育领域.在国内,随着教育正逐渐向数字化. ...

  9. 基于hadoop的电商销售预测分析系统HDFS+MapReduce+springboot或springcloud+Echarts

    基于hadoop的电商销售预测分析系统 使用分布式文件存储系统HDFS+mapreduce+springboot和springcloud+Echarts实现的简单的电商销售数据预测分析系统. 主要通过 ...

最新文章

  1. 手机+笔记本上NET网教程--5步让你轻松上网
  2. OpenCV计算机视觉编程攻略之提取图片轮廓-使用Canny函数
  3. 从零开始小说 html,从零开始的HTML生活
  4. 双频无线网安装设置(5g ) for linux
  5. asp.net DataGridTree表格树控件 下拉树 DropTree c# .net
  6. 安卓案例:网格布局实现计算器界面
  7. python爬取cctalk视频_新媒体编辑怎么批量爬取数据
  8. python4k高清图片_第一次接触,尝试用python抓取国外4k高清图像数据,真方便
  9. EOS 创建钱包与账户
  10. SRIO系统初始化过程和路由配置
  11. 日常工作常用的几款小工具
  12. 《移动互联:用户体验设计指南》读书笔记4——移动UX模式
  13. 目标文件夹访问被拒绝,您需要权限来执行此操作
  14. BitLocker加密怎么解除?
  15. 2023年4月动漫新番最新资讯已公布52部!
  16. SSO单点登录解决方案——Filter方式
  17. 机房的正确布线方法,四招轻松搞定!
  18. 网上买保险靠谱吗?线上保险和线下保险的区别在哪?
  19. Hello Riak
  20. laravel 隐藏 x-powered-by

热门文章

  1. Linux - Ubuntu 18.04 网络配置(YAML)
  2. cmd界面的常用命令
  3. TreeSet的理解
  4. Dicom文件转mhd,raw文件格式
  5. 人鱼小姐主题曲我痛苦的爱铃声 人鱼小姐主题曲我痛苦的爱手机...
  6. 设计模式——适配器模式(附代码示例)
  7. 计算机节能管理,Win7电源管理在哪?Win7电源管理为电脑配置节能方案教程
  8. 横扫6个SOTA,吊打强化学习,谷歌最强行为克隆算法登CoRL顶会,机器人干活10倍速
  9. 在vue中如何高性能渲染十万条数据(虚拟列表)并且增加个搜索框可以搜索到这些数据
  10. wait millis 60010, active 20, maxActive 20 处理 com.alibaba.druid.pool.GetConnectionTimeout