文章目錄
-
- 1 編寫驅動程式
- 2 編寫應用測試程式
- 3 運作測試
信号量可以導緻休眠,是以信号量保護的臨界區沒有運作時間限制,可以在驅動的 open 函數申請信号量,然後在release 函數中釋放信号量。但是信号量不能用在中斷中,本節實驗我們不會在中斷中使用信号量。
1 編寫驅動程式
本實驗例程路徑:i.MX6UL終結者CD光牒資料/06_Linux驅動例程/06_gpioled_semaphore
建立gpioled_semaphore.c檔案,因為和前面的實驗驅動檔案部分是相同不用修改的,下面是部分内容:
1 #include <linux/types.h>
2 #include <linux/kernel.h>
......
17 #include <linux/semaphore.h>
18
19 #define GPIOLED_CNT 1 /* 裝置号個數 */
20 #define GPIOLED_NAME "gpioled" /* 名字 */
21 #define LEDOFF 0 /* 關燈 */
22 #define LEDON 1 /* 開燈 */
23
24 /* gpioled 裝置結構體 */
25 struct gpioled_dev{
26 dev_t devid; /* 裝置号 */
27 struct cdev cdev; /* cdev */
28 struct class *class; /* 類 */
29 struct device *device; /* 裝置 */
30 int major; /* 主裝置号 */
31 int minor; /* 次裝置号 */
32 struct device_node *nd; /* 裝置節點 */
33 int led_gpio; /* led 所使用的 GPIO 編号 */
34 struct semaphore sem; /* 信号量 */
35 };
36
37 struct gpioled_dev gpioled; /* led 裝置 */
38
39 /*
40 * @description : 打開裝置
41 * @param – inode : 傳遞給驅動的 inode
42 * @param - filp : 裝置檔案,file 結構體有個叫做 private_data 的成員變量
43 * 一般在 open 的時候将 private_data 指向裝置結構體。
44 * @return : 0 成功;其他 失敗
45 */
46 static int led_open(struct inode *inode, struct file *filp)
47 {
48 filp->private_data = &gpioled; /* 設定私有資料 */
49
50 /* 擷取信号量,進入休眠狀态的程序可以被信号打斷 */
51 if (down_interruptible(&gpioled.sem)) {
52 return -ERESTARTSYS;
53 }
54 #if 0
55 down(&gpioled.sem); /* 不能被信号打斷 */
56 #endif
57
58 return 0;
59 }
......
105 /*
106 * @description : 關閉/釋放裝置
107 * @param – filp : 要關閉的裝置檔案(檔案描述符)
108 * @return : 0 成功;其他 失敗
109 */
110 static int led_release(struct inode *inode, struct file *filp)
111 {
112 struct gpioled_dev *dev = filp->private_data;
113
114 up(&dev->sem); /* 釋放信号量,信号量值加 1 */
115
116 return 0;
117 }
118
119 /* 裝置操作函數 */
120 static struct file_operations gpioled_fops = {
121 .owner = THIS_MODULE,
122 .open = led_open,
123 .read = led_read,
124 .write = led_write,
125 .release = led_release,
126 };
127
128 /*
129 * @description : 驅動入口函數
130 * @param : 無
131 * @return : 無
132 */
133 static int __init led_init(void)
134 {
135 int ret = 0;
136
137 /* 初始化信号量 */
138 sema_init(&gpioled.sem, 1);
......
198 return 0;
199 }
200 /*
201 * @description : 驅動出口函數
202 * @param : 無
203 * @return : 無
204 */
205 static void __exit led_exit(void)
206 {
207 /* 登出字元裝置驅動 */
208 cdev_del(&gpioled.cdev);/* 删除 cdev */
209 unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
210
211 device_destroy(gpioled.class, gpioled.devid);
212 class_destroy(gpioled.class);
213 }
214
215 module_init(led_init);
216 module_exit(led_exit);
217 MODULE_LICENSE("GPL");
218 MODULE_AUTHOR("topeet");
第 17 行,要使用信号量必須添加<linux/semaphore.h>頭檔案。
第 34 行,在裝置結構體中添加一個信号量成員變量 sem。
第 46~59行,在 open函數中申請信号量,可以使用 down 函數,也可以使用 down_interruptible函數。如果信号量值大于等于 1 就表示可用,那麼應用程式就會開始使用 LED 燈。如果信号量值為 0 就表示應用程式不能使用 LED 燈,此時應用程式就會進入到休眠狀态。等到信号量值大于 1 的時候應用程式就會喚醒,申請信号量,擷取 LED 燈使用權。
第 114 行,在 release 函數中調用 up 函數釋放信号量,這樣其他因為沒有得到信号量而進入休眠狀态的應用程式就會喚醒,擷取信号量。
第 138 行,在驅動入口函數中調用 sema_init 函數初始化信号量 sem 的值為 1,相當于 sem是個二值信号量。
總結一下,當信号量 sem 為 1 的時候表示 LED 燈還沒有被使用,如果應用程式 A 要使用LED 燈,先調用 open 函數打開/dev/gpioled,這個時候會擷取信号量 sem,擷取成功以後 sem 的值減 1 變為 0。如果此時應用程式 B 也要使用 LED 燈,調用 open 函數打開/dev/gpioled 就會因為信号量無效(值為 0)而進入休眠狀态。當應用程式 A 運作完畢,調用 close 函數關閉/dev/gpioled的時候就會釋放信号量 sem,此時信号量 sem 的值就會加 1,變為 1。信号量 sem 再次有效,表示其他應用程式可以使用 LED 燈了,此時在休眠狀态的應用程式 A 就會擷取到信号量 sem,擷取成功以後就開始使用 LED 燈。
2 編寫應用測試程式
應用測試程式使用38.1.2中的應用測試程式即可。
3 運作測試
1、編譯驅動程式
添加Makefile檔案,修改obj-m的值為 gpioled_semaphore.o,内容如下:
KERNELDIR := /home/topeet/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga
CURRENT_PATH := $(shell pwd)
obj-m := gpioled_semaphore.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”指令進行編譯,生成gpioled_semaphore.ko驅動子產品檔案。
2、運作測試
将編譯好的gpioled_semaphore.ko驅動子產品檔案拷貝到/lib/modules/4.1.15目錄下(檢查開發闆根檔案系統中有沒有“/lib/modules/4.1.15”這個目錄,如果沒有的話需要自行建立一下。開發闆中使用的是CD光牒資料裡面提供的busybox檔案系統,CD光牒資料的“i.MX6UL終結者CD光牒資料\08_開發闆系統鏡像\03_檔案系統鏡像\01_Busybox檔案系統”目錄下)。輸入下面指令加載子產品:
depmod
modprobe gpioled_semaphore
驅動子產品加載成功如圖 3.1所示:
圖 3.1
驅動子產品加載成功後,使用gpioled_atomic_test應用測試程式進行測試,測試流程和原子變量測試一樣。使用下面的指令打開LED燈:
./gpioled_atomic_test /dev/gpioled 1 &
然後緊接着輸入LED燈關閉指令:
./gpioled_atomic_test /dev/gpioled 0 &
注意兩個指令都是運作在背景,第一條指令先擷取到信号量,是以可以操作 LED 燈,将LED 燈打開,并且占有 25S。第二條指令因為擷取信号量失敗而進入休眠狀态,等待第一條指令運作完畢并釋放信号量以後才擁有 LED 燈使用權,将 LED 燈關閉,運作結果如圖 39.3.3.2所示:
圖 3.2
可以看到有兩次運作結果。
解除安裝子產品指令:
rmmod gpioled_semaphore