EDA365电子论坛网

标题: 迅为i.MX6ULL终结者Linux INPUT子系统实验Input子系统 [打印本页]

作者: 孤久厌闹    时间: 2021-1-11 10:36
标题: 迅为i.MX6ULL终结者Linux INPUT子系统实验Input子系统
文章目录! R7 v! F. q6 J8 p
1 input子系统简介$ D/ M) g& _4 b
2 input驱动程序编写流程2 w% z4 L: n1 V9 Y: |$ @
3 input_event结构体
# v% f5 b+ q9 \3 u9 V5 Z. M1 input子系统简介
1 h. }- T+ C) D, d8 Q) cinput 子系统就是管理输入的子系统,和 pinctrl 和 gpio 子系统一样,都是 Linux 内核针对某一类设备而创建的框架。 input子系统处理输入事务,任何输入设备的驱动程序都可以通过input输入子系统提供的接口注册到内核,利用子系统提供的功能来与用户空间交互。输入设备一般包括键盘,鼠标,触摸屏等,在内核中都是以输入设备出现的。
3 g. C. N, C( V' t7 P! tinput子系统是分层结构的,总共分为三层: 硬件驱动层,子系统核心层,事件处理层。
1 S. k& c( G) s/ R(1)硬件驱动层负责操作具体的硬件设备,这层的代码是针对具体的驱动程序的,需要驱动程序的作者来编写。
6 h% |$ w6 `% W9 V) _(2)子系统核心层是链接其他两个层之间的纽带与桥梁,向下提供驱动层的接口,向上提供事件处理层的接口。
$ v; I- [2 K+ g2 f- o( S0 q( r% D! y(3)事件处理层负责与用户程序打交道,将硬件驱动层传来的事件报告给用户程序。1 J3 E$ M. r; A. D5 z
" Z/ n7 x1 N4 C: n* _; P) n7 C
各层之间通信的基本单位就是事件,任何一个输入设备的动作都可以抽象成一种事件,如键盘的按下,触摸屏的按下,鼠标的移动等。事件有三种属性:类型(type),编码(code),值(value),input子系统支持的所有事件都定义在input.h中,包括所有支持的类型,所属类型支持的编码等。事件传送的方向是 硬件驱动层–>子系统核心–>事件处理层–>用户空间。
" T1 `; y, N# x5 L; z  D  r( Q1 \2 input驱动程序编写流程
0 b: C  h% W* b7 X* U* b; a8 F! E首先来看一下在input核心层实现了哪些功能,input核心层文件是input.c,路径:drivers/input/input.c,部分内容如下:
1 m8 O- A! R4 ^) m: H8 j  V/ q' N, y
第2418行,注册了一个input类,在系统启动后会在/sys/class目录下生成一个input类的子目录,如图 2.1所示:# ^, N, R6 }7 A+ ?; M

. R0 L! ^7 h7 j. D第2428、2489行,注册了一个字符设备,所以input子系统本质上也是字符设备驱动,主设备号为 INPUT_MAJOR,INPUT_MAJOR 定义在 include/uapi/linux/major.h 文件中,定义如下:  H' E" s* b$ T+ K
#define INPUT_MAJOR 13. ]2 s4 p# P/ R8 {' B! L) G/ S& _
所以input 子系统的所有设备主设备号都为 13,在使用 input 子系统处理输入设备的时候就不需要去注册字符设备了,我们只需要向系统注册一个 input_device 即可。
1 \. a2 ^+ f! V1、input_dev 结构体
3 G# ]3 X0 `9 I0 [: `, g, Einput_dev结构体是input设备基本的设备结构,每个input驱动程序中都必须分配初始化这样一个结构,结构体定义在 include/linux/input.h 文件中,定义如下:$ ]1 s4 g. b% p2 v) @$ w7 e6 \

