欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

5 驱动开发常见问题解决

程序员文章站 2022-06-14 17:15:31
...

5.1.内核与驱动文件的version magic匹配问题 

分类: LINUX

最近做一个底层源码版本管理的工作:本地调试OK的内核源码,驱动源码,文件系统等,作为一个工程上传至SVN服务器,以便同事们可以方便共享同步代码;

1.问题:本地编译的一整套底层代码down到设备跑都正常,但是由这套代码上传SVN服务器而后checkout出来的代码编译的文件,则出现驱动文件加载不上的情况(驱动以模块方式加载),打印如下
log: version magic '3.3.0 preempt mod_unload ARMv5 ' should be '3.3.0-svn87 preempt mod_unload ARMv5 '

2.分析:初步由打印log信息看,是由version magic不匹配造成,找到信息打印点,kernel/module.c

点击(此处)折叠或打开

  1. printk(KERN_ERR "%s: version magic '%s' should be '%s'\n",
  2.          mod->name, modmagic, vermagic);

然后

点击(此处)折叠或打开

  1. static const char vermagic[] = VERMAGIC_STRING;

可知打印语句实际由宏VERMAGIC_STRING定义,接着看include/linux/vermagic.h

点击(此处)折叠或打开

  1. #define VERMAGIC_STRING                         \
  2.     UTS_RELEASE " "                            \
  3.     MODULE_VERMAGIC_SMP MODULE_VERMAGIC_PREEMPT             \
  4.     MODULE_VERMAGIC_MODULE_UNLOAD MODULE_VERMAGIC_MODVERSIONS    \
  5.     MODULE_ARCH_VERMAGIC

打印信息的不同点“'3.3.0”和“3.3.0-svn87”是由宏UTS_RELEASE生成,那么重点关注UTS_RELEASE
由名字推测是编译内核时版本号给打上了svn相关标记,但是在内核源码中没看到任何地方定义UTS_RELEASE,再到Makefile找,搜到这句

点击(此处)折叠或打开

  1. (echo \#define UTS_RELEASE \"$(KERNELRELEASE)\";)

再搜KERNELRELEASE

点击(此处)折叠或打开

  1. # Read KERNELRELEASE from include/config/kernel.release (if it exists)
  2.  382 KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null) 
  3.  383 KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION)

可见KERNELRELEASE由include/config/kernel.release文件读取获得,但是内核源码中并无此文件(为查找差异之前将内核make distclean掉了),再重新编译

一次,果然生成了include/config/kernel.release文件,查看内容

点击(此处)折叠或打开

  1. cat include/config/kernel.release 
  2. 3.3.0-svn87

找到了加载驱动时打印log里的“3.3.0-svn87”

再回到Makefile,搜kernel.release

点击(此处)折叠或打开

  1. # Store (new) KERNELRELASE string in include/config/kernel.release 
  2.  951 include/config/kernel.release: include/config/auto.conf FORCE
  3.  952 $(Q)rm -f [email protected]
  4.  953 $(Q)echo "$(KERNELVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion $(srctree))" > [email protected]

kernel.release就是在这里生成的,前面版面号忽略,“-svn87”实际上是执行setlocalversion得到svn版本号加上去的;

查看得知setlocalversion是一个脚本文件,支持自动获取svn,git等源码管理工具的版本号;

 

3.解决:只需要使version magic一致,驱动就可以顺利加载;由于厂商提供的部分驱动文件没提供源码,并且本地内核也会不断升级(svn版本号会变),所以最简单的方法是将

附加的-svnXX去掉,这样现存的驱动和后续编译的驱动和内核就都可以保持一致了:

修改如下,

点击(此处)折叠或打开

  1. include/config/kernel.release: include/config/auto.conf FORCE

 952 $(Q)rm -f [email protected]

 953 #     $(Q)echo "$(KERNELVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion $(srctree))" > [email protected]

 954       $(Q)echo "$(KERNELVERSION)$$($(CONFIG_SHELL) $(srctree))" > [email protected]

重新编译后,驱动可以正常加载工作了。

PS:当然,version magic是一个很好的功能,如果所有驱动都有源码,对于发布产品的版本,加上svn版本号标记可以有效保证内核

5.2无法卸载驱动模块解决办法

 

驱动版本一致性。

rmmod: chdir(/lib/modules): No such file or directory 解决方法

 

必须创建/lib/modules/2.6.30.4这样一个空目录,否则不能卸载ko模块.
# rmmod nls_cp936
rmmod: chdir(/lib/modules): No such file or directory
但是这样倒是可以卸载nls_cp936,不过会一直有这样一个提示:
rmmod: module 'nls_cp936' not found

 

初步发现,原来这是编译kernel时使用make modules_install生成的一个目录,

但是经测试得知,rmmod: module 'nls_cp936' not found来自于busybox,并不是来自kernel,

 

 

1.创建/lib/modules/2.6.30空目录就.

2.使用如下源码生成rmmod命令,就可以没有任何提示的卸载ko模块了[luther.gliethttp]

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <fcntl.h>

#include <string.h>

#include <errno.h>

int main(int argc, char *argv[])

{

const char *modname = argv[1];

int ret = -1;

int maxtry = 10;

while (maxtry-- > 0) {

ret = delete_module(modname, O_NONBLOCK | O_EXCL);//系统调用sys_delete_module

if (ret < 0 && errno == EAGAIN)

usleep(500000);

else

break;

}

if (ret != 0)

printf("Unable to unload driver module \"%s\": %s\n",

modname, strerror(errno));

}

 

3.把生成的命令复制到文件系统

# arm-linux-gcc -static -o rmmod rmmod.c

# arm-linux-strip -s rmmod

# cp rmmod /nfs/

cp /nfs/rmmod /sbin

 

原文链接不详,侵删