文章目录
-
- 1 编写驱动程序
- 2 编写应用测试程序
- 3 运行测试
信号量可以导致休眠,因此信号量保护的临界区没有运行时间限制,可以在驱动的 open 函数申请信号量,然后在release 函数中释放信号量。但是信号量不能用在中断中,本节实验我们不会在中断中使用信号量。
1 编写驱动程序
本实验例程路径:i.MX6UL终结者光盘资料/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”这个目录,如果没有的话需要自行创建一下。开发板中使用的是光盘资料里面提供的busybox文件系统,光盘资料的“i.MX6UL终结者光盘资料\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