$ t4 r1 O  h) @) ]! ~3 ^第 129 行,evbit 表示输入事件类型,可选的事件类型定义在 include/uapi/linux/input.h 文件中,事件类型如下:
( S- p* t1 \( x0 R  @" t; T5 @% Z& O; P! C
根据使用的不同设备选择不同的事件类型,在本章的实验中我们会用到按键设备,那么我们就需要选择EV_KEY 事件类型。0 z+ D2 o! ~- B- \/ H
在看input_dev结构体中的第129~137行的evbit、keybit等成员变量,都是对应的不同事件类型的值。比如按键事件对应的keybit成员,keybit 就是按键事件使用的位图,Linux 内核定义了很多按键值,这些按键值定义在 include/uapi/linux/input.h 文件中,按键值如下:" z# M! o% V" K9 I( ~+ |

2 c3 V& M7 i- L% q3 F2 I- Y当我们编写input设备驱动时需要先创建一个input_dev 结构体变量,但是不用我们手动创建,input子系统提供了下面两个函数用于创建和注销input_dev 结构体变量。
: a! h/ B4 n& o) H% Y1 O# d1 a6 a# d1 ?& `! `9 \
申请完input_dev结构体后,需要进行初始化,根据自己的设备来指定事件类型和事件值,比如按键设备的事件类型是evbit,事件值是keybit。
/ U! c7 `1 I2 A- D* ]7 r6 ninput_dev结构体初始化完成后,使用input_register_device 函数向Linux内核注册input_dev设备。函数原型如下:- g: \: |& M, G+ {
int input_register_device(struct input_dev *dev)* Q- e1 h0 ]* \# C2 W  H% R2 H
dev:要注册的 input_dev 。" b; s7 ]+ E4 E( f
返回值:0,input_dev 注册成功;负值,input_dev 注册失败。
8 h# G. }9 Z- Y; b) Q. E2 P同样的,注销 input 驱动的时候也需要使用 input_unregister_device 函数来注销掉前面注册的 input_dev,input_unregister_device 函数原型如下:( [' g8 s# h/ b, ^2 ~
void input_unregister_device(struct input_dev *dev)
" y5 T; b3 j6 u1 g总结上面的内容,input_dev注册过程分为下面几步:4 O/ D+ g: D% u! T! c
① 首先使用 input_allocate_device 函数申请一个 input_dev。
9 \$ H; k9 L% i' s5 D( `1 x② 初始化 input_dev 的事件类型以及事件值。; N* o2 P8 J* [3 n) ~
③ input_register_device()函数是输入子系统核心(input core)提供的函数。该函数将input_dev结构体注册到输入子系统核心中  ^) u! H6 Y& L1 j5 n: Z3 u' K
④ input_register_device()函数如果注册失败,必须调用input_free_device()函数释放分配的空间。如果该函数注册成功,在卸载函数中应该调用input_unregister_device()函数来注销输入设备结构体。
. C  y8 H0 \) ?9 D: V8 H# j' Oinput_dev注册过程实例代码如下:" J4 D/ z8 W( Z0 W
: E: P4 a9 Z4 Z

