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