Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL)
0004  *
0005  * Copyright © 2002, Greg Ungerer (gerg@snapgear.com)
0006  *
0007  * Based heavily on the nftlcore.c code which is:
0008  * Copyright © 1999 Machine Vision Holdings, Inc.
0009  * Copyright © 1999 David Woodhouse <dwmw2@infradead.org>
0010  */
0011 
0012 #include <linux/kernel.h>
0013 #include <linux/module.h>
0014 #include <linux/delay.h>
0015 #include <linux/slab.h>
0016 #include <linux/sched.h>
0017 #include <linux/init.h>
0018 #include <linux/kmod.h>
0019 #include <linux/hdreg.h>
0020 #include <linux/mtd/mtd.h>
0021 #include <linux/mtd/nftl.h>
0022 #include <linux/mtd/inftl.h>
0023 #include <linux/mtd/rawnand.h>
0024 #include <linux/uaccess.h>
0025 #include <asm/errno.h>
0026 #include <asm/io.h>
0027 
0028 /*
0029  * Maximum number of loops while examining next block, to have a
0030  * chance to detect consistency problems (they should never happen
0031  * because of the checks done in the mounting.
0032  */
0033 #define MAX_LOOPS 10000
0034 
0035 static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
0036 {
0037     struct INFTLrecord *inftl;
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     if (!mtd->_block_isbad) {
0047         printk(KERN_ERR
0048 "INFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
0049 "Please use the new diskonchip driver under the NAND subsystem.\n");
0050         return;
0051     }
0052 
0053     pr_debug("INFTL: add_mtd for %s\n", mtd->name);
0054 
0055     inftl = kzalloc(sizeof(*inftl), GFP_KERNEL);
0056 
0057     if (!inftl)
0058         return;
0059 
0060     inftl->mbd.mtd = mtd;
0061     inftl->mbd.devnum = -1;
0062 
0063     inftl->mbd.tr = tr;
0064 
0065     if (INFTL_mount(inftl) < 0) {
0066         printk(KERN_WARNING "INFTL: could not mount device\n");
0067         kfree(inftl);
0068         return;
0069     }
0070 
0071     /* OK, it's a new one. Set up all the data structures. */
0072 
0073     /* Calculate geometry */
0074     inftl->cylinders = 1024;
0075     inftl->heads = 16;
0076 
0077     temp = inftl->cylinders * inftl->heads;
0078     inftl->sectors = inftl->mbd.size / temp;
0079     if (inftl->mbd.size % temp) {
0080         inftl->sectors++;
0081         temp = inftl->cylinders * inftl->sectors;
0082         inftl->heads = inftl->mbd.size / temp;
0083 
0084         if (inftl->mbd.size % temp) {
0085             inftl->heads++;
0086             temp = inftl->heads * inftl->sectors;
0087             inftl->cylinders = inftl->mbd.size / temp;
0088         }
0089     }
0090 
0091     if (inftl->mbd.size != inftl->heads * inftl->cylinders * inftl->sectors) {
0092         /*
0093           Oh no we don't have
0094            mbd.size == heads * cylinders * sectors
0095         */
0096         printk(KERN_WARNING "INFTL: cannot calculate a geometry to "
0097                "match size of 0x%lx.\n", inftl->mbd.size);
0098         printk(KERN_WARNING "INFTL: using C:%d H:%d S:%d "
0099             "(== 0x%lx sects)\n",
0100             inftl->cylinders, inftl->heads , inftl->sectors,
0101             (long)inftl->cylinders * (long)inftl->heads *
0102             (long)inftl->sectors );
0103     }
0104 
0105     if (add_mtd_blktrans_dev(&inftl->mbd)) {
0106         kfree(inftl->PUtable);
0107         kfree(inftl->VUtable);
0108         kfree(inftl);
0109         return;
0110     }
0111 #ifdef PSYCHO_DEBUG
0112     printk(KERN_INFO "INFTL: Found new inftl%c\n", inftl->mbd.devnum + 'a');
0113 #endif
0114     return;
0115 }
0116 
0117 static void inftl_remove_dev(struct mtd_blktrans_dev *dev)
0118 {
0119     struct INFTLrecord *inftl = (void *)dev;
0120 
0121     pr_debug("INFTL: remove_dev (i=%d)\n", dev->devnum);
0122 
0123     del_mtd_blktrans_dev(dev);
0124 
0125     kfree(inftl->PUtable);
0126     kfree(inftl->VUtable);
0127 }
0128 
0129 /*
0130  * Actual INFTL access routines.
0131  */
0132 
0133 /*
0134  * Read oob data from flash
0135  */
0136 int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
0137            size_t *retlen, uint8_t *buf)
0138 {
0139     struct mtd_oob_ops ops;
0140     int res;
0141 
0142     ops.mode = MTD_OPS_PLACE_OOB;
0143     ops.ooboffs = offs & (mtd->writesize - 1);
0144     ops.ooblen = len;
0145     ops.oobbuf = buf;
0146     ops.datbuf = NULL;
0147 
0148     res = mtd_read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
0149     *retlen = ops.oobretlen;
0150     return res;
0151 }
0152 
0153 /*
0154  * Write oob data to flash
0155  */
0156 int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
0157             size_t *retlen, uint8_t *buf)
0158 {
0159     struct mtd_oob_ops ops;
0160     int res;
0161 
0162     ops.mode = MTD_OPS_PLACE_OOB;
0163     ops.ooboffs = offs & (mtd->writesize - 1);
0164     ops.ooblen = len;
0165     ops.oobbuf = buf;
0166     ops.datbuf = NULL;
0167 
0168     res = mtd_write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
0169     *retlen = ops.oobretlen;
0170     return res;
0171 }
0172 
0173 /*
0174  * Write data and oob to flash
0175  */
0176 static int inftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
0177                size_t *retlen, uint8_t *buf, uint8_t *oob)
0178 {
0179     struct mtd_oob_ops ops;
0180     int res;
0181 
0182     ops.mode = MTD_OPS_PLACE_OOB;
0183     ops.ooboffs = offs;
0184     ops.ooblen = mtd->oobsize;
0185     ops.oobbuf = oob;
0186     ops.datbuf = buf;
0187     ops.len = len;
0188 
0189     res = mtd_write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
0190     *retlen = ops.retlen;
0191     return res;
0192 }
0193 
0194 /*
0195  * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition.
0196  *  This function is used when the give Virtual Unit Chain.
0197  */
0198 static u16 INFTL_findfreeblock(struct INFTLrecord *inftl, int desperate)
0199 {
0200     u16 pot = inftl->LastFreeEUN;
0201     int silly = inftl->nb_blocks;
0202 
0203     pr_debug("INFTL: INFTL_findfreeblock(inftl=%p,desperate=%d)\n",
0204             inftl, desperate);
0205 
0206     /*
0207      * Normally, we force a fold to happen before we run out of free
0208      * blocks completely.
0209      */
0210     if (!desperate && inftl->numfreeEUNs < 2) {
0211         pr_debug("INFTL: there are too few free EUNs (%d)\n",
0212                 inftl->numfreeEUNs);
0213         return BLOCK_NIL;
0214     }
0215 
0216     /* Scan for a free block */
0217     do {
0218         if (inftl->PUtable[pot] == BLOCK_FREE) {
0219             inftl->LastFreeEUN = pot;
0220             return pot;
0221         }
0222 
0223         if (++pot > inftl->lastEUN)
0224             pot = 0;
0225 
0226         if (!silly--) {
0227             printk(KERN_WARNING "INFTL: no free blocks found!  "
0228                 "EUN range = %d - %d\n", 0, inftl->LastFreeEUN);
0229             return BLOCK_NIL;
0230         }
0231     } while (pot != inftl->LastFreeEUN);
0232 
0233     return BLOCK_NIL;
0234 }
0235 
0236 static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock)
0237 {
0238     u16 BlockMap[MAX_SECTORS_PER_UNIT];
0239     unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
0240     unsigned int thisEUN, prevEUN, status;
0241     struct mtd_info *mtd = inftl->mbd.mtd;
0242     int block, silly;
0243     unsigned int targetEUN;
0244     struct inftl_oob oob;
0245     size_t retlen;
0246 
0247     pr_debug("INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d,pending=%d)\n",
0248             inftl, thisVUC, pendingblock);
0249 
0250     memset(BlockMap, 0xff, sizeof(BlockMap));
0251     memset(BlockDeleted, 0, sizeof(BlockDeleted));
0252 
0253     thisEUN = targetEUN = inftl->VUtable[thisVUC];
0254 
0255     if (thisEUN == BLOCK_NIL) {
0256         printk(KERN_WARNING "INFTL: trying to fold non-existent "
0257                "Virtual Unit Chain %d!\n", thisVUC);
0258         return BLOCK_NIL;
0259     }
0260 
0261     /*
0262      * Scan to find the Erase Unit which holds the actual data for each
0263      * 512-byte block within the Chain.
0264      */
0265     silly = MAX_LOOPS;
0266     while (thisEUN < inftl->nb_blocks) {
0267         for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) {
0268             if ((BlockMap[block] != BLOCK_NIL) ||
0269                 BlockDeleted[block])
0270                 continue;
0271 
0272             if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
0273                        + (block * SECTORSIZE), 16, &retlen,
0274                        (char *)&oob) < 0)
0275                 status = SECTOR_IGNORE;
0276             else
0277                 status = oob.b.Status | oob.b.Status1;
0278 
0279             switch(status) {
0280             case SECTOR_FREE:
0281             case SECTOR_IGNORE:
0282                 break;
0283             case SECTOR_USED:
0284                 BlockMap[block] = thisEUN;
0285                 continue;
0286             case SECTOR_DELETED:
0287                 BlockDeleted[block] = 1;
0288                 continue;
0289             default:
0290                 printk(KERN_WARNING "INFTL: unknown status "
0291                     "for block %d in EUN %d: %x\n",
0292                     block, thisEUN, status);
0293                 break;
0294             }
0295         }
0296 
0297         if (!silly--) {
0298             printk(KERN_WARNING "INFTL: infinite loop in Virtual "
0299                 "Unit Chain 0x%x\n", thisVUC);
0300             return BLOCK_NIL;
0301         }
0302 
0303         thisEUN = inftl->PUtable[thisEUN];
0304     }
0305 
0306     /*
0307      * OK. We now know the location of every block in the Virtual Unit
0308      * Chain, and the Erase Unit into which we are supposed to be copying.
0309      * Go for it.
0310      */
0311     pr_debug("INFTL: folding chain %d into unit %d\n", thisVUC, targetEUN);
0312 
0313     for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) {
0314         unsigned char movebuf[SECTORSIZE];
0315         int ret;
0316 
0317         /*
0318          * If it's in the target EUN already, or if it's pending write,
0319          * do nothing.
0320          */
0321         if (BlockMap[block] == targetEUN || (pendingblock ==
0322             (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) {
0323             continue;
0324         }
0325 
0326         /*
0327          * Copy only in non free block (free blocks can only
0328                  * happen in case of media errors or deleted blocks).
0329          */
0330         if (BlockMap[block] == BLOCK_NIL)
0331             continue;
0332 
0333         ret = mtd_read(mtd,
0334                    (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE),
0335                    SECTORSIZE,
0336                    &retlen,
0337                    movebuf);
0338         if (ret < 0 && !mtd_is_bitflip(ret)) {
0339             ret = mtd_read(mtd,
0340                        (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE),
0341                        SECTORSIZE,
0342                        &retlen,
0343                        movebuf);
0344             if (ret != -EIO)
0345                 pr_debug("INFTL: error went away on retry?\n");
0346         }
0347         memset(&oob, 0xff, sizeof(struct inftl_oob));
0348         oob.b.Status = oob.b.Status1 = SECTOR_USED;
0349 
0350         inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
0351                 (block * SECTORSIZE), SECTORSIZE, &retlen,
0352                 movebuf, (char *)&oob);
0353     }
0354 
0355     /*
0356      * Newest unit in chain now contains data from _all_ older units.
0357      * So go through and erase each unit in chain, oldest first. (This
0358      * is important, by doing oldest first if we crash/reboot then it
0359      * it is relatively simple to clean up the mess).
0360      */
0361     pr_debug("INFTL: want to erase virtual chain %d\n", thisVUC);
0362 
0363     for (;;) {
0364         /* Find oldest unit in chain. */
0365         thisEUN = inftl->VUtable[thisVUC];
0366         prevEUN = BLOCK_NIL;
0367         while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
0368             prevEUN = thisEUN;
0369             thisEUN = inftl->PUtable[thisEUN];
0370         }
0371 
0372         /* Check if we are all done */
0373         if (thisEUN == targetEUN)
0374             break;
0375 
0376         /* Unlink the last block from the chain. */
0377         inftl->PUtable[prevEUN] = BLOCK_NIL;
0378 
0379         /* Now try to erase it. */
0380         if (INFTL_formatblock(inftl, thisEUN) < 0) {
0381             /*
0382              * Could not erase : mark block as reserved.
0383              */
0384             inftl->PUtable[thisEUN] = BLOCK_RESERVED;
0385         } else {
0386             /* Correctly erased : mark it as free */
0387             inftl->PUtable[thisEUN] = BLOCK_FREE;
0388             inftl->numfreeEUNs++;
0389         }
0390     }
0391 
0392     return targetEUN;
0393 }
0394 
0395 static u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock)
0396 {
0397     /*
0398      * This is the part that needs some cleverness applied.
0399      * For now, I'm doing the minimum applicable to actually
0400      * get the thing to work.
0401      * Wear-levelling and other clever stuff needs to be implemented
0402      * and we also need to do some assessment of the results when
0403      * the system loses power half-way through the routine.
0404      */
0405     u16 LongestChain = 0;
0406     u16 ChainLength = 0, thislen;
0407     u16 chain, EUN;
0408 
0409     pr_debug("INFTL: INFTL_makefreeblock(inftl=%p,"
0410         "pending=%d)\n", inftl, pendingblock);
0411 
0412     for (chain = 0; chain < inftl->nb_blocks; chain++) {
0413         EUN = inftl->VUtable[chain];
0414         thislen = 0;
0415 
0416         while (EUN <= inftl->lastEUN) {
0417             thislen++;
0418             EUN = inftl->PUtable[EUN];
0419             if (thislen > 0xff00) {
0420                 printk(KERN_WARNING "INFTL: endless loop in "
0421                     "Virtual Chain %d: Unit %x\n",
0422                     chain, EUN);
0423                 /*
0424                  * Actually, don't return failure.
0425                  * Just ignore this chain and get on with it.
0426                  */
0427                 thislen = 0;
0428                 break;
0429             }
0430         }
0431 
0432         if (thislen > ChainLength) {
0433             ChainLength = thislen;
0434             LongestChain = chain;
0435         }
0436     }
0437 
0438     if (ChainLength < 2) {
0439         printk(KERN_WARNING "INFTL: no Virtual Unit Chains available "
0440             "for folding. Failing request\n");
0441         return BLOCK_NIL;
0442     }
0443 
0444     return INFTL_foldchain(inftl, LongestChain, pendingblock);
0445 }
0446 
0447 static int nrbits(unsigned int val, int bitcount)
0448 {
0449     int i, total = 0;
0450 
0451     for (i = 0; (i < bitcount); i++)
0452         total += (((0x1 << i) & val) ? 1 : 0);
0453     return total;
0454 }
0455 
0456 /*
0457  * INFTL_findwriteunit: Return the unit number into which we can write
0458  *                      for this block. Make it available if it isn't already.
0459  */
0460 static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
0461 {
0462     unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE);
0463     unsigned int thisEUN, writeEUN, prev_block, status;
0464     unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1);
0465     struct mtd_info *mtd = inftl->mbd.mtd;
0466     struct inftl_oob oob;
0467     struct inftl_bci bci;
0468     unsigned char anac, nacs, parity;
0469     size_t retlen;
0470     int silly, silly2 = 3;
0471 
0472     pr_debug("INFTL: INFTL_findwriteunit(inftl=%p,block=%d)\n",
0473             inftl, block);
0474 
0475     do {
0476         /*
0477          * Scan the media to find a unit in the VUC which has
0478          * a free space for the block in question.
0479          */
0480         writeEUN = BLOCK_NIL;
0481         thisEUN = inftl->VUtable[thisVUC];
0482         silly = MAX_LOOPS;
0483 
0484         while (thisEUN <= inftl->lastEUN) {
0485             inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
0486                        blockofs, 8, &retlen, (char *)&bci);
0487 
0488             status = bci.Status | bci.Status1;
0489             pr_debug("INFTL: status of block %d in EUN %d is %x\n",
0490                     block , writeEUN, status);
0491 
0492             switch(status) {
0493             case SECTOR_FREE:
0494                 writeEUN = thisEUN;
0495                 break;
0496             case SECTOR_DELETED:
0497             case SECTOR_USED:
0498                 /* Can't go any further */
0499                 goto hitused;
0500             case SECTOR_IGNORE:
0501                 break;
0502             default:
0503                 /*
0504                  * Invalid block. Don't use it any more.
0505                  * Must implement.
0506                  */
0507                 break;
0508             }
0509 
0510             if (!silly--) {
0511                 printk(KERN_WARNING "INFTL: infinite loop in "
0512                     "Virtual Unit Chain 0x%x\n", thisVUC);
0513                 return BLOCK_NIL;
0514             }
0515 
0516             /* Skip to next block in chain */
0517             thisEUN = inftl->PUtable[thisEUN];
0518         }
0519 
0520 hitused:
0521         if (writeEUN != BLOCK_NIL)
0522             return writeEUN;
0523 
0524 
0525         /*
0526          * OK. We didn't find one in the existing chain, or there
0527          * is no existing chain. Allocate a new one.
0528          */
0529         writeEUN = INFTL_findfreeblock(inftl, 0);
0530 
0531         if (writeEUN == BLOCK_NIL) {
0532             /*
0533              * That didn't work - there were no free blocks just
0534              * waiting to be picked up. We're going to have to fold
0535              * a chain to make room.
0536              */
0537             thisEUN = INFTL_makefreeblock(inftl, block);
0538 
0539             /*
0540              * Hopefully we free something, lets try again.
0541              * This time we are desperate...
0542              */
0543             pr_debug("INFTL: using desperate==1 to find free EUN "
0544                     "to accommodate write to VUC %d\n",
0545                     thisVUC);
0546             writeEUN = INFTL_findfreeblock(inftl, 1);
0547             if (writeEUN == BLOCK_NIL) {
0548                 /*
0549                  * Ouch. This should never happen - we should
0550                  * always be able to make some room somehow.
0551                  * If we get here, we've allocated more storage
0552                  * space than actual media, or our makefreeblock
0553                  * routine is missing something.
0554                  */
0555                 printk(KERN_WARNING "INFTL: cannot make free "
0556                     "space.\n");
0557 #ifdef DEBUG
0558                 INFTL_dumptables(inftl);
0559                 INFTL_dumpVUchains(inftl);
0560 #endif
0561                 return BLOCK_NIL;
0562             }
0563         }
0564 
0565         /*
0566          * Insert new block into virtual chain. Firstly update the
0567          * block headers in flash...
0568          */
0569         anac = 0;
0570         nacs = 0;
0571         thisEUN = inftl->VUtable[thisVUC];
0572         if (thisEUN != BLOCK_NIL) {
0573             inftl_read_oob(mtd, thisEUN * inftl->EraseSize
0574                        + 8, 8, &retlen, (char *)&oob.u);
0575             anac = oob.u.a.ANAC + 1;
0576             nacs = oob.u.a.NACs + 1;
0577         }
0578 
0579         prev_block = inftl->VUtable[thisVUC];
0580         if (prev_block < inftl->nb_blocks)
0581             prev_block -= inftl->firstEUN;
0582 
0583         parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0;
0584         parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0;
0585         parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0;
0586         parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0;
0587 
0588         oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC);
0589         oob.u.a.prevUnitNo = cpu_to_le16(prev_block);
0590         oob.u.a.ANAC = anac;
0591         oob.u.a.NACs = nacs;
0592         oob.u.a.parityPerField = parity;
0593         oob.u.a.discarded = 0xaa;
0594 
0595         inftl_write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8,
0596                 &retlen, (char *)&oob.u);
0597 
0598         /* Also back up header... */
0599         oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC);
0600         oob.u.b.prevUnitNo = cpu_to_le16(prev_block);
0601         oob.u.b.ANAC = anac;
0602         oob.u.b.NACs = nacs;
0603         oob.u.b.parityPerField = parity;
0604         oob.u.b.discarded = 0xaa;
0605 
0606         inftl_write_oob(mtd, writeEUN * inftl->EraseSize +
0607                 SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
0608 
0609         inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC];
0610         inftl->VUtable[thisVUC] = writeEUN;
0611 
0612         inftl->numfreeEUNs--;
0613         return writeEUN;
0614 
0615     } while (silly2--);
0616 
0617     printk(KERN_WARNING "INFTL: error folding to make room for Virtual "
0618         "Unit Chain 0x%x\n", thisVUC);
0619     return BLOCK_NIL;
0620 }
0621 
0622 /*
0623  * Given a Virtual Unit Chain, see if it can be deleted, and if so do it.
0624  */
0625 static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
0626 {
0627     struct mtd_info *mtd = inftl->mbd.mtd;
0628     unsigned char BlockUsed[MAX_SECTORS_PER_UNIT];
0629     unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
0630     unsigned int thisEUN, status;
0631     int block, silly;
0632     struct inftl_bci bci;
0633     size_t retlen;
0634 
0635     pr_debug("INFTL: INFTL_trydeletechain(inftl=%p,"
0636         "thisVUC=%d)\n", inftl, thisVUC);
0637 
0638     memset(BlockUsed, 0, sizeof(BlockUsed));
0639     memset(BlockDeleted, 0, sizeof(BlockDeleted));
0640 
0641     thisEUN = inftl->VUtable[thisVUC];
0642     if (thisEUN == BLOCK_NIL) {
0643         printk(KERN_WARNING "INFTL: trying to delete non-existent "
0644                "Virtual Unit Chain %d!\n", thisVUC);
0645         return;
0646     }
0647 
0648     /*
0649      * Scan through the Erase Units to determine whether any data is in
0650      * each of the 512-byte blocks within the Chain.
0651      */
0652     silly = MAX_LOOPS;
0653     while (thisEUN < inftl->nb_blocks) {
0654         for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) {
0655             if (BlockUsed[block] || BlockDeleted[block])
0656                 continue;
0657 
0658             if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
0659                        + (block * SECTORSIZE), 8 , &retlen,
0660                       (char *)&bci) < 0)
0661                 status = SECTOR_IGNORE;
0662             else
0663                 status = bci.Status | bci.Status1;
0664 
0665             switch(status) {
0666             case SECTOR_FREE:
0667             case SECTOR_IGNORE:
0668                 break;
0669             case SECTOR_USED:
0670                 BlockUsed[block] = 1;
0671                 continue;
0672             case SECTOR_DELETED:
0673                 BlockDeleted[block] = 1;
0674                 continue;
0675             default:
0676                 printk(KERN_WARNING "INFTL: unknown status "
0677                     "for block %d in EUN %d: 0x%x\n",
0678                     block, thisEUN, status);
0679             }
0680         }
0681 
0682         if (!silly--) {
0683             printk(KERN_WARNING "INFTL: infinite loop in Virtual "
0684                 "Unit Chain 0x%x\n", thisVUC);
0685             return;
0686         }
0687 
0688         thisEUN = inftl->PUtable[thisEUN];
0689     }
0690 
0691     for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++)
0692         if (BlockUsed[block])
0693             return;
0694 
0695     /*
0696      * For each block in the chain free it and make it available
0697      * for future use. Erase from the oldest unit first.
0698      */
0699     pr_debug("INFTL: deleting empty VUC %d\n", thisVUC);
0700 
0701     for (;;) {
0702         u16 *prevEUN = &inftl->VUtable[thisVUC];
0703         thisEUN = *prevEUN;
0704 
0705         /* If the chain is all gone already, we're done */
0706         if (thisEUN == BLOCK_NIL) {
0707             pr_debug("INFTL: Empty VUC %d for deletion was already absent\n", thisEUN);
0708             return;
0709         }
0710 
0711         /* Find oldest unit in chain. */
0712         while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
0713             BUG_ON(thisEUN >= inftl->nb_blocks);
0714 
0715             prevEUN = &inftl->PUtable[thisEUN];
0716             thisEUN = *prevEUN;
0717         }
0718 
0719         pr_debug("Deleting EUN %d from VUC %d\n",
0720               thisEUN, thisVUC);
0721 
0722         if (INFTL_formatblock(inftl, thisEUN) < 0) {
0723             /*
0724              * Could not erase : mark block as reserved.
0725              */
0726             inftl->PUtable[thisEUN] = BLOCK_RESERVED;
0727         } else {
0728             /* Correctly erased : mark it as free */
0729             inftl->PUtable[thisEUN] = BLOCK_FREE;
0730             inftl->numfreeEUNs++;
0731         }
0732 
0733         /* Now sort out whatever was pointing to it... */
0734         *prevEUN = BLOCK_NIL;
0735 
0736         /* Ideally we'd actually be responsive to new
0737            requests while we're doing this -- if there's
0738            free space why should others be made to wait? */
0739         cond_resched();
0740     }
0741 
0742     inftl->VUtable[thisVUC] = BLOCK_NIL;
0743 }
0744 
0745 static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block)
0746 {
0747     unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
0748     unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
0749     struct mtd_info *mtd = inftl->mbd.mtd;
0750     unsigned int status;
0751     int silly = MAX_LOOPS;
0752     size_t retlen;
0753     struct inftl_bci bci;
0754 
0755     pr_debug("INFTL: INFTL_deleteblock(inftl=%p,"
0756         "block=%d)\n", inftl, block);
0757 
0758     while (thisEUN < inftl->nb_blocks) {
0759         if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
0760                    blockofs, 8, &retlen, (char *)&bci) < 0)
0761             status = SECTOR_IGNORE;
0762         else
0763             status = bci.Status | bci.Status1;
0764 
0765         switch (status) {
0766         case SECTOR_FREE:
0767         case SECTOR_IGNORE:
0768             break;
0769         case SECTOR_DELETED:
0770             thisEUN = BLOCK_NIL;
0771             goto foundit;
0772         case SECTOR_USED:
0773             goto foundit;
0774         default:
0775             printk(KERN_WARNING "INFTL: unknown status for "
0776                 "block %d in EUN %d: 0x%x\n",
0777                 block, thisEUN, status);
0778             break;
0779         }
0780 
0781         if (!silly--) {
0782             printk(KERN_WARNING "INFTL: infinite loop in Virtual "
0783                 "Unit Chain 0x%x\n",
0784                 block / (inftl->EraseSize / SECTORSIZE));
0785             return 1;
0786         }
0787         thisEUN = inftl->PUtable[thisEUN];
0788     }
0789 
0790 foundit:
0791     if (thisEUN != BLOCK_NIL) {
0792         loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
0793 
0794         if (inftl_read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
0795             return -EIO;
0796         bci.Status = bci.Status1 = SECTOR_DELETED;
0797         if (inftl_write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
0798             return -EIO;
0799         INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE));
0800     }
0801     return 0;
0802 }
0803 
0804 static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
0805                 char *buffer)
0806 {
0807     struct INFTLrecord *inftl = (void *)mbd;
0808     unsigned int writeEUN;
0809     unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
0810     size_t retlen;
0811     struct inftl_oob oob;
0812     char *p, *pend;
0813 
0814     pr_debug("INFTL: inftl_writeblock(inftl=%p,block=%ld,"
0815         "buffer=%p)\n", inftl, block, buffer);
0816 
0817     /* Is block all zero? */
0818     pend = buffer + SECTORSIZE;
0819     for (p = buffer; p < pend && !*p; p++)
0820         ;
0821 
0822     if (p < pend) {
0823         writeEUN = INFTL_findwriteunit(inftl, block);
0824 
0825         if (writeEUN == BLOCK_NIL) {
0826             printk(KERN_WARNING "inftl_writeblock(): cannot find "
0827                 "block to write to\n");
0828             /*
0829              * If we _still_ haven't got a block to use,
0830              * we're screwed.
0831              */
0832             return 1;
0833         }
0834 
0835         memset(&oob, 0xff, sizeof(struct inftl_oob));
0836         oob.b.Status = oob.b.Status1 = SECTOR_USED;
0837 
0838         inftl_write(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
0839                 blockofs, SECTORSIZE, &retlen, (char *)buffer,
0840                 (char *)&oob);
0841         /*
0842          * need to write SECTOR_USED flags since they are not written
0843          * in mtd_writeecc
0844          */
0845     } else {
0846         INFTL_deleteblock(inftl, block);
0847     }
0848 
0849     return 0;
0850 }
0851 
0852 static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
0853                char *buffer)
0854 {
0855     struct INFTLrecord *inftl = (void *)mbd;
0856     unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
0857     unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
0858     struct mtd_info *mtd = inftl->mbd.mtd;
0859     unsigned int status;
0860     int silly = MAX_LOOPS;
0861     struct inftl_bci bci;
0862     size_t retlen;
0863 
0864     pr_debug("INFTL: inftl_readblock(inftl=%p,block=%ld,"
0865         "buffer=%p)\n", inftl, block, buffer);
0866 
0867     while (thisEUN < inftl->nb_blocks) {
0868         if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
0869                   blockofs, 8, &retlen, (char *)&bci) < 0)
0870             status = SECTOR_IGNORE;
0871         else
0872             status = bci.Status | bci.Status1;
0873 
0874         switch (status) {
0875         case SECTOR_DELETED:
0876             thisEUN = BLOCK_NIL;
0877             goto foundit;
0878         case SECTOR_USED:
0879             goto foundit;
0880         case SECTOR_FREE:
0881         case SECTOR_IGNORE:
0882             break;
0883         default:
0884             printk(KERN_WARNING "INFTL: unknown status for "
0885                 "block %ld in EUN %d: 0x%04x\n",
0886                 block, thisEUN, status);
0887             break;
0888         }
0889 
0890         if (!silly--) {
0891             printk(KERN_WARNING "INFTL: infinite loop in "
0892                 "Virtual Unit Chain 0x%lx\n",
0893                 block / (inftl->EraseSize / SECTORSIZE));
0894             return 1;
0895         }
0896 
0897         thisEUN = inftl->PUtable[thisEUN];
0898     }
0899 
0900 foundit:
0901     if (thisEUN == BLOCK_NIL) {
0902         /* The requested block is not on the media, return all 0x00 */
0903         memset(buffer, 0, SECTORSIZE);
0904     } else {
0905         size_t retlen;
0906         loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
0907         int ret = mtd_read(mtd, ptr, SECTORSIZE, &retlen, buffer);
0908 
0909         /* Handle corrected bit flips gracefully */
0910         if (ret < 0 && !mtd_is_bitflip(ret))
0911             return -EIO;
0912     }
0913     return 0;
0914 }
0915 
0916 static int inftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
0917 {
0918     struct INFTLrecord *inftl = (void *)dev;
0919 
0920     geo->heads = inftl->heads;
0921     geo->sectors = inftl->sectors;
0922     geo->cylinders = inftl->cylinders;
0923 
0924     return 0;
0925 }
0926 
0927 static struct mtd_blktrans_ops inftl_tr = {
0928     .name       = "inftl",
0929     .major      = INFTL_MAJOR,
0930     .part_bits  = INFTL_PARTN_BITS,
0931     .blksize    = 512,
0932     .getgeo     = inftl_getgeo,
0933     .readsect   = inftl_readblock,
0934     .writesect  = inftl_writeblock,
0935     .add_mtd    = inftl_add_mtd,
0936     .remove_dev = inftl_remove_dev,
0937     .owner      = THIS_MODULE,
0938 };
0939 
0940 module_mtd_blktrans(inftl_tr);
0941 
0942 MODULE_LICENSE("GPL");
0943 MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>, David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
0944 MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus");