⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nftlcore.c

📁 linux下的MTD设备驱动源代码,配合jffs2 yaffss2文件系统.
💻 C
📖 第 1 页 / 共 2 页
字号:
/* Linux driver for NAND Flash Translation Layer      *//* (c) 1999 Machine Vision Holdings, Inc.             *//* Author: David Woodhouse <dwmw2@infradead.org>      *//* $Id: nftlcore.c,v 1.94 2003/06/23 12:00:08 dwmw2 Exp $ *//*  The contents of this file are distributed under the GNU General  Public License version 2. The author places no additional  restrictions of any kind on it. */#define PRERELEASE#include <linux/config.h>#include <linux/kernel.h>#include <linux/module.h>#include <asm/errno.h>#include <asm/io.h>#include <asm/uaccess.h>#include <linux/miscdevice.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/slab.h>#include <linux/sched.h>#include <linux/init.h>#include <linux/hdreg.h>#include <linux/kmod.h>#include <linux/mtd/mtd.h>#include <linux/mtd/nand.h>#include <linux/mtd/nftl.h>#include <linux/mtd/blktrans.h>/* maximum number of loops while examining next block, to have a   chance to detect consistency problems (they should never happen   because of the checks done in the mounting */#define MAX_LOOPS 10000static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd){	struct NFTLrecord *nftl;	unsigned long temp;	if (mtd->ecctype != MTD_ECC_RS_DiskOnChip)		return;	DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name);	nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL);	if (!nftl) {		printk(KERN_WARNING "NFTL: out of memory for data structures\n");		return;	}	memset(nftl, 0, sizeof(*nftl));	nftl->mbd.mtd = mtd;	nftl->mbd.devnum = -1;	nftl->mbd.blksize = 512;	nftl->mbd.tr = tr;        if (NFTL_mount(nftl) < 0) {		printk(KERN_WARNING "NFTL: could not mount device\n");		kfree(nftl);		return;        }	/* OK, it's a new one. Set up all the data structures. */	/* Calculate geometry */	nftl->cylinders = 1024;	nftl->heads = 16;	temp = nftl->cylinders * nftl->heads;	nftl->sectors = nftl->mbd.size / temp;	if (nftl->mbd.size % temp) {		nftl->sectors++;		temp = nftl->cylinders * nftl->sectors;		nftl->heads = nftl->mbd.size / temp;		if (nftl->mbd.size % temp) {			nftl->heads++;			temp = nftl->heads * nftl->sectors;			nftl->cylinders = nftl->mbd.size / temp;		}	}	if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) {		/*		  Oh no we don't have 		   mbd.size == heads * cylinders * sectors		*/		printk(KERN_WARNING "NFTL: cannot calculate a geometry to "		       "match size of 0x%lx.\n", nftl->mbd.size);		printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d "			"(== 0x%lx sects)\n",			nftl->cylinders, nftl->heads , nftl->sectors, 			(long)nftl->cylinders * (long)nftl->heads *			(long)nftl->sectors );	}	if (add_mtd_blktrans_dev(&nftl->mbd)) {		if (nftl->ReplUnitTable)			kfree(nftl->ReplUnitTable);		if (nftl->EUNtable)			kfree(nftl->EUNtable);		kfree(nftl);		return;	}#ifdef PSYCHO_DEBUG	printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');#endif}static void nftl_remove_dev(struct mtd_blktrans_dev *dev){	struct NFTLrecord *nftl = (void *)dev;	DEBUG(MTD_DEBUG_LEVEL1, "NFTL: remove_dev (i=%d)\n", dev->devnum);	del_mtd_blktrans_dev(dev);	if (nftl->ReplUnitTable)		kfree(nftl->ReplUnitTable);	if (nftl->EUNtable)		kfree(nftl->EUNtable);	kfree(nftl);}#ifdef CONFIG_NFTL_RW/* Actual NFTL access routines *//* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used *	when the give Virtual Unit Chain */static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate ){	/* For a given Virtual Unit Chain: find or create a free block and	   add it to the chain */	/* We're passed the number of the last EUN in the chain, to save us from	   having to look it up again */	u16 pot = nftl->LastFreeEUN;	int silly = nftl->nb_blocks;	/* Normally, we force a fold to happen before we run out of free blocks completely */	if (!desperate && nftl->numfreeEUNs < 2) {		DEBUG(MTD_DEBUG_LEVEL1, "NFTL_findfreeblock: there are too few free EUNs\n");		return 0xffff;	}	/* Scan for a free block */	do {		if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {			nftl->LastFreeEUN = pot;			nftl->numfreeEUNs--;			return pot;		}		/* This will probably point to the MediaHdr unit itself,		   right at the beginning of the partition. But that unit		   (and the backup unit too) should have the UCI set		   up so that it's not selected for overwriting */		if (++pot > nftl->lastEUN)			pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);		if (!silly--) {			printk("Argh! No free blocks found! LastFreeEUN = %d, "			       "FirstEUN = %d\n", nftl->LastFreeEUN, 			       le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));			return 0xffff;		}	} while (pot != nftl->LastFreeEUN);	return 0xffff;}static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock ){	u16 BlockMap[MAX_SECTORS_PER_UNIT];	unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];	unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];	unsigned int thisEUN;	int block;	int silly;	unsigned int targetEUN;	struct nftl_oob oob;	int inplace = 1;        size_t retlen;	memset(BlockMap, 0xff, sizeof(BlockMap));	memset(BlockFreeFound, 0, sizeof(BlockFreeFound));	thisEUN = nftl->EUNtable[thisVUC];	if (thisEUN == BLOCK_NIL) {		printk(KERN_WARNING "Trying to fold non-existent "		       "Virtual Unit Chain %d!\n", thisVUC);		return BLOCK_NIL;	}		/* Scan to find the Erase Unit which holds the actual data for each	   512-byte block within the Chain.	*/        silly = MAX_LOOPS;	targetEUN = BLOCK_NIL;	while (thisEUN <= nftl->lastEUN ) {                unsigned int status, foldmark;		targetEUN = thisEUN;		for (block = 0; block < nftl->EraseSize / 512; block ++) {			MTD_READOOB(nftl->mbd.mtd,				    (thisEUN * nftl->EraseSize) + (block * 512),				    16 , &retlen, (char *)&oob);			if (block == 2) {                                foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;                                if (foldmark == FOLD_MARK_IN_PROGRESS) {                                        DEBUG(MTD_DEBUG_LEVEL1,                                               "Write Inhibited on EUN %d\n", thisEUN);					inplace = 0;				} else {					/* There's no other reason not to do inplace,					   except ones that come later. So we don't need					   to preserve inplace */					inplace = 1;				}			}                        status = oob.b.Status | oob.b.Status1;			BlockLastState[block] = status;			switch(status) {			case SECTOR_FREE:				BlockFreeFound[block] = 1;				break;			case SECTOR_USED:				if (!BlockFreeFound[block])					BlockMap[block] = thisEUN;				else					printk(KERN_WARNING 					       "SECTOR_USED found after SECTOR_FREE "					       "in Virtual Unit Chain %d for block %d\n",					       thisVUC, block);				break;			case SECTOR_DELETED:				if (!BlockFreeFound[block])					BlockMap[block] = BLOCK_NIL;				else					printk(KERN_WARNING 					       "SECTOR_DELETED found after SECTOR_FREE "					       "in Virtual Unit Chain %d for block %d\n",					       thisVUC, block);				break;			case SECTOR_IGNORE:				break;			default:				printk("Unknown status for block %d in EUN %d: %x\n",				       block, thisEUN, status);			}		}		if (!silly--) {			printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",			       thisVUC);			return BLOCK_NIL;		}				thisEUN = nftl->ReplUnitTable[thisEUN];	}	if (inplace) {		/* We're being asked to be a fold-in-place. Check		   that all blocks which actually have data associated		   with them (i.e. BlockMap[block] != BLOCK_NIL) are 		   either already present or SECTOR_FREE in the target		   block. If not, we're going to have to fold out-of-place		   anyway.		*/		for (block = 0; block < nftl->EraseSize / 512 ; block++) {			if (BlockLastState[block] != SECTOR_FREE &&			    BlockMap[block] != BLOCK_NIL &&			    BlockMap[block] != targetEUN) {				DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, "				      "block %d was %x lastEUN, "				      "and is in EUN %d (%s) %d\n",				      thisVUC, block, BlockLastState[block],				      BlockMap[block], 				      BlockMap[block]== targetEUN ? "==" : "!=",				      targetEUN);				inplace = 0;				break;			}		}		if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&		    pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&		    BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=		    SECTOR_FREE) {			DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. "			      "Folding out of place.\n", targetEUN);			inplace = 0;		}	}		if (!inplace) {		DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. "		      "Trying out-of-place\n", thisVUC);		/* We need to find a targetEUN to fold into. */		targetEUN = NFTL_findfreeblock(nftl, 1);		if (targetEUN == BLOCK_NIL) {			/* Ouch. Now we're screwed. We need to do a 			   fold-in-place of another chain to make room			   for this one. We need a better way of selecting			   which chain to fold, because makefreeblock will 			   only ask us to fold the same one again.			*/			printk(KERN_WARNING			       "NFTL_findfreeblock(desperate) returns 0xffff.\n");			return BLOCK_NIL;		}	} else {            /* We put a fold mark in the chain we are folding only if               we fold in place to help the mount check code. If we do               not fold in place, it is possible to find the valid               chain by selecting the longer one */            oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);            oob.u.c.unused = 0xffffffff;            MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,                          8, &retlen, (char *)&oob.u);        }	/* OK. We now know the location of every block in the Virtual Unit Chain,	   and the Erase Unit into which we are supposed to be copying.	   Go for it.	*/	DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);	for (block = 0; block < nftl->EraseSize / 512 ; block++) {		unsigned char movebuf[512];		int ret;		/* If it's in the target EUN already, or if it's pending write, do nothing */		if (BlockMap[block] == targetEUN ||		    (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {			continue;		}                /* copy only in non free block (free blocks can only                   happen in case of media errors or deleted blocks) */                if (BlockMap[block] == BLOCK_NIL)                        continue;                                ret = MTD_READECC(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),				  512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP);                 if (ret < 0) {                    ret = MTD_READECC(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block])                                      + (block * 512), 512, &retlen,                                      movebuf, (char *)&oob, NAND_ECC_DISKONCHIP);                     if (ret != -EIO)                         printk("Error went away on retry.\n");                }                MTD_WRITEECC(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + (block * 512),                             512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP);	}                /* add the header so that it is now a valid chain */        oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum                = cpu_to_le16(thisVUC);        oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;                MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 8,                      8, &retlen, (char *)&oob.u);	/* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */	/* At this point, we have two different chains for this Virtual Unit, and no way to tell 	   them apart. If we crash now, we get confused. However, both contain the same data, so we

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -