Add nand driver that works with a nand simulator
[yaffs2.git] / direct / test-framework / yaffs_nand_drv.c
diff --git a/direct/test-framework/yaffs_nand_drv.c b/direct/test-framework/yaffs_nand_drv.c
new file mode 100644 (file)
index 0000000..6b3a91e
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
+ *
+ * Copyright (C) 2002-2011 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * This is an interface module for handling NAND in yaffs2 mode.
+ */
+
+/* This code calls a driver that accesses "generic" NAND. In the simulator
+ * this is direceted at a file-backed NAND simulator. The NAND access functions
+ * should also work with real NAND.
+ *
+ * This driver is designed for use in yaffs2 mode with a 2k page size and
+ * 64 bytes of spare.
+ *
+ * The spare ares is used as follows:
+ * offset 0: 2 bytes bad block marker.
+ * offset 2: 8x3 bytes of ECC over the data.
+ * offset 26: rest available to store Yaffs tags etc.
+ */
+
+#include "yaffs_nand_drv.h"
+#include "yportenv.h"
+#include "yaffs_trace.h"
+
+#include "nand_store.h"
+#include "yaffs_flashif.h"
+#include "yaffs_guts.h"
+#include "nanddrv.h"
+#include "yaffs_ecc.h"
+
+struct nand_context {
+       struct nand_chip *chip;
+       u8 *buffer;
+};
+
+
+static inline struct nand_chip *dev_to_chip(struct yaffs_dev *dev)
+{
+       struct nand_context *ctxt =
+               (struct nand_context *)(dev->driver_context);
+       return ctxt->chip;
+}
+
+static inline u8 *dev_to_buffer(struct yaffs_dev *dev)
+{
+       struct nand_context *ctxt =
+               (struct nand_context *)(dev->driver_context);
+       return ctxt->buffer;
+}
+
+static int yaffs_nand_drv_WriteChunk(struct yaffs_dev *dev, int nand_chunk,
+                                  const u8 *data, int data_len,
+                                  const u8 *oob, int oob_len)
+{
+       struct nand_chip *chip = dev_to_chip(dev);
+       u8 *buffer = dev_to_buffer(dev);
+       u8 *e;
+       struct nanddrv_transfer tr[2];
+       int i;
+
+       if(!data || !oob)
+               return YAFFS_FAIL;
+
+
+       /* Calc ECC and marshall the oob bytes into the buffer */
+
+       memset(buffer, 0xff, chip->spare_bytes_per_page);
+
+       for(i = 0, e = buffer + 2; i < chip->data_bytes_per_page; i+=256, e+=3)
+               yaffs_ecc_calc(data + i, e);
+
+       memcpy(buffer + 26, oob, oob_len);
+
+       /* Set up and execute transfer */
+
+       tr[0].buffer = data;
+       tr[0].offset = 0;
+       tr[0].nbytes = data_len;
+
+       tr[1].buffer = buffer;
+       tr[1].offset = chip->data_bytes_per_page;
+       tr[1].nbytes = chip->spare_bytes_per_page;
+
+       if(nanddrv_write_tr(chip, nand_chunk, tr, 2) == 0)
+               return YAFFS_OK;
+       else
+               return YAFFS_FAIL;
+}
+
+static int yaffs_nand_drv_ReadChunk(struct yaffs_dev *dev, int nand_chunk,
+                                  u8 *data, int data_len,
+                                  u8 *oob, int oob_len,
+                                  enum yaffs_ecc_result *ecc_result_out)
+{
+       struct nand_chip *chip = dev_to_chip(dev);
+       u8 *buffer = dev_to_buffer(dev);
+       struct nanddrv_transfer tr[2];
+       struct nanddrv_transfer *trp = tr;
+       int n_tr = 0;
+       int ret;
+       enum yaffs_ecc_result ecc_result;
+       int i;
+       u8 *e;
+       u8 read_ecc[3];
+
+       if(data) {
+               trp->buffer = data;
+               trp->offset = 0;
+               trp->nbytes = data_len;
+               n_tr++;
+               trp++;
+       }
+
+
+       trp->buffer = buffer;
+       trp->offset = chip->data_bytes_per_page;
+       trp->nbytes = chip->spare_bytes_per_page;
+       n_tr++;
+       trp++;
+
+
+       ret = nanddrv_read_tr(chip, nand_chunk, tr, n_tr);
+
+       if(ret < 0) {
+               if (ecc_result_out)
+                       *ecc_result_out = YAFFS_ECC_RESULT_UNKNOWN;
+               return YAFFS_FAIL;
+       }
+
+       /* Do ECC and marshalling */
+       if(oob)
+               memcpy(oob, buffer + 26, oob_len);
+
+       ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
+
+#if 0
+       if(data) {
+               for(i = 0, e = buffer + 2; i < chip->data_bytes_per_page; i+=256, e+=3) {
+                       yaffs_ecc_calc(data + i, read_ecc);
+                       ret = yaffs_ecc_correct(data + i, e, read_ecc);
+                       if (ret < 0)
+                               ecc_result = YAFFS_ECC_RESULT_UNFIXED;
+                       else if( ret > 0 && ecc_result == YAFFS_ECC_RESULT_NO_ERROR)
+                               ecc_result = YAFFS_ECC_RESULT_FIXED;
+               }
+       }
+#endif
+
+       if (ecc_result_out)
+               *ecc_result_out = ecc_result;
+
+       return YAFFS_OK;
+}
+
+static int yaffs_nand_drv_EraseBlock(struct yaffs_dev *dev, int block_no)
+{
+       struct nand_chip *chip = dev_to_chip(dev);
+
+       if(nanddrv_erase(chip, block_no) == 0)
+               return YAFFS_OK;
+       else
+               return YAFFS_FAIL;
+}
+
+static int yaffs_nand_drv_MarkBad(struct yaffs_dev *dev, int block_no)
+{
+       struct nand_chip *chip = dev_to_chip(dev);
+       u8 *buffer = dev_to_buffer(dev);
+       int nand_chunk = block_no * chip->pages_per_block;
+       struct nanddrv_transfer tr[1];
+
+       memset(buffer, 0xff, chip->spare_bytes_per_page);
+       buffer[0] = 'Y';
+       buffer[1] = 'Y';
+
+       tr[0].buffer = buffer;
+       tr[0].offset = chip->data_bytes_per_page;
+       tr[0].nbytes = chip->spare_bytes_per_page;
+
+       if(nanddrv_write_tr(chip, nand_chunk, tr, 1) == 0)
+               return YAFFS_OK;
+       else
+               return YAFFS_FAIL;
+}
+
+static int yaffs_nand_drv_CheckBad(struct yaffs_dev *dev, int block_no)
+{
+       struct nand_chip *chip = dev_to_chip(dev);
+       u8 *buffer = dev_to_buffer(dev);
+       int nand_chunk = block_no * chip->pages_per_block;
+       int ret;
+
+       struct nanddrv_transfer tr[1];
+
+       memset(buffer, 0, chip->spare_bytes_per_page);
+
+       tr[0].buffer = buffer;
+       tr[0].offset = chip->data_bytes_per_page;
+       tr[0].nbytes = chip->spare_bytes_per_page;
+
+       ret = nanddrv_read_tr(chip, nand_chunk, tr, 1);
+
+       /* Check that bad block marker is not set */
+       if(yaffs_hweight8(buffer[0]) + yaffs_hweight8(buffer[1]) < 14)
+               return YAFFS_FAIL;
+       else
+               return YAFFS_OK;
+
+}
+
+static int yaffs_nand_drv_Initialise(struct yaffs_dev *dev)
+{
+       struct nand_chip *chip = dev_to_chip(dev);
+
+       return YAFFS_OK;
+}
+
+static int yaffs_nand_drv_Deinitialise(struct yaffs_dev *dev)
+{
+       struct nand_chip *chip = dev_to_chip(dev);
+
+       return YAFFS_OK;
+}
+
+#include "nandsim_file.h"
+
+struct yaffs_dev *yaffs_nandsim_install_drv(const char *name,
+                                       const char *file_name,
+                                       int n_blocks)
+{
+       struct yaffs_dev *dev;
+       char *name_copy = NULL;
+       struct yaffs_param *param;
+       struct yaffs_driver *drv;
+       struct nand_chip *chip = NULL;
+       struct nand_context *ctxt = NULL;
+       u8 *buffer = NULL;
+
+       dev = malloc(sizeof(struct yaffs_dev));
+       ctxt = malloc(sizeof(struct nand_context));
+       name_copy = strdup(name);
+
+       if(!dev || !ctxt || !name_copy)
+               goto fail;
+
+       chip = nandsim_file_init(file_name, n_blocks, 64, 2048, 64, 0);
+       if(!chip)
+               goto fail;
+
+       buffer = malloc(chip->spare_bytes_per_page);
+
+       if(!buffer)
+               goto fail;
+
+       param = &dev->param;
+       drv = &dev->drv;
+
+       memset(dev, 0, sizeof(*dev));
+       memset(ctxt, 0, sizeof(*ctxt));
+
+       param->name = name_copy;
+
+       param->total_bytes_per_chunk = chip->data_bytes_per_page;
+       param->chunks_per_block = chip->pages_per_block;
+       param->n_reserved_blocks = 5;
+       param->start_block = 0; // Can use block 0
+       param->end_block = chip->blocks - 1; // Last block
+       param->is_yaffs2 = 1;
+       param->use_nand_ecc = 1;
+       param->n_caches = 10;
+
+       drv->drv_write_chunk_fn = yaffs_nand_drv_WriteChunk;
+       drv->drv_read_chunk_fn = yaffs_nand_drv_ReadChunk;
+       drv->drv_erase_fn = yaffs_nand_drv_EraseBlock;
+       drv->drv_mark_bad_fn = yaffs_nand_drv_MarkBad;
+       drv->drv_check_bad_fn = yaffs_nand_drv_CheckBad;
+       drv->drv_initialise_fn = yaffs_nand_drv_Initialise;
+       drv->drv_deinitialise_fn = yaffs_nand_drv_Deinitialise;
+
+       ctxt->chip = chip;
+       ctxt->buffer = buffer;
+       dev->driver_context = (void *) ctxt;
+
+       yaffs_add_device(dev);
+
+       return dev;
+
+fail:
+       free(dev);
+       free(ctxt);
+       free(name_copy);
+       free(buffer);
+       return NULL;
+}