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
