zte's code,first commit

Change-Id: I9a04da59e459a9bc0d67f101f700d9d7dc8d681b
diff --git a/ap/os/linux/linux-3.4.x/drivers/mtd/zftl_v3.c b/ap/os/linux/linux-3.4.x/drivers/mtd/zftl_v3.c
new file mode 100755
index 0000000..b09118c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mtd/zftl_v3.c
@@ -0,0 +1,1225 @@
+/*
+ * Linux driver for NAND NV Flash Translation Layer
+ * Copyright (C) 2013, ZTE Corporation.
+ */
+
+
+#include <linux/mtd/mtd.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/hdreg.h>
+#include <linux/blkdev.h>
+
+#include <linux/kmod.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+
+#include <linux/mtd/blktrans.h>
+#include <linux/mtd/zftl.h>
+#include <linux/mtd/zftl_ecc.h>
+
+
+
+#include <linux/types.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_MTD_NVRESUME
+#include <linux/mfd/zx234290.h>
+#endif
+
+#include <linux/mtd/partition_guard.h>
+
+#define ZFTL_DEBUG 0
+
+
+/* DEBUG */
+#if	ZFTL_DEBUG
+#define zftl_debug(fmt,args...)	printk (fmt ,##args)
+#define zftl_debugX(level,fmt,args...) if (DEBUG>=level) printk(fmt,##args);
+#else
+#define zftl_debug(fmt,args...)
+#define zftl_debugX(level,fmt,args...)
+#endif	/* DEBUG */
+
+/* defined in cmd_nand.c */
+int arg_off_size(int argc, char *const argv[], int *idx,
+			loff_t *off, loff_t *size);
+extern int nand_block_isbad(struct mtd_info *mtd, loff_t offs);
+int  zftl_unpack_tags(struct zftl_packed_tags *pt);
+
+
+#ifdef CONFIG_MTD_NVRESUME
+int zftl_res = 0;
+extern int  hal_Comm_Soft_Reset(T_ZDrvSys_RESET_TYPE reset_type);
+extern int zOss_NvInResume(void);					
+extern void zOss_NvSetErrorAddr(int dwAddr);
+#endif
+
+struct ZFTLrecord *zftl_info[CONFIG_ZFTL_MAX_PARTITIONS] = {NULL};
+extern struct mtd_info *mtd_fota;
+extern struct cmdline_mtd_partition *partitions;
+
+struct cmdline_mtd_partition {
+	struct cmdline_mtd_partition *next;
+	char *mtd_id;
+	int num_parts;
+	struct mtd_partition *parts;
+};
+
+int partnum = 0;
+static struct mutex zftl_mutex;
+static int mutex_inited = 0;
+int cdrom_partsize = 0;
+
+/* nvro partition size */
+int nvro_partsize = 0;
+int nvrwo_partsize = 0;
+
+
+int nand_NvProgram(int dwStart, int dwLen, char* from);
+int nand_NvRead(int dwStart, int dwLen, char* to);
+
+
+static int memcmpb(void *a, int c, int n)
+{
+	int i;
+	for (i = 0; i < n; i++) {
+		if (c != ((unsigned char *)a)[i])
+			return 1;
+	}
+	return 0;
+}
+
+/* Read oob data from flash */
+static int zftl_read_oob(struct ZFTLrecord *zftl, uint32_t offs, uint8_t *buf)
+{
+    nand_info_t  *nand = zftl->nand;
+	uint32_t mask = zftl->writesize - 1;
+	struct mtd_oob_ops ops;
+	int32_t res;
+    struct zftl_packed_tags pt;
+	
+	ops.mode = MTD_OPS_AUTO_OOB;
+	ops.ooboffs = offs & mask;
+	
+    #ifdef CONFIG_ZFTL_ENABLE_OOB_ECC
+	ops.ooblen = sizeof(struct zftl_packed_tags);
+	ops.oobbuf = (uint8_t*)&pt;
+	#else
+	ops.ooblen = sizeof(struct zftl_oob);
+	ops.oobbuf = buf;
+	#endif
+	ops.datbuf = NULL;
+
+	res = nand->_read_oob(nand, (offs & ~mask), &ops);
+	
+	#ifdef CONFIG_ZFTL_ENABLE_OOB_ECC
+	zftl_unpack_tags(&pt);
+	memcpy(buf,(uint8_t *)&(pt.t),sizeof(struct zftl_oob ));
+	#endif
+	
+	return res;
+}
+
+
+static int zftl_setup_table(struct ZFTLrecord *zftl, uint32_t *is_writed)
+{
+    nand_info_t  *nand = zftl->nand;
+    uint32_t block_num = zftl->numBlocks;
+    uint32_t first_block = zftl->firstBlock;
+    uint32_t erasesize = zftl->erasesize;
+    uint32_t oobsize = zftl->oobsize;
+    struct zftl_oob *oob = (struct zftl_oob *)zftl->oobbuf;
+    uint32_t index = 0;
+    uint32_t addr = 0;
+    
+    memset(oob, 0x0, oobsize);
+    
+    for( index = 0; index < block_num; index++ )
+    {
+        addr = (first_block + index) * erasesize;
+        if(nand->_block_isbad(nand, (loff_t)addr))
+        {
+            zftl->blockTable[index] = BLOCK_BAD;
+            continue;
+        }
+
+        /* д¿éµÄ˳ÐòΪ ÏÈдµÚ¶þÒ³¿ªÊ¼µ½×îºóÒ»Ò³£¬ÔÙдµÚÒ»Ò³ */
+        /*      µÚÒ»Ò³oob    µÚ¶þÒ³oob   ¿é״̬                     ˵Ã÷    */
+            /*   0xff         0xff    BLOCK_FREE             ¶¼Ã»ÓпªÊ¼Ð´ */
+            /*   0xff         ----    BLOCK_DIRTY            ¿ªÊ¼Ð´£¬µ«Ã»ÓÐдÍêµÚÒ»Ò³£¬Öм䱻ÖÐ¶Ï */
+            /*   ----         0xff    BLOCK_USED/DIRTY       дÍêµÚÒ»Ò³£¬Íê³É¿éдÈë */
+            /*   ----         ----    BLOCK_USED/DIRTY       дÍêµÚÒ»Ò³£¬Íê³É¿éдÈë */
+         
+        if(zftl_read_oob(zftl, addr, (uint8_t *)oob ))/* ¶ÁÈ¡µÚÒ»Ò³ */
+        {
+             printk("[zftl_setup_table] read oob error at = 0x%08x\n", addr);
+             return NAND_READ_WRITE_ERROR;
+        }
+        
+        if(memcmpb(oob, 0xff, oobsize))     /*  µÚÒ»Ò³oob != 0xff */
+        {
+            //if( oob.used == 1 )   /* block is used */
+            if( !memcmp(oob->head, CONFIG_ZFLT_HEAD, CONFIG_ZFLT_HEAD_BYTE) )   /* °üº¬ÓзÖÇøÍ· */
+            {
+                *is_writed += 1;
+                if( oob->version > zftl->versionTable[oob->logicBlockID]) 
+                {
+                    /* version is newer */
+                    if(zftl->versionTable[oob->logicBlockID] != 0)  /* ÒѾ­ÓжÔÓ¦µÄused¿éÁË */
+                        zftl->blockTable[zftl->blockRepTable[oob->logicBlockID]] = BLOCK_DIRTY;
+
+                    zftl->blockRepTable[oob->logicBlockID] = index;
+                    zftl->versionTable[oob->logicBlockID] = oob->version;
+                    zftl->blockTable[index] = BLOCK_USED;
+					/* Added by zhouqi for xxx, 2014/04/23 */
+                    zftl->lastFreeBlock = index + 1; 
+                }
+                else
+                    zftl->blockTable[index] = BLOCK_DIRTY;              
+            }
+            else
+                zftl->blockTable[index] = BLOCK_DIRTY; 
+        }
+        else    /* 0xff£¬block is free */
+        {
+            if(zftl_read_oob(zftl, addr + zftl->writesize, (uint8_t *)oob ))/* ¶ÁÈ¡µÚ¶þÒ³ */
+            {
+                printk("[zftl_setup_table] read oob error at = 0x%08x\n", addr);
+                return NAND_READ_WRITE_ERROR;
+            }
+
+            if(memcmpb(oob, 0xff, oobsize))     /*  µÚ¶þÒ³oob != 0xff */
+            {
+                zftl->blockTable[index] = BLOCK_DIRTY;
+            }
+            else                                      /*  µÚ¶þÒ³oob = 0xff */
+                zftl->blockTable[index] = BLOCK_FREE;
+        }        
+    }
+
+    return 0;
+}
+
+
+/* ²Á³ýÒ»¿é£¬ebnumΪ¿éºÅ */
+static int zftl_erase_block(nand_info_t  *nand, uint32_t ebnum)
+{
+	int err = -1;
+	struct erase_info ei;
+	uint32_t addr = ebnum * nand->erasesize;
+
+	memset(&ei, 0, sizeof(struct erase_info));
+	ei.mtd  = nand;
+	ei.addr = (uint64_t)addr;
+	ei.len  = (uint64_t)nand->erasesize;
+
+	err = nand->_erase(nand, &ei);
+	if (err) 
+    {
+		printk("[zftl_erase_block]: erasing %d  error %d\n", ebnum, err);
+		return err;
+	}
+
+	if (ei.state == MTD_ERASE_FAILED) 
+    {
+		printk("[zftl_erase_block]: erasing %d\n",ebnum);
+		return -1;
+	}
+	return 0;
+}
+
+
+void zftl_garbage_collect(struct ZFTLrecord *zftl)
+{
+    uint32_t block_num = zftl->numBlocks;
+    uint32_t firstBlock = zftl->firstBlock;
+    uint32_t index = 0;
+    nand_info_t  *nand = zftl->nand;
+
+    zftl_debug("\n");
+    
+    for( index = 0; index < block_num; index++ )
+    {
+        if( zftl->blockTable[index] == BLOCK_DIRTY )
+        {
+            zftl_debug("    [zftl_garbage_collect] eraseblock zftl->blockTable[-%d-]\n", index);
+            if( zftl_erase_block(nand, firstBlock + index) )
+            {   
+                /* ²Á³ýʧ°Ü£¬±ê¼ÇΪ»µ¿ì */
+                zftl_debug("    [zftl_garbage_collect] eraseblock zftl->blockTable[-%d-] ERROR\n", index);
+                zftl->blockTable[index] = BLOCK_BAD;
+                /* ±ê¼Ç»µ¿é£¬¸üлµ¿é±í mtd->erase ÖУ¬ÒÔ°üº¬²Á³ýʧ°Ü¸üÐÂBBT*/
+                nand->_block_markbad(nand, (loff_t)((firstBlock + index) << zftl->erasesize_shift)); 
+            }
+            else
+            {
+                /* ²Á³ý³É¹¦ */
+                zftl->blockTable[index] = BLOCK_FREE;;
+            }
+        }
+    }
+    zftl_debug("\n");
+}
+
+
+/* Added by zhouqi for xxx, 2014/04/23 */
+
+static int zftl_find_free_block(struct ZFTLrecord *zftl, uint32_t *freeBlock)
+	{	
+		uint32_t i = 2;
+		uint32_t index = 0;
+		uint32_t max = 0;
+		
+		do
+		{
+			index = zftl->lastFreeBlock;
+		    max = zftl->numBlocks;
+			for( ; index < max; index++ )
+			{
+				if( zftl->blockTable[index] == BLOCK_FREE )
+				{
+					*freeBlock = index ;
+					zftl->lastFreeBlock = index + 1;
+					zftl_debug("	[zftl_find_free_block++]  zftl->blockTable[ -%d- ]	-BLOCK_FREE-\n", index - 1);
+					return 0;
+				}
+			}
+	
+			max = zftl->lastFreeBlock;
+			index = 1;
+			for( ; index < max; index++ )
+			{
+				if( zftl->blockTable[index - 1] == BLOCK_FREE )
+				{
+					*freeBlock = index - 1;
+					zftl->lastFreeBlock = index;
+					zftl_debug("	[zftl_find_free_block--]  zftl->blockTable[ -%d- ]	-BLOCK_FREE-\n", index - 1);
+					return 0;
+				}
+			}
+			
+			/* ûÓÐÕÒµ½¿ÕÏеĿ飬Ôò½øÐÐÀ¬»ø»ØÊÕ */
+			zftl_debug("	[zftl_find_free_block]	  no free block --> zftl_garbage_collect()\n");
+			zftl_garbage_collect(zftl);
+		} while( --i !=0 );  
+	
+		return 1;	 
+	}
+
+
+void zftl_pack_tags(struct zftl_packed_tags *pt,
+		      struct zftl_oob *t)
+{
+	memcpy((uint8_t *)&(pt->t),(uint8_t *)t,sizeof(struct zftl_oob));
+	zftl_ecc_calc_other((uint8_t *)&pt->t,
+				    sizeof(struct zftl_oob),
+				    &pt->ecc);
+	
+}
+
+static uint32_t is_erased(uint8_t *buf, int len)
+{
+	int i = 0;
+	for (i = 0; i < len; i++)
+		if (buf[i] != 0xFF)
+			return 0;
+	return 1;
+}
+
+int zftl_unpack_tags(struct zftl_packed_tags *pt)
+{
+	enum zftl_ecc_result ecc_result = ZFTL_ECC_RESULT_NO_ERROR;
+	struct zftl_ecc_other ecc;
+	int result;
+
+	
+	zftl_ecc_calc_other((unsigned char *)&pt->t,
+			sizeof(struct zftl_oob),
+			&ecc);
+	result = zftl_ecc_correct_other((unsigned char *)&pt->t,\
+			sizeof(struct zftl_oob),\
+			&pt->ecc, &ecc);
+	switch (result) {
+	case 0:
+		ecc_result = ZFTL_ECC_RESULT_NO_ERROR;
+		//printf("ZFTL_ECC_RESULT_NO_ERROR\n");
+		break;
+	case 1:
+		
+		ecc_result = ZFTL_ECC_RESULT_FIXED;
+		printk("ZFTL_ECC_RESULT_FIXED\n");
+		
+		break;
+	case -1:
+		if(!is_erased((unsigned char *)pt,sizeof(struct zftl_packed_tags)))
+		{
+			ecc_result = ZFTL_ECC_RESULT_UNFIXED;
+			
+			//printk("ZFTL_ECC_RESULT_UNFIXED\n");
+		}
+		
+		break;
+	default:
+		ecc_result = ZFTL_ECC_RESULT_UNKNOWN;
+		printk("ZFTL_ECC_RESULT_UNKNOWN\n");
+	}
+	if(ecc_result==ZFTL_ECC_RESULT_UNFIXED)
+	{
+	  
+		return -1;
+	}
+	else
+	{
+		return 0;
+	}
+}
+
+
+static int zftl_write_block(nand_info_t  *nand, uint32_t offs, size_t *retlen,
+                 struct zftl_oob *oob, u_char *buf)
+{ 
+	struct mtd_oob_ops ops;
+	int32_t res = 0;
+    uint32_t offset = offs;
+    size_t ret_len = 0;
+    size_t pagesize = nand->writesize;
+
+    
+    #ifdef CONFIG_ZFTL_ENABLE_OOB_ECC
+	struct zftl_packed_tags pt;
+	zftl_pack_tags(&pt,oob);
+	#endif
+
+    /* write the remain  */
+    res = nand->_write(nand, (loff_t)(offset + nand->writesize), nand->erasesize - pagesize, 
+                        &ret_len, (u_char *)(buf + pagesize) );
+
+    /* write the first page of the block write the oob  */
+	ops.mode = MTD_OPS_AUTO_OOB;
+	ops.ooboffs = 0;
+	
+    #ifdef CONFIG_ZFTL_ENABLE_OOB_ECC
+	ops.ooblen = sizeof(struct zftl_packed_tags);
+	ops.oobbuf = (uint8_t *)&pt;
+	#else
+	ops.ooblen = sizeof(struct zftl_oob);
+	ops.oobbuf = (uint8_t *)oob;
+	#endif
+	ops.datbuf = buf;
+	ops.len = pagesize;
+	res += nand->_write_oob(nand, (loff_t)offset, &ops);   
+	*retlen = ops.retlen + ret_len;
+    return res;
+}
+
+
+
+int zftl_write(struct ZFTLrecord *zftl, uint32_t to, uint32_t len, u_char *buf)
+{  
+    int ret = 0;
+    uint32_t offset_block_phy = 0;
+    uint32_t block_new = 0;
+    size_t retlen = 0;  
+    u_char *buffer = buf;
+    struct zftl_oob *oob = (struct zftl_oob *)zftl->oobbuf;
+    uint32_t block_offset = 0;
+    uint32_t erasesize_shift = zftl->erasesize_shift;
+    uint32_t block_log = 0;   
+    uint32_t erasesize = zftl->erasesize;
+    uint32_t is_upthreshold = 0;
+    uint32_t oob_size = zftl->oobsize;
+    uint32_t firstBlock = zftl->firstBlock;
+    u_char *blockbuf = zftl->blockbuf;
+
+    uint32_t left_to_write = len;
+    uint32_t write_length = 0;
+    uint32_t offset = to;
+    
+    nand_info_t  *nand = zftl->nand;
+	
+	if((zftl == NULL) || (buf == NULL))
+	{
+		BUG();
+	}
+
+    while(left_to_write > 0)
+    {
+        /* ¿Õ¼ä£¬³õʼ»¯Îª 0xFF */
+        memset(blockbuf, 0xff, erasesize);
+        memset(oob, 0xff, oob_size);
+       
+        /* »ñÈ¡Âß¼­¿é£¬¼°¿éÄÚÆ«ÒÆ */
+        block_log = offset >> (zftl->erasesize_shift);               
+        block_offset = offset & (zftl->erasesize - 1);  /* ¿éÄ򵀮«ÒƵØÖ· */
+
+        /* ÅжÏÂß¼­¿éÊÇ·ñÓÐÎïÀí¶ÔÓ¦¿é£¬Èç¹ûÓУ¬ÔòΪ¸ÄдÊý¾Ý£»Èç¹ûûÓУ¬ÔòΪµÚÒ»´Îд´Ë¿é */
+        if(zftl->blockRepTable[block_log] != BLOCK_NIL)
+        {
+            /* »ñÈ¡Âß¼­¿é¶ÔÓ¦µÄÎïÀí¿é */
+            offset_block_phy = zftl->blockRepTable[block_log];
+            zftl_debug("    [zftl_write]  zftl->blockRepTable[%d] = %d\n", block_log, offset_block_phy);
+ 
+            /* Õû¸ö¶Áȡһ¿éÊý¾Ý */
+            zftl_debug("    [zftl_read]  nand->read( -0x%0x-, -0x%0x-)\n", 
+                        ((firstBlock + offset_block_phy) << erasesize_shift),erasesize);
+            ret = nand->_read(nand, ((firstBlock + offset_block_phy) << erasesize_shift), erasesize, &retlen, blockbuf);
+            if (ret || retlen != erasesize)
+            {
+                printk(KERN_ERR"[zfltl]: -zftl_read- error\n");
+#ifdef CONFIG_MTD_NVRESUME    		   
+    		    if((!zOss_NvInResume())&&(zftl==zftl_info[1])&&(zftl_res != 1))
+			    {
+					return -1;
+			    }
+#endif
+            }
+
+            /* ¶ÁÈ¡ÕâÒ»¿éµÄOOBÐÅÏ¢£¬²¢¸üРVERSION, Èç¹û´ïµ½°æ±¾ºÅµÄãÐÖµ£¬Ôò½øÐлØÊÕ */
+            if(zftl_read_oob(zftl, ((firstBlock + offset_block_phy) << erasesize_shift), (uint8_t *)oob ))
+            {
+                 printk("    [zftl_write] read oob error at = 0x%x\n", (offset_block_phy << erasesize_shift));
+                 //return -1;
+            }
+            if( oob->version == CONFIG_ZFLT_VERSION_THRESHOLD )
+            {
+                zftl_debug("    [zftl_write] up to the -CONFIG_ZFLT_VERSION_THRESHOLD-\n");
+                is_upthreshold = 1;
+                zftl_garbage_collect(zftl);
+                oob->version = 1;
+            }
+            else
+            {
+                
+                oob->version += 1;
+				zftl_debug("    [zftl_write]  oob->version = %d,oob->logicBlockID = %d\n", oob->version,oob->logicBlockID);
+            }
+
+            memcpy(oob->head, CONFIG_ZFLT_HEAD, CONFIG_ZFLT_HEAD_BYTE);   /* ¿½±´zftlÍ· */ 
+            oob->logicBlockID = block_log;
+
+            /* Ìî³ä¸üРdata */
+            if (left_to_write < (erasesize - block_offset))
+			    write_length = left_to_write;
+		    else
+			    write_length = nand->erasesize - block_offset;
+
+            memcpy(blockbuf + block_offset, buffer, write_length);
+            zftl_debug("    [zftl_write]  update the date ( -0x%0x-, -0x%0x- )\n",
+                                              block_offset, write_length);
+                        
+            
+            /* ѰÕÒÒ»¸ö FREE µÄÎïÀí¿é */
+            find_next_free_block_1:
+            if( zftl_find_free_block(zftl, &block_new) )/* BLOCK_USED */
+            {
+                /* ûÓÐÕÒµ½FREE¿é */
+                printk("    [zftl_write] there is enough block, please enlarge the partition\n");
+                return CAN_NOT_FIND_FREE_BLOCK;
+            }
+
+            /* дÈëÒ»¿é */
+            zftl_debug("    [zftl_write_block]  nand->write( -0x%0x-, -0x%0x-)\n", 
+                        ((firstBlock + block_new) << erasesize_shift), erasesize);
+     
+            if( zftl_write_block(nand, (loff_t)((firstBlock + block_new) << erasesize_shift), &retlen,
+                    oob, blockbuf) )
+            {
+                /* дÊý¾Ýʧ°Üʱ£¬±ê¼ÇBBT£¬¸üРTABLE£¬ÖØÐ²éÕÒеĿéд */
+                printk("                                                          -ERROR-\n");
+                zftl->blockTable[block_new] = BLOCK_BAD;
+                nand->_block_markbad(nand, (loff_t)((firstBlock + block_new) << erasesize_shift));               
+                goto find_next_free_block_1;
+            }
+                   
+            /* ¸üРblockRepTable¡¢blockTable */
+            zftl->blockRepTable[oob->logicBlockID] = block_new;
+
+			zftl_debug("    [zftl_write]  zftl->blockRepTable[oob->logicBlockID] = %d,oob->logicBlockID = %d\n", zftl->blockRepTable[oob->logicBlockID],oob->logicBlockID);
+			
+            zftl->blockTable[block_new] = BLOCK_USED;
+            zftl->blockTable[offset_block_phy] = BLOCK_DIRTY;
+
+             /* ¸üРblockRepTable¡¢blockTable */
+            if( is_upthreshold == 1 )
+            {
+                zftl_debug("    [zftl_write]  ( is_upthreshold == 1 )\n");
+                zftl_debug("    [zftl_write] eraseblock zftl->blockTable[-%d-]\n", offset_block_phy);
+                if( zftl_erase_block(nand, firstBlock + offset_block_phy) )
+                {   
+                    /* ²Á³ýʧ°Ü£¬±ê¼ÇΪ»µ¿ì */
+                    zftl_debug("    [zftl_write] eraseblock zftl->blockTable[-%d-]  ERROR!\n", 
+                                            (firstBlock + offset_block_phy) << erasesize);
+                    zftl->blockTable[offset_block_phy] = BLOCK_BAD;
+                    nand->_block_markbad(nand, (loff_t)((firstBlock + offset_block_phy) << erasesize_shift)); /* ±ê¼Ç»µ¿é£¬¸üлµ¿é±í */
+                }
+                else
+                    zftl->blockTable[offset_block_phy] = BLOCK_FREE;
+            }
+            zftl_debug("\n");
+                              
+            left_to_write  -= write_length;
+		    offset         += write_length;
+		    buffer         += write_length;
+        }
+        else /* zftl->blockRepTable[block_log] == BLOCK_NIL */
+        {
+            /* ´ËÂß¼­µØÖ·µÚÒ»´Î±»Ð´ÈëÊý¾Ý */
+            zftl_debug("    [zftl_write]  zftl->blockRepTable[%d] == BLOCK_NIL\n", block_log);         
+            
+			/* ²¢¸üÐÂOOB */
+            oob->logicBlockID = block_log;
+            oob->used = 0x1;
+            oob->version = 0x1;
+            memcpy(oob->head, CONFIG_ZFLT_HEAD, CONFIG_ZFLT_HEAD_BYTE);   /* ¿½±´zftlÍ· */ 
+
+            /* Ìî³ä¸üРdata */
+            if (left_to_write < (erasesize - block_offset))
+			    write_length = left_to_write;
+		    else
+			    write_length = nand->erasesize - block_offset;
+
+            memcpy(blockbuf + block_offset, buffer, write_length);
+            zftl_debug("    [zftl_write]  update the date ( -0x%0x-, -0x%0x- )\n",
+                                              block_offset, write_length);
+            
+            /* ѰÕÒÒ»¸ö FREE µÄÎïÀí¿é */
+            find_next_free_block_2:
+            if( zftl_find_free_block(zftl, &block_new) )
+            {
+                /* ûÓÐÕÒµ½FREE¿é */
+                printk("    [zftl_write] there is enough block, please enlarge the partition\n");
+                return CAN_NOT_FIND_FREE_BLOCK;
+            }
+
+            /* дÈëÒ»¿é */
+            zftl_debug("    [zftl_write_block]  nand->write( -0x%0x-, -0x%0x-)\n", 
+                        ((firstBlock + block_new) << erasesize_shift), erasesize);
+            
+            if( zftl_write_block(nand, (loff_t)((firstBlock + block_new) << erasesize_shift), &retlen,
+                    oob, blockbuf) )
+            {
+                /* дÊý¾Ýʧ°Üʱ£¬±ê¼ÇBBT£¬¸üРTABLE£¬ÖØÐ²éÕÒеĿéд */
+                printk("                                                         -ERROR-\n");
+                zftl->blockTable[block_new] = BLOCK_BAD;
+                nand->_block_markbad(nand, (loff_t)((firstBlock + block_new) << erasesize_shift));         
+                goto find_next_free_block_2;
+            }
+
+            /* ¸üРblockRepTable¡¢blockTable */
+            zftl->blockRepTable[oob->logicBlockID] = block_new;
+
+			zftl_debug("    [zftl_write2]  zftl->blockRepTable[oob->logicBlockID] = %d,oob->logicBlockID = %d\n", zftl->blockRepTable[oob->logicBlockID],oob->logicBlockID);
+			
+            zftl->blockTable[block_new] = BLOCK_USED;
+
+            zftl_debug("\n");
+            
+            left_to_write -= write_length;
+		    offset        += write_length;
+		    buffer        += write_length;
+        }
+        
+    }
+
+	/* Added by zhouqi for xxx, 2014/04/23 */
+	zftl_garbage_collect(zftl);
+    zftl_debug("\n");
+   
+	return 0;
+}
+
+
+int zftl_read(struct ZFTLrecord *zftl, uint32_t from, uint32_t len, u_char *buffer)
+{  
+    int ret = 0;
+    uint32_t offset_block_phy;
+    uint32_t left_to_read = len;
+    uint32_t read_length = 0;
+    uint32_t retlen = 0;
+    u_char *buf = buffer;
+    uint32_t offset = from;
+    uint32_t block_offset = 0; 
+    uint32_t block_log = 0;   
+    uint32_t erasesize = zftl->erasesize;
+    uint32_t erasesize_shift = zftl->erasesize_shift;
+    nand_info_t  *nand = zftl->nand;
+    uint32_t firstBlock = zftl->firstBlock;
+	
+	if((zftl == NULL) || (buf == NULL))
+	{
+		BUG();
+	}
+   
+    while( left_to_read > 0 )
+    {   
+        block_offset = offset & (zftl->erasesize - 1); 
+        block_log = offset >> zftl->erasesize_shift; 
+        
+        /* »ñÈ¡Âß¼­¿é¶ÔÓ¦µÄÎïÀí¿é */
+        if( zftl->blockRepTable[block_log] != BLOCK_NIL )
+            offset_block_phy = zftl->blockRepTable[block_log];
+        else 
+        {   
+            /* µ±Ã»ÓÐдÈëÊý¾Ýʱ£¬Ã»ÓÐÓ³Éä¹ØÏµ£¬¶ÁÈ¡Êý¾ÝȫΪ 0XFF */
+            
+            if (left_to_read < (erasesize - block_offset))
+			    read_length = left_to_read;
+		    else
+			    read_length = nand->erasesize - block_offset;
+            
+            memset(buf, 0xff, read_length);
+            
+            left_to_read -= read_length;
+		    offset       += read_length;
+		    buf          += read_length;
+
+            continue;
+            
+        }
+            
+        if (left_to_read < (erasesize - block_offset))
+			read_length = left_to_read;
+		else
+			read_length = nand->erasesize - block_offset;
+        
+
+        zftl_debug("    [zftl_read]  nand->read( -0x%0x-, -0x%0x-)\n", 
+                        ((firstBlock + offset_block_phy) << erasesize_shift) + block_offset,
+                         read_length);
+        ret = nand->_read(nand, ((firstBlock + offset_block_phy) << erasesize_shift) + block_offset, 
+                                read_length, &retlen, buf);
+        if (ret || retlen != read_length)
+        {
+                printk(KERN_ERR"[zfltl]: -zftl_read- error\n");
+		        //return -1;
+#ifdef CONFIG_MTD_NVRESUME
+    		    if((!zOss_NvInResume())&&(zftl==zftl_info[1])&&(zftl_res != 1))
+			    {
+					return -1;
+			    }
+#endif
+        }
+
+        left_to_read -= read_length;
+		offset       += read_length;
+		buf          += read_length;
+    }
+        
+    zftl_debug("\n");
+	return 0;
+}
+
+
+struct ZFTLrecord  * zftl_get_ZFTLrecord_byname(char* part_name)
+{
+    uint32_t i;
+
+    for(i = 0; zftl_partition_name[i] != NULL; i++ )
+    {
+        if(zftl_info[i]!=NULL)
+    	{
+			if(strcmp(zftl_info[i]->mbd.mtd->name, part_name) == 0)
+			{
+				return zftl_info[i];           
+			}
+    	}
+        
+    }
+    return NULL;
+}
+
+
+int zftl_init(unsigned char *part_name,struct ZFTLrecord *zftlinfo )
+{  
+    nand_info_t  *nand = mtd_fota;
+    //uint32_t i, j,k, block;
+    uint32_t k = 0;
+    struct ZFTLrecord *zftl = NULL;
+    uint32_t partition_size = 0;
+    uint32_t partition_offest = 0;
+    struct cmdline_mtd_partition *part;
+
+	printk( "zftl[%d] is %s\n",partnum,part_name);
+           
+     /* ZFTLrecord½á¹¹ÌåÄÚ´æÉêÇë */  
+    	zftl_info[partnum] = zftlinfo;
+    	if (!zftl_info[partnum]) 
+        {
+    		printk( "zftl: out of memory for data structure ZFTLrecord\n");
+    		return 1;
+    	}
+        zftl = zftl_info[partnum];
+        
+        zftl->nand = nand;
+        zftl->erasesize = nand->erasesize;    
+        zftl->writesize = nand->writesize;
+        zftl->erasesize_shift = ffs(nand->erasesize) - 1;  
+        zftl->writesize_shift = ffs(nand->writesize) - 1;
+        zftl->oobsize = nand->oobsize;
+        
+        part = partitions;
+
+	    for(k=0;k<part->num_parts;k++)
+	    {
+	        if ( strcmp( (char *)part->parts[k].name, part_name ) == 0 )
+            break;
+
+	    }    /* nvr */
+            
+        partition_offest = part->parts[k].offset;
+        partition_size = part->parts[k].size;
+        
+		if ( strcmp(part_name,"cdrom") == 0 )
+		{
+			cdrom_partsize = partition_size;
+		}
+
+		if ( strcmp(part_name,"nvro") == 0 )
+		{
+			nvro_partsize = partition_size;
+            printk("nvro_partsize=0x%0x\n",nvro_partsize);
+		}
+
+		if ( strcmp(part_name,"nvrwo") == 0 )
+		{
+			nvrwo_partsize = partition_size;
+            printk("nvrwo_partsize=0x%0x\n",nvrwo_partsize);
+		}
+		
+        zftl->firstBlock= partition_offest >> zftl->erasesize_shift;     /* ¾ßÌåµÄÊýÖµ´Ó·ÖÇøÖлñµÃ */
+        zftl->numBlocks = partition_size / zftl->erasesize;             /* ÔõôȷÈÏÇøÓò·¶Î§ zhouqi,ʵ¼ÊʹÓÃʱ¿éºÅÒª -1  ?*/
+
+        /* ¿é»º´æÉêÇë */
+        zftl->blockbuf = kmalloc(zftl->erasesize, GFP_KERNEL);
+    	if (!zftl->blockbuf) 
+        {
+    		printk( "zftl: out of memory for data structure blockbuf\n");
+    		goto failed_alloc_blockbuf;
+    	}
+
+        /* OOB»º´æÉêÇë */
+        zftl->oobbuf = kmalloc(zftl->oobsize, GFP_KERNEL);
+    	if (!zftl->oobbuf) 
+        {
+    		printk( "zftl: out of memory for data oobbuf\n");
+    		goto failed_alloc_oobbuf;
+    	}
+
+        /* blockRepTable±íÄÚ´æÉêÇë, ³õʼ»¯Îª 0xFFFF*/    
+        zftl->blockRepTable = kmalloc(zftl->numBlocks * sizeof(zftl->blockRepTable), GFP_KERNEL);
+    	if (!zftl->blockRepTable) 
+        {
+    		printk( "zftl: out of memory for data structure blockRepTable\n");
+    		goto failed_alloc_blockreptable;
+    	}
+        memset(zftl->blockRepTable, 0xff, zftl->numBlocks * sizeof(zftl->blockRepTable));
+
+        /* versionTable±íÄÚ´æÉêÇë */  
+        /* ³õʼ»¯Îª¶¼Îª0£¬NANDÖеÄÊý¾Ýversion×îСΪ 1 */ 
+        zftl->versionTable = kzalloc(zftl->numBlocks * sizeof(zftl->versionTable), GFP_KERNEL);
+    	if (!zftl->versionTable) 
+        {
+    		printk( "zftl: out of memory for data structure versionTable\n");
+    		goto failed_alloc_versionTable;
+    	}
+
+        /* blockTable±íÄÚ´æÉêÇë */    
+        zftl->blockTable = kzalloc(zftl->numBlocks * sizeof(zftl->blockTable), GFP_KERNEL);
+    	if (!zftl->blockTable) 
+        {
+    		printk( "zftl: out of memory for data structure blockTable\n");
+    		goto failed_alloc_blocktable;
+    	}
+        
+        /* ·ÖÎö¹ÒÔØzftl·ÖÇø£¬½¨Á¢ÄÚ´æ±í */
+        if ( zftl_setup_table((zftl), &zftl->is_writed))
+        {
+    		printk( "zftl: could not mount device\n");
+    		goto failed_setup_table;
+        }
+  
+        zftl_debug("    [zftl_init] is_writed[%d] = %d\n", partnum, zftl->is_writed);
+    partnum++;
+		if(!mutex_inited)
+		{
+			mutex_init(&zftl_mutex);
+			mutex_inited = 1;
+		}
+    return 0;
+    
+failed_setup_table:
+    kfree(zftl->blockTable);  
+failed_alloc_blocktable:
+    kfree(zftl->versionTable);
+failed_alloc_versionTable:
+    kfree(zftl->blockRepTable);
+failed_alloc_blockreptable:
+    kfree(zftl->oobbuf); 
+failed_alloc_oobbuf:
+    kfree(zftl->blockbuf); 
+failed_alloc_blockbuf:
+    kfree(zftl);    
+    return 1;   
+}
+
+static void zftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
+{
+	uint32_t i;
+    struct ZFTLrecord *zftl = NULL;
+	
+	for(i=0; zftl_partition_name[i] != NULL; i++)
+	{
+		
+		if(strcmp(mtd->name, zftl_partition_name[i]) == 0)
+		{
+
+			break;
+		}	
+	}
+
+	if(zftl_partition_name[i] == NULL)
+	{
+			return;
+	}
+	
+	zftl = kzalloc(sizeof(struct ZFTLrecord), GFP_KERNEL);
+    if (!zftl)
+	{
+	   kfree(zftl);
+	   return;
+	}
+	zftl->mbd.mtd = mtd;
+	zftl->mbd.devnum = mtd->index;
+	zftl->mbd.tr = tr;
+	zftl->mbd.size = mtd->size >> 12;
+    
+	zftl_init(mtd->name,zftl);
+
+	if (add_mtd_blktrans_dev(&zftl->mbd))
+	{
+		kfree(zftl);
+	}
+		
+	return;
+}
+static void zftl_remove_dev(struct mtd_blktrans_dev *dev)
+{
+	del_mtd_blktrans_dev(dev);
+}
+static int zftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
+			  char *buffer)
+{
+    struct ZFTLrecord *zftl = NULL;
+	int ret = 0;
+
+	mutex_lock(&zftl_mutex);
+
+    zftl = zftl_get_ZFTLrecord_byname(mbd->mtd->name);
+	if(!zftl)
+	{	
+		mutex_unlock(&zftl_mutex);
+		return -2;
+	}
+    ret = zftl_read(zftl, block<<12, (uint32_t)4096, (u_char *)buffer);
+	mutex_unlock(&zftl_mutex);
+	return ret;
+}
+
+static int zftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
+			  char *buffer)
+{
+     struct ZFTLrecord *zftl = NULL;
+	 int ret = 0;
+
+	 mutex_lock(&zftl_mutex);
+
+     zftl = zftl_get_ZFTLrecord_byname(mbd->mtd->name);
+	if(!zftl)
+	{	
+		mutex_unlock(&zftl_mutex);
+		return -2;
+	}
+     ret = zftl_write(zftl, block<<12, (uint32_t)4096, (u_char *)buffer);
+	 mutex_unlock(&zftl_mutex);
+	 return ret; 
+}
+
+int zftl_wrapper_write(char *part_name, int dwStart, int dwLen, char* from)
+{
+	int ret = 0;
+	struct ZFTLrecord *zftl = NULL;
+
+	mutex_lock(&zftl_mutex);
+
+	if((part_name == NULL) || (from == NULL))
+	{
+		mutex_unlock(&zftl_mutex);
+		return -5;
+	}
+
+	zftl = zftl_get_ZFTLrecord_byname(part_name);
+	if(zftl == NULL)
+	{	
+		BUG();
+		mutex_unlock(&zftl_mutex);
+		return -1;	//×ö¶ÏÑÔ
+	}
+
+	ret = zftl_write(zftl, dwStart, dwLen, from);
+	if(ret != 0)
+	{
+		mutex_unlock(&zftl_mutex);
+		return ret;
+	}
+
+	mutex_unlock(&zftl_mutex);
+	
+    return 0;  
+}
+EXPORT_SYMBOL_GPL(zftl_wrapper_write);
+
+int zftl_wrapper_read(char *part_name, int dwStart, int dwLen, char* to)
+{
+	int ret = 0;
+    struct ZFTLrecord *zftl = NULL;
+
+	mutex_lock(&zftl_mutex);
+
+	if((part_name == NULL) || (to == NULL))
+	{
+		mutex_unlock(&zftl_mutex);
+		return -5;
+	}
+
+	zftl = zftl_get_ZFTLrecord_byname(part_name);
+	if(zftl == NULL)
+
+	{
+		BUG();
+		mutex_unlock(&zftl_mutex);
+		return -1; //×ö¶ÏÑÔ
+	}
+
+	ret = zftl_read(zftl, dwStart, dwLen, to);
+	if(ret != 0)
+	{
+		mutex_unlock(&zftl_mutex);
+		return ret;
+	}
+
+	mutex_unlock(&zftl_mutex);
+    return 0; 
+}
+EXPORT_SYMBOL_GPL(zftl_wrapper_read);
+
+int nand_NvRead(int dwStart, int dwLen, char* to)
+{
+	mutex_lock(&zftl_mutex);
+    struct ZFTLrecord *zftl = NULL;
+	int ret = 0;
+
+	if( dwStart < nvro_partsize )
+    {
+        zftl = zftl_get_ZFTLrecord_byname("nvro");    /*NVR,0~1M*/      
+    }
+	else if( dwStart < nvro_partsize + nvrwo_partsize)
+	{
+		zftl = zftl_get_ZFTLrecord_byname("nvrwo");    /*NVR,0~1M*/ 
+		dwStart -= nvro_partsize;
+	}
+    else 
+    {
+        zftl = zftl_get_ZFTLrecord_byname("nvrw");    /* NVRW, 1~5M*/ 
+        dwStart = dwStart - nvro_partsize - nvrwo_partsize;
+    }
+	if(!zftl)
+	{	
+		mutex_unlock(&zftl_mutex);
+		return -2;
+	}
+	ret = zftl_read(zftl, (uint32_t)dwStart, (uint32_t)dwLen, (u_char*)to);
+	mutex_unlock(&zftl_mutex);
+
+	return ret;
+
+}
+EXPORT_SYMBOL_GPL(nand_NvRead);
+
+int nand_NvProgram(int dwStart, int dwLen, char* from)
+{
+   	mutex_lock(&zftl_mutex);
+    struct ZFTLrecord *zftl = NULL;
+	int ret = 0;
+
+    if( dwStart < nvro_partsize )
+    {
+        zftl = zftl_get_ZFTLrecord_byname("nvro");    /*NVR,0~1M*/      
+    }
+	else if( dwStart < nvro_partsize + nvrwo_partsize)
+	{
+		zftl = zftl_get_ZFTLrecord_byname("nvrwo");    /*NVR,0~1M*/  
+		dwStart -= nvro_partsize;
+	}
+    else 
+    {
+        zftl = zftl_get_ZFTLrecord_byname("nvrw");    /* NVRW, 1~5M*/ 
+        dwStart = dwStart - nvro_partsize - nvrwo_partsize;
+    }
+	if(!zftl)
+	{	
+		mutex_unlock(&zftl_mutex);
+		return -2;
+	}
+    ret = zftl_write(zftl, (uint32_t)dwStart, (uint32_t)dwLen, (u_char*)from);
+	mutex_unlock(&zftl_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(nand_NvProgram);
+
+int nand_CdromRead(void *devData,void *buffer, uint32_t blkcnt, uint32_t pos )
+{
+	mutex_lock(&zftl_mutex);
+    struct ZFTLrecord *zftl = NULL;
+	int ret = 0;
+
+    zftl = zftl_get_ZFTLrecord_byname("cdrom");
+	if(!zftl)
+	{	
+		mutex_unlock(&zftl_mutex);
+		return -2;
+	}   
+	ret = zftl_read(zftl,  (pos<<9), (uint32_t)(blkcnt<<9), (u_char *)buffer);
+	if(ret == 0)
+	{
+			ret = blkcnt;
+	}
+	mutex_unlock(&zftl_mutex);
+	return ret;
+
+}
+EXPORT_SYMBOL_GPL(nand_CdromRead);
+
+int nand_CdromProgram(void *devData,void *buffer, uint32_t blkcnt, uint32_t pos)
+{
+   	mutex_lock(&zftl_mutex);
+   	struct ZFTLrecord *zftl = NULL;
+	int ret = 0;	
+
+    zftl = zftl_get_ZFTLrecord_byname("cdrom");
+	if(!zftl)
+	{	
+		mutex_unlock(&zftl_mutex);
+		return -2;
+	}
+    ret = zftl_write(zftl,  (pos<<9), (uint32_t)(blkcnt<<9), (u_char *)buffer);
+    
+    if(ret == 0)
+		{
+				ret = blkcnt;
+		}
+	mutex_unlock(&zftl_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(nand_CdromProgram);
+int nand_SmsRead(int dwStart, int dwLen, char* to)
+{
+	mutex_lock(&zftl_mutex);
+    struct ZFTLrecord *zftl = NULL;
+	int ret = 0;
+
+	zftl = zftl_get_ZFTLrecord_byname("sms");
+	if(!zftl)
+	{	
+		mutex_unlock(&zftl_mutex);
+		return -2;
+	}
+	ret = zftl_read(zftl, (uint32_t)dwStart, (uint32_t)dwLen, (u_char*)to);
+	mutex_unlock(&zftl_mutex);
+	return ret;
+
+}
+EXPORT_SYMBOL_GPL(nand_SmsRead);
+
+int nand_SmsProgram(int dwStart, int dwLen, char* from)
+{
+   	mutex_lock(&zftl_mutex);
+   	struct ZFTLrecord *zftl = NULL;
+	int ret = 0;	
+
+	zftl = zftl_get_ZFTLrecord_byname("sms");
+	if(!zftl)
+	{	
+		mutex_unlock(&zftl_mutex);
+		return -2;
+	}
+    ret = zftl_write(zftl, (uint32_t)dwStart, (uint32_t)dwLen, (u_char*)from);
+	mutex_unlock(&zftl_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(nand_SmsProgram);
+
+int nand_ResetFlagProgram(int dwStart, int dwLen, char* from)
+{
+   	mutex_lock(&zftl_mutex);
+    struct ZFTLrecord *zftl = NULL;
+	int ret = 0;
+	
+           
+    //zftl = zftl_info[2];               /* fotaflag */   
+    zftl = zftl_get_ZFTLrecord_byname("fotaflag");
+	if(!zftl)
+	{	
+		mutex_unlock(&zftl_mutex);
+		return -2;
+	}
+    ret = zftl_write(zftl, (uint32_t)dwStart, (uint32_t)dwLen, (u_char*)from);
+	mutex_unlock(&zftl_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(nand_ResetFlagProgram);
+
+static struct mtd_blktrans_ops zftl_tr = {
+	.name		= "zftl",
+	.major		= 35,
+	.part_bits	= 0,
+	.blksize 	= 4096,
+	.readsect	= zftl_readblock,
+	.writesect	= zftl_writeblock,
+	.add_mtd	= zftl_add_mtd,
+	.remove_dev	= zftl_remove_dev,
+	.owner		= THIS_MODULE,
+};
+
+static int __init init_zftl(void)
+{
+	return register_mtd_blktrans(&zftl_tr);
+}
+
+static void __exit cleanup_zftl(void)
+{
+	deregister_mtd_blktrans(&zftl_tr);
+}
+
+module_init(init_zftl);
+module_exit(cleanup_zftl);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
+MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");
+
+
+
+
+
+
+