On Wed, 20 Sep 2006 21:08:56 +1200 Charles Manning wrote: > The oob layout I used for yaffs1 fits in with the SmartMedia spec (in terms of > placement of ECC, block & page state bytes etc). SM uses 1 byte for block > state. YAFFS1 used to use 0x00 to mark bad blocks, but I changed this to 'Y' > to make it easier to tell factory marked bad blocks from yaffs-marked. I don't think you should worry about that much, since there's always a way to figure out bad blocks from MTD/NAND layer. You can think of that as of compatibility thingie, not to intrude into yaffs too much for the moment. > This breaks down with some of the more modern non-SM-compatable parts which > have internal controllers etc and don't give you full access to the 16 bytes. > > > > > > I assume there's a better way to do it, but I don't know it. > > The _real_ way out would be to have the tags fit in not more than 8 bytes, > > better in 6. > > The real way, I think, is to start doing tags-in-data for 512 then it can run > yaffs2 on 512-byte pages. Watch this space...... Oh yeah, that's definitely the best, but let's first stabilize this one. It will work for most cases, I think. > > And thanks a lot for the review! > > Yea, sorry it should have been sooner :-). Well, inlined in the updated patch with your comments/corrections taken into account. This patch adds YAFFS2 <--> MTD interworking support for the 2.6.17+ kernels. fs/yaffs2/yaffs_fs.c | 99 +++++++++++++++++++++++++++++++++++++++++++---- fs/yaffs2/yaffs_mtdif.c | 86 ++++++++++++++++++++++++++++++++++++++++ fs/yaffs2/yaffs_mtdif2.c | 40 ++++++++++++++++++ 3 files changed, 218 insertions(+), 7 deletions(-) Signed-off-by: Vitaly Wool Index: linux-2.6.git/fs/yaffs2/yaffs_fs.c =================================================================== --- linux-2.6.git.orig/fs/yaffs2/yaffs_fs.c +++ linux-2.6.git/fs/yaffs2/yaffs_fs.c @@ -103,7 +103,11 @@ static void yaffs_put_super(struct super static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, loff_t * pos); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) +static int yaffs_file_flush(struct file *file, fl_owner_t id); +#else static int yaffs_file_flush(struct file *file); +#endif static int yaffs_sync_object(struct file *file, struct dentry *dentry, int datasync); @@ -137,10 +141,17 @@ static int yaffs_rename(struct inode *ol struct inode *new_dir, struct dentry *new_dentry); static int yaffs_setattr(struct dentry *dentry, struct iattr *attr); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) +static int yaffs_sync_fs(struct super_block *sb, int wait); +static void yaffs_write_super(struct super_block *sb); +#else static int yaffs_sync_fs(struct super_block *sb); static int yaffs_write_super(struct super_block *sb); +#endif -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) +static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf); +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf); #else static int yaffs_statfs(struct super_block *sb, struct statfs *buf); @@ -438,7 +449,11 @@ static void yaffs_delete_inode(struct in clear_inode(inode); } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) +static int yaffs_file_flush(struct file *file, fl_owner_t id) +#else static int yaffs_file_flush(struct file *file) +#endif { yaffs_Object *obj = yaffs_DentryToObject(file->f_dentry); @@ -1223,14 +1238,21 @@ static int yaffs_setattr(struct dentry * return error; } -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) +static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev; + struct super_block *sb = dentry->d_sb; +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf) +{ + yaffs_Device *dev = yaffs_SuperToDevice(sb); #else static int yaffs_statfs(struct super_block *sb, struct statfs *buf) -#endif { - yaffs_Device *dev = yaffs_SuperToDevice(sb); +#endif + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_statfs\n")); yaffs_GrossLock(dev); @@ -1287,16 +1309,25 @@ static int yaffs_do_sync_fs(struct super } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) +static void yaffs_write_super(struct super_block *sb) +#else static int yaffs_write_super(struct super_block *sb) +#endif { T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_write_super\n")); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)) return 0; /* yaffs_do_sync_fs(sb);*/ - +#endif } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) +static int yaffs_sync_fs(struct super_block *sb, int wait) +#else static int yaffs_sync_fs(struct super_block *sb) +#endif { T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_sync_fs\n")); @@ -1452,7 +1483,11 @@ static struct super_block *yaffs_interna T(YAFFS_TRACE_OS, (" writeoob %p\n", mtd->write_oob)); T(YAFFS_TRACE_OS, (" block_isbad %p\n", mtd->block_isbad)); T(YAFFS_TRACE_OS, (" block_markbad %p\n", mtd->block_markbad)); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) + T(YAFFS_TRACE_OS, (" writesize %d\n", mtd->writesize)); +#else T(YAFFS_TRACE_OS, (" oobblock %d\n", mtd->oobblock)); +#endif T(YAFFS_TRACE_OS, (" oobsize %d\n", mtd->oobsize)); T(YAFFS_TRACE_OS, (" erasesize %d\n", mtd->erasesize)); T(YAFFS_TRACE_OS, (" size %d\n", mtd->size)); @@ -1460,14 +1495,22 @@ static struct super_block *yaffs_interna #ifdef CONFIG_YAFFS_AUTO_YAFFS2 if (yaffsVersion == 1 && +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) + mtd->writesize >= 2048) { +#else mtd->oobblock >= 2048) { +#endif T(YAFFS_TRACE_ALWAYS,("yaffs: auto selecting yaffs2\n")); yaffsVersion = 2; } /* Added NCB 26/5/2006 for completeness */ if (yaffsVersion == 2 && - mtd->oobblock == 512) { +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) + mtd->writesize == 512) { +#else + mtd->oobblock >= 512) { +#endif T(YAFFS_TRACE_ALWAYS,("yaffs: auto selecting yaffs1\n")); yaffsVersion = 1; } @@ -1481,15 +1524,23 @@ static struct super_block *yaffs_interna !mtd->block_markbad || !mtd->read || !mtd->write || +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) + !mtd->read_oob || !mtd->write_oob) { +#else !mtd->write_ecc || !mtd->read_ecc || !mtd->read_oob || !mtd->write_oob) { +#endif T(YAFFS_TRACE_ALWAYS, ("yaffs: MTD device does not support required " "functions\n"));; return NULL; } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) + if (mtd->writesize < YAFFS_MIN_YAFFS2_CHUNK_SIZE || +#else if (mtd->oobblock < YAFFS_MIN_YAFFS2_CHUNK_SIZE || +#endif mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) { T(YAFFS_TRACE_ALWAYS, ("yaffs: MTD device does not have the " @@ -1501,15 +1552,23 @@ static struct super_block *yaffs_interna if (!mtd->erase || !mtd->read || !mtd->write || +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) + !mtd->read_oob || !mtd->write_oob) { +#else !mtd->write_ecc || !mtd->read_ecc || !mtd->read_oob || !mtd->write_oob) { +#endif T(YAFFS_TRACE_ALWAYS, ("yaffs: MTD device does not support required " "functions\n"));; return NULL; } - if (mtd->oobblock != YAFFS_BYTES_PER_CHUNK || +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) + if (mtd->writesize < YAFFS_BYTES_PER_CHUNK || +#else + if (mtd->oobblock < YAFFS_BYTES_PER_CHUNK || +#endif mtd->oobsize != YAFFS_BYTES_PER_SPARE) { T(YAFFS_TRACE_ALWAYS, ("yaffs: MTD device does not support have the " @@ -1560,8 +1619,13 @@ static struct super_block *yaffs_interna dev->queryNANDBlock = nandmtd2_QueryNANDBlock; dev->spareBuffer = YMALLOC(mtd->oobsize); dev->isYaffs2 = 1; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) + dev->nBytesPerChunk = mtd->writesize; + dev->nChunksPerBlock = mtd->erasesize / mtd->writesize; +#else dev->nBytesPerChunk = mtd->oobblock; dev->nChunksPerBlock = mtd->erasesize / mtd->oobblock; +#endif nBlocks = mtd->size / mtd->erasesize; dev->nCheckpointReservedBlocks = 10; @@ -1641,6 +1705,16 @@ static int yaffs_internal_read_super_mtd return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL; } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) +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); +} +#else static struct super_block *yaffs_read_super(struct file_system_type *fs, int flags, const char *dev_name, void *data) @@ -1649,6 +1723,7 @@ static struct super_block *yaffs_read_su return get_sb_bdev(fs, flags, dev_name, data, yaffs_internal_read_super_mtd); } +#endif static struct file_system_type yaffs_fs_type = { .owner = THIS_MODULE, @@ -1678,6 +1753,15 @@ static int yaffs2_internal_read_super_mt return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL; } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) +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); +} +#else static struct super_block *yaffs2_read_super(struct file_system_type *fs, int flags, const char *dev_name, void *data) @@ -1686,6 +1770,7 @@ static struct super_block *yaffs2_read_s return get_sb_bdev(fs, flags, dev_name, data, yaffs2_internal_read_super_mtd); } +#endif static struct file_system_type yaffs2_fs_type = { .owner = THIS_MODULE, Index: linux-2.6.git/fs/yaffs2/yaffs_mtdif.c =================================================================== --- linux-2.6.git.orig/fs/yaffs2/yaffs_mtdif.c +++ linux-2.6.git/fs/yaffs2/yaffs_mtdif.c @@ -26,6 +26,7 @@ const char *yaffs_mtdif_c_version = #include "linux/time.h" #include "linux/mtd/nand.h" +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)) static struct nand_oobinfo yaffs_oobinfo = { .useecc = 1, .eccbytes = 6, @@ -35,16 +36,74 @@ static struct nand_oobinfo yaffs_oobinfo static struct nand_oobinfo yaffs_noeccinfo = { .useecc = 0, }; +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) +static inline void translate_spare2oob(const yaffs_Spare *spare, __u8 *oob) +{ + oob[0] = spare->tagByte0; + oob[1] = spare->tagByte1; + oob[2] = spare->tagByte2; + oob[3] = spare->tagByte3; + oob[4] = spare->tagByte4; + oob[5] = spare->tagByte5 & 0x3f; + oob[5] |= spare->blockStatus == 'Y' ? 0: 0x80; + oob[5] |= spare->pageStatus == 0 ? 0: 0x40; + oob[6] = spare->tagByte6; + oob[7] = spare->tagByte7; +} + +static inline void translate_oob2spare(yaffs_Spare *spare, __u8 *oob) +{ + struct yaffs_NANDSpare *nspare = (struct yaffs_NANDSpare *)spare; + spare->tagByte0 = oob[0]; + spare->tagByte1 = oob[1]; + spare->tagByte2 = oob[2]; + spare->tagByte3 = oob[3]; + spare->tagByte4 = oob[4]; + spare->tagByte5 = oob[5] == 0xff ? 0xff : oob[5] & 0x3f; + spare->blockStatus = oob[5] & 0x80 ? 0xff : 'Y'; + spare->pageStatus = oob[5] & 0x40 ? 0xff : 0; + spare->tagByte6 = oob[6]; + spare->tagByte7 = oob[7]; + + nspare->eccres1 = nspare->eccres2 = 0; /* FIXME */ +} +#endif int nandmtd_WriteChunkToNAND(yaffs_Device * dev, int chunkInNAND, const __u8 * data, const yaffs_Spare * spare) { struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) + struct mtd_oob_ops ops; +#endif size_t dummy; int retval = 0; loff_t addr = ((loff_t) chunkInNAND) * dev->nBytesPerChunk; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) + __u8 spareAsBytes[8]; /* OOB */ + if (data && !spare) + retval = mtd->write(mtd, addr, dev->nBytesPerChunk, + &dummy, data); + else if (spare) { + if (dev->useNANDECC) { + translate_spare2oob(spare, spareAsBytes); + ops.mode = MTD_OOB_AUTO; + ops.ooblen = 8; /* temp hack */ + } else { + ops.mode = MTD_OOB_RAW; + ops.ooblen = YAFFS_BYTES_PER_SPARE; + } + ops.len = data ? dev->nBytesPerChunk : ops.ooblen; + ops.datbuf = (u8 *)data; + ops.ooboffs = 0; + ops.oobbuf = spareAsBytes; + retval = mtd->write_oob(mtd, addr, &ops); + } +#else __u8 *spareAsBytes = (__u8 *) spare; if (data && spare) { @@ -68,6 +127,7 @@ int nandmtd_WriteChunkToNAND(yaffs_Devic mtd->write_oob(mtd, addr, YAFFS_BYTES_PER_SPARE, &dummy, spareAsBytes); } +#endif if (retval == 0) return YAFFS_OK; @@ -79,11 +139,36 @@ int nandmtd_ReadChunkFromNAND(yaffs_Devi yaffs_Spare * spare) { struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) + struct mtd_oob_ops ops; +#endif size_t dummy; int retval = 0; loff_t addr = ((loff_t) chunkInNAND) * dev->nBytesPerChunk; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) + __u8 spareAsBytes[8]; /* OOB */ + if (data && !spare) + retval = mtd->read(mtd, addr, dev->nBytesPerChunk, + &dummy, data); + else if (spare) { + if (dev->useNANDECC) { + ops.mode = MTD_OOB_AUTO; + ops.ooblen = 8; /* temp hack */ + } else { + ops.mode = MTD_OOB_RAW; + ops.ooblen = YAFFS_BYTES_PER_SPARE; + } + ops.len = data ? dev->nBytesPerChunk : ops.ooblen; + ops.datbuf = data; + ops.ooboffs = 0; + ops.oobbuf = spareAsBytes; + retval = mtd->read_oob(mtd, addr, &ops); + if (dev->useNANDECC) + translate_oob2spare(spare, spareAsBytes); + } +#else __u8 *spareAsBytes = (__u8 *) spare; if (data && spare) { @@ -112,6 +197,7 @@ int nandmtd_ReadChunkFromNAND(yaffs_Devi mtd->read_oob(mtd, addr, YAFFS_BYTES_PER_SPARE, &dummy, spareAsBytes); } +#endif if (retval == 0) return YAFFS_OK; Index: linux-2.6.git/fs/yaffs2/yaffs_mtdif2.c =================================================================== --- linux-2.6.git.orig/fs/yaffs2/yaffs_mtdif2.c +++ linux-2.6.git/fs/yaffs2/yaffs_mtdif2.c @@ -34,7 +34,11 @@ int nandmtd2_WriteChunkWithTagsToNAND(ya const yaffs_ExtendedTags * tags) { struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) + struct mtd_oob_ops ops; +#else size_t dummy; +#endif int retval = 0; loff_t addr = ((loff_t) chunkInNAND) * dev->nBytesPerChunk; @@ -46,6 +50,23 @@ int nandmtd2_WriteChunkWithTagsToNAND(ya ("nandmtd2_WriteChunkWithTagsToNAND chunk %d data %p tags %p" TENDSTR), chunkInNAND, data, tags)); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) + if (tags) + yaffs_PackTags2(&pt, tags); + else + BUG(); /* both tags and data should always be present */ + + if (data) { + ops.mode = MTD_OOB_AUTO; + ops.ooblen = sizeof(pt); + ops.len = dev->nBytesPerChunk; + ops.ooboffs = 0; + ops.datbuf = (__u8 *)data; + ops.oobbuf = (void *)&pt; + retval = mtd->write_oob(mtd, addr, &ops); + } else + BUG(); /* both tags and data should always be present */ +#else if (tags) { yaffs_PackTags2(&pt, tags); } @@ -70,6 +91,7 @@ int nandmtd2_WriteChunkWithTagsToNAND(ya (__u8 *) & pt); } +#endif if (retval == 0) return YAFFS_OK; @@ -81,6 +103,9 @@ int nandmtd2_ReadChunkWithTagsFromNAND(y __u8 * data, yaffs_ExtendedTags * tags) { struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) + struct mtd_oob_ops ops; +#endif size_t dummy; int retval = 0; @@ -93,6 +118,20 @@ int nandmtd2_ReadChunkWithTagsFromNAND(y ("nandmtd2_ReadChunkWithTagsFromNAND chunk %d data %p tags %p" TENDSTR), chunkInNAND, data, tags)); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) + if (data && !tags) + retval = mtd->read(mtd, addr, dev->nBytesPerChunk, + &dummy, data); + else if (tags) { + ops.mode = MTD_OOB_AUTO; + ops.ooblen = sizeof(pt); + ops.len = data ? dev->nBytesPerChunk : sizeof(pt); + ops.ooboffs = 0; + ops.datbuf = data; + ops.oobbuf = dev->spareBuffer; + retval = mtd->read_oob(mtd, addr, &ops); + } +#else if (data && tags) { if (dev->useNANDECC) { retval = @@ -115,6 +154,7 @@ int nandmtd2_ReadChunkWithTagsFromNAND(y mtd->read_oob(mtd, addr, mtd->oobsize, &dummy, dev->spareBuffer); } +#endif memcpy(&pt, dev->spareBuffer, sizeof(pt));