1 Subject: yaffs: fix compat tags handling
3 Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
5 --- a/fs/yaffs2/yaffs_tagscompat.c
6 +++ b/fs/yaffs2/yaffs_tagscompat.c
8 #include "yaffs_getblockinfo.h"
9 #include "yaffs_trace.h"
12 static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk);
16 /********** Tags ECC calculations *********/
17 @@ -71,6 +73,7 @@ int yaffs_check_tags_ecc(struct yaffs_ta
22 /********** Tags **********/
24 static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr,
25 @@ -379,3 +382,214 @@ void yaffs_tags_compat_install(struct ya
26 if(!dev->tagger.mark_bad_fn)
27 dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;
31 +#include "yaffs_packedtags1.h"
33 +static int yaffs_tags_compat_write(struct yaffs_dev *dev,
36 + const struct yaffs_ext_tags *tags)
38 + struct yaffs_packed_tags1 pt1;
42 + /* we assume that yaffs_packed_tags1 and yaffs_tags are compatible */
43 + compile_time_assertion(sizeof(struct yaffs_packed_tags1) == 12);
44 + compile_time_assertion(sizeof(struct yaffs_tags) == 8);
46 + yaffs_pack_tags1(&pt1, tags);
47 + yaffs_calc_tags_ecc((struct yaffs_tags *)&pt1);
49 + /* When deleting a chunk, the upper layer provides only skeletal
50 + * tags, one with is_deleted set. However, we need to update the
51 + * tags, not erase them completely. So we use the NAND write property
52 + * that only zeroed-bits stick and set tag bytes to all-ones and
53 + * zero just the (not) deleted bit.
55 + if (!dev->param.tags_9bytes) {
56 + if (tags->is_deleted) {
57 + memset(&pt1, 0xff, 8);
58 + /* clear delete status bit to indicate deleted */
61 + memcpy(tag_buf, &pt1, 8);
63 + if (tags->is_deleted) {
64 + memset(tag_buf, 0xff, 8);
67 + memcpy(tag_buf, &pt1, 8);
72 + retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk,
74 + (data) ? dev->data_bytes_per_chunk : 0,
76 + (dev->param.tags_9bytes) ? 9 : 8);
81 +/* Return with empty extended tags but add ecc_result.
83 +static int return_empty_tags(struct yaffs_ext_tags *tags,
84 + enum yaffs_ecc_result ecc_result,
88 + memset(tags, 0, sizeof(*tags));
89 + tags->ecc_result = ecc_result;
95 +static int yaffs_tags_compat_read(struct yaffs_dev *dev,
98 + struct yaffs_ext_tags *tags)
100 + struct yaffs_packed_tags1 pt1;
101 + enum yaffs_ecc_result ecc_result;
106 + retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk,
107 + data, dev->param.total_bytes_per_chunk,
109 + (dev->param.tags_9bytes) ? 9 : 8,
112 + switch (ecc_result) {
113 + case YAFFS_ECC_RESULT_NO_ERROR:
114 + case YAFFS_ECC_RESULT_FIXED:
117 + case YAFFS_ECC_RESULT_UNFIXED:
119 + return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED, 0);
120 + tags->block_bad = dev->drv.drv_check_bad_fn(dev, nand_chunk);
124 + /* Check for a blank/erased chunk. */
125 + if (yaffs_check_ff(tag_buf, 8)) {
126 + /* when blank, upper layers want ecc_result to be <= NO_ERROR */
127 + return return_empty_tags(tags, YAFFS_ECC_RESULT_NO_ERROR,
131 + memcpy(&pt1, tag_buf, 8);
133 + if (!dev->param.tags_9bytes) {
134 + /* Read deleted status (bit) then return it to it's non-deleted
135 + * state before performing tags mini-ECC check. pt1.deleted is
138 + deleted = !pt1.deleted;
141 + deleted = (hweight8(tag_buf[8]) < 7) ? 1 : 0;
144 + /* Check the packed tags mini-ECC and correct if necessary/possible. */
145 + retval = yaffs_check_tags_ecc((struct yaffs_tags *)&pt1);
148 + /* no tags error, use MTD result */
151 + /* recovered tags-ECC error */
152 + dev->n_tags_ecc_fixed++;
153 + if (ecc_result == YAFFS_ECC_RESULT_NO_ERROR)
154 + ecc_result = YAFFS_ECC_RESULT_FIXED;
157 + /* unrecovered tags-ECC error */
158 + dev->n_tags_ecc_unfixed++;
159 + return return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED,
163 + /* Unpack the tags to extended form and set ECC result.
164 + * [set should_be_ff just to keep yaffs_unpack_tags1 happy]
166 + pt1.should_be_ff = 0xffffffff;
167 + yaffs_unpack_tags1(tags, &pt1);
168 + tags->ecc_result = ecc_result;
170 + /* Set deleted state */
171 + tags->is_deleted = deleted;
175 +static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no)
177 + return dev->drv.drv_mark_bad_fn(dev, block_no);
180 +static int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
182 + enum yaffs_block_state *state,
185 + struct yaffs_ext_tags tags;
188 + yaffs_trace(YAFFS_TRACE_MTD, "%s %d", __func__, block_no);
192 + retval = dev->drv.drv_check_bad_fn(dev, block_no);
193 + if (retval == YAFFS_FAIL) {
194 + *state = YAFFS_BLOCK_STATE_DEAD;
198 + yaffs_tags_compat_read(dev, block_no * dev->param.chunks_per_block,
201 + if (tags.ecc_result != YAFFS_ECC_RESULT_NO_ERROR) {
202 + yaffs_trace(YAFFS_TRACE_MTD, "block %d is marked bad",
204 + *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
205 + } else if (tags.chunk_used) {
206 + *seq_number = tags.seq_number;
207 + *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
209 + *state = YAFFS_BLOCK_STATE_EMPTY;
215 + yaffs_trace(YAFFS_TRACE_MTD,
216 + "block query returns seq %u state %d",
217 + *seq_number, *state);
222 +void yaffs_tags_compat_install(struct yaffs_dev *dev)
224 + if (dev->param.is_yaffs2)
227 + if (!dev->tagger.write_chunk_tags_fn)
228 + dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_write;
230 + if (!dev->tagger.read_chunk_tags_fn)
231 + dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_read;
233 + if (!dev->tagger.query_block_fn)
234 + dev->tagger.query_block_fn = yaffs_tags_compat_query_block;
236 + if (!dev->tagger.mark_bad_fn)
237 + dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;