yaffs: More clean up
[yaffs2.git] / yaffs_tagscompat.c
1 /*
2  * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
3  *
4  * Copyright (C) 2002-2010 Aleph One Ltd.
5  *   for Toby Churchill Ltd and Brightstar Engineering
6  *
7  * Created by Charles Manning <charles@aleph1.co.uk>
8  *
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.
12  */
13
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"
19
20 static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk);
21
22
23 static const char yaffs_count_bits_table[256] = {
24         0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
25         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
26         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
27         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
28         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
29         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
30         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
31         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
32         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
33         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
34         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
35         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
36         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
37         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
38         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
39         4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
40 };
41
42 int yaffs_count_bits(u8 x)
43 {
44         int ret_val;
45         ret_val = yaffs_count_bits_table[x];
46         return ret_val;
47 }
48
49 /********** Tags ECC calculations  *********/
50
51 void yaffs_calc_ecc(const u8 *data, struct yaffs_spare *spare)
52 {
53         yaffs_ecc_cacl(data, spare->ecc1);
54         yaffs_ecc_cacl(&data[256], spare->ecc2);
55 }
56
57 void yaffs_calc_tags_ecc(struct yaffs_tags *tags)
58 {
59         /* Calculate an ecc */
60
61         unsigned char *b = ((union yaffs_tags_union *) tags)->as_bytes;
62         unsigned i, j;
63         unsigned ecc = 0;
64         unsigned bit = 0;
65
66         tags->ecc = 0;
67
68         for (i = 0; i < 8; i++) {
69                 for (j = 1; j & 0xff; j <<= 1) {
70                         bit++;
71                         if (b[i] & j)
72                                 ecc ^= bit;
73                 }
74         }
75
76         tags->ecc = ecc;
77
78 }
79
80 int yaffs_check_tags_ecc(struct yaffs_tags *tags)
81 {
82         unsigned ecc = tags->ecc;
83
84         yaffs_calc_tags_ecc(tags);
85
86         ecc ^= tags->ecc;
87
88         if (ecc && ecc <= 64) {
89                 /* TODO: Handle the failure better. Retire? */
90                 unsigned char *b = ((union yaffs_tags_union *) tags)->as_bytes;
91
92                 ecc--;
93
94                 b[ecc / 8] ^= (1 << (ecc & 7));
95
96                 /* Now recvalc the ecc */
97                 yaffs_calc_tags_ecc(tags);
98
99                 return 1;       /* recovered error */
100         } else if (ecc) {
101                 /* Wierd ecc failure value */
102                 /* TODO Need to do somethiong here */
103                 return -1;      /* unrecovered error */
104         }
105
106         return 0;
107 }
108
109 /********** Tags **********/
110
111 static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr,
112                                 struct yaffs_tags *tags_ptr)
113 {
114         union yaffs_tags_union *tu = (union yaffs_tags_union *) tags_ptr;
115
116         yaffs_calc_tags_ecc(tags_ptr);
117
118         spare_ptr->tb0 = tu->as_bytes[0];
119         spare_ptr->tb1 = tu->as_bytes[1];
120         spare_ptr->tb2 = tu->as_bytes[2];
121         spare_ptr->tb3 = tu->as_bytes[3];
122         spare_ptr->tb4 = tu->as_bytes[4];
123         spare_ptr->tb5 = tu->as_bytes[5];
124         spare_ptr->tb6 = tu->as_bytes[6];
125         spare_ptr->tb7 = tu->as_bytes[7];
126 }
127
128 static void yaffs_get_tags_from_spare(struct yaffs_dev *dev, struct yaffs_spare *spare_ptr,
129                                 struct yaffs_tags *tags_ptr)
130 {
131         union yaffs_tags_union *tu = (union yaffs_tags_union *) tags_ptr;
132         int result;
133
134         tu->as_bytes[0] = spare_ptr->tb0;
135         tu->as_bytes[1] = spare_ptr->tb1;
136         tu->as_bytes[2] = spare_ptr->tb2;
137         tu->as_bytes[3] = spare_ptr->tb3;
138         tu->as_bytes[4] = spare_ptr->tb4;
139         tu->as_bytes[5] = spare_ptr->tb5;
140         tu->as_bytes[6] = spare_ptr->tb6;
141         tu->as_bytes[7] = spare_ptr->tb7;
142
143         result = yaffs_check_tags_ecc(tags_ptr);
144         if (result > 0)
145                 dev->n_tags_ecc_fixed++;
146         else if (result < 0)
147                 dev->n_tags_ecc_unfixed++;
148 }
149
150 static void yaffs_spare_init(struct yaffs_spare *spare)
151 {
152         memset(spare, 0xFF, sizeof(struct yaffs_spare));
153 }
154
155 static int yaffs_wr_nand(struct yaffs_dev *dev,
156                                 int nand_chunk, const u8 *data,
157                                 struct yaffs_spare *spare)
158 {
159         if (nand_chunk < dev->param.start_block * dev->param.chunks_per_block) {
160                 T(YAFFS_TRACE_ERROR,
161                   (TSTR("**>> yaffs chunk %d is not valid" TENDSTR),
162                    nand_chunk));
163                 return YAFFS_FAIL;
164         }
165
166         return dev->param.write_chunk_fn(dev, nand_chunk, data, spare);
167 }
168
169 static int yaffs_rd_chunk_nand(struct yaffs_dev *dev,
170                                    int nand_chunk,
171                                    u8 *data,
172                                    struct yaffs_spare *spare,
173                                    enum yaffs_ecc_result *ecc_result,
174                                    int correct_errors)
175 {
176         int ret_val;
177         struct yaffs_spare local_spare;
178
179         if (!spare && data) {
180                 /* If we don't have a real spare, then we use a local one. */
181                 /* Need this for the calculation of the ecc */
182                 spare = &local_spare;
183         }
184
185         if (!dev->param.use_nand_ecc) {
186                 ret_val = dev->param.read_chunk_fn(dev, nand_chunk, data, spare);
187                 if (data && correct_errors) {
188                         /* Do ECC correction */
189                         /* Todo handle any errors */
190                         int ecc_result1, ecc_result2;
191                         u8 calc_ecc[3];
192
193                         yaffs_ecc_cacl(data, calc_ecc);
194                         ecc_result1 =
195                             yaffs_ecc_correct(data, spare->ecc1, calc_ecc);
196                         yaffs_ecc_cacl(&data[256], calc_ecc);
197                         ecc_result2 =
198                             yaffs_ecc_correct(&data[256], spare->ecc2, calc_ecc);
199
200                         if (ecc_result1 > 0) {
201                                 T(YAFFS_TRACE_ERROR,
202                                   (TSTR
203                                    ("**>>yaffs ecc error fix performed on chunk %d:0"
204                                     TENDSTR), nand_chunk));
205                                 dev->n_ecc_fixed++;
206                         } else if (ecc_result1 < 0) {
207                                 T(YAFFS_TRACE_ERROR,
208                                   (TSTR
209                                    ("**>>yaffs ecc error unfixed on chunk %d:0"
210                                     TENDSTR), nand_chunk));
211                                 dev->n_ecc_unfixed++;
212                         }
213
214                         if (ecc_result2 > 0) {
215                                 T(YAFFS_TRACE_ERROR,
216                                   (TSTR
217                                    ("**>>yaffs ecc error fix performed on chunk %d:1"
218                                     TENDSTR), nand_chunk));
219                                 dev->n_ecc_fixed++;
220                         } else if (ecc_result2 < 0) {
221                                 T(YAFFS_TRACE_ERROR,
222                                   (TSTR
223                                    ("**>>yaffs ecc error unfixed on chunk %d:1"
224                                     TENDSTR), nand_chunk));
225                                 dev->n_ecc_unfixed++;
226                         }
227
228                         if (ecc_result1 || ecc_result2) {
229                                 /* We had a data problem on this page */
230                                 yaffs_handle_rd_data_error(dev, nand_chunk);
231                         }
232
233                         if (ecc_result1 < 0 || ecc_result2 < 0)
234                                 *ecc_result = YAFFS_ECC_RESULT_UNFIXED;
235                         else if (ecc_result1 > 0 || ecc_result2 > 0)
236                                 *ecc_result = YAFFS_ECC_RESULT_FIXED;
237                         else
238                                 *ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
239                 }
240         } else {
241                 /* Must allocate enough memory for spare+2*sizeof(int) */
242                 /* for ecc results from device. */
243                 struct yaffs_nand_spare nspare;
244
245                 memset(&nspare, 0, sizeof(nspare));
246
247                 ret_val = dev->param.read_chunk_fn(dev, nand_chunk, data,
248                                         (struct yaffs_spare *) &nspare);
249                 memcpy(spare, &nspare, sizeof(struct yaffs_spare));
250                 if (data && correct_errors) {
251                         if (nspare.eccres1 > 0) {
252                                 T(YAFFS_TRACE_ERROR,
253                                   (TSTR
254                                    ("**>>mtd ecc error fix performed on chunk %d:0"
255                                     TENDSTR), nand_chunk));
256                         } else if (nspare.eccres1 < 0) {
257                                 T(YAFFS_TRACE_ERROR,
258                                   (TSTR
259                                    ("**>>mtd ecc error unfixed on chunk %d:0"
260                                     TENDSTR), nand_chunk));
261                         }
262
263                         if (nspare.eccres2 > 0) {
264                                 T(YAFFS_TRACE_ERROR,
265                                   (TSTR
266                                    ("**>>mtd ecc error fix performed on chunk %d:1"
267                                     TENDSTR), nand_chunk));
268                         } else if (nspare.eccres2 < 0) {
269                                 T(YAFFS_TRACE_ERROR,
270                                   (TSTR
271                                    ("**>>mtd ecc error unfixed on chunk %d:1"
272                                     TENDSTR), nand_chunk));
273                         }
274
275                         if (nspare.eccres1 || nspare.eccres2) {
276                                 /* We had a data problem on this page */
277                                 yaffs_handle_rd_data_error(dev, nand_chunk);
278                         }
279
280                         if (nspare.eccres1 < 0 || nspare.eccres2 < 0)
281                                 *ecc_result = YAFFS_ECC_RESULT_UNFIXED;
282                         else if (nspare.eccres1 > 0 || nspare.eccres2 > 0)
283                                 *ecc_result = YAFFS_ECC_RESULT_FIXED;
284                         else
285                                 *ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
286
287                 }
288         }
289         return ret_val;
290 }
291
292
293 /*
294  * Functions for robustisizing
295  */
296
297 static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk)
298 {
299         int flash_block = nand_chunk / dev->param.chunks_per_block;
300
301         /* Mark the block for retirement */
302         yaffs_get_block_info(dev, flash_block + dev->block_offset)->needs_retiring = 1;
303         T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
304           (TSTR("**>>Block %d marked for retirement" TENDSTR), flash_block));
305
306         /* TODO:
307          * Just do a garbage collection on the affected block
308          * then retire the block
309          * NB recursion
310          */
311 }
312
313
314 int yaffs_tags_compat_wr(struct yaffs_dev *dev,
315                                                 int nand_chunk,
316                                                 const u8 *data,
317                                                 const struct yaffs_ext_tags *ext_tags)
318 {
319         struct yaffs_spare spare;
320         struct yaffs_tags tags;
321
322         yaffs_spare_init(&spare);
323
324         if (ext_tags->is_deleted)
325                 spare.page_status = 0;
326         else {
327                 tags.obj_id = ext_tags->obj_id;
328                 tags.chunk_id = ext_tags->chunk_id;
329
330                 tags.n_bytes_lsb = ext_tags->n_bytes & 0x3ff;
331
332                 if (dev->data_bytes_per_chunk >= 1024)
333                         tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3;
334                 else
335                         tags.n_bytes_msb = 3;
336
337
338                 tags.serial_number = ext_tags->serial_number;
339
340                 if (!dev->param.use_nand_ecc && data)
341                         yaffs_calc_ecc(data, &spare);
342
343                 yaffs_load_tags_to_spare(&spare, &tags);
344
345         }
346
347         return yaffs_wr_nand(dev, nand_chunk, data, &spare);
348 }
349
350 int yaffs_tags_compat_rd(struct yaffs_dev *dev,
351                                                      int nand_chunk,
352                                                      u8 *data,
353                                                      struct yaffs_ext_tags *ext_tags)
354 {
355
356         struct yaffs_spare spare;
357         struct yaffs_tags tags;
358         enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN;
359
360         static struct yaffs_spare spare_ff;
361         static int init;
362
363         if (!init) {
364                 memset(&spare_ff, 0xFF, sizeof(spare_ff));
365                 init = 1;
366         }
367
368         if (yaffs_rd_chunk_nand
369             (dev, nand_chunk, data, &spare, &ecc_result, 1)) {
370                 /* ext_tags may be NULL */
371                 if (ext_tags) {
372
373                         int deleted =
374                             (yaffs_count_bits(spare.page_status) < 7) ? 1 : 0;
375
376                         ext_tags->is_deleted = deleted;
377                         ext_tags->ecc_result = ecc_result;
378                         ext_tags->block_bad = 0;        /* We're reading it */
379                         /* therefore it is not a bad block */
380                         ext_tags->chunk_used =
381                             (memcmp(&spare_ff, &spare, sizeof(spare_ff)) !=
382                              0) ? 1 : 0;
383
384                         if (ext_tags->chunk_used) {
385                                 yaffs_get_tags_from_spare(dev, &spare, &tags);
386
387                                 ext_tags->obj_id = tags.obj_id;
388                                 ext_tags->chunk_id = tags.chunk_id;
389                                 ext_tags->n_bytes = tags.n_bytes_lsb;
390
391                                 if (dev->data_bytes_per_chunk >= 1024)
392                                         ext_tags->n_bytes |= (((unsigned) tags.n_bytes_msb) << 10);
393
394                                 ext_tags->serial_number = tags.serial_number;
395                         }
396                 }
397
398                 return YAFFS_OK;
399         } else {
400                 return YAFFS_FAIL;
401         }
402 }
403
404 int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev,
405                                             int flash_block)
406 {
407
408         struct yaffs_spare spare;
409
410         memset(&spare, 0xff, sizeof(struct yaffs_spare));
411
412         spare.block_status = 'Y';
413
414         yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL,
415                                &spare);
416         yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1,
417                                NULL, &spare);
418
419         return YAFFS_OK;
420
421 }
422
423 int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
424                                           int block_no,
425                                           enum yaffs_block_state *state,
426                                           u32 *seq_number)
427 {
428
429         struct yaffs_spare spare0, spare1;
430         static struct yaffs_spare spare_ff;
431         static int init;
432         enum yaffs_ecc_result dummy;
433
434         if (!init) {
435                 memset(&spare_ff, 0xFF, sizeof(spare_ff));
436                 init = 1;
437         }
438
439         *seq_number = 0;
440
441         yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block, NULL,
442                                 &spare0, &dummy, 1);
443         yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1, NULL,
444                                 &spare1, &dummy, 1);
445
446         if (yaffs_count_bits(spare0.block_status & spare1.block_status) < 7)
447                 *state = YAFFS_BLOCK_STATE_DEAD;
448         else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0)
449                 *state = YAFFS_BLOCK_STATE_EMPTY;
450         else
451                 *state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
452
453         return YAFFS_OK;
454 }