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