linux/drivers/mtd/nftlcore.c
<<
>>
Prefs
   1/*
   2 * Linux driver for NAND Flash Translation Layer
   3 *
   4 * Copyright © 1999 Machine Vision Holdings, Inc.
   5 * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License, or
  10 * (at your option) any later version.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20 */
  21
  22#define PRERELEASE
  23
  24#include <linux/kernel.h>
  25#include <linux/module.h>
  26#include <asm/errno.h>
  27#include <asm/io.h>
  28#include <asm/uaccess.h>
  29#include <linux/delay.h>
  30#include <linux/slab.h>
  31#include <linux/init.h>
  32#include <linux/hdreg.h>
  33#include <linux/blkdev.h>
  34
  35#include <linux/kmod.h>
  36#include <linux/mtd/mtd.h>
  37#include <linux/mtd/nand.h>
  38#include <linux/mtd/nftl.h>
  39#include <linux/mtd/blktrans.h>
  40
  41/* maximum number of loops while examining next block, to have a
  42   chance to detect consistency problems (they should never happen
  43   because of the checks done in the mounting */
  44
  45#define MAX_LOOPS 10000
  46
  47
  48static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
  49{
  50        struct NFTLrecord *nftl;
  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        pr_debug("NFTL: add_mtd for %s\n", mtd->name);
  60
  61        nftl = kzalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
  62
  63        if (!nftl)
  64                return;
  65
  66        nftl->mbd.mtd = mtd;
  67        nftl->mbd.devnum = -1;
  68
  69        nftl->mbd.tr = tr;
  70
  71        if (NFTL_mount(nftl) < 0) {
  72                printk(KERN_WARNING "NFTL: could not mount device\n");
  73                kfree(nftl);
  74                return;
  75        }
  76
  77        /* OK, it's a new one. Set up all the data structures. */
  78
  79        /* Calculate geometry */
  80        nftl->cylinders = 1024;
  81        nftl->heads = 16;
  82
  83        temp = nftl->cylinders * nftl->heads;
  84        nftl->sectors = nftl->mbd.size / temp;
  85        if (nftl->mbd.size % temp) {
  86                nftl->sectors++;
  87                temp = nftl->cylinders * nftl->sectors;
  88                nftl->heads = nftl->mbd.size / temp;
  89
  90                if (nftl->mbd.size % temp) {
  91                        nftl->heads++;
  92                        temp = nftl->heads * nftl->sectors;
  93                        nftl->cylinders = nftl->mbd.size / temp;
  94                }
  95        }
  96
  97        if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) {
  98                /*
  99                  Oh no we don't have
 100                   mbd.size == heads * cylinders * sectors
 101                */
 102                printk(KERN_WARNING "NFTL: cannot calculate a geometry to "
 103                       "match size of 0x%lx.\n", nftl->mbd.size);
 104                printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d "
 105                        "(== 0x%lx sects)\n",
 106                        nftl->cylinders, nftl->heads , nftl->sectors,
 107                        (long)nftl->cylinders * (long)nftl->heads *
 108                        (long)nftl->sectors );
 109        }
 110
 111        if (add_mtd_blktrans_dev(&nftl->mbd)) {
 112                kfree(nftl->ReplUnitTable);
 113                kfree(nftl->EUNtable);
 114                kfree(nftl);
 115                return;
 116        }
 117#ifdef PSYCHO_DEBUG
 118        printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
 119#endif
 120}
 121
 122static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
 123{
 124        struct NFTLrecord *nftl = (void *)dev;
 125
 126        pr_debug("NFTL: remove_dev (i=%d)\n", dev->devnum);
 127
 128        del_mtd_blktrans_dev(dev);
 129        kfree(nftl->ReplUnitTable);
 130        kfree(nftl->EUNtable);
 131}
 132
 133/*
 134 * Read oob data from flash
 135 */
 136int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
 137                  size_t *retlen, uint8_t *buf)
 138{
 139        loff_t mask = mtd->writesize - 1;
 140        struct mtd_oob_ops ops;
 141        int res;
 142
 143        ops.mode = MTD_OPS_PLACE_OOB;
 144        ops.ooboffs = offs & mask;
 145        ops.ooblen = len;
 146        ops.oobbuf = buf;
 147        ops.datbuf = NULL;
 148
 149        res = mtd_read_oob(mtd, offs & ~mask, &ops);
 150        *retlen = ops.oobretlen;
 151        return res;
 152}
 153
 154/*
 155 * Write oob data to flash
 156 */
 157int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
 158                   size_t *retlen, uint8_t *buf)
 159{
 160        loff_t mask = mtd->writesize - 1;
 161        struct mtd_oob_ops ops;
 162        int res;
 163
 164        ops.mode = MTD_OPS_PLACE_OOB;
 165        ops.ooboffs = offs & mask;
 166        ops.ooblen = len;
 167        ops.oobbuf = buf;
 168        ops.datbuf = NULL;
 169
 170        res = mtd_write_oob(mtd, offs & ~mask, &ops);
 171        *retlen = ops.oobretlen;
 172        return res;
 173}
 174
 175#ifdef CONFIG_NFTL_RW
 176
 177/*
 178 * Write data and oob to flash
 179 */
 180static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
 181                      size_t *retlen, uint8_t *buf, uint8_t *oob)
 182{
 183        loff_t mask = mtd->writesize - 1;
 184        struct mtd_oob_ops ops;
 185        int res;
 186
 187        ops.mode = MTD_OPS_PLACE_OOB;
 188        ops.ooboffs = offs & mask;
 189        ops.ooblen = mtd->oobsize;
 190        ops.oobbuf = oob;
 191        ops.datbuf = buf;
 192        ops.len = len;
 193
 194        res = mtd_write_oob(mtd, offs & ~mask, &ops);
 195        *retlen = ops.retlen;
 196        return res;
 197}
 198
 199/* Actual NFTL access routines */
 200/* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
 201 *      when the give Virtual Unit Chain
 202 */
 203static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
 204{
 205        /* For a given Virtual Unit Chain: find or create a free block and
 206           add it to the chain */
 207        /* We're passed the number of the last EUN in the chain, to save us from
 208           having to look it up again */
 209        u16 pot = nftl->LastFreeEUN;
 210        int silly = nftl->nb_blocks;
 211
 212        /* Normally, we force a fold to happen before we run out of free blocks completely */
 213        if (!desperate && nftl->numfreeEUNs < 2) {
 214                pr_debug("NFTL_findfreeblock: there are too few free EUNs\n");
 215                return BLOCK_NIL;
 216        }
 217
 218        /* Scan for a free block */
 219        do {
 220                if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
 221                        nftl->LastFreeEUN = pot;
 222                        nftl->numfreeEUNs--;
 223                        return pot;
 224                }
 225
 226                /* This will probably point to the MediaHdr unit itself,
 227                   right at the beginning of the partition. But that unit
 228                   (and the backup unit too) should have the UCI set
 229                   up so that it's not selected for overwriting */
 230                if (++pot > nftl->lastEUN)
 231                        pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
 232
 233                if (!silly--) {
 234                        printk("Argh! No free blocks found! LastFreeEUN = %d, "
 235                               "FirstEUN = %d\n", nftl->LastFreeEUN,
 236                               le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
 237                        return BLOCK_NIL;
 238                }
 239        } while (pot != nftl->LastFreeEUN);
 240
 241        return BLOCK_NIL;
 242}
 243
 244static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
 245{
 246        struct mtd_info *mtd = nftl->mbd.mtd;
 247        u16 BlockMap[MAX_SECTORS_PER_UNIT];
 248        unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
 249        unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
 250        unsigned int thisEUN;
 251        int block;
 252        int silly;
 253        unsigned int targetEUN;
 254        struct nftl_oob oob;
 255        int inplace = 1;
 256        size_t retlen;
 257
 258        memset(BlockMap, 0xff, sizeof(BlockMap));
 259        memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
 260
 261        thisEUN = nftl->EUNtable[thisVUC];
 262
 263        if (thisEUN == BLOCK_NIL) {
 264                printk(KERN_WARNING "Trying to fold non-existent "
 265                       "Virtual Unit Chain %d!\n", thisVUC);
 266                return BLOCK_NIL;
 267        }
 268
 269        /* Scan to find the Erase Unit which holds the actual data for each
 270           512-byte block within the Chain.
 271        */
 272        silly = MAX_LOOPS;
 273        targetEUN = BLOCK_NIL;
 274        while (thisEUN <= nftl->lastEUN ) {
 275                unsigned int status, foldmark;
 276
 277                targetEUN = thisEUN;
 278                for (block = 0; block < nftl->EraseSize / 512; block ++) {
 279                        nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
 280                                      (block * 512), 16 , &retlen,
 281                                      (char *)&oob);
 282                        if (block == 2) {
 283                                foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
 284                                if (foldmark == FOLD_MARK_IN_PROGRESS) {
 285                                        pr_debug("Write Inhibited on EUN %d\n", thisEUN);
 286                                        inplace = 0;
 287                                } else {
 288                                        /* There's no other reason not to do inplace,
 289                                           except ones that come later. So we don't need
 290                                           to preserve inplace */
 291                                        inplace = 1;
 292                                }
 293                        }
 294                        status = oob.b.Status | oob.b.Status1;
 295                        BlockLastState[block] = status;
 296
 297                        switch(status) {
 298                        case SECTOR_FREE:
 299                                BlockFreeFound[block] = 1;
 300                                break;
 301
 302                        case SECTOR_USED:
 303                                if (!BlockFreeFound[block])
 304                                        BlockMap[block] = thisEUN;
 305                                else
 306                                        printk(KERN_WARNING
 307                                               "SECTOR_USED found after SECTOR_FREE "
 308                                               "in Virtual Unit Chain %d for block %d\n",
 309                                               thisVUC, block);
 310                                break;
 311                        case SECTOR_DELETED:
 312                                if (!BlockFreeFound[block])
 313                                        BlockMap[block] = BLOCK_NIL;
 314                                else
 315                                        printk(KERN_WARNING
 316                                               "SECTOR_DELETED found after SECTOR_FREE "
 317                                               "in Virtual Unit Chain %d for block %d\n",
 318                                               thisVUC, block);
 319                                break;
 320
 321                        case SECTOR_IGNORE:
 322                                break;
 323                        default:
 324                                printk("Unknown status for block %d in EUN %d: %x\n",
 325                                       block, thisEUN, status);
 326                        }
 327                }
 328
 329                if (!silly--) {
 330                        printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
 331                               thisVUC);
 332                        return BLOCK_NIL;
 333                }
 334
 335                thisEUN = nftl->ReplUnitTable[thisEUN];
 336        }
 337
 338        if (inplace) {
 339                /* We're being asked to be a fold-in-place. Check
 340                   that all blocks which actually have data associated
 341                   with them (i.e. BlockMap[block] != BLOCK_NIL) are
 342                   either already present or SECTOR_FREE in the target
 343                   block. If not, we're going to have to fold out-of-place
 344                   anyway.
 345                */
 346                for (block = 0; block < nftl->EraseSize / 512 ; block++) {
 347                        if (BlockLastState[block] != SECTOR_FREE &&
 348                            BlockMap[block] != BLOCK_NIL &&
 349                            BlockMap[block] != targetEUN) {
 350                                pr_debug("Setting inplace to 0. VUC %d, "
 351                                      "block %d was %x lastEUN, "
 352                                      "and is in EUN %d (%s) %d\n",
 353                                      thisVUC, block, BlockLastState[block],
 354                                      BlockMap[block],
 355                                      BlockMap[block]== targetEUN ? "==" : "!=",
 356                                      targetEUN);
 357                                inplace = 0;
 358                                break;
 359                        }
 360                }
 361
 362                if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
 363                    pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
 364                    BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
 365                    SECTOR_FREE) {
 366                        pr_debug("Pending write not free in EUN %d. "
 367                              "Folding out of place.\n", targetEUN);
 368                        inplace = 0;
 369                }
 370        }
 371
 372        if (!inplace) {
 373                pr_debug("Cannot fold Virtual Unit Chain %d in place. "
 374                      "Trying out-of-place\n", thisVUC);
 375                /* We need to find a targetEUN to fold into. */
 376                targetEUN = NFTL_findfreeblock(nftl, 1);
 377                if (targetEUN == BLOCK_NIL) {
 378                        /* Ouch. Now we're screwed. We need to do a
 379                           fold-in-place of another chain to make room
 380                           for this one. We need a better way of selecting
 381                           which chain to fold, because makefreeblock will
 382                           only ask us to fold the same one again.
 383                        */
 384                        printk(KERN_WARNING
 385                               "NFTL_findfreeblock(desperate) returns 0xffff.\n");
 386                        return BLOCK_NIL;
 387                }
 388        } else {
 389                /* We put a fold mark in the chain we are folding only if we
 390               fold in place to help the mount check code. If we do not fold in
 391               place, it is possible to find the valid chain by selecting the
 392               longer one */
 393                oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
 394                oob.u.c.unused = 0xffffffff;
 395                nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
 396                               8, &retlen, (char *)&oob.u);
 397        }
 398
 399        /* OK. We now know the location of every block in the Virtual Unit Chain,
 400           and the Erase Unit into which we are supposed to be copying.
 401           Go for it.
 402        */
 403        pr_debug("Folding chain %d into unit %d\n", thisVUC, targetEUN);
 404        for (block = 0; block < nftl->EraseSize / 512 ; block++) {
 405                unsigned char movebuf[512];
 406                int ret;
 407
 408                /* If it's in the target EUN already, or if it's pending write, do nothing */
 409                if (BlockMap[block] == targetEUN ||
 410                    (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
 411                        continue;
 412                }
 413
 414                /* copy only in non free block (free blocks can only
 415                   happen in case of media errors or deleted blocks) */
 416                if (BlockMap[block] == BLOCK_NIL)
 417                        continue;
 418
 419                ret = mtd_read(mtd,
 420                               (nftl->EraseSize * BlockMap[block]) + (block * 512),
 421                               512,
 422                               &retlen,
 423                               movebuf);
 424                if (ret < 0 && !mtd_is_bitflip(ret)) {
 425                        ret = mtd_read(mtd,
 426                                       (nftl->EraseSize * BlockMap[block]) + (block * 512),
 427                                       512,
 428                                       &retlen,
 429                                       movebuf);
 430                        if (ret != -EIO)
 431                                printk("Error went away on retry.\n");
 432                }
 433                memset(&oob, 0xff, sizeof(struct nftl_oob));
 434                oob.b.Status = oob.b.Status1 = SECTOR_USED;
 435
 436                nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) +
 437                           (block * 512), 512, &retlen, movebuf, (char *)&oob);
 438        }
 439
 440        /* add the header so that it is now a valid chain */
 441        oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
 442        oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = BLOCK_NIL;
 443
 444        nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
 445                       8, &retlen, (char *)&oob.u);
 446
 447        /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
 448
 449        /* At this point, we have two different chains for this Virtual Unit, and no way to tell
 450           them apart. If we crash now, we get confused. However, both contain the same data, so we
 451           shouldn't actually lose data in this case. It's just that when we load up on a medium which
 452           has duplicate chains, we need to free one of the chains because it's not necessary any more.
 453        */
 454        thisEUN = nftl->EUNtable[thisVUC];
 455        pr_debug("Want to erase\n");
 456
 457        /* For each block in the old chain (except the targetEUN of course),
 458           free it and make it available for future use */
 459        while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
 460                unsigned int EUNtmp;
 461
 462                EUNtmp = nftl->ReplUnitTable[thisEUN];
 463
 464                if (NFTL_formatblock(nftl, thisEUN) < 0) {
 465                        /* could not erase : mark block as reserved
 466                         */
 467                        nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
 468                } else {
 469                        /* correctly erased : mark it as free */
 470                        nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
 471                        nftl->numfreeEUNs++;
 472                }
 473                thisEUN = EUNtmp;
 474        }
 475
 476        /* Make this the new start of chain for thisVUC */
 477        nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
 478        nftl->EUNtable[thisVUC] = targetEUN;
 479
 480        return targetEUN;
 481}
 482
 483static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
 484{
 485        /* This is the part that needs some cleverness applied.
 486           For now, I'm doing the minimum applicable to actually
 487           get the thing to work.
 488           Wear-levelling and other clever stuff needs to be implemented
 489           and we also need to do some assessment of the results when
 490           the system loses power half-way through the routine.
 491        */
 492        u16 LongestChain = 0;
 493        u16 ChainLength = 0, thislen;
 494        u16 chain, EUN;
 495
 496        for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) {
 497                EUN = nftl->EUNtable[chain];
 498                thislen = 0;
 499
 500                while (EUN <= nftl->lastEUN) {
 501                        thislen++;
 502                        //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
 503                        EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
 504                        if (thislen > 0xff00) {
 505                                printk("Endless loop in Virtual Chain %d: Unit %x\n",
 506                                       chain, EUN);
 507                        }
 508                        if (thislen > 0xff10) {
 509                                /* Actually, don't return failure. Just ignore this chain and
 510                                   get on with it. */
 511                                thislen = 0;
 512                                break;
 513                        }
 514                }
 515
 516                if (thislen > ChainLength) {
 517                        //printk("New longest chain is %d with length %d\n", chain, thislen);
 518                        ChainLength = thislen;
 519                        LongestChain = chain;
 520                }
 521        }
 522
 523        if (ChainLength < 2) {
 524                printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
 525                       "Failing request\n");
 526                return BLOCK_NIL;
 527        }
 528
 529        return NFTL_foldchain (nftl, LongestChain, pendingblock);
 530}
 531
 532/* NFTL_findwriteunit: Return the unit number into which we can write
 533                       for this block. Make it available if it isn't already
 534*/
 535static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
 536{
 537        u16 lastEUN;
 538        u16 thisVUC = block / (nftl->EraseSize / 512);
 539        struct mtd_info *mtd = nftl->mbd.mtd;
 540        unsigned int writeEUN;
 541        unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
 542        size_t retlen;
 543        int silly, silly2 = 3;
 544        struct nftl_oob oob;
 545
 546        do {
 547                /* Scan the media to find a unit in the VUC which has
 548                   a free space for the block in question.
 549                */
 550
 551                /* This condition catches the 0x[7f]fff cases, as well as
 552                   being a sanity check for past-end-of-media access
 553                */
 554                lastEUN = BLOCK_NIL;
 555                writeEUN = nftl->EUNtable[thisVUC];
 556                silly = MAX_LOOPS;
 557                while (writeEUN <= nftl->lastEUN) {
 558                        struct nftl_bci bci;
 559                        size_t retlen;
 560                        unsigned int status;
 561
 562                        lastEUN = writeEUN;
 563
 564                        nftl_read_oob(mtd,
 565                                      (writeEUN * nftl->EraseSize) + blockofs,
 566                                      8, &retlen, (char *)&bci);
 567
 568                        pr_debug("Status of block %d in EUN %d is %x\n",
 569                              block , writeEUN, le16_to_cpu(bci.Status));
 570
 571                        status = bci.Status | bci.Status1;
 572                        switch(status) {
 573                        case SECTOR_FREE:
 574                                return writeEUN;
 575
 576                        case SECTOR_DELETED:
 577                        case SECTOR_USED:
 578                        case SECTOR_IGNORE:
 579                                break;
 580                        default:
 581                                // Invalid block. Don't use it any more. Must implement.
 582                                break;
 583                        }
 584
 585                        if (!silly--) {
 586                                printk(KERN_WARNING
 587                                       "Infinite loop in Virtual Unit Chain 0x%x\n",
 588                                       thisVUC);
 589                                return BLOCK_NIL;
 590                        }
 591
 592                        /* Skip to next block in chain */
 593                        writeEUN = nftl->ReplUnitTable[writeEUN];
 594                }
 595
 596                /* OK. We didn't find one in the existing chain, or there
 597                   is no existing chain. */
 598
 599                /* Try to find an already-free block */
 600                writeEUN = NFTL_findfreeblock(nftl, 0);
 601
 602                if (writeEUN == BLOCK_NIL) {
 603                        /* That didn't work - there were no free blocks just
 604                           waiting to be picked up. We're going to have to fold
 605                           a chain to make room.
 606                        */
 607
 608                        /* First remember the start of this chain */
 609                        //u16 startEUN = nftl->EUNtable[thisVUC];
 610
 611                        //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
 612                        writeEUN = NFTL_makefreeblock(nftl, BLOCK_NIL);
 613
 614                        if (writeEUN == BLOCK_NIL) {
 615                                /* OK, we accept that the above comment is
 616                                   lying - there may have been free blocks
 617                                   last time we called NFTL_findfreeblock(),
 618                                   but they are reserved for when we're
 619                                   desperate. Well, now we're desperate.
 620                                */
 621                                pr_debug("Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
 622                                writeEUN = NFTL_findfreeblock(nftl, 1);
 623                        }
 624                        if (writeEUN == BLOCK_NIL) {
 625                                /* Ouch. This should never happen - we should
 626                                   always be able to make some room somehow.
 627                                   If we get here, we've allocated more storage
 628                                   space than actual media, or our makefreeblock
 629                                   routine is missing something.
 630                                */
 631                                printk(KERN_WARNING "Cannot make free space.\n");
 632                                return BLOCK_NIL;
 633                        }
 634                        //printk("Restarting scan\n");
 635                        lastEUN = BLOCK_NIL;
 636                        continue;
 637                }
 638
 639                /* We've found a free block. Insert it into the chain. */
 640
 641                if (lastEUN != BLOCK_NIL) {
 642                        thisVUC |= 0x8000; /* It's a replacement block */
 643                } else {
 644                        /* The first block in a new chain */
 645                        nftl->EUNtable[thisVUC] = writeEUN;
 646                }
 647
 648                /* set up the actual EUN we're writing into */
 649                /* Both in our cache... */
 650                nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
 651
 652                /* ... and on the flash itself */
 653                nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
 654                              &retlen, (char *)&oob.u);
 655
 656                oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
 657
 658                nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
 659                               &retlen, (char *)&oob.u);
 660
 661                /* we link the new block to the chain only after the
 662                   block is ready. It avoids the case where the chain
 663                   could point to a free block */
 664                if (lastEUN != BLOCK_NIL) {
 665                        /* Both in our cache... */
 666                        nftl->ReplUnitTable[lastEUN] = writeEUN;
 667                        /* ... and on the flash itself */
 668                        nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
 669                                      8, &retlen, (char *)&oob.u);
 670
 671                        oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
 672                                = cpu_to_le16(writeEUN);
 673
 674                        nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
 675                                       8, &retlen, (char *)&oob.u);
 676                }
 677
 678                return writeEUN;
 679
 680        } while (silly2--);
 681
 682        printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
 683               thisVUC);
 684        return BLOCK_NIL;
 685}
 686
 687static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
 688                           char *buffer)
 689{
 690        struct NFTLrecord *nftl = (void *)mbd;
 691        u16 writeEUN;
 692        unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
 693        size_t retlen;
 694        struct nftl_oob oob;
 695
 696        writeEUN = NFTL_findwriteunit(nftl, block);
 697
 698        if (writeEUN == BLOCK_NIL) {
 699                printk(KERN_WARNING
 700                       "NFTL_writeblock(): Cannot find block to write to\n");
 701                /* If we _still_ haven't got a block to use, we're screwed */
 702                return 1;
 703        }
 704
 705        memset(&oob, 0xff, sizeof(struct nftl_oob));
 706        oob.b.Status = oob.b.Status1 = SECTOR_USED;
 707
 708        nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
 709                   512, &retlen, (char *)buffer, (char *)&oob);
 710        return 0;
 711}
 712#endif /* CONFIG_NFTL_RW */
 713
 714static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
 715                          char *buffer)
 716{
 717        struct NFTLrecord *nftl = (void *)mbd;
 718        struct mtd_info *mtd = nftl->mbd.mtd;
 719        u16 lastgoodEUN;
 720        u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
 721        unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
 722        unsigned int status;
 723        int silly = MAX_LOOPS;
 724        size_t retlen;
 725        struct nftl_bci bci;
 726
 727        lastgoodEUN = BLOCK_NIL;
 728
 729        if (thisEUN != BLOCK_NIL) {
 730                while (thisEUN < nftl->nb_blocks) {
 731                        if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
 732                                          blockofs, 8, &retlen,
 733                                          (char *)&bci) < 0)
 734                                status = SECTOR_IGNORE;
 735                        else
 736                                status = bci.Status | bci.Status1;
 737
 738                        switch (status) {
 739                        case SECTOR_FREE:
 740                                /* no modification of a sector should follow a free sector */
 741                                goto the_end;
 742                        case SECTOR_DELETED:
 743                                lastgoodEUN = BLOCK_NIL;
 744                                break;
 745                        case SECTOR_USED:
 746                                lastgoodEUN = thisEUN;
 747                                break;
 748                        case SECTOR_IGNORE:
 749                                break;
 750                        default:
 751                                printk("Unknown status for block %ld in EUN %d: %x\n",
 752                                       block, thisEUN, status);
 753                                break;
 754                        }
 755
 756                        if (!silly--) {
 757                                printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
 758                                       block / (nftl->EraseSize / 512));
 759                                return 1;
 760                        }
 761                        thisEUN = nftl->ReplUnitTable[thisEUN];
 762                }
 763        }
 764
 765 the_end:
 766        if (lastgoodEUN == BLOCK_NIL) {
 767                /* the requested block is not on the media, return all 0x00 */
 768                memset(buffer, 0, 512);
 769        } else {
 770                loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
 771                size_t retlen;
 772                int res = mtd_read(mtd, ptr, 512, &retlen, buffer);
 773
 774                if (res < 0 && !mtd_is_bitflip(res))
 775                        return -EIO;
 776        }
 777        return 0;
 778}
 779
 780static int nftl_getgeo(struct mtd_blktrans_dev *dev,  struct hd_geometry *geo)
 781{
 782        struct NFTLrecord *nftl = (void *)dev;
 783
 784        geo->heads = nftl->heads;
 785        geo->sectors = nftl->sectors;
 786        geo->cylinders = nftl->cylinders;
 787
 788        return 0;
 789}
 790
 791/****************************************************************************
 792 *
 793 * Module stuff
 794 *
 795 ****************************************************************************/
 796
 797
 798static struct mtd_blktrans_ops nftl_tr = {
 799        .name           = "nftl",
 800        .major          = NFTL_MAJOR,
 801        .part_bits      = NFTL_PARTN_BITS,
 802        .blksize        = 512,
 803        .getgeo         = nftl_getgeo,
 804        .readsect       = nftl_readblock,
 805#ifdef CONFIG_NFTL_RW
 806        .writesect      = nftl_writeblock,
 807#endif
 808        .add_mtd        = nftl_add_mtd,
 809        .remove_dev     = nftl_remove_dev,
 810        .owner          = THIS_MODULE,
 811};
 812
 813static int __init init_nftl(void)
 814{
 815        return register_mtd_blktrans(&nftl_tr);
 816}
 817
 818static void __exit cleanup_nftl(void)
 819{
 820        deregister_mtd_blktrans(&nftl_tr);
 821}
 822
 823module_init(init_nftl);
 824module_exit(cleanup_nftl);
 825
 826MODULE_LICENSE("GPL");
 827MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
 828MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");
 829MODULE_ALIAS_BLOCKDEV_MAJOR(NFTL_MAJOR);
 830
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.