2 * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
4 * Copyright (C) 2002-2011 Aleph One Ltd.
5 * for Toby Churchill Ltd and Brightstar Engineering
7 * Created by Charles Manning <charles@aleph1.co.uk>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
14 #include "yaffs_guts.h"
15 #include "yaffs_tagscompat.h"
16 #include "yaffs_ecc.h"
17 #include "yaffs_getblockinfo.h"
18 #include "yaffs_trace.h"
20 static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk);
23 /********** Tags ECC calculations *********/
25 static void yaffs_calc_ecc(const u8 *data, struct yaffs_spare *spare)
27 yaffs_ecc_calc(data, spare->ecc1);
28 yaffs_ecc_calc(&data[256], spare->ecc2);
31 void yaffs_calc_tags_ecc(struct yaffs_tags *tags)
33 /* Calculate an ecc */
34 unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes;
41 for (i = 0; i < 8; i++) {
42 for (j = 1; j & 0xff; j <<= 1) {
51 int yaffs_check_tags_ecc(struct yaffs_tags *tags)
53 unsigned ecc = tags->ecc;
55 yaffs_calc_tags_ecc(tags);
59 if (ecc && ecc <= 64) {
60 /* TODO: Handle the failure better. Retire? */
61 unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes;
65 b[ecc / 8] ^= (1 << (ecc & 7));
67 /* Now recvalc the ecc */
68 yaffs_calc_tags_ecc(tags);
70 return 1; /* recovered error */
72 /* Wierd ecc failure value */
73 /* TODO Need to do somethiong here */
74 return -1; /* unrecovered error */
79 /********** Tags **********/
81 static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr,
82 struct yaffs_tags *tags_ptr)
84 union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr;
86 yaffs_calc_tags_ecc(tags_ptr);
88 spare_ptr->tb0 = tu->as_bytes[0];
89 spare_ptr->tb1 = tu->as_bytes[1];
90 spare_ptr->tb2 = tu->as_bytes[2];
91 spare_ptr->tb3 = tu->as_bytes[3];
92 spare_ptr->tb4 = tu->as_bytes[4];
93 spare_ptr->tb5 = tu->as_bytes[5];
94 spare_ptr->tb6 = tu->as_bytes[6];
95 spare_ptr->tb7 = tu->as_bytes[7];
98 static void yaffs_get_tags_from_spare(struct yaffs_dev *dev,
99 struct yaffs_spare *spare_ptr,
100 struct yaffs_tags *tags_ptr)
102 union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr;
105 tu->as_bytes[0] = spare_ptr->tb0;
106 tu->as_bytes[1] = spare_ptr->tb1;
107 tu->as_bytes[2] = spare_ptr->tb2;
108 tu->as_bytes[3] = spare_ptr->tb3;
109 tu->as_bytes[4] = spare_ptr->tb4;
110 tu->as_bytes[5] = spare_ptr->tb5;
111 tu->as_bytes[6] = spare_ptr->tb6;
112 tu->as_bytes[7] = spare_ptr->tb7;
114 result = yaffs_check_tags_ecc(tags_ptr);
116 dev->n_tags_ecc_fixed++;
118 dev->n_tags_ecc_unfixed++;
121 static void yaffs_spare_init(struct yaffs_spare *spare)
123 memset(spare, 0xff, sizeof(struct yaffs_spare));
126 static int yaffs_wr_nand(struct yaffs_dev *dev,
127 int nand_chunk, const u8 *data,
128 struct yaffs_spare *spare)
132 u8 spare_buffer[100];
134 if (nand_chunk < dev->param.start_block * dev->param.chunks_per_block) {
135 yaffs_trace(YAFFS_TRACE_ERROR,
136 "**>> yaffs chunk %d is not valid",
141 /* Format up the spare */
143 return dev->param.drv_write_chunk_fn(dev, nand_chunk,
145 spare_buffer, spare_size);
148 static int yaffs_rd_chunk_nand(struct yaffs_dev *dev,
151 struct yaffs_spare *spare,
152 enum yaffs_ecc_result *ecc_result,
156 struct yaffs_spare local_spare;
161 /* If we don't have a real spare, then we use a local one. */
162 /* Need this for the calculation of the ecc */
163 spare = &local_spare;
166 if (!dev->param.use_nand_ecc) {
168 dev->param.drv_read_chunk_fn(dev, nand_chunk,
170 (u8 *)spare, spare_size,
172 if (data && correct_errors) {
173 /* Do ECC correction */
174 /* Todo handle any errors */
175 int ecc_result1, ecc_result2;
178 yaffs_ecc_calc(data, calc_ecc);
180 yaffs_ecc_correct(data, spare->ecc1, calc_ecc);
181 yaffs_ecc_calc(&data[256], calc_ecc);
183 yaffs_ecc_correct(&data[256], spare->ecc2,
186 if (ecc_result1 > 0) {
187 yaffs_trace(YAFFS_TRACE_ERROR,
188 "**>>yaffs ecc error fix performed on chunk %d:0",
191 } else if (ecc_result1 < 0) {
192 yaffs_trace(YAFFS_TRACE_ERROR,
193 "**>>yaffs ecc error unfixed on chunk %d:0",
195 dev->n_ecc_unfixed++;
198 if (ecc_result2 > 0) {
199 yaffs_trace(YAFFS_TRACE_ERROR,
200 "**>>yaffs ecc error fix performed on chunk %d:1",
203 } else if (ecc_result2 < 0) {
204 yaffs_trace(YAFFS_TRACE_ERROR,
205 "**>>yaffs ecc error unfixed on chunk %d:1",
207 dev->n_ecc_unfixed++;
210 if (ecc_result1 || ecc_result2) {
211 /* We had a data problem on this page */
212 yaffs_handle_rd_data_error(dev, nand_chunk);
215 if (ecc_result1 < 0 || ecc_result2 < 0)
216 *ecc_result = YAFFS_ECC_RESULT_UNFIXED;
217 else if (ecc_result1 > 0 || ecc_result2 > 0)
218 *ecc_result = YAFFS_ECC_RESULT_FIXED;
220 *ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
223 /* Must allocate enough memory for spare+2*sizeof(int) */
224 /* for ecc results from device. */
225 struct yaffs_nand_spare nspare;
227 memset(&nspare, 0, sizeof(nspare));
229 ret_val = dev->param.drv_read_chunk_fn(dev, nand_chunk,
231 (u8 *) &nspare, sizeof(nspare),
233 memcpy(spare, &nspare, sizeof(struct yaffs_spare));
234 if (data && correct_errors) {
235 if (nspare.eccres1 > 0) {
236 yaffs_trace(YAFFS_TRACE_ERROR,
237 "**>>mtd ecc error fix performed on chunk %d:0",
239 } else if (nspare.eccres1 < 0) {
240 yaffs_trace(YAFFS_TRACE_ERROR,
241 "**>>mtd ecc error unfixed on chunk %d:0",
245 if (nspare.eccres2 > 0) {
246 yaffs_trace(YAFFS_TRACE_ERROR,
247 "**>>mtd ecc error fix performed on chunk %d:1",
249 } else if (nspare.eccres2 < 0) {
250 yaffs_trace(YAFFS_TRACE_ERROR,
251 "**>>mtd ecc error unfixed on chunk %d:1",
255 if (nspare.eccres1 || nspare.eccres2) {
256 /* We had a data problem on this page */
257 yaffs_handle_rd_data_error(dev, nand_chunk);
260 if (nspare.eccres1 < 0 || nspare.eccres2 < 0)
261 *ecc_result = YAFFS_ECC_RESULT_UNFIXED;
262 else if (nspare.eccres1 > 0 || nspare.eccres2 > 0)
263 *ecc_result = YAFFS_ECC_RESULT_FIXED;
265 *ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
273 * Functions for robustisizing
276 static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk)
278 int flash_block = nand_chunk / dev->param.chunks_per_block;
280 /* Mark the block for retirement */
281 yaffs_get_block_info(dev, flash_block + dev->block_offset)->
283 yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
284 "**>>Block %d marked for retirement",
288 * Just do a garbage collection on the affected block
289 * then retire the block
294 static int yaffs_tags_compat_wr(struct yaffs_dev *dev,
296 const u8 *data, const struct yaffs_ext_tags *ext_tags)
298 struct yaffs_spare spare;
299 struct yaffs_tags tags;
301 yaffs_spare_init(&spare);
303 if (ext_tags->is_deleted)
304 spare.page_status = 0;
306 tags.obj_id = ext_tags->obj_id;
307 tags.chunk_id = ext_tags->chunk_id;
309 tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1);
311 if (dev->data_bytes_per_chunk >= 1024)
312 tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3;
314 tags.n_bytes_msb = 3;
316 tags.serial_number = ext_tags->serial_number;
318 if (!dev->param.use_nand_ecc && data)
319 yaffs_calc_ecc(data, &spare);
321 yaffs_load_tags_to_spare(&spare, &tags);
323 return yaffs_wr_nand(dev, nand_chunk, data, &spare);
326 static int yaffs_tags_compat_rd(struct yaffs_dev *dev,
328 u8 *data, struct yaffs_ext_tags *ext_tags)
330 struct yaffs_spare spare;
331 struct yaffs_tags tags;
332 enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN;
333 static struct yaffs_spare spare_ff;
338 memset(&spare_ff, 0xff, sizeof(spare_ff));
342 if (!yaffs_rd_chunk_nand(dev, nand_chunk,
343 data, &spare, &ecc_result, 1))
346 /* ext_tags may be NULL */
350 deleted = (hweight8(spare.page_status) < 7) ? 1 : 0;
352 ext_tags->is_deleted = deleted;
353 ext_tags->ecc_result = ecc_result;
354 ext_tags->block_bad = 0; /* We're reading it */
355 /* therefore it is not a bad block */
356 ext_tags->chunk_used =
357 memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0;
359 if (ext_tags->chunk_used) {
360 yaffs_get_tags_from_spare(dev, &spare, &tags);
361 ext_tags->obj_id = tags.obj_id;
362 ext_tags->chunk_id = tags.chunk_id;
363 ext_tags->n_bytes = tags.n_bytes_lsb;
365 if (dev->data_bytes_per_chunk >= 1024)
367 (((unsigned)tags.n_bytes_msb) << 10);
369 ext_tags->serial_number = tags.serial_number;
375 static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block)
377 struct yaffs_spare spare;
379 memset(&spare, 0xff, sizeof(struct yaffs_spare));
381 spare.block_status = 'Y';
383 yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL,
385 yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1,
391 static int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
393 enum yaffs_block_state *state,
396 struct yaffs_spare spare0, spare1;
397 static struct yaffs_spare spare_ff;
399 enum yaffs_ecc_result dummy;
402 memset(&spare_ff, 0xff, sizeof(spare_ff));
408 yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block, NULL,
410 yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1,
411 NULL, &spare1, &dummy, 1);
413 if (hweight8(spare0.block_status & spare1.block_status) < 7)
414 *state = YAFFS_BLOCK_STATE_DEAD;
415 else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0)
416 *state = YAFFS_BLOCK_STATE_EMPTY;
418 *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
423 void yaffs_tags_compat_install(struct yaffs_dev *dev)
425 if(dev->param.is_yaffs2)
427 if(!dev->param.write_chunk_tags_fn)
428 dev->param.write_chunk_tags_fn = yaffs_tags_compat_wr;
429 if(!dev->param.read_chunk_tags_fn)
430 dev->param.read_chunk_tags_fn = yaffs_tags_compat_rd;
431 if(!dev->param.read_chunk_tags_fn)
432 dev->param.query_block_fn = yaffs_tags_compat_query_block;
433 if(!dev->param.mark_bad_fn)
434 dev->param.mark_bad_fn = yaffs_tags_compat_mark_bad;