1/* 2 * YAFFS: Yet another FFS. A NAND-flash specific file system. 3 * yaffs_mtdif.c NAND mtd wrapper functions. 4 * 5 * Copyright (C) 2002 Aleph One Ltd. 6 * for Toby Churchill Ltd and Brightstar Engineering 7 * 8 * Created by Charles Manning <charles@aleph1.co.uk> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 * 14 */ 15 16/* mtd interface for YAFFS2 */ 17 18const char *yaffs_mtdif2_c_version = 19 "$Id: yaffs_mtdif2.c,v 1.14 2006/10/03 10:13:03 charles Exp $"; 20 21#include "yportenv.h" 22 23 24#include "yaffs_mtdif2.h" 25 26#include "linux/mtd/mtd.h" 27#include "linux/types.h" 28#include "linux/time.h" 29 30#include "yaffs_packedtags2.h" 31 32 33void nandmtd2_pt2buf(yaffs_Device *dev, yaffs_PackedTags2 *pt, int is_raw) 34{ 35 struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); 36 __u8 *ptab = (__u8 *)pt; /* packed tags as bytes */ 37 38 int i, j = 0, k, n; 39#ifdef CONFIG_YAFFS_DOES_ECC 40 size_t packed_size = sizeof(yaffs_PackedTags2); 41#else 42 size_t packed_size = sizeof(yaffs_PackedTags2TagsPart); 43#endif 44 45 /* Pack buffer with 0xff */ 46 for (i = 0; i < mtd->oobsize; i++) 47 dev->spareBuffer[i] = 0xff; 48 49 if(!is_raw){ 50 memcpy(dev->spareBuffer,pt,packed_size); 51 } else { 52 j = 0; 53 k = mtd->ecclayout->oobfree[j].offset; 54 n = mtd->ecclayout->oobfree[j].length; 55 56 //printk("nandmtd2_pt2buf: writing %d bytes of extra data into %d\n", packed_size, mtd->oobsize); 57 58 if (n == 0) { 59 T(YAFFS_TRACE_ERROR, (TSTR("No OOB space for tags" TENDSTR))); 60 YBUG(); 61 } 62 63 for (i = 0; i < packed_size; i++) { 64 if (n == 0) { 65 j++; 66 k = mtd->ecclayout->oobfree[j].offset; 67 n = mtd->ecclayout->oobfree[j].length; 68 if (n == 0 || j >= (sizeof(mtd->ecclayout->oobfree) / sizeof(mtd->ecclayout->oobfree[0]))) { 69 T(YAFFS_TRACE_ERROR, (TSTR("No OOB space for tags" TENDSTR))); 70 YBUG(); 71 } 72 } 73 dev->spareBuffer[k] = ptab[i]; 74 k++; 75 n--; 76 } 77 } 78 79} 80 81void nandmtd2_buf2pt(yaffs_Device *dev, yaffs_PackedTags2 *pt, int is_raw) 82{ 83 struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); 84 int i, j = 0, k, n; 85 __u8 *ptab = (__u8 *)pt; /* packed tags as bytes */ 86 size_t packed_size = dev->useNANDECC ? sizeof(yaffs_PackedTags2TagsPart) : sizeof(yaffs_PackedTags2); 87 88 if (!is_raw) { 89 90 memcpy(pt,dev->spareBuffer,packed_size); 91 } else { 92 j = 0; 93 k = mtd->ecclayout->oobfree[j].offset; 94 n = mtd->ecclayout->oobfree[j].length; 95 96 if (n == 0) { 97 T(YAFFS_TRACE_ERROR, (TSTR("No space in OOB for tags" TENDSTR))); 98 YBUG(); 99 } 100 101 for (i = 0; i < packed_size; i++) { 102 if (n == 0) { 103 j++; 104 k = mtd->ecclayout->oobfree[j].offset; 105 n = mtd->ecclayout->oobfree[j].length; 106 if (n == 0 || j >= (sizeof(mtd->ecclayout->oobfree) / sizeof(mtd->ecclayout->oobfree[0]))) { 107 T(YAFFS_TRACE_ERROR, (TSTR("No space in OOB for tags" TENDSTR))); 108 YBUG(); 109 } 110 } 111 ptab[i] = dev->spareBuffer[k]; 112 k++; 113 n--; 114 } 115 } 116 117} 118 119int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device * dev, int chunkInNAND, 120 const __u8 * data, 121 const yaffs_ExtendedTags * tags) 122{ 123 struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); 124#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) 125 struct mtd_oob_ops ops; 126#else 127 size_t dummy; 128#endif 129 int retval = 0; 130 131 loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk; 132 133 yaffs_PackedTags2 pt; 134 135 T(YAFFS_TRACE_MTD, 136 (TSTR 137 ("nandmtd2_WriteChunkWithTagsToNAND chunk %d data %p tags %p" 138 TENDSTR), chunkInNAND, data, tags)); 139 140#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) 141 if (tags) 142 yaffs_PackTags2(&pt, tags); 143 else 144 BUG(); /* both tags and data should always be present */ 145 146 nandmtd2_pt2buf(dev, &pt, 1); 147 if (data) { 148 ops.mode = MTD_OOB_AUTO; 149 ops.ooblen = mtd->oobsize; 150 ops.len = dev->nDataBytesPerChunk; 151 ops.ooboffs = 0; 152 ops.datbuf = (__u8 *)data; 153 ops.oobbuf = dev->spareBuffer; 154 retval = mtd->write_oob(mtd, addr, &ops); 155 } else 156 BUG(); /* both tags and data should always be present */ 157#else 158 if (tags) { 159 yaffs_PackTags2(&pt, tags); 160 } 161 162 if (tags) { 163 nandmtd2_pt2buf(dev, &pt, 1); 164 retval = 165 mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk, 166 &dummy, data, dev->spareBuffer, NULL); 167 } else if (data) { 168 retval = 169 mtd->write(mtd, addr, dev->nDataBytesPerChunk, &dummy, 170 data); 171 } 172#endif 173 174 if (retval == 0) 175 return YAFFS_OK; 176 else 177 return YAFFS_FAIL; 178} 179 180int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND, 181 __u8 * data, yaffs_ExtendedTags * tags) 182{ 183 struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); 184#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) 185 struct mtd_oob_ops ops; 186#endif 187 size_t dummy; 188 int retval = 0; 189 190 loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk; 191 192 yaffs_PackedTags2 pt; 193 194 T(YAFFS_TRACE_MTD, 195 (TSTR 196 ("nandmtd2_ReadChunkWithTagsFromNAND chunk %d data %p tags %p" 197 TENDSTR), chunkInNAND, data, tags)); 198 199#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) 200 if (data && !tags) 201 retval = mtd->read(mtd, addr, dev->nDataBytesPerChunk, 202 &dummy, data); 203 else if (tags) { 204 ops.mode = MTD_OOB_AUTO; 205 ops.ooblen = mtd->oobsize; 206 ops.len = data ? dev->nDataBytesPerChunk : mtd->oobsize; 207 ops.ooboffs = 0; 208 ops.datbuf = data; 209 ops.oobbuf = dev->spareBuffer; 210 retval = mtd->read_oob(mtd, addr, &ops); 211 nandmtd2_buf2pt(dev, &pt, 1); 212 } 213#else 214 if (data && tags) { 215 retval = 216 mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk, 217 &dummy, data, dev->spareBuffer, 218 NULL); 219 } else { 220 retval = 221 mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk, 222 &dummy, data, dev->spareBuffer, 223 NULL); 224 } 225 } else { 226 if (data) 227 retval = 228 mtd->read(mtd, addr, dev->nDataBytesPerChunk, &dummy, 229 data); 230 if (tags) { 231 retval = 232 mtd->read_oob(mtd, addr, mtd->oobsize, &dummy, 233 dev->spareBuffer); 234 nandmtd2_buf2pt(dev, &pt, 1); 235 } 236 } 237#endif 238 239 if (tags) 240 yaffs_UnpackTags2(tags, &pt); 241 242 if(tags && retval == -EBADMSG && tags->eccResult == YAFFS_ECC_RESULT_NO_ERROR) 243 tags->eccResult = YAFFS_ECC_RESULT_UNFIXED; 244 245 if (retval == 0) 246 return YAFFS_OK; 247 else 248 return YAFFS_FAIL; 249} 250 251int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo) 252{ 253 struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); 254 int retval; 255 T(YAFFS_TRACE_MTD, 256 (TSTR("nandmtd2_MarkNANDBlockBad %d" TENDSTR), blockNo)); 257 258 retval = 259 mtd->block_markbad(mtd, 260 blockNo * dev->nChunksPerBlock * 261 dev->nDataBytesPerChunk); 262 263 if (retval == 0) 264 return YAFFS_OK; 265 else 266 return YAFFS_FAIL; 267 268} 269 270int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, 271 yaffs_BlockState * state, int *sequenceNumber) 272{ 273 struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); 274 int retval; 275 276 T(YAFFS_TRACE_MTD, 277 (TSTR("nandmtd2_QueryNANDBlock %d" TENDSTR), blockNo)); 278 retval = 279 mtd->block_isbad(mtd, 280 blockNo * dev->nChunksPerBlock * 281 dev->nDataBytesPerChunk); 282 283 if (retval) { 284 T(YAFFS_TRACE_MTD, (TSTR("block is bad" TENDSTR))); 285 286 *state = YAFFS_BLOCK_STATE_DEAD; 287 *sequenceNumber = 0; 288 } else { 289 yaffs_ExtendedTags t; 290 nandmtd2_ReadChunkWithTagsFromNAND(dev, 291 blockNo * 292 dev->nChunksPerBlock, NULL, 293 &t); 294 295 if (t.chunkUsed) { 296 *sequenceNumber = t.sequenceNumber; 297 *state = YAFFS_BLOCK_STATE_NEEDS_SCANNING; 298 } else { 299 *sequenceNumber = 0; 300 *state = YAFFS_BLOCK_STATE_EMPTY; 301 } 302 303 T(YAFFS_TRACE_MTD, 304 (TSTR("block is OK seq %d state %d" TENDSTR), *sequenceNumber, 305 *state)); 306 } 307 308 if (retval == 0) 309 return YAFFS_OK; 310 else 311 return YAFFS_FAIL; 312} 313 314