基于ARM6410-Ubuntu的NFS挂载方法及驱动模块加载方法

学习驱动,首先当然要先搭好环境,当我们把编好的驱动编译到内核或者以模块的方式

加载进内核后,我们还要写一个测试应用程序,这个程序是在ARM-LINUX下运行的,与其对应的是PC-LINUX,这就又引出了交叉编译的概念,所谓交叉编译,是指在一个环境下编译程序,而在另一个环境下运行,比如我们在PC-linux下编译一个helloworld程序(就是最简单的helloworld程序)但是我们在编译的时候用arm-linux_-gcc命令,这个命令就是指当前编译的程序就是在arm体系结构下运行的,如果你在PC-linux的环境下执行./helloworld命令则会出现”不能执行未知二进制文件”的错误。但把它下载到板子上运行的话则正常运行。

       当然,我们要把编译好在板子上运行的程序传到板子上的方法有很多,比如通过串口下载,USB下载,FTP上传等等,这些都是板子与PC机的通讯方式。当然用的最多最方便的我个人认为就是NFS了,NFS即网络文件系统,我个人理解是这样的:如果我启动了nfs系统挂载成功 那么我就可以把一些在linux上交叉编译好的程序放到这个linux挂载的目录下 然后通过像SecureCRT这样的终端进入这个目录去执行程序,也就省去了把程序下到板子上的麻烦,因为当我们传输的文件过大的时候,用串口USB传送的这种方法就会消耗我们大量的宝贵时间。这个linux挂载的目录其实就是一个共享目录,即ARM-LINUX把板子的资源都挂载到这个目录下,这样PC-LINUX可以通过这个目录来访问板子的资源,当然我们也可以往这个目录里传输文件程序,可以理解为板子里Linux系统的一个目录.

       构建NFS系统并非一件容易的事,之前我也是苦熬了两三天查阅了不少资料,也咨询了不少学长,网上挂载失败发帖的一大把,我想把我构建过程中以及后面编译加载驱动遇到和解决问题的方法和大家分享,我个人认为构建过程当中设置这个设置那个并不重要,重要的是为什么这样设置,这样设置的影响是什么,那样设置会怎么样。因为一般买的开发板带的资料(比如我买的S3C6410)它的确讲解了如何搭建NFS环境,但是发现没几个挂载成功的?为什么 因为它只是一步一步讲的些大众化的步骤,告诉你这一步设置这个,那一步设置那个,至于为什么都不谈。当然,这不是教你学习,公司当然希望越简洁越好,但是,我们作为学习,当然就要刨根问底。下面我就具体谈谈构建加载过程中我遇到的一些问题。

 

       首先,我们要设置一个共享目录,这个目录就是可以被NFS挂载的那个目录,所以接下来就是要配置NFS配置文件了,即/etc/exports文件,如图

 

在下面一行,我们加上这个新目录,我的目录就是/home/localhost/arm6410,后面分别表示可读写,资料同步写入(即当我们把程序或一些文档放进这个目录时,它也会相应的把这份资料写入ARM板子中,这很好理解,因为这个目录就是挂载的板子资源的目录), 最后一个参数表示登入 NFS 主机使用分享目录的使用者,如果是 root的话,那么对于这个分享的目录来说,他就具有 root 的权限。

       接下来,我们就要在这个共享目录下在添加一个根文件系统所需要的目录,我用的是飞凌的板子,已经提供了直接解压就行了,这个目录和PC-LINUX下的root目录一模一样,就是搭建了一个linux环境。

 接下来就是设置IP地址这类东西了,这一点尤为重要,我也是在这个上面纠结了好一阵子,因为这里相关了三个环境,Ubantu的LINUX环境,PC的XP环境,还有一个ARM-LINUX环境,我们要一一对其进行设置,不然挂载就不会成功,

 下面我贴出三个环境的地址

 

如上图所示,首先,三个环境的IP地址要设置在同一个网段,子网掩码设置相同,PC机和板子的网关要相同,最后虚拟机的默认路由要设为PC的IP地址。这样就完成了三个环境的设置,最后还要注意的是虚拟机的设置

 

 

