备忘一次嵌入式实训之二 根文件系统制作及led驱动

三 制作根文件系统

说明:

三种启动方式的根文件系统制作大致相同,细微区别将会说明。

制作根文件系统的busybox版本为busybox-1.15.2;

1.创建根文件系统目录:
#cd
转入到你的工作目录。
#mkdir rootfs
#cd rootfs
#mkdir bin dev etc lib proc sbin sys usr mnt tmp var
#mkdir usr/bin usr/lib usr/sbin lib/modules etc/sysconfig

2. 创建最基本的设备文件:
#cd dev

#mknod -m 666 console c 5 1

#mknod -m 666 null c 1 3

#cd ..

 

3. 安装/etc配置文件

etc/fstab文件:

proc /proc proc defaults 0 0

tmpfs /tmp tmpfs defaults 0 0

sysfs /sys sysfs defaults 0 0

tmpfs /dev tmpfs defaults 0 0

var /dev tmpfs defaults 0 0

 

etc/inittab文件:

#/etc/inittab

::sysinit:/etc/init.d/rcS

console::askfirst:-/bin/sh

::ctrlaltdel:/sbin/reboot

::shutdown:/bin/umount -a -r

 

etc/init.d/rcS文件:

#!/bin/sh

echo "Processing /etc/init.d/rcS"

echo "mount -a"

mount -a

exec /etc/rc.local

 

etc/rc.local文件:

#!/bin/sh

PATH=/bin:/sbin:/usr/bin:/usr/sbin

runlevel=S

prevlevel=N

export PATH runlevel prevlevel

mount -a

mkdir /dev/pts

mount -t devpts devpts /dev/pts

echo "Processing /etc/rc.local"

echo "get hostname"

/bin/hostname -F /etc/sysconfig/HOSTNAME

echo "Starting mdev"

echo /sbin/mdev > /proc/sys/kernel/hotplug

mdev -s

mkdir -p /var/lock

echo "ifconfig eth0 10.10.66.135 netmask 255.255.255.0 up"

ifconfig eth0 10.10.66.135 netmask 255.255.255.0 up

echo "**************************************************"

echo " “

echo " Linux 2.6.36 ”

echo " “

echo " by lc ”

echo " “

echo " 2012-12-16 ”

echo " “

echo "**************************************************"

 

etc/sysconfig/HOSTNAME 文件:

lvchao-study (可任意取)

 

etc/profile 文件:

# /etc/profile: system-wide .profile file for the Bourne shells

# Ash profile

# vim: syntax=sh

# No core files by default

#ulimit -S -c 0 > /dev/null 2>&1

USER="`id -un`"

LOGNAME=$USER

PS1='[\u@\h]# '

PATH=$PATH

HOSTNAME=`/bin/hostname`

echo "Processing /etc/profile... "

echo "Done"

export USER LOGNAME PS1 PATH

etc/passwd 文件:

#username:password:User ID:Group ID:comment:home directory:shell

root:x:0:0:root:/root:/bin/sh

 

etc目录执行命令

>touchmdev.conf

PS: 上面步骤可以使用脚本自动完成,请参见附件rootfs.sh(需root权限)

 

4. 编译内核模块:

#cd linux-2.6.36

#make modules

 

5. 安装内核模块:

#make modules_install INSTALL_MOD_PATH=/xxx/rootfs

 

6. 配置busybox

#cd busybox-xxx

#make menuconfig,配置如下:

Busybox Settings -> build Options ->选中"Build busybox as a static

binary“,即静态链接,免去拷贝大量的库文件,但是这就导致在编译驱动测试程序的时候加上编译参数 -static 

Installation Options -> 选中"Don't use /usr",以免busybox不慎被安装到

宿主机系统的相应目录下,破坏宿主机系统。
Busybox Installation Prefix 
/xxx/rootfs),修改该选项表明编译后的

busybox将安装到该位置。

添加

Busybox Settings --->

Busybox Library Tuning --->

[*] Fancy shell prompts

//Setting this option allows for prompts to use things like \w and $ and escape codes.
PS: 
如果遇到’Makefile:1270: *** 混和的隐含和普通规则。 停止‘之类的错误,无法进入配置图形界面,需修改其Makefile文件,编辑Makefile

查找这里使用的busybox1270

/ %/: prepare scripts FORCE

修改为

%/: prepare scripts FORCE

查找 422

config %config: scripts_basic outputmakefile FORCE

修改为

%config: scripts_basic outputmakefile FORCE

造成这种错误的原因是版本gcc版本过高
7. 
编译、安装busybox
#make ARCH=arm CROSS_COMPILE=arm-linux-

