linux-bk/drivers/mtd/inftlcore.c
<<
>>
Prefs
   1/* 
   2 * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL)
   3 *
   4 * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
   5 *
   6 * Based heavily on the nftlcore.c code which is:
   7 * (c) 1999 Machine Vision Holdings, Inc.
   8 * Author: David Woodhouse <dwmw2@infradead.org>
   9 *
  10 * $Id: inftlcore.c,v 1.18 2004/11/16 18:28:59 dwmw2 Exp $
  11 *
  12 * This program is free software; you can redistribute it and/or modify
  13 * it under the terms of the GNU General Public License as published by
  14 * the Free Software Foundation; either version 2 of the License, or
  15 * (at your option) any later version.
  16 *
  17 * This program is distributed in the hope that it will be useful,
  18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20 * GNU General Public License for more details.
  21 *
  22 * You should have received a copy of the GNU General Public License
  23 * along with this program; if not, write to the Free Software
  24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  25 */
  26
  27#include <linux/config.h>
  28#include <linux/kernel.h>
  29#include <linux/module.h>
  30#include <linux/delay.h>
  31#include <linux/slab.h>
  32#include <linux/sched.h>
  33#include <linux/init.h>
  34#include <linux/kmod.h>
  35#include <linux/hdreg.h>
  36#include <linux/mtd/mtd.h>
  37#include <linux/mtd/nftl.h>
  38#include <linux/mtd/inftl.h>
  39#include <asm/uaccess.h>
  40#include <asm/errno.h>
  41#include <asm/io.h>
  42
  43/*
  44 * Maximum number of loops while examining next block, to have a
  45 * chance to detect consistency problems (they should never happen
  46 * because of the checks done in the mounting.
  47 */
  48#define MAX_LOOPS 10000
  49
  50extern void INFTL_dumptables(struct INFTLrecord *inftl);
  51extern void INFTL_dumpVUchains(struct INFTLrecord *inftl);
  52
  53static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
  54{
  55        struct INFTLrecord *inftl;
  56        unsigned long temp;
  57
  58        if (mtd->type != MTD_NANDFLASH)
  59                return;
  60        /* OK, this is moderately ugly.  But probably safe.  Alternatives? */
  61        if (memcmp(mtd->name, "DiskOnChip", 10))
  62                return;
  63
  64        if (!mtd->block_isbad) {
  65                printk(KERN_ERR
  66"INFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
  67"Please use the new diskonchip driver under the NAND subsystem.\n");
  68                return;
  69        }
  70
  71        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name);
  72
  73        inftl = kmalloc(sizeof(*inftl), GFP_KERNEL);
  74
  75        if (!inftl) {
  76                printk(KERN_WARNING "INFTL: Out of memory for data structures\n");
  77                return;
  78        }
  79        memset(inftl, 0, sizeof(*inftl));
  80
  81        inftl->mbd.mtd = mtd;
  82        inftl->mbd.devnum = -1;
  83        inftl->mbd.blksize = 512;
  84        inftl->mbd.tr = tr;
  85        memcpy(&inftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo));
  86        inftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY;
  87
  88        if (INFTL_mount(inftl) < 0) {
  89                printk(KERN_WARNING "INFTL: could not mount device\n");
  90                kfree(inftl);
  91                return;
  92        }
  93
  94        /* OK, it's a new one. Set up all the data structures. */
  95
  96        /* Calculate geometry */
  97        inftl->cylinders = 1024;
  98        inftl->heads = 16;
  99
 100        temp = inftl->cylinders * inftl->heads;
 101        inftl->sectors = inftl->mbd.size / temp;
 102        if (inftl->mbd.size % temp) {
 103                inftl->sectors++;
 104                temp = inftl->cylinders * inftl->sectors;
 105                inftl->heads = inftl->mbd.size / temp;
 106
 107                if (inftl->mbd.size % temp) {
 108                        inftl->heads++;
 109                        temp = inftl->heads * inftl->sectors;
 110                        inftl->cylinders = inftl->mbd.size / temp;
 111                }
 112        }
 113
 114        if (inftl->mbd.size != inftl->heads * inftl->cylinders * inftl->sectors) {
 115                /*
 116                  Oh no we don't have 
 117                   mbd.size == heads * cylinders * sectors
 118                */
 119                printk(KERN_WARNING "INFTL: cannot calculate a geometry to "
 120                       "match size of 0x%lx.\n", inftl->mbd.size);
 121                printk(KERN_WARNING "INFTL: using C:%d H:%d S:%d "
 122                        "(== 0x%lx sects)\n",
 123                        inftl->cylinders, inftl->heads , inftl->sectors, 
 124                        (long)inftl->cylinders * (long)inftl->heads *
 125                        (long)inftl->sectors );
 126        }
 127
 128        if (add_mtd_blktrans_dev(&inftl->mbd)) {
 129                if (inftl->PUtable)
 130                        kfree(inftl->PUtable);
 131                if (inftl->VUtable)
 132                        kfree(inftl->VUtable);
 133                kfree(inftl);
 134                return;
 135        }
 136#ifdef PSYCHO_DEBUG
 137        printk(KERN_INFO "INFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
 138#endif
 139        return;
 140}
 141
 142static void inftl_remove_dev(struct mtd_blktrans_dev *dev)
 143{
 144        struct INFTLrecord *inftl = (void *)dev;
 145
 146        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: remove_dev (i=%d)\n", dev->devnum);
 147
 148        del_mtd_blktrans_dev(dev);
 149
 150        if (inftl->PUtable)
 151                kfree(inftl->PUtable);
 152        if (inftl->VUtable)
 153                kfree(inftl->VUtable);
 154        kfree(inftl);
 155}
 156
 157/*
 158 * Actual INFTL access routines.
 159 */
 160
 161/*
 162 * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition.
 163 *      This function is used when the give Virtual Unit Chain.
 164 */
 165static u16 INFTL_findfreeblock(struct INFTLrecord *inftl, int desperate)
 166{
 167        u16 pot = inftl->LastFreeEUN;
 168        int silly = inftl->nb_blocks;
 169
 170        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findfreeblock(inftl=%p,"
 171                "desperate=%d)\n", inftl, desperate);
 172
 173        /*
 174         * Normally, we force a fold to happen before we run out of free
 175         * blocks completely.
 176         */
 177        if (!desperate && inftl->numfreeEUNs < 2) {
 178                DEBUG(MTD_DEBUG_LEVEL1, "INFTL: there are too few free "
 179                        "EUNs (%d)\n", inftl->numfreeEUNs);
 180                return 0xffff;
 181        }
 182
 183        /* Scan for a free block */
 184        do {
 185                if (inftl->PUtable[pot] == BLOCK_FREE) {
 186                        inftl->LastFreeEUN = pot;
 187                        return pot;
 188                }
 189
 190                if (++pot > inftl->lastEUN)
 191                        pot = 0;
 192
 193                if (!silly--) {
 194                        printk(KERN_WARNING "INFTL: no free blocks found!  "
 195                                "EUN range = %d - %d\n", 0, inftl->LastFreeEUN);
 196                        return BLOCK_NIL;
 197                }
 198        } while (pot != inftl->LastFreeEUN);
 199
 200        return BLOCK_NIL;
 201}
 202
 203static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock)
 204{
 205        u16 BlockMap[MAX_SECTORS_PER_UNIT];
 206        unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
 207        unsigned int thisEUN, prevEUN, status;
 208        int block, silly;
 209        unsigned int targetEUN;
 210        struct inftl_oob oob;
 211        size_t retlen;
 212
 213        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d,"
 214                "pending=%d)\n", inftl, thisVUC, pendingblock);
 215
 216        memset(BlockMap, 0xff, sizeof(BlockMap));
 217        memset(BlockDeleted, 0, sizeof(BlockDeleted));
 218
 219        thisEUN = targetEUN = inftl->VUtable[thisVUC];
 220
 221        if (thisEUN == BLOCK_NIL) {
 222                printk(KERN_WARNING "INFTL: trying to fold non-existent "
 223                       "Virtual Unit Chain %d!\n", thisVUC);
 224                return BLOCK_NIL;
 225        }
 226        
 227        /*
 228         * Scan to find the Erase Unit which holds the actual data for each
 229         * 512-byte block within the Chain.
 230         */
 231        silly = MAX_LOOPS;
 232        while (thisEUN < inftl->nb_blocks) {
 233                for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) {
 234                        if ((BlockMap[block] != 0xffff) || BlockDeleted[block])
 235                                continue;
 236
 237                        if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize)
 238                             + (block * SECTORSIZE), 16 , &retlen,
 239                             (char *)&oob) < 0)
 240                                status = SECTOR_IGNORE;
 241                        else
 242                                status = oob.b.Status | oob.b.Status1;
 243
 244                        switch(status) {
 245                        case SECTOR_FREE:
 246                        case SECTOR_IGNORE:
 247                                break;
 248                        case SECTOR_USED:
 249                                BlockMap[block] = thisEUN;
 250                                continue;
 251                        case SECTOR_DELETED:
 252                                BlockDeleted[block] = 1;
 253                                continue;
 254                        default:
 255                                printk(KERN_WARNING "INFTL: unknown status "
 256                                        "for block %d in EUN %d: %x\n",
 257                                        block, thisEUN, status);
 258                                break;
 259                        }
 260                }
 261
 262                if (!silly--) {
 263                        printk(KERN_WARNING "INFTL: infinite loop in Virtual "
 264                                "Unit Chain 0x%x\n", thisVUC);
 265                        return BLOCK_NIL;
 266                }
 267                
 268                thisEUN = inftl->PUtable[thisEUN];
 269        }
 270
 271        /*
 272         * OK. We now know the location of every block in the Virtual Unit
 273         * Chain, and the Erase Unit into which we are supposed to be copying.
 274         * Go for it.
 275         */
 276        DEBUG(MTD_DEBUG_LEVEL1, "INFTL: folding chain %d into unit %d\n",
 277                thisVUC, targetEUN);
 278
 279        for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) {
 280                unsigned char movebuf[SECTORSIZE];
 281                int ret;
 282
 283                /*
 284                 * If it's in the target EUN already, or if it's pending write,
 285                 * do nothing.
 286                 */
 287                if (BlockMap[block] == targetEUN || (pendingblock ==
 288                    (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) {
 289                        continue;
 290                }
 291
 292                /*
 293                 * Copy only in non free block (free blocks can only
 294                 * happen in case of media errors or deleted blocks).
 295                 */
 296                if (BlockMap[block] == BLOCK_NIL)
 297                        continue;
 298                
 299                ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize *
 300                        BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE,
 301                        &retlen, movebuf); 
 302                if (ret < 0) {
 303                        ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize *
 304                                BlockMap[block]) + (block * SECTORSIZE),
 305                                SECTORSIZE, &retlen, movebuf);
 306                        if (ret != -EIO) 
 307                                DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "
 308                                        "away on retry?\n");
 309                }
 310                memset(&oob, 0xff, sizeof(struct inftl_oob));
 311                oob.b.Status = oob.b.Status1 = SECTOR_USED;
 312                MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
 313                        (block * SECTORSIZE), SECTORSIZE, &retlen,
 314                        movebuf, (char *)&oob, &inftl->oobinfo);
 315        }
 316
 317        /*
 318         * Newest unit in chain now contains data from _all_ older units.
 319         * So go through and erase each unit in chain, oldest first. (This
 320         * is important, by doing oldest first if we crash/reboot then it
 321         * it is relatively simple to clean up the mess).
 322         */
 323        DEBUG(MTD_DEBUG_LEVEL1, "INFTL: want to erase virtual chain %d\n",
 324                thisVUC);
 325
 326        for (;;) {
 327                /* Find oldest unit in chain. */
 328                thisEUN = inftl->VUtable[thisVUC];
 329                prevEUN = BLOCK_NIL;
 330                while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
 331                        prevEUN = thisEUN;
 332                        thisEUN = inftl->PUtable[thisEUN];
 333                }
 334
 335                /* Check if we are all done */
 336                if (thisEUN == targetEUN)
 337                        break;
 338
 339                if (INFTL_formatblock(inftl, thisEUN) < 0) {
 340                        /*
 341                         * Could not erase : mark block as reserved.
 342                         */
 343                        inftl->PUtable[thisEUN] = BLOCK_RESERVED;
 344                } else {
 345                        /* Correctly erased : mark it as free */
 346                        inftl->PUtable[thisEUN] = BLOCK_FREE;
 347                        inftl->PUtable[prevEUN] = BLOCK_NIL;
 348                        inftl->numfreeEUNs++;
 349                }
 350        }
 351
 352        return targetEUN;
 353}
 354
 355static u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock)
 356{
 357        /*
 358         * This is the part that needs some cleverness applied. 
 359         * For now, I'm doing the minimum applicable to actually
 360         * get the thing to work.
 361         * Wear-levelling and other clever stuff needs to be implemented
 362         * and we also need to do some assessment of the results when
 363         * the system loses power half-way through the routine.
 364         */
 365        u16 LongestChain = 0;
 366        u16 ChainLength = 0, thislen;
 367        u16 chain, EUN;
 368
 369        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_makefreeblock(inftl=%p,"
 370                "pending=%d)\n", inftl, pendingblock);
 371
 372        for (chain = 0; chain < inftl->nb_blocks; chain++) {
 373                EUN = inftl->VUtable[chain];
 374                thislen = 0;
 375
 376                while (EUN <= inftl->lastEUN) {
 377                        thislen++;
 378                        EUN = inftl->PUtable[EUN];
 379                        if (thislen > 0xff00) {
 380                                printk(KERN_WARNING "INFTL: endless loop in "
 381                                        "Virtual Chain %d: Unit %x\n",
 382                                        chain, EUN);
 383                                /*
 384                                 * Actually, don't return failure.
 385                                 * Just ignore this chain and get on with it.
 386                                 */
 387                                thislen = 0;
 388                                break;
 389                        }
 390                }
 391
 392                if (thislen > ChainLength) {
 393                        ChainLength = thislen;
 394                        LongestChain = chain;
 395                }
 396        }
 397
 398        if (ChainLength < 2) {
 399                printk(KERN_WARNING "INFTL: no Virtual Unit Chains available "
 400                        "for folding. Failing request\n");
 401                return BLOCK_NIL;
 402        }
 403
 404        return INFTL_foldchain(inftl, LongestChain, pendingblock);
 405}
 406
 407static int nrbits(unsigned int val, int bitcount)
 408{
 409        int i, total = 0;
 410
 411        for (i = 0; (i < bitcount); i++)
 412                total += (((0x1 << i) & val) ? 1 : 0);
 413        return total;
 414}
 415
 416/*
 417 * INFTL_findwriteunit: Return the unit number into which we can write 
 418 *                      for this block. Make it available if it isn't already.
 419 */
 420static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
 421{
 422        unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE);
 423        unsigned int thisEUN, writeEUN, prev_block, status;
 424        unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1);
 425        struct inftl_oob oob;
 426        struct inftl_bci bci;
 427        unsigned char anac, nacs, parity;
 428        size_t retlen;
 429        int silly, silly2 = 3;
 430
 431        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findwriteunit(inftl=%p,"
 432                "block=%d)\n", inftl, block);
 433
 434        do {
 435                /*
 436                 * Scan the media to find a unit in the VUC which has
 437                 * a free space for the block in question.
 438                 */
 439                writeEUN = BLOCK_NIL;
 440                thisEUN = inftl->VUtable[thisVUC];
 441                silly = MAX_LOOPS;
 442
 443                while (thisEUN <= inftl->lastEUN) {
 444                        MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
 445                                blockofs, 8, &retlen, (char *)&bci);
 446
 447                        status = bci.Status | bci.Status1;
 448                        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in "
 449                                "EUN %d is %x\n", block , writeEUN, status);
 450
 451                        switch(status) {
 452                        case SECTOR_FREE:
 453                                writeEUN = thisEUN;
 454                                break;
 455                        case SECTOR_DELETED:
 456                        case SECTOR_USED:
 457                                /* Can't go any further */
 458                                goto hitused;
 459                        case SECTOR_IGNORE:
 460                                break;
 461                        default:
 462                                /*
 463                                 * Invalid block. Don't use it any more.
 464                                 * Must implement.
 465                                 */
 466                                break;                  
 467                        }
 468                        
 469                        if (!silly--) { 
 470                                printk(KERN_WARNING "INFTL: infinite loop in "
 471                                        "Virtual Unit Chain 0x%x\n", thisVUC);
 472                                return 0xffff;
 473                        }
 474
 475                        /* Skip to next block in chain */
 476                        thisEUN = inftl->PUtable[thisEUN];
 477                }
 478
 479hitused:
 480                if (writeEUN != BLOCK_NIL)
 481                        return writeEUN;
 482
 483
 484                /*
 485                 * OK. We didn't find one in the existing chain, or there 
 486                 * is no existing chain. Allocate a new one.
 487                 */
 488                writeEUN = INFTL_findfreeblock(inftl, 0);
 489
 490                if (writeEUN == BLOCK_NIL) {
 491                        /*
 492                         * That didn't work - there were no free blocks just
 493                         * waiting to be picked up. We're going to have to fold
 494                         * a chain to make room.
 495                         */
 496                        thisEUN = INFTL_makefreeblock(inftl, 0xffff);
 497
 498                        /*
 499                         * Hopefully we free something, lets try again.
 500                         * This time we are desperate...
 501                         */
 502                        DEBUG(MTD_DEBUG_LEVEL1, "INFTL: using desperate==1 "
 503                                "to find free EUN to accommodate write to "
 504                                "VUC %d\n", thisVUC);
 505                        writeEUN = INFTL_findfreeblock(inftl, 1);
 506                        if (writeEUN == BLOCK_NIL) {
 507                                /*
 508                                 * Ouch. This should never happen - we should
 509                                 * always be able to make some room somehow. 
 510                                 * If we get here, we've allocated more storage 
 511                                 * space than actual media, or our makefreeblock
 512                                 * routine is missing something.
 513                                 */
 514                                printk(KERN_WARNING "INFTL: cannot make free "
 515                                        "space.\n");
 516#ifdef DEBUG
 517                                INFTL_dumptables(inftl);
 518                                INFTL_dumpVUchains(inftl);
 519#endif
 520                                return BLOCK_NIL;
 521                        }                       
 522                }
 523
 524                /*
 525                 * Insert new block into virtual chain. Firstly update the
 526                 * block headers in flash...
 527                 */
 528                anac = 0;
 529                nacs = 0;
 530                thisEUN = inftl->VUtable[thisVUC];
 531                if (thisEUN != BLOCK_NIL) {
 532                        MTD_READOOB(inftl->mbd.mtd, thisEUN * inftl->EraseSize
 533                                + 8, 8, &retlen, (char *)&oob.u);
 534                        anac = oob.u.a.ANAC + 1;
 535                        nacs = oob.u.a.NACs + 1;
 536                }
 537
 538                prev_block = inftl->VUtable[thisVUC];
 539                if (prev_block < inftl->nb_blocks)
 540                        prev_block -= inftl->firstEUN;
 541
 542                parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0;
 543                parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0;
 544                parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0;
 545                parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0;
 546 
 547                oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC);
 548                oob.u.a.prevUnitNo = cpu_to_le16(prev_block);
 549                oob.u.a.ANAC = anac;
 550                oob.u.a.NACs = nacs;
 551                oob.u.a.parityPerField = parity;
 552                oob.u.a.discarded = 0xaa;
 553
 554                MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + 8, 8,
 555                        &retlen, (char *)&oob.u);
 556
 557                /* Also back up header... */
 558                oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC);
 559                oob.u.b.prevUnitNo = cpu_to_le16(prev_block);
 560                oob.u.b.ANAC = anac;
 561                oob.u.b.NACs = nacs;
 562                oob.u.b.parityPerField = parity;
 563                oob.u.b.discarded = 0xaa;
 564
 565                MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + 
 566                        SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
 567
 568                inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC];
 569                inftl->VUtable[thisVUC] = writeEUN;
 570
 571                inftl->numfreeEUNs--;
 572                return writeEUN;
 573
 574        } while (silly2--);
 575
 576        printk(KERN_WARNING "INFTL: error folding to make room for Virtual "
 577                "Unit Chain 0x%x\n", thisVUC);
 578        return 0xffff;
 579}
 580
 581/*
 582 * Given a Virtual Unit Chain, see if it can be deleted, and if so do it.
 583 */
 584static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
 585{
 586        unsigned char BlockUsed[MAX_SECTORS_PER_UNIT];
 587        unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
 588        unsigned int thisEUN, status;
 589        int block, silly;
 590        struct inftl_bci bci;
 591        size_t retlen;
 592
 593        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_trydeletechain(inftl=%p,"
 594                "thisVUC=%d)\n", inftl, thisVUC);
 595
 596        memset(BlockUsed, 0, sizeof(BlockUsed));
 597        memset(BlockDeleted, 0, sizeof(BlockDeleted));
 598
 599        thisEUN = inftl->VUtable[thisVUC];
 600        if (thisEUN == BLOCK_NIL) {
 601                printk(KERN_WARNING "INFTL: trying to delete non-existent "
 602                       "Virtual Unit Chain %d!\n", thisVUC);
 603                return;
 604        }
 605        
 606        /*
 607         * Scan through the Erase Units to determine whether any data is in
 608         * each of the 512-byte blocks within the Chain.
 609         */
 610        silly = MAX_LOOPS;
 611        while (thisEUN < inftl->nb_blocks) {
 612                for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) {
 613                        if (BlockUsed[block] || BlockDeleted[block])
 614                                continue;
 615
 616                        if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize)
 617                            + (block * SECTORSIZE), 8 , &retlen,
 618                            (char *)&bci) < 0)
 619                                status = SECTOR_IGNORE;
 620                        else
 621                                status = bci.Status | bci.Status1;
 622
 623                        switch(status) {
 624                        case SECTOR_FREE:
 625                        case SECTOR_IGNORE:
 626                                break;
 627                        case SECTOR_USED:
 628                                BlockUsed[block] = 1;
 629                                continue;
 630                        case SECTOR_DELETED:
 631                                BlockDeleted[block] = 1;
 632                                continue;
 633                        default:
 634                                printk(KERN_WARNING "INFTL: unknown status "
 635                                        "for block %d in EUN %d: 0x%x\n",
 636                                        block, thisEUN, status);
 637                        }
 638                }
 639
 640                if (!silly--) {
 641                        printk(KERN_WARNING "INFTL: infinite loop in Virtual "
 642                                "Unit Chain 0x%x\n", thisVUC);
 643                        return;
 644                }
 645                
 646                thisEUN = inftl->PUtable[thisEUN];
 647        }
 648
 649        for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++)
 650                if (BlockUsed[block])
 651                        return;
 652
 653        /*
 654         * For each block in the chain free it and make it available
 655         * for future use. Erase from the oldest unit first.
 656         */
 657        DEBUG(MTD_DEBUG_LEVEL1, "INFTL: deleting empty VUC %d\n", thisVUC);
 658
 659        for (;;) {
 660                u16 *prevEUN = &inftl->VUtable[thisVUC];
 661                thisEUN = *prevEUN;
 662
 663                /* If the chain is all gone already, we're done */
 664                if (thisEUN == BLOCK_NIL) {
 665                        DEBUG(MTD_DEBUG_LEVEL2, "INFTL: Empty VUC %d for deletion was already absent\n", thisEUN);
 666                        return;
 667                }
 668
 669                /* Find oldest unit in chain. */
 670                while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
 671                        BUG_ON(thisEUN >= inftl->nb_blocks);
 672
 673                        prevEUN = &inftl->PUtable[thisEUN];
 674                        thisEUN = *prevEUN;
 675                }
 676
 677                DEBUG(MTD_DEBUG_LEVEL3, "Deleting EUN %d from VUC %d\n",
 678                      thisEUN, thisVUC);
 679
 680                if (INFTL_formatblock(inftl, thisEUN) < 0) {
 681                        /*
 682                         * Could not erase : mark block as reserved.
 683                         */
 684                        inftl->PUtable[thisEUN] = BLOCK_RESERVED;
 685                } else {
 686                        /* Correctly erased : mark it as free */
 687                        inftl->PUtable[thisEUN] = BLOCK_FREE;
 688                        inftl->numfreeEUNs++;
 689                }
 690
 691                /* Now sort out whatever was pointing to it... */
 692                *prevEUN = BLOCK_NIL;
 693
 694                /* Ideally we'd actually be responsive to new
 695                   requests while we're doing this -- if there's
 696                   free space why should others be made to wait? */
 697                cond_resched();
 698        }
 699
 700        inftl->VUtable[thisVUC] = BLOCK_NIL;
 701}
 702
 703static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block)
 704{
 705        unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
 706        unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
 707        unsigned int status;
 708        int silly = MAX_LOOPS;
 709        size_t retlen;
 710        struct inftl_bci bci;
 711
 712        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_deleteblock(inftl=%p,"
 713                "block=%d)\n", inftl, block);
 714
 715        while (thisEUN < inftl->nb_blocks) {
 716                if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
 717                    blockofs, 8, &retlen, (char *)&bci) < 0)
 718                        status = SECTOR_IGNORE;
 719                else
 720                        status = bci.Status | bci.Status1;
 721
 722                switch (status) {
 723                case SECTOR_FREE:
 724                case SECTOR_IGNORE:
 725                        break;
 726                case SECTOR_DELETED:
 727                        thisEUN = BLOCK_NIL;
 728                        goto foundit;
 729                case SECTOR_USED:
 730                        goto foundit;
 731                default:
 732                        printk(KERN_WARNING "INFTL: unknown status for "
 733                                "block %d in EUN %d: 0x%x\n",
 734                                block, thisEUN, status);
 735                        break;
 736                }
 737
 738                if (!silly--) {
 739                        printk(KERN_WARNING "INFTL: infinite loop in Virtual "
 740                                "Unit Chain 0x%x\n",
 741                                block / (inftl->EraseSize / SECTORSIZE));
 742                        return 1;
 743                }
 744                thisEUN = inftl->PUtable[thisEUN];
 745        }
 746
 747foundit:
 748        if (thisEUN != BLOCK_NIL) {
 749                loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
 750
 751                if (MTD_READOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0)
 752                        return -EIO;
 753                bci.Status = bci.Status1 = SECTOR_DELETED;
 754                if (MTD_WRITEOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0)
 755                        return -EIO;
 756                INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE));
 757        }
 758        return 0;
 759}
 760
 761static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, 
 762                            char *buffer)
 763{
 764        struct INFTLrecord *inftl = (void *)mbd;
 765        unsigned int writeEUN;
 766        unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
 767        size_t retlen;
 768        struct inftl_oob oob;
 769        char *p, *pend;
 770
 771        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=%p,block=%ld,"
 772                "buffer=%p)\n", inftl, block, buffer);
 773
 774        /* Is block all zero? */
 775        pend = buffer + SECTORSIZE;
 776        for (p = buffer; p < pend && !*p; p++)
 777                ;
 778
 779        if (p < pend) {
 780                writeEUN = INFTL_findwriteunit(inftl, block);
 781
 782                if (writeEUN == BLOCK_NIL) {
 783                        printk(KERN_WARNING "inftl_writeblock(): cannot find "
 784                                "block to write to\n");
 785                        /*
 786                         * If we _still_ haven't got a block to use,
 787                         * we're screwed.
 788                         */
 789                        return 1;
 790                }
 791
 792                memset(&oob, 0xff, sizeof(struct inftl_oob));
 793                oob.b.Status = oob.b.Status1 = SECTOR_USED;
 794                MTD_WRITEECC(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
 795                        blockofs, SECTORSIZE, &retlen, (char *)buffer,
 796                        (char *)&oob, &inftl->oobinfo);
 797                /*
 798                 * need to write SECTOR_USED flags since they are not written
 799                 * in mtd_writeecc
 800                 */
 801        } else {
 802                INFTL_deleteblock(inftl, block);
 803        }
 804
 805        return 0;
 806}
 807
 808static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
 809                           char *buffer)
 810{
 811        struct INFTLrecord *inftl = (void *)mbd;
 812        unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
 813        unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
 814        unsigned int status;
 815        int silly = MAX_LOOPS;
 816        struct inftl_bci bci;
 817        size_t retlen;
 818
 819        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=%p,block=%ld,"
 820                "buffer=%p)\n", inftl, block, buffer);
 821
 822        while (thisEUN < inftl->nb_blocks) {
 823                if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
 824                     blockofs, 8, &retlen, (char *)&bci) < 0)
 825                        status = SECTOR_IGNORE;
 826                else
 827                        status = bci.Status | bci.Status1;
 828
 829                switch (status) {
 830                case SECTOR_DELETED:
 831                        thisEUN = BLOCK_NIL;
 832                        goto foundit;
 833                case SECTOR_USED:
 834                        goto foundit;
 835                case SECTOR_FREE:
 836                case SECTOR_IGNORE:
 837                        break;
 838                default:
 839                        printk(KERN_WARNING "INFTL: unknown status for "
 840                                "block %ld in EUN %d: 0x%04x\n",
 841                                block, thisEUN, status);
 842                        break;
 843                }
 844
 845                if (!silly--) {
 846                        printk(KERN_WARNING "INFTL: infinite loop in "
 847                                "Virtual Unit Chain 0x%lx\n",
 848                                block / (inftl->EraseSize / SECTORSIZE));
 849                        return 1;
 850                }
 851
 852                thisEUN = inftl->PUtable[thisEUN];
 853        }
 854
 855foundit:
 856        if (thisEUN == BLOCK_NIL) {
 857                /* The requested block is not on the media, return all 0x00 */
 858                memset(buffer, 0, SECTORSIZE);
 859        } else {
 860                size_t retlen;
 861                loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
 862                if (MTD_READ(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen,
 863                    buffer))
 864                        return -EIO;
 865        }
 866        return 0;
 867}
 868
 869static int inftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
 870{
 871        struct INFTLrecord *inftl = (void *)dev;
 872
 873        geo->heads = inftl->heads;
 874        geo->sectors = inftl->sectors;
 875        geo->cylinders = inftl->cylinders;
 876
 877        return 0;
 878}
 879
 880static struct mtd_blktrans_ops inftl_tr = {
 881        .name           = "inftl",
 882        .major          = INFTL_MAJOR,
 883        .part_bits      = INFTL_PARTN_BITS,
 884        .getgeo         = inftl_getgeo,
 885        .readsect       = inftl_readblock,
 886        .writesect      = inftl_writeblock,
 887        .add_mtd        = inftl_add_mtd,
 888        .remove_dev     = inftl_remove_dev,
 889        .owner          = THIS_MODULE,
 890};
 891
 892extern char inftlmountrev[];
 893
 894static int __init init_inftl(void)
 895{
 896        printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.18 $, "
 897                "inftlmount.c %s\n", inftlmountrev);
 898
 899        return register_mtd_blktrans(&inftl_tr);
 900}
 901
 902static void __exit cleanup_inftl(void)
 903{
 904        deregister_mtd_blktrans(&inftl_tr);
 905}
 906
 907module_init(init_inftl);
 908module_exit(cleanup_inftl);
 909
 910MODULE_LICENSE("GPL");
 911MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>, David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
 912MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus");
 913
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.