Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Linux driver for NAND Flash Translation Layer
0004  *
0005  * Copyright © 1999 Machine Vision Holdings, Inc.
0006  * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org>
0007  */
0008 
0009 #define PRERELEASE
0010 
0011 #include <linux/kernel.h>
0012 #include <linux/module.h>
0013 #include <asm/errno.h>
0014 #include <asm/io.h>
0015 #include <linux/uaccess.h>
0016 #include <linux/delay.h>
0017 #include <linux/slab.h>
0018 #include <linux/init.h>
0019 #include <linux/hdreg.h>
0020 #include <linux/blkdev.h>
0021 
0022 #include <linux/kmod.h>
0023 #include <linux/mtd/mtd.h>
0024 #include <linux/mtd/rawnand.h>
0025 #include <linux/mtd/nftl.h>
0026 #include <linux/mtd/blktrans.h>
0027 
0028 /* maximum number of loops while examining next block, to have a
0029    chance to detect consistency problems (they should never happen
0030    because of the checks done in the mounting */
0031 
0032 #define MAX_LOOPS 10000
0033 
0034 
0035 static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
0036 {
0037     struct NFTLrecord *nftl;
0038     unsigned long temp;
0039 
0040     if (!mtd_type_is_nand(mtd) || mtd->size > UINT_MAX)
0041         return;
0042     /* OK, this is moderately ugly.  But probably safe.  Alternatives? */
0043     if (memcmp(mtd->name, "DiskOnChip", 10))
0044         return;
0045 
0046     pr_debug("NFTL: add_mtd for %s\n", mtd->name);
0047 
0048     nftl = kzalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
0049 
0050     if (!nftl)
0051         return;
0052 
0053     nftl->mbd.mtd = mtd;
0054     nftl->mbd.devnum = -1;
0055 
0056     nftl->mbd.tr = tr;
0057 
0058         if (NFTL_mount(nftl) < 0) {
0059         printk(KERN_WARNING "NFTL: could not mount device\n");
0060         kfree(nftl);
0061         return;
0062         }
0063 
0064     /* OK, it's a new one. Set up all the data structures. */
0065 
0066     /* Calculate geometry */
0067     nftl->cylinders = 1024;
0068     nftl->heads = 16;
0069 
0070     temp = nftl->cylinders * nftl->heads;
0071     nftl->sectors = nftl->mbd.size / temp;
0072     if (nftl->mbd.size % temp) {
0073         nftl->sectors++;
0074         temp = nftl->cylinders * nftl->sectors;
0075         nftl->heads = nftl->mbd.size / temp;
0076 
0077         if (nftl->mbd.size % temp) {
0078             nftl->heads++;
0079             temp = nftl->heads * nftl->sectors;
0080             nftl->cylinders = nftl->mbd.size / temp;
0081         }
0082     }
0083 
0084     if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) {
0085         /*
0086           Oh no we don't have
0087            mbd.size == heads * cylinders * sectors
0088         */
0089         printk(KERN_WARNING "NFTL: cannot calculate a geometry to "
0090                "match size of 0x%lx.\n", nftl->mbd.size);
0091         printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d "
0092             "(== 0x%lx sects)\n",
0093             nftl->cylinders, nftl->heads , nftl->sectors,
0094             (long)nftl->cylinders * (long)nftl->heads *
0095             (long)nftl->sectors );
0096     }
0097 
0098     if (add_mtd_blktrans_dev(&nftl->mbd)) {
0099         kfree(nftl->ReplUnitTable);
0100         kfree(nftl->EUNtable);
0101         kfree(nftl);
0102         return;
0103     }
0104 #ifdef PSYCHO_DEBUG
0105     printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
0106 #endif
0107 }
0108 
0109 static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
0110 {
0111     struct NFTLrecord *nftl = (void *)dev;
0112 
0113     pr_debug("NFTL: remove_dev (i=%d)\n", dev->devnum);
0114 
0115     del_mtd_blktrans_dev(dev);
0116     kfree(nftl->ReplUnitTable);
0117     kfree(nftl->EUNtable);
0118 }
0119 
0120 /*
0121  * Read oob data from flash
0122  */
0123 int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
0124           size_t *retlen, uint8_t *buf)
0125 {
0126     loff_t mask = mtd->writesize - 1;
0127     struct mtd_oob_ops ops;
0128     int res;
0129 
0130     ops.mode = MTD_OPS_PLACE_OOB;
0131     ops.ooboffs = offs & mask;
0132     ops.ooblen = len;
0133     ops.oobbuf = buf;
0134     ops.datbuf = NULL;
0135 
0136     res = mtd_read_oob(mtd, offs & ~mask, &ops);
0137     *retlen = ops.oobretlen;
0138     return res;
0139 }
0140 
0141 /*
0142  * Write oob data to flash
0143  */
0144 int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
0145            size_t *retlen, uint8_t *buf)
0146 {
0147     loff_t mask = mtd->writesize - 1;
0148     struct mtd_oob_ops ops;
0149     int res;
0150 
0151     ops.mode = MTD_OPS_PLACE_OOB;
0152     ops.ooboffs = offs & mask;
0153     ops.ooblen = len;
0154     ops.oobbuf = buf;
0155     ops.datbuf = NULL;
0156 
0157     res = mtd_write_oob(mtd, offs & ~mask, &ops);
0158     *retlen = ops.oobretlen;
0159     return res;
0160 }
0161 
0162 #ifdef CONFIG_NFTL_RW
0163 
0164 /*
0165  * Write data and oob to flash
0166  */
0167 static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
0168               size_t *retlen, uint8_t *buf, uint8_t *oob)
0169 {
0170     loff_t mask = mtd->writesize - 1;
0171     struct mtd_oob_ops ops;
0172     int res;
0173 
0174     ops.mode = MTD_OPS_PLACE_OOB;
0175     ops.ooboffs = offs & mask;
0176     ops.ooblen = mtd->oobsize;
0177     ops.oobbuf = oob;
0178     ops.datbuf = buf;
0179     ops.len = len;
0180 
0181     res = mtd_write_oob(mtd, offs & ~mask, &ops);
0182     *retlen = ops.retlen;
0183     return res;
0184 }
0185 
0186 /* Actual NFTL access routines */
0187 /* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
0188  *  when the give Virtual Unit Chain
0189  */
0190 static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
0191 {
0192     /* For a given Virtual Unit Chain: find or create a free block and
0193        add it to the chain */
0194     /* We're passed the number of the last EUN in the chain, to save us from
0195        having to look it up again */
0196     u16 pot = nftl->LastFreeEUN;
0197     int silly = nftl->nb_blocks;
0198 
0199     /* Normally, we force a fold to happen before we run out of free blocks completely */
0200     if (!desperate && nftl->numfreeEUNs < 2) {
0201         pr_debug("NFTL_findfreeblock: there are too few free EUNs\n");
0202         return BLOCK_NIL;
0203     }
0204 
0205     /* Scan for a free block */
0206     do {
0207         if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
0208             nftl->LastFreeEUN = pot;
0209             nftl->numfreeEUNs--;
0210             return pot;
0211         }
0212 
0213         /* This will probably point to the MediaHdr unit itself,
0214            right at the beginning of the partition. But that unit
0215            (and the backup unit too) should have the UCI set
0216            up so that it's not selected for overwriting */
0217         if (++pot > nftl->lastEUN)
0218             pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
0219 
0220         if (!silly--) {
0221             printk("Argh! No free blocks found! LastFreeEUN = %d, "
0222                    "FirstEUN = %d\n", nftl->LastFreeEUN,
0223                    le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
0224             return BLOCK_NIL;
0225         }
0226     } while (pot != nftl->LastFreeEUN);
0227 
0228     return BLOCK_NIL;
0229 }
0230 
0231 static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
0232 {
0233     struct mtd_info *mtd = nftl->mbd.mtd;
0234     u16 BlockMap[MAX_SECTORS_PER_UNIT];
0235     unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
0236     unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
0237     unsigned int thisEUN;
0238     int block;
0239     int silly;
0240     unsigned int targetEUN;
0241     struct nftl_oob oob;
0242     int inplace = 1;
0243     size_t retlen;
0244 
0245     memset(BlockMap, 0xff, sizeof(BlockMap));
0246     memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
0247 
0248     thisEUN = nftl->EUNtable[thisVUC];
0249 
0250     if (thisEUN == BLOCK_NIL) {
0251         printk(KERN_WARNING "Trying to fold non-existent "
0252                "Virtual Unit Chain %d!\n", thisVUC);
0253         return BLOCK_NIL;
0254     }
0255 
0256     /* Scan to find the Erase Unit which holds the actual data for each
0257        512-byte block within the Chain.
0258     */
0259     silly = MAX_LOOPS;
0260     targetEUN = BLOCK_NIL;
0261     while (thisEUN <= nftl->lastEUN ) {
0262         unsigned int status, foldmark;
0263 
0264         targetEUN = thisEUN;
0265         for (block = 0; block < nftl->EraseSize / 512; block ++) {
0266             nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
0267                       (block * 512), 16 , &retlen,
0268                       (char *)&oob);
0269             if (block == 2) {
0270                 foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
0271                 if (foldmark == FOLD_MARK_IN_PROGRESS) {
0272                     pr_debug("Write Inhibited on EUN %d\n", thisEUN);
0273                     inplace = 0;
0274                 } else {
0275                     /* There's no other reason not to do inplace,
0276                        except ones that come later. So we don't need
0277                        to preserve inplace */
0278                     inplace = 1;
0279                 }
0280             }
0281             status = oob.b.Status | oob.b.Status1;
0282             BlockLastState[block] = status;
0283 
0284             switch(status) {
0285             case SECTOR_FREE:
0286                 BlockFreeFound[block] = 1;
0287                 break;
0288 
0289             case SECTOR_USED:
0290                 if (!BlockFreeFound[block])
0291                     BlockMap[block] = thisEUN;
0292                 else
0293                     printk(KERN_WARNING
0294                            "SECTOR_USED found after SECTOR_FREE "
0295                            "in Virtual Unit Chain %d for block %d\n",
0296                            thisVUC, block);
0297                 break;
0298             case SECTOR_DELETED:
0299                 if (!BlockFreeFound[block])
0300                     BlockMap[block] = BLOCK_NIL;
0301                 else
0302                     printk(KERN_WARNING
0303                            "SECTOR_DELETED found after SECTOR_FREE "
0304                            "in Virtual Unit Chain %d for block %d\n",
0305                            thisVUC, block);
0306                 break;
0307 
0308             case SECTOR_IGNORE:
0309                 break;
0310             default:
0311                 printk("Unknown status for block %d in EUN %d: %x\n",
0312                        block, thisEUN, status);
0313             }
0314         }
0315 
0316         if (!silly--) {
0317             printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
0318                    thisVUC);
0319             return BLOCK_NIL;
0320         }
0321 
0322         thisEUN = nftl->ReplUnitTable[thisEUN];
0323     }
0324 
0325     if (inplace) {
0326         /* We're being asked to be a fold-in-place. Check
0327            that all blocks which actually have data associated
0328            with them (i.e. BlockMap[block] != BLOCK_NIL) are
0329            either already present or SECTOR_FREE in the target
0330            block. If not, we're going to have to fold out-of-place
0331            anyway.
0332         */
0333         for (block = 0; block < nftl->EraseSize / 512 ; block++) {
0334             if (BlockLastState[block] != SECTOR_FREE &&
0335                 BlockMap[block] != BLOCK_NIL &&
0336                 BlockMap[block] != targetEUN) {
0337                 pr_debug("Setting inplace to 0. VUC %d, "
0338                       "block %d was %x lastEUN, "
0339                       "and is in EUN %d (%s) %d\n",
0340                       thisVUC, block, BlockLastState[block],
0341                       BlockMap[block],
0342                       BlockMap[block]== targetEUN ? "==" : "!=",
0343                       targetEUN);
0344                 inplace = 0;
0345                 break;
0346             }
0347         }
0348 
0349         if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
0350             pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
0351             BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
0352             SECTOR_FREE) {
0353             pr_debug("Pending write not free in EUN %d. "
0354                   "Folding out of place.\n", targetEUN);
0355             inplace = 0;
0356         }
0357     }
0358 
0359     if (!inplace) {
0360         pr_debug("Cannot fold Virtual Unit Chain %d in place. "
0361               "Trying out-of-place\n", thisVUC);
0362         /* We need to find a targetEUN to fold into. */
0363         targetEUN = NFTL_findfreeblock(nftl, 1);
0364         if (targetEUN == BLOCK_NIL) {
0365             /* Ouch. Now we're screwed. We need to do a
0366                fold-in-place of another chain to make room
0367                for this one. We need a better way of selecting
0368                which chain to fold, because makefreeblock will
0369                only ask us to fold the same one again.
0370             */
0371             printk(KERN_WARNING
0372                    "NFTL_findfreeblock(desperate) returns 0xffff.\n");
0373             return BLOCK_NIL;
0374         }
0375     } else {
0376         /* We put a fold mark in the chain we are folding only if we
0377                fold in place to help the mount check code. If we do not fold in
0378                place, it is possible to find the valid chain by selecting the
0379                longer one */
0380         oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
0381         oob.u.c.unused = 0xffffffff;
0382         nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
0383                    8, &retlen, (char *)&oob.u);
0384     }
0385 
0386     /* OK. We now know the location of every block in the Virtual Unit Chain,
0387        and the Erase Unit into which we are supposed to be copying.
0388        Go for it.
0389     */
0390     pr_debug("Folding chain %d into unit %d\n", thisVUC, targetEUN);
0391     for (block = 0; block < nftl->EraseSize / 512 ; block++) {
0392         unsigned char movebuf[512];
0393         int ret;
0394 
0395         /* If it's in the target EUN already, or if it's pending write, do nothing */
0396         if (BlockMap[block] == targetEUN ||
0397             (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
0398             continue;
0399         }
0400 
0401         /* copy only in non free block (free blocks can only
0402                    happen in case of media errors or deleted blocks) */
0403         if (BlockMap[block] == BLOCK_NIL)
0404             continue;
0405 
0406         ret = mtd_read(mtd,
0407                    (nftl->EraseSize * BlockMap[block]) + (block * 512),
0408                    512,
0409                    &retlen,
0410                    movebuf);
0411         if (ret < 0 && !mtd_is_bitflip(ret)) {
0412             ret = mtd_read(mtd,
0413                        (nftl->EraseSize * BlockMap[block]) + (block * 512),
0414                        512,
0415                        &retlen,
0416                        movebuf);
0417             if (ret != -EIO)
0418                 printk("Error went away on retry.\n");
0419         }
0420         memset(&oob, 0xff, sizeof(struct nftl_oob));
0421         oob.b.Status = oob.b.Status1 = SECTOR_USED;
0422 
0423         nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) +
0424                (block * 512), 512, &retlen, movebuf, (char *)&oob);
0425     }
0426 
0427     /* add the header so that it is now a valid chain */
0428     oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
0429     oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = BLOCK_NIL;
0430 
0431     nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
0432                8, &retlen, (char *)&oob.u);
0433 
0434     /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
0435 
0436     /* At this point, we have two different chains for this Virtual Unit, and no way to tell
0437        them apart. If we crash now, we get confused. However, both contain the same data, so we
0438        shouldn't actually lose data in this case. It's just that when we load up on a medium which
0439        has duplicate chains, we need to free one of the chains because it's not necessary any more.
0440     */
0441     thisEUN = nftl->EUNtable[thisVUC];
0442     pr_debug("Want to erase\n");
0443 
0444     /* For each block in the old chain (except the targetEUN of course),
0445        free it and make it available for future use */
0446     while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
0447         unsigned int EUNtmp;
0448 
0449         EUNtmp = nftl->ReplUnitTable[thisEUN];
0450 
0451         if (NFTL_formatblock(nftl, thisEUN) < 0) {
0452             /* could not erase : mark block as reserved
0453              */
0454             nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
0455         } else {
0456             /* correctly erased : mark it as free */
0457             nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
0458             nftl->numfreeEUNs++;
0459         }
0460         thisEUN = EUNtmp;
0461     }
0462 
0463     /* Make this the new start of chain for thisVUC */
0464     nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
0465     nftl->EUNtable[thisVUC] = targetEUN;
0466 
0467     return targetEUN;
0468 }
0469 
0470 static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
0471 {
0472     /* This is the part that needs some cleverness applied.
0473        For now, I'm doing the minimum applicable to actually
0474        get the thing to work.
0475        Wear-levelling and other clever stuff needs to be implemented
0476        and we also need to do some assessment of the results when
0477        the system loses power half-way through the routine.
0478     */
0479     u16 LongestChain = 0;
0480     u16 ChainLength = 0, thislen;
0481     u16 chain, EUN;
0482 
0483     for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) {
0484         EUN = nftl->EUNtable[chain];
0485         thislen = 0;
0486 
0487         while (EUN <= nftl->lastEUN) {
0488             thislen++;
0489             //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
0490             EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
0491             if (thislen > 0xff00) {
0492                 printk("Endless loop in Virtual Chain %d: Unit %x\n",
0493                        chain, EUN);
0494             }
0495             if (thislen > 0xff10) {
0496                 /* Actually, don't return failure. Just ignore this chain and
0497                    get on with it. */
0498                 thislen = 0;
0499                 break;
0500             }
0501         }
0502 
0503         if (thislen > ChainLength) {
0504             //printk("New longest chain is %d with length %d\n", chain, thislen);
0505             ChainLength = thislen;
0506             LongestChain = chain;
0507         }
0508     }
0509 
0510     if (ChainLength < 2) {
0511         printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
0512                "Failing request\n");
0513         return BLOCK_NIL;
0514     }
0515 
0516     return NFTL_foldchain (nftl, LongestChain, pendingblock);
0517 }
0518 
0519 /* NFTL_findwriteunit: Return the unit number into which we can write
0520                        for this block. Make it available if it isn't already
0521 */
0522 static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
0523 {
0524     u16 lastEUN;
0525     u16 thisVUC = block / (nftl->EraseSize / 512);
0526     struct mtd_info *mtd = nftl->mbd.mtd;
0527     unsigned int writeEUN;
0528     unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
0529     size_t retlen;
0530     int silly, silly2 = 3;
0531     struct nftl_oob oob;
0532 
0533     do {
0534         /* Scan the media to find a unit in the VUC which has
0535            a free space for the block in question.
0536         */
0537 
0538         /* This condition catches the 0x[7f]fff cases, as well as
0539            being a sanity check for past-end-of-media access
0540         */
0541         lastEUN = BLOCK_NIL;
0542         writeEUN = nftl->EUNtable[thisVUC];
0543         silly = MAX_LOOPS;
0544         while (writeEUN <= nftl->lastEUN) {
0545             struct nftl_bci bci;
0546             size_t retlen;
0547             unsigned int status;
0548 
0549             lastEUN = writeEUN;
0550 
0551             nftl_read_oob(mtd,
0552                       (writeEUN * nftl->EraseSize) + blockofs,
0553                       8, &retlen, (char *)&bci);
0554 
0555             pr_debug("Status of block %d in EUN %d is %x\n",
0556                   block , writeEUN, le16_to_cpu(bci.Status));
0557 
0558             status = bci.Status | bci.Status1;
0559             switch(status) {
0560             case SECTOR_FREE:
0561                 return writeEUN;
0562 
0563             case SECTOR_DELETED:
0564             case SECTOR_USED:
0565             case SECTOR_IGNORE:
0566                 break;
0567             default:
0568                 // Invalid block. Don't use it any more. Must implement.
0569                 break;
0570             }
0571 
0572             if (!silly--) {
0573                 printk(KERN_WARNING
0574                        "Infinite loop in Virtual Unit Chain 0x%x\n",
0575                        thisVUC);
0576                 return BLOCK_NIL;
0577             }
0578 
0579             /* Skip to next block in chain */
0580             writeEUN = nftl->ReplUnitTable[writeEUN];
0581         }
0582 
0583         /* OK. We didn't find one in the existing chain, or there
0584            is no existing chain. */
0585 
0586         /* Try to find an already-free block */
0587         writeEUN = NFTL_findfreeblock(nftl, 0);
0588 
0589         if (writeEUN == BLOCK_NIL) {
0590             /* That didn't work - there were no free blocks just
0591                waiting to be picked up. We're going to have to fold
0592                a chain to make room.
0593             */
0594 
0595             /* First remember the start of this chain */
0596             //u16 startEUN = nftl->EUNtable[thisVUC];
0597 
0598             //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
0599             writeEUN = NFTL_makefreeblock(nftl, BLOCK_NIL);
0600 
0601             if (writeEUN == BLOCK_NIL) {
0602                 /* OK, we accept that the above comment is
0603                    lying - there may have been free blocks
0604                    last time we called NFTL_findfreeblock(),
0605                    but they are reserved for when we're
0606                    desperate. Well, now we're desperate.
0607                 */
0608                 pr_debug("Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
0609                 writeEUN = NFTL_findfreeblock(nftl, 1);
0610             }
0611             if (writeEUN == BLOCK_NIL) {
0612                 /* Ouch. This should never happen - we should
0613                    always be able to make some room somehow.
0614                    If we get here, we've allocated more storage
0615                    space than actual media, or our makefreeblock
0616                    routine is missing something.
0617                 */
0618                 printk(KERN_WARNING "Cannot make free space.\n");
0619                 return BLOCK_NIL;
0620             }
0621             //printk("Restarting scan\n");
0622             continue;
0623         }
0624 
0625         /* We've found a free block. Insert it into the chain. */
0626 
0627         if (lastEUN != BLOCK_NIL) {
0628             thisVUC |= 0x8000; /* It's a replacement block */
0629         } else {
0630             /* The first block in a new chain */
0631             nftl->EUNtable[thisVUC] = writeEUN;
0632         }
0633 
0634         /* set up the actual EUN we're writing into */
0635         /* Both in our cache... */
0636         nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
0637 
0638         /* ... and on the flash itself */
0639         nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
0640                   &retlen, (char *)&oob.u);
0641 
0642         oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
0643 
0644         nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
0645                    &retlen, (char *)&oob.u);
0646 
0647         /* we link the new block to the chain only after the
0648                    block is ready. It avoids the case where the chain
0649                    could point to a free block */
0650         if (lastEUN != BLOCK_NIL) {
0651             /* Both in our cache... */
0652             nftl->ReplUnitTable[lastEUN] = writeEUN;
0653             /* ... and on the flash itself */
0654             nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
0655                       8, &retlen, (char *)&oob.u);
0656 
0657             oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
0658                 = cpu_to_le16(writeEUN);
0659 
0660             nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
0661                        8, &retlen, (char *)&oob.u);
0662         }
0663 
0664         return writeEUN;
0665 
0666     } while (silly2--);
0667 
0668     printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
0669            thisVUC);
0670     return BLOCK_NIL;
0671 }
0672 
0673 static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
0674                char *buffer)
0675 {
0676     struct NFTLrecord *nftl = (void *)mbd;
0677     u16 writeEUN;
0678     unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
0679     size_t retlen;
0680     struct nftl_oob oob;
0681 
0682     writeEUN = NFTL_findwriteunit(nftl, block);
0683 
0684     if (writeEUN == BLOCK_NIL) {
0685         printk(KERN_WARNING
0686                "NFTL_writeblock(): Cannot find block to write to\n");
0687         /* If we _still_ haven't got a block to use, we're screwed */
0688         return 1;
0689     }
0690 
0691     memset(&oob, 0xff, sizeof(struct nftl_oob));
0692     oob.b.Status = oob.b.Status1 = SECTOR_USED;
0693 
0694     nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
0695            512, &retlen, (char *)buffer, (char *)&oob);
0696     return 0;
0697 }
0698 #endif /* CONFIG_NFTL_RW */
0699 
0700 static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
0701               char *buffer)
0702 {
0703     struct NFTLrecord *nftl = (void *)mbd;
0704     struct mtd_info *mtd = nftl->mbd.mtd;
0705     u16 lastgoodEUN;
0706     u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
0707     unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
0708     unsigned int status;
0709     int silly = MAX_LOOPS;
0710     size_t retlen;
0711     struct nftl_bci bci;
0712 
0713     lastgoodEUN = BLOCK_NIL;
0714 
0715     if (thisEUN != BLOCK_NIL) {
0716         while (thisEUN < nftl->nb_blocks) {
0717             if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
0718                       blockofs, 8, &retlen,
0719                       (char *)&bci) < 0)
0720                 status = SECTOR_IGNORE;
0721             else
0722                 status = bci.Status | bci.Status1;
0723 
0724             switch (status) {
0725             case SECTOR_FREE:
0726                 /* no modification of a sector should follow a free sector */
0727                 goto the_end;
0728             case SECTOR_DELETED:
0729                 lastgoodEUN = BLOCK_NIL;
0730                 break;
0731             case SECTOR_USED:
0732                 lastgoodEUN = thisEUN;
0733                 break;
0734             case SECTOR_IGNORE:
0735                 break;
0736             default:
0737                 printk("Unknown status for block %ld in EUN %d: %x\n",
0738                        block, thisEUN, status);
0739                 break;
0740             }
0741 
0742             if (!silly--) {
0743                 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
0744                        block / (nftl->EraseSize / 512));
0745                 return 1;
0746             }
0747             thisEUN = nftl->ReplUnitTable[thisEUN];
0748         }
0749     }
0750 
0751  the_end:
0752     if (lastgoodEUN == BLOCK_NIL) {
0753         /* the requested block is not on the media, return all 0x00 */
0754         memset(buffer, 0, 512);
0755     } else {
0756         loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
0757         size_t retlen;
0758         int res = mtd_read(mtd, ptr, 512, &retlen, buffer);
0759 
0760         if (res < 0 && !mtd_is_bitflip(res))
0761             return -EIO;
0762     }
0763     return 0;
0764 }
0765 
0766 static int nftl_getgeo(struct mtd_blktrans_dev *dev,  struct hd_geometry *geo)
0767 {
0768     struct NFTLrecord *nftl = (void *)dev;
0769 
0770     geo->heads = nftl->heads;
0771     geo->sectors = nftl->sectors;
0772     geo->cylinders = nftl->cylinders;
0773 
0774     return 0;
0775 }
0776 
0777 /****************************************************************************
0778  *
0779  * Module stuff
0780  *
0781  ****************************************************************************/
0782 
0783 
0784 static struct mtd_blktrans_ops nftl_tr = {
0785     .name       = "nftl",
0786     .major      = NFTL_MAJOR,
0787     .part_bits  = NFTL_PARTN_BITS,
0788     .blksize    = 512,
0789     .getgeo     = nftl_getgeo,
0790     .readsect   = nftl_readblock,
0791 #ifdef CONFIG_NFTL_RW
0792     .writesect  = nftl_writeblock,
0793 #endif
0794     .add_mtd    = nftl_add_mtd,
0795     .remove_dev = nftl_remove_dev,
0796     .owner      = THIS_MODULE,
0797 };
0798 
0799 module_mtd_blktrans(nftl_tr);
0800 
0801 MODULE_LICENSE("GPL");
0802 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
0803 MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");
0804 MODULE_ALIAS_BLOCKDEV_MAJOR(NFTL_MAJOR);