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 i = xfer->EraseCount;
575 xfer->EraseCount = eun->EraseCount;
576 eun->EraseCount = i;
577 i = xfer->Offset;
578 xfer->Offset = eun->Offset;
579 eun->Offset = i;
580 part->FreeTotal -= eun->Free;
581 part->FreeTotal += free;
582 eun->Free = free;
583 eun->Deleted = 0;
584
585
586 part->bam_index = srcunit;
587
588 return 0;
589}
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607static int reclaim_block(partition_t *part)
608{
609 uint16_t i, eun, xfer;
610 uint32_t best;
611 int queued, ret;
612
613 pr_debug("ftl_cs: reclaiming space...\n");
614 pr_debug("NumTransferUnits == %x\n", part->header.NumTransferUnits);
615
616 best = 0xffffffff; xfer = 0xffff;
617 do {
618 queued = 0;
619 for (i = 0; i < part->header.NumTransferUnits; i++) {
620 int n=0;
621 if (part->XferInfo[i].state == XFER_UNKNOWN) {
622 pr_debug("XferInfo[%d].state == XFER_UNKNOWN\n",i);
623 n=1;
624 erase_xfer(part, i);
625 }
626 if (part->XferInfo[i].state == XFER_ERASING) {
627 pr_debug("XferInfo[%d].state == XFER_ERASING\n",i);
628 n=1;
629 queued = 1;
630 }
631 else if (part->XferInfo[i].state == XFER_ERASED) {
632 pr_debug("XferInfo[%d].state == XFER_ERASED\n",i);
633 n=1;
634 prepare_xfer(part, i);
635 }
636 if (part->XferInfo[i].state == XFER_PREPARED) {
637 pr_debug("XferInfo[%d].state == XFER_PREPARED\n",i);
638 n=1;
639 if (part->XferInfo[i].EraseCount <= best) {
640 best = part->XferInfo[i].EraseCount;
641 xfer = i;
642 }
643 }
644 if (!n)
645 pr_debug("XferInfo[%d].state == %x\n",i, part->XferInfo[i].state);
646
647 }
648 if (xfer == 0xffff) {
649 if (queued) {
650 pr_debug("ftl_cs: waiting for transfer "
651 "unit to be prepared...\n");
652 mtd_sync(part->mbd.mtd);
653 } else {
654 static int ne = 0;
655 if (++ne < 5)
656 printk(KERN_NOTICE "ftl_cs: reclaim failed: no "
657 "suitable transfer units!\n");
658 else
659 pr_debug("ftl_cs: reclaim failed: no "
660 "suitable transfer units!\n");
661
662 return -EIO;
663 }
664 }
665 } while (xfer == 0xffff);
666
667 eun = 0;
668 if ((jiffies % shuffle_freq) == 0) {
669 pr_debug("ftl_cs: recycling freshest block...\n");
670 best = 0xffffffff;
671 for (i = 0; i < part->DataUnits; i++)
672 if (part->EUNInfo[i].EraseCount <= best) {
673 best = part->EUNInfo[i].EraseCount;
674 eun = i;
675 }
676 } else {
677 best = 0;
678 for (i = 0; i < part->DataUnits; i++)
679 if (part->EUNInfo[i].Deleted >= best) {
680 best = part->EUNInfo[i].Deleted;
681 eun = i;
682 }
683 if (best == 0) {
684 static int ne = 0;
685 if (++ne < 5)
686 printk(KERN_NOTICE "ftl_cs: reclaim failed: "
687 "no free blocks!\n");
688 else
689 pr_debug("ftl_cs: reclaim failed: "
690 "no free blocks!\n");
691
692 return -EIO;
693 }
694 }
695 ret = copy_erase_unit(part, eun, xfer);
696 if (!ret)
697 erase_xfer(part, xfer);
698 else
699 printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed!\n");
700 return ret;
701}
702
703
704
705
706
707
708
709
710
711
712
713#ifdef PSYCHO_DEBUG
714static void dump_lists(partition_t *part)
715{
716 int i;
717 printk(KERN_DEBUG "ftl_cs: Free total = %d\n", part->FreeTotal);
718 for (i = 0; i < part->DataUnits; i++)
719 printk(KERN_DEBUG "ftl_cs: unit %d: %d phys, %d free, "
720 "%d deleted\n", i,
721 part->EUNInfo[i].Offset >> part->header.EraseUnitSize,
722 part->EUNInfo[i].Free, part->EUNInfo[i].Deleted);
723}
724#endif
725
726static uint32_t find_free(partition_t *part)
727{
728 uint16_t stop, eun;
729 uint32_t blk;
730 size_t retlen;
731 int ret;
732
733
734 stop = (part->bam_index == 0xffff) ? 0 : part->bam_index;
735 eun = stop;
736 do {
737 if (part->EUNInfo[eun].Free != 0) break;
738
739 if (++eun == part->DataUnits) eun = 0;
740 } while (eun != stop);
741
742 if (part->EUNInfo[eun].Free == 0)
743 return 0;
744
745
746 if (eun != part->bam_index) {
747
748 part->bam_index = 0xffff;
749
750 ret = mtd_read(part->mbd.mtd,
751 part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
752 part->BlocksPerUnit * sizeof(uint32_t),
753 &retlen,
754 (u_char *)(part->bam_cache));
755
756 if (ret) {
757 printk(KERN_WARNING"ftl: Error reading BAM in find_free\n");
758 return 0;
759 }
760 part->bam_index = eun;
761 }
762
763
764 for (blk = 0; blk < part->BlocksPerUnit; blk++)
765 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break;
766 if (blk == part->BlocksPerUnit) {
767#ifdef PSYCHO_DEBUG
768 static int ne = 0;
769 if (++ne == 1)
770 dump_lists(part);
771#endif
772 printk(KERN_NOTICE "ftl_cs: bad free list!\n");
773 return 0;
774 }
775 pr_debug("ftl_cs: found free block at %d in %d\n", blk, eun);
776 return blk;
777
778}
779
780
781
782
783
784
785
786
787static int ftl_read(partition_t *part, caddr_t buffer,
788 u_long sector, u_long nblocks)
789{
790 uint32_t log_addr, bsize;
791 u_long i;
792 int ret;
793 size_t offset, retlen;
794
795 pr_debug("ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
796 part, sector, nblocks);
797 if (!(part->state & FTL_FORMATTED)) {
798 printk(KERN_NOTICE "ftl_cs: bad partition\n");
799 return -EIO;
800 }
801 bsize = 1 << part->header.EraseUnitSize;
802
803 for (i = 0; i < nblocks; i++) {
804 if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) {
805 printk(KERN_NOTICE "ftl_cs: bad read offset\n");
806 return -EIO;
807 }
808 log_addr = part->VirtualBlockMap[sector+i];
809 if (log_addr == 0xffffffff)
810 memset(buffer, 0, SECTOR_SIZE);
811 else {
812 offset = (part->EUNInfo[log_addr / bsize].Offset
813 + (log_addr % bsize));
814 ret = mtd_read(part->mbd.mtd, offset, SECTOR_SIZE, &retlen,
815 (u_char *)buffer);
816
817 if (ret) {
818 printk(KERN_WARNING "Error reading MTD device in ftl_read()\n");
819 return ret;
820 }
821 }
822 buffer += SECTOR_SIZE;
823 }
824 return 0;
825}
826
827
828
829
830
831
832
833static int set_bam_entry(partition_t *part, uint32_t log_addr,
834 uint32_t virt_addr)
835{
836 uint32_t bsize, blk, le_virt_addr;
837#ifdef PSYCHO_DEBUG
838 uint32_t old_addr;
839#endif
840 uint16_t eun;
841 int ret;
842 size_t retlen, offset;
843
844 pr_debug("ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
845 part, log_addr, virt_addr);
846 bsize = 1 << part->header.EraseUnitSize;
847 eun = log_addr / bsize;
848 blk = (log_addr % bsize) / SECTOR_SIZE;
849 offset = (part->EUNInfo[eun].Offset + blk * sizeof(uint32_t) +
850 le32_to_cpu(part->header.BAMOffset));
851
852#ifdef PSYCHO_DEBUG
853 ret = mtd_read(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
854 (u_char *)&old_addr);
855 if (ret) {
856 printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
857 return ret;
858 }
859 old_addr = le32_to_cpu(old_addr);
860
861 if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) ||
862 ((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) ||
863 (!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) {
864 static int ne = 0;
865 if (++ne < 5) {
866 printk(KERN_NOTICE "ftl_cs: set_bam_entry() inconsistency!\n");
867 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, old = 0x%x"
868 ", new = 0x%x\n", log_addr, old_addr, virt_addr);
869 }
870 return -EIO;
871 }
872#endif
873 le_virt_addr = cpu_to_le32(virt_addr);
874 if (part->bam_index == eun) {
875#ifdef PSYCHO_DEBUG
876 if (le32_to_cpu(part->bam_cache[blk]) != old_addr) {
877 static int ne = 0;
878 if (++ne < 5) {
879 printk(KERN_NOTICE "ftl_cs: set_bam_entry() "
880 "inconsistency!\n");
881 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, cache"
882 " = 0x%x\n",
883 le32_to_cpu(part->bam_cache[blk]), old_addr);
884 }
885 return -EIO;
886 }
887#endif
888 part->bam_cache[blk] = le_virt_addr;
889 }
890 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
891 (u_char *)&le_virt_addr);
892
893 if (ret) {
894 printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!\n");
895 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, new = 0x%x\n",
896 log_addr, virt_addr);
897 }
898 return ret;
899}
900
901static int ftl_write(partition_t *part, caddr_t buffer,
902 u_long sector, u_long nblocks)
903{
904 uint32_t bsize, log_addr, virt_addr, old_addr, blk;
905 u_long i;
906 int ret;
907 size_t retlen, offset;
908
909 pr_debug("ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
910 part, sector, nblocks);
911 if (!(part->state & FTL_FORMATTED)) {
912 printk(KERN_NOTICE "ftl_cs: bad partition\n");
913 return -EIO;
914 }
915
916 while (part->FreeTotal < nblocks) {
917 ret = reclaim_block(part);
918 if (ret)
919 return ret;
920 }
921
922 bsize = 1 << part->header.EraseUnitSize;
923
924 virt_addr = sector * SECTOR_SIZE | BLOCK_DATA;
925 for (i = 0; i < nblocks; i++) {
926 if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) {
927 printk(KERN_NOTICE "ftl_cs: bad write offset\n");
928 return -EIO;
929 }
930
931
932 blk = find_free(part);
933 if (blk == 0) {
934 static int ne = 0;
935 if (++ne < 5)
936 printk(KERN_NOTICE "ftl_cs: internal error: "
937 "no free blocks!\n");
938 return -ENOSPC;
939 }
940
941
942 log_addr = part->bam_index * bsize + blk * SECTOR_SIZE;
943 part->EUNInfo[part->bam_index].Free--;
944 part->FreeTotal--;
945 if (set_bam_entry(part, log_addr, 0xfffffffe))
946 return -EIO;
947 part->EUNInfo[part->bam_index].Deleted++;
948 offset = (part->EUNInfo[part->bam_index].Offset +
949 blk * SECTOR_SIZE);
950 ret = mtd_write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, buffer);
951
952 if (ret) {
953 printk(KERN_NOTICE "ftl_cs: block write failed!\n");
954 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, virt_addr"
955 " = 0x%x, Offset = 0x%zx\n", log_addr, virt_addr,
956 offset);
957 return -EIO;
958 }
959
960
961 old_addr = part->VirtualBlockMap[sector+i];
962 if (old_addr != 0xffffffff) {
963 part->VirtualBlockMap[sector+i] = 0xffffffff;
964 part->EUNInfo[old_addr/bsize].Deleted++;
965 if (set_bam_entry(part, old_addr, 0))
966 return -EIO;
967 }
968
969
970 if (set_bam_entry(part, log_addr, virt_addr))
971 return -EIO;
972 part->VirtualBlockMap[sector+i] = log_addr;
973 part->EUNInfo[part->bam_index].Deleted--;
974
975 buffer += SECTOR_SIZE;
976 virt_addr += SECTOR_SIZE;
977 }
978 return 0;
979}
980
981static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
982{
983 partition_t *part = (void *)dev;
984 u_long sect;
985
986
987 sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
988
989 geo->heads = 1;
990 geo->sectors = 8;
991 geo->cylinders = sect >> 3;
992
993 return 0;
994}
995
996static int ftl_readsect(struct mtd_blktrans_dev *dev,
997 unsigned long block, char *buf)
998{
999 return ftl_read((void *)dev, buf, block, 1);
1000}
1001
1002static int ftl_writesect(struct mtd_blktrans_dev *dev,
1003 unsigned long block, char *buf)
1004{
1005 return ftl_write((void *)dev, buf, block, 1);
1006}
1007
1008static int ftl_discardsect(struct mtd_blktrans_dev *dev,
1009 unsigned long sector, unsigned nr_sects)
1010{
1011 partition_t *part = (void *)dev;
1012 uint32_t bsize = 1 << part->header.EraseUnitSize;
1013
1014 pr_debug("FTL erase sector %ld for %d sectors\n",
1015 sector, nr_sects);
1016
1017 while (nr_sects) {
1018 uint32_t old_addr = part->VirtualBlockMap[sector];
1019 if (old_addr != 0xffffffff) {
1020 part->VirtualBlockMap[sector] = 0xffffffff;
1021 part->EUNInfo[old_addr/bsize].Deleted++;
1022 if (set_bam_entry(part, old_addr, 0))
1023 return -EIO;
1024 }
1025 nr_sects--;
1026 sector++;
1027 }
1028
1029 return 0;
1030}
1031
1032
1033static void ftl_freepart(partition_t *part)
1034{
1035 vfree(part->VirtualBlockMap);
1036 part->VirtualBlockMap = NULL;
1037 kfree(part->EUNInfo);
1038 part->EUNInfo = NULL;
1039 kfree(part->XferInfo);
1040 part->XferInfo = NULL;
1041 kfree(part->bam_cache);
1042 part->bam_cache = NULL;
1043}
1044
1045static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
1046{
1047 partition_t *partition;
1048
1049 partition = kzalloc(sizeof(partition_t), GFP_KERNEL);
1050
1051 if (!partition) {
1052 printk(KERN_WARNING "No memory to scan for FTL on %s\n",
1053 mtd->name);
1054 return;
1055 }
1056
1057 partition->mbd.mtd = mtd;
1058
1059 if ((scan_header(partition) == 0) &&
1060 (build_maps(partition) == 0)) {
1061
1062 partition->state = FTL_FORMATTED;
1063#ifdef PCMCIA_DEBUG
1064 printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n",
1065 le32_to_cpu(partition->header.FormattedSize) >> 10);
1066#endif
1067 partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9;
1068
1069 partition->mbd.tr = tr;
1070 partition->mbd.devnum = -1;
1071 if (!add_mtd_blktrans_dev((void *)partition))
1072 return;
1073 }
1074
1075 kfree(partition);
1076}
1077
1078static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
1079{
1080 del_mtd_blktrans_dev(dev);
1081 ftl_freepart((partition_t *)dev);
1082}
1083
1084static struct mtd_blktrans_ops ftl_tr = {
1085 .name = "ftl",
1086 .major = FTL_MAJOR,
1087 .part_bits = PART_BITS,
1088 .blksize = SECTOR_SIZE,
1089 .readsect = ftl_readsect,
1090 .writesect = ftl_writesect,
1091 .discard = ftl_discardsect,
1092 .getgeo = ftl_getgeo,
1093 .add_mtd = ftl_add_mtd,
1094 .remove_dev = ftl_remove_dev,
1095 .owner = THIS_MODULE,
1096};
1097
1098static int __init init_ftl(void)
1099{
1100 return register_mtd_blktrans(&ftl_tr);
1101}
1102
1103static void __exit cleanup_ftl(void)
1104{
1105 deregister_mtd_blktrans(&ftl_tr);
1106}
1107
1108module_init(init_ftl);
1109module_exit(cleanup_ftl);
1110
1111
1112MODULE_LICENSE("Dual MPL/GPL");
1113MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1114MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");
1115