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