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

📄 inftlmount.c

📁 基于linux-2.6.28的mtd驱动
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * inftlmount.c -- INFTL mount code with extensive checks. * * Author: Greg Ungerer (gerg@snapgear.com) * (C) Copyright 2002-2003, Greg Ungerer (gerg@snapgear.com) * * Based heavily on the nftlmount.c code which is: * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * * 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 <asm/errno.h>#include <asm/io.h>#include <asm/uaccess.h>#include <linux/miscdevice.h>#include <linux/delay.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/mtd/mtd.h>#include <linux/mtd/nftl.h>#include <linux/mtd/inftl.h>#include <linux/mtd/compatmac.h>/* * find_boot_record: Find the INFTL Media Header and its Spare copy which *	contains the various device information of the INFTL partition and *	Bad Unit Table. Update the PUtable[] table according to the Bad *	Unit Table. PUtable[] is used for management of Erase Unit in *	other routines in inftlcore.c and inftlmount.c. */static int find_boot_record(struct INFTLrecord *inftl){	struct inftl_unittail h1;	//struct inftl_oob oob;	unsigned int i, block;	u8 buf[SECTORSIZE];	struct INFTLMediaHeader *mh = &inftl->MediaHdr;	struct mtd_info *mtd = inftl->mbd.mtd;	struct INFTLPartition *ip;	size_t retlen;	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: find_boot_record(inftl=%p)\n", inftl);        /*	 * Assume logical EraseSize == physical erasesize for starting the	 * scan. We'll sort it out later if we find a MediaHeader which says	 * otherwise.	 */	inftl->EraseSize = inftl->mbd.mtd->erasesize;        inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize;	inftl->MediaUnit = BLOCK_NIL;	/* Search for a valid boot record */	for (block = 0; block < inftl->nb_blocks; block++) {		int ret;		/*		 * Check for BNAND header first. Then whinge if it's found		 * but later checks fail.		 */		ret = mtd->read(mtd, block * inftl->EraseSize,				SECTORSIZE, &retlen, buf);		/* We ignore ret in case the ECC of the MediaHeader is invalid		   (which is apparently acceptable) */		if (retlen != SECTORSIZE) {			static int warncount = 5;			if (warncount) {				printk(KERN_WARNING "INFTL: block read at 0x%x "					"of mtd%d failed: %d\n",					block * inftl->EraseSize,					inftl->mbd.mtd->index, ret);				if (!--warncount)					printk(KERN_WARNING "INFTL: further "						"failures for this block will "						"not be printed\n");			}			continue;		}		if (retlen < 6 || memcmp(buf, "BNAND", 6)) {			/* BNAND\0 not found. Continue */			continue;		}		/* To be safer with BIOS, also use erase mark as discriminant */		if ((ret = inftl_read_oob(mtd, block * inftl->EraseSize +					  SECTORSIZE + 8, 8, &retlen,					  (char *)&h1) < 0)) {			printk(KERN_WARNING "INFTL: ANAND header found at "				"0x%x in mtd%d, but OOB data read failed "				"(err %d)\n", block * inftl->EraseSize,				inftl->mbd.mtd->index, ret);			continue;		}		/*		 * This is the first we've seen.		 * Copy the media header structure into place.		 */		memcpy(mh, buf, sizeof(struct INFTLMediaHeader));		/* Read the spare media header at offset 4096 */		mtd->read(mtd, block * inftl->EraseSize + 4096,			  SECTORSIZE, &retlen, buf);		if (retlen != SECTORSIZE) {			printk(KERN_WARNING "INFTL: Unable to read spare "			       "Media Header\n");			return -1;		}		/* Check if this one is the same as the first one we found. */		if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) {			printk(KERN_WARNING "INFTL: Primary and spare Media "			       "Headers disagree.\n");			return -1;		}		mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks);		mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions);		mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions);		mh->BlockMultiplierBits = le32_to_cpu(mh->BlockMultiplierBits);		mh->FormatFlags = le32_to_cpu(mh->FormatFlags);		mh->PercentUsed = le32_to_cpu(mh->PercentUsed);#ifdef CONFIG_MTD_DEBUG_VERBOSE		if (CONFIG_MTD_DEBUG_VERBOSE >= 2) {			printk("INFTL: Media Header ->\n"				"    bootRecordID          = %s\n"				"    NoOfBootImageBlocks   = %d\n"				"    NoOfBinaryPartitions  = %d\n"				"    NoOfBDTLPartitions    = %d\n"				"    BlockMultiplerBits    = %d\n"				"    FormatFlgs            = %d\n"				"    OsakVersion           = 0x%x\n"				"    PercentUsed           = %d\n",				mh->bootRecordID, mh->NoOfBootImageBlocks,				mh->NoOfBinaryPartitions,				mh->NoOfBDTLPartitions,				mh->BlockMultiplierBits, mh->FormatFlags,				mh->OsakVersion, mh->PercentUsed);		}#endif		if (mh->NoOfBDTLPartitions == 0) {			printk(KERN_WARNING "INFTL: Media Header sanity check "				"failed: NoOfBDTLPartitions (%d) == 0, "				"must be at least 1\n", mh->NoOfBDTLPartitions);			return -1;		}		if ((mh->NoOfBDTLPartitions + mh->NoOfBinaryPartitions) > 4) {			printk(KERN_WARNING "INFTL: Media Header sanity check "				"failed: Total Partitions (%d) > 4, "				"BDTL=%d Binary=%d\n", mh->NoOfBDTLPartitions +				mh->NoOfBinaryPartitions,				mh->NoOfBDTLPartitions,				mh->NoOfBinaryPartitions);			return -1;		}		if (mh->BlockMultiplierBits > 1) {			printk(KERN_WARNING "INFTL: sorry, we don't support "				"UnitSizeFactor 0x%02x\n",				mh->BlockMultiplierBits);			return -1;		} else if (mh->BlockMultiplierBits == 1) {			printk(KERN_WARNING "INFTL: support for INFTL with "				"UnitSizeFactor 0x%02x is experimental\n",				mh->BlockMultiplierBits);			inftl->EraseSize = inftl->mbd.mtd->erasesize <<				mh->BlockMultiplierBits;			inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize;			block >>= mh->BlockMultiplierBits;		}		/* Scan the partitions */		for (i = 0; (i < 4); i++) {			ip = &mh->Partitions[i];			ip->virtualUnits = le32_to_cpu(ip->virtualUnits);			ip->firstUnit = le32_to_cpu(ip->firstUnit);			ip->lastUnit = le32_to_cpu(ip->lastUnit);			ip->flags = le32_to_cpu(ip->flags);			ip->spareUnits = le32_to_cpu(ip->spareUnits);			ip->Reserved0 = le32_to_cpu(ip->Reserved0);#ifdef CONFIG_MTD_DEBUG_VERBOSE			if (CONFIG_MTD_DEBUG_VERBOSE >= 2) {				printk("    PARTITION[%d] ->\n"					"        virtualUnits    = %d\n"					"        firstUnit       = %d\n"					"        lastUnit        = %d\n"					"        flags           = 0x%x\n"					"        spareUnits      = %d\n",					i, ip->virtualUnits, ip->firstUnit,					ip->lastUnit, ip->flags,					ip->spareUnits);			}#endif			if (ip->Reserved0 != ip->firstUnit) {				struct erase_info *instr = &inftl->instr;				instr->mtd = inftl->mbd.mtd;				/*				 * 	Most likely this is using the				 * 	undocumented qiuck mount feature.				 * 	We don't support that, we will need				 * 	to erase the hidden block for full				 * 	compatibility.				 */				instr->addr = ip->Reserved0 * inftl->EraseSize;				instr->len = inftl->EraseSize;				mtd->erase(mtd, instr);			}			if ((ip->lastUnit - ip->firstUnit + 1) < ip->virtualUnits) {				printk(KERN_WARNING "INFTL: Media Header "					"Partition %d sanity check failed\n"					"    firstUnit %d : lastUnit %d  >  "					"virtualUnits %d\n", i, ip->lastUnit,					ip->firstUnit, ip->Reserved0);				return -1;			}			if (ip->Reserved1 != 0) {				printk(KERN_WARNING "INFTL: Media Header "					"Partition %d sanity check failed: "					"Reserved1 %d != 0\n",					i, ip->Reserved1);				return -1;			}			if (ip->flags & INFTL_BDTL)				break;		}		if (i >= 4) {			printk(KERN_WARNING "INFTL: Media Header Partition "				"sanity check failed:\n       No partition "				"marked as Disk Partition\n");			return -1;		}		inftl->nb_boot_blocks = ip->firstUnit;		inftl->numvunits = ip->virtualUnits;		if (inftl->numvunits > (inftl->nb_blocks -		    inftl->nb_boot_blocks - 2)) {			printk(KERN_WARNING "INFTL: Media Header sanity check "				"failed:\n        numvunits (%d) > nb_blocks "				"(%d) - nb_boot_blocks(%d) - 2\n",				inftl->numvunits, inftl->nb_blocks,				inftl->nb_boot_blocks);			return -1;		}		inftl->mbd.size  = inftl->numvunits *			(inftl->EraseSize / SECTORSIZE);		/*		 * Block count is set to last used EUN (we won't need to keep		 * any meta-data past that point).		 */		inftl->firstEUN = ip->firstUnit;		inftl->lastEUN = ip->lastUnit;		inftl->nb_blocks = ip->lastUnit + 1;		/* Memory alloc */		inftl->PUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL);		if (!inftl->PUtable) {			printk(KERN_WARNING "INFTL: allocation of PUtable "				"failed (%zd bytes)\n",				inftl->nb_blocks * sizeof(u16));			return -ENOMEM;		}		inftl->VUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL);		if (!inftl->VUtable) {			kfree(inftl->PUtable);			printk(KERN_WARNING "INFTL: allocation of VUtable "				"failed (%zd bytes)\n",				inftl->nb_blocks * sizeof(u16));			return -ENOMEM;		}		/* Mark the blocks before INFTL MediaHeader as reserved */		for (i = 0; i < inftl->nb_boot_blocks; i++)			inftl->PUtable[i] = BLOCK_RESERVED;		/* Mark all remaining blocks as potentially containing data */		for (; i < inftl->nb_blocks; i++)			inftl->PUtable[i] = BLOCK_NOTEXPLORED;		/* Mark this boot record (NFTL MediaHeader) block as reserved */		inftl->PUtable[block] = BLOCK_RESERVED;		/* Read Bad Erase Unit Table and modify PUtable[] accordingly */		for (i = 0; i < inftl->nb_blocks; i++) {			int physblock;			/* If any of the physical eraseblocks are bad, don't			   use the unit. */			for (physblock = 0; physblock < inftl->EraseSize; physblock += inftl->mbd.mtd->erasesize) {				if (inftl->mbd.mtd->block_isbad(inftl->mbd.mtd, i * inftl->EraseSize + physblock))					inftl->PUtable[i] = BLOCK_RESERVED;			}		}		inftl->MediaUnit = block;		return 0;	}	/* Not found. */	return -1;}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;}/* * check_free_sector: check if a free sector is actually FREE, *	i.e. All 0xff in data and oob area. */static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address,	int len, int check_oob){	u8 buf[SECTORSIZE + inftl->mbd.mtd->oobsize];	struct mtd_info *mtd = inftl->mbd.mtd;	size_t retlen;	int i;	for (i = 0; i < len; i += SECTORSIZE) {		if (mtd->read(mtd, address, SECTORSIZE, &retlen, buf))			return -1;		if (memcmpb(buf, 0xff, SECTORSIZE) != 0)			return -1;		if (check_oob) {			if(inftl_read_oob(mtd, address, mtd->oobsize,					  &retlen, &buf[SECTORSIZE]) < 0)				return -1;			if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0)				return -1;		}		address += SECTORSIZE;	}	return 0;}/* * INFTL_format: format a Erase Unit by erasing ALL Erase Zones in the Erase *		 Unit and Update INFTL metadata. Each erase operation is *		 checked with check_free_sectors. * * Return: 0 when succeed, -1 on error. * * ToDo: 1. Is it neceressary to check_free_sector after erasing ?? */int INFTL_formatblock(struct INFTLrecord *inftl, int block){	size_t retlen;	struct inftl_unittail uci;	struct erase_info *instr = &inftl->instr;	struct mtd_info *mtd = inftl->mbd.mtd;	int physblock;	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=%p,"		"block=%d)\n", inftl, block);	memset(instr, 0, sizeof(struct erase_info));	/* FIXME: Shouldn't we be setting the 'discarded' flag to zero	   _first_? */	/* Use async erase interface, test return code */	instr->mtd = inftl->mbd.mtd;	instr->addr = block * inftl->EraseSize;	instr->len = inftl->mbd.mtd->erasesize;	/* Erase one physical eraseblock at a time, even though the NAND api	   allows us to group them.  This way we if we have a failure, we can	   mark only the failed block in the bbt. */	for (physblock = 0; physblock < inftl->EraseSize;	     physblock += instr->len, instr->addr += instr->len) {

⌨️ 快捷键说明

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