3、Makefile的编写
前面讲完了最简单的驱动模块的代码结构,这里继续讲解Makefile文件的编写。
代码语言:javascript复制KERNEL_DIR=/home/jianfei/workdir/linux_driver/ebf-buster-linux/build_image/build
ARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH CROSS_COMPILE
obj-m := led_driver.o
app_obj=led
all:
$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules
arm-linux-gnueabihf-gcc led.c -o $(app_obj)
.PHONE:clean copy
clean:
$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean
sudo rm /home/jianfei/workdir/*.ko
sudo rm /home/jianfei/workdir/$(app_obj)
copy:
sudo cp *.ko $(app_obj) /home/jianfei/workdir
(1)KERNEL_DIR,变量的值就是我们用来编译这个模块的内核源码树的目录
(2)obj-m := led_driver.o,这一行就表示我们要将led_driver.c文件编译成一个模块
(3)make clean ,用来清除编译痕迹
总结:模块的makefile非常简单,本身并不能完成模块的编译,而是通过make -C进入到内核源码树下借用内核源码的体系来完成模块的编译链接的。这个Makefile本身是非常模式化的。
要注意第一个路径必须是自己编译的内核源码树,另外要注意编译工具链的选择,如果是在主机Ubuntu中编译,要使用交叉编译工具链。
编译好了之后就会生成一个.ko的驱动模块文件,我们就可以在linux系统中去安装这个模块,这在上一节已经提到过。
4、验证驱动程序工作是否正常
安装好这个模块之后,如何验证这个驱动模块能否正常工作呢?
在之前的驱动代码中,我们的代码逻辑就是当写入“on”的时候,点亮led灯,当写入“off”的时候,就关闭led灯。那么,到底是向哪里写入?这里涉及到设备文件的创建。之前说过,每个设备都可以抽象成一个设备文件,那么首先要创建这个设备文件,然后将这个设备文件和设备绑定起来,绑定的方法就是依据设备的设备号。我们可以使用mknod命令来创建设备文件。
代码语言:javascript复制mknod /dev/xxx c 主设备号 次设备号
设备文件的关键信息是:设备号 = 主设备号 次设备号,使用ls -l去查看设备文件,就可以得到这个设备文件对应的主次设备号。
在这个试验中,要验证驱动程序是否正常,应该向设备文件中写入值,方法有两种,一种是直接在终端里面通过echo指令,这样比较简单,还有一种方法就是编写一个应用程序,这种方法在实际中比较常用。
第一种方法很简单,就是
代码语言:javascript复制sudo sh -c "echo on >/dev/led c 244 0"
on是要写入的内容,/dev/led是设备文件,c代表字符设备,后面两个是主设备号和次设备号,这个是通过前面的mknod指定的。
这里主要讲一下应用程序的编写。其实也很容易,就是利用了之前的系统编程的知识,无非就是open、write这些。
代码语言:javascript复制#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define filename "/dev/led"
char rbuf[10]={0};
int main(void)
{
int fd;
int ret;
fd=open(filename,O_RDWR);
if(fd<0)
{
perror("open");
return -1;
}
while (1)
{
printf("please input on or off to control the led,and q for exitn");
memset(rbuf,0,sizeof(rbuf));
scanf("%s",rbuf);
if( (!strcmp(rbuf,"on")) || (!strcmp(rbuf,"off")) )
{
write(fd,rbuf,strlen(rbuf));
}
else if(!strcmp(rbuf,"q"))
{
return 0;
}
else
{
printf("error! please input again");
}
}
close(fd);
return 0;
}
我们把编译好的程序放到开发板上执行,就可以在命令行里控制led灯的亮灭了。
当然,如果再要完整一点,应该还要包括类似GUI的设计,因为用户不可能去操作命令行。不过这里暂时不涉及。
好了,以上就是字符设备驱动开发的大致流程,后面将会逐步完善代码,包括建立一些框架什么的。总的来说,驱动部分应该只实现最基本的驱动硬件功能,而应用层部分就要完成用户的各种需求。