本文章主要针对FS100,S5PC100的Android4.0.4 Yaffs2文件系统移植过程,对于类似Android系统或类似Linux内核(版本高于2.6.35)有参考意义。
本文分两部分:
- Nandflash驱动移植
- Yaffs2文件系统移植
一、Nandflash驱动移植
由于内核里面没有S5PC100的Nandflash驱动,所以,我们将Nandflash的驱动文件拷贝到内核目录中:
将s3c_nand.c拷贝到:drivers/mtd/nand/中(s3c_nand.c放到了空间共享里了:http://download.csdn.net/detail/mr_raptor/4848511)。
Nand驱动修改位置:
1. 添加Nand物理地址
@arch/arm/mach-s5pc100/include/mach/map.h
添加如下内容:
-
-
- #define S5PC1XX_PA_NAND (0xE7200000)
- #define S5PC1XX_SZ_NAND SZ_1M
-
2. 添加一个新的头文件:
@arch/arm/mach-s5pc100/include/mach/nand.h
- #ifndef __MACH_NAND_H
- #define __MACH_NAND_H
- #include <linux/mtd/partitions.h>
- struct s3c_nand_mtd_info {
- uint chip_nr;
- uint mtd_part_nr;
- struct mtd_partition *partition;
- };
- #endif
3. 修改寄存器头文件
@arch/arm/plat-samsung/include/plat/regs-nand.h
在regs-nand.h中添加如下宏:
-
-
- #define S3C_NFCONF S3C2410_NFREG(0x00)
- #define S3C_NFCONT S3C2410_NFREG(0x04)
- #define S3C_NFCMMD S3C2410_NFREG(0x08)
- #define S3C_NFADDR S3C2410_NFREG(0x0c)
- #define S3C_NFDATA8 S3C2410_NFREG(0x10)
- #define S3C_NFDATA S3C2410_NFREG(0x10)
- #define S3C_NFMECCDATA0 S3C2410_NFREG(0x14)
- #define S3C_NFMECCDATA1 S3C2410_NFREG(0x18)
- #define S3C_NFSECCDATA S3C2410_NFREG(0x1c)
- #define S3C_NFSBLK S3C2410_NFREG(0x20)
- #define S3C_NFEBLK S3C2410_NFREG(0x24)
- #define S3C_NFSTAT S3C2410_NFREG(0x28)
- #define S3C_NFMECCERR0 S3C2410_NFREG(0x2c)
- #define S3C_NFMECCERR1 S3C2410_NFREG(0x30)
- #define S3C_NFMECC0 S3C2410_NFREG(0x34)
- #define S3C_NFMECC1 S3C2410_NFREG(0x38)
- #define S3C_NFSECC S3C2410_NFREG(0x3c)
- #define S3C_NFMLCBITPT S3C2410_NFREG(0x40)
- #define S3C_NF8ECCERR0 S3C2410_NFREG(0x44)
- #define S3C_NF8ECCERR1 S3C2410_NFREG(0x48)
- #define S3C_NF8ECCERR2 S3C2410_NFREG(0x4C)
- #define S3C_NFM8ECC0 S3C2410_NFREG(0x50)
- #define S3C_NFM8ECC1 S3C2410_NFREG(0x54)
- #define S3C_NFM8ECC2 S3C2410_NFREG(0x58)
- #define S3C_NFM8ECC3 S3C2410_NFREG(0x5C)
- #define S3C_NFMLC8BITPT0 S3C2410_NFREG(0x60)
- #define S3C_NFMLC8BITPT1 S3C2410_NFREG(0x64)
-
- #define S3C_NFCONF_NANDBOOT (1<<31)
- #define S3C_NFCONF_ECCCLKCON (1<<30)
- #define S3C_NFCONF_ECC_MLC (1<<24)
- #define S3C_NFCONF_ECC_1BIT (0<<23)
- #define S3C_NFCONF_ECC_4BIT (2<<23)
- #define S3C_NFCONF_ECC_8BIT (1<<23)
- #define S3C_NFCONF_TACLS(x) ((x)<<12)
- #define S3C_NFCONF_TWRPH0(x) ((x)<<8)
- #define S3C_NFCONF_TWRPH1(x) ((x)<<4)
- #define S3C_NFCONF_ADVFLASH (1<<3)
- #define S3C_NFCONF_PAGESIZE (1<<2)
- #define S3C_NFCONF_ADDRCYCLE (1<<1)
- #define S3C_NFCONF_BUSWIDTH (1<<0)
-
- #define S3C_NFCONT_ECC_ENC (1<<18)
- #define S3C_NFCONT_LOCKTGHT (1<<17)
- #define S3C_NFCONT_LOCKSOFT (1<<16)
- #define S3C_NFCONT_MECCLOCK (1<<7)
- #define S3C_NFCONT_SECCLOCK (1<<6)
- #define S3C_NFCONT_INITMECC (1<<5)
- #define S3C_NFCONT_INITSECC (1<<4)
- #define S3C_NFCONT_nFCE1 (1<<2)
- #define S3C_NFCONT_nFCE0 (1<<1)
- #define S3C_NFCONT_INITECC (S3C_NFCONT_INITSECC | S3C_NFCONT_INITMECC)
-
- #define S3C_NFSTAT_ECCENCDONE (1<<7)
- #define S3C_NFSTAT_ECCDECDONE (1<<6)
- #define S3C_NFSTAT_ILEGL_ACC (1<<5)
- #define S3C_NFSTAT_RnB_CHANGE (1<<4)
- #define S3C_NFSTAT_nFCE1 (1<<3)
- #define S3C_NFSTAT_nFCE0 (1<<2)
- #define S3C_NFSTAT_Res1 (1<<1)
- #define S3C_NFSTAT_READY (1<<0)
- #define S3C_NFSTAT_CLEAR ((1<<7) |(1<<6) |(1<<5) |(1<<4))
-
- #define S3C_NFECCERR0_ECCBUSY (1<<31)
-
4. 修改平台机器代码初始化代码
@arch/arm/mach-s5pc100/mach-smdkc100.c
添加如下内容:
-
- #include <linux/mtd/partitions.h>
- #include <mach/nand.h>
- ...
-
- static struct resource s5pc_nand_resource[] = {
- [0] = {
- .start = S5PC1XX_PA_NAND,
- .end = S5PC1XX_PA_NAND + S5PC1XX_SZ_NAND - 1,
- .flags = IORESOURCE_MEM,
- }
- };
-
- struct platform_device s5pc_device_nand = {
- .name = "s5pc100-nand",
- .id = -1,
- .num_resources = ARRAY_SIZE(s5pc_nand_resource),
- .resource = s5pc_nand_resource,
- };
- struct mtd_partition s3c_partition_info[] = {
- {
- .name = "bootloader",
- .offset = 0,
- .size = SZ_1M,
-
- },
- {
- .name = "kernel",
- .offset = MTDPART_OFS_APPEND,
- .size = (4*SZ_1M),
-
- },
- {
- .name = "rootfs",
- .offset = MTDPART_OFS_APPEND,
- .size = MTDPART_SIZ_FULL,
- },
- };
-
- struct s3c_nand_mtd_info s3c_nand_mtd_part_info = {
- .chip_nr = 1,
- .mtd_part_nr = ARRAY_SIZE(s3c_partition_info),
- .partition = s3c_partition_info,
- };
-
- static struct platform_device *smdkc100_devices[] __initdata = {
- ....
-
- &s5pc_device_nand,
- ....
-
- static void __init smdkc100_machine_init(void)
- {
- ....
-
- s5pc_device_nand.dev.platform_data = &s3c_nand_mtd_part_info;
- ....
5. 由于新添加了驱动文件,要在menuconfig中添加编译项,并且让修改Makefile来编译驱动文件。
@drivers/mtd/nand/Kconfig
添加Kconfig的编译项
- config MTD_NAND_S3C
- tristate "NAND Flash support for S3C SoC"
- depends on ARCH_S5PC100 && MTD_NAND
- help
- This enables the NAND flash controller on the S3C.
-
-
- No board specfic support is done by this driver, each board
- must advertise a platform_device for the driver to attach.
-
-
- config MTD_NAND_S3C_HWECC
- bool "S3C NAND Hardware ECC"
- depends on MTD_NAND_S3C
- help
- Enable the use of the S3C's internal ECC generator when
- using NAND. Early versions of the chip have had problems with
- incorrect ECC generation, and if using these, the default of
- software ECC is preferable.
-
- If you lay down a device with the hardware ECC, then you will
- currently not be able to switch to software, as there is no
- implementation for ECC method used by the S3C
6. 添加驱动编译依赖文件
@drivers/mtd/nand/Makefile
- # MichaelTang add for nand start
- obj-$(CONFIG_MTD_NAND_S3C) += s3c_nand.o
- # MichaelTang add for nand end
7. 执行make menuconfig,将新添加的Nandflash驱动及mtd支持编译到内核中。
选择Kernel编译项:
- Device Drivers --->
- <*> Memory Technology Device (MTD) support
- <*> Caching block device access to MTD devices
- 注:用于支持MTD块设备
- <*> NAND Device Support --->
- <*> NAND Flash support for S3C SoC
- [*] S3C NAND Hardware ECC
- 注:选择新添加的Nand设备驱动,选择ECC硬件校验
二、Yaffs2文件系统移植
我们的Android文件系统使用YAFFS2文件系统格式,选择对应的支持项:
- File systems --->
- [*] Miscellaneous filesystems --->
- <*> YAFFS2 file system support
注:默认Linux内核里不支持YAFFS2文件系统,我们可以从yaffs的官方网站上去下载其源码,然后给你的内核打上补丁即可:
http://www.yaffs.net/download-yaffs-using-git
保存退出,执行make zImage。
常见问题:
问题1:
- fs/yaffs2/yaffs_vfs.c: In function 'yaffs_fill_inode_from_obj':
- fs/yaffs2/yaffs_vfs.c:1333: error: assignment of read-only member 'i_nlink'
- fs/yaffs2/yaffs_vfs.c: In function 'yaffs_unlink':
- fs/yaffs2/yaffs_vfs.c:1747: error: decrement of read-only member 'i_nlink'
- fs/yaffs2/yaffs_vfs.c: In function 'yaffs_link':
- fs/yaffs2/yaffs_vfs.c:1782: error: assignment of read-only member 'i_nlink'
- fs/yaffs2/yaffs_vfs.c: In function 'yaffs_rename':
- fs/yaffs2/yaffs_vfs.c:1901: error: decrement of read-only member 'i_nlink'
提示错误,说给只读的成员i_nlink赋值,出错代码如下:
- inode->i_nlink = yaffs_get_obj_link_count(obj);
查看inode定义在 include/linux/fs.h
- ......
-
-
-
-
-
-
-
- union {
- const unsigned int i_nlink;
- unsigned int __i_nlink;
- };
- ......
通过上面的注释可知,我们可以直接读取i_nlink成员,但是不能对其进行修改操作,必须使用(set|clear|inc|drop)_nlink进行修改操作。
于是,将出错的代码处改为:
-
- set_nlink(inode, yaffs_get_obj_link_count(obj));
-
问题2:
- fs/yaffs2/yaffs_vfs.c:440: warning: initialization from incompatible pointer type
- fs/yaffs2/yaffs_vfs.c:445: warning: initialization from incompatible pointer type
- fs/yaffs2/yaffs_vfs.c:447: warning: initialization from incompatible pointer type
- fs/yaffs2/yaffs_vfs.c: In function 'yaffs_mtd_put_super':
- fs/yaffs2/yaffs_vfs.c:2514: error: 'struct mtd_info' has no member named 'sync'
- fs/yaffs2/yaffs_vfs.c:2515: error: 'struct mtd_info' has no member named 'sync'
- fs/yaffs2/yaffs_vfs.c: In function 'yaffs_internal_read_super':
- fs/yaffs2/yaffs_vfs.c:2702: error: 'struct mtd_info' has no member named 'erase'
- fs/yaffs2/yaffs_vfs.c:2703: error: 'struct mtd_info' has no member named 'read'
- fs/yaffs2/yaffs_vfs.c:2704: error: 'struct mtd_info' has no member named 'write'
- fs/yaffs2/yaffs_vfs.c:2705: error: 'struct mtd_info' has no member named 'read_oob'
- fs/yaffs2/yaffs_vfs.c:2706: error: 'struct mtd_info' has no member named 'write_oob'
- fs/yaffs2/yaffs_vfs.c:2707: error: 'struct mtd_info' has no member named 'block_isbad'
- fs/yaffs2/yaffs_vfs.c:2708: error: 'struct mtd_info' has no member named 'block_markbad'
- fs/yaffs2/yaffs_vfs.c:2732: error: 'struct mtd_info' has no member named 'erase'
- fs/yaffs2/yaffs_vfs.c:2733: error: 'struct mtd_info' has no member named 'block_isbad'
- fs/yaffs2/yaffs_vfs.c:2734: error: 'struct mtd_info' has no member named 'block_markbad'
- fs/yaffs2/yaffs_vfs.c:2734: error: 'struct mtd_info' has no member named 'read'
- fs/yaffs2/yaffs_vfs.c:2734: error: 'struct mtd_info' has no member named 'write'
- fs/yaffs2/yaffs_vfs.c:2736: error: 'struct mtd_info' has no member named 'read_oob'
- fs/yaffs2/yaffs_vfs.c:2736: error: 'struct mtd_info' has no member named 'write_oob'
- fs/yaffs2/yaffs_vfs.c:2757: error: 'struct mtd_info' has no member named 'erase'
- fs/yaffs2/yaffs_vfs.c:2757: error: 'struct mtd_info' has no member named 'read'
- fs/yaffs2/yaffs_vfs.c:2757: error: 'struct mtd_info' has no member named 'write'
- fs/yaffs2/yaffs_vfs.c:2759: error: 'struct mtd_info' has no member named 'read_oob'
- fs/yaffs2/yaffs_vfs.c:2759: error: 'struct mtd_info' has no member named 'write_oob'
- make[2]: *** [fs/yaffs2/yaffs_vfs.o] 错误 1
- make[1]: *** [fs/yaffs2] 错误 2
- make: *** [fs] 错误 2
根据错误信息,mtd_info结构体成员没有对应的:sync、erase、read、write等成员,找到struct mtd_info的定义:
@include/linux/mtd/mtd.h
- struct mtd_info {
- ......
-
-
-
-
-
-
-
-
- int (*_erase) (struct mtd_info *mtd, struct erase_info *instr);
- int (*_point) (struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, void **virt, resource_size_t *phys);
- int (*_unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
- unsigned long (*_get_unmapped_area) (struct mtd_info *mtd,
- unsigned long len,
- unsigned long offset,
- unsigned long flags);
- int (*_read) (struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf);
- int (*_write) (struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf);
- int (*_panic_write) (struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf);
- int (*_read_oob) (struct mtd_info *mtd, loff_t from,
- struct mtd_oob_ops *ops);
- int (*_write_oob) (struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops);
- int (*_get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
- size_t len);
- int (*_read_fact_prot_reg) (struct mtd_info *mtd, loff_t from,
- size_t len, size_t *retlen, u_char *buf);
- int (*_get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
- size_t len);
- int (*_read_user_prot_reg) (struct mtd_info *mtd, loff_t from,
- size_t len, size_t *retlen, u_char *buf);
- int (*_write_user_prot_reg) (struct mtd_info *mtd, loff_t to,
- size_t len, size_t *retlen, u_char *buf);
- int (*_lock_user_prot_reg) (struct mtd_info *mtd, loff_t from,
- size_t len);
- int (*_writev) (struct mtd_info *mtd, const struct kvec *vecs,
- unsigned long count, loff_t to, size_t *retlen);
- void (*_sync) (struct mtd_info *mtd);
- int (*_lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
- int (*_unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
- int (*_is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
- int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);
- int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
- int (*_suspend) (struct mtd_info *mtd);
- void (*_resume) (struct mtd_info *mtd);
- ......
-
- static inline void mtd_sync(struct mtd_info *mtd)
- {
- if (mtd->_sync)
- mtd->_sync(mtd);
- }
-
- ......
由代码及注释可知,mtd_info中成员发生改变是为了让大家使用新的mtd_xxx()封装函数而不使用函数指针。我们有两种方式修改代码:
1>.直接将mtd->sync()的调用,改成mtd->_sync(),这种方式修改简单,也没有问题,但是和新mtd_info的初衷相悖。
2>.将mtd->sync()改成mtd_sync()
修改位置:
@fs/yaffs2/yaffs_vfs.c
@fs/yaffs2/yaffs_mtdif.c
@fs/yaffs2/yaffs_mtdif1.c
@fs/yaffs2/yaffs_mtdif2.c
问题3:
- fs/yaffs2/yaffs_vfs.c:2967: error: implicit declaration of function 'd_alloc_root'
- fs/yaffs2/yaffs_vfs.c:2967: warning: assignment makes pointer from integer without a cast
-
- make[2]: *** [fs/yaffs2/yaffs_vfs.o] 错误 1
- make[1]: *** [fs/yaffs2] 错误 1
- make: *** [fs] 错误 1
根据错误信息知,找不到d_alloc_root方法,原因是新的linux内核fs/dcache.c里使用d_make_root函数,所以直接将函数名换下即可。
@fs/yaffs2/yaffs_vfs.c
-
- root = d_make_root(inode);
-
- yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: d_make_root done");
-
问题4:
- fs/yaffs2/yaffs_vfs.c:2392: error: implicit declaration of function 'get_sb_bdev'
- fs/yaffs2/yaffs_vfs.c: At top level:
- fs/yaffs2/yaffs_vfs.c:2399: error: unknown field 'get_sb' specified in initializer
- fs/yaffs2/yaffs_vfs.c:2399: warning: initialization makes integer from pointer without a cast
- fs/yaffs2/yaffs_vfs.c:2423: error: unknown field 'get_sb' specified in initializer
错误信息提示,找不到get_sb_bdev和get_sb。
分析2.6.35前的代码可知,这两个函数是旧内核提供的函数,新内核使用了mount_bdev和mount函数,所以我们做如下代码修改:
第一处,针对yaffs的修改:
-
- #if 0
- static int yaffs_read_super(struct file_system_type *fs,
- int flags, const char *dev_name,
- void *data, struct vfsmount *mnt)
- {
-
-
- return get_sb_bdev(fs, flags, dev_name, data,
- yaffs_internal_read_super_mtd, mnt);
- }
-
-
- static struct file_system_type yaffs_fs_type = {
- .owner = THIS_MODULE,
- .name = "yaffs",
- .get_sb = yaffs_read_super,
- .kill_sb = kill_block_super,
- .fs_flags = FS_REQUIRES_DEV,
- };
- #else
- static struct super_block *yaffs_read_super(struct file_system_type *fs,
- int flags, const char *dev_name,
- void *data)
- {
- return mount_bdev(fs, flags, dev_name, data,
- yaffs_internal_read_super_mtd);
- }
- static struct file_system_type yaffs_fs_type = {
- .owner = THIS_MODULE,
- .name = "yaffs",
-
- .mount = yaffs_read_super,
- .kill_sb = kill_block_super,
- .fs_flags = FS_REQUIRES_DEV,
- };
- #endif
-
第二处,针对Yaffs2的修改:
- #ifdef CONFIG_YAFFS_YAFFS2
-
- static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data,
- int silent)
- {
- return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL;
- }
-
-
-
- #if 0
- static int yaffs2_read_super(struct file_system_type *fs,
- int flags, const char *dev_name, void *data,
- struct vfsmount *mnt)
- {
- return get_sb_bdev(fs, flags, dev_name, data,
- yaffs2_internal_read_super_mtd, mnt);
- }
- static struct file_system_type yaffs2_fs_type = {
- .owner = THIS_MODULE,
- .name = "yaffs2",
- .get_sb = yaffs2_read_super,
- .kill_sb = kill_block_super,
- .fs_flags = FS_REQUIRES_DEV,
- };
- #else
- static struct super_block *yaffs2_read_super(struct file_system_type *fs,
- int flags, const char *dev_name,
- void *data)
- {
- return mount_bdev(fs, flags, dev_name, data,
- yaffs2_internal_read_super_mtd);
- }
- static struct file_system_type yaffs2_fs_type = {
- .owner = THIS_MODULE,
- .name = "yaffs2",
-
- .mount = yaffs2_read_super,
- .kill_sb = kill_block_super,
- .fs_flags = FS_REQUIRES_DEV,
- };
- #endif
-
问题5:
error: 'MTD_OOB_AUTO' undeclared
找不到符号MTD_OOB_AUTO,新内核里名字发生了改变:
@include/mtd/mtd-abi.h
MTD_OOB_AUTO -> MTD_OPS_AUTO_OOB
所以源码中所有的位置都改一下即可,共有2个文件使用到:
@fs/yaffs2/yaffs_mtdif1.c
@fs/yaffs2/yaffs_mtdif2.c
可能出的问题:
end_writeback找不到符号
解决方法:end_writeback(inode)改成clear_inode(inode)
---------------------------------------
另:随着Android4.0.4版本的更新,其文件系统体积也直线提升,生成的映像有250M左右,由于S5PC100内存只有256M,所以使用UBOOT烧写时就可能出问题,通过USB或tftp下载的文件系统将Uboot代码覆盖了,我们将uboot的运行地址放到最高的几M处。
假如,默认运行地址为0x27e00000,新运行地址为0x2f000000,S5PC100物理内存空间(0x20000000~0x30000000共256M)。
grep "0x27e00000" -R ./
替换了所有的地址就行了
主要是config.h、smdkc100.h和汇编代码文件中定义的几个和地址搬运有关的宏。