$ t" {/ r' b; M: l" h, B: g' _# ^4 o第 10~23 行都是初始化 input 设备事件和按键值,这里用了三种方法来设置事件和按键值。
7 Q  {& [5 _1 _* F2、上报输入事件
" ?4 {2 n1 j9 z在input设备驱动中申请、注册完成input_dev结构体后,还不能正常使用input子系统,因为input设备是输入一些信息,但是Linux内核还不清楚输入的信息表示什么意思,有什么作用,所以我们需要驱动获取到具体的输入值,或者说输入事件,然后将输入事件上报给Linux内核。比如按键设备,我们需要在按键产生后将按键值上报给Linux内核,Linux内核获取到具体的按键值后,才会执行相应的功能。不同的事件上报的函数不同,我们分别来看一下有哪些常用的API函数。
1 `1 W+ I" }8 |7 F. Y. Q' ninput_event函数:用于上报指定的事件以及对应的值。函数原型如下:2 p# ?. Y( b" N6 X* ]6 u
8 B/ M' E6 \2 L- ?
函数参数和返回值含义如下:
+ }4 U5 x# y/ v5 idev:需要上报的 input_dev。
" ~7 R$ F3 f% V) k- U: `2 Etype: 上报的事件类型,比如 EV_KEY。! u% R+ |* T  B! P+ X; z" C
code:事件码,也就是我们注册的按键值,比如 KEY_0、KEY_1 等等。1 i3 f2 |4 G' L; e( U4 E) @
value:事件值,比如 1 表示按键按下,0 表示按键松开。7 z- x, X# v3 \) q
返回值:无。
% q3 ?0 @6 n( F+ e$ @input_event函数可以用于所有事件类型和事件值的上报。
9 Y% M$ U+ Y$ sinput_report_key 函数:上报按键事件。具体函数内容如下:6 l  s# [# P3 w1 C0 P
; b- R2 N8 L9 n8 a
可以看出,input_report_key 函数的本质就是 input_event 函数,当然使用哪个函数都没有问题,不同的设备使用对应的函数更加合适一点。; X1 X/ J4 i/ `1 O
同样的还有一些其他事件对应的上报函数:
  y2 [* N- n" b! Z9 E4 p6 w2 T
( Y- A; N3 h9 }8 }" cinput_sync 函数:用来告诉Linux内核input子系统上报结束。input_sync 函数本质上是上报一个同步事件,函数原型如下:% T( P- I+ a# @2 W' _8 k3 P
void input_sync(struct input_dev *dev)
( S+ D4 g+ ]+ n2 o; C/ ~1 i) l列举了好几个函数,以按键设备为例,看一下如何使用:
+ d5 S% J- x4 l9 B. w9 d4 J; F$ w% q# H' }' q
获取按键的值,然后判断按键是否按下,通过input_report_key函数上报按键的值,input_sync函数表示上报结束。' Q2 A' c% Q. G+ B2 J
3 input_event结构体
& s8 o* \! J% A' SLinux 内核使用 input_event 这个结构体来表示所有的输入事件,input_envent 结构体定义在include/uapi/linux/input.h 文件中,结构体内容如下:
4 Z" m" h) S, S2 D  w
# r: a3 o  L; j, k7 T依次来看一下 input_event 结构体中的各个成员变量:4 n, b* B1 c$ u& Z) k3 e
time:时间,也就是此事件发生的时间,为 timeval 结构体类型,timeval 结构体定义如下:/ `" e- f* i9 N9 K
* ~/ N% o# h8 j0 @+ L
tv_sec 和 tv_usec 这两个成员变量都为 long 类型,也就是 32位,这个一定要记住,后面我们分析 event 事件上报数据的时候要用到。
  S% z. k& C# Z8 Xtype:事件类型,比如 EV_KEY,表示此次事件为按键事件,此成员变量为 16 位。8 [$ t3 \4 x7 X7 p$ D
code:事件码,比如在 EV_KEY 事件中 code 就表示具体的按键码,如:KEY_0、KEY_1等等这些按键。此成员变量为 16 位。: q5 Y: P  M" G% G! G9 P
value:值,比如 EV_KEY 事件中 value 就是按键值,表示按键有没有被按下,如果为 1 的话说明按键按下,如果为 0 的话说明按键没有被按下或者按键松开了。$ I" f. u! t$ M2 D4 l0 ^! c$ l
input_envent 这个结构体非常重要,因为所有的输入设备最终都是按照 input_event 结构体呈现给用户的,用户应用程序可以通过 input_event 来获取到具体的输入事件或相关的值,比如按键值等。
/ n6 q4 T2 g4 `- D: O7 o6 U
8 f: Z% Q4 Y9 M+ o0 i& T0 U5 B9 v" H. n/ ]6 k6 E0 u. g3 p
终结者资料全开源,不买也可以自由下载软硬件资源
您只需要关注VX公众号:迅为电子 ,  回复 :终结者,免费获取产品资料
) g% [* [7 d) ^& _& S

/ v/ T' _* ~: Y' R# V9 R% z" ~/ m1 b0 q

作者: tutututut    时间: 2021-1-12 19:25
很详细,谢谢




欢迎光临 EDA365电子论坛网 (https://bbs.eda365.com/) Powered by Discuz! X3.2