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
58#include <linux/module.h>
59#include <linux/mtd/compatmac.h>
60#include <linux/mtd/mtd.h>
61
62
63#include <linux/kernel.h>
64#include <linux/sched.h>
65#include <linux/ptrace.h>
66#include <linux/slab.h>
67#include <linux/string.h>
68#include <linux/timer.h>
69#include <linux/major.h>
70#include <linux/fs.h>
71#include <linux/ioctl.h>
72#include <linux/hdreg.h>
73
74#if (LINUX_VERSION_CODE >= 0x20100)
75#include <linux/vmalloc.h>
76#endif
77#if (LINUX_VERSION_CODE >= 0x20303)
78#include <linux/blkpg.h>
79#endif
80
81#include <linux/mtd/ftl.h>
82
83
84
85#if (LINUX_VERSION_CODE < 0x20328)
86#define register_disk(dev, drive, minors, ops, size) \
87 do { (dev)->part[(drive)*(minors)].nr_sects = size; \
88 if (size == 0) (dev)->part[(drive)*(minors)].start_sect = -1; \
89 resetup_one_dev(dev, drive); } while (0)
90#endif
91
92#if (LINUX_VERSION_CODE < 0x20320)
93#define BLK_DEFAULT_QUEUE(n) blk_dev[n].request_fn
94#define blk_init_queue(q, req) q = (req)
95#define blk_cleanup_queue(q) q = NULL
96#define request_arg_t void
97#else
98#define request_arg_t request_queue_t *q
99#endif
100
101#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14)
102#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT
103#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT
104#else
105#define BLK_INC_USE_COUNT do {} while(0)
106#define BLK_DEC_USE_COUNT do {} while(0)
107#endif
108
109
110
111
112static int shuffle_freq = 50;
113MODULE_PARM(shuffle_freq, "i");
114
115
116
117
118#ifndef FTL_MAJOR
119#define FTL_MAJOR 44
120#endif
121
122
123#define MAJOR_NR FTL_MAJOR
124#define DEVICE_NAME "ftl"
125#define DEVICE_REQUEST do_ftl_request
126#define DEVICE_ON(device)
127#define DEVICE_OFF(device)
128
129#define DEVICE_NR(minor) ((minor)>>5)
130#define REGION_NR(minor) (((minor)>>3)&3)
131#define PART_NR(minor) ((minor)&7)
132#define MINOR_NR(dev,reg,part) (((dev)<<5)+((reg)<<3)+(part))
133
134#include <linux/blk.h>
135
136
137
138
139#define MAX_DEV 4
140
141
142#define MAX_REGION 4
143
144
145#define PART_BITS 3
146#define MAX_PART 8
147
148
149#define MAX_ERASE 8
150
151
152#define SECTOR_SIZE 512
153
154
155
156typedef struct partition_t {
157 struct mtd_info *mtd;
158 u_int32_t state;
159 u_int32_t *VirtualBlockMap;
160 u_int32_t *VirtualPageMap;
161 u_int32_t FreeTotal;
162 struct eun_info_t {
163 u_int32_t Offset;
164 u_int32_t EraseCount;
165 u_int32_t Free;
166 u_int32_t Deleted;
167 } *EUNInfo;
168 struct xfer_info_t {
169 u_int32_t Offset;
170 u_int32_t EraseCount;
171 u_int16_t state;
172 } *XferInfo;
173 u_int16_t bam_index;
174 u_int32_t *bam_cache;
175 u_int16_t DataUnits;
176 u_int32_t BlocksPerUnit;
177 erase_unit_header_t header;
178#if 0
179 region_info_t region;
180 memory_handle_t handle;
181#endif
182 atomic_t open;
183} partition_t;
184
185partition_t *myparts[MAX_MTD_DEVICES];
186
187static void ftl_notify_add(struct mtd_info *mtd);
188static void ftl_notify_remove(struct mtd_info *mtd);
189
190void ftl_freepart(partition_t *part);
191
192static struct mtd_notifier ftl_notifier = {
193 add: ftl_notify_add,
194 remove: ftl_notify_remove,
195};
196
197
198#define FTL_FORMATTED 0x01
199
200
201#define XFER_UNKNOWN 0x00
202#define XFER_ERASING 0x01
203#define XFER_ERASED 0x02
204#define XFER_PREPARED 0x03
205#define XFER_FAILED 0x04
206
207static struct hd_struct ftl_hd[MINOR_NR(MAX_DEV, 0, 0)];
208static int ftl_sizes[MINOR_NR(MAX_DEV, 0, 0)];
209static int ftl_blocksizes[MINOR_NR(MAX_DEV, 0, 0)];
210
211static struct gendisk ftl_gendisk = {
212 major: FTL_MAJOR,
213 major_name: "ftl",
214 minor_shift: PART_BITS,
215 max_p: MAX_PART,
216#if (LINUX_VERSION_CODE < 0x20328)
217 max_nr: MAX_DEV*MAX_PART,
218#endif
219 part: ftl_hd,
220 sizes: ftl_sizes,
221};
222
223
224
225static int ftl_ioctl(struct inode *inode, struct file *file,
226 u_int cmd, u_long arg);
227static int ftl_open(struct inode *inode, struct file *file);
228static release_t ftl_close(struct inode *inode, struct file *file);
229static int ftl_reread_partitions(int minor);
230
231static void ftl_erase_callback(struct erase_info *done);
232
233#if LINUX_VERSION_CODE < 0x20326
234static struct file_operations ftl_blk_fops = {
235 open: ftl_open,
236 release: ftl_close,
237 ioctl: ftl_ioctl,
238 read: block_read,
239 write: block_write,
240 fsync: block_fsync
241};
242#else
243static struct block_device_operations ftl_blk_fops = {
244#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14)
245 owner: THIS_MODULE,
246#endif
247 open: ftl_open,
248 release: ftl_close,
249 ioctl: ftl_ioctl,
250};
251#endif
252
253
254
255
256
257
258
259
260
261static int scan_header(partition_t *part)
262{
263 erase_unit_header_t header;
264 loff_t offset, max_offset;
265 int ret;
266 part->header.FormattedSize = 0;
267 max_offset = (0x100000<part->mtd->size)?0x100000:part->mtd->size;
268
269 for (offset = 0;
270 (offset + sizeof(header)) < max_offset;
271 offset += part->mtd->erasesize ? : 0x2000) {
272
273 ret = part->mtd->read(part->mtd, offset, sizeof(header), &ret,
274 (unsigned char *)&header);
275
276 if (ret)
277 return ret;
278
279 if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break;
280 }
281
282 if (offset == max_offset) {
283 printk(KERN_NOTICE "ftl_cs: FTL header not found.\n");
284 return -ENOENT;
285 }
286 if ((le16_to_cpu(header.NumEraseUnits) > 65536) || header.BlockSize != 9 ||
287 (header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) ||
288 (header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) {
289 printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n");
290 return -1;
291 }
292 if ((1 << header.EraseUnitSize) != part->mtd->erasesize) {
293 printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x\n",
294 1 << header.EraseUnitSize,part->mtd->erasesize);
295 return -1;
296 }
297 part->header = header;
298 return 0;
299}
300
301static int build_maps(partition_t *part)
302{
303 erase_unit_header_t header;
304 u_int16_t xvalid, xtrans, i;
305 u_int blocks, j;
306 int hdr_ok, ret = -1;
307 ssize_t retval;
308 loff_t offset;
309
310
311 part->DataUnits = le16_to_cpu(part->header.NumEraseUnits) -
312 part->header.NumTransferUnits;
313 part->EUNInfo = kmalloc(part->DataUnits * sizeof(struct eun_info_t),
314 GFP_KERNEL);
315 if (!part->EUNInfo)
316 goto out;
317 for (i = 0; i < part->DataUnits; i++)
318 part->EUNInfo[i].Offset = 0xffffffff;
319 part->XferInfo =
320 kmalloc(part->header.NumTransferUnits * sizeof(struct xfer_info_t),
321 GFP_KERNEL);
322 if (!part->XferInfo)
323 goto out_EUNInfo;
324
325 xvalid = xtrans = 0;
326 for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) {
327 offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN))
328 << part->header.EraseUnitSize);
329 ret = part->mtd->read(part->mtd, offset, sizeof(header), &retval,
330 (unsigned char *)&header);
331
332 if (ret)
333 goto out_XferInfo;
334
335 ret = -1;
336
337 hdr_ok = (strcmp(header.DataOrgTuple+3, "FTL100") == 0);
338 if (hdr_ok && (le16_to_cpu(header.LogicalEUN) < part->DataUnits) &&
339 (part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset == 0xffffffff)) {
340 part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset = offset;
341 part->EUNInfo[le16_to_cpu(header.LogicalEUN)].EraseCount =
342 le32_to_cpu(header.EraseCount);
343 xvalid++;
344 } else {
345 if (xtrans == part->header.NumTransferUnits) {
346 printk(KERN_NOTICE "ftl_cs: format error: too many "
347 "transfer units!\n");
348 goto out_XferInfo;
349 }
350 if (hdr_ok && (le16_to_cpu(header.LogicalEUN) == 0xffff)) {
351 part->XferInfo[xtrans].state = XFER_PREPARED;
352 part->XferInfo[xtrans].EraseCount = le32_to_cpu(header.EraseCount);
353 } else {
354 part->XferInfo[xtrans].state = XFER_UNKNOWN;
355
356 part->XferInfo[xtrans].EraseCount =
357 le32_to_cpu(part->header.EraseCount);
358 }
359 part->XferInfo[xtrans].Offset = offset;
360 xtrans++;
361 }
362 }
363
364 header = part->header;
365 if ((xtrans != header.NumTransferUnits) ||
366 (xvalid+xtrans != le16_to_cpu(header.NumEraseUnits))) {
367 printk(KERN_NOTICE "ftl_cs: format error: erase units "
368 "don't add up!\n");
369 goto out_XferInfo;
370 }
371
372
373 blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize;
374 part->VirtualBlockMap = vmalloc(blocks * sizeof(u_int32_t));
375 if (!part->VirtualBlockMap)
376 goto out_XferInfo;
377
378 memset(part->VirtualBlockMap, 0xff, blocks * sizeof(u_int32_t));
379 part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize;
380
381 part->bam_cache = kmalloc(part->BlocksPerUnit * sizeof(u_int32_t),
382 GFP_KERNEL);
383 if (!part->bam_cache)
384 goto out_VirtualBlockMap;
385
386 part->bam_index = 0xffff;
387 part->FreeTotal = 0;
388
389 for (i = 0; i < part->DataUnits; i++) {
390 part->EUNInfo[i].Free = 0;
391 part->EUNInfo[i].Deleted = 0;
392 offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset);
393
394 ret = part->mtd->read(part->mtd, offset,
395 part->BlocksPerUnit * sizeof(u_int32_t), &retval,
396 (unsigned char *)part->bam_cache);
397
398 if (ret)
399 goto out_bam_cache;
400
401 for (j = 0; j < part->BlocksPerUnit; j++) {
402 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) {
403 part->EUNInfo[i].Free++;
404 part->FreeTotal++;
405 } else if ((BLOCK_TYPE(le32_to_cpu(part->bam_cache[j])) == BLOCK_DATA) &&
406 (BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j])) < blocks))
407 part->VirtualBlockMap[BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j]))] =
408 (i << header.EraseUnitSize) + (j << header.BlockSize);
409 else if (BLOCK_DELETED(le32_to_cpu(part->bam_cache[j])))
410 part->EUNInfo[i].Deleted++;
411 }
412 }
413
414 ret = 0;
415 goto out;
416
417out_bam_cache:
418 kfree(part->bam_cache);
419out_VirtualBlockMap:
420 vfree(part->VirtualBlockMap);
421out_XferInfo:
422 kfree(part->XferInfo);
423out_EUNInfo:
424 kfree(part->EUNInfo);
425out:
426 return ret;
427}
428
429
430
431
432
433
434
435
436static int erase_xfer(partition_t *part,
437 u_int16_t xfernum)
438{
439 int ret;
440 struct xfer_info_t *xfer;
441 struct erase_info *erase;
442
443 xfer = &part->XferInfo[xfernum];
444 DEBUG(1, "ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset);
445 xfer->state = XFER_ERASING;
446
447
448
449
450 erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL);
451 if (!erase)
452 return -ENOMEM;
453
454 erase->callback = ftl_erase_callback;
455 erase->addr = xfer->Offset;
456 erase->len = 1 << part->header.EraseUnitSize;
457 erase->priv = (u_long)part;
458
459 ret = part->mtd->erase(part->mtd, erase);
460
461 if (!ret)
462 xfer->EraseCount++;
463 else
464 kfree(erase);
465
466 return ret;
467}
468
469
470
471
472
473
474
475
476static void ftl_erase_callback(struct erase_info *erase)
477{
478 partition_t *part;
479 struct xfer_info_t *xfer;
480 int i;
481
482
483 part = (partition_t *)(erase->priv);
484
485 for (i = 0; i < part->header.NumTransferUnits; i++)
486 if (part->XferInfo[i].Offset == erase->addr) break;
487
488 if (i == part->header.NumTransferUnits) {
489 printk(KERN_NOTICE "ftl_cs: internal error: "
490 "erase lookup failed!\n");
491 return;
492 }
493
494 xfer = &part->XferInfo[i];
495 if (erase->state == MTD_ERASE_DONE)
496 xfer->state = XFER_ERASED;
497 else {
498 xfer->state = XFER_FAILED;
499 printk(KERN_NOTICE "ftl_cs: erase failed: state = %d\n",
500 erase->state);
501 }
502
503 kfree(erase);
504
505}
506
507static int prepare_xfer(partition_t *part, int i)
508{
509 erase_unit_header_t header;
510 struct xfer_info_t *xfer;
511 int nbam, ret;
512 u_int32_t ctl;
513 ssize_t retlen;
514 loff_t offset;
515
516 xfer = &part->XferInfo[i];
517 xfer->state = XFER_FAILED;
518
519 DEBUG(1, "ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset);
520
521
522 header = part->header;
523 header.LogicalEUN = cpu_to_le16(0xffff);
524 header.EraseCount = cpu_to_le32(xfer->EraseCount);
525
526 ret = part->mtd->write(part->mtd, xfer->Offset, sizeof(header),
527 &retlen, (u_char *)&header);
528
529 if (ret) {
530 return ret;
531 }
532
533
534 nbam = (part->BlocksPerUnit * sizeof(u_int32_t) +
535 le32_to_cpu(part->header.BAMOffset) + SECTOR_SIZE - 1) / SECTOR_SIZE;
536
537 offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
538 ctl = cpu_to_le32(BLOCK_CONTROL);
539
540 for (i = 0; i < nbam; i++, offset += sizeof(u_int32_t)) {
541
542 ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t),
543 &retlen, (u_char *)&ctl);
544
545 if (ret)
546 return ret;
547 }
548 xfer->state = XFER_PREPARED;
549 return 0;
550
551}
552
553
554
555
556
557
558
559
560
561
562
563
564
565static int copy_erase_unit(partition_t *part, u_int16_t srcunit,
566 u_int16_t xferunit)
567{
568 u_char buf[SECTOR_SIZE];
569 struct eun_info_t *eun;
570 struct xfer_info_t *xfer;
571 u_int32_t src, dest, free, i;
572 u_int16_t unit;
573 int ret;
574 ssize_t retlen;
575 loff_t offset;
576 u_int16_t srcunitswap = cpu_to_le16(srcunit);
577
578 eun = &part->EUNInfo[srcunit];
579 xfer = &part->XferInfo[xferunit];
580 DEBUG(2, "ftl_cs: copying block 0x%x to 0x%x\n",
581 eun->Offset, xfer->Offset);
582
583
584
585 if (part->bam_index != srcunit) {
586
587 offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
588
589 ret = part->mtd->read(part->mtd, offset,
590 part->BlocksPerUnit * sizeof(u_int32_t),
591 &retlen, (u_char *) (part->bam_cache));
592
593
594 part->bam_index = 0xffff;
595
596 if (ret) {
597 printk( KERN_WARNING "ftl: Failed to read BAM cache in copy_erase_unit()!\n");
598 return ret;
599 }
600 }
601
602
603 xfer->state = XFER_UNKNOWN;
604 offset = xfer->Offset + 20;
605 unit = cpu_to_le16(0x7fff);
606
607 ret = part->mtd->write(part->mtd, offset, sizeof(u_int16_t),
608 &retlen, (u_char *) &unit);
609
610 if (ret) {
611 printk( KERN_WARNING "ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
612 return ret;
613 }
614
615
616 src = eun->Offset; dest = xfer->Offset;
617
618 free = 0;
619 ret = 0;
620 for (i = 0; i < part->BlocksPerUnit; i++) {
621 switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) {
622 case BLOCK_CONTROL:
623
624 break;
625 case BLOCK_DATA:
626 case BLOCK_REPLACEMENT:
627 ret = part->mtd->read(part->mtd, src, SECTOR_SIZE,
628 &retlen, (u_char *) buf);
629 if (ret) {
630 printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n");
631 return ret;
632 }
633
634
635 ret = part->mtd->write(part->mtd, dest, SECTOR_SIZE,
636 &retlen, (u_char *) buf);
637 if (ret) {
638 printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n");
639 return ret;
640 }
641
642 break;
643 default:
644
645 part->bam_cache[i] = cpu_to_le32(0xffffffff);
646 free++;
647 break;
648 }
649 src += SECTOR_SIZE;
650 dest += SECTOR_SIZE;
651 }
652
653
654 ret = part->mtd->write(part->mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset),
655 part->BlocksPerUnit * sizeof(int32_t), &retlen,
656 (u_char *)part->bam_cache);
657 if (ret) {
658 printk( KERN_WARNING "ftl: Error writing BAM in copy_erase_unit\n");
659 return ret;
660 }
661
662
663
664 ret = part->mtd->write(part->mtd, xfer->Offset + 20, sizeof(u_int16_t),
665 &retlen, (u_char *)&srcunitswap);
666
667 if (ret) {
668 printk(KERN_WARNING "ftl: Error writing new LogicalEUN in copy_erase_unit\n");
669 return ret;
670 }
671
672
673
674 i = xfer->EraseCount;
675 xfer->EraseCount = eun->EraseCount;
676 eun->EraseCount = i;
677 i = xfer->Offset;
678 xfer->Offset = eun->Offset;
679 eun->Offset = i;
680 part->FreeTotal -= eun->Free;
681 part->FreeTotal += free;
682 eun->Free = free;
683 eun->Deleted = 0;
684
685
686 part->bam_index = srcunit;
687
688 return 0;
689}
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707static int reclaim_block(partition_t *part)
708{
709 u_int16_t i, eun, xfer;
710 u_int32_t best;
711 int queued, ret;
712
713 DEBUG(0, "ftl_cs: reclaiming space...\n");
714 DEBUG(3, "NumTransferUnits == %x\n", part->header.NumTransferUnits);
715
716 best = 0xffffffff; xfer = 0xffff;
717 do {
718 queued = 0;
719 for (i = 0; i < part->header.NumTransferUnits; i++) {
720 int n=0;
721 if (part->XferInfo[i].state == XFER_UNKNOWN) {
722 DEBUG(3,"XferInfo[%d].state == XFER_UNKNOWN\n",i);
723 n=1;
724 erase_xfer(part, i);
725 }
726 if (part->XferInfo[i].state == XFER_ERASING) {
727 DEBUG(3,"XferInfo[%d].state == XFER_ERASING\n",i);
728 n=1;
729 queued = 1;
730 }
731 else if (part->XferInfo[i].state == XFER_ERASED) {
732 DEBUG(3,"XferInfo[%d].state == XFER_ERASED\n",i);
733 n=1;
734 prepare_xfer(part, i);
735 }
736 if (part->XferInfo[i].state == XFER_PREPARED) {
737 DEBUG(3,"XferInfo[%d].state == XFER_PREPARED\n",i);
738 n=1;
739 if (part->XferInfo[i].EraseCount <= best) {
740 best = part->XferInfo[i].EraseCount;
741 xfer = i;
742 }
743 }
744 if (!n)
745 DEBUG(3,"XferInfo[%d].state == %x\n",i, part->XferInfo[i].state);
746
747 }
748 if (xfer == 0xffff) {
749 if (queued) {
750 DEBUG(1, "ftl_cs: waiting for transfer "
751 "unit to be prepared...\n");
752 if (part->mtd->sync)
753 part->mtd->sync(part->mtd);
754 } else {
755 static int ne = 0;
756 if (++ne < 5)
757 printk(KERN_NOTICE "ftl_cs: reclaim failed: no "
758 "suitable transfer units!\n");
759 else
760 DEBUG(1, "ftl_cs: reclaim failed: no "
761 "suitable transfer units!\n");
762
763 return -EIO;
764 }
765 }
766 } while (xfer == 0xffff);
767
768 eun = 0;
769 if ((jiffies % shuffle_freq) == 0) {
770 DEBUG(1, "ftl_cs: recycling freshest block...\n");
771 best = 0xffffffff;
772 for (i = 0; i < part->DataUnits; i++)
773 if (part->EUNInfo[i].EraseCount <= best) {
774 best = part->EUNInfo[i].EraseCount;
775 eun = i;
776 }
777 } else {
778 best = 0;
779 for (i = 0; i < part->DataUnits; i++)
780 if (part->EUNInfo[i].Deleted >= best) {
781 best = part->EUNInfo[i].Deleted;
782 eun = i;
783 }
784 if (best == 0) {
785 static int ne = 0;
786 if (++ne < 5)
787 printk(KERN_NOTICE "ftl_cs: reclaim failed: "
788 "no free blocks!\n");
789 else
790 DEBUG(1,"ftl_cs: reclaim failed: "
791 "no free blocks!\n");
792
793 return -EIO;
794 }
795 }
796 ret = copy_erase_unit(part, eun, xfer);
797 if (!ret)
798 erase_xfer(part, xfer);
799 else
800 printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed!\n");
801 return ret;
802}
803
804
805
806
807
808
809
810
811
812
813
814#ifdef PSYCHO_DEBUG
815static void dump_lists(partition_t *part)
816{
817 int i;
818 printk(KERN_DEBUG "ftl_cs: Free total = %d\n", part->FreeTotal);
819 for (i = 0; i < part->DataUnits; i++)
820 printk(KERN_DEBUG "ftl_cs: unit %d: %d phys, %d free, "
821 "%d deleted\n", i,
822 part->EUNInfo[i].Offset >> part->header.EraseUnitSize,
823 part->EUNInfo[i].Free, part->EUNInfo[i].Deleted);
824}
825#endif
826
827static u_int32_t find_free(partition_t *part)
828{
829 u_int16_t stop, eun;
830 u_int32_t blk;
831 size_t retlen;
832 int ret;
833
834
835 stop = (part->bam_index == 0xffff) ? 0 : part->bam_index;
836 eun = stop;
837 do {
838 if (part->EUNInfo[eun].Free != 0) break;
839
840 if (++eun == part->DataUnits) eun = 0;
841 } while (eun != stop);
842
843 if (part->EUNInfo[eun].Free == 0)
844 return 0;
845
846
847 if (eun != part->bam_index) {
848
849 part->bam_index = 0xffff;
850
851 ret = part->mtd->read(part->mtd,
852 part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
853 part->BlocksPerUnit * sizeof(u_int32_t),
854 &retlen, (u_char *) (part->bam_cache));
855
856 if (ret) {
857 printk(KERN_WARNING"ftl: Error reading BAM in find_free\n");
858 return 0;
859 }
860 part->bam_index = eun;
861 }
862
863
864 for (blk = 0; blk < part->BlocksPerUnit; blk++)
865 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break;
866 if (blk == part->BlocksPerUnit) {
867#ifdef PSYCHO_DEBUG
868 static int ne = 0;
869 if (++ne == 1)
870 dump_lists(part);
871#endif
872 printk(KERN_NOTICE "ftl_cs: bad free list!\n");
873 return 0;
874 }
875 DEBUG(2, "ftl_cs: found free block at %d in %d\n", blk, eun);
876 return blk;
877
878}
879
880
881
882
883
884
885
886
887static int ftl_open(struct inode *inode, struct file *file)
888{
889 int minor = MINOR(inode->i_rdev);
890 partition_t *partition;
891
892 if (minor>>4 >= MAX_MTD_DEVICES)
893 return -ENODEV;
894
895 partition = myparts[minor>>4];
896
897 if (!partition)
898 return -ENODEV;
899
900 if (partition->state != FTL_FORMATTED)
901 return -ENXIO;
902
903 if (ftl_gendisk.part[minor].nr_sects == 0)
904 return -ENXIO;
905
906 BLK_INC_USE_COUNT;
907
908 if (!get_mtd_device(partition->mtd, -1)) {
909 BLK_DEC_USE_COUNT;
910 return -ENXIO;
911 }
912
913 if ((file->f_mode & 2) && !(partition->mtd->flags & MTD_CLEAR_BITS) ) {
914 put_mtd_device(partition->mtd);
915 BLK_DEC_USE_COUNT;
916 return -EROFS;
917 }
918
919 DEBUG(0, "ftl_cs: ftl_open(%d)\n", minor);
920
921 atomic_inc(&partition->open);
922
923 return 0;
924}
925
926
927
928static release_t ftl_close(struct inode *inode, struct file *file)
929{
930 int minor = MINOR(inode->i_rdev);
931 partition_t *part = myparts[minor >> 4];
932 int i;
933
934 DEBUG(0, "ftl_cs: ftl_close(%d)\n", minor);
935
936
937 if (part->mtd->sync)
938 part->mtd->sync(part->mtd);
939
940 for (i = 0; i < part->header.NumTransferUnits; i++) {
941 if (part->XferInfo[i].state == XFER_ERASED)
942 prepare_xfer(part, i);
943 }
944
945 atomic_dec(&part->open);
946
947 put_mtd_device(part->mtd);
948 BLK_DEC_USE_COUNT;
949 release_return(0);
950}
951
952
953
954
955
956
957
958
959static int ftl_read(partition_t *part, caddr_t buffer,
960 u_long sector, u_long nblocks)
961{
962 u_int32_t log_addr, bsize;
963 u_long i;
964 int ret;
965 size_t offset, retlen;
966
967 DEBUG(2, "ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
968 part, sector, nblocks);
969 if (!(part->state & FTL_FORMATTED)) {
970 printk(KERN_NOTICE "ftl_cs: bad partition\n");
971 return -EIO;
972 }
973 bsize = 1 << part->header.EraseUnitSize;
974
975 for (i = 0; i < nblocks; i++) {
976 if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) {
977 printk(KERN_NOTICE "ftl_cs: bad read offset\n");
978 return -EIO;
979 }
980 log_addr = part->VirtualBlockMap[sector+i];
981 if (log_addr == 0xffffffff)
982 memset(buffer, 0, SECTOR_SIZE);
983 else {
984 offset = (part->EUNInfo[log_addr / bsize].Offset
985 + (log_addr % bsize));
986 ret = part->mtd->read(part->mtd, offset, SECTOR_SIZE,
987 &retlen, (u_char *) buffer);
988
989 if (ret) {
990 printk(KERN_WARNING "Error reading MTD device in ftl_read()\n");
991 return ret;
992 }
993 }
994 buffer += SECTOR_SIZE;
995 }
996 return 0;
997}
998
999
1000
1001
1002
1003
1004
1005static int set_bam_entry(partition_t *part, u_int32_t log_addr,
1006 u_int32_t virt_addr)
1007{
1008 u_int32_t bsize, blk, le_virt_addr;
1009#ifdef PSYCHO_DEBUG
1010 u_int32_t old_addr;
1011#endif
1012 u_int16_t eun;
1013 int ret;
1014 size_t retlen, offset;
1015
1016 DEBUG(2, "ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
1017 part, log_addr, virt_addr);
1018 bsize = 1 << part->header.EraseUnitSize;
1019 eun = log_addr / bsize;
1020 blk = (log_addr % bsize) / SECTOR_SIZE;
1021 offset = (part->EUNInfo[eun].Offset + blk * sizeof(u_int32_t) +
1022 le32_to_cpu(part->header.BAMOffset));
1023
1024#ifdef PSYCHO_DEBUG
1025 ret = part->mtd->read(part->mtd, offset, sizeof(u_int32_t),
1026 &retlen, (u_char *)&old_addr);
1027 if (ret) {
1028 printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
1029 return ret;
1030 }
1031 old_addr = le32_to_cpu(old_addr);
1032
1033 if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) ||
1034 ((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) ||
1035 (!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) {
1036 static int ne = 0;
1037 if (++ne < 5) {
1038 printk(KERN_NOTICE "ftl_cs: set_bam_entry() inconsistency!\n");
1039 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, old = 0x%x"
1040 ", new = 0x%x\n", log_addr, old_addr, virt_addr);
1041 }
1042 return -EIO;
1043 }
1044#endif
1045 le_virt_addr = cpu_to_le32(virt_addr);
1046 if (part->bam_index == eun) {
1047#ifdef PSYCHO_DEBUG
1048 if (le32_to_cpu(part->bam_cache[blk]) != old_addr) {
1049 static int ne = 0;
1050 if (++ne < 5) {
1051 printk(KERN_NOTICE "ftl_cs: set_bam_entry() "
1052 "inconsistency!\n");
1053 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, cache"
1054 " = 0x%x\n",
1055 le32_to_cpu(part->bam_cache[blk]), old_addr);
1056 }
1057 return -EIO;
1058 }
1059#endif
1060 part->bam_cache[blk] = le_virt_addr;
1061 }
1062 ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t),
1063 &retlen, (u_char *)&le_virt_addr);
1064
1065 if (ret) {
1066 printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!\n");
1067 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, new = 0x%x\n",
1068 log_addr, virt_addr);
1069 }
1070 return ret;
1071}
1072
1073static int ftl_write(partition_t *part, caddr_t buffer,
1074 u_long sector, u_long nblocks)
1075{
1076 u_int32_t bsize, log_addr, virt_addr, old_addr, blk;
1077 u_long i;
1078 int ret;
1079 size_t retlen, offset;
1080
1081 DEBUG(2, "ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
1082 part, sector, nblocks);
1083 if (!(part->state & FTL_FORMATTED)) {
1084 printk(KERN_NOTICE "ftl_cs: bad partition\n");
1085 return -EIO;
1086 }
1087
1088 while (part->FreeTotal < nblocks) {
1089 ret = reclaim_block(part);
1090 if (ret)
1091 return ret;
1092 }
1093
1094 bsize = 1 << part->header.EraseUnitSize;
1095
1096 virt_addr = sector * SECTOR_SIZE | BLOCK_DATA;
1097 for (i = 0; i < nblocks; i++) {
1098 if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) {
1099 printk(KERN_NOTICE "ftl_cs: bad write offset\n");
1100 return -EIO;
1101 }
1102
1103
1104 blk = find_free(part);
1105 if (blk == 0) {
1106 static int ne = 0;
1107 if (++ne < 5)
1108 printk(KERN_NOTICE "ftl_cs: internal error: "
1109 "no free blocks!\n");
1110 return -ENOSPC;
1111 }
1112
1113
1114 log_addr = part->bam_index * bsize + blk * SECTOR_SIZE;
1115 part->EUNInfo[part->bam_index].Free--;
1116 part->FreeTotal--;
1117 if (set_bam_entry(part, log_addr, 0xfffffffe))
1118 return -EIO;
1119 part->EUNInfo[part->bam_index].Deleted++;
1120 offset = (part->EUNInfo[part->bam_index].Offset +
1121 blk * SECTOR_SIZE);
1122 ret = part->mtd->write(part->mtd, offset, SECTOR_SIZE, &retlen,
1123 buffer);
1124
1125 if (ret) {
1126 printk(KERN_NOTICE "ftl_cs: block write failed!\n");
1127 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, virt_addr"
1128 " = 0x%x, Offset = 0x%x\n", log_addr, virt_addr,
1129 offset);
1130 return -EIO;
1131 }
1132
1133
1134 old_addr = part->VirtualBlockMap[sector+i];
1135 if (old_addr != 0xffffffff) {
1136 part->VirtualBlockMap[sector+i] = 0xffffffff;
1137 part->EUNInfo[old_addr/bsize].Deleted++;
1138 if (set_bam_entry(part, old_addr, 0))
1139 return -EIO;
1140 }
1141
1142
1143 if (set_bam_entry(part, log_addr, virt_addr))
1144 return -EIO;
1145 part->VirtualBlockMap[sector+i] = log_addr;
1146 part->EUNInfo[part->bam_index].Deleted--;
1147
1148 buffer += SECTOR_SIZE;
1149 virt_addr += SECTOR_SIZE;
1150 }
1151 return 0;
1152}
1153
1154
1155
1156
1157
1158
1159
1160static int ftl_ioctl(struct inode *inode, struct file *file,
1161 u_int cmd, u_long arg)
1162{
1163 struct hd_geometry *geo = (struct hd_geometry *)arg;
1164 int ret = 0, minor = MINOR(inode->i_rdev);
1165 partition_t *part= myparts[minor >> 4];
1166 u_long sect;
1167
1168 if (!part)
1169 return -ENODEV;
1170
1171 switch (cmd) {
1172 case HDIO_GETGEO:
1173 ret = verify_area(VERIFY_WRITE, (long *)arg, sizeof(*geo));
1174 if (ret) return ret;
1175
1176 sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
1177 put_user(1, (char *)&geo->heads);
1178 put_user(8, (char *)&geo->sectors);
1179 put_user((sect>>3), (short *)&geo->cylinders);
1180 put_user(ftl_hd[minor].start_sect, (u_long *)&geo->start);
1181 break;
1182 case BLKGETSIZE:
1183 ret = put_user(ftl_hd[minor].nr_sects, (unsigned long *)arg);
1184 break;
1185#ifdef BLKGETSIZE64
1186 case BLKGETSIZE64:
1187 ret = put_user((u64)ftl_hd[minor].nr_sects << 9, (u64 *)arg);
1188 break;
1189#endif
1190 case BLKRRPART:
1191 ret = ftl_reread_partitions(minor);
1192 break;
1193#if (LINUX_VERSION_CODE < 0x20303)
1194 case BLKFLSBUF:
1195#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
1196 if (!capable(CAP_SYS_ADMIN)) return -EACCES;
1197#endif
1198 fsync_dev(inode->i_rdev);
1199 invalidate_buffers(inode->i_rdev);
1200 break;
1201 RO_IOCTLS(inode->i_rdev, arg);
1202#else
1203 case BLKROSET:
1204 case BLKROGET:
1205 case BLKFLSBUF:
1206 ret = blk_ioctl(inode->i_rdev, cmd, arg);
1207 break;
1208#endif
1209 default:
1210 ret = -EINVAL;
1211 }
1212
1213 return ret;
1214}
1215
1216
1217
1218
1219
1220
1221
1222static int ftl_reread_partitions(int minor)
1223{
1224 partition_t *part = myparts[minor >> 4];
1225 int i, whole;
1226
1227 DEBUG(0, "ftl_cs: ftl_reread_partition(%d)\n", minor);
1228 if ((atomic_read(&part->open) > 1)) {
1229 return -EBUSY;
1230 }
1231 whole = minor & ~(MAX_PART-1);
1232
1233 i = MAX_PART - 1;
1234 while (i-- > 0) {
1235 if (ftl_hd[whole+i].nr_sects > 0) {
1236 kdev_t rdev = MKDEV(FTL_MAJOR, whole+i);
1237
1238 invalidate_device(rdev, 1);
1239 }
1240 ftl_hd[whole+i].start_sect = 0;
1241 ftl_hd[whole+i].nr_sects = 0;
1242 }
1243
1244 scan_header(part);
1245
1246 register_disk(&ftl_gendisk, whole >> PART_BITS, MAX_PART,
1247 &ftl_blk_fops, le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE);
1248
1249#ifdef PCMCIA_DEBUG
1250 for (i = 0; i < MAX_PART; i++) {
1251 if (ftl_hd[whole+i].nr_sects > 0)
1252 printk(KERN_INFO " %d: start %ld size %ld\n", i,
1253 ftl_hd[whole+i].start_sect,
1254 ftl_hd[whole+i].nr_sects);
1255 }
1256#endif
1257 return 0;
1258}
1259
1260
1261
1262
1263
1264
1265
1266static void do_ftl_request(request_arg_t)
1267{
1268 int ret, minor;
1269 partition_t *part;
1270
1271 do {
1272
1273 INIT_REQUEST;
1274
1275 minor = MINOR(CURRENT->rq_dev);
1276
1277 part = myparts[minor >> 4];
1278 if (part) {
1279 ret = 0;
1280
1281 switch (CURRENT->cmd) {
1282 case READ:
1283 ret = ftl_read(part, CURRENT->buffer,
1284 CURRENT->sector+ftl_hd[minor].start_sect,
1285 CURRENT->current_nr_sectors);
1286 if (ret) printk("ftl_read returned %d\n", ret);
1287 break;
1288
1289 case WRITE:
1290 ret = ftl_write(part, CURRENT->buffer,
1291 CURRENT->sector+ftl_hd[minor].start_sect,
1292 CURRENT->current_nr_sectors);
1293 if (ret) printk("ftl_write returned %d\n", ret);
1294 break;
1295
1296 default:
1297 panic("ftl_cs: unknown block command!\n");
1298
1299 }
1300 } else {
1301 ret = 1;
1302 printk("NULL part in ftl_request\n");
1303 }
1304
1305 if (!ret) {
1306 CURRENT->sector += CURRENT->current_nr_sectors;
1307 }
1308
1309 end_request((ret == 0) ? 1 : 0);
1310 } while (1);
1311}
1312
1313
1314
1315void ftl_freepart(partition_t *part)
1316{
1317 if (part->VirtualBlockMap) {
1318 vfree(part->VirtualBlockMap);
1319 part->VirtualBlockMap = NULL;
1320 }
1321 if (part->VirtualPageMap) {
1322 kfree(part->VirtualPageMap);
1323 part->VirtualPageMap = NULL;
1324 }
1325 if (part->EUNInfo) {
1326 kfree(part->EUNInfo);
1327 part->EUNInfo = NULL;
1328 }
1329 if (part->XferInfo) {
1330 kfree(part->XferInfo);
1331 part->XferInfo = NULL;
1332 }
1333 if (part->bam_cache) {
1334 kfree(part->bam_cache);
1335 part->bam_cache = NULL;
1336 }
1337
1338}
1339
1340static void ftl_notify_add(struct mtd_info *mtd)
1341{
1342 partition_t *partition;
1343 int device;
1344
1345 for (device=0; device < MAX_MTD_DEVICES && myparts[device]; device++)
1346 ;
1347
1348 if (device == MAX_MTD_DEVICES) {
1349 printk(KERN_NOTICE "Maximum number of FTL partitions reached\n"
1350 "Not scanning <%s>\n", mtd->name);
1351 return;
1352 }
1353
1354 partition = kmalloc(sizeof(partition_t), GFP_KERNEL);
1355
1356 if (!partition) {
1357 printk(KERN_WARNING "No memory to scan for FTL on %s\n",
1358 mtd->name);
1359 return;
1360 }
1361
1362 memset(partition, 0, sizeof(partition_t));
1363
1364 partition->mtd = mtd;
1365
1366 if ((scan_header(partition) == 0) &&
1367 (build_maps(partition) == 0)) {
1368
1369 partition->state = FTL_FORMATTED;
1370 atomic_set(&partition->open, 0);
1371 myparts[device] = partition;
1372 ftl_reread_partitions(device << 4);
1373#ifdef PCMCIA_DEBUG
1374 printk(KERN_INFO "ftl_cs: opening %d kb FTL partition\n",
1375 le32_to_cpu(partition->header.FormattedSize) >> 10);
1376#endif
1377 } else
1378 kfree(partition);
1379}
1380
1381static void ftl_notify_remove(struct mtd_info *mtd)
1382{
1383 int i,j;
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395 for (i=0; i< MAX_MTD_DEVICES; i++)
1396 if (myparts[i] && myparts[i]->mtd == mtd) {
1397
1398 if (myparts[i]->state == FTL_FORMATTED)
1399 ftl_freepart(myparts[i]);
1400
1401 myparts[i]->state = 0;
1402 for (j=0; j<16; j++) {
1403 ftl_gendisk.part[j].nr_sects=0;
1404 ftl_gendisk.part[j].start_sect=0;
1405 }
1406 kfree(myparts[i]);
1407 myparts[i] = NULL;
1408 }
1409}
1410
1411int init_ftl(void)
1412{
1413 int i;
1414
1415 memset(myparts, 0, sizeof(myparts));
1416
1417 DEBUG(0, "$Id: ftl.c,v 1.45 2003/01/24 23:31:27 dwmw2 Exp $\n");
1418
1419 if (register_blkdev(FTL_MAJOR, "ftl", &ftl_blk_fops)) {
1420 printk(KERN_NOTICE "ftl_cs: unable to grab major "
1421 "device number!\n");
1422 return -EAGAIN;
1423 }
1424
1425 for (i = 0; i < MINOR_NR(MAX_DEV, 0, 0); i++)
1426 ftl_blocksizes[i] = 1024;
1427 for (i = 0; i < MAX_DEV*MAX_PART; i++) {
1428 ftl_hd[i].nr_sects = 0;
1429 ftl_hd[i].start_sect = 0;
1430 }
1431 blksize_size[FTL_MAJOR] = ftl_blocksizes;
1432 ftl_gendisk.major = FTL_MAJOR;
1433 blk_init_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR), &do_ftl_request);
1434 add_gendisk(&ftl_gendisk);
1435
1436 register_mtd_user(&ftl_notifier);
1437
1438 return 0;
1439}
1440
1441static void __exit cleanup_ftl(void)
1442{
1443 unregister_mtd_user(&ftl_notifier);
1444
1445 unregister_blkdev(FTL_MAJOR, "ftl");
1446 blk_cleanup_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR));
1447 blksize_size[FTL_MAJOR] = NULL;
1448
1449 del_gendisk(&ftl_gendisk);
1450}
1451
1452module_init(init_ftl);
1453module_exit(cleanup_ftl);
1454
1455
1456MODULE_LICENSE("Dual MPL/GPL");
1457MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1458MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");
1459