输入系统应用编程

1 输入系统框架及调试

说输入系统框架前,先了解什么是输入系统?

1.1 什么是输入系统?

⚫ 先来了解什么是输入设备?

常见的输入设备有键盘、鼠标、遥控杆、书写板、触摸屏等等,用户通过这些输入设备与 Linux 系统进行数据交换。

⚫ 什么是输入系统?

输入设备种类繁多,能否统一它们的接口?既在驱动层面统一,也在应用程序层面统一?可以的。 Linux 系统为了统一管理这些输入设备,实现了一套能兼容所有输入设备的框架:输入系统。驱动开发人员基于这套框架开发出程序,应用开发人员就可以使用统一的 API 去使用设备。(API应用编程接口)

1.2 输入系统框架

1.2.1 前言:什么是设备节点?

所谓Linux设备节点,就是应用程序和设备驱动程序沟通的一个桥梁,更是连接内核与用户层的枢纽。人和人之间沟通桥梁是语言。同样,应用程序和设备驱动程序沟通也需要一个桥梁。这个桥梁就是设备节点。

对于Linux系统,所有的IO资源都是文件,包括文件、目录、硬盘、设备等。那么,键盘作为计算机系统中的一款输入设备,操作系统同样也把它抽象了文件,要想获取用户从键盘上输入的数据时,只需要读取键盘提供的设备节点即可。

总结:每个输入设备都有它自己的设备节点,它不能直接把数据给电脑,所有它把它要传给电脑的信息放在设备节点中,电脑通过读取设备节点来知道输入设备要传输的数据。(打个比方,一个中国人,一个外国人,两个人只要一台手机,两者语言不同,两个人想要交流,必须通过手机。这里的手机就是设备节点)

设备节点存放在:/dev/input/event中

1.2.2.输入系统框架

假设用户程序直接访问/dev/input/event0 设备节点,或者使用 tslib访问设备节点,数据的流程如下:

(1)APP 发起读操作,若无数据则休眠;

(2)用户操作设备,硬件上产生中断;

(3)输入系统驱动层对应的驱动程序处理中断: 读取到数据,转换为标准的输入事件,向核心层汇报。 所谓输入事件就是一个“struct input_event”结构体

(4)核心层可以决定把输入事件转发给上面哪个 handler 来处理: 从 handler 的名字来看,它就是用来处输入操作的。有多种 handler,比 如:evdev_handler、kbd_handler、joydev_handler 等等。 最常用的是 evdev_handler:它只是把 input_event 结构体保存在内核 buffer 等,APP 来读取时就原原本本地返回。它支持多个 APP 同时访问输入设备,每个 APP 都可以获得同一份输入事件。 当 APP 正在等待数据时,evdev_handler 会把它唤醒,这样 APP 就可以返 回数据。

(5)APP 对输入事件的处理: APP获得数据的方法有 2 种 : 直 接 访 问 设 备 节 点 ( 比 如 /dev/input/event0,1,2,...),或者通过 tslib、libinput 这类库来间接访 问设备节点。这些库简化了对数据的处理。

1.3 编写APP需要掌握的知识

1.3.1 内核中如何表示一个输入设备

使用input_dev结构体来表示输入设备

1.3.2 APP会得到什么数据(驱动程序上报的数据)

得到一个个输入事件,就是一个个"struct input_event"

输入事件 input_event 中有:type(哪类事件)、code(哪个事件)、 value(事件值)

(1)type(哪类事件)

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

(2)code(哪个事件)

就是表示tpye(该类事件)下的哪一个事件

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

对于触摸屏,它提供的是绝对位置信息,有 X 方向、Y 方向,还有压力值。 所以 code值

(3)value(事件值)

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

补充:APP 读取数据时,可以得到一个或多个数据,比如一个触摸屏的一个触点会 上报 X、Y 位置信息,也可能会上报压力值。

◼ APP 怎么知道它已经读到了完整的数据?

驱动程序上报完一系列的数据后,会上报一个“同步事件”,表示数据上报完 毕。APP 读到“同步事件”时,就知道已经读完了当前的数据。 同步事件也是一个 input_event 结构体,它的 type、code、value 三项都 是 0。

1.4 调试技巧

技巧1:观看有多少个设备节点