#make install

安装到Busybox Installation Prefix /xxx/rootfs)设定的目录里。

完成根文件系统的创建

 

8. 因为initramfs根文件系统启动时执行的第一个程序是init,而不是

linuxrc,所以在此,我们制作的根文件系统需要添加一个init文件,

相应的linuxrc文件就不再需要了。
按照如下方式修改根文件系统

#cd /xxx/rootfs //切换到根文件系统目录
#ln -s bin/busybox init

 

因为initramfs根文件系统启动时执行的第一个程序是init,而不是linuxrc,所以在此,我们制作的根文件系统需要添加一个init文件,相应的linuxrc文件就不再需要了

 

注意:

此文档说明的是initramfs启动Linux方法,所以跟文件系统制作到此便结束

了,如果你需要的是ramdisk方式启动linux,那么你还需要将其打包成randisk镜像,并且linuxrc文件也是必要的。

打包方法如下:

 

1dd if=/dev/zero of=ramdisk.img bs=4k count=4096,这样制作的镜像有4MB,可以根据自己的需要修改
2mkfs.ext2 -m0 ramdisk.img,将ramdisk.img格式化为ext2格式
3mount -o loop ramdisk.img /mnt/ramdisk/,将ramdisk.img挂载到/mnt/ramdisk目录
4cp -a /rootfs /mnt/ramdisk,将自己的文件系统全部拷贝到/mnt/ramdisk目录
5umount /mnt/ramdisk,卸载ramdisk挂载。此时生成可用的ramdisk.img镜像
6gzip -v9 ramdisk.img,压缩镜像。

 

 

如若是网络根文件系统,那么也不需要将其打包成镜像。

 

四 网卡驱动改动及内核编译生成uImage

网卡驱动需要改动源代码才能使用

修改文件linux-2.6.36/arch/arm/mach-s3c64xx/mach-smdk6410.c

定位180行找到smdk6410_smsc911x_resources 结构体定义:

static struct resource smdk6410_smsc911x_resources[] = {

[0] = {

.start = S3C64XX_PA_XM0CSN1,

.end = S3C64XX_PA_XM0CSN1 + SZ_64K - 1,

.flags = IORESOURCE_MEM,

},

[1] = {

.start = S3C_EINT(10),

.end = S3C_EINT(10),

.flags = IORESOURCE_IRQ | IRQ_TYPE_LEVEL_LOW,

},

};

修改为:

static struct resource smdk6410_smsc911x_resources[] = {

[0] = {

.start = S3C64XX_PA_XM0CSN5,

.end = S3C64XX_PA_XM0CSN5 + SZ_64K - 1,

.flags = IORESOURCE_MEM,

},

[1] = {

.start = S3C_EINT(10),

.end = S3C_EINT(10),

.flags = IORESOURCE_IRQ | IRQ_TYPE_LEVEL_LOW,

},

}

make uImage

;

 

接下来,使用:

 

生成内核镜像uImage

如果提示为找到打包工具,未生成uImage,但是生成Image ,说明缺少uboot生成组件

五 uboot引导参数设置



说明:对于如何烧写到实验板上,本文档不在赘述,请自行谷歌。

请在uboot命令行下输入(其中ip应按实际情况调整)

 

set bootargs console=ttySAC2,115200 mem=256M

 

为方便调试驱动程序,挂载PCnfs文件系统:

内核成功启动后,在命令行输入:

 

mount -t nfs -o intr,nolock,rsize=1024,wsize=1024 10.10.66.109:/home/nfs /mnt

 

六 led&lcd驱动程序移植

移植准备:

开发板提供的驱动程序及其测试程序源码

开发板提供的linux内核源码

以下为移植过程:

记录下将s3c6410_nixietube这个led驱动移植linux2.6.36下的过程

 

找到了linux把硬件地址到虚拟地址映射的大概流程

# 2.6.36版本上的一个关于 struct file_operations结构体 的改动



2012-12-19~20

 

 

S3C64XX_GPMCON:

file:///home/lvchao/sc/s3c6410-2.6.27-samsung-box/arch/arm/plat-s3c64xx/include/plat/gpio-bank-m.h

#define S3C64XX_GPMCON (S3C64XX_GPM_BASE + 0x00) //17h

 

S3C64XX_GPM_BASE:

file:///home/lvchao/sc/s3c6410-2.6.27-samsung-box/arch/arm/plat-s3c64xx/include/plat/regs-gpio.h

#define S3C64XX_GPM_BASE (S3C64XX_VA_GPIO + 0x0820) //47h

#include <plat/gpio-bank-m.h> //定义 S3C64XX_GPM_BASE

