嵌入式ARM linux系统如何设置开机启动应用程序? | 您所在的位置:网站首页 › 设备的mac地址与预想的不符怎么解决 › 嵌入式ARM linux系统如何设置开机启动应用程序? |
其实楼主的问题很专业,之前我在周立功那边也了解过不少。\x0d\x0a 当用户需要EasyARM-iMX283在开机启动后就运行指定的应用程序或指令时,可以通过vi命令编辑/etc/rc.d/init.d/start_userapp,将要执行的指令添加到里面。若用户有一个hellow的程序放在/home/目录中,那么设置hellow程序开机启动的方法如程序清单 1.1红色部分所示。\x0d\x0a程序清单1.1 用户启动文件\x0d\x0a#!/bin/sh \x0d\x0a#you can add your app start_command three \x0d\x0a/home/hellow \x0d\x0a#start qt command,you can delete it 下面是启动QT界面的指令,若用户不需要启动QT,可以直接删除\x0d\x0aexport TSLIB_PLUGINDIR=/usrb/ts/ \x0d\x0aexport TSLIB_CONFFILE=/etc/ts.conf \x0d\x0aexport TSLIB_TSDEVICE=/dev/input/ts0 \x0d\x0aexport TSLIB_CALIBFILE=/etc/pointercal \x0d\x0aexport QT_QWS_FONTDIR=/usrb/fonts\x0d\x0aexport QWS_MOUSE_PROTO=Tslib:/dev/input/ts0 \x0d\x0a/usr/zhiyuan/zylauncher/start_zylauncher \x0d\x0a\x0d\x0a如果程序是一个阻塞程序(程序被运行后不会退出或返回),则可能会导致位于其后的指令或程序无法得到执行,并且始终占用串口终端,造成其他程序(比如Shell)无法通过串口终端与用户交互。对于此类应用程序,可以在其后面添加“ ”(注意:是“空格”+“”符号)让其在后台运行,如下所示:\x0d\x0a/home/hellow 这文章算是最近工作的备忘。 FDT是ARM Linux最新的设备驱动程序信息表,使用FDT的内核,就不用像过去的内核那样,一个板子加一个mach的C文件,所有的设备信息可以记录在一个树状信息文件里面。 目前这方面资料比较少,我以AM335x处理器为例概括一下FDT的使用: FDT仅仅是一个信息的目录和参数表,要使用某个功能内核中还必须有相应的驱动程序代码 FDT的源文件位置在:arch/arm/boot/dts,例如,TI的Beagle bone black,源文件是arch/arm/boot/dts/am335x_boneblack.dts FDT在make ARCH=arm的时候就会自动生成,也可用make ARCH=arm dtbs来生成,例如TI的Beagle bone black生成的文件是arch/arm/boot/dts/am335x_boneblack.dtb,这是一个二进制文件 要想新增你自定义的FDT,请修改arch/arm/boot/dts/Makefile,并在相应的Kconfig中增加config选项,例如,TI的Beagle bone black,Kconfig的位置在arch/arm/mach-omap2/Kconfig FDT的dtb文件由u-boot传递给内核,u-boot必须把这个文件拷贝到内核解压地址之后的某个位置,确保内核解压的时候不会覆盖,然后使用“bootm [内核地址] - [dtb地址]”来启动内核 如果dtb文件不正确,对于3.10以上的内核,可能什么显示都没有,3.8内核,可能就显示到Uncompressing kernel......done FDT的编写规则说明在Documentation/devicetree/bindings,不同的设备有相应的txt文件说明,其中的“compatible”可以作为关键字搜索驱动程序的源文件,例如,AM335x的GPIO,用“ti,omap4-gpio”为关键字,可以找到其代码位于drivers/gpio/gpio-omap.c FDT可以包含子文件,比如am335x_boneblack.dts就包含了am33xx.dtsi,am335x-bone-common.dtsi 以一个例子来说明编写规则,我的板子上,I2C0上挂了一个音频CODEC,其地址是0x18,型号是TLV320AIC3104IRHBT。 先找到i2c0节点的位置,这在arch/arm/boot/dts/am33xx.dtsi中: i2c0: i2c@44e0b000 { compatible = "ti,omap4-i2c" #address-cells = 1 #size-cells = 0 ti,hwmods = "i2c1" reg = 0x44e0b000 0x1000 interrupts = 70 status = "disabled" } 要在这个节点上挂东西,可以直接在am33xx.dtsi中挂,可以写成这样: i2c0: i2c@44e0b000 { compatible = "ti,omap4-i2c" #address-cells = 1 #size-cells = 0 ti,hwmods = "i2c1" reg = 0x44e0b000 0x1000 interrupts = 70 status = "okay" tlv320aic3x: tlv320aic3x@18 { compatible = "ti,tlv320aic3x" reg = 0x18 status = "okay" AVDD-supply = ldo4_reg IOVDD-supply = ldo4_reg DRVDD-supply = ldo4_reg DVDD-supply = ldo4_reg } } 其中compatible字串“ti,tlv320aic3x”是在Documentation/devicetree/bindings里面全文搜索“tlv320aic”获得的,“tlv320aic3x: tlv320aic3x@18”遵循的是“标识符:名称@地址”的格式,前面的“i2c0: i2c@44e0b000”也是这个格式。这里的标识符可以在包含这个文件的文件或这个文件的其他位置引用,因此,可以使用arch/arm/boot/dts/am335x-boneblack.dts包含arch/arm/boot/dts/am33xx.dtsi,然后在am335x-boneblack.dts里写: i2c0 { status = "okay" tlv320aic3x: tlv320aic3x@18 { compatible = "ti,tlv320aic3x" reg = 0x18 status = "okay" AVDD-supply = ldo4_reg IOVDD-supply = ldo4_reg DRVDD-supply = ldo4_reg DVDD-supply = ldo4_reg } } i2c0表示引用了i2c0这个标识符,然后把括号里的内容挂载到标识符下,如果属性的名字相同,例如status出现两次,前面是“disabled”后面是“okay”,以后面的为准,引用标识符的次数不受限制。 也许一开始会觉得FDT的工作过程很神秘,但你只要用compatible的字串去全文搜索一下C文件,然后仔细阅读一下,就会发现很简单,没过几分钟你就可以自定义FDT节点的属性了。反倒是这些操作过程我没找到什么文档说,比较头痛,所以我把这些写出来,希望能给大家帮助。 下面说说initrd,initrd的用处是给内核一个初始的基本文件系统,用来加载内核模块之类的东西。很多人觉得嵌入式系统不需要initrd,也可以把initrd作为最终的根文件系统。我用initrd是用来校验真正的根文件系统,因为在嵌入式设备上,无法预测用户到底什么时候关机,可能会造成文件系统问题。 initrd可以用buildroot,像制作正常文件系统一样做,最后把根下的linuxrc换成一个例如下面这样的文件: #!/bin/sh /bin/echo Now Check SD Card /sbin/fsck.ext4 /dev/mmcblk0p5 虽然Documentation/initrd.txt里面说,内核会执行initrd里面的/sbin/init,但在我用的linux-3.8.13上,init/do_mounts_initrd.c里面,执行的是/linuxrc,不知道是不是文档没有更新过来。具体的调用顺序是,kernel_init(init/main.c) kernel_init_freeable(init/main.c) prepare_namespace(init/do_mounts.c) initrd_load(init/do_mounts_initrd.c) handle_initrd (init/do_mounts_initrd.c)。 在使用initrd的时候有几点需要注意的: 不建议在initrd上挂载别的东西,会引起未知的问题,貌似看到个文章说这个,找不到了 因为上面的这条,而且在initrd的时候,内核还没有挂载devtmpfs,因此建议使用静态设备节点,以AM335x为例(内核参数console=/dev/ttyO0,115200n8),必须的节点有: /dev/null /dev/console /dev/ttyO0 这些节点可以用fakeroot之后mknod在buildroot的output/target/dev里创建,除了/dev/console,buildroot会自己创建,其他也可以写到buildroot的system/device_table.txt里面让buildroot自动创建: # /dev/null c 6660 0 1 3 - - - /dev/ttyO0c 600 0 0 250 0 - - - 如果你用的是Atmel的处理器,上面的ttyO0可能是ttyS0,如果是三星的,可能是ttySAC0,而且major和minor也会不一样,请自行解决。如果你像我一样要检验SD卡,那就还必须加上SD卡的分区对应的节点。 /linuxrc可以是个程序也可以是个脚本,脚本的话,命令写绝对路径,而且记得把/linuxrc的mode改为755 使用initrd只需要用u-boot把buildroot制作的文件系统映像拷贝到内存里,然后传递initrd=[地址],[容量]这样的参数给内核,例如initrd=0x81300000,8M,最终的root参数可以不变,例如root=/dev/mmcblk0p5,这表示最终的root是SD卡上扩展分区中的第一个逻辑分区。给两个内核参数的例子: console=ttyO0,115200n8 root=/dev/mmcblk0p5 initrd=0x81300000,8M vram=16M consoleblank=0 console=ttyO0,115200n8 initrd=0x81300000,8M root=/dev/nfs rw nfsroot=192.168.5.226:/home/cdu/nfsroot ip=192.168.5.222:192.168.5.226:192.168.5.1:255.255.255.0:core335x:eth0:off vram=16M consoleblank=0 第一个不解释了,第二个表示使用initrd,同时使用nfsroot。 最后啰嗦一句,使用initrd需要在内核配置里打开支持,这个网上的资料太多了,我就不说在哪里了。 【问题】 此处我要实现的是将芯片的ID用于网卡MAC地址,网卡驱动是enc28j60_init。 但是,读取芯片ID的函数,在as352x_afe_init模块中,所以要先初始化as352x_afe_init。 此处,内核编译完之后,在生成的system.map中可以看到, enc28j60_init在as352x_afe_init之前,所以,无法去读芯片ID。 所以我们的目标是,将as352x_afe_init驱动初始化放到enc28j60_init之前, 然后才能读取芯片ID,才能用于网卡初始化的时候的,将芯片ID设置成网卡MAC地址。 【解决过程】 【1】 最简单想到的,是内核里面的 archarmmach-as352xcore.c 中,去改devices设备列表中的顺序。 enc28j60_init对应的是ssp_device,因为网卡初始化用到的是SPI驱动去进行和通讯的。 as352x_afe_init对应的是afe_device。 原先是: 把afe改到最前面: 但是,实际结果是,没有任何影响,连systemp.map生成的,那么模块初始化顺序,都没有任何变化。 也就说明,想要实现驱动加载顺序的改变,改core.c里面的设备列表顺序是没有用的。 更多linux内核视频教程文档资料免费领取后台私信 【内核】 自行获取. Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂 【2】 在网上看到很多帖子,其说明的也很清楚了,就是: Linux内核为不同驱动的加载顺序对应不同的优先级,定义了一些宏: includelinuxinit.h 把自己的驱动的函数名用这些宏去定义之后, 就会对应不同的加载时候的优先级。 其中,我们写驱动中所用到的module_init对应的是 #define module_init(x) __initcall(x)而 #define __initcall(fn) device_initcall(fn) 所以,驱动对应的加载的优先级为6 在上面的不同的优先级中, 数字越小,优先级越高。 同一等级的优先级的驱动,加载顺序是链接过程决定的,结果是不确定的,我们无法去手动设置谁先谁后。 不同等级的驱动加载的顺序是先优先级高,后优先级低,这是可以确定的。 所以,像我们之前在驱动中用: 所以,大家都是同一个优先级去初始化, 最后这些驱动加载的顺序,可以查看在根目录下, 生成的system.map: 此处就是由于 c0019920 t __initcall_i2c_dev_init6 c0019924 t __initcall_as352x_afe_i2c_init6 c0019928 t __initcall_as352x_afe_init6 在c00198e4 t __initcall_enc28j60_init6之前,所以我这里才要去改。。。 知道原理,能想到的,就是要么把as352x_afe_init改到enc28j60_init之前一级,即优先级为5。即在驱动中,调用:fs_initcall(as352x_afe_init);要么把enc28j60_init改到as352x_afe_init之后,即优先级为7即在驱动中,调用:late_initcall(enc28j60_init);但是,此处麻烦就麻烦在,如果把as352x_afe_init改到enc28j60_init之前一级,发现后面网卡初始化enc28j60_init中,虽然读取芯片ID对了,但是后面的IP-auto configure 有问题。所以放弃。 如果把enc28j60_init改到as352x_afe_init之后,但是,从system.map中看到的是,优先级为7的驱动中,明显有几个驱动,也是和网卡初始化相关的,所以,这样改,尝试后,还是失败了。 所以,没法简单的通过调整现有的驱动的顺序,去实现顺序的调整。最后,被逼无奈,想到了一个可以实现我们需求的办法,那就是,单独定义一个优先级,把afe相关的初始化都放到那里面去,这样,就可以保证,其他没什么相关的冲突了。最后证实,这样是可以实现目的的。 具体添加一个新的优先级的步骤如下: 1.定义新的优先级 includelinuxinit.h中: 2.用对应新的宏,定义我们的驱动: 做到这里,本以为可以了,但是编译后,在system.map中,发现之前优先级为7的那几个函数,被放到system.map最后了,而不是预想的,在优先级7之后,在 之前。最后,发现时没有把对应的链接文件中的宏加进去: 3.includeasm-genericvmlinux.lds.h 最后,再重新编译,就可以实现我们要的,和afe相关的驱动初始化,都在网卡enc28j60_init之前了。也就可以在网卡里面读芯片ID了。当然,对应编译生成的system.map文件中,对应的通过module_init定义的驱动,优先级也都变成7了。而late_initcall对应优先级8了。 注:当前开发板arm的板子,所以,对应的load 脚本在: linux-2.6.28.4archarmkernelvmlinux.lds 看起来,应该是这个文件: linux-2.6.28.4archarmkernelvmlinux.lds.S 生成上面那个脚本的。vmlinux.lds中的这一行: 就是将之前那些对应的init类型的函数,展开,放到这对应的位置。 【3】 不过,最后的最后,竟然发现网卡还是工作不正常,结果第二天,无意间发现是网卡地址设置导致网卡工作不正常的。 也就是说,实际是直接将afe设置到原先的优先级5就可以的,而不用这么麻烦去改系统的东西的... 不过,至少这也是一种办法,虽然不是那么的好...
欢迎分享,转载请注明来源:内存溢出 原文地址:https://outofmemory.cn/yw/7218221.html |
CopyRight 2018-2019 实验室设备网 版权所有 |