1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57#include <linux/mtd/blktrans.h>
58#include <linux/module.h>
59#include <linux/mtd/mtd.h>
60
61
62#include <linux/kernel.h>
63#include <linux/ptrace.h>
64#include <linux/slab.h>
65#include <linux/string.h>
66#include <linux/timer.h>
67#include <linux/major.h>
68#include <linux/fs.h>
69#include <linux/init.h>
70#include <linux/hdreg.h>
71#include <linux/vmalloc.h>
72#include <linux/blkpg.h>
73#include <asm/uaccess.h>
74
75#include <linux/mtd/ftl.h>
76
77
78
79
80static int shuffle_freq = 50;
81module_param(shuffle_freq, int, 0);
82
83
84
85
86#ifndef FTL_MAJOR
87#define FTL_MAJOR 44
88#endif
89
90
91
92
93
94#define MAX_DEV 4
95
96
97#define MAX_REGION 4
98
99
100#define PART_BITS 4
101
102
103#define MAX_ERASE 8
104
105
106#define SECTOR_SIZE 512
107
108
109
110typedef struct partition_t {
111 struct mtd_blktrans_dev mbd;
112 uint32_t state;
113 uint32_t *VirtualBlockMap;
114 uint32_t *VirtualPageMap;
115 uint32_t FreeTotal;
116 struct eun_info_t {
117 uint32_t Offset;
118 uint32_t EraseCount;
119 uint32_t Free;
120 uint32_t Deleted;
121 } *EUNInfo;
122 struct xfer_info_t {
123 uint32_t Offset;
124 uint32_t EraseCount;
125 uint16_t state;
126 } *XferInfo;
127 uint16_t bam_index;
128 uint32_t *bam_cache;
129 uint16_t DataUnits;
130 uint32_t BlocksPerUnit;
131 erase_unit_header_t header;
132} partition_t;
133
134
135#define FTL_FORMATTED 0x01
136
137
138#define XFER_UNKNOWN 0x00
139#define XFER_ERASING 0x01
140#define XFER_ERASED 0x02
141#define XFER_PREPARED 0x03
142#define XFER_FAILED 0x04
143
144
145
146
147static void ftl_erase_callback(struct erase_info *done);
148
149
150
151
152
153
154
155
156
157
158static int scan_header(partition_t *part)
159{
160 erase_unit_header_t header;
161 loff_t offset, max_offset;
162 size_t ret;
163 int err;
164 part->header.FormattedSize = 0;
165 max_offset = (0x100000<part->mbd.mtd->size)?0x100000:part->mbd.mtd->size;
166
167 for (offset = 0;
168 (offset + sizeof(header)) < max_offset;
169 offset += part->mbd.mtd->erasesize ? : 0x2000) {
170
171 err = mtd_read(part->mbd.mtd, offset, sizeof(header), &ret,
172 (unsigned char *)&header);
173
174 if (err)
175 return err;
176
177 if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break;
178 }
179
180 if (offset == max_offset) {
181 printk(KERN_NOTICE "ftl_cs: FTL header not found.\n");
182 return -ENOENT;
183 }
184 if (header.BlockSize != 9 ||
185 (header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) ||
186 (header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) {
187 printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n");
188 return -1;
189 }
190 if ((1 << header.EraseUnitSize) != part->mbd.mtd->erasesize) {
191 printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x\n",
192 1 << header.EraseUnitSize,part->mbd.mtd->erasesize);
193 return -1;
194 }
195 part->header = header;
196 return 0;
197}
198
199static int build_maps(partition_t *part)
200{
201 erase_unit_header_t header;
202 uint16_t xvalid, xtrans, i;
203 unsigned blocks, j;
204 int hdr_ok, ret = -1;
205 ssize_t retval;
206 loff_t offset;
207
208
209 part->DataUnits = le16_to_cpu(part->header.NumEraseUnits) -
210 part->header.NumTransferUnits;
211 part->EUNInfo = kmalloc(part->DataUnits * sizeof(struct eun_info_t),
212 GFP_KERNEL);
213 if (!part->EUNInfo)
214 goto out;
215 for (i = 0; i < part->DataUnits; i++)
216 part->EUNInfo[i].Offset = 0xffffffff;
217 part->XferInfo =
218 kmalloc(part->header.NumTransferUnits * sizeof(struct xfer_info_t),
219 GFP_KERNEL);
220 if (!part->XferInfo)
221 goto out_EUNInfo;
222
223 xvalid = xtrans = 0;
224 for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) {
225 offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN))
226 << part->header.EraseUnitSize);
227 ret = mtd_read(part->mbd.mtd, offset, sizeof(header), &retval,
228 (unsigned char *)&header);
229
230 if (ret)
231 goto out_XferInfo;
232
233 ret = -1;
234
235 hdr_ok = (strcmp(header.DataOrgTuple+3, "FTL100") == 0);
236 if (hdr_ok && (le16_to_cpu(header.LogicalEUN) < part->DataUnits) &&
237 (part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset == 0xffffffff)) {
238 part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset = offset;
239 part->EUNInfo[le16_to_cpu(header.LogicalEUN)].EraseCount =
240 le32_to_cpu(header.EraseCount);
241 xvalid++;
242 } else {
243 if (xtrans == part->header.NumTransferUnits) {
244 printk(KERN_NOTICE "ftl_cs: format error: too many "
245 "transfer units!\n");
246 goto out_XferInfo;
247 }
248 if (hdr_ok && (le16_to_cpu(header.LogicalEUN) == 0xffff)) {
249 part->XferInfo[xtrans].state = XFER_PREPARED;
250 part->XferInfo[xtrans].EraseCount = le32_to_cpu(header.EraseCount);
251 } else {
252 part->XferInfo[xtrans].state = XFER_UNKNOWN;
253
254 part->XferInfo[xtrans].EraseCount =
255 le32_to_cpu(part->header.EraseCount);
256 }
257 part->XferInfo[xtrans].Offset = offset;
258 xtrans++;
259 }
260 }
261
262 header = part->header;
263 if ((xtrans != header.NumTransferUnits) ||
264 (xvalid+xtrans != le16_to_cpu(header.NumEraseUnits))) {
265 printk(KERN_NOTICE "ftl_cs: format error: erase units "
266 "don't add up!\n");
267 goto out_XferInfo;
268 }
269
270
271 blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize;
272 part->VirtualBlockMap = vmalloc(blocks * sizeof(uint32_t));
273 if (!part->VirtualBlockMap)
274 goto out_XferInfo;
275
276 memset(part->VirtualBlockMap, 0xff, blocks * sizeof(uint32_t));
277 part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize;
278
279 part->bam_cache = kmalloc(part->BlocksPerUnit * sizeof(uint32_t),
280 GFP_KERNEL);
281 if (!part->bam_cache)
282 goto out_VirtualBlockMap;
283
284 part->bam_index = 0xffff;
285 part->FreeTotal = 0;
286
287 for (i = 0; i < part->DataUnits; i++) {
288 part->EUNInfo[i].Free = 0;
289 part->EUNInfo[i].Deleted = 0;
290 offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset);
291
292 ret = mtd_read(part->mbd.mtd, offset,
293 part->BlocksPerUnit * sizeof(uint32_t), &retval,
294 (unsigned char *)part->bam_cache);
295
296 if (ret)
297 goto out_bam_cache;
298
299 for (j = 0; j < part->BlocksPerUnit; j++) {
300 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) {
301 part->EUNInfo[i].Free++;
302 part->FreeTotal++;
303 } else if ((BLOCK_TYPE(le32_to_cpu(part->bam_cache[j])) == BLOCK_DATA) &&
304 (BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j])) < blocks))
305 part->VirtualBlockMap[BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j]))] =
306 (i << header.EraseUnitSize) + (j << header.BlockSize);
307 else if (BLOCK_DELETED(le32_to_cpu(part->bam_cache[j])))
308 part->EUNInfo[i].Deleted++;
309 }
310 }
311
312 ret = 0;
313 goto out;
314
315out_bam_cache:
316 kfree(part->bam_cache);
317out_VirtualBlockMap:
318 vfree(part->VirtualBlockMap);
319out_XferInfo:
320 kfree(part->XferInfo);
321out_EUNInfo:
322 kfree(part->EUNInfo);
323out:
324 return ret;
325}
326
327
328
329
330
331
332
333
334static int erase_xfer(partition_t *part,
335 uint16_t xfernum)
336{
337 int ret;
338 struct xfer_info_t *xfer;
339 struct erase_info *erase;
340
341 xfer = &part->XferInfo[xfernum];
342 pr_debug("ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset);
343 xfer->state = XFER_ERASING;
344
345
346
347
348 erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL);
349 if (!erase)
350 return -ENOMEM;
351
352 erase->mtd = part->mbd.mtd;
353 erase->callback = ftl_erase_callback;
354 erase->addr = xfer->Offset;
355 erase->len = 1 << part->header.EraseUnitSize;
356 erase->priv = (u_long)part;
357
358 ret = mtd_erase(part->mbd.mtd, erase);
359
360 if (!ret)
361 xfer->EraseCount++;
362 else
363 kfree(erase);
364
365 return ret;
366}
367
368
369
370
371
372
373
374
375static void ftl_erase_callback(struct erase_info *erase)
376{
377 partition_t *part;
378 struct xfer_info_t *xfer;
379 int i;
380
381
382 part = (partition_t *)(erase->priv);
383
384 for (i = 0; i < part->header.NumTransferUnits; i++)
385 if (part->XferInfo[i].Offset == erase->addr) break;
386
387 if (i == part->header.NumTransferUnits) {
388 printk(KERN_NOTICE "ftl_cs: internal error: "
389 "erase lookup failed!\n");
390 return;
391 }
392
393 xfer = &part->XferInfo[i];
394 if (erase->state == MTD_ERASE_DONE)
395 xfer->state = XFER_ERASED;
396 else {
397 xfer->state = XFER_FAILED;
398 printk(KERN_NOTICE "ftl_cs: erase failed: state = %d\n",
399 erase->state);
400 }
401
402 kfree(erase);
403
404}
405
406static int prepare_xfer(partition_t *part, int i)
407{
408 erase_unit_header_t header;
409 struct xfer_info_t *xfer;
410 int nbam, ret;
411 uint32_t ctl;
412 ssize_t retlen;
413 loff_t offset;
414
415 xfer = &part->XferInfo[i];
416 xfer->state = XFER_FAILED;
417
418 pr_debug("ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset);
419
420
421 header = part->header;
422 header.LogicalEUN = cpu_to_le16(0xffff);
423 header.EraseCount = cpu_to_le32(xfer->EraseCount);
424
425 ret = mtd_write(part->mbd.mtd, xfer->Offset, sizeof(header), &retlen,
426 (u_char *)&header);
427
428 if (ret) {
429 return ret;
430 }
431
432
433 nbam = (part->BlocksPerUnit * sizeof(uint32_t) +
434 le32_to_cpu(part->header.BAMOffset) + SECTOR_SIZE - 1) / SECTOR_SIZE;
435
436 offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
437 ctl = cpu_to_le32(BLOCK_CONTROL);
438
439 for (i = 0; i < nbam; i++, offset += sizeof(uint32_t)) {
440
441 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
442 (u_char *)&ctl);
443
444 if (ret)
445 return ret;
446 }
447 xfer->state = XFER_PREPARED;
448 return 0;
449
450}
451
452
453
454
455
456
457
458
459
460
461
462
463
464static int copy_erase_unit(partition_t *part, uint16_t srcunit,
465 uint16_t xferunit)
466{
467 u_char buf[SECTOR_SIZE];
468 struct eun_info_t *eun;
469 struct xfer_info_t *xfer;
470 uint32_t src, dest, free, i;
471 uint16_t unit;
472 int ret;
473 ssize_t retlen;
474 loff_t offset;
475 uint16_t srcunitswap = cpu_to_le16(srcunit);
476
477 eun = &part->EUNInfo[srcunit];
478 xfer = &part->XferInfo[xferunit];
479 pr_debug("ftl_cs: copying block 0x%x to 0x%x\n",
480 eun->Offset, xfer->Offset);
481
482
483
484 if (part->bam_index != srcunit) {
485
486 offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
487
488 ret = mtd_read(part->mbd.mtd, offset,
489 part->BlocksPerUnit * sizeof(uint32_t), &retlen,
490 (u_char *)(part->bam_cache));
491
492
493 part->bam_index = 0xffff;
494
495 if (ret) {
496 printk( KERN_WARNING "ftl: Failed to read BAM cache in copy_erase_unit()!\n");
497 return ret;
498 }
499 }
500
501
502 xfer->state = XFER_UNKNOWN;
503 offset = xfer->Offset + 20;
504 unit = cpu_to_le16(0x7fff);
505
506 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint16_t), &retlen,
507 (u_char *)&unit);
508
509 if (ret) {
510 printk( KERN_WARNING "ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
511 return ret;
512 }
513
514
515 src = eun->Offset; dest = xfer->Offset;
516
517 free = 0;
518 ret = 0;
519 for (i = 0; i < part->BlocksPerUnit; i++) {
520 switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) {
521 case BLOCK_CONTROL:
522
523 break;
524 case BLOCK_DATA:
525 case BLOCK_REPLACEMENT:
526 ret = mtd_read(part->mbd.mtd, src, SECTOR_SIZE, &retlen,
527 (u_char *)buf);
528 if (ret) {
529 printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n");
530 return ret;
531 }
532
533
534 ret = mtd_write(part->mbd.mtd, dest, SECTOR_SIZE, &retlen,
535 (u_char *)buf);
536 if (ret) {
537 printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n");
538 return ret;
539 }
540
541 break;
542 default:
543
544 part->bam_cache[i] = cpu_to_le32(0xffffffff);
545 free++;
546 break;
547 }
548 src += SECTOR_SIZE;
549 dest += SECTOR_SIZE;
550 }
551
552
553 ret = mtd_write(part->mbd.mtd,
554 xfer->Offset + le32_to_cpu(part->header.BAMOffset),
555 part->BlocksPerUnit * sizeof(int32_t),
556 &retlen,
557 (u_char *)part->bam_cache);
558 if (ret) {
559 printk( KERN_WARNING "ftl: Error writing BAM in copy_erase_unit\n");
560 return ret;
561 }
562
563
564
565 ret = mtd_write(part->mbd.mtd, xfer->Offset + 20, sizeof(uint16_t),
566 &retlen, (u_char *)&srcunitswap);
567
568 if (ret) {
569 printk(KERN_WARNING "ftl: Error writing new LogicalEUN in copy_erase_unit\n");
570 return ret;
571 }
572
573
574
575 i = xfer->EraseCount;
576 xfer->EraseCount = eun->EraseCount;
577 eun->EraseCount = i;
578 i = xfer->Offset;
579 xfer->Offset = eun->Offset;
580 eun->Offset = i;
581 part->FreeTotal -= eun->Free;
582 part->FreeTotal += free;
583 eun->Free = free;
584 eun->Deleted = 0;
585
586
587 part->bam_index = srcunit;
588
589 return 0;
590}
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608static int reclaim_block(partition_t *part)
609{
610 uint16_t i, eun, xfer;
611 uint32_t best;
612 int queued, ret;
613
614 pr_debug("ftl_cs: reclaiming space...\n");
615 pr_debug("NumTransferUnits == %x\n", part->header.NumTransferUnits);
616
617 best = 0xffffffff; xfer = 0xffff;
618 do {
619 queued = 0;
620 for (i = 0; i < part->header.NumTransferUnits; i++) {
621 int n=0;
622 if (part->XferInfo[i].state == XFER_UNKNOWN) {
623 pr_debug("XferInfo[%d].state == XFER_UNKNOWN\n",i);
624 n=1;
625 erase_xfer(part, i);
626 }
627 if (part->XferInfo[i].state == XFER_ERASING) {
628 pr_debug("XferInfo[%d].state == XFER_ERASING\n",i);
629 n=1;
630 queued = 1;
631 }
632 else if (part->XferInfo[i].state == XFER_ERASED) {
633 pr_debug("XferInfo[%d].state == XFER_ERASED\n",i);
634 n=1;
635 prepare_xfer(part, i);
636 }
637 if (part->XferInfo[i].state == XFER_PREPARED) {
638 pr_debug("XferInfo[%d].state == XFER_PREPARED\n",i);
639 n=1;
640 if (part->XferInfo[i].EraseCount <= best) {
641 best = part->XferInfo[i].EraseCount;
642 xfer = i;
643 }
644 }
645 if (!n)
646 pr_debug("XferInfo[%d].state == %x\n",i, part->XferInfo[i].state);
647
648 }
649 if (xfer == 0xffff) {
650 if (queued) {
651 pr_debug("ftl_cs: waiting for transfer "
652 "unit to be prepared...\n");
653 mtd_sync(part->mbd.mtd);
654 } else {
655 static int ne = 0;
656 if (++ne < 5)
657 printk(KERN_NOTICE "ftl_cs: reclaim failed: no "
658 "suitable transfer units!\n");
659 else
660 pr_debug("ftl_cs: reclaim failed: no "
661 "suitable transfer units!\n");
662
663 return -EIO;
664 }
665 }
666 } while (xfer == 0xffff);
667
668 eun = 0;
669 if ((jiffies % shuffle_freq) == 0) {
670 pr_debug("ftl_cs: recycling freshest block...\n");
671 best = 0xffffffff;
672 for (i = 0; i < part->DataUnits; i++)
673 if (part->EUNInfo[i].EraseCount <= best) {
674 best = part->EUNInfo[i].EraseCount;
675 eun = i;
676 }
677 } else {
678 best = 0;
679 for (i = 0; i < part->DataUnits; i++)
680 if (part->EUNInfo[i].Deleted >= best) {
681 best = part->EUNInfo[i].Deleted;
682 eun = i;
683 }
684 if (best == 0) {
685 static int ne = 0;
686 if (++ne < 5)
687 printk(KERN_NOTICE "ftl_cs: reclaim failed: "
688 "no free blocks!\n");
689 else
690 pr_debug("ftl_cs: reclaim failed: "
691 "no free blocks!\n");
692
693 return -EIO;
694 }
695 }
696 ret = copy_erase_unit(part, eun, xfer);
697 if (!ret)
698 erase_xfer(part, xfer);
699 else
700 printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed!\n");
701 return ret;
702}
703
704
705
706
707
708
709
710
711
712
713
714#ifdef PSYCHO_DEBUG
715static void dump_lists(partition_t *part)
716{
717 int i;
718 printk(KERN_DEBUG "ftl_cs: Free total = %d\n", part->FreeTotal);
719 for (i = 0; i < part->DataUnits; i++)
720 printk(KERN_DEBUG "ftl_cs: unit %d: %d phys, %d free, "
721 "%d deleted\n", i,
722 part->EUNInfo[i].Offset >> part->header.EraseUnitSize,
723 part->EUNInfo[i].Free, part->EUNInfo[i].Deleted);
724}
725#endif
726
727static uint32_t find_free(partition_t *part)
728{
729 uint16_t stop, eun;
730 uint32_t blk;
731 size_t retlen;
732 int ret;
733
734
735 stop = (part->bam_index == 0xffff) ? 0 : part->bam_index;
736 eun = stop;
737 do {
738 if (part->EUNInfo[eun].Free != 0) break;
739
740 if (++eun == part->DataUnits) eun = 0;
741 } while (eun != stop);
742
743 if (part->EUNInfo[eun].Free == 0)
744 return 0;
745
746
747 if (eun != part->bam_index) {
748
749 part->bam_index = 0xffff;
750
751 ret = mtd_read(part->mbd.mtd,
752 part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
753 part->BlocksPerUnit * sizeof(uint32_t),
754 &retlen,
755 (u_char *)(part->bam_cache));
756
757 if (ret) {
758 printk(KERN_WARNING"ftl: Error reading BAM in find_free\n");
759 return 0;
760 }
761 part->bam_index = eun;
762 }
763
764
765 for (blk = 0; blk < part->BlocksPerUnit; blk++)
766 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break;
767 if (blk == part->BlocksPerUnit) {
768#ifdef PSYCHO_DEBUG
769 static int ne = 0;
770 if (++ne == 1)
771 dump_lists(part);
772#endif
773 printk(KERN_NOTICE "ftl_cs: bad free list!\n");
774 return 0;
775 }
776 pr_debug("ftl_cs: found free block at %d in %d\n", blk, eun);
777 return blk;
778
779}
780
781
782
783
784
785
786
787
788static int ftl_read(partition_t *part, caddr_t buffer,
789 u_long sector, u_long nblocks)
790{
791 uint32_t log_addr, bsize;
792 u_long i;
793 int ret;
794 size_t offset, retlen;
795
796 pr_debug("ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
797 part, sector, nblocks);
798 if (!(part->state & FTL_FORMATTED)) {
799 printk(KERN_NOTICE "ftl_cs: bad partition\n");
800 return -EIO;
801 }
802 bsize = 1 << part->header.EraseUnitSize;
803
804 for (i = 0; i < nblocks; i++) {
805 if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) {
806 printk(KERN_NOTICE "ftl_cs: bad read offset\n");
807 return -EIO;
808 }
809 log_addr = part->VirtualBlockMap[sector+i];
810 if (log_addr == 0xffffffff)
811 memset(buffer, 0, SECTOR_SIZE);
812 else {
813 offset = (part->EUNInfo[log_addr / bsize].Offset
814 + (log_addr % bsize));
815 ret = mtd_read(part->mbd.mtd, offset, SECTOR_SIZE, &retlen,
816 (u_char *)buffer);
817
818 if (ret) {
819 printk(KERN_WARNING "Error reading MTD device in ftl_read()\n");
820 return ret;
821 }
822 }
823 buffer += SECTOR_SIZE;
824 }
825 return 0;
826}
827
828
829
830
831
832
833
834static int set_bam_entry(partition_t *part, uint32_t log_addr,
835 uint32_t virt_addr)
836{
837 uint32_t bsize, blk, le_virt_addr;
838#ifdef PSYCHO_DEBUG
839 uint32_t old_addr;
840#endif
841 uint16_t eun;
842 int ret;
843 size_t retlen, offset;
844
845 pr_debug("ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
846 part, log_addr, virt_addr);
847 bsize = 1 << part->header.EraseUnitSize;
848 eun = log_addr / bsize;
849 blk = (log_addr % bsize) / SECTOR_SIZE;
850 offset = (part->EUNInfo[eun].Offset + blk * sizeof(uint32_t) +
851 le32_to_cpu(part->header.BAMOffset));
852
853#ifdef PSYCHO_DEBUG
854 ret = mtd_read(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
855 (u_char *)&old_addr);
856 if (ret) {
857 printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
858 return ret;
859 }
860 old_addr = le32_to_cpu(old_addr);
861
862 if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) ||
863 ((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) ||
864 (!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) {
865 static int ne = 0;
866 if (++ne < 5) {
867 printk(KERN_NOTICE "ftl_cs: set_bam_entry() inconsistency!\n");
868 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, old = 0x%x"
869 ", new = 0x%x\n", log_addr, old_addr, virt_addr);
870 }
871 return -EIO;
872 }
873#endif
874 le_virt_addr = cpu_to_le32(virt_addr);
875 if (part->bam_index == eun) {
876#ifdef PSYCHO_DEBUG
877 if (le32_to_cpu(part->bam_cache[blk]) != old_addr) {
878 static int ne = 0;
879 if (++ne < 5) {
880 printk(KERN_NOTICE "ftl_cs: set_bam_entry() "
881 "inconsistency!\n");
882 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, cache"
883 " = 0x%x\n",
884 le32_to_cpu(part->bam_cache[blk]), old_addr);
885 }
886 return -EIO;
887 }
888#endif
889 part->bam_cache[blk] = le_virt_addr;
890 }
891 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
892 (u_char *)&le_virt_addr);
893
894 if (ret) {
895 printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!\n");
896 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, new = 0x%x\n",
897 log_addr, virt_addr);
898 }
899 return ret;
900}
901
902static int ftl_write(partition_t *part, caddr_t buffer,
903 u_long sector, u_long nblocks)
904{
905 uint32_t bsize, log_addr, virt_addr, old_addr, blk;
906 u_long i;
907 int ret;
908 size_t retlen, offset;
909
910 pr_debug("ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
911 part, sector, nblocks);
912 if (!(part->state & FTL_FORMATTED)) {
913 printk(KERN_NOTICE "ftl_cs: bad partition\n");
914 return -EIO;
915 }
916
917 while (part->FreeTotal < nblocks) {
918 ret = reclaim_block(part);
919 if (ret)
920 return ret;
921 }
922
923 bsize = 1 << part->header.EraseUnitSize;
924
925 virt_addr = sector * SECTOR_SIZE | BLOCK_DATA;
926 for (i = 0; i < nblocks; i++) {
927 if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) {
928 printk(KERN_NOTICE "ftl_cs: bad write offset\n");
929 return -EIO;
930 }
931
932
933 blk = find_free(part);
934 if (blk == 0) {
935 static int ne = 0;
936 if (++ne < 5)
937 printk(KERN_NOTICE "ftl_cs: internal error: "
938 "no free blocks!\n");
939 return -ENOSPC;
940 }
941
942
943 log_addr = part->bam_index * bsize + blk * SECTOR_SIZE;
944 part->EUNInfo[part->bam_index].Free--;
945 part->FreeTotal--;
946 if (set_bam_entry(part, log_addr, 0xfffffffe))
947 return -EIO;
948 part->EUNInfo[part->bam_index].Deleted++;
949 offset = (part->EUNInfo[part->bam_index].Offset +
950 blk * SECTOR_SIZE);
951 ret = mtd_write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, buffer);
952
953 if (ret) {
954 printk(KERN_NOTICE "ftl_cs: block write failed!\n");
955 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, virt_addr"
956 " = 0x%x, Offset = 0x%zx\n", log_addr, virt_addr,
957 offset);
958 return -EIO;
959 }
960
961
962 old_addr = part->VirtualBlockMap[sector+i];
963 if (old_addr != 0xffffffff) {
964 part->VirtualBlockMap[sector+i] = 0xffffffff;
965 part->EUNInfo[old_addr/bsize].Deleted++;
966 if (set_bam_entry(part, old_addr, 0))
967 return -EIO;
968 }
969
970
971 if (set_bam_entry(part, log_addr, virt_addr))
972 return -EIO;
973 part->VirtualBlockMap[sector+i] = log_addr;
974 part->EUNInfo[part->bam_index].Deleted--;
975
976 buffer += SECTOR_SIZE;
977 virt_addr += SECTOR_SIZE;
978 }
979 return 0;
980}
981
982static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
983{
984 partition_t *part = (void *)dev;
985 u_long sect;
986
987
988 sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
989
990 geo->heads = 1;
991 geo->sectors = 8;
992 geo->cylinders = sect >> 3;
993
994 return 0;
995}
996
997static int ftl_readsect(struct mtd_blktrans_dev *dev,
998 unsigned long block, char *buf)
999{
1000 return ftl_read((void *)dev, buf, block, 1);
1001}
1002
1003static int ftl_writesect(struct mtd_blktrans_dev *dev,
1004 unsigned long block, char *buf)
1005{
1006 return ftl_write((void *)dev, buf, block, 1);
1007}
1008
1009static int ftl_discardsect(struct mtd_blktrans_dev *dev,
1010 unsigned long sector, unsigned nr_sects)
1011{
1012 partition_t *part = (void *)dev;
1013 uint32_t bsize = 1 << part->header.EraseUnitSize;
1014
1015 pr_debug("FTL erase sector %ld for %d sectors\n",
1016 sector, nr_sects);
1017
1018 while (nr_sects) {
1019 uint32_t old_addr = part->VirtualBlockMap[sector];
1020 if (old_addr != 0xffffffff) {
1021 part->VirtualBlockMap[sector] = 0xffffffff;
1022 part->EUNInfo[old_addr/bsize].Deleted++;
1023 if (set_bam_entry(part, old_addr, 0))
1024 return -EIO;
1025 }
1026 nr_sects--;
1027 sector++;
1028 }
1029
1030 return 0;
1031}
1032
1033
1034static void ftl_freepart(partition_t *part)
1035{
1036 vfree(part->VirtualBlockMap);
1037 part->VirtualBlockMap = NULL;
1038 kfree(part->VirtualPageMap);
1039 part->VirtualPageMap = NULL;
1040 kfree(part->EUNInfo);
1041 part->EUNInfo = NULL;
1042 kfree(part->XferInfo);
1043 part->XferInfo = NULL;
1044 kfree(part->bam_cache);
1045 part->bam_cache = NULL;
1046}
1047
1048static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
1049{
1050 partition_t *partition;
1051
1052 partition = kzalloc(sizeof(partition_t), GFP_KERNEL);
1053
1054 if (!partition) {
1055 printk(KERN_WARNING "No memory to scan for FTL on %s\n",
1056 mtd->name);
1057 return;
1058 }
1059
1060 partition->mbd.mtd = mtd;
1061
1062 if ((scan_header(partition) == 0) &&
1063 (build_maps(partition) == 0)) {
1064
1065 partition->state = FTL_FORMATTED;
1066#ifdef PCMCIA_DEBUG
1067 printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n",
1068 le32_to_cpu(partition->header.FormattedSize) >> 10);
1069#endif
1070 partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9;
1071
1072 partition->mbd.tr = tr;
1073 partition->mbd.devnum = -1;
1074 if (!add_mtd_blktrans_dev((void *)partition))
1075 return;
1076 }
1077
1078 ftl_freepart(partition);
1079 kfree(partition);
1080}
1081
1082static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
1083{
1084 del_mtd_blktrans_dev(dev);
1085 ftl_freepart((partition_t *)dev);
1086}
1087
1088static struct mtd_blktrans_ops ftl_tr = {
1089 .name = "ftl",
1090 .major = FTL_MAJOR,
1091 .part_bits = PART_BITS,
1092 .blksize = SECTOR_SIZE,
1093 .readsect = ftl_readsect,
1094 .writesect = ftl_writesect,
1095 .discard = ftl_discardsect,
1096 .getgeo = ftl_getgeo,
1097 .add_mtd = ftl_add_mtd,
1098 .remove_dev = ftl_remove_dev,
1099 .owner = THIS_MODULE,
1100};
1101
1102static int __init init_ftl(void)
1103{
1104 return register_mtd_blktrans(&ftl_tr);
1105}
1106
1107static void __exit cleanup_ftl(void)
1108{
1109 deregister_mtd_blktrans(&ftl_tr);
1110}
1111
1112module_init(init_ftl);
1113module_exit(cleanup_ftl);
1114
1115
1116MODULE_LICENSE("Dual MPL/GPL");
1117MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1118MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");
1119