我们的驱动模块已经可以被自动分配主设备号了,可以说到目前为止,一个驱动模块所具备的一些“基础设施”它都具备了,就像是养兵前日,用兵一时,已经可以让first_drv这个驱动模块出去打仗了。
那么说是出去打仗,总得有个发号施令的首长吧,在linux系统中,这位调兵遣将的首长就是应用程序,应用程序才是大Boss,我们“一直精雕细琢的驱动模块”说白了只是个跑腿的。
这就是为什么我们的专题是第1个linux驱动却要讲应用程序的原因,因为应用程序是让驱动程序能够被应用的,而驱动程序是让应用被驱动的,两者是一种主仆关系、合作关系,在linux系统中缺一不可。
其实说到应用程序,大家应该还会有一种亲和感,它也是C语言写的,不过在写应用程序之前,你最好对C语言的文件IO操作有所了解。
应用程序的名字就定为app.c吧,首先得有一个main函数:
int main(void)
{
return 0;
}
框架已经完成,那我们要在app.c里写什么才能实现对first_drv这个驱动的调用呢。
我们说过是通过register_chrdev这个函数向内核中注册驱动后,系统中就能够访问并使用这个驱动了,而向内核注册驱动指的是将驱动的信息存放在一个数组中,而存放在这个数组中的第几个元素是取决于驱动程序的主设备号的,比如主设备号是100,就那就是存放在数组的第100个格子中。
其实应用程序是通过设备文件来找到驱动的,那么最好的方法就是通过打开这个设备文件,得知驱动的主设备号是多少,再通过这个主设备号来找到驱动。
我们通过执行:"mknod /dev/test 主设备号 次设备号"这个命令来建立一个设备文件。
如执行:mknod /dev/test 100 0 就建立了一个主设备号为100、次设备号为0的设备文件,这个次设备号是什么我们暂时不去关心,我们只要知道主设备号100对应了唯一的一个驱动模块。
有了100这个主设备号,就能找到确定的驱动模块。
我们稍后再建立first_drv的设备文件/dev/test,先来在app.c中把打开设备文件/dev/test的代码写好:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
int fd = -1;
fd = open("/dev/test", O_RDWR);
if(fd < 0)
{
printf("Open /dev/test failure!\n");
return -1;
}
printf("Open /dev/test success...\n");
close(fd);
return 0;
}
open是linux内核为我们提供的访问系统的API接口,像这样的系统调用还有read、write、close等。
由于添加了app.c,在编译之前,我们得修改Makefile的all、cp和clean命令如下:
#KERN_VER = $(shell uname -r) #KERN_DIR = /lib/modules/$(KERN_VER)/build KERN_DIR = /work/kernel/linux-2.6.22.6 all: make -C $(KERN_DIR) M=`pwd` modules arm-linux-gcc -o app app.c cp: cp *.ko app /work/my_drivers/ko_file clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf Module.symvers app obj-m += zero_drv.o
修改好Makefile后,来执行make:
可以看到生成了app这个可执行文件,make cp将它和first_drv.ko拷贝到最小根文件系统的/mnt目录下。
在/mnt目录下执行:./app(执行前要确保已经安装驱动模块first_drv)
打印:Open /dev/test failure!
打开设备文件/dev/test失败,这是我们app.c中自己写的打开设备文件失败时的打印信息。
为什么打开失败,因为我们还没有创建这个设备文件,
我们先来cat /proc/devices查看一下当前first_drv驱动模块的主设备号是多少,我这里是252,你们去查看自己的主设备号是多少。次设备号暂定0好了。
我这边执行:mknod /dev/test c 252 0
没有异常,创建成功。
然后执行./app
显示:
It is in first_drv_open.
Open /dev/test success...It is in first_drv_release.
(此处是空行)
(光标在这行)
其中“It is in first_drv_open.”是我们驱动程序中first_drv_open函数的打印信息,而“Open /dev/test success...”是应用程序中的打印信息,我们看到紧接着没有回车,就打印了“It is in first_drv_release.”,这是我们驱动程序中first_drv_release函数的打印信息。
奇怪的是我们明明加了/n,为什么没有回车呢,这是因为驱动程序的优先级比应用程序要高,当应用程序正在打印信息时,如果驱动程序中也要执行打印信息,那么就会优先让驱动程序来打印信息,当驱动程序打印完成后,再让应用程序来打印信息,这就是为什么第3句话直接接在了第2句话的后面,而第3句话后又输出了第2句话最后的回车。
这一点不影响我们的功能测试,对于这个现象我们心知肚明就好。
总而言之,我们通过运行应用程序,让驱动得以工作了。
我们来对应用程序app.c进行一点小改进:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE "/dev/test"
int main(void)
{
int fd = -1;
fd = open(FILE, O_RDWR);
if(fd < 0)
{
printf("Open %s failure!\n", FILE);
return -1;
}
printf("Open %s success...\n", FILE);
close(fd);
return 0;
}
无非就是添加了一个宏定义,这样便于修改设备文件名。
再测试一次,同样可以让驱动程序工作。
但我们目前的调用仅仅只是让驱动程序打印个信息,下一篇博文我们来继续完善驱动程序,让驱动程序在被应用程序调用的时候,能够做更多的事情。