注意右方,我们选择的是桥接。所谓桥接,就是通过一台设备(可能不止一个)把几个网络串起来形成的连接,我们这里就是把三个网络环境串起来形成连接。

 

关于开发板,在上电后,我们可以通过”printenv”命令(这个命令是uboot里的,表示打印出当前开发板的环境配置)我的环境配置信息如下:

 

在这里,我们可以看到其MAC地址,IP地址,网关,子网掩码。这里对我们最重要的也就是bootargs这个参数了,这个参数表示传递给内核的参数,我们并不是把所有的任务都交给操作系统来完成的,我们可以在这之前设定好一些最基本的参数,然后把它传递给内核,比如这里最简单的是串口的初始化设置,设置波特率什么的,就可以通过bootargs传递给内核,这样我们就可以通过串口打印反馈信息来判断程序的执行状态。

bootargs非常的灵活,内核和文件系统的不同搭配就会有不同的设置方法,甚至你也可以不设置bootargs,而直接将其写到内核中去(在配置内核的选项中可以进行这样的设置),正是这些原因导致了bootargs使用上的困难。

root: 用来指定rootfs(即文件系统)的位置,我们这里是这样设置的

root=/dev/nfs

表示在文件系统为基于nfs的文件系统的时候使用。当然指定root=/dev/nfs之后,还需要指定nfsroot=serverip:nfs_dir,即指明文件系统目录存在哪个主机的那个目录下面,这里我们设置的是nfsroot=192.168.1.102:/home/localhost/arm6410/root

注意这里的IP地址我们就设置的是虚拟机的IP地址,后面的目录就是之前设置的共享目录,root就是根文件系统的目录。

console

console=tty  使用虚拟串口终端设备 .

console=ttyS[,options] 使用特定的串口,options可以是这样的形式bbbbpnx,这里bbbb是指串口的波特率,p是奇偶校验位,n是指的bits。

console=ttySAC[,options] 同上面。

这样 总的环境变量就可以设置成

#setenv  bootargs “root=/dev/nfs nfsroot=192.168.0.102:/home/localhost/root

ip=192.168.0.20: 192.168.0.102:192.168.1.1:255.255.255.0:witech.com.cn:eth0:on

console=ttySAC0,115200”

192.168.0.102:ubantu的IP

192.168.0.20:板子的IP

192.168.1.1:网关

255.255.255.0:默认的子网掩码

saveenv

 

这里,setenv也是uboot的一个命令,表示设置环境变量,最后用saveenv保存即可。

  接下来,我以加载S3C_6410自带流水灯的驱动过程谈谈我所遇到的一些问题。

我们要生成目标文件(在linux2.6版本中,驱动文件时以.ko的形式出现的),首先我们要找到驱动程序的源文件。流水灯属于字符设备的驱动程序,所以在/driver/char目录下。第一步,打开该目录下的config文件,加上配置选项如图最后(这里时之前添加好的,如果我们要添加新的驱动程序就要在这里添加新的供内核配置的条目)。

 

 

TE6410_LED表示内核配置时显示(后面有讲)的驱动名称。

Tristate:表示的是三态,所谓的三态就是指当我们配置这个驱动时可以以三种方式设置:1、编译进内核2、以模块的形式加载2、卸载模块

后面的ARCH_S3C64XX表示是其依赖的体系架构,这里我们是基于S3C6410的ARM11的板子。Help下面表示的是帮助信息。

 紧接着我们要修改其makefile文件。如图

 

 

在这里,我们加入目标文件。

 

 下面我们的工作就是编译了。以root用户登录linux的根目录,输入make menuconfig进行内核图形化配置,如图

我们找到led的驱动选项,在这里我们通过敲击回车改变其加载的方式,这里我们选择M即模块加载的方式,(我们之前修改config配置文件的目的其实就是在这里有一条对应修改led驱动配置的选项,当面移动到help选项时也会显示出我们之前编写的帮助提示信息)最后保存退出。

