|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
在此文章之前,我们讲解的都是简单的字符驱动,涉及的内容有字符驱动的框架、自动创建设备节点、linux中断、poll机制、异步通知、同步互斥、非阻塞、定时器去抖动。
5 B, T! G6 j& B" f
$ m! I/ i8 e# m9 w: ~0 A) f7 ^: d上一节文章:
! x: b% Y1 w$ }" ~5 U
! G% F: c) _, m( _9 W+ K3 ~. }+ V% J8 x# E1 f4 n
在这一节里,我们要引入linux的分离分层的概念,linux输入子系统是一个很好的代表,在讲解如何编写input子系统的驱动之前,我们理所当然的要先好好认识一下input子系统的框架。
; x: g. j- \% V/ r/ p6 y( W* H* u$ d- p- d7 M5 f f
一、linux输入子系统的框架
. ^! e. N2 O0 j( a) c7 E N
0 w/ |7 \7 G+ j2 e下图是input输入子系统框架,输入子系统由输入子系统核心层(Input Core),驱动层和事件处理层(Event Handler)% s; Z6 X, W$ f w; ^; `3 D" A3 |
3 w2 m9 Z. G a, e4 |# Y% Z三部份组成。一个输入事件,如鼠标移动,键盘按键按下,joystick的移动等等通过
' o- E6 c/ R8 z" [$ u" R2 h: F, K Z( }- ?5 I+ I
input driver -> Input core -> Event handler -> userspace 到达用户空间传给应用程序。$ t. R5 W- o% Q4 c8 J- j) U
) f9 ^3 ^+ K& T6 K9 a8 d
8 `1 Q) c+ K8 _3 Y8 Y8 P0 S6 l! Z& J
二、drivers/input/input.c:% z4 z& u$ ?2 h2 Z, t8 ^
/ X9 i8 C9 h+ G: m% a2 T: z入口函数input_init > err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
% W# R$ w- t9 b( T0 X0 N
# \ n& v7 u5 K. [0 l3 c, hstatic int __init input_init(void)
* O$ J0 C: B+ ^9 w{
! ?) x1 B1 b7 m# `/ S int err;
4 [/ |% J2 @% o ...+ `' t' I& z) I" B3 F! L+ ^
/* 创建类 */5 N ^& r. H# {9 ]! |' e% r3 U
err = class_register(&input_class);- F4 O3 O* e) j
...& i: d$ |2 @0 E! P- w
/* 注册一个字符驱动,主设备号为13 */
- r! l; W( R" [ err = register_chrdev(INPUT_MAJOR, "input", &input_fops); X: |% |" z/ X/ n& ]
.... [0 k7 Z1 n: J& m: D5 t3 b
return 0;4 B( h6 Q/ E8 R* | a6 U" o
}
1 I9 S+ {9 x$ S8 E) V/ U只有一个open函数,其他read,write函数呢?9 I# d9 s6 T8 ?5 X6 ?. B* l" w) B2 e
static const struct file_operations input_fops = {! c9 W6 P1 }' _# r7 i/ I
.owner = THIS_MODULE,2 \/ G/ ?. K: X( ]* D7 r
.open = input_open_file,
$ `$ \- h) R: ^};; ? @# s& w. b5 s, G w" h
input_open_file函数; t+ Z# t g0 h0 h2 a0 \
3 e* h2 f# O- e6 y* B' Pstatic int input_open_file(struct inode *inode, struct file *file)) i" G" G( a- s. R& M& g& r' m
{
& r% B3 A* `2 B& |) b. c struct input_handler *handler;
% V# Q a) |( m2 P! C const struct file_operations *old_fops, *new_fops = NULL;
$ q6 H6 w6 K+ q& h, F! B/ D% Y# s int err;
) q" m. e! ^. `5 U/ b0 Q& H4 X/ h ...
/ O% q0 K3 G/ X! }) u /* 以次设备号为下标,在input_table数组找到一项handler */
0 n" I( {) M, F$ _, l8 } handler = input_table[iminor(inode) >> 5];
1 h8 F* v# r; g' b( F- Z6 C 5 S& _: I# L+ s& s" F% O& s$ L W
/* 通过handler找到一个新的fops */
0 a7 g8 [" ?+ Z3 V1 D: o2 y new_fops = fops_get(handler->fops);
8 Z$ t# [+ z5 d, r- r1 h' I: X ...3 g2 F! ]/ \7 l1 |7 Q$ O
old_fops = file->f_op;
; g' w! Q, n* C1 x% e /* 从此file->f_op = new_fops */+ m$ g5 N6 _3 o7 ~; H4 q7 }/ O
file->f_op = new_fops;
2 Z$ F0 V3 M* f1 x* u ...
) _% g" T# H& r( E0 v /* 用新的new_fops的打开函数 */
5 ?! g2 T+ }8 [$ X! w5 l) U err = new_fops->open(inode, file);* F6 @% S5 M+ v0 B% _2 e% `
...
, y: ~; n, z ^ I return err;
+ S# }# d( V" u}
; K3 `: T. J; ~4 s5 sinput_handlerj结构体成员
* e- W$ l9 i# ^& D c9 I: k/ estruct input_handler {' i/ a: b6 ?& h$ Z( o
- [! F3 S( P, K6 d; {
void *private;
|1 h g+ E* R' Y' ]8 B1 X
: P# h* L5 A* W! A void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);' B; ?2 q2 q6 f, d6 N3 G
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);% Y; r$ v) ?' @& p: T. f; b
void (*disconnect)(struct input_handle *handle);5 L+ R5 H% @, v2 B
void (*start)(struct input_handle *handle);
5 d, j9 S _. j& ^1 X, m. _6 B, { l" q" H
const struct file_operations *fops;
# }: Y" F) z( r; Y0 l int minor;
% H; s; F! S4 z! v6 f; R( e const char *name;
' _3 |& C% i# G+ q& C8 x! W( b* F$ M$ D, u" `/ n
const struct input_device_id *id_table;
4 S+ |' k L* B const struct input_device_id *blacklist;0 Q3 y5 W2 ]* ^% F. G& I
* P: P, T8 ~! O struct list_head h_list;) j( T! o, K4 R e0 w; Y" o
struct list_head node;
1 q; J) z# q5 `0 F4 H};4 X* G9 {3 k; g
问:怎么读按键?
5 q7 P2 u% m( Z* kAPP:read > ... > file->f_op->read 8 b+ G/ k! r8 X% t8 x$ V+ K4 W7 c" p
' p7 f, E7 h' |, M0 \
问:input_table数组由谁构造? r$ n- [. M2 B4 j/ O# M
1 [/ l% C5 K. f. T; w, w
答:input_register_handler. J" }# b0 l, } x, D
% Y8 g) I3 B- s& z
三、input_register_handler函数(注册input_handler)# {8 ?9 U8 B2 o: Q
, N: t! {( {& b( }8 b7 s5 Q* u/ r
* K9 {. }% t8 }% G5 U1 C. D. k
int input_register_handler(struct input_handler *handler)
- f, Y) R) b/ p$ f{( y3 ?$ d. k2 @
struct input_dev *dev;& R% ?1 u- j% x
...5 d0 I- L3 K7 S1 P/ y" L
INIT_LIST_HEAD(&handler->h_list);
6 u) B) Y# l" W4 b) V3 q W0 c ...
4 J8 \$ o5 K. @9 h /* 将handler放入input_table数组 */
% i' i' G8 O5 t& Y( h# ] input_table[handler->minor >> 5] = handler;
! a* @+ F# @& r' Y) W( @; a4 j ... x3 [/ ?$ [0 _
/* 将handler放入input_handler_list链表 */
) W" B% H) D; \9 j list_add_tail(&handler->node, &input_handler_list);
+ X% [7 q6 P- j9 i [/ H' Z( b$ N ...# J' W) v; K" a- a8 p; r# V% q
/* 对于每个input_dev,调用input_attach_handler" q2 f# E" l' ^6 X6 H( |2 {
* 根据input_handler的id_table判断能否支持这个input_dev' Y8 I* \0 B5 a! ?/ X" E3 }
*/2 _6 ]) x& B% U2 W
list_for_each_entry(dev, &input_dev_list, node)' M( b- p: }# R
input_attach_handler(dev, handler);+ B0 c- G8 w5 Q0 N W8 D k
...
0 z( d$ v; X7 B! w1 B1 F}
$ Z, T7 e+ v2 i' g% f0 @+ H3 J; o5 ? R
四、input_register_device函数(注册inout_dev)3 u9 m9 ~2 n* ?3 m! P
9 ?) s G9 W& j) M5 wint input_register_device(struct input_dev *dev) D. ]4 c9 C! K. f; T w( i
{
9 {% h1 W# R* z4 Q/ R' ` ...
/ v) \. X# L3 A4 F6 i" _2 C$ t struct input_handler *handler;
( e ^3 u( ?8 I$ Q4 C ...
$ x8 G" Q- q+ ]+ {$ c$ T device_add(&dev->dev);
' k8 P* x! S4 w9 Q- l ...
+ `/ A8 v P7 J& F: V# Z /* 把input_dev放入input_dev_list链表 */
* J7 p# e" }" F \ list_add_tail(&dev->node, &input_dev_list);
5 A8 c! K! `" @ ...
% y5 T# C! j, o. _ /* 对于每一个input_handler,都调用input_attach_handler# c$ F, A3 G+ M9 F% ^) p
* 根据input_handler的id_table判断能否支持这个input_dev
0 I: v7 d7 Z- r4 |, x: t */
" a3 g k- y& B- z7 k: R list_for_each_entry(handler, &input_handler_list, node)
* K& b9 Y( z. |, V; |" ~, ]7 Q+ i input_attach_handler(dev, handler);
: n: Y4 o5 ?8 Y8 z ...
; ]* P) u/ x! p( U4 l}3 B8 K/ K8 T: E( k4 {
5 ]' [ x+ [9 J" r- S
五、input_attach_handler函数
& z# H" z* W- n- M' sstatic int input_attach_handler(struct input_dev *dev, struct input_handler *handler)' {$ y: Y# V- }- q* U
{
# O) W- w0 ^3 H9 h/ f const struct input_device_id *id;
f& d6 x. h! P0 J5 h& l3 V% e6 w ...
; r& ]# }7 J- }; d /* 根据input_handler的id_table判断能否支持这个input_dev */
# T( h4 j1 o- T input_match_device(handler->id_table, dev);
" A- C5 N5 c6 H, V ...
& c# z) a& r" d5 |; [4 {4 Q/ {" a- v& ^ /* 若支持,则调用handler的connect函数,建立连接 */
; [5 h1 Q" K |7 ]: {2 R handler->connect(handler, dev, id);
* R& f" Y. E+ f7 {$ } ...8 x- D/ d$ r) |' N G
} g. D; r6 E0 g# r2 k5 X- {
7 R. h. ^" s w) j小总结:
# i1 V3 M+ H7 c* k注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,根据input_handler的id_table判断这个input_handler能否支持这个input_dev,如果能支持,则调用input_handler的connect函数建立"连接"。0 g6 N6 H) [' W. c
, \1 ]' M2 y7 {( y: f. `# o问:如何建立连接connect?
& G" |3 Y( N: O- O3 j
( r5 y4 @# g' a/ t" @' Z e! [% q" f答:举例,evdev_connect函数& o, e- Y: I6 q+ J" }
2 E+ ~1 L! l$ G7 C
' K+ i. y1 {7 J9 ?3 n, astatic int evdev_connect(struct input_handler *handler, struct input_dev *dev,4 r. {& r; [7 a7 k, _6 M6 s
const struct input_device_id *id)
% S. b& F. w, D! k4 o9 G{* s9 D: k0 p# a! G$ @2 p E
struct evdev *evdev;! b! `0 c! S6 Q- r# x1 E7 W) ~. b5 d# {
...
" ^6 I( o2 ]) r( a2 r" e
2 U1 ], q: Q, b7 e /* 分配一个input_handle */
4 y" E9 H5 A5 o- C0 c4 s evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
2 z) Y8 p4 c/ I# f( G ...! V) a% m q& ~- I0 L+ g# L
snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
3 ]$ x+ c3 G Q2 d: W evdev->exist = 1;
( _* \& a5 n; a3 j evdev->minor = minor;* I) u+ k2 @: u4 L2 b
6 T9 K. y, {+ K+ d; X( f
evdev->handle.dev = input_get_device(dev); // 指向左边的input_dev
) `" j/ c. c. e1 _ evdev->handle.name = evdev->name;
7 j. T# z% N1 P6 H2 E7 Q evdev->handle.handler = handler; // 指向右边的input_handler
$ T: v: m# n6 S4 }3 v evdev->handle.private = evdev;
% I2 B/ X/ V+ P; |7 i [9 {" v
6 G# H$ a+ X4 N& m$ f. F /* 设置dev结构体成员 */+ E2 M8 L1 ^! Q
dev_set_name(&evdev->dev, evdev->name);7 S# ^" K f( C& Z3 C
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
3 Z4 u& S# p1 E evdev->dev.class = &input_class;% m8 q6 ]& ~, n
evdev->dev.parent = &dev->dev;
8 c- s: ^. J6 K% A* y6 o5 e6 J f evdev->dev.release = evdev_free;3 s; ?6 z: J3 d5 \" D+ X5 X4 Q' T
device_initialize(&evdev->dev);4 b- `8 L) g4 |3 Y$ D
' Y, ~. E d+ |9 I0 K1 P
/* 注册 */7 l4 e+ W3 \2 v- {
input_register_handle(&evdev->handle);
8 T& \: _% t' N& q1 e ...
) R$ k, ]( P4 o# X4 Y! n( t% u: G}6 t3 S: } H9 W& D2 ^
input_handle结构体成员0 ^2 n! F1 o2 t" w/ P
+ j$ C2 E4 \8 S6 i- E9 Xstruct input_handle {' Z9 H8 C5 f, a5 ?* ^' p+ ?- X
" M; h: ]1 J) N7 j- O: i void *private;
. c B4 C8 V( L" z1 o4 `2 z! b, d% C. _# b% Q6 m* \# K p
int open;
5 ~ z, N. S& G, {( E- [5 n' M const char *name;- y) u$ t6 e$ \+ D+ m
6 Y. V( |3 G1 g# H struct input_dev *dev;
; W9 i7 S& c, o$ C struct input_handler *handler;
( v2 A$ d7 I8 D0 [& p/ `* a W! T# K2 l6 s* a+ S
struct list_head d_node;
& O, V6 d. j4 k struct list_head h_node;
' f. \# O s; ^1 O% `! C};* s/ N# o* `8 w9 F1 u+ l1 _% j
问:input_register_handle如何注册?
" \) Y. l7 T2 g+ v6 E& m9 {; w" Z; g9 S" t* P5 e2 T1 I
int input_register_handle(struct input_handle *handle)+ Y% S6 } U9 n
{* U: U" y# H. n% D/ `- Z
struct input_handler *handler = handle->handler;
2 t& V' g) w) @- l- Z1 l! u struct input_dev *dev = handle->dev;
1 c7 x9 W2 h$ o+ ]9 v8 V7 k ...+ `' L& s& }7 F( M* ^) p
2 b" b% T3 b7 h" L h$ q. ?% n* `+ h
/* 把handle->d_node添加到dev->h_list* c c$ g+ `# k1 e
* 这样,就可以从dev->h_list找到handle,进而找到handler
) N2 j2 x8 D- ]* n9 C */2 P2 ^6 ]9 @" a$ F
list_add_tail_rcu(&handle->d_node, &dev->h_list);
& O8 s, V: j0 \! N9 U) Y2 s ...4 X( M( D' E8 t/ z
+ [$ J- M9 A! ^! b" C /* 把handle->h_node添加到handler->h_list
% h; v. O% k: T7 J. g1 T8 G1 R * 这样,就可以从handler->h_list找到handle,进而找到dev4 Z' U" b) `2 P, p! P. M3 t
*/
y h& Y) z1 s {0 z" B list_add_tail(&handle->h_node, &handler->h_list);, B% J, r: }$ x" b7 l
... V4 {/ V. ]" c U1 F- M* s) s
return 0;; Q9 r b$ M1 F }% n' C3 b" E( @
}' [2 X& S+ x" \, p' J' t
小总结:1 r# O2 X7 t5 c. P5 ?3 c# j
怎么建立连接connect?: X: p/ W& U# E* p2 B+ B. J s
1. 分配一个input_handle结构体
. t+ o* Y6 z0 T* ^$ {+ ^$ j2.
; i: D: c9 }+ a# Y2 }& Sinput_handle.dev = input_dev; // 指向左边的input_dev
0 A" G) j* g. ]; pinput_handle.handler = input_handler; // 指向右边的input_handler
1 q7 p6 o) ?9 p3 v2 v+ N; H3. 注册:; z$ q `, o. ]2 @2 ~
input_handler->h_list = &input_handle;/ k: I" ?& A" s- e8 }9 E0 d9 Y6 D
inpu_dev->h_list = &input_handle;0 d, h: o3 z4 x6 Q4 P1 F" J! g& B
- N. b8 T3 D1 r" K6 ~0 ^0 `
六、怎么读按键?0 ^, {( j% H- V, t Q, M' B
+ u3 Y. w9 e9 T# X4 T1 o
答:举例,evdev_read
) S! g: u" [1 W. U) c* m. F9 l+ p. n! o, Y0 U J7 \
static ssize_t evdev_read(struct file *file, char __user *buffer,
, @9 t A) Z) ~( ] size_t count, loff_t *ppos)! X! N( y# K, \$ O
{3 f- b: r6 _7 h. G# S$ {$ [! F
struct evdev_client *client = file->private_data;) v* d) P7 M) J: B5 L; c
struct evdev *evdev = client->evdev;
/ y; O! M! u5 |* S( V struct input_event event;* ?+ q9 f. h3 M. c" m
...
; v6 \+ L" x' p7 h }" E2 }; p$ z; ^) S- M) ~4 }; ^+ U4 U
/* 无数据并且是非阻塞方式打开,则立刻返回 */! x" X$ W. M4 w
if (client->head == client->tail && evdev->exist &&
2 W1 }7 |! B& x! m/ y (file->f_flags & O_NONBLOCK)): g$ X: q, ]7 w
return -EAGAIN;+ U- _8 b8 K5 ~3 j, T5 X- h9 q
7 W3 j% R' Q& F }+ D S6 G /* 否则休眠 */
( m! c; L2 |* R- y# x retval = wait_event_interruptible(evdev->wait,, T- C- U2 p+ y! F1 J
client->head != client->tail || !evdev->exist);
* K3 c! l+ C, U ...
9 x, _' W1 m' U! d8 f3 ^: X}& n, C i" @9 E' ~8 |
问:谁来唤醒?- Y: h4 U8 e6 B. o3 i
搜索evdev->wait发现是evdev_event唤醒的
% f: d3 H2 C3 w5 ]. I- _* W4 }8 p
1 Z1 z: Y/ m* mstatic void evdev_event(struct input_handle *handle,% t$ E% \9 j- ]- j# ^7 }
unsigned int type, unsigned int code, int value)
: \) Z {- n6 q, J{4 h# h9 ?, u/ z3 U* S/ d' Y: X
struct evdev *evdev = handle->private;3 x+ Q* s/ F1 ?5 `+ I
struct evdev_client *client;' Y* f( O/ F4 e$ g& s2 j4 n
struct input_event event;9 ^1 F& x) E# Z( d
...
( v) `, A8 m( g1 [0 b) J /* 唤醒 */
1 z( C& D. Z2 a O5 ]6 ?* F wake_up_interruptible(&evdev->wait);! @0 z3 _. U: ~9 g6 S
}7 h1 o3 F$ [9 M, m* x
问:evdev_event被谁调用?3 _/ D, \4 D/ f9 N
答:应该是硬件相关的代码,input_dev那层调用的在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数。
* F5 R, N5 t, ?6 D! n% R7 ]8 ], H/ S& q+ o2 i
举例,在drivers/input/keyboard/gpio_keys.c里的gpio_keys_isr函数
( L1 Y9 m0 [5 i4 o
( r& m9 d5 y0 Q& h' E* U' ystatic irqreturn_t gpio_keys_isr(int irq, void *dev_id)1 J9 ]( u: j( J. L
{* |# m) T1 D3 U. _
struct gpio_button_data *bdata = dev_id;: t4 ~ {; [5 t' G
struct gpio_keys_button *button = bdata->button;
9 m9 r7 N6 }8 q$ u# A# y ...* |- v, \5 l( d- K6 c
/* 上报事件 */4 u P# Q# I! I. o
gpio_keys_report_event(bdata);
7 O0 a! Y/ {/ |' `' `% j E return IRQ_HANDLED;
# @/ p8 B. I' m; T, p2 l9 v% g}' L1 L4 ?0 e: M8 F: z% q
gpio_keys_report_event函数; A! m, Q# v" Y$ f8 G ?* e) G
4 ? L; @! e' E/ J. \4 Nstatic void gpio_keys_report_event(struct gpio_button_data *bdata), S1 b" a% N" |( M# \
{
+ U4 C6 i6 p) |2 N struct gpio_keys_button *button = bdata->button;
' |. c8 M4 l! Y" B# F struct input_dev *input = bdata->input;+ ]/ ^6 A/ @) g+ P
unsigned int type = button->type ?: EV_KEY;8 [0 Z* j; a* M1 Y/ E
int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;
5 ]& P4 B/ |6 R! l2 f1 {* S! w- _" Z, p8 V: x3 L e o' w
/* 上报事件 */
2 g5 J N* _$ Y Q# u input_event(input, type, button->code, !!state);
) |1 `/ [2 [& {- u3 v% G8 \4 N input_sync(input);
4 E' ~& }$ w1 y: M% [}9 M' T/ j- ^, F3 H; I( H
问:input_event函数如何上报事件* M' C8 c+ \; |9 [ F9 X N) b5 b
6 l; r7 p3 O3 N" h
答:
- R* ^9 g/ ?$ q+ h: v( _- g2 F( m9 G1 {" B" E+ _
input_event-->input_handle_event-->input_pass_event& J) K' U7 J; W+ d7 d" Q
list_for_each_entry_rcu(handle, &dev->h_list, d_node)1 x. B, ] o; C
if (handle->open)! @8 W3 K0 j) Q8 |) @
handle->handler->event(handle,! e0 y: E+ \! w
type, code, value);0 F/ O( @7 X( f9 |3 P$ V
怎么写符合输入子系统框架的驱动程序?0 m9 ?. t% O: E1 D5 u/ d
" {5 X6 w+ ^4 f" Y
1. 分配一个input_dev结构体
8 b; y7 e$ J6 m+ M, }2. 设置
1 {: x" ^! s! R9 n! {# \( K" p3. 注册8 Y8 y% I" y. d' B" p
4. 硬件相关的代码,比如在中断服务程序里上报事件9 @- ?/ ]/ c: y/ K
4 Q* ^/ D' q; n+ V3 r" L3 x" x; @4 q$ b* j4 ~/ s/ v4 [! q
$ g0 x" h# Y; [2 l
8 o' |$ F* {8 w$ v, V5 U: q5 @6 x0 ~' I4 E3 V
|
|