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

📄 inftlcore.c

📁 linux和2410结合开发 用他可以生成2410所需的zImage文件
💻 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> * * $Id: inftlcore.c,v 1.1.4.1 2003/10/16 18:40:42 bushi Exp $ * * 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/config.h>#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 <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 10000extern void INFTL_dumptables(struct INFTLrecord *inftl);extern void INFTL_dumpVUchains(struct INFTLrecord *inftl);static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd){	struct INFTLrecord *inftl;	unsigned long temp;	if (mtd->ecctype != MTD_ECC_RS_DiskOnChip)		return;	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name);	inftl = kmalloc(sizeof(*inftl), GFP_KERNEL);	if (!inftl) {		printk(KERN_WARNING "INFTL: Out of memory for data structures\n");		return;	}	memset(inftl, 0, sizeof(*inftl));	inftl->mbd.mtd = mtd;	inftl->mbd.devnum = -1;	inftl->mbd.blksize = 512;	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)) {		if (inftl->PUtable)			kfree(inftl->PUtable);		if (inftl->VUtable)			kfree(inftl->VUtable);		kfree(inftl);		return;	}#ifdef PSYCHO_DEBUG	printk(KERN_INFO "INFTL: Found new nftl%c\n", nftl->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);	if (inftl->PUtable)		kfree(inftl->PUtable);	if (inftl->VUtable)		kfree(inftl->VUtable);	kfree(inftl);}/* * Actual INFTL access routines. *//* * 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=0x%x,"		"desperate=%d)\n", (int)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;	int block, silly;	unsigned int targetEUN;	struct inftl_oob oob;        size_t retlen;	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=0x%x,thisVUC=%d,"		"pending=%d)\n", (int)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 (MTD_READOOB(inftl->mbd.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_READECC(inftl->mbd.mtd, (inftl->EraseSize *			BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE,			&retlen, movebuf, (char *)&oob, NULL);                 if (ret < 0) {			ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize *				BlockMap[block]) + (block * SECTORSIZE),				SECTORSIZE, &retlen, movebuf, (char *)&oob,				NULL); 			if (ret != -EIO)                         	DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "					"away on retry?\n");                }                MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +			(block * SECTORSIZE), SECTORSIZE, &retlen,			movebuf, (char *)&oob, NULL);	}	/*	 * 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;                if (INFTL_formatblock(inftl, thisEUN) < 0) {			/*			 * Could not erase : mark block as reserved.			 * FixMe: Update Bad Unit Table on disk.			 */			inftl->PUtable[thisEUN] = BLOCK_RESERVED;                } else {			/* Correctly erased : mark it as free */			inftl->PUtable[thisEUN] = BLOCK_FREE;			inftl->PUtable[prevEUN] = BLOCK_NIL;			inftl->numfreeEUNs++;                }	}	return targetEUN;}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=0x%x,"		"pending=%d)\n", (int)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 inftl_oob oob;	struct inftl_bci bci;	unsigned char anac, nacs, parity;	size_t retlen;	int silly, silly2 = 3;	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findwriteunit(inftl=0x%x,"		"block=%d)\n", (int)inftl, block);	do {		/*		 * Scan the media to find a unit in the VUC which has		 * a free space for the block in question.		 */		writeEUN = BLOCK_NIL;		thisEUN = inftl->VUtable[thisVUC];		silly = MAX_LOOPS;		while (thisEUN <= inftl->lastEUN) {			MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +				blockofs, 8, &retlen, (char *)&bci);                        status = bci.Status | bci.Status1;			DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in "				"EUN %d is %x\n", block , writeEUN, status);			switch(status) {			case SECTOR_FREE:				writeEUN = thisEUN;				break;			case SECTOR_DELETED:			case SECTOR_USED:				/* Can't go any further */				goto hitused;			case SECTOR_IGNORE:				break;			default:				/*				 * Invalid block. Don't use it any more.				 * Must implement.

⌨️ 快捷键说明

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