如果没有编译过,那么首先make一下,注意:我们是要把linux烧到板子中运行的,所以要使用make zImage命令,这样会生成一个zImage镜像文件,它是linux内核的一个压缩文件。关于zImage文件,我想说明一下它的机理,它是一个压缩文件,它只能在开发板上运行,但是并不是把所有的信息都压缩了,其中还含有未压缩的一部分程序:比如解压程序,当zImage下载到开发板上运行的时候,首先其自带的解压程序会对linux内核信息下进行加压并载到指定地址运行,经过层层跳转(中间过程略呵呵)最后跳转到init文件夹中的main函数(即整个内核函数的入口函数)中运行内核代码。紧接着我们要执行make dep命令,这个命令的目的是使新加入的模块与内核的模块建立连接关系。我做这个实验的时候没有做这一步因为这个流水灯的驱动是之前做好的,所以已经建立了联系。但是当我们加载了一个新的模块的时候,这一步是不能省略的。最后我们执行make modules。则会在相应目录下(我们是在driver/char/)生成模块.ko文件。

在这里,我想稍微提一下交叉编译的问题,当我们写完驱动程序时,要对其进行编译,我们在PC-LINUX下运行的程序是通过gcc命令运行的(即我们使用的是gcc编译器),但我们要编译在板子上运行的程序时就需要用交叉编译工具,这里我们使用的是arm-linux-gcc命令,于是我们这个命令的描述就是

/usr/local/arm/4.2.2-eabi/usr/bin/arm-linux-gcc –c leds.o leds.c

如果嫌命令太长 可以把之前的路径加到环境变量当中去,这样直接用arm-linux-gcc命令就可以了。当然,这里并没有使用这种方法,我们是修改makefile然后在make modules 的时候就执行了编译的过程并生成了相应的模块(如果我们要添加多个需要编译的对象的时候这种方法就体现出它的方便了)。

 

最后我们就要挂载NFS系统并把驱动程序以模块的形式加载到内核。首先我们要注意的就是硬件设施是否准备完毕

1、串口连接

2、J-LINK的连接(这里我始终不明白,为什么每次都需要连接j-link,如果不连则没有串口反馈信息)

3、网线连接(这里尤其容易遗漏)

如果没有接网线 则挂载时会出现以下错误内容

 

当然,我们在连接上之后,第一步就是要先ping一下,如果双方都互相ping通,则网络方面就没有什么大的问题。

 

之前看到网上说需要关掉防火墙,其实就实践来看没有那个必要。

接下来要开启pc-linux的NFS服务。

我们执行两条命令

#sudo /etc/init.d/portmap restart

#sudo /etc/init.d/nfs-kernel-server restart

上电开发板后,如果挂载成功则会显示如下的信息

 

 

这里会提示你按回车键进入控制台。我们按回车后就进入了arm-linux的目录

接下来我们在pc-linux上把那个模块文件(les.ko)放到共享目录下,就可以在这里访问到那个驱动文件,并把它加载进内核,我们用insmod命令,这里只是一个模块文件,如果我们的模块牵扯到多个文件,我们可以用modprobe命令,这个命令会把该模块和与该模块相关的驱动程序加载进内核。如果加载成功则如图所示

首先,串口打印出进入初始化程序,最后显示初始化成功。这些都可以在驱动程序里设置便于使用者调试。当然一个模块只能加载一次,否则会报错。还有一点要注意的事如果该驱动以内核的方式加载进内核,那么它就理所当然不能再以模块的方式加载。这个不是问题,但是有时候我们会忘记,就本人来说,我第一次把这个流水灯驱动程序下载到开发板上加载后,发现加载失败,但是显示的信息不是“重复加载”而是一些乱七八糟的信息。我以为是驱动程序的错误但是用的是它给的源程序。这让我很是纠结,最后才发现原来我烧录的内核是飞凌公司光盘当中已经配置好的内核镜像。也就是说他把驱动程序已经加载进内核了,所以当然加载不成功,所以我们在烧录内核前一定要卸载内核的驱动(不能全卸载,只是对你试验有影响的驱动可以先从内核卸载掉),如果你是下载的一个全新的内核,就不存在这个问题了。

就说到这吧,顺便提一下,我是个驱动的菜鸟级人物仅代表我个人的观点,还望多多批评指正。

 

你可能感兴趣的:(基于ARM6410-Ubuntu的NFS挂载方法及驱动模块加载方法)