Linux內核驅動學習(十)Input子系統詳解
文章目錄
前言
框架
如何實現`input device` 設備驅動?
頭文件
注冊input_dev設備
上報按鍵值
dev->open()和dev->close()
其他事件類型,處理輸出事件
查看input device信息
附錄
前言
這次主要會學習linux中對于輸入設備統一封裝的框架,在計算機組成原理中,我們可以知道計算機的組成主要分為五個部分:控制器,運算器,存儲器,輸入,輸出。可見,輸入作為其中的一個子系統,但是對于眾多的設備來說,需要一套統一的規范。所以,在嵌入式系統中的外設,鼠標、鍵盤、按鍵、G-Sensor等等都可以注冊為Input設備。Linux在用戶層提供了相應的接口讀取數據,這里我暫時只介紹在上一篇文章的基礎上,如何編寫一個Input驅動。
框架
首先還是先看一下Input子系統的整體框架,具體如下圖所示;
如何實現input device 設備驅動?
如何編寫input驅動的sample?在內核文檔中已經有詳細的介紹,雖然是英文的,但是相當規范,簡單,詳細,本文就是在此基礎上展開,具體可以參考內核input文檔。
在這里,我們需要在代碼中做哪些工作呢?下面將會重點羅列成每一個步驟進行講解。
頭文件
首先需要包含頭文件#include
注冊input_dev設備
首先定義一個用來注冊input device的變量,這里會用到input_dev這個結構體。目前的情況是,我把需要的各種變量都封裝到gpio_demo_device這個結構體中,但是,實際上這里暫時這關心input_demo就可以了。
struct gpio_demo_device { struct platform_device *pdev; struct device *dev; struct gpio_platform_data *pdata; struct workqueue_struct *gpio_monitor_wq; struct delayed_work gpio_delay_work ; struct input_dev *input_demo; };
1
2
3
4
5
6
7
8
9
10
在這里需要為我們創建的gpio_demo_device結構體定義一個變量priv,并且使用devm_kzalloc為其分配相應的內存。
struct gpio_demo_device *priv; priv = devm_kzalloc(dev, sizeof(*priv) , GFP_KERNEL);
1
2
然后,我們再為input_demo分配內存;
priv->input_demo = devm_input_allocate_device(priv->dev);
1
初始化input_demo;
// kernel/include/uapi/linux/input-event-codes.h priv->input_demo->name = "input-demo"; priv->input_demo->dev.parent = priv->dev; priv->input_demo->evbit[0] = BIT_MASK(EV_KEY); //事件類型注冊為EV_KEY priv->input_demo->keybit[BIT_WORD(KEY_VOLUMEDOWN)] = BIT_MASK(KEY_VOLUMEDOWN); //按鍵值
1
2
3
4
5
name:當前初始化的input device的名字;
evbit[0]:配置為BIT_MASK(EV_KEY),將事件類型注冊為EV_KEY類型,具體有哪些類型如下所示;
所有定義都在kernel/include/uapi/linux/input-event-codes.h文件中。
除了EV_KEY之外,還有兩種基本事件類型:EV_REL和EV_ABS。它們用于設備提供的相對值和絕對值。相對值可以是例如X軸上的鼠標移動。鼠標將其報告為與上一個位置的相對差異,因為它沒有任何絕對坐標系可供使用。絕對事件即操縱桿和數字化儀 - 在絕對坐標系中工作的設備。
/*
Event types
*/
#define EV_SYN 0x00
#define EV_KEY 0x01
#define EV_REL 0x02
#define EV_ABS 0x03
#define EV_MSC 0x04
#define EV_SW 0x05
#define EV_LED 0x11
#define EV_SND 0x12
#define EV_REP 0x14
#define EV_FF 0x15
#define EV_PWR 0x16
#define EV_FF_STATUS 0x17
#define EV_MAX 0x1f
#define EV_CNT (EV_MAX+1)
keybit[BIT_WORD(KEY_VOLUMEDOWN)]:按鍵值設置為BIT_MASK(KEY_VOLUMEDOWN),就是音量減的按鍵;
BITS_TO_LONGS(), BIT_WORD(), BIT_MASK()
These three macros from bitops.h help some bitfield computations:
BITS_TO_LONGS(x) - returns the length of a bitfield array in longs for x bits
BIT_WORD(x) - returns the index in the array in longs for bit x
BIT_MASK(x) - returns the index in a long for bit x
完成初始化之后,然后注冊input_demo;
ret = input_register_device(priv->input_demo);
1
上報按鍵值
當用戶發生了向系統輸入一定信息的操作之后,input device需要將一些列信息上傳,本文需要上傳按鍵值。在這里要可以通過中斷觸發,或者輪詢去檢測用戶的動作,前面已經有提及,這里不再贅述,
本文代碼已經在附錄中,已經涵蓋這兩種方式。
關于上報數據可以通過input_report_key和input_sync去完成。
dev->open()和dev->close()
如果驅動程序必須重復輪詢設備,因為它沒有來自它的中斷,并且輪詢太昂貴而無法一直進行,或者如果設備中斷資源比較稀缺,可以使用open和close回調來通知設備何時可以停止輪詢或釋放中斷以及何時必須重新開始輪詢或再次獲取中斷。為此,我們將以下代碼添加到示例驅動程序:
static int button_open(struct input_dev *dev) { if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) { printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_irq); return -EBUSY; } return 0; } static void button_close(struct input_dev *dev) { free_irq(IRQ_AMIGA_VERTB, button_interrupt); } static int gpio_demo_probe(struct platform_device *pdev)t(void) { ... button_dev->open = button_open; button_dev->close = button_close; ... }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
請注意,輸入內核會跟蹤設備的用戶數,并確保僅在第一個用戶連接到設備時調用dev->open(),并在最后一個用戶斷開連接時調用dev-> close() 。對兩個回調的調用都是序列化的。
open()回調應該在成功的情況下返回0,或者在失敗的情況下返回任何非零值。close()回調(無效)必須始終成功。
其他事件類型,處理輸出事件
到目前為止的其他事件類型是:
EV_LED - 用于鍵盤LED。
EV_SND - 用于鍵盤蜂鳴聲。
它們與按鍵事件非常相似,但它們朝另一個方向 - 從系統到輸入設備驅動程序。如果您的輸入設備驅動程序可以處理這些事件,則必須在evbit中設置相應的位, 以及回調例程:
button_dev->event = button_event; int button_event(struct input_dev * dev,unsigned int type, unsigned int code,int value){ if(type == EV_SND && code == SND_BELL){ outb(value,BUTTON_BELL); return 0; } return -1; }
1
2
3
4
5
6
7
8
9
10
這個回調例程可以從一個中斷或一個BH調用(雖然這不是一個規則),因此不能休眠,并且不能花太長時間才能完成。
查看input device信息
系統啟動之后,可以通過cat /proc/bus/input/devices來查看當前系統中已經注冊的input device信息,這里可以看到已經注冊成功的input-demo
附錄
#include
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
Linux
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。