0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057 #include <linux/mtd/blktrans.h>
0058 #include <linux/module.h>
0059 #include <linux/mtd/mtd.h>
0060
0061
0062 #include <linux/kernel.h>
0063 #include <linux/ptrace.h>
0064 #include <linux/slab.h>
0065 #include <linux/string.h>
0066 #include <linux/timer.h>
0067 #include <linux/major.h>
0068 #include <linux/fs.h>
0069 #include <linux/init.h>
0070 #include <linux/hdreg.h>
0071 #include <linux/vmalloc.h>
0072 #include <linux/blkpg.h>
0073 #include <linux/uaccess.h>
0074
0075 #include <linux/mtd/ftl.h>
0076
0077
0078
0079
0080 static int shuffle_freq = 50;
0081 module_param(shuffle_freq, int, 0);
0082
0083
0084
0085
0086 #ifndef FTL_MAJOR
0087 #define FTL_MAJOR 44
0088 #endif
0089
0090
0091
0092
0093
0094 #define MAX_DEV 4
0095
0096
0097 #define MAX_REGION 4
0098
0099
0100 #define PART_BITS 4
0101
0102
0103 #define MAX_ERASE 8
0104
0105
0106 #define SECTOR_SIZE 512
0107
0108
0109
0110 typedef struct partition_t {
0111 struct mtd_blktrans_dev mbd;
0112 uint32_t state;
0113 uint32_t *VirtualBlockMap;
0114 uint32_t FreeTotal;
0115 struct eun_info_t {
0116 uint32_t Offset;
0117 uint32_t EraseCount;
0118 uint32_t Free;
0119 uint32_t Deleted;
0120 } *EUNInfo;
0121 struct xfer_info_t {
0122 uint32_t Offset;
0123 uint32_t EraseCount;
0124 uint16_t state;
0125 } *XferInfo;
0126 uint16_t bam_index;
0127 uint32_t *bam_cache;
0128 uint16_t DataUnits;
0129 uint32_t BlocksPerUnit;
0130 erase_unit_header_t header;
0131 } partition_t;
0132
0133
0134 #define FTL_FORMATTED 0x01
0135
0136
0137 #define XFER_UNKNOWN 0x00
0138 #define XFER_ERASING 0x01
0139 #define XFER_ERASED 0x02
0140 #define XFER_PREPARED 0x03
0141 #define XFER_FAILED 0x04
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151 static int scan_header(partition_t *part)
0152 {
0153 erase_unit_header_t header;
0154 loff_t offset, max_offset;
0155 size_t ret;
0156 int err;
0157 part->header.FormattedSize = 0;
0158 max_offset = (0x100000<part->mbd.mtd->size)?0x100000:part->mbd.mtd->size;
0159
0160 for (offset = 0;
0161 (offset + sizeof(header)) < max_offset;
0162 offset += part->mbd.mtd->erasesize ? : 0x2000) {
0163
0164 err = mtd_read(part->mbd.mtd, offset, sizeof(header), &ret,
0165 (unsigned char *)&header);
0166
0167 if (err)
0168 return err;
0169
0170 if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break;
0171 }
0172
0173 if (offset == max_offset) {
0174 printk(KERN_NOTICE "ftl_cs: FTL header not found.\n");
0175 return -ENOENT;
0176 }
0177 if (header.BlockSize != 9 ||
0178 (header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) ||
0179 (header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) {
0180 printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n");
0181 return -1;
0182 }
0183 if ((1 << header.EraseUnitSize) != part->mbd.mtd->erasesize) {
0184 printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x\n",
0185 1 << header.EraseUnitSize,part->mbd.mtd->erasesize);
0186 return -1;
0187 }
0188 part->header = header;
0189 return 0;
0190 }
0191
0192 static int build_maps(partition_t *part)
0193 {
0194 erase_unit_header_t header;
0195 uint16_t xvalid, xtrans, i;
0196 unsigned blocks, j;
0197 int hdr_ok, ret = -1;
0198 ssize_t retval;
0199 loff_t offset;
0200
0201
0202 part->DataUnits = le16_to_cpu(part->header.NumEraseUnits) -
0203 part->header.NumTransferUnits;
0204 part->EUNInfo = kmalloc_array(part->DataUnits, sizeof(struct eun_info_t),
0205 GFP_KERNEL);
0206 if (!part->EUNInfo)
0207 goto out;
0208 for (i = 0; i < part->DataUnits; i++)
0209 part->EUNInfo[i].Offset = 0xffffffff;
0210 part->XferInfo =
0211 kmalloc_array(part->header.NumTransferUnits,
0212 sizeof(struct xfer_info_t),
0213 GFP_KERNEL);
0214 if (!part->XferInfo)
0215 goto out_EUNInfo;
0216
0217 xvalid = xtrans = 0;
0218 for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) {
0219 offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN))
0220 << part->header.EraseUnitSize);
0221 ret = mtd_read(part->mbd.mtd, offset, sizeof(header), &retval,
0222 (unsigned char *)&header);
0223
0224 if (ret)
0225 goto out_XferInfo;
0226
0227 ret = -1;
0228
0229 hdr_ok = (strcmp(header.DataOrgTuple+3, "FTL100") == 0);
0230 if (hdr_ok && (le16_to_cpu(header.LogicalEUN) < part->DataUnits) &&
0231 (part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset == 0xffffffff)) {
0232 part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset = offset;
0233 part->EUNInfo[le16_to_cpu(header.LogicalEUN)].EraseCount =
0234 le32_to_cpu(header.EraseCount);
0235 xvalid++;
0236 } else {
0237 if (xtrans == part->header.NumTransferUnits) {
0238 printk(KERN_NOTICE "ftl_cs: format error: too many "
0239 "transfer units!\n");
0240 goto out_XferInfo;
0241 }
0242 if (hdr_ok && (le16_to_cpu(header.LogicalEUN) == 0xffff)) {
0243 part->XferInfo[xtrans].state = XFER_PREPARED;
0244 part->XferInfo[xtrans].EraseCount = le32_to_cpu(header.EraseCount);
0245 } else {
0246 part->XferInfo[xtrans].state = XFER_UNKNOWN;
0247
0248 part->XferInfo[xtrans].EraseCount =
0249 le32_to_cpu(part->header.EraseCount);
0250 }
0251 part->XferInfo[xtrans].Offset = offset;
0252 xtrans++;
0253 }
0254 }
0255
0256 header = part->header;
0257 if ((xtrans != header.NumTransferUnits) ||
0258 (xvalid+xtrans != le16_to_cpu(header.NumEraseUnits))) {
0259 printk(KERN_NOTICE "ftl_cs: format error: erase units "
0260 "don't add up!\n");
0261 goto out_XferInfo;
0262 }
0263
0264
0265 blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize;
0266 part->VirtualBlockMap = vmalloc(array_size(blocks, sizeof(uint32_t)));
0267 if (!part->VirtualBlockMap)
0268 goto out_XferInfo;
0269
0270 memset(part->VirtualBlockMap, 0xff, blocks * sizeof(uint32_t));
0271 part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize;
0272
0273 part->bam_cache = kmalloc_array(part->BlocksPerUnit, sizeof(uint32_t),
0274 GFP_KERNEL);
0275 if (!part->bam_cache)
0276 goto out_VirtualBlockMap;
0277
0278 part->bam_index = 0xffff;
0279 part->FreeTotal = 0;
0280
0281 for (i = 0; i < part->DataUnits; i++) {
0282 part->EUNInfo[i].Free = 0;
0283 part->EUNInfo[i].Deleted = 0;
0284 offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset);
0285
0286 ret = mtd_read(part->mbd.mtd, offset,
0287 part->BlocksPerUnit * sizeof(uint32_t), &retval,
0288 (unsigned char *)part->bam_cache);
0289
0290 if (ret)
0291 goto out_bam_cache;
0292
0293 for (j = 0; j < part->BlocksPerUnit; j++) {
0294 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) {
0295 part->EUNInfo[i].Free++;
0296 part->FreeTotal++;
0297 } else if ((BLOCK_TYPE(le32_to_cpu(part->bam_cache[j])) == BLOCK_DATA) &&
0298 (BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j])) < blocks))
0299 part->VirtualBlockMap[BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j]))] =
0300 (i << header.EraseUnitSize) + (j << header.BlockSize);
0301 else if (BLOCK_DELETED(le32_to_cpu(part->bam_cache[j])))
0302 part->EUNInfo[i].Deleted++;
0303 }
0304 }
0305
0306 ret = 0;
0307 goto out;
0308
0309 out_bam_cache:
0310 kfree(part->bam_cache);
0311 out_VirtualBlockMap:
0312 vfree(part->VirtualBlockMap);
0313 out_XferInfo:
0314 kfree(part->XferInfo);
0315 out_EUNInfo:
0316 kfree(part->EUNInfo);
0317 out:
0318 return ret;
0319 }
0320
0321
0322
0323
0324
0325
0326
0327
0328 static int erase_xfer(partition_t *part,
0329 uint16_t xfernum)
0330 {
0331 int ret;
0332 struct xfer_info_t *xfer;
0333 struct erase_info *erase;
0334
0335 xfer = &part->XferInfo[xfernum];
0336 pr_debug("ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset);
0337 xfer->state = XFER_ERASING;
0338
0339
0340
0341
0342 erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL);
0343 if (!erase)
0344 return -ENOMEM;
0345
0346 erase->addr = xfer->Offset;
0347 erase->len = 1 << part->header.EraseUnitSize;
0348
0349 ret = mtd_erase(part->mbd.mtd, erase);
0350 if (!ret) {
0351 xfer->state = XFER_ERASED;
0352 xfer->EraseCount++;
0353 } else {
0354 xfer->state = XFER_FAILED;
0355 pr_notice("ftl_cs: erase failed: err = %d\n", ret);
0356 }
0357
0358 kfree(erase);
0359
0360 return ret;
0361 }
0362
0363
0364
0365
0366
0367
0368
0369
0370 static int prepare_xfer(partition_t *part, int i)
0371 {
0372 erase_unit_header_t header;
0373 struct xfer_info_t *xfer;
0374 int nbam, ret;
0375 uint32_t ctl;
0376 ssize_t retlen;
0377 loff_t offset;
0378
0379 xfer = &part->XferInfo[i];
0380 xfer->state = XFER_FAILED;
0381
0382 pr_debug("ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset);
0383
0384
0385 header = part->header;
0386 header.LogicalEUN = cpu_to_le16(0xffff);
0387 header.EraseCount = cpu_to_le32(xfer->EraseCount);
0388
0389 ret = mtd_write(part->mbd.mtd, xfer->Offset, sizeof(header), &retlen,
0390 (u_char *)&header);
0391
0392 if (ret) {
0393 return ret;
0394 }
0395
0396
0397 nbam = DIV_ROUND_UP(part->BlocksPerUnit * sizeof(uint32_t) +
0398 le32_to_cpu(part->header.BAMOffset), SECTOR_SIZE);
0399
0400 offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
0401 ctl = cpu_to_le32(BLOCK_CONTROL);
0402
0403 for (i = 0; i < nbam; i++, offset += sizeof(uint32_t)) {
0404
0405 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
0406 (u_char *)&ctl);
0407
0408 if (ret)
0409 return ret;
0410 }
0411 xfer->state = XFER_PREPARED;
0412 return 0;
0413
0414 }
0415
0416
0417
0418
0419
0420
0421
0422
0423
0424
0425
0426
0427
0428 static int copy_erase_unit(partition_t *part, uint16_t srcunit,
0429 uint16_t xferunit)
0430 {
0431 u_char buf[SECTOR_SIZE];
0432 struct eun_info_t *eun;
0433 struct xfer_info_t *xfer;
0434 uint32_t src, dest, free, i;
0435 uint16_t unit;
0436 int ret;
0437 ssize_t retlen;
0438 loff_t offset;
0439 uint16_t srcunitswap = cpu_to_le16(srcunit);
0440
0441 eun = &part->EUNInfo[srcunit];
0442 xfer = &part->XferInfo[xferunit];
0443 pr_debug("ftl_cs: copying block 0x%x to 0x%x\n",
0444 eun->Offset, xfer->Offset);
0445
0446
0447
0448 if (part->bam_index != srcunit) {
0449
0450 offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
0451
0452 ret = mtd_read(part->mbd.mtd, offset,
0453 part->BlocksPerUnit * sizeof(uint32_t), &retlen,
0454 (u_char *)(part->bam_cache));
0455
0456
0457 part->bam_index = 0xffff;
0458
0459 if (ret) {
0460 printk( KERN_WARNING "ftl: Failed to read BAM cache in copy_erase_unit()!\n");
0461 return ret;
0462 }
0463 }
0464
0465
0466 xfer->state = XFER_UNKNOWN;
0467 offset = xfer->Offset + 20;
0468 unit = cpu_to_le16(0x7fff);
0469
0470 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint16_t), &retlen,
0471 (u_char *)&unit);
0472
0473 if (ret) {
0474 printk( KERN_WARNING "ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
0475 return ret;
0476 }
0477
0478
0479 src = eun->Offset; dest = xfer->Offset;
0480
0481 free = 0;
0482 ret = 0;
0483 for (i = 0; i < part->BlocksPerUnit; i++) {
0484 switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) {
0485 case BLOCK_CONTROL:
0486
0487 break;
0488 case BLOCK_DATA:
0489 case BLOCK_REPLACEMENT:
0490 ret = mtd_read(part->mbd.mtd, src, SECTOR_SIZE, &retlen,
0491 (u_char *)buf);
0492 if (ret) {
0493 printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n");
0494 return ret;
0495 }
0496
0497
0498 ret = mtd_write(part->mbd.mtd, dest, SECTOR_SIZE, &retlen,
0499 (u_char *)buf);
0500 if (ret) {
0501 printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n");
0502 return ret;
0503 }
0504
0505 break;
0506 default:
0507
0508 part->bam_cache[i] = cpu_to_le32(0xffffffff);
0509 free++;
0510 break;
0511 }
0512 src += SECTOR_SIZE;
0513 dest += SECTOR_SIZE;
0514 }
0515
0516
0517 ret = mtd_write(part->mbd.mtd,
0518 xfer->Offset + le32_to_cpu(part->header.BAMOffset),
0519 part->BlocksPerUnit * sizeof(int32_t),
0520 &retlen,
0521 (u_char *)part->bam_cache);
0522 if (ret) {
0523 printk( KERN_WARNING "ftl: Error writing BAM in copy_erase_unit\n");
0524 return ret;
0525 }
0526
0527
0528
0529 ret = mtd_write(part->mbd.mtd, xfer->Offset + 20, sizeof(uint16_t),
0530 &retlen, (u_char *)&srcunitswap);
0531
0532 if (ret) {
0533 printk(KERN_WARNING "ftl: Error writing new LogicalEUN in copy_erase_unit\n");
0534 return ret;
0535 }
0536
0537
0538
0539 swap(xfer->EraseCount, eun->EraseCount);
0540 swap(xfer->Offset, eun->Offset);
0541 part->FreeTotal -= eun->Free;
0542 part->FreeTotal += free;
0543 eun->Free = free;
0544 eun->Deleted = 0;
0545
0546
0547 part->bam_index = srcunit;
0548
0549 return 0;
0550 }
0551
0552
0553
0554
0555
0556
0557
0558
0559
0560
0561
0562
0563
0564
0565
0566
0567
0568 static int reclaim_block(partition_t *part)
0569 {
0570 uint16_t i, eun, xfer;
0571 uint32_t best;
0572 int queued, ret;
0573
0574 pr_debug("ftl_cs: reclaiming space...\n");
0575 pr_debug("NumTransferUnits == %x\n", part->header.NumTransferUnits);
0576
0577 best = 0xffffffff; xfer = 0xffff;
0578 do {
0579 queued = 0;
0580 for (i = 0; i < part->header.NumTransferUnits; i++) {
0581 int n=0;
0582 if (part->XferInfo[i].state == XFER_UNKNOWN) {
0583 pr_debug("XferInfo[%d].state == XFER_UNKNOWN\n",i);
0584 n=1;
0585 erase_xfer(part, i);
0586 }
0587 if (part->XferInfo[i].state == XFER_ERASING) {
0588 pr_debug("XferInfo[%d].state == XFER_ERASING\n",i);
0589 n=1;
0590 queued = 1;
0591 }
0592 else if (part->XferInfo[i].state == XFER_ERASED) {
0593 pr_debug("XferInfo[%d].state == XFER_ERASED\n",i);
0594 n=1;
0595 prepare_xfer(part, i);
0596 }
0597 if (part->XferInfo[i].state == XFER_PREPARED) {
0598 pr_debug("XferInfo[%d].state == XFER_PREPARED\n",i);
0599 n=1;
0600 if (part->XferInfo[i].EraseCount <= best) {
0601 best = part->XferInfo[i].EraseCount;
0602 xfer = i;
0603 }
0604 }
0605 if (!n)
0606 pr_debug("XferInfo[%d].state == %x\n",i, part->XferInfo[i].state);
0607
0608 }
0609 if (xfer == 0xffff) {
0610 if (queued) {
0611 pr_debug("ftl_cs: waiting for transfer "
0612 "unit to be prepared...\n");
0613 mtd_sync(part->mbd.mtd);
0614 } else {
0615 static int ne = 0;
0616 if (++ne < 5)
0617 printk(KERN_NOTICE "ftl_cs: reclaim failed: no "
0618 "suitable transfer units!\n");
0619 else
0620 pr_debug("ftl_cs: reclaim failed: no "
0621 "suitable transfer units!\n");
0622
0623 return -EIO;
0624 }
0625 }
0626 } while (xfer == 0xffff);
0627
0628 eun = 0;
0629 if ((jiffies % shuffle_freq) == 0) {
0630 pr_debug("ftl_cs: recycling freshest block...\n");
0631 best = 0xffffffff;
0632 for (i = 0; i < part->DataUnits; i++)
0633 if (part->EUNInfo[i].EraseCount <= best) {
0634 best = part->EUNInfo[i].EraseCount;
0635 eun = i;
0636 }
0637 } else {
0638 best = 0;
0639 for (i = 0; i < part->DataUnits; i++)
0640 if (part->EUNInfo[i].Deleted >= best) {
0641 best = part->EUNInfo[i].Deleted;
0642 eun = i;
0643 }
0644 if (best == 0) {
0645 static int ne = 0;
0646 if (++ne < 5)
0647 printk(KERN_NOTICE "ftl_cs: reclaim failed: "
0648 "no free blocks!\n");
0649 else
0650 pr_debug("ftl_cs: reclaim failed: "
0651 "no free blocks!\n");
0652
0653 return -EIO;
0654 }
0655 }
0656 ret = copy_erase_unit(part, eun, xfer);
0657 if (!ret)
0658 erase_xfer(part, xfer);
0659 else
0660 printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed!\n");
0661 return ret;
0662 }
0663
0664
0665
0666
0667
0668
0669
0670
0671
0672
0673
0674 #ifdef PSYCHO_DEBUG
0675 static void dump_lists(partition_t *part)
0676 {
0677 int i;
0678 printk(KERN_DEBUG "ftl_cs: Free total = %d\n", part->FreeTotal);
0679 for (i = 0; i < part->DataUnits; i++)
0680 printk(KERN_DEBUG "ftl_cs: unit %d: %d phys, %d free, "
0681 "%d deleted\n", i,
0682 part->EUNInfo[i].Offset >> part->header.EraseUnitSize,
0683 part->EUNInfo[i].Free, part->EUNInfo[i].Deleted);
0684 }
0685 #endif
0686
0687 static uint32_t find_free(partition_t *part)
0688 {
0689 uint16_t stop, eun;
0690 uint32_t blk;
0691 size_t retlen;
0692 int ret;
0693
0694
0695 stop = (part->bam_index == 0xffff) ? 0 : part->bam_index;
0696 eun = stop;
0697 do {
0698 if (part->EUNInfo[eun].Free != 0) break;
0699
0700 if (++eun == part->DataUnits) eun = 0;
0701 } while (eun != stop);
0702
0703 if (part->EUNInfo[eun].Free == 0)
0704 return 0;
0705
0706
0707 if (eun != part->bam_index) {
0708
0709 part->bam_index = 0xffff;
0710
0711 ret = mtd_read(part->mbd.mtd,
0712 part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
0713 part->BlocksPerUnit * sizeof(uint32_t),
0714 &retlen,
0715 (u_char *)(part->bam_cache));
0716
0717 if (ret) {
0718 printk(KERN_WARNING"ftl: Error reading BAM in find_free\n");
0719 return 0;
0720 }
0721 part->bam_index = eun;
0722 }
0723
0724
0725 for (blk = 0; blk < part->BlocksPerUnit; blk++)
0726 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break;
0727 if (blk == part->BlocksPerUnit) {
0728 #ifdef PSYCHO_DEBUG
0729 static int ne = 0;
0730 if (++ne == 1)
0731 dump_lists(part);
0732 #endif
0733 printk(KERN_NOTICE "ftl_cs: bad free list!\n");
0734 return 0;
0735 }
0736 pr_debug("ftl_cs: found free block at %d in %d\n", blk, eun);
0737 return blk;
0738
0739 }
0740
0741
0742
0743
0744
0745
0746
0747
0748 static int ftl_read(partition_t *part, caddr_t buffer,
0749 u_long sector, u_long nblocks)
0750 {
0751 uint32_t log_addr, bsize;
0752 u_long i;
0753 int ret;
0754 size_t offset, retlen;
0755
0756 pr_debug("ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
0757 part, sector, nblocks);
0758 if (!(part->state & FTL_FORMATTED)) {
0759 printk(KERN_NOTICE "ftl_cs: bad partition\n");
0760 return -EIO;
0761 }
0762 bsize = 1 << part->header.EraseUnitSize;
0763
0764 for (i = 0; i < nblocks; i++) {
0765 if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) {
0766 printk(KERN_NOTICE "ftl_cs: bad read offset\n");
0767 return -EIO;
0768 }
0769 log_addr = part->VirtualBlockMap[sector+i];
0770 if (log_addr == 0xffffffff)
0771 memset(buffer, 0, SECTOR_SIZE);
0772 else {
0773 offset = (part->EUNInfo[log_addr / bsize].Offset
0774 + (log_addr % bsize));
0775 ret = mtd_read(part->mbd.mtd, offset, SECTOR_SIZE, &retlen,
0776 (u_char *)buffer);
0777
0778 if (ret) {
0779 printk(KERN_WARNING "Error reading MTD device in ftl_read()\n");
0780 return ret;
0781 }
0782 }
0783 buffer += SECTOR_SIZE;
0784 }
0785 return 0;
0786 }
0787
0788
0789
0790
0791
0792
0793
0794 static int set_bam_entry(partition_t *part, uint32_t log_addr,
0795 uint32_t virt_addr)
0796 {
0797 uint32_t bsize, blk, le_virt_addr;
0798 #ifdef PSYCHO_DEBUG
0799 uint32_t old_addr;
0800 #endif
0801 uint16_t eun;
0802 int ret;
0803 size_t retlen, offset;
0804
0805 pr_debug("ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
0806 part, log_addr, virt_addr);
0807 bsize = 1 << part->header.EraseUnitSize;
0808 eun = log_addr / bsize;
0809 blk = (log_addr % bsize) / SECTOR_SIZE;
0810 offset = (part->EUNInfo[eun].Offset + blk * sizeof(uint32_t) +
0811 le32_to_cpu(part->header.BAMOffset));
0812
0813 #ifdef PSYCHO_DEBUG
0814 ret = mtd_read(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
0815 (u_char *)&old_addr);
0816 if (ret) {
0817 printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
0818 return ret;
0819 }
0820 old_addr = le32_to_cpu(old_addr);
0821
0822 if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) ||
0823 ((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) ||
0824 (!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) {
0825 static int ne = 0;
0826 if (++ne < 5) {
0827 printk(KERN_NOTICE "ftl_cs: set_bam_entry() inconsistency!\n");
0828 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, old = 0x%x"
0829 ", new = 0x%x\n", log_addr, old_addr, virt_addr);
0830 }
0831 return -EIO;
0832 }
0833 #endif
0834 le_virt_addr = cpu_to_le32(virt_addr);
0835 if (part->bam_index == eun) {
0836 #ifdef PSYCHO_DEBUG
0837 if (le32_to_cpu(part->bam_cache[blk]) != old_addr) {
0838 static int ne = 0;
0839 if (++ne < 5) {
0840 printk(KERN_NOTICE "ftl_cs: set_bam_entry() "
0841 "inconsistency!\n");
0842 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, cache"
0843 " = 0x%x\n",
0844 le32_to_cpu(part->bam_cache[blk]), old_addr);
0845 }
0846 return -EIO;
0847 }
0848 #endif
0849 part->bam_cache[blk] = le_virt_addr;
0850 }
0851 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
0852 (u_char *)&le_virt_addr);
0853
0854 if (ret) {
0855 printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!\n");
0856 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, new = 0x%x\n",
0857 log_addr, virt_addr);
0858 }
0859 return ret;
0860 }
0861
0862 static int ftl_write(partition_t *part, caddr_t buffer,
0863 u_long sector, u_long nblocks)
0864 {
0865 uint32_t bsize, log_addr, virt_addr, old_addr, blk;
0866 u_long i;
0867 int ret;
0868 size_t retlen, offset;
0869
0870 pr_debug("ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
0871 part, sector, nblocks);
0872 if (!(part->state & FTL_FORMATTED)) {
0873 printk(KERN_NOTICE "ftl_cs: bad partition\n");
0874 return -EIO;
0875 }
0876
0877 while (part->FreeTotal < nblocks) {
0878 ret = reclaim_block(part);
0879 if (ret)
0880 return ret;
0881 }
0882
0883 bsize = 1 << part->header.EraseUnitSize;
0884
0885 virt_addr = sector * SECTOR_SIZE | BLOCK_DATA;
0886 for (i = 0; i < nblocks; i++) {
0887 if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) {
0888 printk(KERN_NOTICE "ftl_cs: bad write offset\n");
0889 return -EIO;
0890 }
0891
0892
0893 blk = find_free(part);
0894 if (blk == 0) {
0895 static int ne = 0;
0896 if (++ne < 5)
0897 printk(KERN_NOTICE "ftl_cs: internal error: "
0898 "no free blocks!\n");
0899 return -ENOSPC;
0900 }
0901
0902
0903 log_addr = part->bam_index * bsize + blk * SECTOR_SIZE;
0904 part->EUNInfo[part->bam_index].Free--;
0905 part->FreeTotal--;
0906 if (set_bam_entry(part, log_addr, 0xfffffffe))
0907 return -EIO;
0908 part->EUNInfo[part->bam_index].Deleted++;
0909 offset = (part->EUNInfo[part->bam_index].Offset +
0910 blk * SECTOR_SIZE);
0911 ret = mtd_write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, buffer);
0912
0913 if (ret) {
0914 printk(KERN_NOTICE "ftl_cs: block write failed!\n");
0915 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, virt_addr"
0916 " = 0x%x, Offset = 0x%zx\n", log_addr, virt_addr,
0917 offset);
0918 return -EIO;
0919 }
0920
0921
0922 old_addr = part->VirtualBlockMap[sector+i];
0923 if (old_addr != 0xffffffff) {
0924 part->VirtualBlockMap[sector+i] = 0xffffffff;
0925 part->EUNInfo[old_addr/bsize].Deleted++;
0926 if (set_bam_entry(part, old_addr, 0))
0927 return -EIO;
0928 }
0929
0930
0931 if (set_bam_entry(part, log_addr, virt_addr))
0932 return -EIO;
0933 part->VirtualBlockMap[sector+i] = log_addr;
0934 part->EUNInfo[part->bam_index].Deleted--;
0935
0936 buffer += SECTOR_SIZE;
0937 virt_addr += SECTOR_SIZE;
0938 }
0939 return 0;
0940 }
0941
0942 static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
0943 {
0944 partition_t *part = (void *)dev;
0945 u_long sect;
0946
0947
0948 sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
0949
0950 geo->heads = 1;
0951 geo->sectors = 8;
0952 geo->cylinders = sect >> 3;
0953
0954 return 0;
0955 }
0956
0957 static int ftl_readsect(struct mtd_blktrans_dev *dev,
0958 unsigned long block, char *buf)
0959 {
0960 return ftl_read((void *)dev, buf, block, 1);
0961 }
0962
0963 static int ftl_writesect(struct mtd_blktrans_dev *dev,
0964 unsigned long block, char *buf)
0965 {
0966 return ftl_write((void *)dev, buf, block, 1);
0967 }
0968
0969 static int ftl_discardsect(struct mtd_blktrans_dev *dev,
0970 unsigned long sector, unsigned nr_sects)
0971 {
0972 partition_t *part = (void *)dev;
0973 uint32_t bsize = 1 << part->header.EraseUnitSize;
0974
0975 pr_debug("FTL erase sector %ld for %d sectors\n",
0976 sector, nr_sects);
0977
0978 while (nr_sects) {
0979 uint32_t old_addr = part->VirtualBlockMap[sector];
0980 if (old_addr != 0xffffffff) {
0981 part->VirtualBlockMap[sector] = 0xffffffff;
0982 part->EUNInfo[old_addr/bsize].Deleted++;
0983 if (set_bam_entry(part, old_addr, 0))
0984 return -EIO;
0985 }
0986 nr_sects--;
0987 sector++;
0988 }
0989
0990 return 0;
0991 }
0992
0993
0994 static void ftl_freepart(partition_t *part)
0995 {
0996 vfree(part->VirtualBlockMap);
0997 part->VirtualBlockMap = NULL;
0998 kfree(part->EUNInfo);
0999 part->EUNInfo = NULL;
1000 kfree(part->XferInfo);
1001 part->XferInfo = NULL;
1002 kfree(part->bam_cache);
1003 part->bam_cache = NULL;
1004 }
1005
1006 static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
1007 {
1008 partition_t *partition;
1009
1010 partition = kzalloc(sizeof(partition_t), GFP_KERNEL);
1011
1012 if (!partition) {
1013 printk(KERN_WARNING "No memory to scan for FTL on %s\n",
1014 mtd->name);
1015 return;
1016 }
1017
1018 partition->mbd.mtd = mtd;
1019
1020 if ((scan_header(partition) == 0) &&
1021 (build_maps(partition) == 0)) {
1022
1023 partition->state = FTL_FORMATTED;
1024 #ifdef PCMCIA_DEBUG
1025 printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n",
1026 le32_to_cpu(partition->header.FormattedSize) >> 10);
1027 #endif
1028 partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9;
1029
1030 partition->mbd.tr = tr;
1031 partition->mbd.devnum = -1;
1032 if (!add_mtd_blktrans_dev(&partition->mbd))
1033 return;
1034 }
1035
1036 kfree(partition);
1037 }
1038
1039 static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
1040 {
1041 del_mtd_blktrans_dev(dev);
1042 ftl_freepart((partition_t *)dev);
1043 }
1044
1045 static struct mtd_blktrans_ops ftl_tr = {
1046 .name = "ftl",
1047 .major = FTL_MAJOR,
1048 .part_bits = PART_BITS,
1049 .blksize = SECTOR_SIZE,
1050 .readsect = ftl_readsect,
1051 .writesect = ftl_writesect,
1052 .discard = ftl_discardsect,
1053 .getgeo = ftl_getgeo,
1054 .add_mtd = ftl_add_mtd,
1055 .remove_dev = ftl_remove_dev,
1056 .owner = THIS_MODULE,
1057 };
1058
1059 module_mtd_blktrans(ftl_tr);
1060
1061 MODULE_LICENSE("Dual MPL/GPL");
1062 MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1063 MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");