一 编写Linux驱动程序
1.建立Linux驱动骨架
Linux内核在使用驱动时需要装载与卸载驱动
装载驱动:建立设备文件、分配内存地址空间等;module_init 函数处理驱动初始化
卸载驱动:删除设备文件、释放内存地址空间等;module_exit函数处理退出
包含这两个函数的两个宏的C程序文件也可看做是Linux驱动的骨架
2.注册和注销设备文件
任何一个Linux驱动都需要有一个设备文件,否则应用程序将无法与驱动程序交互。
建立设备文件:在第一步编写的处理Linux初始化工作的函数中完成。misc_register函数
删除设备文件:在第一步编写的处理Linux退出工作的函数中完成。misc_deregister函数
3.指定与驱动相关的信息
驱动程序是自描述的,驱动程序的作者姓名、使用的开源协议、别名、驱动描述等信息。这些信息都需要在驱动源代码中指定。
MODULE_AUTHOR、MODULE_LICENSE、MODULE_ALLS、MODULE_DESCRIPION等宏可以指定与驱动相关的信息
4.指定回调函数
一个驱动程序并不一定要指定所有的回调函数,回调函数会通过相关机制进行注册
5.编写业务逻辑
具体的业务逻辑与驱动的功能有关,业务逻辑可能由多个函数、多个文件甚至是多个Linux驱动模块组成
6.编写Makefile文件
Linux内核源代码的编译规则是通过Makefile文件定义的。因此编写一个新的Linux驱动程序必须有一个Makefile文件
7.编译Linux驱动程序
可以直接编译进内核,也可以作为模块单独编译
8.安装和卸载Linux驱动
若将Linux驱动编译进内核,只要Linux使用该内核,驱动程序就会自动转载
若Linux驱动程序以模块单独存在,需使用insmod或modprode命令装载Linux驱动模块,rmmod命令卸载Linux驱动模块
二 第一个Linux驱动,以word_count为例
(一)基础编写代码
#mkdir -p /root/drivers/ch06/word_count 建立目录存放Linux驱动程序
#cd /root/drivers/ch06/word_count
#echo '' > word_count.c 建立驱动源代码文件
#echo 'obj-m := word_count.o' > Makefile 编写一个Makefile文件 make命令会吧Linux驱动源代码目录中的word_count.c或 word_count.s文件编译成word_count.o文件
obj-m 表示将Linux驱动作为模块(.ko文件)编译,word_count.o会被连接进word_count.ko文件,然后使用insmod或modprode命令装载word_count.ko
obj-y 表示将Linux驱动编译进Linux内核,word_count.o会被连接进built-in.o 文件,最终会被连接进内核
Linux系统将内存分为了用户空间和内核空间,两者空间的程序不能直接访问,printk函数运行在内核空间,printf函数运行在用户空间,因此属于内核程序的Linux驱动是不能直接访问printf函数的。
#make -C /usr/src/linux-headers-3.0.0-15-generic M=/root/driver/ch06/word_count 编译Linux驱动源代码
# insmod word_count.ko 装载驱动
# lsmod | grep word_count 查看word_count是否安装成功
# rmmod word_count 卸载Linux驱动
#dmesg | grep word_count | tail -n 2 查看有Linux驱动输出的日志信息
(二)加入有关指定的信息的代码
模块作者:MODULE_AUTHOR("lining");
模块描述:MODULE_DESCRPTION("statistics of word count .");
模块别名:MODULE_ALIAS("word count module.");
开源协议: MODULE_LICENSE("GPL");
#define DEVICE_NAME "wordcount" //定义设备文件名
//描述与设备文件触发的事件对应的回调函数指针
//owner:设备事件回调幻术应用于哪些驱动模块,THIS_MODULE表示应用于当前驱动模块
static struct file_operations dev_fops={.owner = THIS_MODULE};
//描述设备文件的信息
//minor:次设备号 MISC_DYNAMIC_MINOR,:动态生成次设备号 name : 设备文件名称
//fops : file_operations 结构体变量指针
static struct miscdevice misc={.minor = MISC_DYNAMIC_MINOR, .name=DEVICE_NAME,.fops = &dev_fops};
//初始化Linux驱动
static int word_count_init(void)
{ int ret;
ret = misc_register(&misc);
printk("word_count_init_success\n");
return ret;
}
// 卸载Linux驱动
static void word_count_exit(void)
{ misc_deregister(&misc);
printk("word_inti_exit_success\n");
}
由于内核空间的程序不能直接访问用户空间中的数据,因此,需要在word_count_read(从设备文件读数据)和word_count_write(向设备文件写数据) 函数中分别使用copy_to_user和copy_from_user函数将数据从内核空间复制到用户空间或从用户空间复制到内核空间
(三)装载与卸载驱动
检查word_count驱动工作是否完全正常
#dmesg | tail -n 1
#modinfo word_count.ko
检测Linux驱动模块的依赖关系
#depmod /root/drivers/ch06/word_count/word_count.ko
调用命令装载Linux驱动
#modprode word_count
注:insmod 和 modprode 命令都是加载驱动,后者可以检查驱动模块的依赖性
三 多种方法测试Linux驱动
(一)使用Ubuntu Linux 测试Linux驱动
需要编写专门用于测试的程序,例如test_word_count.c
#gcc test_word_count.c -o test_word_count
#test_word_count
#test_word_count "I love you."
输出结果: String:I love you.
word byte display:0,0,0,3
word count:3
(二)在android模拟器上通过原生C程序测试Linux驱动
#cd ~/kernel/goldfish
#make menuconfig
按照如图所示进行设置,之后重新编译Linux内核,成功编译内核后,android模拟器可以使用性生成的zImage内核文件动态装载Linux驱动模块。
两个条件满足可以直接运行普通的Linux程序:android模拟器、开发板或手机需要有root权限;可执行文件需要使用交叉编译器来编译test_word_count.c文件,建立Android.mk设置编译参数,并使用make命令进行编译
#mm 编译.c文件,在相应目录下
#adb push ./emulator/test_word_count /data/local 上传到Android模拟器
执行下面命令测试驱动
#chmod 777 /data/local/test_word_count 设置可执行权限
#/data/local/test_word_count
#/data/local/test_word_count 'a bb ccc ddd eee'
输出结果:5,表示测试成功
(三)使用Android NDK测试
(四)使用开发板测试
(五)将驱动编译进Linux内核进行测试
四 在eclipse中开发Linux驱动程序
第一步:建立C工程
第二步:建立C源代码文件链接 New>Source Folder Folder name 输入 src,导入word_count.c文件
第三步:设置include路径 单击菜单项properties,C/C++General > Paths and Symbols ,选中Include的GNU C项,Add添加两个路径:/root/kernel/goldfish/include和/root/kernel/goldfish/arch/arm/include
第四步:编译Linux驱动
测试Linux驱动
第一步:导入test_word_count.c文件
第二步:设置include路径
第三步:建立Target Make Target > Create ,在Target name 文本框中输入word_count_eclipse_test,点击OK
第四步:Build工程 Make Target > Build,选中第三步中建立的文件,然后点击Build
第五步:运行测试程序 Run As > Local C/C++ Application