前段时间编译了android源码,内核源码以及第一个android程序到system.img镜像,接下来想自己写一个设备驱动模块添加到android源码内核中,但是这方面一点都不了解,于是乎,打算先看看ldd3(linux devices driver 3rd)了解一下基本知识,然后再去给android内核写设备驱动模块,看了之后,打算根据书上面说的例子来写一个helloworld的驱动程序,过程中还真是遇到了不少的问题,这个主要是因为环境的不同,毕竟现在这个时候,linux kernel还是已经发布了很多版本了,于是,打算参考书本上面的理论知识,在我现有的环境上来实现,中间有区别是肯定的,下面就记录一下我整个的实现过程。
操作的过程中,应该保证在root权限下!
---------------------------------------------------------------------------------
LDD3环境
kernel:2.6
我的环境:
Ubuntu 12.04
kernel:3.2.0-23-generic
查看ubuntu内核版本的命令:cat /proc/version,内核一般在/lib/modules/文件夹下面
-------------------------------------------------------------------------------
我再桌面上面建立我文件夹,路径为:/home/gavin-guo/Desktop/workdir/driver/hellodriver
这个里面有两个文件:hellodriver.c和Makefile
他们的内容为:
hellodriver.c:
#include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("Dual BSD/GPL"); static int hellodriver_init(void) { printk(KERN_ALERT “hellodriver.driver's world”); return 0; } static void hellodriver_exit(void) { printk(KERN_ALERT “Goodbye,hellodriver”); } module_init(hellodriver_init); module_exit(hellodriver_exit);
1.两个include是头文件。
2.MODULE_LICENSE是一个内核的宏,用于表明该模块带有自由许可证。
3.printk在内核中定义并对模块可用,它与标准c库函数printf类似,但是一般在ubuntu的terminal页面是看不到这个输出的,至于怎么样才能看到,我们后面再说。
4.KERN_ALERT是表示输出信息的级别,值为“<1>”,这个具体大家可以百度。
5.module_init和module_exit两个函数使用了两个特别的内核宏来告诉内核它们的角色,init在模块加载到内核中被调用,exit在卸载的时候被调用。
MakeFile:obj-m := hellodriver.o KERNELDIR := $(shell uname -r) PWD := $(shell pwd) modules: 这个是一个tab符号$(MAKE) -C /lib/modules/$(KERNELDIR)/build M=$(PWD) modules modules_install: 这个是一个tab符号$(MAKE) -C /lib/modules/$(KERNELDIR)/build M=$(PWD) modules_install1.obj-m应该是指生成的目标文件吧(不太懂)
2.KERNELDIR其实,这个卸载这里已经不准确了,它仅仅是代表了内核的版本,而没有明确指出来内核的路径,这个在后面能看出来的。
3.PWD是Makefile所在的整个路径。
---------------------------------------------------------------------------
文件编译好了之后,我们在项目的根目录进行编译:make
中间出了一些小问题,比如路径没有配置对,好着语法错误之类的,这些都是常见问题,大家根据提示信息修改是没有问题的,这里就不列举出来了。
最后会在目录下面生成几个文件,生成之后的目录清单为:
hellodriver.c hellodriver.ko hellodriver.mod.c hellodriver.mod.o helloriver.o Makefile module.symvers modules.order
这样,我们就生成成功了。
--------------------------------------------------------------------------
对于动态加载模块和卸载,我们使用insmod和rmmod工具来进行测试。
先加载:insmod hellodriver.ko
这个时候,并没有在terminal中出现我们想要的log数据。这个原因和解释嘛有很多,我现在也没太明白,这个大家自己研究吧,我就不误人子弟了。这里我只说,我们可以怎么看到它们和判断是否模块已经被加载了。
看到log:
1.有人说,生成的log在/var/log/syslog文件里面,这个大家可以去找找,反正我的是没有。
2.有人说,在/var/log/messages文件里面,可是我的文件系统里面压根就找不到这个文件,我们可以通过下面的操作来进行找回:
我么可以通过修改 /etc/rsyslog.d/50-default.conf 配置文件实现
去掉注释或添加:
*.=info;*.=notice;*.=warn;\
auth,authpriv.none;\
cron,daemon.none;\
mail,news.none -/var/log/message
然后重启rsyslog服务
sudo service rsyslog restart
这样,我们就可以具有messages记录日志的正常功能。
但是,messages还是看不到,输出的日志,具体为什么吗?我还不知道,毕竟我对linux系统也就知道那么一点点。
3.使用dmesg -c命令来查看,这个是没有问题的,是可查看到日志的。
查看模块是否加载:
cat /proc/modules可以看到当前内核中加载到的所有的模块。
------------------------------------------------------------------------------------
加载了之后,就是卸载了:rmmod hellodriver
然后,悲剧的事情就来了,尽然没有办法卸载:ERROR: Removing ‘hello‘: Device or resource busy
这么一个错误,问题也可能有很多,下面说一些解决办法,一般的就不说了,搜得到,就说一下不常见的:
1.gcc版本和内核的gcc版本不一直。
于是看了一下:gcc -v和cat /proc/version
发现gcc的版本是4.4.7的,而内核里面使用的gcc是4.6的,这才想起来,我再编译android源码的时候,因为一个问题,把4.6的gcc降低为4.4的了,具体这个版本怎么修改,大家参考一下这个博客吧:http://blog.csdn.net/vrix/article/details/8330135
2.现在的内核模块在插入卸载时都会要转到b/modules/内核版本号/这个目录里。所以只要建立这个目录并且把要使用的模块.ko文件复制到这个目录就行了。 于是,我再我的/lib/modules/3.2.0-23-generic/文件夹下面建立了hellodriver文件夹,然后把原来项目里面生成的hellodriver.ko文件mv到这个里面来,就可以卸载了。
3,当然了,log还是看不到的,依旧使用dmesg -c命令和cat /proc/modules来查看log输出以及模块是否被删除。
-----------------------------------------------------------------------------------
ok,经过上面的折腾,总算是勉强成功了,虽然过程很打击人(一个最简单的hellowrold,不是说helloworld都是用来找自信的吗?~!~),但是完成了之后,对于linux系统以及内核编写的基本认识总算是加深了一些,对于一些奇怪的问题,就不去深究了,一方面本身在linux方面就没有什么知识储备,另一方面因为版本的更迭,中间产生一些变化是很正常的,当我们深入了解了之后,在回头看这些问题都可以迎刃而解,所以现在不用那么纠结。
ubuntu12.04下实现第一个设备驱动模块HelloWorld
原文地址:http://blog.csdn.net/jiguangcanhen/article/details/42145753