| /* | 
|  * 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. | 
|  */ | 
|   | 
| #include "yportenv.h" | 
| #include "yaffs_guts.h" | 
|   | 
|   | 
| #include "yaffs_nandif.h" | 
| #include "yaffs_packedtags2.h" | 
|   | 
| #include "yramsim.h" | 
|   | 
| #include "yaffs_trace.h" | 
| #include "yaffsfs.h" | 
|   | 
|   | 
| /* NB For use with inband tags.... | 
|  * We assume that the data buffer is of size totalBytersPerChunk so that | 
|  * we can also use it to load the tags. | 
|  */ | 
| int ynandif_WriteChunkWithTagsToNAND(struct yaffs_dev *dev, int nand_chunk, | 
|                       const u8 *data, | 
|                       const struct yaffs_ext_tags *tags) | 
| { | 
|   | 
|     int retval = 0; | 
|     struct yaffs_packed_tags2 pt; | 
|     void *spare; | 
|     unsigned spareSize = 0; | 
|     struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context); | 
|   | 
|     yaffs_trace(YAFFS_TRACE_MTD, | 
|         "nandmtd2_WriteChunkWithTagsToNAND chunk %d data %p tags %p", | 
|         nand_chunk, data, tags); | 
|   | 
|   | 
|     /* For yaffs2 writing there must be both data and tags. | 
|      * If we're using inband tags, then the tags are stuffed into | 
|      * the end of the data buffer. | 
|      */ | 
|   | 
|     if (dev->param.inband_tags) { | 
|         struct yaffs_packed_tags2_tags_only *pt2tp; | 
|   | 
|         pt2tp = (struct yaffs_packed_tags2_tags_only *) | 
|             (data + dev->data_bytes_per_chunk); | 
|         yaffs_pack_tags2_tags_only(pt2tp, tags); | 
|         spare = NULL; | 
|         spareSize = 0; | 
|     } else { | 
|         yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc); | 
|         spare = &pt; | 
|         spareSize = sizeof(struct yaffs_packed_tags2); | 
|     } | 
|   | 
|     retval = geometry->writeChunk(dev, nand_chunk, | 
|                 data, dev->param.total_bytes_per_chunk, | 
|                 spare, spareSize); | 
|   | 
|     return retval; | 
| } | 
|   | 
| int ynandif_ReadChunkWithTagsFromNAND(struct yaffs_dev *dev, int nand_chunk, | 
|                        u8 *data, struct yaffs_ext_tags *tags) | 
| { | 
|     struct yaffs_packed_tags2 pt; | 
|     int localData = 0; | 
|     void *spare = NULL; | 
|     unsigned spareSize; | 
|     int retval = 0; | 
|     int eccStatus; /* 0 = ok, 1 = fixed, -1 = unfixed */ | 
|     struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context); | 
|   | 
|     yaffs_trace(YAFFS_TRACE_MTD, | 
|         "nandmtd2_ReadChunkWithTagsFromNAND chunk %d data %p tags %p", | 
|         nand_chunk, data, tags); | 
|   | 
|     if (!tags) { | 
|         spare = NULL; | 
|         spareSize = 0; | 
|     } else if (dev->param.inband_tags) { | 
|   | 
|         if (!data) { | 
|             localData = 1; | 
|             data = yaffs_get_temp_buffer(dev); | 
|         } | 
|         spare = NULL; | 
|         spareSize = 0; | 
|     } else { | 
|         spare = &pt; | 
|         spareSize = sizeof(struct yaffs_packed_tags2); | 
|     } | 
|   | 
|     retval = geometry->readChunk(dev, nand_chunk, | 
|                  data, | 
|                  data ? dev->param.total_bytes_per_chunk : 0, | 
|                  spare, spareSize, | 
|                  &eccStatus); | 
|   | 
|     if (dev->param.inband_tags) { | 
|         if (tags) { | 
|             struct yaffs_packed_tags2_tags_only *pt2tp; | 
|             pt2tp = (struct yaffs_packed_tags2_tags_only *) | 
|                     &data[dev->data_bytes_per_chunk]; | 
|             yaffs_unpack_tags2_tags_only(tags, pt2tp); | 
|         } | 
|     } else { | 
|         if (tags) | 
|             yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc); | 
|     } | 
|   | 
|     if (tags && tags->chunk_used) { | 
|         if (eccStatus < 0 || | 
|            tags->ecc_result == YAFFS_ECC_RESULT_UNFIXED) | 
|             tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED; | 
|         else if (eccStatus > 0 || | 
|                  tags->ecc_result == YAFFS_ECC_RESULT_FIXED) | 
|             tags->ecc_result = YAFFS_ECC_RESULT_FIXED; | 
|         else | 
|             tags->ecc_result = YAFFS_ECC_RESULT_NO_ERROR; | 
|     } | 
|   | 
|     if (localData) | 
|         yaffs_release_temp_buffer(dev, data); | 
|   | 
|     return retval; | 
| } | 
|   | 
| int ynandif_MarkNANDBlockBad(struct yaffs_dev *dev, int blockId) | 
| { | 
|     struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context); | 
|   | 
|     return geometry->markBlockBad(dev, blockId); | 
| } | 
|   | 
| int ynandif_EraseBlockInNAND(struct yaffs_dev *dev, int blockId) | 
| { | 
|     struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context); | 
|   | 
|     return geometry->eraseBlock(dev, blockId); | 
|   | 
| } | 
|   | 
|   | 
| static int ynandif_IsBlockOk(struct yaffs_dev *dev, int blockId) | 
| { | 
|     struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context); | 
|   | 
|     return geometry->checkBlockOk(dev, blockId); | 
| } | 
|   | 
| int ynandif_QueryNANDBlock(struct yaffs_dev *dev, int blockId, | 
|         enum yaffs_block_state *state, u32 *seq_number) | 
| { | 
|     unsigned chunkNo; | 
|     struct yaffs_ext_tags tags; | 
|   | 
|     *seq_number = 0; | 
|   | 
|     chunkNo = blockId * dev->param.chunks_per_block; | 
|   | 
|     if (!ynandif_IsBlockOk(dev, blockId)) { | 
|         *state = YAFFS_BLOCK_STATE_DEAD; | 
|     } else { | 
|         ynandif_ReadChunkWithTagsFromNAND(dev, chunkNo, NULL, &tags); | 
|   | 
|         if (!tags.chunk_used) { | 
|             *state = YAFFS_BLOCK_STATE_EMPTY; | 
|         } else { | 
|             *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; | 
|             *seq_number = tags.seq_number; | 
|         } | 
|     } | 
|   | 
|     return YAFFS_OK; | 
| } | 
|   | 
|   | 
| int ynandif_InitialiseNAND(struct yaffs_dev *dev) | 
| { | 
|     struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context); | 
|   | 
|     geometry->initialise(dev); | 
|   | 
|     return YAFFS_OK; | 
| } | 
|   | 
| int ynandif_Deinitialise_flash_fn(struct yaffs_dev *dev) | 
| { | 
|     struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context); | 
|   | 
|     geometry->deinitialise(dev); | 
|   | 
|     return YAFFS_OK; | 
| } | 
|   | 
|   | 
| struct yaffs_dev * | 
|     yaffs_add_dev_from_geometry(const YCHAR *name, | 
|                     const struct ynandif_Geometry *geometry) | 
| { | 
|     YCHAR *clonedName = malloc(sizeof(YCHAR) * | 
|                 (strnlen(name, YAFFS_MAX_NAME_LENGTH)+1)); | 
|     struct yaffs_dev *dev = malloc(sizeof(struct yaffs_dev)); | 
|     struct yaffs_param *param; | 
|   | 
|     if (dev && clonedName) { | 
|         memset(dev, 0, sizeof(struct yaffs_dev)); | 
|         strcpy(clonedName, name); | 
|   | 
|         param = &dev->param; | 
|   | 
|         param->name = clonedName; | 
|         param->write_chunk_tags_fn = ynandif_WriteChunkWithTagsToNAND; | 
|         param->read_chunk_tags_fn = ynandif_ReadChunkWithTagsFromNAND; | 
|         param->erase_fn = ynandif_EraseBlockInNAND; | 
|         param->initialise_flash_fn = ynandif_InitialiseNAND; | 
|         param->query_block_fn = ynandif_QueryNANDBlock; | 
|         param->bad_block_fn = ynandif_MarkNANDBlockBad; | 
|         param->n_caches = 20; | 
|         param->start_block = geometry->start_block; | 
|         param->end_block   = geometry->end_block; | 
|         param->total_bytes_per_chunk  = geometry->dataSize; | 
|         param->spare_bytes_per_chunk  = geometry->spareSize; | 
|         param->inband_tags          = geometry->inband_tags; | 
|         param->chunks_per_block      = geometry->pagesPerBlock; | 
|         param->use_nand_ecc          = geometry->hasECC; | 
|         param->is_yaffs2          = geometry->useYaffs2; | 
|         param->n_reserved_blocks      = 5; | 
|         dev->driver_context          = (void *)geometry; | 
|   | 
|         yaffs_add_device(dev); | 
|   | 
|         return dev; | 
|     } | 
|   | 
|     free(dev); | 
|     free(clonedName); | 
|   | 
|     return NULL; | 
| } |