|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
文章目录
. X4 g0 p" S6 [; g& @1 input子系统简介
; \8 O; G7 I2 H* C2 input驱动程序编写流程; q& j1 J0 v) O$ @1 w+ Z. I
3 input_event结构体4 R( C; v3 _. b0 o, k: V
1 input子系统简介* Y7 h% o# W% `1 `2 v
input 子系统就是管理输入的子系统,和 pinctrl 和 gpio 子系统一样,都是 Linux 内核针对某一类设备而创建的框架。 input子系统处理输入事务,任何输入设备的驱动程序都可以通过input输入子系统提供的接口注册到内核,利用子系统提供的功能来与用户空间交互。输入设备一般包括键盘,鼠标,触摸屏等,在内核中都是以输入设备出现的。
) ^5 s! L! ?6 F8 Winput子系统是分层结构的,总共分为三层: 硬件驱动层,子系统核心层,事件处理层。
( [+ l4 a/ A5 C; F/ A8 \# N% a2 }. Z(1)硬件驱动层负责操作具体的硬件设备,这层的代码是针对具体的驱动程序的,需要驱动程序的作者来编写。
$ a8 F' `9 \# D( r4 E8 s$ ~(2)子系统核心层是链接其他两个层之间的纽带与桥梁,向下提供驱动层的接口,向上提供事件处理层的接口。
' X& U6 U1 R5 ~# Q: z8 u(3)事件处理层负责与用户程序打交道,将硬件驱动层传来的事件报告给用户程序。
( X2 _; J. t+ q- q! ?/ g 9 j$ e# U( i+ J. q' d
各层之间通信的基本单位就是事件,任何一个输入设备的动作都可以抽象成一种事件,如键盘的按下,触摸屏的按下,鼠标的移动等。事件有三种属性:类型(type),编码(code),值(value),input子系统支持的所有事件都定义在input.h中,包括所有支持的类型,所属类型支持的编码等。事件传送的方向是 硬件驱动层–>子系统核心–>事件处理层–>用户空间。
' e9 i# D; F+ r$ X8 w$ Q6 R8 q' Z2 input驱动程序编写流程
" R: \! c i+ X! `, K首先来看一下在input核心层实现了哪些功能,input核心层文件是input.c,路径:drivers/input/input.c,部分内容如下:
4 X6 O# k! ?* l, P![]()
/ @( T' N1 \- ?( n8 ? ~第2418行,注册了一个input类,在系统启动后会在/sys/class目录下生成一个input类的子目录,如图 2.1所示:
, J9 L% H. F( Z' p' G! D/ o ! D$ O3 `7 S) z: H+ k4 a
第2428、2489行,注册了一个字符设备,所以input子系统本质上也是字符设备驱动,主设备号为 INPUT_MAJOR,INPUT_MAJOR 定义在 include/uapi/linux/major.h 文件中,定义如下:
9 ~5 U* p) b% v8 J: M#define INPUT_MAJOR 133 e( c" A; Z1 E A1 i
所以input 子系统的所有设备主设备号都为 13,在使用 input 子系统处理输入设备的时候就不需要去注册字符设备了,我们只需要向系统注册一个 input_device 即可。
# S. Q$ v$ [9 j% |8 b3 ^1、input_dev 结构体
" i4 ?& K3 X+ z7 i% ^7 H" Linput_dev结构体是input设备基本的设备结构,每个input驱动程序中都必须分配初始化这样一个结构,结构体定义在 include/linux/input.h 文件中,定义如下:% u: K0 _$ o9 k" c- ^8 r7 N
![]()
7 P6 H! M8 }# h5 M& e8 D' h& _6 b1 ~第 129 行,evbit 表示输入事件类型,可选的事件类型定义在 include/uapi/linux/input.h 文件中,事件类型如下:
: X6 D9 K6 M; G; m: w4 g 6 n! b) U# Y; @. {# ~0 p& e9 i+ C/ Y
根据使用的不同设备选择不同的事件类型,在本章的实验中我们会用到按键设备,那么我们就需要选择EV_KEY 事件类型。- H# d# a+ P$ h# `( c3 q) ^, T
在看input_dev结构体中的第129~137行的evbit、keybit等成员变量,都是对应的不同事件类型的值。比如按键事件对应的keybit成员,keybit 就是按键事件使用的位图,Linux 内核定义了很多按键值,这些按键值定义在 include/uapi/linux/input.h 文件中,按键值如下:
6 j# H, X5 n$ K9 O) k![]()
6 L9 ?; p! B/ {2 F$ a! J6 l% S当我们编写input设备驱动时需要先创建一个input_dev 结构体变量,但是不用我们手动创建,input子系统提供了下面两个函数用于创建和注销input_dev 结构体变量。 z; \$ [+ U# X- u+ a. ~
![]()
7 S, T! y" O2 C. c. g申请完input_dev结构体后,需要进行初始化,根据自己的设备来指定事件类型和事件值,比如按键设备的事件类型是evbit,事件值是keybit。9 s& k9 L/ [7 F* a8 ^
input_dev结构体初始化完成后,使用input_register_device 函数向Linux内核注册input_dev设备。函数原型如下:
7 `+ }/ I# f8 N+ \- k% W2 t8 Oint input_register_device(struct input_dev *dev)
W3 K& Z' D7 A5 hdev:要注册的 input_dev 。3 S! Q- r. t) u7 I3 E
返回值:0,input_dev 注册成功;负值,input_dev 注册失败。
/ k+ P( Q, \1 ^# f同样的,注销 input 驱动的时候也需要使用 input_unregister_device 函数来注销掉前面注册的 input_dev,input_unregister_device 函数原型如下:
+ }2 Y+ G$ n9 |% X6 O6 U {void input_unregister_device(struct input_dev *dev)
3 O! t/ F' v9 s- _$ D7 I+ D总结上面的内容,input_dev注册过程分为下面几步:' @5 s! m' T7 q$ i R3 d5 a& k* K
① 首先使用 input_allocate_device 函数申请一个 input_dev。
) t7 u C0 _5 J7 Y _② 初始化 input_dev 的事件类型以及事件值。
' M* g6 p/ U( E* F③ input_register_device()函数是输入子系统核心(input core)提供的函数。该函数将input_dev结构体注册到输入子系统核心中% E9 [6 [2 q. o1 N
④ input_register_device()函数如果注册失败,必须调用input_free_device()函数释放分配的空间。如果该函数注册成功,在卸载函数中应该调用input_unregister_device()函数来注销输入设备结构体。
. `" A" V0 T, z; C8 r& h8 T }input_dev注册过程实例代码如下:+ L# J) x$ o1 l, F. t1 R
4 b! p+ {- }$ K+ k& b; x
![]()
& E$ U' v5 P/ {/ k& o0 E第 10~23 行都是初始化 input 设备事件和按键值,这里用了三种方法来设置事件和按键值。2 b+ T6 x+ ^; ~, b5 f/ _
2、上报输入事件/ e2 Z* O6 h, t, ^8 J. Y) u
在input设备驱动中申请、注册完成input_dev结构体后,还不能正常使用input子系统,因为input设备是输入一些信息,但是Linux内核还不清楚输入的信息表示什么意思,有什么作用,所以我们需要驱动获取到具体的输入值,或者说输入事件,然后将输入事件上报给Linux内核。比如按键设备,我们需要在按键产生后将按键值上报给Linux内核,Linux内核获取到具体的按键值后,才会执行相应的功能。不同的事件上报的函数不同,我们分别来看一下有哪些常用的API函数。5 Y8 I K% o7 L: W+ u
input_event函数:用于上报指定的事件以及对应的值。函数原型如下:- U. H9 l3 t s2 s! V
![]()
) Z: i2 c, O9 x! M w ` d函数参数和返回值含义如下:7 K4 c1 r) J- |; `9 \% S5 c
dev:需要上报的 input_dev。9 H+ i9 w, Z: K% l# v: R O! t
type: 上报的事件类型,比如 EV_KEY。
* S4 r3 d- L1 C7 fcode:事件码,也就是我们注册的按键值,比如 KEY_0、KEY_1 等等。# ?1 C3 V3 g% M1 I& u% y, c
value:事件值,比如 1 表示按键按下,0 表示按键松开。
/ [) P! ?* a, d5 `2 W) y% a) p' }返回值:无。2 d9 a. q3 ?* n, O
input_event函数可以用于所有事件类型和事件值的上报。/ j. k/ k/ G" f5 L( W8 D* m
input_report_key 函数:上报按键事件。具体函数内容如下:
( k% y, m) K8 ?/ v9 |7 S Q4 E1 u![]()
' @( Q! d4 y# s可以看出,input_report_key 函数的本质就是 input_event 函数,当然使用哪个函数都没有问题,不同的设备使用对应的函数更加合适一点。: l' g5 Y7 A& ?
同样的还有一些其他事件对应的上报函数:
; j; Z& B: F# f6 p5 O# ]0 e $ [) i; O! k; `: x& M
input_sync 函数:用来告诉Linux内核input子系统上报结束。input_sync 函数本质上是上报一个同步事件,函数原型如下:
% T0 L5 _/ E4 J+ Hvoid input_sync(struct input_dev *dev)) y. E9 E! g* o \! B
列举了好几个函数,以按键设备为例,看一下如何使用:
' k+ q! m% I6 H. ^" \% O 3 A/ j" T0 f) [% `0 p3 d/ F1 @
获取按键的值,然后判断按键是否按下,通过input_report_key函数上报按键的值,input_sync函数表示上报结束。
1 ?1 x0 J8 r$ ~3 N6 R, X3 input_event结构体
1 R3 [8 h ]2 P4 o wLinux 内核使用 input_event 这个结构体来表示所有的输入事件,input_envent 结构体定义在include/uapi/linux/input.h 文件中,结构体内容如下:! ^ [# w, t7 X. T2 y H% w
![]()
$ T+ ~* [/ _5 D2 B依次来看一下 input_event 结构体中的各个成员变量:
& q( T: v- |* k: O1 c' ~4 C( Wtime:时间,也就是此事件发生的时间,为 timeval 结构体类型,timeval 结构体定义如下:
/ ?- u. ^. S+ k# l$ N i; @ / r9 ]) [4 X! O! `( v
tv_sec 和 tv_usec 这两个成员变量都为 long 类型,也就是 32位,这个一定要记住,后面我们分析 event 事件上报数据的时候要用到。
7 }" V3 I, R) B. i- r# ]* A: ^/ btype:事件类型,比如 EV_KEY,表示此次事件为按键事件,此成员变量为 16 位。
, r. G& e& b) L! x" \code:事件码,比如在 EV_KEY 事件中 code 就表示具体的按键码,如:KEY_0、KEY_1等等这些按键。此成员变量为 16 位。
' ^( ^4 z$ G6 G$ h9 xvalue:值,比如 EV_KEY 事件中 value 就是按键值,表示按键有没有被按下,如果为 1 的话说明按键按下,如果为 0 的话说明按键没有被按下或者按键松开了。) m2 T0 | D' I( X* e: {1 O- o
input_envent 这个结构体非常重要,因为所有的输入设备最终都是按照 input_event 结构体呈现给用户的,用户应用程序可以通过 input_event 来获取到具体的输入事件或相关的值,比如按键值等。
3 Y* u: K' b0 T# o u) s! J K- J% `# y7 l# G0 @- M2 W
![]()
$ a. S& |1 m- |: ~( ]终结者资料全开源,不买也可以自由下载软硬件资源 您只需要关注VX公众号:迅为电子 , 回复 :终结者,免费获取产品资料 3 K z/ H" ]+ G
% D6 Z3 s5 w& ?1 \1 U$ {) ^
% e9 @( o/ U) a% W) W& h+ i! F3 i) e |
|