#include <mach/map.h> //定义 S3C64XX_VA_GPIO

 

S3C64XX_VA_GPIO:

file:///home/lvchao/sc/s3c6410-2.6.27-samsung-box/arch/arm/mach-s3c6400/include/mach/map.h

#define S3C64XX_VA_GPIO S3C_ADDR(0x00500000) //49h

#include <plat/map-base.h> //定义 S3C_ADDR



S3C_ADDR:

file:///home/lvchao/sc/s3c6410-2.6.27-samsung-box/arch/arm/plat-s3c/include/plat/map-base.h

#define S3C_ADDR_BASE (0xF4000000)

#ifndef __ASSEMBLY__

#define S3C_ADDR(x) ((void __iomem __force *)S3C_ADDR_BASE + (x))

#else

#define S3C_ADDR(x) (S3C_ADDR_BASE + (x))

#endif



关于 #ifndef __ASSEMBLY__ ,网上查了下资料,说说我的理解,__ASSEMBLY__ 是用来区分ARM汇编和C在宏替换上细节的不同,在ARM汇编程序编译时会用到AFLAGS选项,此时__ASSEMBLY__被定义,反之编译C程序时该宏未被定义,那么为什么要区分呢,因为该头文件可能被用于ARM汇编时使用。

显然这里不是编译什么汇编程序,所以可以无视宏定义 #define S3C_ADDR(x) ((void __iomem __force *)S3C_ADDR_BASE + (x))

生效的是 #define S3C_ADDR(x) (S3C_ADDR_BASE + (x))





相关:

__force:表示所定义的变量类型是可以做强制类型转换的,在进行Sparse分析的时候,是不用报告警信息的。

//Sparse是个检测内核漏洞的工具

# define __iomem __attribute__((noderef, address_space(2)))

这个定义与__user, __user是一样的,只不过这里的变量地址是需要在设备地址映射空间的。

# define __user __attribute__((noderef, address_space(1)))

__user这个特性,即__attribute__((noderef, address_space(1))),是用来修饰一个变量的,这个变量必须是非解除参考(no dereference)的,即这个变量地址必须是有效的,而且变量所在的地址空间必须是1,即用户程序空间的。

这里把程序空间分成了3个部分,0表示normal space,即普通地址空间,对内核代码来说,当然就是内核空间地址了。1表示用户地址空间,这个不用多讲,还有一个2,表示是设备地址映射空间,例如硬件设备的寄存器在内核里所映射的地址空间。

所以在内核函数里,有一个copy_to_user的函数,函数的参数定义就使用了这种方式。当然,这种特性检查,只有当机器上安装了Sparse这个工具,而且进行了编译的时候调用,才能起作用的。

__iomem linux2.6.9内核中加入的特性。是用来个表示指针是指向一个I/O的内存空间。主要是为了驱动程序的通用性考虑。由于不同的CPU体系结构对I/O空间的表示可能不同。当使用__iomem时,编译器会忽略对变量的检查(因为用的是void __iomem)。若要对它进行检查,当__iomem的指针和正常的指针混用时,就会发出一些警告

关于为何要将硬件地址映射到虚拟地址:

1.根据系统的具体情况,把寄存器映射到虚拟地址的特定位置,这样,访问寄存器就可以像访问普通内存一样简单方便了。

2.安全

file:///home/lvchao/sc/s3c6410-2.6.27-samsung-box/arch/arm/plat-s3c/include/plat/gpio-cfg.h

#define S3C_GPIO_OUTPUT (S3C_GPIO_SPECIAL(1)) //63h





看了这个板子的外设电路图(核心板的图呢?!!),也到了那宏定义的地址什么的,尽管知道了这个内核版本里此驱动是怎么走的,地址是怎么映射的,但是我是要将其移植到linux-2.6.36版本上的,不知道会不会有所不同,不过相信不同也是细节上很小的的不同,结合Makefile/c语法,首先让它通过编译再说,现在来试试

首先,我们在前面已经找出了驱动源码(s3c6410_nixietube.c)里所有未在当前源文件里定义的宏,也找到了其定义所在的头文件,先来检查下2.6.36里面这些有木有,

#include <asm/io.h> /* __raw_real,__raw_writel */

#include <linux/fs.h> /* struct inode */

#include <mach/gpio.h> /* S3C64XX_GPM(_nr) */

#include <asm/delay.h> /* udelay */

#include <linux/delay.h> /* mdelay */

#include <asm/uaccess.h>

#include <linux/module.h> /* struct module */

#include <linux/kernel.h> /* printk */

#include <plat/gpio-cfg.h> /* s3c_gpio_cfgpin */

