This source file includes following definitions.
- nftl_add_mtd
- nftl_remove_dev
- nftl_read_oob
- nftl_write_oob
- nftl_write
- NFTL_findfreeblock
- NFTL_foldchain
- NFTL_makefreeblock
- NFTL_findwriteunit
- nftl_writeblock
- nftl_readblock
- nftl_getgeo
- init_nftl
- cleanup_nftl
   1 
   2 
   3 
   4 
   5 
   6 
   7 
   8 
   9 #define PRERELEASE
  10 
  11 #include <linux/kernel.h>
  12 #include <linux/module.h>
  13 #include <asm/errno.h>
  14 #include <asm/io.h>
  15 #include <linux/uaccess.h>
  16 #include <linux/delay.h>
  17 #include <linux/slab.h>
  18 #include <linux/init.h>
  19 #include <linux/hdreg.h>
  20 #include <linux/blkdev.h>
  21 
  22 #include <linux/kmod.h>
  23 #include <linux/mtd/mtd.h>
  24 #include <linux/mtd/rawnand.h>
  25 #include <linux/mtd/nftl.h>
  26 #include <linux/mtd/blktrans.h>
  27 
  28 
  29 
  30 
  31 
  32 #define MAX_LOOPS 10000
  33 
  34 
  35 static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
  36 {
  37         struct NFTLrecord *nftl;
  38         unsigned long temp;
  39 
  40         if (!mtd_type_is_nand(mtd) || mtd->size > UINT_MAX)
  41                 return;
  42         
  43         if (memcmp(mtd->name, "DiskOnChip", 10))
  44                 return;
  45 
  46         pr_debug("NFTL: add_mtd for %s\n", mtd->name);
  47 
  48         nftl = kzalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
  49 
  50         if (!nftl)
  51                 return;
  52 
  53         nftl->mbd.mtd = mtd;
  54         nftl->mbd.devnum = -1;
  55 
  56         nftl->mbd.tr = tr;
  57 
  58         if (NFTL_mount(nftl) < 0) {
  59                 printk(KERN_WARNING "NFTL: could not mount device\n");
  60                 kfree(nftl);
  61                 return;
  62         }
  63 
  64         
  65 
  66         
  67         nftl->cylinders = 1024;
  68         nftl->heads = 16;
  69 
  70         temp = nftl->cylinders * nftl->heads;
  71         nftl->sectors = nftl->mbd.size / temp;
  72         if (nftl->mbd.size % temp) {
  73                 nftl->sectors++;
  74                 temp = nftl->cylinders * nftl->sectors;
  75                 nftl->heads = nftl->mbd.size / temp;
  76 
  77                 if (nftl->mbd.size % temp) {
  78                         nftl->heads++;
  79                         temp = nftl->heads * nftl->sectors;
  80                         nftl->cylinders = nftl->mbd.size / temp;
  81                 }
  82         }
  83 
  84         if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) {
  85                 
  86 
  87 
  88 
  89                 printk(KERN_WARNING "NFTL: cannot calculate a geometry to "
  90                        "match size of 0x%lx.\n", nftl->mbd.size);
  91                 printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d "
  92                         "(== 0x%lx sects)\n",
  93                         nftl->cylinders, nftl->heads , nftl->sectors,
  94                         (long)nftl->cylinders * (long)nftl->heads *
  95                         (long)nftl->sectors );
  96         }
  97 
  98         if (add_mtd_blktrans_dev(&nftl->mbd)) {
  99                 kfree(nftl->ReplUnitTable);
 100                 kfree(nftl->EUNtable);
 101                 kfree(nftl);
 102                 return;
 103         }
 104 #ifdef PSYCHO_DEBUG
 105         printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
 106 #endif
 107 }
 108 
 109 static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
 110 {
 111         struct NFTLrecord *nftl = (void *)dev;
 112 
 113         pr_debug("NFTL: remove_dev (i=%d)\n", dev->devnum);
 114 
 115         del_mtd_blktrans_dev(dev);
 116         kfree(nftl->ReplUnitTable);
 117         kfree(nftl->EUNtable);
 118 }
 119 
 120 
 121 
 122 
 123 int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
 124                   size_t *retlen, uint8_t *buf)
 125 {
 126         loff_t mask = mtd->writesize - 1;
 127         struct mtd_oob_ops ops;
 128         int res;
 129 
 130         ops.mode = MTD_OPS_PLACE_OOB;
 131         ops.ooboffs = offs & mask;
 132         ops.ooblen = len;
 133         ops.oobbuf = buf;
 134         ops.datbuf = NULL;
 135 
 136         res = mtd_read_oob(mtd, offs & ~mask, &ops);
 137         *retlen = ops.oobretlen;
 138         return res;
 139 }
 140 
 141 
 142 
 143 
 144 int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
 145                    size_t *retlen, uint8_t *buf)
 146 {
 147         loff_t mask = mtd->writesize - 1;
 148         struct mtd_oob_ops ops;
 149         int res;
 150 
 151         ops.mode = MTD_OPS_PLACE_OOB;
 152         ops.ooboffs = offs & mask;
 153         ops.ooblen = len;
 154         ops.oobbuf = buf;
 155         ops.datbuf = NULL;
 156 
 157         res = mtd_write_oob(mtd, offs & ~mask, &ops);
 158         *retlen = ops.oobretlen;
 159         return res;
 160 }
 161 
 162 #ifdef CONFIG_NFTL_RW
 163 
 164 
 165 
 166 
 167 static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
 168                       size_t *retlen, uint8_t *buf, uint8_t *oob)
 169 {
 170         loff_t mask = mtd->writesize - 1;
 171         struct mtd_oob_ops ops;
 172         int res;
 173 
 174         ops.mode = MTD_OPS_PLACE_OOB;
 175         ops.ooboffs = offs & mask;
 176         ops.ooblen = mtd->oobsize;
 177         ops.oobbuf = oob;
 178         ops.datbuf = buf;
 179         ops.len = len;
 180 
 181         res = mtd_write_oob(mtd, offs & ~mask, &ops);
 182         *retlen = ops.retlen;
 183         return res;
 184 }
 185 
 186 
 187 
 188 
 189 
 190 static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
 191 {
 192         
 193 
 194         
 195 
 196         u16 pot = nftl->LastFreeEUN;
 197         int silly = nftl->nb_blocks;
 198 
 199         
 200         if (!desperate && nftl->numfreeEUNs < 2) {
 201                 pr_debug("NFTL_findfreeblock: there are too few free EUNs\n");
 202                 return BLOCK_NIL;
 203         }
 204 
 205         
 206         do {
 207                 if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
 208                         nftl->LastFreeEUN = pot;
 209                         nftl->numfreeEUNs--;
 210                         return pot;
 211                 }
 212 
 213                 
 214 
 215 
 216 
 217                 if (++pot > nftl->lastEUN)
 218                         pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
 219 
 220                 if (!silly--) {
 221                         printk("Argh! No free blocks found! LastFreeEUN = %d, "
 222                                "FirstEUN = %d\n", nftl->LastFreeEUN,
 223                                le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
 224                         return BLOCK_NIL;
 225                 }
 226         } while (pot != nftl->LastFreeEUN);
 227 
 228         return BLOCK_NIL;
 229 }
 230 
 231 static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
 232 {
 233         struct mtd_info *mtd = nftl->mbd.mtd;
 234         u16 BlockMap[MAX_SECTORS_PER_UNIT];
 235         unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
 236         unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
 237         unsigned int thisEUN;
 238         int block;
 239         int silly;
 240         unsigned int targetEUN;
 241         struct nftl_oob oob;
 242         int inplace = 1;
 243         size_t retlen;
 244 
 245         memset(BlockMap, 0xff, sizeof(BlockMap));
 246         memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
 247 
 248         thisEUN = nftl->EUNtable[thisVUC];
 249 
 250         if (thisEUN == BLOCK_NIL) {
 251                 printk(KERN_WARNING "Trying to fold non-existent "
 252                        "Virtual Unit Chain %d!\n", thisVUC);
 253                 return BLOCK_NIL;
 254         }
 255 
 256         
 257 
 258 
 259         silly = MAX_LOOPS;
 260         targetEUN = BLOCK_NIL;
 261         while (thisEUN <= nftl->lastEUN ) {
 262                 unsigned int status, foldmark;
 263 
 264                 targetEUN = thisEUN;
 265                 for (block = 0; block < nftl->EraseSize / 512; block ++) {
 266                         nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
 267                                       (block * 512), 16 , &retlen,
 268                                       (char *)&oob);
 269                         if (block == 2) {
 270                                 foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
 271                                 if (foldmark == FOLD_MARK_IN_PROGRESS) {
 272                                         pr_debug("Write Inhibited on EUN %d\n", thisEUN);
 273                                         inplace = 0;
 274                                 } else {
 275                                         
 276 
 277 
 278                                         inplace = 1;
 279                                 }
 280                         }
 281                         status = oob.b.Status | oob.b.Status1;
 282                         BlockLastState[block] = status;
 283 
 284                         switch(status) {
 285                         case SECTOR_FREE:
 286                                 BlockFreeFound[block] = 1;
 287                                 break;
 288 
 289                         case SECTOR_USED:
 290                                 if (!BlockFreeFound[block])
 291                                         BlockMap[block] = thisEUN;
 292                                 else
 293                                         printk(KERN_WARNING
 294                                                "SECTOR_USED found after SECTOR_FREE "
 295                                                "in Virtual Unit Chain %d for block %d\n",
 296                                                thisVUC, block);
 297                                 break;
 298                         case SECTOR_DELETED:
 299                                 if (!BlockFreeFound[block])
 300                                         BlockMap[block] = BLOCK_NIL;
 301                                 else
 302                                         printk(KERN_WARNING
 303                                                "SECTOR_DELETED found after SECTOR_FREE "
 304                                                "in Virtual Unit Chain %d for block %d\n",
 305                                                thisVUC, block);
 306                                 break;
 307 
 308                         case SECTOR_IGNORE:
 309                                 break;
 310                         default:
 311                                 printk("Unknown status for block %d in EUN %d: %x\n",
 312                                        block, thisEUN, status);
 313                         }
 314                 }
 315 
 316                 if (!silly--) {
 317                         printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
 318                                thisVUC);
 319                         return BLOCK_NIL;
 320                 }
 321 
 322                 thisEUN = nftl->ReplUnitTable[thisEUN];
 323         }
 324 
 325         if (inplace) {
 326                 
 327 
 328 
 329 
 330 
 331 
 332 
 333                 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
 334                         if (BlockLastState[block] != SECTOR_FREE &&
 335                             BlockMap[block] != BLOCK_NIL &&
 336                             BlockMap[block] != targetEUN) {
 337                                 pr_debug("Setting inplace to 0. VUC %d, "
 338                                       "block %d was %x lastEUN, "
 339                                       "and is in EUN %d (%s) %d\n",
 340                                       thisVUC, block, BlockLastState[block],
 341                                       BlockMap[block],
 342                                       BlockMap[block]== targetEUN ? "==" : "!=",
 343                                       targetEUN);
 344                                 inplace = 0;
 345                                 break;
 346                         }
 347                 }
 348 
 349                 if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
 350                     pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
 351                     BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
 352                     SECTOR_FREE) {
 353                         pr_debug("Pending write not free in EUN %d. "
 354                               "Folding out of place.\n", targetEUN);
 355                         inplace = 0;
 356                 }
 357         }
 358 
 359         if (!inplace) {
 360                 pr_debug("Cannot fold Virtual Unit Chain %d in place. "
 361                       "Trying out-of-place\n", thisVUC);
 362                 
 363                 targetEUN = NFTL_findfreeblock(nftl, 1);
 364                 if (targetEUN == BLOCK_NIL) {
 365                         
 366 
 367 
 368 
 369 
 370 
 371                         printk(KERN_WARNING
 372                                "NFTL_findfreeblock(desperate) returns 0xffff.\n");
 373                         return BLOCK_NIL;
 374                 }
 375         } else {
 376                 
 377 
 378 
 379 
 380                 oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
 381                 oob.u.c.unused = 0xffffffff;
 382                 nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
 383                                8, &retlen, (char *)&oob.u);
 384         }
 385 
 386         
 387 
 388 
 389 
 390         pr_debug("Folding chain %d into unit %d\n", thisVUC, targetEUN);
 391         for (block = 0; block < nftl->EraseSize / 512 ; block++) {
 392                 unsigned char movebuf[512];
 393                 int ret;
 394 
 395                 
 396                 if (BlockMap[block] == targetEUN ||
 397                     (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
 398                         continue;
 399                 }
 400 
 401                 
 402 
 403                 if (BlockMap[block] == BLOCK_NIL)
 404                         continue;
 405 
 406                 ret = mtd_read(mtd,
 407                                (nftl->EraseSize * BlockMap[block]) + (block * 512),
 408                                512,
 409                                &retlen,
 410                                movebuf);
 411                 if (ret < 0 && !mtd_is_bitflip(ret)) {
 412                         ret = mtd_read(mtd,
 413                                        (nftl->EraseSize * BlockMap[block]) + (block * 512),
 414                                        512,
 415                                        &retlen,
 416                                        movebuf);
 417                         if (ret != -EIO)
 418                                 printk("Error went away on retry.\n");
 419                 }
 420                 memset(&oob, 0xff, sizeof(struct nftl_oob));
 421                 oob.b.Status = oob.b.Status1 = SECTOR_USED;
 422 
 423                 nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) +
 424                            (block * 512), 512, &retlen, movebuf, (char *)&oob);
 425         }
 426 
 427         
 428         oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
 429         oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = BLOCK_NIL;
 430 
 431         nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
 432                        8, &retlen, (char *)&oob.u);
 433 
 434         
 435 
 436         
 437 
 438 
 439 
 440 
 441         thisEUN = nftl->EUNtable[thisVUC];
 442         pr_debug("Want to erase\n");
 443 
 444         
 445 
 446         while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
 447                 unsigned int EUNtmp;
 448 
 449                 EUNtmp = nftl->ReplUnitTable[thisEUN];
 450 
 451                 if (NFTL_formatblock(nftl, thisEUN) < 0) {
 452                         
 453 
 454                         nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
 455                 } else {
 456                         
 457                         nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
 458                         nftl->numfreeEUNs++;
 459                 }
 460                 thisEUN = EUNtmp;
 461         }
 462 
 463         
 464         nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
 465         nftl->EUNtable[thisVUC] = targetEUN;
 466 
 467         return targetEUN;
 468 }
 469 
 470 static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
 471 {
 472         
 473 
 474 
 475 
 476 
 477 
 478 
 479         u16 LongestChain = 0;
 480         u16 ChainLength = 0, thislen;
 481         u16 chain, EUN;
 482 
 483         for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) {
 484                 EUN = nftl->EUNtable[chain];
 485                 thislen = 0;
 486 
 487                 while (EUN <= nftl->lastEUN) {
 488                         thislen++;
 489                         
 490                         EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
 491                         if (thislen > 0xff00) {
 492                                 printk("Endless loop in Virtual Chain %d: Unit %x\n",
 493                                        chain, EUN);
 494                         }
 495                         if (thislen > 0xff10) {
 496                                 
 497 
 498                                 thislen = 0;
 499                                 break;
 500                         }
 501                 }
 502 
 503                 if (thislen > ChainLength) {
 504                         
 505                         ChainLength = thislen;
 506                         LongestChain = chain;
 507                 }
 508         }
 509 
 510         if (ChainLength < 2) {
 511                 printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
 512                        "Failing request\n");
 513                 return BLOCK_NIL;
 514         }
 515 
 516         return NFTL_foldchain (nftl, LongestChain, pendingblock);
 517 }
 518 
 519 
 520 
 521 
 522 static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
 523 {
 524         u16 lastEUN;
 525         u16 thisVUC = block / (nftl->EraseSize / 512);
 526         struct mtd_info *mtd = nftl->mbd.mtd;
 527         unsigned int writeEUN;
 528         unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
 529         size_t retlen;
 530         int silly, silly2 = 3;
 531         struct nftl_oob oob;
 532 
 533         do {
 534                 
 535 
 536 
 537 
 538                 
 539 
 540 
 541                 lastEUN = BLOCK_NIL;
 542                 writeEUN = nftl->EUNtable[thisVUC];
 543                 silly = MAX_LOOPS;
 544                 while (writeEUN <= nftl->lastEUN) {
 545                         struct nftl_bci bci;
 546                         size_t retlen;
 547                         unsigned int status;
 548 
 549                         lastEUN = writeEUN;
 550 
 551                         nftl_read_oob(mtd,
 552                                       (writeEUN * nftl->EraseSize) + blockofs,
 553                                       8, &retlen, (char *)&bci);
 554 
 555                         pr_debug("Status of block %d in EUN %d is %x\n",
 556                               block , writeEUN, le16_to_cpu(bci.Status));
 557 
 558                         status = bci.Status | bci.Status1;
 559                         switch(status) {
 560                         case SECTOR_FREE:
 561                                 return writeEUN;
 562 
 563                         case SECTOR_DELETED:
 564                         case SECTOR_USED:
 565                         case SECTOR_IGNORE:
 566                                 break;
 567                         default:
 568                                 
 569                                 break;
 570                         }
 571 
 572                         if (!silly--) {
 573                                 printk(KERN_WARNING
 574                                        "Infinite loop in Virtual Unit Chain 0x%x\n",
 575                                        thisVUC);
 576                                 return BLOCK_NIL;
 577                         }
 578 
 579                         
 580                         writeEUN = nftl->ReplUnitTable[writeEUN];
 581                 }
 582 
 583                 
 584 
 585 
 586                 
 587                 writeEUN = NFTL_findfreeblock(nftl, 0);
 588 
 589                 if (writeEUN == BLOCK_NIL) {
 590                         
 591 
 592 
 593 
 594 
 595                         
 596                         
 597 
 598                         
 599                         writeEUN = NFTL_makefreeblock(nftl, BLOCK_NIL);
 600 
 601                         if (writeEUN == BLOCK_NIL) {
 602                                 
 603 
 604 
 605 
 606 
 607 
 608                                 pr_debug("Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
 609                                 writeEUN = NFTL_findfreeblock(nftl, 1);
 610                         }
 611                         if (writeEUN == BLOCK_NIL) {
 612                                 
 613 
 614 
 615 
 616 
 617 
 618                                 printk(KERN_WARNING "Cannot make free space.\n");
 619                                 return BLOCK_NIL;
 620                         }
 621                         
 622                         lastEUN = BLOCK_NIL;
 623                         continue;
 624                 }
 625 
 626                 
 627 
 628                 if (lastEUN != BLOCK_NIL) {
 629                         thisVUC |= 0x8000; 
 630                 } else {
 631                         
 632                         nftl->EUNtable[thisVUC] = writeEUN;
 633                 }
 634 
 635                 
 636                 
 637                 nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
 638 
 639                 
 640                 nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
 641                               &retlen, (char *)&oob.u);
 642 
 643                 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
 644 
 645                 nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
 646                                &retlen, (char *)&oob.u);
 647 
 648                 
 649 
 650 
 651                 if (lastEUN != BLOCK_NIL) {
 652                         
 653                         nftl->ReplUnitTable[lastEUN] = writeEUN;
 654                         
 655                         nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
 656                                       8, &retlen, (char *)&oob.u);
 657 
 658                         oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
 659                                 = cpu_to_le16(writeEUN);
 660 
 661                         nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
 662                                        8, &retlen, (char *)&oob.u);
 663                 }
 664 
 665                 return writeEUN;
 666 
 667         } while (silly2--);
 668 
 669         printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
 670                thisVUC);
 671         return BLOCK_NIL;
 672 }
 673 
 674 static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
 675                            char *buffer)
 676 {
 677         struct NFTLrecord *nftl = (void *)mbd;
 678         u16 writeEUN;
 679         unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
 680         size_t retlen;
 681         struct nftl_oob oob;
 682 
 683         writeEUN = NFTL_findwriteunit(nftl, block);
 684 
 685         if (writeEUN == BLOCK_NIL) {
 686                 printk(KERN_WARNING
 687                        "NFTL_writeblock(): Cannot find block to write to\n");
 688                 
 689                 return 1;
 690         }
 691 
 692         memset(&oob, 0xff, sizeof(struct nftl_oob));
 693         oob.b.Status = oob.b.Status1 = SECTOR_USED;
 694 
 695         nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
 696                    512, &retlen, (char *)buffer, (char *)&oob);
 697         return 0;
 698 }
 699 #endif 
 700 
 701 static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
 702                           char *buffer)
 703 {
 704         struct NFTLrecord *nftl = (void *)mbd;
 705         struct mtd_info *mtd = nftl->mbd.mtd;
 706         u16 lastgoodEUN;
 707         u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
 708         unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
 709         unsigned int status;
 710         int silly = MAX_LOOPS;
 711         size_t retlen;
 712         struct nftl_bci bci;
 713 
 714         lastgoodEUN = BLOCK_NIL;
 715 
 716         if (thisEUN != BLOCK_NIL) {
 717                 while (thisEUN < nftl->nb_blocks) {
 718                         if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
 719                                           blockofs, 8, &retlen,
 720                                           (char *)&bci) < 0)
 721                                 status = SECTOR_IGNORE;
 722                         else
 723                                 status = bci.Status | bci.Status1;
 724 
 725                         switch (status) {
 726                         case SECTOR_FREE:
 727                                 
 728                                 goto the_end;
 729                         case SECTOR_DELETED:
 730                                 lastgoodEUN = BLOCK_NIL;
 731                                 break;
 732                         case SECTOR_USED:
 733                                 lastgoodEUN = thisEUN;
 734                                 break;
 735                         case SECTOR_IGNORE:
 736                                 break;
 737                         default:
 738                                 printk("Unknown status for block %ld in EUN %d: %x\n",
 739                                        block, thisEUN, status);
 740                                 break;
 741                         }
 742 
 743                         if (!silly--) {
 744                                 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
 745                                        block / (nftl->EraseSize / 512));
 746                                 return 1;
 747                         }
 748                         thisEUN = nftl->ReplUnitTable[thisEUN];
 749                 }
 750         }
 751 
 752  the_end:
 753         if (lastgoodEUN == BLOCK_NIL) {
 754                 
 755                 memset(buffer, 0, 512);
 756         } else {
 757                 loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
 758                 size_t retlen;
 759                 int res = mtd_read(mtd, ptr, 512, &retlen, buffer);
 760 
 761                 if (res < 0 && !mtd_is_bitflip(res))
 762                         return -EIO;
 763         }
 764         return 0;
 765 }
 766 
 767 static int nftl_getgeo(struct mtd_blktrans_dev *dev,  struct hd_geometry *geo)
 768 {
 769         struct NFTLrecord *nftl = (void *)dev;
 770 
 771         geo->heads = nftl->heads;
 772         geo->sectors = nftl->sectors;
 773         geo->cylinders = nftl->cylinders;
 774 
 775         return 0;
 776 }
 777 
 778 
 779 
 780 
 781 
 782 
 783 
 784 
 785 static struct mtd_blktrans_ops nftl_tr = {
 786         .name           = "nftl",
 787         .major          = NFTL_MAJOR,
 788         .part_bits      = NFTL_PARTN_BITS,
 789         .blksize        = 512,
 790         .getgeo         = nftl_getgeo,
 791         .readsect       = nftl_readblock,
 792 #ifdef CONFIG_NFTL_RW
 793         .writesect      = nftl_writeblock,
 794 #endif
 795         .add_mtd        = nftl_add_mtd,
 796         .remove_dev     = nftl_remove_dev,
 797         .owner          = THIS_MODULE,
 798 };
 799 
 800 static int __init init_nftl(void)
 801 {
 802         return register_mtd_blktrans(&nftl_tr);
 803 }
 804 
 805 static void __exit cleanup_nftl(void)
 806 {
 807         deregister_mtd_blktrans(&nftl_tr);
 808 }
 809 
 810 module_init(init_nftl);
 811 module_exit(cleanup_nftl);
 812 
 813 MODULE_LICENSE("GPL");
 814 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
 815 MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");
 816 MODULE_ALIAS_BLOCKDEV_MAJOR(NFTL_MAJOR);