|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
在此文章之前,我们讲解的都是简单的字符驱动,涉及的内容有字符驱动的框架、自动创建设备节点、linux中断、poll机制、异步通知、同步互斥、非阻塞、定时器去抖动。
. n6 j1 n! g7 U+ ~/ z2 Y/ _6 T" p( w% I# h
上一节文章:
( l/ e+ I) {- _& a+ U- m# M0 x5 j0 E& S
8 k3 }' f# y- o M在这一节里,我们要引入linux的分离分层的概念,linux输入子系统是一个很好的代表,在讲解如何编写input子系统的驱动之前,我们理所当然的要先好好认识一下input子系统的框架。
) L! Y2 u, [% k/ \, y# \8 \, O- i- O' E) A
一、linux输入子系统的框架( s- ~) y& _9 R2 q% P2 @/ Z
0 V! j5 S3 t5 i下图是input输入子系统框架,输入子系统由输入子系统核心层(Input Core),驱动层和事件处理层(Event Handler): {. l" f7 r* P9 f
" I! z9 r# A; n P- l三部份组成。一个输入事件,如鼠标移动,键盘按键按下,joystick的移动等等通过% O" }5 a1 p* H2 M
9 }* d- R" ]! Winput driver -> Input core -> Event handler -> userspace 到达用户空间传给应用程序。
' }8 D% ^5 z. `/ ^$ s7 F' E8 u
$ t; P4 D9 }5 E7 ]% v% s$ b9 i
) Q; G& y' W6 }0 `$ ^4 S5 X* b* X' q! S2 n, O/ s
二、drivers/input/input.c:/ a9 k/ M* I$ p- k' r
( f6 V- `) c" @" s
入口函数input_init > err = register_chrdev(INPUT_MAJOR, "input", &input_fops);$ S5 _( N8 U2 ` A
5 N" ~( G, j' t( D6 Estatic int __init input_init(void)/ ~2 p) Z0 m& v" _9 k( _; Z+ A
{. z# V! x) h" U# A$ I
int err;
9 x3 D( \) N9 a0 z$ o0 [ ...
7 b W V& o* }) x /* 创建类 */
/ G3 \, e3 @; w err = class_register(&input_class);
. j8 L* G" }, E# q8 ? ...
8 K- \/ u7 n7 D4 p1 {% p& N /* 注册一个字符驱动,主设备号为13 */
! K& B, E6 \* |- i err = register_chrdev(INPUT_MAJOR, "input", &input_fops);) U+ X7 w7 M; M2 G9 e2 s' o v
...' t! D3 L9 k" s
return 0;7 ~. r: Y3 c( P
}: w7 X% Q7 }% e. ^2 B1 [ m w
只有一个open函数,其他read,write函数呢?
* h) s- h) i+ D$ N) d9 V4 tstatic const struct file_operations input_fops = {
! z8 \6 Z; [4 A3 s c- F" p; ?2 a4 x .owner = THIS_MODULE,% a y. k; W n$ B6 B
.open = input_open_file,( h0 ?4 {0 `# l4 E
};% X M+ C/ T8 r, V( Q1 D B$ w
input_open_file函数
) |) z7 D# t6 Z3 B* O B. n5 J9 A$ V; w E
static int input_open_file(struct inode *inode, struct file *file)# Y0 _" \$ n& G
{, Z* r" e, L0 S
struct input_handler *handler;! j! X8 D' S% S( _% d q/ T
const struct file_operations *old_fops, *new_fops = NULL;
# Z3 U5 I! L: r" c9 e4 o int err;) p! e: r4 E; h# P1 k
...
; H# P8 [" e: y6 s. J. y /* 以次设备号为下标,在input_table数组找到一项handler */( t( K; I' w7 L7 `6 y3 F
handler = input_table[iminor(inode) >> 5];9 X8 _* f2 u3 @0 a% j
2 G& x" n2 Z2 B" L A
/* 通过handler找到一个新的fops */+ \% e6 x# u+ i* C! f2 i% J
new_fops = fops_get(handler->fops);
' E* w `$ S" q0 o ...
, O& u2 w1 ?0 E+ d old_fops = file->f_op;3 O4 _+ B2 k4 _# i6 v: r
/* 从此file->f_op = new_fops */ ?2 e0 I! J) _) e5 i5 F' |" n
file->f_op = new_fops;
% I" _1 B6 K6 S/ l# M$ u+ g9 z ...
; ]( N- ~ ~8 S. P /* 用新的new_fops的打开函数 */
) N8 c; E" W3 C7 ^ err = new_fops->open(inode, file);
0 h9 X, z6 K0 B ...7 e) v7 B9 J' g# C$ V
return err;1 A5 R/ t% E. [3 P/ ], V1 t
}% m7 x( z; G0 m) G, o0 O
input_handlerj结构体成员
: z6 b4 ~. y, y5 T* ostruct input_handler {
" X4 g3 g' a: ]& V
$ B9 `, o* o" G4 t# } void *private; i; G7 J, K' e; ] R* g
: b9 A2 \0 D: H1 C' j5 g* x void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);) u: U5 j# G0 x) e4 F
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
0 g) T: g% e8 M void (*disconnect)(struct input_handle *handle);+ J6 s' y1 q) K: H
void (*start)(struct input_handle *handle);
0 [6 D- |# @$ p( W7 S3 P3 |- O5 m; p2 D& v5 i2 r# Z) g6 \4 `: ^
const struct file_operations *fops;5 p1 v; l7 ?3 J; D
int minor;
$ ]6 f8 i& w: j) \! b6 Q const char *name;
# ^& t# J9 l& Z0 j" x7 g7 V2 d: p5 n n
const struct input_device_id *id_table;! S; S( p0 h& M5 Q9 O* U2 t9 J3 i
const struct input_device_id *blacklist;
( s0 R; t( r% e4 N" {# Z1 B0 G- L x1 f/ R/ F8 q, \; `4 A% A: k; A4 d
struct list_head h_list;( g. D" T7 @+ B- z
struct list_head node;+ l4 V, p1 i8 y% E2 H' t: b5 { v
};2 F; a8 I# I) F6 I! z
问:怎么读按键?( }! g; x- p% x% Z
APP:read > ... > file->f_op->read : P5 S% w7 C" p7 U/ e
4 }8 E2 u: l1 n问:input_table数组由谁构造?
( ?, x. B Z! ^ f' @0 z
4 E+ Z: O: [8 E: v- J1 x. U; d答:input_register_handler
# }, J$ o4 T- t, }# t5 S- c" N0 T" O& Q
三、input_register_handler函数(注册input_handler)0 S: i1 N8 I/ J) R" [
( M# T$ P7 `/ o/ U9 o' e' F k
6 A1 x2 S; @' A- i. Aint input_register_handler(struct input_handler *handler)
c4 ^* ? j8 ~/ z' T# v{# O# E5 f5 L; ^5 K! c
struct input_dev *dev;
Z- B" q3 W6 _ ...
2 n* l1 v+ V: O! B- S INIT_LIST_HEAD(&handler->h_list);
9 B7 Z: ~" y1 U% E( x ...$ q- d* C8 `; A( y! ^, l/ L% h2 ~
/* 将handler放入input_table数组 */8 m/ @4 b. P; D# o3 N$ n; f2 V) B
input_table[handler->minor >> 5] = handler;
) ~7 ]/ K" b+ X7 \4 v5 L9 }3 p ...
" u# `, j( N" C: C /* 将handler放入input_handler_list链表 */% `2 }) z% ^1 S# ~$ T+ Q( J8 j1 f
list_add_tail(&handler->node, &input_handler_list);
0 U$ H" u( j9 T& s ...
1 F* l5 R$ r" O X3 } /* 对于每个input_dev,调用input_attach_handler( t. V6 V: L6 k9 O9 o# e
* 根据input_handler的id_table判断能否支持这个input_dev
$ H( A5 L. M" z$ e y( C4 u */9 G' m- G7 }. {5 J* n% c
list_for_each_entry(dev, &input_dev_list, node)! S4 H# K8 y! x
input_attach_handler(dev, handler);
" e% J* y2 z, O ...0 g4 i+ K5 N# A" x j9 d& e
}/ k9 Y' v7 R: P2 U6 {+ J
- w8 L5 l: X, L四、input_register_device函数(注册inout_dev)
$ \8 X4 k) v2 {4 e5 {
0 P8 ^4 h7 E6 B7 V$ R, zint input_register_device(struct input_dev *dev)
3 E, }- e. z3 q* ]$ l" V% t{" P8 }; U+ [& L' O k6 u( B
.... ~1 ~0 F6 ~' y* X
struct input_handler *handler;& }* G9 S1 A+ s% o
...* p3 w# b N9 t# J' o
device_add(&dev->dev);
1 @7 Z. p6 B9 h# ~* X8 V% M2 `5 Q ...3 y$ O6 ^( d' P! T$ j0 t* ^3 q
/* 把input_dev放入input_dev_list链表 */! E& M* o1 B9 ~9 |$ E
list_add_tail(&dev->node, &input_dev_list);
4 z$ ~0 C5 ?7 J4 _0 U: o9 Y5 V ...
) o% H1 E0 v2 U+ e) y /* 对于每一个input_handler,都调用input_attach_handler
6 M3 U v$ @" v9 p: w7 L+ n) q" P * 根据input_handler的id_table判断能否支持这个input_dev
: \! ~, d' K; p" A3 v6 d f */- Z$ l7 {- ]0 Z0 }. }+ e& Q( K/ G+ [
list_for_each_entry(handler, &input_handler_list, node)# {& M# b4 V0 Y( \% Z; ]3 ]
input_attach_handler(dev, handler);
& P/ b, z2 O7 _1 y ...
$ d" ?8 y5 }# H! P: y}
& b. }) R, P. f/ D6 c
4 P6 J: C6 a; L8 z& t( J五、input_attach_handler函数. H, w" W% y& G6 l8 M
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler); _4 a t/ g7 p/ W8 l/ W, N( g
{# x: r/ L/ Q# q' f6 |9 O0 a N+ M
const struct input_device_id *id;
! G# G8 y: l7 _" g/ ] ...
- }& r& F- C9 t2 D8 U, N /* 根据input_handler的id_table判断能否支持这个input_dev */& `, ^# i- ?# k9 H( h' t, G. W- @
input_match_device(handler->id_table, dev);0 H5 v$ S q5 B7 f* X- ~, s# z
...0 F* Z9 U1 n5 U1 \7 L2 D1 f
/* 若支持,则调用handler的connect函数,建立连接 */
8 j! g8 o2 e8 J$ X/ ^ O$ H handler->connect(handler, dev, id);0 Y1 I- P" t4 }; a& o D
...' y( ~6 D" ^" R7 w( X
}
/ V* d, W# d5 t: M7 v
8 r+ ~, P b8 P9 l G1 D小总结:& q% q& o, b% N) A5 o
注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,根据input_handler的id_table判断这个input_handler能否支持这个input_dev,如果能支持,则调用input_handler的connect函数建立"连接"。
' m# v( K# I# V5 T9 _) a) m$ t. D9 L& c
问:如何建立连接connect?& v: h3 o. g6 E) g' H9 k/ }
4 b( `+ g+ }" g2 Q- q
答:举例,evdev_connect函数
( E0 C+ a1 @0 y( S2 ]$ q3 K
9 g* q$ z2 }9 B! h2 M
9 F) ]# C3 s" s Ystatic int evdev_connect(struct input_handler *handler, struct input_dev *dev,* h# Q$ X: F7 C: r# P! n& X
const struct input_device_id *id)
% K6 A0 ^- k3 G8 H& S; L{
7 L# ~+ e1 N7 M" `" _ struct evdev *evdev;! @0 u& E! {5 S2 f; {
...) a: r0 E( f6 d( P% F
" [7 x9 M' G. W/ B8 i" m
/* 分配一个input_handle */
# H& `& e0 O& U y; t7 k evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
9 k6 E# t/ k" B8 Y6 g& m. o2 ?% w4 Q ...
1 G. r6 `3 |; b! r& Y snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);* b. J) a1 A# y; p$ i4 y
evdev->exist = 1;% `$ o; |0 P( j- N0 h! \
evdev->minor = minor;2 C9 C; ?2 H( o% I& j" ?- D5 ]
9 G" C# k" ]. U, ?# G
evdev->handle.dev = input_get_device(dev); // 指向左边的input_dev
; y/ b! y$ X7 e) d: | evdev->handle.name = evdev->name; _" ]6 O* v$ I1 V9 \
evdev->handle.handler = handler; // 指向右边的input_handler+ |5 C' X3 D: `. k" V n
evdev->handle.private = evdev;
# u3 |( l; T, C2 A1 H2 X2 K" f, S: q# X( E9 N
/* 设置dev结构体成员 */
7 S! r/ C; h. _. r9 b8 E ?- e* G dev_set_name(&evdev->dev, evdev->name);
$ T' h$ X" n* |* o evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
$ ^" h- G9 v+ X, Z5 j0 R evdev->dev.class = &input_class;: o a, j" `& e8 w5 k) ?
evdev->dev.parent = &dev->dev;- L7 j+ Y$ l3 T) r- J
evdev->dev.release = evdev_free;7 O, V# ~4 [* \% Z) o- B) ^4 o1 X
device_initialize(&evdev->dev);
, g6 Z4 @7 g$ V7 h
* ^' K0 i5 Y4 [0 i F6 G /* 注册 */
6 t0 {. x; Y! j% e7 v input_register_handle(&evdev->handle); E( H; l1 [( V$ S
...
. }$ f* D% x8 y}
+ ?4 ]. V3 D5 x, x( G4 }' einput_handle结构体成员8 p+ Y: t' L) X; ? s* D" H
5 {! U b1 a2 Q estruct input_handle {# g, N! L8 t B; F) ^
0 ^' B, z) w6 I1 {2 p/ W* w$ I
void *private;
% n0 ^, \! O2 A V @' L$ h: S3 \6 B
int open;
% v( k" R! J% D2 w. x const char *name;
( R; W: U2 J6 M1 X) r5 V, `# z& }6 e% c
struct input_dev *dev;
+ q3 I6 H9 q r- s' y struct input_handler *handler;4 i9 y7 w N4 N) X* \+ S
& V8 h* f6 Q8 X; O, |2 [( P- n
struct list_head d_node;
+ Q0 p! P, Y4 U+ W4 U# D6 J/ u struct list_head h_node;
+ b% s1 T* w& ?1 v2 O};8 z1 s& s8 D& Q, U
问:input_register_handle如何注册?
; Z1 a4 c. ]& d1 \' y. B7 |; h$ h9 \
int input_register_handle(struct input_handle *handle)" ^5 {! y, O" M5 K5 x
{; G, A! l3 z% T/ [4 S% ~
struct input_handler *handler = handle->handler;
. U( f. ]1 R' S3 v" \0 d struct input_dev *dev = handle->dev;7 j6 l; f9 `. b6 W, J# E( }
...$ I8 w9 I$ X! }4 s, @
1 ^# i0 g5 G" ]: c
/* 把handle->d_node添加到dev->h_list5 N8 s! Z4 W& L+ O! E$ Q8 ?! l
* 这样,就可以从dev->h_list找到handle,进而找到handler5 ^' ^7 G Z' H$ ]: y# D) s
*/
n8 ]' g8 X+ o I- D: I list_add_tail_rcu(&handle->d_node, &dev->h_list);/ g) g- R W* R/ T
...0 l0 R* L7 o8 E) J
: ?3 R1 d7 }7 H8 X; l$ M9 M8 r" c% M5 x /* 把handle->h_node添加到handler->h_list
$ ^$ ]6 r2 |; n( l- @ * 这样,就可以从handler->h_list找到handle,进而找到dev) @) I M( |# H6 l
*/
U4 f0 e7 Q, w/ W4 u6 b list_add_tail(&handle->h_node, &handler->h_list);3 v Q0 I( u% ]$ n
...
2 E% L% S) S! l. J return 0;
: [- o8 `, j& Z! j7 f& H6 A7 O}! `2 t% b% X& ]8 l4 L
小总结:
. x; R/ L: Q' _0 }) h; J怎么建立连接connect?7 M6 `. H7 ?" U& P
1. 分配一个input_handle结构体/ J2 |9 g0 [1 L4 F; n
2. + l9 I( S/ E8 e7 Z/ P
input_handle.dev = input_dev; // 指向左边的input_dev; o8 e$ @ C& n9 U5 s# r
input_handle.handler = input_handler; // 指向右边的input_handler
' b6 f' E7 o( n; D: k, [) b+ K3. 注册:
! T& g- w" D, F input_handler->h_list = &input_handle;
' o. g8 _8 R6 H( e# ^. a5 E inpu_dev->h_list = &input_handle;8 m+ f- i$ i: q/ P% a& O' r
% t2 V; C# t: C( l n六、怎么读按键?! u7 K* m4 h7 [# G: N
* N$ i& G5 z- A
答:举例,evdev_read
+ k4 C6 ]. C% l5 }8 Q0 |
1 A$ ^) s0 t' j }& y# v& xstatic ssize_t evdev_read(struct file *file, char __user *buffer,8 [' J2 y3 M2 K. R) }
size_t count, loff_t *ppos)
* W8 P ?/ H, H5 G{, C; S$ o5 f- X4 ]3 Q& g
struct evdev_client *client = file->private_data;
! D! y8 Z- W4 B' ^ struct evdev *evdev = client->evdev;
+ T5 V7 M' b5 |0 L& E struct input_event event;
$ n2 K. H8 u q+ F% t$ Y ...
! V9 a) c% V, [- l( a
: m$ H, ]/ e0 K7 q* H7 i /* 无数据并且是非阻塞方式打开,则立刻返回 */
* U+ y5 N8 j: }/ v5 U4 o: V$ ` if (client->head == client->tail && evdev->exist &&$ g( B7 x5 K+ D. Q
(file->f_flags & O_NONBLOCK))$ u, _* ~* H4 _1 i
return -EAGAIN;
$ Z _) N+ j; ]
& _# X/ x q) I) _ /* 否则休眠 */7 x' }1 I n) E7 F6 B3 X7 J0 ?
retval = wait_event_interruptible(evdev->wait,
" ~/ H; s: ^/ `$ n+ a6 w# ^; R client->head != client->tail || !evdev->exist);
3 p, p x% o) Z9 ?/ o( U ...# G; [5 m* ?' j, p) o2 g1 W
}
. [; L1 [- G! s A问:谁来唤醒?# j# V# z3 | s4 M0 r- S
搜索evdev->wait发现是evdev_event唤醒的
* y, `1 C( ]9 k. Z) w7 p" c. W) n; `" E9 d+ w% X
static void evdev_event(struct input_handle *handle,
$ z) A+ A0 J3 u0 j unsigned int type, unsigned int code, int value)
2 b" ^+ x& G V$ K4 p+ }( @' ]$ z{
+ {2 T- K- p; y* `& G( p; N struct evdev *evdev = handle->private;
$ S* a% I- v9 d) `( h4 w, s struct evdev_client *client;
: b X; ~5 w& f7 ]* D1 C struct input_event event;
% F; i& W3 C9 h: T' j- R7 T: m2 H ...
& f3 `7 i3 w( v5 b# T; P /* 唤醒 */% V4 }* z' m/ Z& E; C6 m9 ?: S
wake_up_interruptible(&evdev->wait);) `, o8 U' o! _( ]9 k/ C
}
1 `; v k& ~! o# V" e问:evdev_event被谁调用?
! g0 ^" U3 ~4 S" ]' z# {8 j答:应该是硬件相关的代码,input_dev那层调用的在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数。
; t* `7 i6 H# Q& {0 D1 Y3 Y' Q% N1 \6 Q2 m7 s2 N
举例,在drivers/input/keyboard/gpio_keys.c里的gpio_keys_isr函数
5 {% y7 l4 X7 x& a! g5 u* ~) f* g T# O5 m2 x2 n# e
static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
" ~! a* L2 q/ T{% Y! n1 A2 D8 p6 k3 ?, \; [" b
struct gpio_button_data *bdata = dev_id;8 a' R7 C0 ?) u8 X3 m+ L0 f
struct gpio_keys_button *button = bdata->button;8 A3 l) v- B& M5 J4 s5 t8 m! E
...5 d: s9 \) h0 Q( j4 l' x1 @' w/ z
/* 上报事件 */
- Y# x; c4 @/ L gpio_keys_report_event(bdata);
0 I+ g- `2 n- L5 C return IRQ_HANDLED;, Q% _. R& @+ r6 n
}
3 D) E# }: L) v- mgpio_keys_report_event函数
( e% ^. A8 l8 u/ c" k0 |0 J# i7 n
( C0 ~: C( P: K; Y' jstatic void gpio_keys_report_event(struct gpio_button_data *bdata)5 U3 u- C: R% B2 A! i
{1 p/ J2 V1 |. D6 E
struct gpio_keys_button *button = bdata->button;
" O* K3 G4 A% n3 H+ S K2 B3 Q c struct input_dev *input = bdata->input;
6 C! t% p5 N2 v. k, Q( Y unsigned int type = button->type ?: EV_KEY;
' c$ I' v4 d2 F! @8 F# c( { int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;$ U ^0 \! y ?. N4 h* |7 G
- `( O- V% h! W' ?5 {# C
/* 上报事件 */7 X3 S: i `& Z6 w0 F
input_event(input, type, button->code, !!state);$ V/ q% i/ T9 O7 @' |1 s Y
input_sync(input);
/ ` q3 t# n- B0 |* y}+ p# W. f, b* B! l) |
问:input_event函数如何上报事件6 Z" [0 f7 Z1 Z, T( i# a
2 i4 `6 w+ W! Y$ C1 }" I
答:2 |1 ]' i# i+ P5 i# P
+ p0 i) D1 ] B% W% V3 l
input_event-->input_handle_event-->input_pass_event
. l( V- a. S8 c% A2 wlist_for_each_entry_rcu(handle, &dev->h_list, d_node)% x+ n1 i" D, h6 L* g
if (handle->open)
. s' K0 y5 r) r, ?handle->handler->event(handle, K- E4 i+ n9 y6 s/ b4 R
type, code, value);, I* Z: }$ D3 l* V! v7 J$ O. h0 K5 c
怎么写符合输入子系统框架的驱动程序?8 x( e1 B# y! g6 T e/ \0 a; m
3 ?- L9 U9 |7 C) f) J
1. 分配一个input_dev结构体9 d( D6 L2 N! @/ Q+ @
2. 设置8 H" n. C, ]' o
3. 注册) e. W# b& r2 P- K9 T
4. 硬件相关的代码,比如在中断服务程序里上报事件
$ \) U' y0 B1 o" U- o3 K9 z) T- X9 O" a0 `2 Y+ S3 C3 e2 z s( l
4 i7 d! }, W2 E) C6 K
! K: `$ R- [. `& p$ b
3 B D* D1 K# C" s$ c1 _( V7 u
9 K7 c+ b& u1 e }) @$ d* { |
|