* Author: Fabrice Bellard (fabrice.bellard@netgem.com)
* Copyright (C) 2000 Netgem S.A.
*
- * $Id: inftlmount.c,v 1.14 2004/08/09 13:57:42 dwmw2 Exp $
+ * $Id: inftlmount.c,v 1.11 2003/06/23 07:39:21 dwmw2 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
#include <linux/mtd/inftl.h>
#include <linux/mtd/compatmac.h>
-char inftlmountrev[]="$Revision: 1.14 $";
+char inftlmountrev[]="$Revision: 1.11 $";
/*
* find_boot_record: Find the INFTL Media Header and its Spare copy which
{
struct inftl_unittail h1;
//struct inftl_oob oob;
- unsigned int i, block;
+ unsigned int i, block, boot_record_count = 0;
u8 buf[SECTORSIZE];
struct INFTLMediaHeader *mh = &inftl->MediaHdr;
struct INFTLPartition *ip;
- size_t retlen;
+ int retlen;
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: find_boot_record(inftl=%p)\n", inftl);
+ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: find_boot_record(inftl=0x%x)\n",
+ (int)inftl);
/*
* Assume logical EraseSize == physical erasesize for starting the
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++) {
* Check for BNAND header first. Then whinge if it's found
* but later checks fail.
*/
- ret = MTD_READ(inftl->mbd.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) {
+ if ((ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize,
+ SECTORSIZE, &retlen, buf))) {
static int warncount = 5;
if (warncount) {
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(inftl->mbd.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);
"UnitSizeFactor 0x%02x is experimental\n",
mh->BlockMultiplierBits);
inftl->EraseSize = inftl->mbd.mtd->erasesize <<
- mh->BlockMultiplierBits;
+ (0xff - mh->BlockMultiplierBits);
inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize;
- block >>= mh->BlockMultiplierBits;
}
/* Scan the partitions */
inftl->PUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL);
if (!inftl->PUtable) {
printk(KERN_WARNING "INFTL: allocation of PUtable "
- "failed (%zd bytes)\n",
+ "failed (%d bytes)\n",
inftl->nb_blocks * sizeof(u16));
return -ENOMEM;
}
if (!inftl->VUtable) {
kfree(inftl->PUtable);
printk(KERN_WARNING "INFTL: allocation of VUtable "
- "failed (%zd bytes)\n",
+ "failed (%d bytes)\n",
inftl->nb_blocks * sizeof(u16));
return -ENOMEM;
}
/* 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++) {
- 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;
+ 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;
+ }
}
+ /* Mark the Bad Erase Unit as RESERVED in PUtable */
+ if (buf[i & (SECTORSIZE - 1)] != 0xff)
+ inftl->PUtable[i] = BLOCK_RESERVED;
}
+#endif
inftl->MediaUnit = block;
- return 0;
+ boot_record_count++;
}
-
- /* Not found. */
- return -1;
+
+ return boot_record_count ? 0 : -1;
}
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)
{
- u8 buf[SECTORSIZE + inftl->mbd.mtd->oobsize];
- size_t retlen;
- int i;
+ int i, retlen;
+ u8 buf[SECTORSIZE];
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=%p,"
- "address=0x%x,len=%d,check_oob=%d)\n", inftl,
+ 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);
for (i = 0; i < len; i += SECTORSIZE) {
- if (MTD_READECC(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &inftl->oobinfo) < 0)
+ /*
+ * 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)
return -1;
if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
return -1;
if (check_oob) {
- if (memcmpb(buf + SECTORSIZE, 0xff, inftl->mbd.mtd->oobsize) != 0)
+ if (MTD_READOOB(inftl->mbd.mtd, address,
+ inftl->mbd.mtd->oobsize, &retlen, buf) < 0)
+ return -1;
+ if (memcmpb(buf, 0xff, inftl->mbd.mtd->oobsize) != 0)
return -1;
}
address += SECTORSIZE;
* Return: 0 when succeed, -1 on error.
*
* ToDo: 1. Is it neceressary to check_free_sector after erasing ??
+ * 2. UnitSizeFactor != 0xFF
*/
int INFTL_formatblock(struct INFTLrecord *inftl, int block)
{
- size_t retlen;
+ int retlen;
struct inftl_unittail uci;
struct erase_info *instr = &inftl->instr;
- int physblock;
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=%p,"
- "block=%d)\n", inftl, block);
+ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=0x%x,"
+ "block=%d)\n", (int)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->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) {
- 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;
- }
+ instr->len = inftl->EraseSize;
+ MTD_ERASE(inftl->mbd.mtd, instr);
+ if (instr->state == MTD_ERASE_FAILED) {
/*
- * 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, instr->len, 1) != 0)
- goto fail;
+ * Could not format, FixMe: We should update the BadUnitTable
+ * both in memory and on disk.
+ */
+ printk(KERN_WARNING "INFTL: error while formatting block %d\n",
+ block);
+ return -1;
}
+ /*
+ * 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;
- instr->addr = block * inftl->EraseSize + SECTORSIZE * 2;
- if (MTD_WRITEOOB(inftl->mbd.mtd, instr->addr +
+ if (MTD_WRITEOOB(inftl->mbd.mtd, block * inftl->EraseSize + SECTORSIZE * 2 +
8, 8, &retlen, (char *)&uci) < 0)
- goto fail;
+ return -1;
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;
}
/*
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 {
int chain_length, do_format_chain;
struct inftl_unithead1 h0;
struct inftl_unittail h1;
- size_t retlen;
- int i;
+ int i, retlen;
u8 *ANACtable, ANAC;
- DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_mount(inftl=%p)\n", s);
+ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_mount(inftl=0x%x)\n", (int)s);
/* Search for INFTL MediaHeader and Spare INFTL Media Header */
if (find_boot_record(s) < 0) {