#include <plat/regs-gpio.h> /* S3C64XX_GPM_BASE S3C64XX_GPK_BASE */

其中什么linux/asm开头的都有的,这些一般不会有太大改动,2.6.272.6.36还没差太远

第三行 <mach/gpio.h> 

file:///home/lvchao/sc/linux-2.6.36/arch/arm/mach-s3c64xx/include/mach/gpio.h

源码里要用到的宏S3C64XX_GPM在里面内容也没差,倒是2.6.27的那份源码里我找了半天才发现到了 s3c6410-2.6.27-samsung-box/arch/arm/mach-s3c6400/include/mach/gpio.h 居然在6400的里面,大概是通用的吧.

倒数第二行 <plat/gpio-cfg.h> 没差,位置在

file:///home/lvchao/sc/linux-2.6.36/arch/arm/plat-samsung/include/plat/gpio-cfg.h

最后一行 <plat/regs-gpio.h>

看名字是注册gpio设备,我却在2.6.36源码的 plat-samsung 文件夹下没到,翻了下,倒是在 mach-s3c64xx/include/mach/ 里有个,

看里面的内容,发现有所不同,首先那些(gpio-bank-*之类的)头文件就没在里面,想了下,干脆就不在源文件里加了,在这个头文件里加上

/*

* lc add start

*/

#include <mach/gpio-bank-a.h>

#include <mach/gpio-bank-b.h>

#include <mach/gpio-bank-c.h>

#include <mach/gpio-bank-d.h>

#include <mach/gpio-bank-e.h>

#include <mach/gpio-bank-f.h>

#include <mach/gpio-bank-g.h>

#include <mach/gpio-bank-h.h>

#include <mach/gpio-bank-i.h>

#include <mach/gpio-bank-j.h>

#include <mach/gpio-bank-k.h>

#include <mach/gpio-bank-l.h>

#include <mach/gpio-bank-n.h>

#include <mach/gpio-bank-m.h> /* S3C64XX_GPMCON/S3C64XX_GPMDAT */

#include <mach/gpio-bank-o.h>

#include <mach/gpio-bank-p.h>

#include <mach/gpio-bank-q.h>

#include <mach/map.h> /* S3C64XX_VA_GPIO */

/*

*lc add end

*/

如果看仔细的话,这里已经将目录plat改成了mach,因为这些头文件位置也和27的源码不同,

同理,源码(s3c6410_nixietube.c)里面的 #include <plat/regs-gpio.h> 也要改成 #include <mach/regs-gpio.h>

突然发现,在2.6.36源码里面比那份27的还少了3个头文件( gpio-bank-k.h gpio-bank-l.h gpio-bank-m.h )

这可不能少,用的就是 gpio-bank-m.h 把这个三个从27那份源码里全拷过来。

// gpio-bank-*.h 这些文件有点不同,27的里面有预处理判断 #ifndef __ASM_PLAT_S3C64XX_GPIO_BANK_M_H ,没事 不用理会

恩,差不多就这些了,哦,顺便说一句,这个宏(S3C_GPIO_OUTPUT)也在 plat/gpio-cfg.h 里面。

//前面编译成功过内核,那说明交叉编译环境已经OK,修改好Makefile里面的路径就接着往下走吧

开始编译...



报错了

/home/lvchao/Documents/驱动源代码/nixietube/s3c6410_36_lc.c:323: error: unknown field 'ioctl' specified in initializer

/home/lvchao/Documents/驱动源代码/nixietube/s3c6410_36_lc.c:323: warning: initialization from incompatible pointer type

 

这里就没去源码里找为什么了,直接网上查资料

 

error:unknown field 'ioctl' specified in initializer

原因是:在2.6.36内核上file_operations发生了重大的改变:

原先的

int (*ioctl)(struct inode*, struct file*, unsigned int, unsigned long);

被改为了

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

long (*compat_ioctl) (struct file *, unsigned int, unsigned long);



所以呢,把 struct file_operations结构体里的 .ioctl 改为 .unlocked_ioctl

把实现部分也改掉,不然有警告,网上说这警告也会对程序有影响

有警告 static int s3c6410_nixietube_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)

没警告 static long s3c6410_nixietube_ioctl ( struct file *file, unsigned int cmd, unsigned long arg)



这下编译通过了,现在去机房跑跑看...



运行成功,接下来改驱动

 

posted @ 2013-01-02 15:34  赤色  阅读(334)  评论(0编辑  收藏  举报
知识共享许可协议本博客作品采用知识共享署名-相同方式共享 3.0 未本地化版本许可协议进行许可。