文章目錄
-
- 1 編寫驅動程式
- 2 編寫應用測試程式
- 3 運作測試
1 編寫驅動程式
本實驗例程路徑:i.MX6UL終結者CD光牒資料/06_Linux驅動例程/12_key_poll
建立key_poll.c檔案,具體内容如下(在阻塞實驗的驅動檔案上修改,内容有省略):
1 #include <linux/types.h>
2 #include <linux/kernel.h>
3 #include <linux/delay.h>
......
21 #include <asm/uaccess.h>
22 #include <asm/io.h>
23
24 #define IMX6UIRQ_CNT 1 /* 裝置号個數 */
25 #define IMX6UIRQ_NAME "noblockio" /* 名字 */
26 #define KEY0VALUE 0X01 /* KEY0按鍵值 */
27 #define INVAKEY 0XFF /* 無效的按鍵值 */
28 #define KEY_NUM 1 /* 按鍵數量 */
......
178 /*
179 * @description : 從裝置讀取資料
180 * @param - filp : 要打開的裝置檔案(檔案描述符)
181 * @param - buf : 傳回給使用者空間的資料緩沖區
182 * @param - cnt : 要讀取的資料長度
183 * @param - offt : 相對于檔案首位址的偏移
184 * @return : 讀取的位元組數,如果為負值,表示讀取失敗
185 */
186 static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
187 {
188 int ret = 0;
189 unsigned char keyvalue = 0;
190 unsigned char releasekey = 0;
191 struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;
192
193 if (filp->f_flags & O_NONBLOCK) { /* 非阻塞通路 */
194 if(atomic_read(&dev->releasekey) == 0) /* 沒有按鍵按下,傳回-EAGAIN */
195 return -EAGAIN;
196 } else { /* 阻塞通路 */
197 /* 加入等待隊列,等待被喚醒,也就是有按鍵按下 */
198 ret = wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey));
199 if (ret) {
200 goto wait_error;
201 }
202 }
......
220 wait_error:
221 return ret;
222 data_error:
223 return -EINVAL;
224 }
225
226 /*
227 * @description : poll函數,用于處理非阻塞通路
228 * @param - filp : 要打開的裝置檔案(檔案描述符)
229 * @param - wait : 等待清單(poll_table)
230 * @return : 裝置或者資源狀态,
231 */
232 unsigned int imx6uirq_poll(struct file *filp, struct poll_table_struct *wait)
233 {
234 unsigned int mask = 0;
235 struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;
236
237 poll_wait(filp, &dev->r_wait, wait); /* 将等待隊列頭添加到poll_table中 */
238
239 if(atomic_read(&dev->releasekey)) { /* 按鍵按下 */
240 mask = POLLIN | POLLRDNORM; /* 傳回PLLIN */
241 }
242 return mask;
243 }
244
245 /* 裝置操作函數 */
246 static struct file_operations imx6uirq_fops = {
247 .owner = THIS_MODULE,
248 .open = imx6uirq_open,
249 .read = imx6uirq_read,
250 .poll = imx6uirq_poll,
251 };
252
253 /*
254 * @description : 驅動入口函數
255 * @param : 無
256 * @return : 無
257 */
258 static int __init imx6uirq_init(void)
259 {
......
286 /* 5、始化按鍵 */
287 atomic_set(&imx6uirq.keyvalue, INVAKEY);
288 atomic_set(&imx6uirq.releasekey, 0);
289 keyio_init();
290 return 0;
291 }
292
293 /*
294 * @description : 驅動出口函數
295 * @param : 無
296 * @return : 無
297 */
298 static void __exit imx6uirq_exit(void)
299 {
300 unsigned i = 0;
301 /* 删除定時器 */
302 del_timer_sync(&imx6uirq.timer); /* 删除定時器 */
303
304 /* 釋放中斷 */
305 for (i = 0; i < KEY_NUM; i++) {
306 free_irq(imx6uirq.irqkeydesc[i].irqnum, &imx6uirq);
307 }
308 cdev_del(&imx6uirq.cdev);
309 unregister_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT);
310 device_destroy(imx6uirq.class, imx6uirq.devid);
311 class_destroy(imx6uirq.class);
312 }
313
314 module_init(imx6uirq_init);
315 module_exit(imx6uirq_exit);
316 MODULE_LICENSE("GPL");
第 25 行,修改裝置檔案名字為“noblockio”,當驅動程式加載成功以後就會在根檔案系統中出現一個名為“/dev/noblockio”的檔案。
第 193~195 行,判斷是否為非阻塞式讀取通路,如果是的話就判斷按鍵是否有效,也就是判斷一下有沒有按鍵按下,如果沒有的話就傳回-EAGAIN。
第 232~243 行,imx6uirq_poll 函數就是 file_operations 驅動操作集中的 poll 函數,當應用程式調用 select 或者 poll 函數的時候 imx6uirq_poll 函數就會執行。第 237 行調用 poll_wait 函數将等待隊列頭添加到 poll_table 中,第 239~241 行判斷按鍵是否有效,如果按鍵有效的話就向應用程式傳回 POLLIN 這個事件,表示有資料可以讀取。
第 250 行,設定 file_operations 的 poll 成員變量為 imx6uirq_poll。
2 編寫應用測試程式
建立key_poll_test.c檔案,具體内容如下:
1 #include "stdio.h"
2 #include "unistd.h"
3 #include "sys/types.h"
4 #include "sys/stat.h"
5 #include "fcntl.h"
6 #include "stdlib.h"
7 #include "string.h"
8 #include "poll.h"
9 #include "sys/select.h"
10 #include "sys/time.h"
11 #include "linux/ioctl.h"
12
13 /*
14 * @description : main主程式
15 * @param - argc : argv數組元素個數
16 * @param - argv : 具體參數
17 * @return : 0 成功;其他 失敗
18 */
19 int main(int argc, char *argv[])
20 {
21 int fd;
22 int ret = 0;
23 char *filename;
24 struct pollfd fds;
25 fd_set readfds;
26 struct timeval timeout;
27 unsigned char data;
28
29 if (argc != 2) {
30 printf("Error Usage!\r\n");
31 return -1;
32 }
33
34 filename = argv[1];
35 fd = open(filename, O_RDWR | O_NONBLOCK); /* 非阻塞通路 */
36 if (fd < 0) {
37 printf("Can't open file %s\r\n", filename);
38 return -1;
39 }
40
41 #if 0
42 /* 構造結構體 */
43 fds.fd = fd;
44 fds.events = POLLIN;
45
46 while (1) {
47 ret = poll(&fds, 1, 500);
48 if (ret) { /* 資料有效 */
49 ret = read(fd, &data, sizeof(data));
50 if(ret < 0) {
51 /* 讀取錯誤 */
52 } else {
53 if(data)
54 printf("key value = %d \r\n", data);
55 }
56 } else if (ret == 0) { /* 逾時 */
57 /* 使用者自定義逾時處理 */
58 } else if (ret < 0) { /* 錯誤 */
59 /* 使用者自定義錯誤處理 */
60 }
61 }
62 #endif
63
64 while (1) {
65 FD_ZERO(&readfds);
66 FD_SET(fd, &readfds);
67 /* 構造逾時時間 */
68 timeout.tv_sec = 0;
69 timeout.tv_usec = 500000; /* 500ms */
70 ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
71 switch (ret) {
72 case 0: /* 逾時 */
73 /* 使用者自定義逾時處理 */
74 break;
75 case -1: /* 錯誤 */
76 /* 使用者自定義錯誤處理 */
77 break;
78 default: /* 可以讀取資料 */
79 if(FD_ISSET(fd, &readfds)) {
80 ret = read(fd, &data, sizeof(data));
81 if (ret < 0) {
82 /* 讀取錯誤 */
83 } else {
84 if (data)
85 printf("key value=%d\r\n", data);
86 }
87 }
88 break;
89 }
90 }
91
92 close(fd);
93 return ret;
94 }
第 41~62行,這段代碼使用 poll 函數來實作非阻塞通路,在 while 循環中使用 poll 函數不斷的輪詢,檢查驅動程式是否有資料可以讀取,如果可以讀取的話就調用 read 函數讀取按鍵資料。
第 64~90行,這段代碼使用 select 函數來實作非阻塞通路。
3 運作測試
1、編譯驅動程式
和前面章節中驅動測試程式一樣需要一個Makefile檔案,隻是将obj-m的值改為key_poll.o,Makefile檔案内容如下:
KERNELDIR := /home/topeet/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga
CURRENT_PATH := $(shell pwd)
obj-m := key_poll.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
首先我們在終端輸入兩個指令(設定兩個環境變量):
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
然後執行“make”指令編譯子產品,編譯完成生成key_poll.ko子產品檔案。
2、 編譯應用測試程式
輸入如下指令編譯應用測試程式:
arm-linux-gnueabihf-gcc -o key_poll_test key_poll_test.c
編譯完成後,會生成key_poll_test可執行檔案。
3、運作測試
啟動開發闆,将編譯好的key_poll.ko驅動子產品檔案和key_poll_test應用測試檔案拷貝到/lib/modules/4.1.15目錄下(檢查開發闆根檔案系統中有沒有“/lib/modules/4.1.15”這個目錄,如果沒有的話需要自行建立一下。開發闆中使用的是CD光牒資料裡面提供的busybox檔案系統,CD光牒資料的“i.MX6UL終結者CD光牒資料\08_開發闆系統鏡像\03_檔案系統鏡像\01_Busybox檔案系統”目錄下)。輸入下面指令加載子產品:
depmod
modprobe key_poll
驅動加載成功後,然後使用key_poll_test應用測試檔案來測試驅動檔案,使用下面的指令:
./key_poll_test /dev/noblockio &
按下開發闆上的KEY0按鍵,結果如圖 3.1所示:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL2kDO0QDOzcTMyIjMxAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
圖 3.1
按下按鍵KEY0後,有資訊輸出說明驅動正常,然後使用“top”指令檢視key_poll_test應用程式的CPU使用率,如圖 3.2所示:
圖 3.2
可以看出采用非阻塞IO的情況和阻塞IO基本一緻,CPU使用率都非常低。使用“q”或者“ctrl + c”退出top指令。
在解除安裝子產品驅動之前先使用“ps”加“kill”指令關閉key_poll_test應用程序,然後解除安裝子產品:
rmmod key_poll