|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
在此文章之前,我们讲解的都是简单的字符驱动,涉及的内容有字符驱动的框架、自动创建设备节点、linux中断、poll机制、异步通知、同步互斥、非阻塞、定时器去抖动。
5 A3 V, h: X/ Y/ X1 r# Z6 n
$ Q0 T- V+ c: L% i# v0 t M5 M上一节文章:
/ y0 R/ j3 O3 f) Q" k* t
1 w D {. I* |7 ?5 k) P, [; _2 r1 s7 w9 q; ^' v8 a8 @7 H
在这一节里,我们要引入linux的分离分层的概念,linux输入子系统是一个很好的代表,在讲解如何编写input子系统的驱动之前,我们理所当然的要先好好认识一下input子系统的框架。
& ]. }" c% u+ O6 s
6 S! Y9 j: a5 ?8 J& T一、linux输入子系统的框架$ j+ A5 R% T8 M$ h( [
9 g0 }. Q; k @1 Z+ S8 f下图是input输入子系统框架,输入子系统由输入子系统核心层(Input Core),驱动层和事件处理层(Event Handler)
/ x) S% ]$ P+ ?0 P0 d% N- g$ m3 ^
/ @, d1 k( K& s, K# @8 f& {, {8 `三部份组成。一个输入事件,如鼠标移动,键盘按键按下,joystick的移动等等通过5 u8 x6 s0 T$ K2 O+ ~- D$ ^; E4 f
! `1 u) Q9 Q: L* Finput driver -> Input core -> Event handler -> userspace 到达用户空间传给应用程序。" M! ^4 K+ l8 U$ [! B9 p8 ^0 }8 A
) u4 M. i: v [3 c' t
4 M# h( u5 A3 G$ L4 W( a
0 o& E! s2 a7 M, {* j/ c/ l二、drivers/input/input.c:' q) B( ] j: J/ W+ o& q
* [7 W* a. m2 @$ t$ W
入口函数input_init > err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
/ x4 k2 N5 ^4 s% c4 _
0 o. U4 Z, |$ {4 b5 Jstatic int __init input_init(void)
7 a" ?' W9 P$ a& S' R5 X( w{! h' S1 w& N7 M/ i
int err;
5 I& S: ~ g) V" m ...+ C# R5 W2 }$ G6 d' C; Z5 |
/* 创建类 */
9 }2 u9 q( {2 h, K1 D err = class_register(&input_class);
. j, n/ C1 W' p9 |/ W ...
3 q5 F$ w; c7 U: u7 D /* 注册一个字符驱动,主设备号为13 */2 B3 y1 s$ T* C
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
6 m3 m! B9 {- V) Q( z+ m ...5 T3 R; g6 Y! U, J0 P$ T( z4 ?
return 0;
0 h' ~9 }. `7 N2 s6 i}
3 z, _* H6 F( w. e只有一个open函数,其他read,write函数呢?
" Y. ^7 Z4 M: u4 v4 B, |static const struct file_operations input_fops = {) z. T2 h" F- Q. L- o+ `
.owner = THIS_MODULE,
! v# M* ^* M# V2 a( g .open = input_open_file,
( e# e% ]5 b* [};
& L' e! h. \6 O+ cinput_open_file函数/ Q! r- i" F4 y' l& e' T
! h. I3 Q( q$ `
static int input_open_file(struct inode *inode, struct file *file)
: A; y: [# }# n8 X{
( x* i( c1 O8 G) H7 f. v! o struct input_handler *handler;. q0 \- T5 O4 \) L8 u) u( o
const struct file_operations *old_fops, *new_fops = NULL;! g" r7 |7 D6 ?9 @6 g4 S
int err;0 Y) h* u" q, I7 V& ~
...
4 t) x: X$ Q3 F /* 以次设备号为下标,在input_table数组找到一项handler */* F6 g# ]; ?2 W; @
handler = input_table[iminor(inode) >> 5];
. N9 Z: U, ^/ r! f" U' j$ j- d + w% G5 y8 ^- e0 ~' E
/* 通过handler找到一个新的fops */. o- |5 l: i. L t) }1 t
new_fops = fops_get(handler->fops);( i& Y X& u6 _) v, o
...$ Z4 t' m. y- b( Y+ U
old_fops = file->f_op;9 b, _) f, |% T& _1 z4 [# P
/* 从此file->f_op = new_fops */+ M0 g: x+ Z* j+ b" ^2 m$ M
file->f_op = new_fops;
! X0 O1 ` @% J7 Z7 _ ...
\2 M' i+ g. D& W* O /* 用新的new_fops的打开函数 */' w1 A0 q S( g; n$ c0 `" a N+ L3 f
err = new_fops->open(inode, file);& y" X. N5 Y7 l9 W7 v7 y/ k0 `
...
4 k- A* s. J/ B6 ?1 U- | return err;
. r1 g6 U. l8 B: x2 s}$ f) @6 f7 ~: r6 Z3 ~
input_handlerj结构体成员8 d1 ?+ ^. {, f: k& \9 \) _
struct input_handler {6 Y" T, I2 t/ H5 ^
) O! a- e( M: f4 P7 d; ^7 \6 C void *private;0 L4 B) P% @6 f2 J# |
) q' t% V, a) z/ N void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);7 g' V) K: F& }% @( `. q! R
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
. }1 `% l5 E) Z0 e9 M6 a( G void (*disconnect)(struct input_handle *handle);
- b6 C9 F* R# V5 v: a0 s2 z! D* ~ void (*start)(struct input_handle *handle);
6 P6 E5 _: p6 e x3 L! ?
5 u* b- j. i1 [# Z( w7 k const struct file_operations *fops;
) G" e' A9 l( g5 A- L, M int minor;
9 s3 r6 x# N' L const char *name;4 ^2 ?7 D, c5 O, s0 r4 d: D! \
5 q" Z- Y3 v+ w
const struct input_device_id *id_table;1 C: A2 r( Z9 p) ^9 N! p
const struct input_device_id *blacklist;/ w" i$ D" K6 f; |8 m/ n
& J5 u& q" n. R, w6 ~
struct list_head h_list;% z/ `( C; U( d
struct list_head node;6 w, g/ t& S# J# v" n8 y9 R
};, W( y! w1 ^5 j2 w3 k- Y* Y
问:怎么读按键?
; D6 k* |% O; L+ W' }: EAPP:read > ... > file->f_op->read
9 l) ~ p( `2 g8 `# x
' L1 p; Z8 L5 n% D8 U7 A问:input_table数组由谁构造?
" M9 c$ I/ b$ {" C& C6 ^
. i V4 f, [* G; C答:input_register_handler5 @! S% c+ n4 p) X( j
3 Q. r) K9 N0 ]# G
三、input_register_handler函数(注册input_handler)( H- l$ ]1 G- N; i4 N
( l) O& e4 Q2 z/ {: x! h( z: b0 f: P# y3 v3 f
int input_register_handler(struct input_handler *handler) ^/ G* @" m" G$ j1 s: I2 [
{
/ M# A8 v9 g. v' ?( H' Y/ C/ c struct input_dev *dev;* w6 I! G9 m7 u, V" r) D; o
...
( i) r5 c, _- u* ^0 _3 b INIT_LIST_HEAD(&handler->h_list);
7 X) I/ m) z9 j% b/ g ...
) W2 x: P& R# i) M. U /* 将handler放入input_table数组 */
- A" @3 k+ Q& {/ T. Z* z* Y input_table[handler->minor >> 5] = handler;
6 U* V# \+ u4 @. c& x5 ^ ...
2 s* y' q& I7 y2 U) q% r /* 将handler放入input_handler_list链表 */
2 f2 V r+ R6 T: H) v( ~" \! J list_add_tail(&handler->node, &input_handler_list);
( f1 B- G. }5 f ...9 c' c [* v( a! Z; o
/* 对于每个input_dev,调用input_attach_handler
) t* F. b; ]4 S( }5 e$ x3 T! a+ n * 根据input_handler的id_table判断能否支持这个input_dev3 y' K* c' B6 }
*/
' G% H7 ]7 N) T list_for_each_entry(dev, &input_dev_list, node)& Z) a0 k- k# h, p0 f4 f
input_attach_handler(dev, handler);# x: P8 K1 C e* f6 e& Y- e
...
) w+ }& H8 l) z% p& P. a1 T}
# V$ t" f$ G0 p o+ m% Q( q L
$ W6 A& }+ F: W, }& w7 O四、input_register_device函数(注册inout_dev)
' m6 Y1 S# b8 S: `8 N7 B. o" K8 q8 j5 y6 Q
int input_register_device(struct input_dev *dev)1 H4 `8 z% p7 E+ b; _2 {
{
N- {6 t1 N+ | b7 Y ...' B" Y# J! }; P! L4 L6 m
struct input_handler *handler;, F3 b) T8 ]2 R% a9 b* W
.... U3 N! N1 w! G! t$ d
device_add(&dev->dev);7 x; w' E5 \7 T7 t( s! h
...3 E; B X' I% `1 b
/* 把input_dev放入input_dev_list链表 */1 d1 [( f3 H" ^
list_add_tail(&dev->node, &input_dev_list);/ B# v" s, B7 s( p& \( Z. z
...; d& l, V( M: s T$ J- A
/* 对于每一个input_handler,都调用input_attach_handler
+ g0 I9 O0 W3 B6 C. m" A * 根据input_handler的id_table判断能否支持这个input_dev
" ~( R8 ^9 w8 ?! m% e- K */" a, P# }2 U! Y- L% {% I
list_for_each_entry(handler, &input_handler_list, node)
9 r5 e+ U0 c/ H5 f2 P" O; ~& a input_attach_handler(dev, handler);
0 o7 S8 e* n( o ...
6 H3 `5 S0 n* \2 ^4 v2 @, s; Z}0 U% W- p' A2 Q
2 g* I1 ~) K, t, j1 i2 I五、input_attach_handler函数+ B H& L, ]& K/ E; Y- b
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)! s' {- o! {6 m) _0 P: K" p
{
7 M8 |: @ N; |0 D" I( j const struct input_device_id *id;9 g( S9 ^/ }+ y1 ]* o
...% o& z( Z h. L7 n
/* 根据input_handler的id_table判断能否支持这个input_dev */
( I# }" J0 w& U6 j) o input_match_device(handler->id_table, dev);0 F5 M2 ~4 i* A9 ^
...
# ?7 ^1 t+ E- v& v9 w# g /* 若支持,则调用handler的connect函数,建立连接 */( a' W$ M5 Y; a: i4 p
handler->connect(handler, dev, id);
- E. S7 w% C' U8 m/ J+ U# {4 ] ...& x* f* ^% x$ [$ O+ K1 W5 e
}
2 f' z% m# d! J/ z! [
8 Z- P: U& H$ c1 _* D小总结:
& s% _( h- C: t% A& V注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,根据input_handler的id_table判断这个input_handler能否支持这个input_dev,如果能支持,则调用input_handler的connect函数建立"连接"。7 i/ j2 R5 U1 b% ^. `6 p% a K: B. z7 k6 I
- m5 W8 i; Y! \! O问:如何建立连接connect?' D, B* I3 q' S, M
) Y) y9 C. i* ? x
答:举例,evdev_connect函数
8 T$ `( E9 Q3 [6 C
- Z" C4 d6 i, O# E+ C4 W7 F) j# i7 W+ \+ q# d1 {+ c; c
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,1 c2 S w5 E4 _1 q" \
const struct input_device_id *id)
' w4 k) R. y+ ]0 i' c4 s{
5 s _, A0 _! a4 C5 `: p struct evdev *evdev;/ }2 D$ o: K5 P% D( U9 F9 d3 E6 u6 p
...
4 C7 X0 c/ W3 ~; n; V h, a. K: P% [/ {
/* 分配一个input_handle */
' u" G9 }9 e8 f2 m5 E8 T evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
* | P% s C. Q: H8 @$ q, s$ R ...
- a) }# A+ X, q% I% l' i snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
: ^+ {0 j+ ^1 f6 P& F6 i$ [ evdev->exist = 1;
+ E: e/ I2 ` ` evdev->minor = minor;# o; v4 E2 I* W( x! a6 g8 Q* s
; W* N8 T3 J: F6 i
evdev->handle.dev = input_get_device(dev); // 指向左边的input_dev
0 B# K, K. m3 y* Z8 a: M6 C, y evdev->handle.name = evdev->name;4 ~5 y' T: _/ o& ?. g
evdev->handle.handler = handler; // 指向右边的input_handler8 `, }6 f4 l8 W' T4 g
evdev->handle.private = evdev;
! u+ L) Q0 L; M8 W9 w$ }: q# A. c' U* p* i v+ Q2 {
/* 设置dev结构体成员 */6 S0 k2 ?2 o6 _) |
dev_set_name(&evdev->dev, evdev->name);% F$ u5 A( }8 `0 W1 E2 U
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
+ }5 f, N' j6 ~+ j evdev->dev.class = &input_class;
+ _" G0 I" L( p$ m evdev->dev.parent = &dev->dev;
. A6 x( ~. R# _2 v$ N0 a: R7 F+ D evdev->dev.release = evdev_free;
8 Z' F# V- l8 X device_initialize(&evdev->dev);# N: c# o1 Z3 s- M( h6 l4 M& N
6 w2 v$ U; P( ~2 O
/* 注册 */( H* B3 f8 E8 Z* s
input_register_handle(&evdev->handle);8 k# G5 i v$ y1 B; G( N
...6 K3 B8 g; ]( T6 q" \' Q& D
}
1 }5 \0 n- C0 x- Q6 Xinput_handle结构体成员
9 c2 ?6 b+ R4 P' u9 m2 ], W6 t9 b/ R9 J
struct input_handle {5 l/ h# ?3 u( \
" |, \0 d3 r* w void *private;/ k, g7 J- K; p8 \5 H! V
- I) ]+ U8 }/ }- Y+ z, C
int open;
5 X2 F0 A" o2 F/ y+ E const char *name;
* D8 E4 x& r8 q. I' p: U8 W7 K+ ?) I# v% B) W9 f
struct input_dev *dev;
2 m, Z0 D: d2 B5 o; X struct input_handler *handler;) w$ j0 Z0 H! q# I, t
7 z8 z, V. d! k# y+ ~3 d4 l
struct list_head d_node;7 t# d$ |& i0 |' P+ F# q
struct list_head h_node;8 \$ f( L _# ^! ~& }% H: W
};5 |( Z6 X* v o* ]; x6 V0 F
问:input_register_handle如何注册?# u+ D5 P* m8 D# e
6 t7 k9 U9 z! m; Q# W/ Y2 jint input_register_handle(struct input_handle *handle)0 B8 y* F8 @9 e" W2 M4 h
{1 G t; g+ r; [7 v
struct input_handler *handler = handle->handler;
+ ~9 ~; V0 C/ W3 P5 k0 c2 h. r1 t1 w struct input_dev *dev = handle->dev;
" L _& ?+ |4 I6 j* k8 t ...6 N, p8 c$ k# _6 i. b5 b }8 R7 {; p
9 F7 [1 h$ i: N& D1 D* u3 w, R& T' I
/* 把handle->d_node添加到dev->h_list
6 W; [! f' t# d% \ * 这样,就可以从dev->h_list找到handle,进而找到handler
1 ]- \( g6 j6 v: @5 l% ? */
% W# s! m2 S1 t! t+ R' u5 Y$ v" o* n list_add_tail_rcu(&handle->d_node, &dev->h_list);3 L. n, |( M! f( W2 {+ {0 G% r8 W# N
...
! x* ~4 U) R* x8 D9 h) R. B) ]- K0 T0 [! f
/* 把handle->h_node添加到handler->h_list 4 Z+ q! ]3 U* V8 U
* 这样,就可以从handler->h_list找到handle,进而找到dev
+ i" F( {( P: a! { C' m3 T U */$ B b6 K- O4 N5 F3 ]! P
list_add_tail(&handle->h_node, &handler->h_list);: r! ~# s1 F. y" L
... V0 V9 |) p9 _5 {' i
return 0;
$ \) E5 q/ `* F- Y4 F! l% A% j( e* G7 X1 s} }0 Z* B4 O; r! q, r' L# g
小总结:" q' P$ R# Z# L3 g% k
怎么建立连接connect?/ @, f* ?7 ~9 x/ `
1. 分配一个input_handle结构体
9 ^- k' q! B0 t* b8 j& J. V: p9 K2. # o4 O0 g7 M* r( g8 q7 I- f
input_handle.dev = input_dev; // 指向左边的input_dev. s2 b$ K8 k1 F | V) u
input_handle.handler = input_handler; // 指向右边的input_handler9 h; h3 L; O& K4 |/ y7 Q6 s& C
3. 注册:
$ f$ Z% b( |8 S) d2 n1 D input_handler->h_list = &input_handle;" V- v! a8 ?! J3 z4 y' O, y& V
inpu_dev->h_list = &input_handle;$ ^4 c0 N5 [' y. V9 j$ f4 R0 V
$ V3 e, ]. h3 t* t2 t. s
六、怎么读按键?+ y8 d9 O* g& |( m6 C& o
. }2 J }; k+ Z* {" L
答:举例,evdev_read* Q) @8 H4 H4 x8 p
4 w3 E1 y" l2 R7 |% J, y9 h7 d
static ssize_t evdev_read(struct file *file, char __user *buffer,4 G& |2 _4 `: V+ K( U1 s$ _- n
size_t count, loff_t *ppos)2 U' O; \: W5 ~- J! Z6 ~% T
{; o* m* r3 p3 d* \2 J
struct evdev_client *client = file->private_data;, `) [3 X& z! j& h
struct evdev *evdev = client->evdev;
6 f0 L6 o @& S8 K5 a% \6 K struct input_event event;
) R0 J L* h, D) h% j ...+ f, u# F1 I4 X! ]/ x$ `' s
( K9 y# z0 h2 n% }( s& k% F /* 无数据并且是非阻塞方式打开,则立刻返回 */1 L6 o2 V' E! q, d
if (client->head == client->tail && evdev->exist &&4 }) y- f$ A/ \" Y
(file->f_flags & O_NONBLOCK))
7 d% X% l$ ?- A- a8 {; ?; B# p8 X5 S return -EAGAIN;
* R- p4 s# |: z6 A) B6 F0 g% F5 V$ Y) z- y
/* 否则休眠 */3 { I, v. j/ ~5 ?" u( y6 b0 p
retval = wait_event_interruptible(evdev->wait,
" \# `# b- t2 J- t8 {$ Y client->head != client->tail || !evdev->exist);. G5 J; l' C# @3 m
...
2 P' K, [/ A4 ]9 ~}
1 D' |1 k: X7 [问:谁来唤醒?
3 N z. C" T) E1 z5 W4 w5 x7 K搜索evdev->wait发现是evdev_event唤醒的
+ o5 g+ p2 \; G# k6 u2 Y5 l3 u5 w5 s# P* A/ J
static void evdev_event(struct input_handle *handle,2 l! `& t, ?9 W$ f# I. H
unsigned int type, unsigned int code, int value)
) I& ~$ m& ]: v+ h8 z) q{3 _" Y7 q$ W' m' C1 }
struct evdev *evdev = handle->private;& `' i# N* d5 \# ?4 O" q
struct evdev_client *client;
$ i: ] q, P0 ^+ L, F7 F _ struct input_event event;* n) W/ X0 G7 j0 d) B5 Q
...
D( i3 N% y6 G5 d4 D5 m t- p /* 唤醒 */
7 _+ G4 E4 q5 K6 n wake_up_interruptible(&evdev->wait);! x1 f& c' x0 g+ N
}! z; j% v! E: B' a
问:evdev_event被谁调用?/ k) _: ^$ K, J, O7 s. @
答:应该是硬件相关的代码,input_dev那层调用的在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数。7 ~2 ^4 ^3 v: T9 q6 S
5 D- V2 z- n; B& t举例,在drivers/input/keyboard/gpio_keys.c里的gpio_keys_isr函数8 @. o* j" M1 T& ]& U8 b# _, p
% C- I* S, d b5 Q. E! o1 u1 vstatic irqreturn_t gpio_keys_isr(int irq, void *dev_id)
" `7 |1 o, H+ O, g) I{
, j9 }/ F7 X$ b& v+ m6 Z! r8 ] struct gpio_button_data *bdata = dev_id;
$ Y" ^- ^' j9 z4 T$ H! E$ m5 D2 M struct gpio_keys_button *button = bdata->button;9 u* @% L7 `4 F% N2 L: S, p" P
...3 e5 K0 ^$ I. n7 m( J
/* 上报事件 */1 f5 ~! ?9 X/ H- H9 Y% s9 [
gpio_keys_report_event(bdata);8 W& L/ ^+ C# z/ [
return IRQ_HANDLED;, q' s5 b- b- O1 k3 @
} r( I( _6 l. T m' ^
gpio_keys_report_event函数
/ K, T% s5 p8 ^# J9 K2 l5 c
$ g4 {2 A% A$ |. E9 ~. P4 V4 B& gstatic void gpio_keys_report_event(struct gpio_button_data *bdata)
U* @8 n7 i, m5 F9 {; b' v6 }{% y, Z) ], i" W7 _# ]' u$ C
struct gpio_keys_button *button = bdata->button;) T4 D7 Q, T! q7 d5 g
struct input_dev *input = bdata->input;" |& T5 q4 y+ G4 q m, m# j
unsigned int type = button->type ?: EV_KEY;8 V% E( J' a) e, i4 x- ~" p# [
int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;
8 o) n6 }( o; l2 j" \
0 ^1 W5 a/ f- ]$ Y8 d /* 上报事件 */
* F* ?* o1 `' ?+ j$ O input_event(input, type, button->code, !!state);
* d/ ?) N. [$ U( d" z input_sync(input);
^0 A; a4 W$ m1 J% \9 p1 U}
3 D/ K! R2 S/ {1 _ c I9 p+ s问:input_event函数如何上报事件
i0 r7 T- `6 Z1 `# K: t: {1 Q Q2 {) p
答:
/ \: U8 J: E8 [% h/ N% X1 \. m: ~! a4 u) x1 h; t
input_event-->input_handle_event-->input_pass_event" B4 l" _ G% l& n$ C% @' X0 S
list_for_each_entry_rcu(handle, &dev->h_list, d_node); r, w4 c2 `; [1 q' ?+ [
if (handle->open)' I ~: |8 w% j. g) V j, r( l3 K
handle->handler->event(handle," M" A1 B. p) W- P
type, code, value);
# v# w0 T1 h+ S$ |+ ~2 q怎么写符合输入子系统框架的驱动程序?+ z4 {1 A% P" A
$ t0 A' y- a; U, D6 Z# B/ q
1. 分配一个input_dev结构体
. P# d; u0 Q' m' Q, C2. 设置- d# J7 I8 V/ G3 N( P
3. 注册
+ H6 ` d& o$ m4 X, N4. 硬件相关的代码,比如在中断服务程序里上报事件
7 d' C3 d2 ?6 R4 E4 f" d* r
n6 Z5 R5 O7 J& _2 k6 I1 n, Y+ Z* l1 q
: b; ^1 Z4 Q! p0 B7 Y" ^
2 ` v$ ~/ y% c5 q9 t* g1 G: y; N4 F, g( ^
|
|