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

📄 inftlcore.c

📁 基于linux-2.6.28的mtd驱动
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL) * * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) * * Based heavily on the nftlcore.c code which is: * (c) 1999 Machine Vision Holdings, Inc. * Author: David Woodhouse <dwmw2@infradead.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */#include <linux/kernel.h>#include <linux/module.h>#include <linux/delay.h>#include <linux/slab.h>#include <linux/sched.h>#include <linux/init.h>#include <linux/kmod.h>#include <linux/hdreg.h>#include <linux/mtd/mtd.h>#include <linux/mtd/nftl.h>#include <linux/mtd/inftl.h>#include <linux/mtd/nand.h>#include <asm/uaccess.h>#include <asm/errno.h>#include <asm/io.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 inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd){	struct INFTLrecord *inftl;	unsigned long temp;	if (mtd->type != MTD_NANDFLASH)		return;	/* OK, this is moderately ugly.  But probably safe.  Alternatives? */	if (memcmp(mtd->name, "DiskOnChip", 10))		return;	if (!mtd->block_isbad) {		printk(KERN_ERR"INFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n""Please use the new diskonchip driver under the NAND subsystem.\n");		return;	}	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name);	inftl = kzalloc(sizeof(*inftl), GFP_KERNEL);	if (!inftl) {		printk(KERN_WARNING "INFTL: Out of memory for data structures\n");		return;	}	inftl->mbd.mtd = mtd;	inftl->mbd.devnum = -1;	inftl->mbd.tr = tr;	if (INFTL_mount(inftl) < 0) {		printk(KERN_WARNING "INFTL: could not mount device\n");		kfree(inftl);		return;	}	/* OK, it's a new one. Set up all the data structures. */	/* Calculate geometry */	inftl->cylinders = 1024;	inftl->heads = 16;	temp = inftl->cylinders * inftl->heads;	inftl->sectors = inftl->mbd.size / temp;	if (inftl->mbd.size % temp) {		inftl->sectors++;		temp = inftl->cylinders * inftl->sectors;		inftl->heads = inftl->mbd.size / temp;		if (inftl->mbd.size % temp) {			inftl->heads++;			temp = inftl->heads * inftl->sectors;			inftl->cylinders = inftl->mbd.size / temp;		}	}	if (inftl->mbd.size != inftl->heads * inftl->cylinders * inftl->sectors) {		/*		  Oh no we don't have		   mbd.size == heads * cylinders * sectors		*/		printk(KERN_WARNING "INFTL: cannot calculate a geometry to "		       "match size of 0x%lx.\n", inftl->mbd.size);		printk(KERN_WARNING "INFTL: using C:%d H:%d S:%d "			"(== 0x%lx sects)\n",			inftl->cylinders, inftl->heads , inftl->sectors,			(long)inftl->cylinders * (long)inftl->heads *			(long)inftl->sectors );	}	if (add_mtd_blktrans_dev(&inftl->mbd)) {		kfree(inftl->PUtable);		kfree(inftl->VUtable);		kfree(inftl);		return;	}#ifdef PSYCHO_DEBUG	printk(KERN_INFO "INFTL: Found new inftl%c\n", inftl->mbd.devnum + 'a');#endif	return;}static void inftl_remove_dev(struct mtd_blktrans_dev *dev){	struct INFTLrecord *inftl = (void *)dev;	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: remove_dev (i=%d)\n", dev->devnum);	del_mtd_blktrans_dev(dev);	kfree(inftl->PUtable);	kfree(inftl->VUtable);	kfree(inftl);}/* * Actual INFTL access routines. *//* * Read oob data from flash */int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,		   size_t *retlen, uint8_t *buf){	struct mtd_oob_ops ops;	int res;	ops.mode = MTD_OOB_PLACE;	ops.ooboffs = offs & (mtd->writesize - 1);	ops.ooblen = len;	ops.oobbuf = buf;	ops.datbuf = NULL;	res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);	*retlen = ops.oobretlen;	return res;}/* * Write oob data to flash */int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,		    size_t *retlen, uint8_t *buf){	struct mtd_oob_ops ops;	int res;	ops.mode = MTD_OOB_PLACE;	ops.ooboffs = offs & (mtd->writesize - 1);	ops.ooblen = len;	ops.oobbuf = buf;	ops.datbuf = NULL;	res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);	*retlen = ops.oobretlen;	return res;}/* * Write data and oob to flash */static int inftl_write(struct mtd_info *mtd, loff_t offs, size_t len,		       size_t *retlen, uint8_t *buf, uint8_t *oob){	struct mtd_oob_ops ops;	int res;	ops.mode = MTD_OOB_PLACE;	ops.ooboffs = offs;	ops.ooblen = mtd->oobsize;	ops.oobbuf = oob;	ops.datbuf = buf;	ops.len = len;	res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);	*retlen = ops.retlen;	return res;}/* * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition. *	This function is used when the give Virtual Unit Chain. */static u16 INFTL_findfreeblock(struct INFTLrecord *inftl, int desperate){	u16 pot = inftl->LastFreeEUN;	int silly = inftl->nb_blocks;	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findfreeblock(inftl=%p,"		"desperate=%d)\n", inftl, desperate);	/*	 * Normally, we force a fold to happen before we run out of free	 * blocks completely.	 */	if (!desperate && inftl->numfreeEUNs < 2) {		DEBUG(MTD_DEBUG_LEVEL1, "INFTL: there are too few free "			"EUNs (%d)\n", inftl->numfreeEUNs);		return 0xffff;	}	/* Scan for a free block */	do {		if (inftl->PUtable[pot] == BLOCK_FREE) {			inftl->LastFreeEUN = pot;			return pot;		}		if (++pot > inftl->lastEUN)			pot = 0;		if (!silly--) {			printk(KERN_WARNING "INFTL: no free blocks found!  "				"EUN range = %d - %d\n", 0, inftl->LastFreeEUN);			return BLOCK_NIL;		}	} while (pot != inftl->LastFreeEUN);	return BLOCK_NIL;}static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock){	u16 BlockMap[MAX_SECTORS_PER_UNIT];	unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];	unsigned int thisEUN, prevEUN, status;	struct mtd_info *mtd = inftl->mbd.mtd;	int block, silly;	unsigned int targetEUN;	struct inftl_oob oob;	size_t retlen;	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d,"		"pending=%d)\n", inftl, thisVUC, pendingblock);	memset(BlockMap, 0xff, sizeof(BlockMap));	memset(BlockDeleted, 0, sizeof(BlockDeleted));	thisEUN = targetEUN = inftl->VUtable[thisVUC];	if (thisEUN == BLOCK_NIL) {		printk(KERN_WARNING "INFTL: 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;	while (thisEUN < inftl->nb_blocks) {		for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) {			if ((BlockMap[block] != 0xffff) || BlockDeleted[block])				continue;			if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)					   + (block * SECTORSIZE), 16, &retlen,					   (char *)&oob) < 0)				status = SECTOR_IGNORE;			else				status = oob.b.Status | oob.b.Status1;			switch(status) {			case SECTOR_FREE:			case SECTOR_IGNORE:				break;			case SECTOR_USED:				BlockMap[block] = thisEUN;				continue;			case SECTOR_DELETED:				BlockDeleted[block] = 1;				continue;			default:				printk(KERN_WARNING "INFTL: unknown status "					"for block %d in EUN %d: %x\n",					block, thisEUN, status);				break;			}		}		if (!silly--) {			printk(KERN_WARNING "INFTL: infinite loop in Virtual "				"Unit Chain 0x%x\n", thisVUC);			return BLOCK_NIL;		}		thisEUN = inftl->PUtable[thisEUN];	}	/*	 * 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, "INFTL: folding chain %d into unit %d\n",		thisVUC, targetEUN);	for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) {		unsigned char movebuf[SECTORSIZE];		int ret;		/*		 * If it's in the target EUN already, or if it's pending write,		 * do nothing.		 */		if (BlockMap[block] == targetEUN || (pendingblock ==		    (thisVUC * (inftl->EraseSize / SECTORSIZE) + 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->read(mtd, (inftl->EraseSize * BlockMap[block]) +				(block * SECTORSIZE), SECTORSIZE, &retlen,				movebuf);		if (ret < 0 && ret != -EUCLEAN) {			ret = mtd->read(mtd,					(inftl->EraseSize * BlockMap[block]) +					(block * SECTORSIZE), SECTORSIZE,					&retlen, movebuf);			if (ret != -EIO)				DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "				      "away on retry?\n");		}		memset(&oob, 0xff, sizeof(struct inftl_oob));		oob.b.Status = oob.b.Status1 = SECTOR_USED;		inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +			    (block * SECTORSIZE), SECTORSIZE, &retlen,			    movebuf, (char *)&oob);	}	/*	 * Newest unit in chain now contains data from _all_ older units.	 * So go through and erase each unit in chain, oldest first. (This	 * is important, by doing oldest first if we crash/reboot then it	 * it is relatively simple to clean up the mess).	 */	DEBUG(MTD_DEBUG_LEVEL1, "INFTL: want to erase virtual chain %d\n",		thisVUC);	for (;;) {		/* Find oldest unit in chain. */		thisEUN = inftl->VUtable[thisVUC];		prevEUN = BLOCK_NIL;		while (inftl->PUtable[thisEUN] != BLOCK_NIL) {			prevEUN = thisEUN;			thisEUN = inftl->PUtable[thisEUN];		}		/* Check if we are all done */		if (thisEUN == targetEUN)			break;		/* Unlink the last block from the chain. */		inftl->PUtable[prevEUN] = BLOCK_NIL;		/* Now try to erase it. */		if (INFTL_formatblock(inftl, thisEUN) < 0) {			/*			 * Could not erase : mark block as reserved.			 */			inftl->PUtable[thisEUN] = BLOCK_RESERVED;		} else {			/* Correctly erased : mark it as free */			inftl->PUtable[thisEUN] = BLOCK_FREE;			inftl->numfreeEUNs++;		}	}	return targetEUN;}static u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock){	/*	 * This is the part that needs some cleverness applied.	 * For now, I'm doing the minimum applicable to actually	 * get the thing to work.	 * Wear-levelling and other clever stuff needs to be implemented	 * and we also need to do some assessment of the results when	 * the system loses power half-way through the routine.	 */	u16 LongestChain = 0;	u16 ChainLength = 0, thislen;	u16 chain, EUN;	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_makefreeblock(inftl=%p,"		"pending=%d)\n", inftl, pendingblock);	for (chain = 0; chain < inftl->nb_blocks; chain++) {		EUN = inftl->VUtable[chain];		thislen = 0;		while (EUN <= inftl->lastEUN) {			thislen++;			EUN = inftl->PUtable[EUN];			if (thislen > 0xff00) {				printk(KERN_WARNING "INFTL: endless loop in "					"Virtual Chain %d: Unit %x\n",					chain, EUN);				/*				 * Actually, don't return failure.				 * Just ignore this chain and get on with it.				 */				thislen = 0;				break;			}		}		if (thislen > ChainLength) {			ChainLength = thislen;			LongestChain = chain;		}	}	if (ChainLength < 2) {		printk(KERN_WARNING "INFTL: no Virtual Unit Chains available "			"for folding. Failing request\n");		return BLOCK_NIL;	}	return INFTL_foldchain(inftl, LongestChain, pendingblock);}static int nrbits(unsigned int val, int bitcount){	int i, total = 0;	for (i = 0; (i < bitcount); i++)		total += (((0x1 << i) & val) ? 1 : 0);	return total;}/* * INFTL_findwriteunit: Return the unit number into which we can write *                      for this block. Make it available if it isn't already. */static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block){	unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE);	unsigned int thisEUN, writeEUN, prev_block, status;	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1);	struct mtd_info *mtd = inftl->mbd.mtd;	struct inftl_oob oob;	struct inftl_bci bci;	unsigned char anac, nacs, parity;	size_t retlen;	int silly, silly2 = 3;

⌨️ 快捷键说明

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