|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
文章目录3 c6 [; @. }' N3 O$ C6 G
1 input子系统简介7 ?) W/ f: m# z4 l3 [9 c
2 input驱动程序编写流程) j% _* H5 i# s" @' a& }2 c
3 input_event结构体2 x7 Y* j" T1 Y/ z; V, R6 a
1 input子系统简介
' D9 j( `! e6 b! Qinput 子系统就是管理输入的子系统,和 pinctrl 和 gpio 子系统一样,都是 Linux 内核针对某一类设备而创建的框架。 input子系统处理输入事务,任何输入设备的驱动程序都可以通过input输入子系统提供的接口注册到内核,利用子系统提供的功能来与用户空间交互。输入设备一般包括键盘,鼠标,触摸屏等,在内核中都是以输入设备出现的。
7 H1 z. ]. _% D% N6 z( U4 ninput子系统是分层结构的,总共分为三层: 硬件驱动层,子系统核心层,事件处理层。/ a' E) R% b9 k
(1)硬件驱动层负责操作具体的硬件设备,这层的代码是针对具体的驱动程序的,需要驱动程序的作者来编写。
/ i1 N; f% t! | ?' h(2)子系统核心层是链接其他两个层之间的纽带与桥梁,向下提供驱动层的接口,向上提供事件处理层的接口。; A; R* w6 }: _* J5 c& I
(3)事件处理层负责与用户程序打交道,将硬件驱动层传来的事件报告给用户程序。$ L* f" `$ ]: f) w5 m! O1 h- h
![]()
* M5 L% a: [+ `$ s% C2 ~% B& K! _各层之间通信的基本单位就是事件,任何一个输入设备的动作都可以抽象成一种事件,如键盘的按下,触摸屏的按下,鼠标的移动等。事件有三种属性:类型(type),编码(code),值(value),input子系统支持的所有事件都定义在input.h中,包括所有支持的类型,所属类型支持的编码等。事件传送的方向是 硬件驱动层–>子系统核心–>事件处理层–>用户空间。) O, r v4 v' g: R; ]" |0 }
2 input驱动程序编写流程2 d- v o0 i4 J* y4 t/ I
首先来看一下在input核心层实现了哪些功能,input核心层文件是input.c,路径:drivers/input/input.c,部分内容如下:: Q3 P& s1 X* @( m2 C' i
8 d4 s7 D4 r# w: j
第2418行,注册了一个input类,在系统启动后会在/sys/class目录下生成一个input类的子目录,如图 2.1所示:& ?# X! t; _- b2 C3 h3 ]
![]()
5 U0 \1 s; Y: z+ L% q. p7 p% D: }第2428、2489行,注册了一个字符设备,所以input子系统本质上也是字符设备驱动,主设备号为 INPUT_MAJOR,INPUT_MAJOR 定义在 include/uapi/linux/major.h 文件中,定义如下:7 ?9 {% w. R+ S( m+ G$ e7 V8 F
#define INPUT_MAJOR 13
- ^* j0 f! F: a; Q' j所以input 子系统的所有设备主设备号都为 13,在使用 input 子系统处理输入设备的时候就不需要去注册字符设备了,我们只需要向系统注册一个 input_device 即可。- b; a5 r$ ^9 o( _' X4 v8 @
1、input_dev 结构体
N( S! d! D5 Q: K) M) Tinput_dev结构体是input设备基本的设备结构,每个input驱动程序中都必须分配初始化这样一个结构,结构体定义在 include/linux/input.h 文件中,定义如下:
7 L _. r: C8 L0 t9 d $ @" i! I% b9 h# {( U) l
第 129 行,evbit 表示输入事件类型,可选的事件类型定义在 include/uapi/linux/input.h 文件中,事件类型如下:! ^$ {& \9 ~3 D! S9 ?
![]()
! X j5 K X; g根据使用的不同设备选择不同的事件类型,在本章的实验中我们会用到按键设备,那么我们就需要选择EV_KEY 事件类型。- H" M" p( i* Q9 r8 F% {5 |+ E V
在看input_dev结构体中的第129~137行的evbit、keybit等成员变量,都是对应的不同事件类型的值。比如按键事件对应的keybit成员,keybit 就是按键事件使用的位图,Linux 内核定义了很多按键值,这些按键值定义在 include/uapi/linux/input.h 文件中,按键值如下:! K% t3 }2 w$ D n% t6 ?4 M) a O
![]()
& S7 [# u: p4 P当我们编写input设备驱动时需要先创建一个input_dev 结构体变量,但是不用我们手动创建,input子系统提供了下面两个函数用于创建和注销input_dev 结构体变量。9 m, ]" _ g' }2 y/ R# w
+ q1 z+ t$ m- y& {# _6 E6 b8 m
申请完input_dev结构体后,需要进行初始化,根据自己的设备来指定事件类型和事件值,比如按键设备的事件类型是evbit,事件值是keybit。3 T7 a: c) ?7 r/ X t9 M
input_dev结构体初始化完成后,使用input_register_device 函数向Linux内核注册input_dev设备。函数原型如下:
% n' c0 F4 I5 Xint input_register_device(struct input_dev *dev)
9 {3 G1 \* q& e! |" E+ \$ fdev:要注册的 input_dev 。. J& j4 H* x! n
返回值:0,input_dev 注册成功;负值,input_dev 注册失败。
' y! I& u" B7 O同样的,注销 input 驱动的时候也需要使用 input_unregister_device 函数来注销掉前面注册的 input_dev,input_unregister_device 函数原型如下:. `4 P/ z* b. j$ {+ Z
void input_unregister_device(struct input_dev *dev)/ {$ w9 M" r: k, D9 C, f `- U- A
总结上面的内容,input_dev注册过程分为下面几步:& c( H0 Y( x$ r+ T8 l% k
① 首先使用 input_allocate_device 函数申请一个 input_dev。
* {: H- s* Y' D, e' p② 初始化 input_dev 的事件类型以及事件值。
# M0 [3 P+ h& h% X" f( p! q+ l③ input_register_device()函数是输入子系统核心(input core)提供的函数。该函数将input_dev结构体注册到输入子系统核心中- x/ k+ E5 N0 f# `/ s
④ input_register_device()函数如果注册失败,必须调用input_free_device()函数释放分配的空间。如果该函数注册成功,在卸载函数中应该调用input_unregister_device()函数来注销输入设备结构体。
9 r0 Z) B( A' Dinput_dev注册过程实例代码如下:
- Q4 }8 n+ ~# E5 p, q![]()
4 M. {0 W$ l. ^9 y: x. A![]()
4 n. Q( ]7 Z: {4 Q* B+ s8 C; k第 10~23 行都是初始化 input 设备事件和按键值,这里用了三种方法来设置事件和按键值。
0 x6 I$ R9 p+ Q5 K I2、上报输入事件
+ A/ O6 z$ g5 A9 u8 F3 p- F在input设备驱动中申请、注册完成input_dev结构体后,还不能正常使用input子系统,因为input设备是输入一些信息,但是Linux内核还不清楚输入的信息表示什么意思,有什么作用,所以我们需要驱动获取到具体的输入值,或者说输入事件,然后将输入事件上报给Linux内核。比如按键设备,我们需要在按键产生后将按键值上报给Linux内核,Linux内核获取到具体的按键值后,才会执行相应的功能。不同的事件上报的函数不同,我们分别来看一下有哪些常用的API函数。% [& u3 j. G) F- o& K- X. c
input_event函数:用于上报指定的事件以及对应的值。函数原型如下:4 {& o$ m" H3 h3 B, f
6 p$ B; w. U/ z+ D: R. ]
函数参数和返回值含义如下:( L) X1 S. A8 }4 y& a3 [
dev:需要上报的 input_dev。
( M+ I- r4 q9 E- a, h, Qtype: 上报的事件类型,比如 EV_KEY。7 m2 ?! g& O/ S! ?2 t/ V l- O" r
code:事件码,也就是我们注册的按键值,比如 KEY_0、KEY_1 等等。
. w9 P( v- e$ a, i& a0 T' xvalue:事件值,比如 1 表示按键按下,0 表示按键松开。
3 m! m( A# n2 a4 A6 d& G* P返回值:无。
1 Y" K N+ P, h# ]/ Ainput_event函数可以用于所有事件类型和事件值的上报。0 q: X- P3 f: q; i
input_report_key 函数:上报按键事件。具体函数内容如下:
( j* w9 p7 ^6 L + n' G W, u1 [. A! T# O5 m7 w6 S
可以看出,input_report_key 函数的本质就是 input_event 函数,当然使用哪个函数都没有问题,不同的设备使用对应的函数更加合适一点。* T1 [0 X9 Q6 C0 S6 M3 Q0 j! p/ i
同样的还有一些其他事件对应的上报函数:' k- i! c0 @/ u5 j
3 H- z0 v' j* N/ |) [+ x8 S
input_sync 函数:用来告诉Linux内核input子系统上报结束。input_sync 函数本质上是上报一个同步事件,函数原型如下:
) B6 L$ S: r9 \7 r7 |( ^' ^* Mvoid input_sync(struct input_dev *dev)
3 J$ f/ Y* ^1 [) i' I列举了好几个函数,以按键设备为例,看一下如何使用:
' I+ K' \- _+ S, g& H" c0 k5 ^ , K' n" j! g# A& V5 [- S
获取按键的值,然后判断按键是否按下,通过input_report_key函数上报按键的值,input_sync函数表示上报结束。9 `) L- ?5 Y; q0 r$ A9 z* \
3 input_event结构体, u) z s7 T4 }/ p
Linux 内核使用 input_event 这个结构体来表示所有的输入事件,input_envent 结构体定义在include/uapi/linux/input.h 文件中,结构体内容如下:1 Z5 B- x$ f4 S
![]()
% m9 N+ `" e# Y% T8 `5 S依次来看一下 input_event 结构体中的各个成员变量:
( U. E: J/ j3 w0 D7 rtime:时间,也就是此事件发生的时间,为 timeval 结构体类型,timeval 结构体定义如下:# @" W) l* I8 C; [' M
![]()
: g. h' w2 m5 y3 n0 }; {; d8 stv_sec 和 tv_usec 这两个成员变量都为 long 类型,也就是 32位,这个一定要记住,后面我们分析 event 事件上报数据的时候要用到。
& c. w7 r$ b6 E, c5 e! {% {, P7 B' jtype:事件类型,比如 EV_KEY,表示此次事件为按键事件,此成员变量为 16 位。* [. @! L7 y! }! _4 X- w( {' ]
code:事件码,比如在 EV_KEY 事件中 code 就表示具体的按键码,如:KEY_0、KEY_1等等这些按键。此成员变量为 16 位。/ j$ O$ X% G% R- h0 \1 s
value:值,比如 EV_KEY 事件中 value 就是按键值,表示按键有没有被按下,如果为 1 的话说明按键按下,如果为 0 的话说明按键没有被按下或者按键松开了。2 L' t7 l, E9 x5 F
input_envent 这个结构体非常重要,因为所有的输入设备最终都是按照 input_event 结构体呈现给用户的,用户应用程序可以通过 input_event 来获取到具体的输入事件或相关的值,比如按键值等。
, k& W+ O* x3 Y2 K' ~4 \6 q" V! f3 y; p" r7 ?1 P, T+ i* N
![]()
8 L( s! {/ H6 m5 R! b2 m5 s5 }终结者资料全开源,不买也可以自由下载软硬件资源 您只需要关注VX公众号:迅为电子 , 回复 :终结者,免费获取产品资料 - W9 G0 l9 K8 Q3 {0 P
( @! p2 E; s4 @
* g# y1 |/ J6 W+ }6 E; R |
|