0001
0002
0003
0004
0005
0006
0007
0008
0009
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
0030
0031
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
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
0072
0073
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
0094
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
0131
0132
0133
0134
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
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
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
0196
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
0208
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
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
0263
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
0308
0309
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
0319
0320
0321 if (BlockMap[block] == targetEUN || (pendingblock ==
0322 (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) {
0323 continue;
0324 }
0325
0326
0327
0328
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
0357
0358
0359
0360
0361 pr_debug("INFTL: want to erase virtual chain %d\n", thisVUC);
0362
0363 for (;;) {
0364
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
0373 if (thisEUN == targetEUN)
0374 break;
0375
0376
0377 inftl->PUtable[prevEUN] = BLOCK_NIL;
0378
0379
0380 if (INFTL_formatblock(inftl, thisEUN) < 0) {
0381
0382
0383
0384 inftl->PUtable[thisEUN] = BLOCK_RESERVED;
0385 } else {
0386
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
0399
0400
0401
0402
0403
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
0425
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
0458
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
0478
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
0499 goto hitused;
0500 case SECTOR_IGNORE:
0501 break;
0502 default:
0503
0504
0505
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
0517 thisEUN = inftl->PUtable[thisEUN];
0518 }
0519
0520 hitused:
0521 if (writeEUN != BLOCK_NIL)
0522 return writeEUN;
0523
0524
0525
0526
0527
0528
0529 writeEUN = INFTL_findfreeblock(inftl, 0);
0530
0531 if (writeEUN == BLOCK_NIL) {
0532
0533
0534
0535
0536
0537 thisEUN = INFTL_makefreeblock(inftl, block);
0538
0539
0540
0541
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
0550
0551
0552
0553
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
0567
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
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
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
0650
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
0697
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
0706 if (thisEUN == BLOCK_NIL) {
0707 pr_debug("INFTL: Empty VUC %d for deletion was already absent\n", thisEUN);
0708 return;
0709 }
0710
0711
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
0725
0726 inftl->PUtable[thisEUN] = BLOCK_RESERVED;
0727 } else {
0728
0729 inftl->PUtable[thisEUN] = BLOCK_FREE;
0730 inftl->numfreeEUNs++;
0731 }
0732
0733
0734 *prevEUN = BLOCK_NIL;
0735
0736
0737
0738
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
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
0830
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
0843
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
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
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");