aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/patches-4.1/502-yaffs-fix-compat-tags-handling.patch
blob: a18cf6fd7bbb78d273a9c64ee6b057d0c152d0cb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
Subject: yaffs: fix compat tags handling

Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
---
--- a/fs/yaffs2/yaffs_tagscompat.c
+++ b/fs/yaffs2/yaffs_tagscompat.c
@@ -17,7 +17,9 @@
 #include "yaffs_getblockinfo.h"
 #include "yaffs_trace.h"
 
+#if 0
 static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk);
+#endif
 
 
 /********** Tags ECC calculations  *********/
@@ -71,6 +73,7 @@ int yaffs_check_tags_ecc(struct yaffs_ta
 	return 0;
 }
 
+#if 0
 /********** Tags **********/
 
 static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr,
@@ -379,3 +382,214 @@ void yaffs_tags_compat_install(struct ya
 	if(!dev->tagger.mark_bad_fn)
 		dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;
 }
+#else
+
+#include "yaffs_packedtags1.h"
+
+static int yaffs_tags_compat_write(struct yaffs_dev *dev,
+				   int nand_chunk,
+				   const u8 *data,
+				   const struct yaffs_ext_tags *tags)
+{
+	struct yaffs_packed_tags1 pt1;
+	u8 tag_buf[9];
+	int retval;
+
+	/* we assume that yaffs_packed_tags1 and yaffs_tags are compatible */
+	compile_time_assertion(sizeof(struct yaffs_packed_tags1) == 12);
+	compile_time_assertion(sizeof(struct yaffs_tags) == 8);
+
+	yaffs_pack_tags1(&pt1, tags);
+	yaffs_calc_tags_ecc((struct yaffs_tags *)&pt1);
+
+	/* When deleting a chunk, the upper layer provides only skeletal
+	 * tags, one with is_deleted set.  However, we need to update the
+	 * tags, not erase them completely.  So we use the NAND write property
+	 * that only zeroed-bits stick and set tag bytes to all-ones and
+	 * zero just the (not) deleted bit.
+	 */
+	if (!dev->param.tags_9bytes) {
+		if (tags->is_deleted) {
+			memset(&pt1, 0xff, 8);
+			/* clear delete status bit to indicate deleted */
+			pt1.deleted = 0;
+		}
+		memcpy(tag_buf, &pt1, 8);
+	} else {
+		if (tags->is_deleted) {
+			memset(tag_buf, 0xff, 8);
+			tag_buf[8] = 0;
+		} else {
+			memcpy(tag_buf, &pt1, 8);
+			tag_buf[8] = 0xff;
+		}
+	}
+
+	retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk,
+			data,
+			(data) ? dev->data_bytes_per_chunk : 0,
+			tag_buf,
+			(dev->param.tags_9bytes) ? 9 : 8);
+
+	return retval;
+}
+
+/* Return with empty extended tags but add ecc_result.
+ */
+static int return_empty_tags(struct yaffs_ext_tags *tags,
+			     enum yaffs_ecc_result ecc_result,
+			     int retval)
+{
+	if (tags) {
+		memset(tags, 0, sizeof(*tags));
+		tags->ecc_result = ecc_result;
+	}
+
+	return retval;
+}
+
+static int yaffs_tags_compat_read(struct yaffs_dev *dev,
+				  int nand_chunk,
+				  u8 *data,
+				  struct yaffs_ext_tags *tags)
+{
+	struct yaffs_packed_tags1 pt1;
+	enum yaffs_ecc_result ecc_result;
+	int retval;
+	int deleted;
+	u8 tag_buf[9];
+
+	retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk,
+			data, dev->param.total_bytes_per_chunk,
+			tag_buf,
+			(dev->param.tags_9bytes) ? 9 : 8,
+			&ecc_result);
+
+	switch (ecc_result) {
+	case YAFFS_ECC_RESULT_NO_ERROR:
+	case YAFFS_ECC_RESULT_FIXED:
+		break;
+
+	case YAFFS_ECC_RESULT_UNFIXED:
+	default:
+		return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED, 0);
+		tags->block_bad = dev->drv.drv_check_bad_fn(dev, nand_chunk);
+		return YAFFS_FAIL;
+	}
+
+	/* Check for a blank/erased chunk. */
+	if (yaffs_check_ff(tag_buf, 8)) {
+		/* when blank, upper layers want ecc_result to be <= NO_ERROR */
+		return return_empty_tags(tags, YAFFS_ECC_RESULT_NO_ERROR,
+					 YAFFS_OK);
+	}
+
+	memcpy(&pt1, tag_buf, 8);
+
+	if (!dev->param.tags_9bytes) {
+		/* Read deleted status (bit) then return it to it's non-deleted
+		 * state before performing tags mini-ECC check. pt1.deleted is
+		 * inverted.
+		 */
+		deleted = !pt1.deleted;
+		pt1.deleted = 1;
+	} else {
+		deleted = (hweight8(tag_buf[8]) < 7) ? 1 : 0;
+	}
+
+	/* Check the packed tags mini-ECC and correct if necessary/possible. */
+	retval = yaffs_check_tags_ecc((struct yaffs_tags *)&pt1);
+	switch (retval) {
+	case 0:
+		/* no tags error, use MTD result */
+		break;
+	case 1:
+		/* recovered tags-ECC error */
+		dev->n_tags_ecc_fixed++;
+		if (ecc_result == YAFFS_ECC_RESULT_NO_ERROR)
+			ecc_result = YAFFS_ECC_RESULT_FIXED;
+		break;
+	default:
+		/* unrecovered tags-ECC error */
+		dev->n_tags_ecc_unfixed++;
+		return return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED,
+					 YAFFS_FAIL);
+	}
+
+	/* Unpack the tags to extended form and set ECC result.
+	 * [set should_be_ff just to keep yaffs_unpack_tags1 happy]
+	 */
+	pt1.should_be_ff = 0xffffffff;
+	yaffs_unpack_tags1(tags, &pt1);
+	tags->ecc_result = ecc_result;
+
+	/* Set deleted state */
+	tags->is_deleted = deleted;
+	return YAFFS_OK;
+}
+
+static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no)
+{
+	return dev->drv.drv_mark_bad_fn(dev, block_no);
+}
+
+static int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
+					 int block_no,
+					 enum yaffs_block_state *state,
+					 u32 *seq_number)
+{
+	struct yaffs_ext_tags tags;
+	int retval;
+
+	yaffs_trace(YAFFS_TRACE_MTD, "%s %d", __func__, block_no);
+
+	*seq_number = 0;
+
+	retval = dev->drv.drv_check_bad_fn(dev, block_no);
+	if (retval == YAFFS_FAIL) {
+		*state = YAFFS_BLOCK_STATE_DEAD;
+		goto out;
+	}
+
+	yaffs_tags_compat_read(dev, block_no * dev->param.chunks_per_block,
+			       NULL, &tags);
+
+	if (tags.ecc_result != YAFFS_ECC_RESULT_NO_ERROR) {
+		yaffs_trace(YAFFS_TRACE_MTD, "block %d is marked bad",
+			    block_no);
+		*state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
+	} else if (tags.chunk_used) {
+		*seq_number = tags.seq_number;
+		*state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
+	} else {
+		*state = YAFFS_BLOCK_STATE_EMPTY;
+	}
+
+	retval = YAFFS_OK;
+
+out:
+	yaffs_trace(YAFFS_TRACE_MTD,
+		    "block query returns seq %u state %d",
+		    *seq_number, *state);
+
+	return retval;
+}
+
+void yaffs_tags_compat_install(struct yaffs_dev *dev)
+{
+	if (dev->param.is_yaffs2)
+		return;
+
+	if (!dev->tagger.write_chunk_tags_fn)
+		dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_write;
+
+	if (!dev->tagger.read_chunk_tags_fn)
+		dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_read;
+
+	if (!dev->tagger.query_block_fn)
+		dev->tagger.query_block_fn = yaffs_tags_compat_query_block;
+
+	if (!dev->tagger.mark_bad_fn)
+		dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;
+}
+#endif