ls /dev/input/* -l

技巧2:如何知道设备几点分别对应哪些设备

cat /proc/bus/input/devices

解释

(1).I表示ID;vendor表示厂家;Product表示产品ID;version表示版本号。

(2).Phys(Physics)物理名字;

(3).N表示Name名字

(4).EV = b                #b的二进制为1011,表示选择事件种类type的第一个(EV_SYN同步事件) 第二个(EV_REL相对事件) 第四个(EV_ABS绝对事件)。

(5).

技巧3.使用命令读取数据

hexdump /dep/input/event0

在开发板上执行上述命令之后,点击按键或是点击触摸屏,就会打印图

注:type,code,value全为0时表示为同步事件,来分隔独立的完全数据。

2 输入系统支持完整的API操作

支持这些机制:阻塞、非阻塞、POLL/SELECT、异步通知

理解APP 访问硬件的 4 种方式:妈妈怎么知道孩子醒了

妈妈怎么知道卧室里小孩醒了? 时不时进房间看一下:查询方式

简单,但是累

进去房间陪小孩一起睡觉,小孩醒了会吵醒她:休眠-唤醒

不累,但是妈妈干不了活了

妈妈要干很多活,但是可以陪小孩睡一会,定个闹钟:poll 方式

要浪费点时间,但是可以继续干活。 妈妈要么是被小孩吵醒,要么是被闹钟吵醒。

妈妈在客厅干活,小孩醒了他会自己走出房门告诉妈妈:异步通知

妈妈、小孩互不耽误。

这 4 种方法没有优劣之分,在不同的场合使用不同的方法。

2.1查询方式(非阻塞)

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

2.2休眠——唤醒方式(阻塞)

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

2.3 POLL/SELECT方式

功能:poll与select机制完全一样,只是APP接口函数不一样。

简单说,它们是”定个闹钟“,在调用POLL,SELECT函数时会传入”超时时间“,在这段时间内,条件合适时(比如有数据可读,有空间可写)就会立刻返回,否则等到”超时时间“结束时返回错误。

使用流程

(1). APP调用open函数。

(2).APP调用poll或select函数,这两个函数可以传入”超时时间“,他们的作用是:如果驱动程序中有数据,则立刻返回,否则就休眠,休眠期间,如果有人操作了硬件,驱动程序获得数据后,就会把APP唤醒,导致poll或select立刻返回。如果在”超时时间“内无人操作硬件,则到时间后,POLL或SELECT函数也会返回。APP可根据函数返回值判断返回原因,有数据?无数据超时返回?

(3).APP根据POLL或SELECT返回值判断数据后,调用read函数读取数据时,返回就会立刻获得数据。

注:POLL或SELECT函数可监测多个文件,可监测多种事件。

POLL函数

int poll(struct pollfd fds,int nfds,int timeout_MS)

参数1:

struct pollfd fds

{

        int fd;                                #想监测哪个文件

        int events;                           #监测文件的哪些事件

        int revents;                          #在poll返回时,要判断状态,这里的revents = event。

                                                    #注:revents=return events

}

参数2:

int nfd                                           #要执行监测文件的number数量

参数3:

int timeout_MS                             #超时时间

函数返回值:如果返回值大于0表期待的事发生了。如果返回值等于0表示超时,如果小于0表发生了错误。

poll监测事件:

事件类型说明
POLLIN        有数据可读(驱动程序有数据到应用程序)
POLLRDNORM等同于POLLIN
POLLRDBANDPriority band data can be read,有优先级较较高的“band data”可读 Linux 系统中很少使用这个事件
POLLRPRI高优先级数据可读
POLLOUT可以写数据
POLLNRNORM等同于 POLLOUT
POLLWRBANDPriority data may be written
POLLERR发生了错误
POLLHUP挂起
POLLNVAL无效的请求,一般是 fd 未 open

2.4异步通知方式

功能:所谓同步,就是”你慢我等你“,异步是:APP可忙自己的事,当驱动程序用数据它会主动给APP发信息,这会导致APP执行信息处理函数。

理解'发信息'相关问题

谁发:驱动程序

发什么:信号

发什么信号:SIGIO

怎么发:内核里有提供函数

发给谁:APP,APP要把自己告诉驱动

APP收到后做什么:执行信号处理函数

信号处理函数和信号之间怎么挂钩:APP注册信号处理函数。

注:Linux中有许多信号,SIGIO是其中之一,用于驱动程序通知APP时。

总结:实现异步通信所需要做的几点:

(1)内核中有许多驱动,如何实现指定哪个驱动发SIGIO?

        :APP打开驱动程序的设备节点·

(2)驱动程序如何知道自己的数据发给谁?

        :APP把自己进程ID告诉驱动

(3)APP接收到SIGIO时,要如何做

        :注册SIGIO的处理函数

(4)APP如何控制是否收到信号?

        :在Flag中的FASYNC为1,使能异步通知

应用编程

(1)编写信号处理函数

signal(SIGIO , my_sig_hindler);

void my_sig_hanlder(int sig)

{

        struct input_event event;

        while(read(fd , event , sizeof(event))) == sizeof(event) )

        {

                printf("get event : type = 0x%x , code = 0x%x , value = 0x%x" , event.type , event.code                                                                 , event.value);

        }

}

(2)打开驱动程序

fd = open(argv[1] , O_RDWR | O_NONBLOCK);

if(fd < 0)

{

        printf("open %s err \n" , argv[1]);

        return 1;

}

(3)把APP的进程号告诉驱动程序

fcntl(fd , F_setown , getpid());

(4)使能”异步通知“

flags = fcntl(fd , F_GETFL);

fcntl(fd , F_SETFL , flags | FASYNC);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值