1 /* This version ported to the Linux-MTD system by dwmw2@infradead.org
2 * $Id: ftl.c,v 1.45 2003/01/24 23:31:27 dwmw2 Exp $
4 * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
5 * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
9 /*======================================================================
11 A Flash Translation Layer memory card driver
13 This driver implements a disk-like block device driver with an
14 apparent block size of 512 bytes for flash memory cards.
16 ftl_cs.c 1.62 2000/02/01 00:59:04
18 The contents of this file are subject to the Mozilla Public
19 License Version 1.1 (the "License"); you may not use this file
20 except in compliance with the License. You may obtain a copy of
21 the License at http://www.mozilla.org/MPL/
23 Software distributed under the License is distributed on an "AS
24 IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
25 implied. See the License for the specific language governing
26 rights and limitations under the License.
28 The initial developer of the original code is David A. Hinds
29 <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
30 are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
32 Alternatively, the contents of this file may be used under the
33 terms of the GNU General Public License version 2 (the "GPL"), in
34 which case the provisions of the GPL are applicable instead of the
35 above. If you wish to allow the use of your version of this file
36 only under the terms of the GPL and not to allow others to use
37 your version of this file under the MPL, indicate your decision
38 by deleting the provisions above and replace them with the notice
39 and other provisions required by the GPL. If you do not delete
40 the provisions above, a recipient may use your version of this
41 file under either the MPL or the GPL.
43 LEGAL NOTE: The FTL format is patented by M-Systems. They have
44 granted a license for its use with PCMCIA devices:
46 "M-Systems grants a royalty-free, non-exclusive license under
47 any presently existing M-Systems intellectual property rights
48 necessary for the design and development of FTL-compatible
49 drivers, file systems and utilities using the data formats with
50 PCMCIA PC Cards as described in the PCMCIA Flash Translation
51 Layer (FTL) Specification."
53 Use of the FTL format for non-PCMCIA applications may be an
54 infringement of these patents. For additional information,
55 contact M-Systems (http://www.m-sys.com) directly.
57 ======================================================================*/
58 #include <linux/module.h>
59 #include <linux/mtd/compatmac.h>
60 #include <linux/mtd/mtd.h>
61 /*#define PSYCHO_DEBUG */
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>
71 #include <linux/ioctl.h>
72 #include <linux/hdreg.h>
74 #if (LINUX_VERSION_CODE >= 0x20100)
75 #include <linux/vmalloc.h>
77 #if (LINUX_VERSION_CODE >= 0x20303)
78 #include <linux/blkpg.h>
81 #include <linux/mtd/ftl.h>
82 /*====================================================================*/
83 /* Stuff which really ought to be in compatmac.h */
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)
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
98 #define request_arg_t request_queue_t *q
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
105 #define BLK_INC_USE_COUNT do {} while(0)
106 #define BLK_DEC_USE_COUNT do {} while(0)
109 /*====================================================================*/
111 /* Parameters that can be set with 'insmod' */
112 static int shuffle_freq = 50;
113 MODULE_PARM(shuffle_freq, "i");
115 /*====================================================================*/
117 /* Major device # for FTL device */
122 /* Funky stuff for setting up a block device */
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)
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))
134 #include <linux/blk.h>
136 /*====================================================================*/
138 /* Maximum number of separate memory devices we'll allow */
141 /* Maximum number of regions per device */
144 /* Maximum number of partitions in an FTL region */
148 /* Maximum number of outstanding erase requests per socket */
151 /* Sector size -- shouldn't need to change */
152 #define SECTOR_SIZE 512
155 /* Each memory region corresponds to a minor device */
156 typedef struct partition_t {
157 struct mtd_info *mtd;
159 u_int32_t *VirtualBlockMap;
160 u_int32_t *VirtualPageMap;
164 u_int32_t EraseCount;
170 u_int32_t EraseCount;
174 u_int32_t *bam_cache;
176 u_int32_t BlocksPerUnit;
177 erase_unit_header_t header;
179 region_info_t region;
180 memory_handle_t handle;
185 partition_t *myparts[MAX_MTD_DEVICES];
187 static void ftl_notify_add(struct mtd_info *mtd);
188 static void ftl_notify_remove(struct mtd_info *mtd);
190 void ftl_freepart(partition_t *part);
192 static struct mtd_notifier ftl_notifier = {
194 remove: ftl_notify_remove,
197 /* Partition state flags */
198 #define FTL_FORMATTED 0x01
200 /* Transfer unit states */
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
207 static struct hd_struct ftl_hd[MINOR_NR(MAX_DEV, 0, 0)];
208 static int ftl_sizes[MINOR_NR(MAX_DEV, 0, 0)];
209 static int ftl_blocksizes[MINOR_NR(MAX_DEV, 0, 0)];
211 static struct gendisk ftl_gendisk = {
214 minor_shift: PART_BITS,
216 #if (LINUX_VERSION_CODE < 0x20328)
217 max_nr: MAX_DEV*MAX_PART,
223 /*====================================================================*/
225 static int ftl_ioctl(struct inode *inode, struct file *file,
226 u_int cmd, u_long arg);
227 static int ftl_open(struct inode *inode, struct file *file);
228 static release_t ftl_close(struct inode *inode, struct file *file);
229 static int ftl_reread_partitions(int minor);
231 static void ftl_erase_callback(struct erase_info *done);
233 #if LINUX_VERSION_CODE < 0x20326
234 static struct file_operations ftl_blk_fops = {
243 static struct block_device_operations ftl_blk_fops = {
244 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14)
253 /*======================================================================
255 Scan_header() checks to see if a memory region contains an FTL
256 partition. build_maps() reads all the erase unit headers, builds
257 the erase unit map, and then builds the virtual page map.
259 ======================================================================*/
261 static int scan_header(partition_t *part)
263 erase_unit_header_t header;
264 loff_t offset, max_offset;
266 part->header.FormattedSize = 0;
267 max_offset = (0x100000<part->mtd->size)?0x100000:part->mtd->size;
268 /* Search first megabyte for a valid FTL header */
270 (offset + sizeof(header)) < max_offset;
271 offset += part->mtd->erasesize ? : 0x2000) {
273 ret = part->mtd->read(part->mtd, offset, sizeof(header), &ret,
274 (unsigned char *)&header);
279 if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break;
282 if (offset == max_offset) {
283 printk(KERN_NOTICE "ftl_cs: FTL header not found.\n");
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");
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);
297 part->header = header;
301 static int build_maps(partition_t *part)
303 erase_unit_header_t header;
304 u_int16_t xvalid, xtrans, i;
306 int hdr_ok, ret = -1;
310 /* Set up erase unit maps */
311 part->DataUnits = le16_to_cpu(part->header.NumEraseUnits) -
312 part->header.NumTransferUnits;
313 part->EUNInfo = kmalloc(part->DataUnits * sizeof(struct eun_info_t),
317 for (i = 0; i < part->DataUnits; i++)
318 part->EUNInfo[i].Offset = 0xffffffff;
320 kmalloc(part->header.NumTransferUnits * sizeof(struct xfer_info_t),
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);
336 /* Is this a transfer partition? */
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);
345 if (xtrans == part->header.NumTransferUnits) {
346 printk(KERN_NOTICE "ftl_cs: format error: too many "
347 "transfer units!\n");
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);
354 part->XferInfo[xtrans].state = XFER_UNKNOWN;
355 /* Pick anything reasonable for the erase count */
356 part->XferInfo[xtrans].EraseCount =
357 le32_to_cpu(part->header.EraseCount);
359 part->XferInfo[xtrans].Offset = offset;
363 /* Check for format trouble */
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 "
372 /* Set up virtual page map */
373 blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize;
374 part->VirtualBlockMap = vmalloc(blocks * sizeof(u_int32_t));
375 if (!part->VirtualBlockMap)
378 memset(part->VirtualBlockMap, 0xff, blocks * sizeof(u_int32_t));
379 part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize;
381 part->bam_cache = kmalloc(part->BlocksPerUnit * sizeof(u_int32_t),
383 if (!part->bam_cache)
384 goto out_VirtualBlockMap;
386 part->bam_index = 0xffff;
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);
394 ret = part->mtd->read(part->mtd, offset,
395 part->BlocksPerUnit * sizeof(u_int32_t), &retval,
396 (unsigned char *)part->bam_cache);
401 for (j = 0; j < part->BlocksPerUnit; j++) {
402 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) {
403 part->EUNInfo[i].Free++;
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++;
418 kfree(part->bam_cache);
420 vfree(part->VirtualBlockMap);
422 kfree(part->XferInfo);
424 kfree(part->EUNInfo);
429 /*======================================================================
431 Erase_xfer() schedules an asynchronous erase operation for a
434 ======================================================================*/
436 static int erase_xfer(partition_t *part,
440 struct xfer_info_t *xfer;
441 struct erase_info *erase;
443 xfer = &part->XferInfo[xfernum];
444 DEBUG(1, "ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset);
445 xfer->state = XFER_ERASING;
447 /* Is there a free erase slot? Always in MTD. */
450 erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL);
454 erase->callback = ftl_erase_callback;
455 erase->addr = xfer->Offset;
456 erase->len = 1 << part->header.EraseUnitSize;
457 erase->priv = (u_long)part;
459 ret = part->mtd->erase(part->mtd, erase);
469 /*======================================================================
471 Prepare_xfer() takes a freshly erased transfer unit and gives
472 it an appropriate header.
474 ======================================================================*/
476 static void ftl_erase_callback(struct erase_info *erase)
479 struct xfer_info_t *xfer;
482 /* Look up the transfer unit */
483 part = (partition_t *)(erase->priv);
485 for (i = 0; i < part->header.NumTransferUnits; i++)
486 if (part->XferInfo[i].Offset == erase->addr) break;
488 if (i == part->header.NumTransferUnits) {
489 printk(KERN_NOTICE "ftl_cs: internal error: "
490 "erase lookup failed!\n");
494 xfer = &part->XferInfo[i];
495 if (erase->state == MTD_ERASE_DONE)
496 xfer->state = XFER_ERASED;
498 xfer->state = XFER_FAILED;
499 printk(KERN_NOTICE "ftl_cs: erase failed: state = %d\n",
505 } /* ftl_erase_callback */
507 static int prepare_xfer(partition_t *part, int i)
509 erase_unit_header_t header;
510 struct xfer_info_t *xfer;
516 xfer = &part->XferInfo[i];
517 xfer->state = XFER_FAILED;
519 DEBUG(1, "ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset);
521 /* Write the transfer unit header */
522 header = part->header;
523 header.LogicalEUN = cpu_to_le16(0xffff);
524 header.EraseCount = cpu_to_le32(xfer->EraseCount);
526 ret = part->mtd->write(part->mtd, xfer->Offset, sizeof(header),
527 &retlen, (u_char *)&header);
533 /* Write the BAM stub */
534 nbam = (part->BlocksPerUnit * sizeof(u_int32_t) +
535 le32_to_cpu(part->header.BAMOffset) + SECTOR_SIZE - 1) / SECTOR_SIZE;
537 offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
538 ctl = cpu_to_le32(BLOCK_CONTROL);
540 for (i = 0; i < nbam; i++, offset += sizeof(u_int32_t)) {
542 ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t),
543 &retlen, (u_char *)&ctl);
548 xfer->state = XFER_PREPARED;
553 /*======================================================================
555 Copy_erase_unit() takes a full erase block and a transfer unit,
556 copies everything to the transfer unit, then swaps the block
559 All data blocks are copied to the corresponding blocks in the
560 target unit, so the virtual block map does not need to be
563 ======================================================================*/
565 static int copy_erase_unit(partition_t *part, u_int16_t srcunit,
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;
576 u_int16_t srcunitswap = cpu_to_le16(srcunit);
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);
584 /* Read current BAM */
585 if (part->bam_index != srcunit) {
587 offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
589 ret = part->mtd->read(part->mtd, offset,
590 part->BlocksPerUnit * sizeof(u_int32_t),
591 &retlen, (u_char *) (part->bam_cache));
593 /* mark the cache bad, in case we get an error later */
594 part->bam_index = 0xffff;
597 printk( KERN_WARNING "ftl: Failed to read BAM cache in copy_erase_unit()!\n");
602 /* Write the LogicalEUN for the transfer unit */
603 xfer->state = XFER_UNKNOWN;
604 offset = xfer->Offset + 20; /* Bad! */
605 unit = cpu_to_le16(0x7fff);
607 ret = part->mtd->write(part->mtd, offset, sizeof(u_int16_t),
608 &retlen, (u_char *) &unit);
611 printk( KERN_WARNING "ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
615 /* Copy all data blocks from source unit to transfer unit */
616 src = eun->Offset; dest = xfer->Offset;
620 for (i = 0; i < part->BlocksPerUnit; i++) {
621 switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) {
623 /* This gets updated later */
626 case BLOCK_REPLACEMENT:
627 ret = part->mtd->read(part->mtd, src, SECTOR_SIZE,
628 &retlen, (u_char *) buf);
630 printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n");
635 ret = part->mtd->write(part->mtd, dest, SECTOR_SIZE,
636 &retlen, (u_char *) buf);
638 printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n");
644 /* All other blocks must be free */
645 part->bam_cache[i] = cpu_to_le32(0xffffffff);
653 /* Write the BAM to the transfer unit */
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);
658 printk( KERN_WARNING "ftl: Error writing BAM in copy_erase_unit\n");
663 /* All clear? Then update the LogicalEUN again */
664 ret = part->mtd->write(part->mtd, xfer->Offset + 20, sizeof(u_int16_t),
665 &retlen, (u_char *)&srcunitswap);
668 printk(KERN_WARNING "ftl: Error writing new LogicalEUN in copy_erase_unit\n");
673 /* Update the maps and usage stats*/
674 i = xfer->EraseCount;
675 xfer->EraseCount = eun->EraseCount;
678 xfer->Offset = eun->Offset;
680 part->FreeTotal -= eun->Free;
681 part->FreeTotal += free;
685 /* Now, the cache should be valid for the new block */
686 part->bam_index = srcunit;
689 } /* copy_erase_unit */
691 /*======================================================================
693 reclaim_block() picks a full erase unit and a transfer unit and
694 then calls copy_erase_unit() to copy one to the other. Then, it
695 schedules an erase on the expired block.
697 What's a good way to decide which transfer unit and which erase
698 unit to use? Beats me. My way is to always pick the transfer
699 unit with the fewest erases, and usually pick the data unit with
700 the most deleted blocks. But with a small probability, pick the
701 oldest data unit instead. This means that we generally postpone
702 the next reclaimation as long as possible, but shuffle static
703 stuff around a bit for wear leveling.
705 ======================================================================*/
707 static int reclaim_block(partition_t *part)
709 u_int16_t i, eun, xfer;
713 DEBUG(0, "ftl_cs: reclaiming space...\n");
714 DEBUG(3, "NumTransferUnits == %x\n", part->header.NumTransferUnits);
715 /* Pick the least erased transfer unit */
716 best = 0xffffffff; xfer = 0xffff;
719 for (i = 0; i < part->header.NumTransferUnits; i++) {
721 if (part->XferInfo[i].state == XFER_UNKNOWN) {
722 DEBUG(3,"XferInfo[%d].state == XFER_UNKNOWN\n",i);
726 if (part->XferInfo[i].state == XFER_ERASING) {
727 DEBUG(3,"XferInfo[%d].state == XFER_ERASING\n",i);
731 else if (part->XferInfo[i].state == XFER_ERASED) {
732 DEBUG(3,"XferInfo[%d].state == XFER_ERASED\n",i);
734 prepare_xfer(part, i);
736 if (part->XferInfo[i].state == XFER_PREPARED) {
737 DEBUG(3,"XferInfo[%d].state == XFER_PREPARED\n",i);
739 if (part->XferInfo[i].EraseCount <= best) {
740 best = part->XferInfo[i].EraseCount;
745 DEBUG(3,"XferInfo[%d].state == %x\n",i, part->XferInfo[i].state);
748 if (xfer == 0xffff) {
750 DEBUG(1, "ftl_cs: waiting for transfer "
751 "unit to be prepared...\n");
753 part->mtd->sync(part->mtd);
757 printk(KERN_NOTICE "ftl_cs: reclaim failed: no "
758 "suitable transfer units!\n");
760 DEBUG(1, "ftl_cs: reclaim failed: no "
761 "suitable transfer units!\n");
766 } while (xfer == 0xffff);
769 if ((jiffies % shuffle_freq) == 0) {
770 DEBUG(1, "ftl_cs: recycling freshest block...\n");
772 for (i = 0; i < part->DataUnits; i++)
773 if (part->EUNInfo[i].EraseCount <= best) {
774 best = part->EUNInfo[i].EraseCount;
779 for (i = 0; i < part->DataUnits; i++)
780 if (part->EUNInfo[i].Deleted >= best) {
781 best = part->EUNInfo[i].Deleted;
787 printk(KERN_NOTICE "ftl_cs: reclaim failed: "
788 "no free blocks!\n");
790 DEBUG(1,"ftl_cs: reclaim failed: "
791 "no free blocks!\n");
796 ret = copy_erase_unit(part, eun, xfer);
798 erase_xfer(part, xfer);
800 printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed!\n");
802 } /* reclaim_block */
804 /*======================================================================
806 Find_free() searches for a free block. If necessary, it updates
807 the BAM cache for the erase unit containing the free block. It
808 returns the block index -- the erase unit is just the currently
809 cached unit. If there are no free blocks, it returns 0 -- this
810 is never a valid data block because it contains the header.
812 ======================================================================*/
815 static void dump_lists(partition_t *part)
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, "
822 part->EUNInfo[i].Offset >> part->header.EraseUnitSize,
823 part->EUNInfo[i].Free, part->EUNInfo[i].Deleted);
827 static u_int32_t find_free(partition_t *part)
834 /* Find an erase unit with some free space */
835 stop = (part->bam_index == 0xffff) ? 0 : part->bam_index;
838 if (part->EUNInfo[eun].Free != 0) break;
839 /* Wrap around at end of table */
840 if (++eun == part->DataUnits) eun = 0;
841 } while (eun != stop);
843 if (part->EUNInfo[eun].Free == 0)
846 /* Is this unit's BAM cached? */
847 if (eun != part->bam_index) {
848 /* Invalidate cache */
849 part->bam_index = 0xffff;
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));
857 printk(KERN_WARNING"ftl: Error reading BAM in find_free\n");
860 part->bam_index = eun;
863 /* Find a free block */
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) {
872 printk(KERN_NOTICE "ftl_cs: bad free list!\n");
875 DEBUG(2, "ftl_cs: found free block at %d in %d\n", blk, eun);
880 /*======================================================================
882 This gets a memory handle for the region corresponding to the
885 ======================================================================*/
887 static int ftl_open(struct inode *inode, struct file *file)
889 int minor = MINOR(inode->i_rdev);
890 partition_t *partition;
892 if (minor>>4 >= MAX_MTD_DEVICES)
895 partition = myparts[minor>>4];
900 if (partition->state != FTL_FORMATTED)
903 if (ftl_gendisk.part[minor].nr_sects == 0)
908 if (!get_mtd_device(partition->mtd, -1)) {
913 if ((file->f_mode & 2) && !(partition->mtd->flags & MTD_CLEAR_BITS) ) {
914 put_mtd_device(partition->mtd);
919 DEBUG(0, "ftl_cs: ftl_open(%d)\n", minor);
921 atomic_inc(&partition->open);
926 /*====================================================================*/
928 static release_t ftl_close(struct inode *inode, struct file *file)
930 int minor = MINOR(inode->i_rdev);
931 partition_t *part = myparts[minor >> 4];
934 DEBUG(0, "ftl_cs: ftl_close(%d)\n", minor);
936 /* Wait for any pending erase operations to complete */
938 part->mtd->sync(part->mtd);
940 for (i = 0; i < part->header.NumTransferUnits; i++) {
941 if (part->XferInfo[i].state == XFER_ERASED)
942 prepare_xfer(part, i);
945 atomic_dec(&part->open);
947 put_mtd_device(part->mtd);
953 /*======================================================================
955 Read a series of sectors from an FTL partition.
957 ======================================================================*/
959 static int ftl_read(partition_t *part, caddr_t buffer,
960 u_long sector, u_long nblocks)
962 u_int32_t log_addr, bsize;
965 size_t offset, retlen;
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");
973 bsize = 1 << part->header.EraseUnitSize;
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");
980 log_addr = part->VirtualBlockMap[sector+i];
981 if (log_addr == 0xffffffff)
982 memset(buffer, 0, SECTOR_SIZE);
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);
990 printk(KERN_WARNING "Error reading MTD device in ftl_read()\n");
994 buffer += SECTOR_SIZE;
999 /*======================================================================
1001 Write a series of sectors to an FTL partition
1003 ======================================================================*/
1005 static int set_bam_entry(partition_t *part, u_int32_t log_addr,
1006 u_int32_t virt_addr)
1008 u_int32_t bsize, blk, le_virt_addr;
1014 size_t retlen, offset;
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));
1025 ret = part->mtd->read(part->mtd, offset, sizeof(u_int32_t),
1026 &retlen, (u_char *)&old_addr);
1028 printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
1031 old_addr = le32_to_cpu(old_addr);
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))) {
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);
1045 le_virt_addr = cpu_to_le32(virt_addr);
1046 if (part->bam_index == eun) {
1048 if (le32_to_cpu(part->bam_cache[blk]) != old_addr) {
1051 printk(KERN_NOTICE "ftl_cs: set_bam_entry() "
1052 "inconsistency!\n");
1053 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, cache"
1055 le32_to_cpu(part->bam_cache[blk]), old_addr);
1060 part->bam_cache[blk] = le_virt_addr;
1062 ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t),
1063 &retlen, (u_char *)&le_virt_addr);
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);
1071 } /* set_bam_entry */
1073 static int ftl_write(partition_t *part, caddr_t buffer,
1074 u_long sector, u_long nblocks)
1076 u_int32_t bsize, log_addr, virt_addr, old_addr, blk;
1079 size_t retlen, offset;
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");
1087 /* See if we need to reclaim space, before we start */
1088 while (part->FreeTotal < nblocks) {
1089 ret = reclaim_block(part);
1094 bsize = 1 << part->header.EraseUnitSize;
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");
1103 /* Grab a free block */
1104 blk = find_free(part);
1108 printk(KERN_NOTICE "ftl_cs: internal error: "
1109 "no free blocks!\n");
1113 /* Tag the BAM entry, and write the new block */
1114 log_addr = part->bam_index * bsize + blk * SECTOR_SIZE;
1115 part->EUNInfo[part->bam_index].Free--;
1117 if (set_bam_entry(part, log_addr, 0xfffffffe))
1119 part->EUNInfo[part->bam_index].Deleted++;
1120 offset = (part->EUNInfo[part->bam_index].Offset +
1122 ret = part->mtd->write(part->mtd, offset, SECTOR_SIZE, &retlen,
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,
1133 /* Only delete the old entry when the new entry is ready */
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))
1142 /* Finally, set up the new pointers */
1143 if (set_bam_entry(part, log_addr, virt_addr))
1145 part->VirtualBlockMap[sector+i] = log_addr;
1146 part->EUNInfo[part->bam_index].Deleted--;
1148 buffer += SECTOR_SIZE;
1149 virt_addr += SECTOR_SIZE;
1154 /*======================================================================
1156 IOCTL calls for getting device parameters.
1158 ======================================================================*/
1160 static int ftl_ioctl(struct inode *inode, struct file *file,
1161 u_int cmd, u_long arg)
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];
1169 return -ENODEV; /* How? */
1173 ret = verify_area(VERIFY_WRITE, (long *)arg, sizeof(*geo));
1174 if (ret) return ret;
1175 /* Sort of arbitrary: round size down to 4K boundary */
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);
1183 ret = put_user(ftl_hd[minor].nr_sects, (unsigned long *)arg);
1187 ret = put_user((u64)ftl_hd[minor].nr_sects << 9, (u64 *)arg);
1191 ret = ftl_reread_partitions(minor);
1193 #if (LINUX_VERSION_CODE < 0x20303)
1195 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
1196 if (!capable(CAP_SYS_ADMIN)) return -EACCES;
1198 fsync_dev(inode->i_rdev);
1199 invalidate_buffers(inode->i_rdev);
1201 RO_IOCTLS(inode->i_rdev, arg);
1206 ret = blk_ioctl(inode->i_rdev, cmd, arg);
1216 /*======================================================================
1218 Handler for block device requests
1220 ======================================================================*/
1222 static int ftl_reread_partitions(int minor)
1224 partition_t *part = myparts[minor >> 4];
1227 DEBUG(0, "ftl_cs: ftl_reread_partition(%d)\n", minor);
1228 if ((atomic_read(&part->open) > 1)) {
1231 whole = minor & ~(MAX_PART-1);
1235 if (ftl_hd[whole+i].nr_sects > 0) {
1236 kdev_t rdev = MKDEV(FTL_MAJOR, whole+i);
1238 invalidate_device(rdev, 1);
1240 ftl_hd[whole+i].start_sect = 0;
1241 ftl_hd[whole+i].nr_sects = 0;
1246 register_disk(&ftl_gendisk, whole >> PART_BITS, MAX_PART,
1247 &ftl_blk_fops, le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE);
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);
1260 /*======================================================================
1262 Handler for block device requests
1264 ======================================================================*/
1266 static void do_ftl_request(request_arg_t)
1275 minor = MINOR(CURRENT->rq_dev);
1277 part = myparts[minor >> 4];
1281 switch (CURRENT->cmd) {
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);
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);
1297 panic("ftl_cs: unknown block command!\n");
1302 printk("NULL part in ftl_request\n");
1306 CURRENT->sector += CURRENT->current_nr_sectors;
1309 end_request((ret == 0) ? 1 : 0);
1311 } /* do_ftl_request */
1313 /*====================================================================*/
1315 void ftl_freepart(partition_t *part)
1317 if (part->VirtualBlockMap) {
1318 vfree(part->VirtualBlockMap);
1319 part->VirtualBlockMap = NULL;
1321 if (part->VirtualPageMap) {
1322 kfree(part->VirtualPageMap);
1323 part->VirtualPageMap = NULL;
1325 if (part->EUNInfo) {
1326 kfree(part->EUNInfo);
1327 part->EUNInfo = NULL;
1329 if (part->XferInfo) {
1330 kfree(part->XferInfo);
1331 part->XferInfo = NULL;
1333 if (part->bam_cache) {
1334 kfree(part->bam_cache);
1335 part->bam_cache = NULL;
1338 } /* ftl_freepart */
1340 static void ftl_notify_add(struct mtd_info *mtd)
1342 partition_t *partition;
1345 for (device=0; device < MAX_MTD_DEVICES && myparts[device]; device++)
1348 if (device == MAX_MTD_DEVICES) {
1349 printk(KERN_NOTICE "Maximum number of FTL partitions reached\n"
1350 "Not scanning <%s>\n", mtd->name);
1354 partition = kmalloc(sizeof(partition_t), GFP_KERNEL);
1357 printk(KERN_WARNING "No memory to scan for FTL on %s\n",
1362 memset(partition, 0, sizeof(partition_t));
1364 partition->mtd = mtd;
1366 if ((scan_header(partition) == 0) &&
1367 (build_maps(partition) == 0)) {
1369 partition->state = FTL_FORMATTED;
1370 atomic_set(&partition->open, 0);
1371 myparts[device] = partition;
1372 ftl_reread_partitions(device << 4);
1374 printk(KERN_INFO "ftl_cs: opening %d kb FTL partition\n",
1375 le32_to_cpu(partition->header.FormattedSize) >> 10);
1381 static void ftl_notify_remove(struct mtd_info *mtd)
1385 /* Q: What happens if you try to remove a device which has
1386 * a currently-open FTL partition on it?
1388 * A: You don't. The ftl_open routine is responsible for
1389 * increasing the use count of the driver module which
1393 /* That's the theory, anyway :) */
1395 for (i=0; i< MAX_MTD_DEVICES; i++)
1396 if (myparts[i] && myparts[i]->mtd == mtd) {
1398 if (myparts[i]->state == FTL_FORMATTED)
1399 ftl_freepart(myparts[i]);
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;
1415 memset(myparts, 0, sizeof(myparts));
1417 DEBUG(0, "$Id: ftl.c,v 1.45 2003/01/24 23:31:27 dwmw2 Exp $\n");
1419 if (register_blkdev(FTL_MAJOR, "ftl", &ftl_blk_fops)) {
1420 printk(KERN_NOTICE "ftl_cs: unable to grab major "
1421 "device number!\n");
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;
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);
1436 register_mtd_user(&ftl_notifier);
1441 static void __exit cleanup_ftl(void)
1443 unregister_mtd_user(&ftl_notifier);
1445 unregister_blkdev(FTL_MAJOR, "ftl");
1446 blk_cleanup_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR));
1447 blksize_size[FTL_MAJOR] = NULL;
1449 del_gendisk(&ftl_gendisk);
1452 module_init(init_ftl);
1453 module_exit(cleanup_ftl);
1456 MODULE_LICENSE("Dual MPL/GPL");
1457 MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1458 MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");