X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmtd%2Finftlmount.c;h=8f6006f1a519480c3fbba9581294b901bc2571b9;hb=ae33d880de1750c6cad28fa539f12c86a3497862;hp=042c4eeadf8ec6100b978a9e1b310fc25702306e;hpb=daddc0d38b3571bed170afa273a49a0eba090c1e;p=linux-2.6.git diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index 042c4eead..8f6006f1a 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -1,14 +1,14 @@ -/* +/* * 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) + * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: inftlmount.c,v 1.11 2003/06/23 07:39:21 dwmw2 Exp $ + * $Id: inftlmount.c,v 1.18 2005/11/07 11:14:20 gleixner 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 @@ -41,7 +41,12 @@ #include #include -char inftlmountrev[]="$Revision: 1.11 $"; +char inftlmountrev[]="$Revision: 1.18 $"; + +extern int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf); +extern int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf); /* * find_boot_record: Find the INFTL Media Header and its Spare copy which @@ -54,14 +59,14 @@ static int find_boot_record(struct INFTLrecord *inftl) { struct inftl_unittail h1; //struct inftl_oob oob; - unsigned int i, block, boot_record_count = 0; + unsigned int i, block; u8 buf[SECTORSIZE]; struct INFTLMediaHeader *mh = &inftl->MediaHdr; + struct mtd_info *mtd = inftl->mbd.mtd; struct INFTLPartition *ip; - int retlen; + size_t retlen; - DEBUG(MTD_DEBUG_LEVEL3, "INFTL: find_boot_record(inftl=0x%x)\n", - (int)inftl); + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: find_boot_record(inftl=%p)\n", inftl); /* * Assume logical EraseSize == physical erasesize for starting the @@ -72,7 +77,6 @@ static int find_boot_record(struct INFTLrecord *inftl) inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; inftl->MediaUnit = BLOCK_NIL; - inftl->SpareMediaUnit = BLOCK_NIL; /* Search for a valid boot record */ for (block = 0; block < inftl->nb_blocks; block++) { @@ -82,8 +86,11 @@ static int find_boot_record(struct INFTLrecord *inftl) * Check for BNAND header first. Then whinge if it's found * but later checks fail. */ - if ((ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize, - SECTORSIZE, &retlen, buf))) { + 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) { @@ -105,8 +112,9 @@ static int find_boot_record(struct INFTLrecord *inftl) } /* To be safer with BIOS, also use erase mark as discriminant */ - if ((ret = MTD_READOOB(inftl->mbd.mtd, block * inftl->EraseSize + - SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0)) { + 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, @@ -114,36 +122,28 @@ static int find_boot_record(struct INFTLrecord *inftl) continue; } - if (boot_record_count) { - /* - * We've already processed one. So we just check if - * this one is the same as the first one we found. - */ - if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) { - printk(KERN_WARNING "INFTL: Media Headers at " - "0x%x and 0x%x disagree.\n", - inftl->MediaUnit * inftl->EraseSize, - block * inftl->EraseSize); - return -1; - } - if (boot_record_count == 1) - inftl->SpareMediaUnit = block; - - /* - * Mark this boot record (INFTL MediaHeader) block as - * reserved. - */ - inftl->PUtable[block] = BLOCK_RESERVED; - - boot_record_count++; - 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); @@ -197,8 +197,9 @@ static int find_boot_record(struct INFTLrecord *inftl) "UnitSizeFactor 0x%02x is experimental\n", mh->BlockMultiplierBits); inftl->EraseSize = inftl->mbd.mtd->erasesize << - (0xff - mh->BlockMultiplierBits); + mh->BlockMultiplierBits; inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; + block >>= mh->BlockMultiplierBits; } /* Scan the partitions */ @@ -228,6 +229,8 @@ static int find_boot_record(struct INFTLrecord *inftl) 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. @@ -237,7 +240,7 @@ static int find_boot_record(struct INFTLrecord *inftl) */ instr->addr = ip->Reserved0 * inftl->EraseSize; instr->len = inftl->EraseSize; - MTD_ERASE(inftl->mbd.mtd, instr); + mtd->erase(mtd, instr); } if ((ip->lastUnit - ip->firstUnit + 1) < ip->virtualUnits) { printk(KERN_WARNING "INFTL: Media Header " @@ -277,7 +280,7 @@ static int find_boot_record(struct INFTLrecord *inftl) inftl->nb_boot_blocks); return -1; } - + inftl->mbd.size = inftl->numvunits * (inftl->EraseSize / SECTORSIZE); @@ -293,7 +296,7 @@ static int find_boot_record(struct INFTLrecord *inftl) inftl->PUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL); if (!inftl->PUtable) { printk(KERN_WARNING "INFTL: allocation of PUtable " - "failed (%d bytes)\n", + "failed (%zd bytes)\n", inftl->nb_blocks * sizeof(u16)); return -ENOMEM; } @@ -302,11 +305,11 @@ static int find_boot_record(struct INFTLrecord *inftl) if (!inftl->VUtable) { kfree(inftl->PUtable); printk(KERN_WARNING "INFTL: allocation of VUtable " - "failed (%d bytes)\n", + "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; @@ -317,34 +320,23 @@ static int find_boot_record(struct INFTLrecord *inftl) /* Mark this boot record (NFTL MediaHeader) block as reserved */ inftl->PUtable[block] = BLOCK_RESERVED; -#if 0 /* Read Bad Erase Unit Table and modify PUtable[] accordingly */ for (i = 0; i < inftl->nb_blocks; i++) { - if ((i & (SECTORSIZE - 1)) == 0) { - /* read one sector for every SECTORSIZE of blocks */ - if ((ret = MTD_READECC(inftl->mbd.mtd, - block * inftl->EraseSize + i + SECTORSIZE, - SECTORSIZE, &retlen, buf, - (char *)&oob, NULL)) < 0) { - printk(KERN_WARNING "INFTL: read of " - "bad sector table failed " - "(err %d)\n", ret); - kfree(inftl->VUtable); - kfree(inftl->PUtable); - return -1; - } + 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; } - /* Mark the Bad Erase Unit as RESERVED in PUtable */ - if (buf[i & (SECTORSIZE - 1)] != 0xff) - inftl->PUtable[i] = BLOCK_RESERVED; } -#endif inftl->MediaUnit = block; - boot_record_count++; + return 0; } - - return boot_record_count ? 0 : -1; + + /* Not found. */ + return -1; } static int memcmpb(void *a, int c, int n) @@ -364,28 +356,22 @@ static int memcmpb(void *a, int c, int n) static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address, int len, int check_oob) { - int i, retlen; - u8 buf[SECTORSIZE]; - - DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=0x%x," - "address=0x%x,len=%d,check_oob=%d)\n", (int)inftl, - address, len, 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) { - /* - * We want to read the sector without ECC check here since a - * free sector does not have ECC syndrome on it yet. - */ - if (MTD_READ(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf) < 0) + if (mtd->read(mtd, address, SECTORSIZE, &retlen, buf)) return -1; if (memcmpb(buf, 0xff, SECTORSIZE) != 0) return -1; if (check_oob) { - if (MTD_READOOB(inftl->mbd.mtd, address, - inftl->mbd.mtd->oobsize, &retlen, buf) < 0) + if(inftl_read_oob(mtd, address, mtd->oobsize, + &retlen, &buf[SECTORSIZE]) < 0) return -1; - if (memcmpb(buf, 0xff, inftl->mbd.mtd->oobsize) != 0) + if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0) return -1; } address += SECTORSIZE; @@ -401,53 +387,65 @@ static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address, * * Return: 0 when succeed, -1 on error. * - * ToDo: 1. Is it neceressary to check_free_sector after erasing ?? - * 2. UnitSizeFactor != 0xFF + * ToDo: 1. Is it neceressary to check_free_sector after erasing ?? */ int INFTL_formatblock(struct INFTLrecord *inftl, int block) { - int retlen; + 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=0x%x," - "block=%d)\n", (int)inftl, block); + 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->EraseSize; - MTD_ERASE(inftl->mbd.mtd, instr); + 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) { + mtd->erase(inftl->mbd.mtd, instr); + + if (instr->state == MTD_ERASE_FAILED) { + printk(KERN_WARNING "INFTL: error while formatting block %d\n", + block); + goto fail; + } - if (instr->state == MTD_ERASE_FAILED) { /* - * Could not format, FixMe: We should update the BadUnitTable - * both in memory and on disk. + * Check the "freeness" of Erase Unit before updating metadata. + * FixMe: is this check really necessary? Since we have check + * the return code after the erase operation. */ - printk(KERN_WARNING "INFTL: error while formatting block %d\n", - block); - return -1; + if (check_free_sectors(inftl, instr->addr, instr->len, 1) != 0) + goto fail; } - /* - * Check the "freeness" of Erase Unit before updating metadata. - * FixMe: is this check really necessary? Since we have check the - * return code after the erase operation. - */ - if (check_free_sectors(inftl, instr->addr, inftl->EraseSize, 1) != 0) - return -1; - uci.EraseMark = cpu_to_le16(ERASE_MARK); uci.EraseMark1 = cpu_to_le16(ERASE_MARK); uci.Reserved[0] = 0; uci.Reserved[1] = 0; uci.Reserved[2] = 0; uci.Reserved[3] = 0; - if (MTD_WRITEOOB(inftl->mbd.mtd, block * inftl->EraseSize + SECTORSIZE * 2 + - 8, 8, &retlen, (char *)&uci) < 0) - return -1; + instr->addr = block * inftl->EraseSize + SECTORSIZE * 2; + if (inftl_write_oob(mtd, instr->addr + 8, 8, &retlen, (char *)&uci) < 0) + goto fail; return 0; +fail: + /* could not format, update the bad block table (caller is responsible + for setting the PUtable to BLOCK_RESERVED on failure) */ + inftl->mbd.mtd->block_markbad(inftl->mbd.mtd, instr->addr); + return -1; } /* @@ -472,7 +470,6 @@ static void format_chain(struct INFTLrecord *inftl, unsigned int first_block) if (INFTL_formatblock(inftl, block) < 0) { /* * Cannot format !!!! Mark it as Bad Unit, - * FixMe: update the BadUnitTable on disk. */ inftl->PUtable[block] = BLOCK_RESERVED; } else { @@ -560,20 +557,22 @@ void INFTL_dumpVUchains(struct INFTLrecord *s) int INFTL_mount(struct INFTLrecord *s) { + struct mtd_info *mtd = s->mbd.mtd; unsigned int block, first_block, prev_block, last_block; unsigned int first_logical_block, logical_block, erase_mark; int chain_length, do_format_chain; struct inftl_unithead1 h0; struct inftl_unittail h1; - int i, retlen; + size_t retlen; + int i; u8 *ANACtable, ANAC; - DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_mount(inftl=0x%x)\n", (int)s); + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_mount(inftl=%p)\n", s); /* Search for INFTL MediaHeader and Spare INFTL Media Header */ if (find_boot_record(s) < 0) { printk(KERN_WARNING "INFTL: could not find valid boot record?\n"); - return -1; + return -ENXIO; } /* Init the logical to physical table */ @@ -584,6 +583,12 @@ int INFTL_mount(struct INFTLrecord *s) /* Temporary buffer to store ANAC numbers. */ ANACtable = kmalloc(s->nb_blocks * sizeof(u8), GFP_KERNEL); + if (!ANACtable) { + printk(KERN_WARNING "INFTL: allocation of ANACtable " + "failed (%zd bytes)\n", + s->nb_blocks * sizeof(u8)); + return -ENOMEM; + } memset(ANACtable, 0, s->nb_blocks); /* @@ -605,16 +610,17 @@ int INFTL_mount(struct INFTLrecord *s) for (chain_length = 0; ; chain_length++) { - if ((chain_length == 0) && + if ((chain_length == 0) && (s->PUtable[block] != BLOCK_NOTEXPLORED)) { /* Nothing to do here, onto next block */ break; } - if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8, - 8, &retlen, (char *)&h0) < 0 || - MTD_READOOB(s->mbd.mtd, block * s->EraseSize + - 2 * SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) { + if (inftl_read_oob(mtd, block * s->EraseSize + 8, + 8, &retlen, (char *)&h0) < 0 || + inftl_read_oob(mtd, block * s->EraseSize + + 2 * SECTORSIZE + 8, 8, &retlen, + (char *)&h1) < 0) { /* Should never happen? */ do_format_chain++; break; @@ -752,7 +758,7 @@ int INFTL_mount(struct INFTLrecord *s) "in virtual chain %d\n", s->PUtable[block], logical_block); s->PUtable[block] = BLOCK_NIL; - + } if (ANACtable[block] != ANAC) { /*