|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
在此文章之前,我们讲解的都是简单的字符驱动,涉及的内容有字符驱动的框架、自动创建设备节点、linux中断、poll机制、异步通知、同步互斥、非阻塞、定时器去抖动。
2 o' d' f0 x8 \1 u, k0 p: X! W" P, l4 z' J+ i0 M$ @, q
上一节文章:( N: y3 F% s% d) E( }! }1 i) v
) N o& o$ P- b- W7 a
5 W& s$ u+ q G8 g. M! M
在这一节里,我们要引入linux的分离分层的概念,linux输入子系统是一个很好的代表,在讲解如何编写input子系统的驱动之前,我们理所当然的要先好好认识一下input子系统的框架。
/ Q& k8 ^$ a5 p0 U% c* m' d% u) P
1 c5 ?2 G( E4 y$ K+ @/ F" o1 V一、linux输入子系统的框架
7 N q& q2 _+ e2 h8 @) R& n* b6 [( q* z) F: U! x
下图是input输入子系统框架,输入子系统由输入子系统核心层(Input Core),驱动层和事件处理层(Event Handler)
+ J' R5 Z( I" G, `2 g& h
6 i9 }* y3 J" P) `) Q! K三部份组成。一个输入事件,如鼠标移动,键盘按键按下,joystick的移动等等通过
8 E6 G% \0 C# B2 ~' ~& d! N; y+ n* y8 Y6 \' [: p% o
input driver -> Input core -> Event handler -> userspace 到达用户空间传给应用程序。" q, r9 }2 j& `- C/ I) d) k+ n
$ Z1 H( J- U6 p+ G) o/ y; s
" K% X7 Y- ~) B
9 ?& ~% g* \4 X2 U# h5 C, w
二、drivers/input/input.c:
& N) s- U7 K& b5 i* j6 {3 L3 D2 T# g# p
入口函数input_init > err = register_chrdev(INPUT_MAJOR, "input", &input_fops);. o3 c. D1 w3 Q, h; M1 h
$ g( X: ?: M7 \
static int __init input_init(void)( ]% U* J! U% S# y1 }) i
{* e- `* p' w6 U6 T7 k2 T
int err;
' j" o3 n; R3 _ ...
i3 z" n8 M1 Y1 d+ n+ Y /* 创建类 */
6 C, ~" Q& h. m2 a err = class_register(&input_class);
9 D" ?. h1 M4 f+ j% e6 v: Q ...+ N$ B% w; G* g3 V% B
/* 注册一个字符驱动,主设备号为13 */
1 K2 i q8 n. T1 Q/ }' b err = register_chrdev(INPUT_MAJOR, "input", &input_fops); v% T a# \" ^' B. R* B1 C' n
..." ]) ]- a; r! W+ A# }
return 0;1 @ ~4 J( R, p6 |1 X+ a9 \
}
7 s& C8 d% ]- I' W只有一个open函数,其他read,write函数呢?
8 X: L1 p {1 c6 S6 I+ Y( v8 Ystatic const struct file_operations input_fops = {
* \% h8 n- U/ p, I9 S- D .owner = THIS_MODULE,
4 E8 i$ A: q5 L; d1 Y# Z .open = input_open_file,
9 q- k! o. T9 t% x z};9 n% z, n: G/ J5 ?9 e. K' `
input_open_file函数
' ]+ P4 b; w) }- x8 i% _' y k7 r
static int input_open_file(struct inode *inode, struct file *file)# P5 @, j0 Q7 ]) ?( G
{! f4 t: _. g# M7 D% T7 K
struct input_handler *handler;
4 I6 A4 e2 I& s4 @5 k8 B; K const struct file_operations *old_fops, *new_fops = NULL;
9 V' P; @1 }+ n! \: s int err;
& Y7 H9 @7 `) ?4 t" u ...
1 n* V- y) j; V( j( Y" W /* 以次设备号为下标,在input_table数组找到一项handler */8 h, h" f! l8 X2 [7 _3 y
handler = input_table[iminor(inode) >> 5];5 p& x' ]2 A/ Y& K5 W7 J; x
/ _& _$ O' G7 J* x: K0 D /* 通过handler找到一个新的fops */) [+ [7 z7 [8 L6 H. P) A8 |% `
new_fops = fops_get(handler->fops);; r8 o G& G6 ?" x
.../ a( h) }% W* U6 Z& y; X
old_fops = file->f_op;
7 v% f# ^" n2 z: Y# ]% K- ~ /* 从此file->f_op = new_fops */$ S7 y ~) a- v( H) ]0 Y
file->f_op = new_fops;
0 m Y) p; p6 |& W! r) O ...
# R8 k# l% _" R) `/ o /* 用新的new_fops的打开函数 */3 }7 f5 g& u3 N. ]( {9 k' ]
err = new_fops->open(inode, file);
5 V8 G: o. x1 X+ Z9 v ...
. I% E/ q5 J% s' J$ t( ] return err;. r& o& _- h" l; U+ u0 c
}* w# w5 A3 F0 h0 l5 M% k ]
input_handlerj结构体成员
7 p% t' f9 l" a$ Ystruct input_handler {
/ J+ x5 {; m) l7 T5 z1 t& y. }: a6 A |) X
void *private;
) r: x5 H) c# @+ N K9 h0 ?
' ?; |! o% I9 _0 j2 {0 l% a void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
) m9 b% c/ x3 S' b int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);$ e7 A& ]9 M p( g/ C& p
void (*disconnect)(struct input_handle *handle);6 ^9 N$ n8 X+ \3 ~/ m3 _5 j
void (*start)(struct input_handle *handle);( F& z0 q0 W2 t* H% Q. d2 V t; L
8 \8 h; r" Y1 s4 F: T/ P( W
const struct file_operations *fops;. F3 h M. M) ^% U
int minor;
; M( |' X- V" J% q* s) I4 t8 V/ [ const char *name;
% J" n+ `& X7 [* b0 s* v! V
: D( |- l# Q0 o const struct input_device_id *id_table;
Y$ Q( n' u9 d3 R$ J3 @ const struct input_device_id *blacklist; Z6 ^- s: x0 m7 B2 K& T
2 z" v6 G) l; `4 H
struct list_head h_list;$ T# H/ \& W3 w+ d" C4 L5 z% o
struct list_head node;
0 A" o2 V% m6 i# y+ n};# V! @- U; M$ w: m7 o6 v
问:怎么读按键?3 S5 I, y- G) Z) L5 M
APP:read > ... > file->f_op->read ; U# z; N I+ n7 M# W, x
8 t, A, p& K: k0 p& F* w问:input_table数组由谁构造?; \4 a8 r2 f: w: ]) L" F7 x, Y
, @$ R" Y# _; `0 P) {
答:input_register_handler: j: I5 c7 _ k! n4 R! W0 h( Z: m
0 u& W& F0 v, o: j5 ?( ~3 f
三、input_register_handler函数(注册input_handler)
- M+ Z t" D- `2 M
/ V: Y9 D' m( q8 n
3 s% V: ^3 O! iint input_register_handler(struct input_handler *handler)
4 q) q3 K1 E! n" c{
& `3 P" l$ D, J struct input_dev *dev;
. ?' H" J4 S W2 e( S( g6 f ...0 u. c% x, q7 l( m
INIT_LIST_HEAD(&handler->h_list);$ D9 K/ ]9 k$ m, b
...; \# o4 |6 p, q8 T
/* 将handler放入input_table数组 */
9 x/ V7 I8 z, h0 s% \ input_table[handler->minor >> 5] = handler;- |, ?- L5 f+ T4 W
...
+ _) ^9 J; [$ ?# r /* 将handler放入input_handler_list链表 */* G# B7 m, w+ E& T( j# x8 M
list_add_tail(&handler->node, &input_handler_list);
4 ~5 Y" t# T' h& M- x, u( M: L .... [) N% J3 |: B' ]
/* 对于每个input_dev,调用input_attach_handler
1 k" e$ M; I4 {6 x( V * 根据input_handler的id_table判断能否支持这个input_dev$ {( I) } ]0 m Q" M
*/
. Q- b2 j; @7 O# a# o0 g' | list_for_each_entry(dev, &input_dev_list, node)5 j; x- M- p! C" w5 e
input_attach_handler(dev, handler);
. a( ~, [& s+ Z. { ...4 y: |& w0 D& A) W) l7 B# w: ^
}* h. ~0 i9 V' U. ?) r' ^
' K. a8 C F1 P S! X
四、input_register_device函数(注册inout_dev)9 X( }/ `9 e: L7 `, Z% ^+ S
) H4 d1 [: P1 |, U5 O+ Qint input_register_device(struct input_dev *dev)2 y" ]4 `. H. q
{7 p8 H* ^4 n0 I6 X
...
9 o" u3 L& P7 b struct input_handler *handler;9 E3 {3 i2 f" U& X. d; y$ L0 x1 S
...
" o+ e: B$ ^3 _) L device_add(&dev->dev);" M* M) l0 a4 o; F% G, p: [% ^! u/ o B
...
% i, I) Y5 Y; Z# ^, D/ N /* 把input_dev放入input_dev_list链表 */
8 H8 a& f+ u- ]0 @ list_add_tail(&dev->node, &input_dev_list);* K) i2 R6 Y4 C
...
9 s; d3 N- n r, g B /* 对于每一个input_handler,都调用input_attach_handler3 [6 O, E# \3 u+ W' u% ~% m
* 根据input_handler的id_table判断能否支持这个input_dev
; E' x1 n+ `. C L */
1 E# A1 X! g: j+ D list_for_each_entry(handler, &input_handler_list, node)& X1 w. c# e5 e( N
input_attach_handler(dev, handler);
6 E4 F% P5 ^/ ^# {, B% j6 ^ ...& a/ U g& U: p& ]
}
* H7 O2 m8 Y4 i* Y
/ \$ e( H. Q# P五、input_attach_handler函数
: ]' L5 i: e( G j# q5 O' }' e# pstatic int input_attach_handler(struct input_dev *dev, struct input_handler *handler)% c. ]) j8 j8 N. J ?3 J
{8 Z& q- x1 p. o; Z& d8 M$ H
const struct input_device_id *id;7 w0 m+ g( O3 g7 q: t" a/ z
...# @" e7 s, z+ C5 Y6 e4 c5 J D6 m
/* 根据input_handler的id_table判断能否支持这个input_dev */. _5 a% B; w2 h3 \ \+ k: K& A2 b% _
input_match_device(handler->id_table, dev);. ]8 H2 @9 f/ g! O" l
...
8 t3 F/ f8 A0 }! ~" {/ g% J /* 若支持,则调用handler的connect函数,建立连接 */
' l$ y# B8 H: H handler->connect(handler, dev, id);
; Z4 [# H8 K, X! F$ y7 s ...4 v7 m' m* V H7 v9 E
}. E0 J: q& Y& `/ y
6 d: ^& X8 K: ~( q9 w2 F小总结:
2 ~; I* S2 @' w& s* q. u5 x注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,根据input_handler的id_table判断这个input_handler能否支持这个input_dev,如果能支持,则调用input_handler的connect函数建立"连接"。. ~3 n4 A* {! C" Q6 l
U$ h. k) f. X( p
问:如何建立连接connect?
6 U0 g' b, R" X( y
1 e9 S! r' Q0 X9 `* e. D! q答:举例,evdev_connect函数6 y4 V4 z. q" b1 F9 r9 h$ J9 c
/ p$ U6 z( d ?5 ~* J
( D [* j: _7 O# m. bstatic int evdev_connect(struct input_handler *handler, struct input_dev *dev,
0 k8 @! p9 z( |( u const struct input_device_id *id)
( H, c' e! ?* k' f+ O{2 g& [4 b; ~. J0 e. n+ E; i
struct evdev *evdev; T, [7 F: C4 p( z5 F- f) M
...; b* x7 e2 A( k+ @# k2 {0 f
! Z1 x4 `+ p: N6 V& S2 } /* 分配一个input_handle */
' p, M6 k8 B h evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); 0 Y0 P9 l _5 z' O' j$ `* b8 n
...- q4 [1 }. v3 a1 F/ O7 D# i
snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
$ r- Y* l; t% _1 `2 X: @0 \ evdev->exist = 1;. C. u. }; p% ?
evdev->minor = minor;; ~4 v+ @1 H4 Q. C+ @( f! m
, B- j7 I+ |6 @# r5 A0 ]5 {
evdev->handle.dev = input_get_device(dev); // 指向左边的input_dev$ r) O h, P. l, P% N5 ~
evdev->handle.name = evdev->name;$ Q, _0 r5 e" E- d" O+ `
evdev->handle.handler = handler; // 指向右边的input_handler
2 u+ @ U; O8 X! b9 }3 o evdev->handle.private = evdev;: z3 z' V; Y# w. I- ^0 ^
) ~ t; O; }. ~7 N2 S* X /* 设置dev结构体成员 */9 G' r+ f) b R ^: A
dev_set_name(&evdev->dev, evdev->name);7 B' ^% B7 o" p! X7 e
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);: K/ o: D0 V! q+ Z3 o1 t
evdev->dev.class = &input_class;' t& N2 r, b) ]+ S
evdev->dev.parent = &dev->dev;, }. O6 V* S7 q8 R
evdev->dev.release = evdev_free;/ b5 p, p3 o6 J" C/ Y; \
device_initialize(&evdev->dev);2 b; v) @6 q. g
& s, d h" |3 I' t' R5 l) ^ /* 注册 */
* h/ t* q! E- y8 A2 m input_register_handle(&evdev->handle);. ~9 y- Z+ b. e W1 n# x
...8 M0 n+ K3 k6 g/ d Y- d
}+ Y0 W+ j: y7 c( v2 q. d
input_handle结构体成员
. M, e, j7 T) Y) ^
% F6 Q$ {6 X9 X: e: W( h3 Tstruct input_handle {2 {0 c6 f. Q4 n
4 w/ S9 f- ]+ R$ h6 G. a# v8 n, N7 j
void *private;* L, v; ~ w2 B/ |3 c; m6 I' {
. L8 u: V6 Y7 n5 ]. R+ w int open;0 r# R1 b7 D" w
const char *name;' D2 m# _: c9 `: X
; y/ Y, e b2 Y+ _- p1 V' V7 ] struct input_dev *dev;. r* W$ P% P! N& l2 o
struct input_handler *handler;) A* b8 i2 C, Q$ k$ p9 o- z0 G% V! y
# E% Z* L5 ~5 t) @ struct list_head d_node;" v" b- `. ^ f* h0 I+ }' e) h
struct list_head h_node;0 U, j% K* h3 R$ [( k2 z
};
& n0 V! \/ G# R% e2 W) y+ e+ k问:input_register_handle如何注册?9 J, k9 J' z9 b% u
) r, V8 x3 o( P8 Oint input_register_handle(struct input_handle *handle)
) Q5 e* |0 d1 K{
$ |5 _. a2 c. B! v struct input_handler *handler = handle->handler;( U) I' g( v) H+ y* g9 ^/ @
struct input_dev *dev = handle->dev;" h5 [4 V7 u2 ^. n. n9 P2 b
...+ x( F. m0 M9 L
" G. T1 H9 u+ e- u7 ^) l# C, S. [+ u
/* 把handle->d_node添加到dev->h_list
5 w) o d3 ^5 H( r5 @! m * 这样,就可以从dev->h_list找到handle,进而找到handler' G6 Y1 b, M- u
*/* ]0 L9 s( {( q* z$ L. Q/ {
list_add_tail_rcu(&handle->d_node, &dev->h_list);
$ a$ d& K. ~' l2 E* M ...
4 A6 A) N) G6 k& W5 U
M( M: `( D0 f: \& L /* 把handle->h_node添加到handler->h_list
: v, }* t6 C# C/ ]' N * 这样,就可以从handler->h_list找到handle,进而找到dev
1 ?' t$ r; ^, R' v */
- y4 V/ Z" j/ I list_add_tail(&handle->h_node, &handler->h_list);
" r! o; J& T1 n! a ...
8 s6 q4 H- |1 i return 0;
/ P1 S5 i* S& J) O}
3 [, m* K! g0 j! T2 O( S! U7 u& A- y小总结:
; f* ?9 _ x0 {怎么建立连接connect?% Z5 O, [5 _" y# C) b; V3 ~
1. 分配一个input_handle结构体
; n( |4 i$ Q. r7 h4 K2.
/ u/ A$ v! O; o/ h1 {input_handle.dev = input_dev; // 指向左边的input_dev1 \2 C% C) X2 _4 N2 B, N
input_handle.handler = input_handler; // 指向右边的input_handler0 S' [; \' E5 r+ O1 l- C0 {; b
3. 注册:
* z4 _5 M2 r* B0 p; @/ Y input_handler->h_list = &input_handle;% I% y3 _2 Y: t2 N* |+ K
inpu_dev->h_list = &input_handle;
# F7 |3 ^/ p) ]/ Z: K' N. p& t p9 h! {5 t5 G6 ~" U
六、怎么读按键?4 ^" r4 E3 \# ]. O3 i" G
) F! c( I0 J2 @/ z& A
答:举例,evdev_read
5 H" z% |- S% J$ D. \" O+ V) y' d" V: I# I" Y7 ~$ Q* K3 p
static ssize_t evdev_read(struct file *file, char __user *buffer,
$ l; L+ b% I( q" [- G& d size_t count, loff_t *ppos)+ Q: m% f2 @! g+ z
{* x6 [5 n8 R7 X* f d9 @
struct evdev_client *client = file->private_data;' I- v4 Z" ~6 C8 ~7 m
struct evdev *evdev = client->evdev;
! s) j+ ?! i1 W) ` struct input_event event;. z: C$ ^. P4 a9 ]
...
- a- j" C4 ?; a9 d
8 z5 ?7 G0 \( p" s0 b o% W! I /* 无数据并且是非阻塞方式打开,则立刻返回 */7 x2 p, v2 i! g, ?0 m+ \
if (client->head == client->tail && evdev->exist &&
0 W: \/ f3 r* x (file->f_flags & O_NONBLOCK))
7 a6 I a4 x4 ?% N. Q5 N return -EAGAIN;
' X/ N2 Z8 K- J2 J H. \. ]* A; ]) |
/* 否则休眠 */
$ G2 `( P( s' K$ \$ R retval = wait_event_interruptible(evdev->wait,
; Z. I+ @* H! T5 F, f; P6 ~( G! t client->head != client->tail || !evdev->exist);) E. \# g8 Q( a# h* O0 U9 l; O$ q
...
2 q* u. @ c( \1 Y% K}
~. l% f# ]' X ]9 s问:谁来唤醒?% n& c" Y. [, b) _4 F4 p
搜索evdev->wait发现是evdev_event唤醒的% h9 ~2 q. Q7 m$ M$ _: j# ?# m
" W: Y/ k- E/ j8 l' r
static void evdev_event(struct input_handle *handle,
7 X. b( D6 Y# b" b5 a u3 F unsigned int type, unsigned int code, int value)
; {/ M% b2 P7 S$ o% ~6 S{! @# f6 u) S. {( k9 `& \0 K
struct evdev *evdev = handle->private;
$ m# k) a' M7 E& m; Q/ }. j8 I struct evdev_client *client;' w4 {( ?) E$ W) ~$ O) K: S
struct input_event event;
2 j1 ^5 [2 E5 _2 { ...# ^$ J2 d: C# b- |9 N3 Q4 R% Z
/* 唤醒 */
+ s5 \* s% Y# \8 D0 ~) f wake_up_interruptible(&evdev->wait);
6 e1 Q/ J- d4 w- _/ ]2 j}5 F0 g8 m( F" Y p% ^
问:evdev_event被谁调用?* v! F4 L. Z7 Z2 L# C% c- S
答:应该是硬件相关的代码,input_dev那层调用的在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数。: J5 R5 k5 R- C5 Q Z3 ~0 y: q! p8 _
3 Q! q% ]% s! @- y2 |
举例,在drivers/input/keyboard/gpio_keys.c里的gpio_keys_isr函数% y( l4 G9 S4 _4 X( q% x1 L M; D
4 T7 q$ g' a7 L1 T g2 x# y0 L/ ^
static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
, q( }1 F9 L) w{2 j0 B0 \5 z. c. q/ s
struct gpio_button_data *bdata = dev_id;
% s- X7 R; U4 B8 ? struct gpio_keys_button *button = bdata->button;9 ^& {& P3 v+ F9 d
..." |5 t" T6 ?5 _
/* 上报事件 */, y/ L( |" K! R& l4 H3 S
gpio_keys_report_event(bdata);) J7 s1 Q; ^; B6 h5 ?
return IRQ_HANDLED;% e7 v$ M- b! X* R1 E' m# T6 ~
}0 F" ^" P# r2 B$ B4 Y
gpio_keys_report_event函数
" q; v9 |+ r* R# T4 S; C$ K: m! X% I5 G$ s
static void gpio_keys_report_event(struct gpio_button_data *bdata)
% W, d3 n+ X9 O* K( n{
0 N+ X1 c+ F& b# C" h1 Z" b4 k struct gpio_keys_button *button = bdata->button;3 O' H% O( n- E4 I% B
struct input_dev *input = bdata->input;0 `; ]) J6 d: q$ G7 Y% G
unsigned int type = button->type ?: EV_KEY;( @! P, M6 w' P4 f0 z- D
int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;$ B) N/ V9 K. ` O8 j# `! l
/ l& Z1 m6 L. ~& H( u /* 上报事件 */. m* c5 C1 P- `9 l
input_event(input, type, button->code, !!state);4 M8 `3 K. W) I7 w
input_sync(input);
* M1 C \+ Q( k* \* `) b}: o" ^! v0 v- T
问:input_event函数如何上报事件
+ M* S" N! w$ q( h$ N$ w
0 w( k- X- O2 l- { W- w答:
5 N% p8 r2 q7 |" w
( ?: C" N6 O9 p# ^7 `! oinput_event-->input_handle_event-->input_pass_event
- x$ |' D* S i" e$ Plist_for_each_entry_rcu(handle, &dev->h_list, d_node)* e4 P+ \) i8 ~! }7 _4 y
if (handle->open)5 _7 M) [1 f$ e( s4 O
handle->handler->event(handle,
( u/ O5 g0 `6 Z4 C1 j3 otype, code, value);8 M8 y6 W8 n- B- i/ b. O7 O
怎么写符合输入子系统框架的驱动程序?. @- @! R8 U3 K$ `# b$ }
$ |3 D' @) `2 L
1. 分配一个input_dev结构体
( L; q6 j1 G- [- s& F: K# S2. 设置! _- `! e: f* u; c' H X4 A
3. 注册
' M* C$ s+ f, I, R+ Q8 g4 m7 p( }4. 硬件相关的代码,比如在中断服务程序里上报事件7 K; N: {1 i1 u2 Q- H$ @# x
# h d5 [( Z( p3 f
5 f4 ~( y# |$ y7 } P; V7 O& E+ V
( |# m0 W4 Q( N, c3 m/ F+ B$ F0 c* r5 u* ?
9 p9 N7 N. ~& R# H6 u6 o: j
|
|