- pre2
[davej-history.git] / drivers / mtd / ftl.c
blob1cf4a5f160a0283783791d3e617cbd51ce605bfa
1 /* This version ported to the Linux-MTD system by dwmw2@infradead.org
2 * $Id: ftl.c,v 1.20 2000/06/23 15:17:53 dwmw2 Exp $
3 * Based on:
4 */
5 /*======================================================================
7 A Flash Translation Layer memory card driver
9 This driver implements a disk-like block device driver with an
10 apparent block size of 512 bytes for flash memory cards.
12 ftl_cs.c 1.62 2000/02/01 00:59:04
14 The contents of this file are subject to the Mozilla Public
15 License Version 1.1 (the "License"); you may not use this file
16 except in compliance with the License. You may obtain a copy of
17 the License at http://www.mozilla.org/MPL/
19 Software distributed under the License is distributed on an "AS
20 IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
21 implied. See the License for the specific language governing
22 rights and limitations under the License.
24 The initial developer of the original code is David A. Hinds
25 <dhinds@pcmcia.sourceforge.org>. Portions created by David A. Hinds
26 are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
28 Alternatively, the contents of this file may be used under the
29 terms of the GNU Public License version 2 (the "GPL"), in which
30 case the provisions of the GPL are applicable instead of the
31 above. If you wish to allow the use of your version of this file
32 only under the terms of the GPL and not to allow others to use
33 your version of this file under the MPL, indicate your decision
34 by deleting the provisions above and replace them with the notice
35 and other provisions required by the GPL. If you do not delete
36 the provisions above, a recipient may use your version of this
37 file under either the MPL or the GPL.
39 LEGAL NOTE: The FTL format is patented by M-Systems. They have
40 granted a license for its use with PCMCIA devices:
42 "M-Systems grants a royalty-free, non-exclusive license under
43 any presently existing M-Systems intellectual property rights
44 necessary for the design and development of FTL-compatible
45 drivers, file systems and utilities using the data formats with
46 PCMCIA PC Cards as described in the PCMCIA Flash Translation
47 Layer (FTL) Specification."
49 Use of the FTL format for non-PCMCIA applications may be an
50 infringement of these patents. For additional information,
51 contact M-Systems (http://www.m-sys.com) directly.
53 ======================================================================*/
54 #define FTL_DEBUG 5
55 #ifdef FTL_DEBUG
56 #define DEBUGLVL debug
57 #endif
59 #include <linux/module.h>
60 #include <linux/mtd/compatmac.h>
61 #include <linux/mtd/mtd.h>
62 /*#define PSYCHO_DEBUG */
64 #include <linux/kernel.h>
65 #include <linux/sched.h>
66 #include <linux/ptrace.h>
67 #include <linux/malloc.h>
68 #include <linux/string.h>
69 #include <linux/timer.h>
70 #include <linux/major.h>
71 #include <linux/fs.h>
72 #include <linux/ioctl.h>
73 #include <linux/hdreg.h>
74 #include <stdarg.h>
76 #if (LINUX_VERSION_CODE >= 0x20100)
77 #include <linux/vmalloc.h>
78 #endif
79 #if (LINUX_VERSION_CODE >= 0x20303)
80 #include <linux/blkpg.h>
81 #endif
83 #include <linux/mtd/ftl.h>
84 /*====================================================================*/
85 /* Stuff which really ought to be in compatmac.h */
87 #if (LINUX_VERSION_CODE < 0x20328)
88 #define register_disk(dev, drive, minors, ops, size) \
89 do { (dev)->part[(drive)*(minors)].nr_sects = size; \
90 if (size == 0) (dev)->part[(drive)*(minors)].start_sect = -1; \
91 resetup_one_dev(dev, drive); } while (0);
92 #endif
94 #if (LINUX_VERSION_CODE < 0x20320)
95 #define BLK_DEFAULT_QUEUE(n) blk_dev[n].request_fn
96 #define blk_init_queue(q, req) q = (req)
97 #define blk_cleanup_queue(q) q = NULL
98 #define request_arg_t void
99 #else
100 #define request_arg_t request_queue_t *q
101 #endif
104 /*====================================================================*/
106 /* Parameters that can be set with 'insmod' */
108 /* Major device # for FTL device */
109 static int shuffle_freq = 50;
111 MODULE_PARM(shuffle_freq, "i");
113 /*====================================================================*/
115 #ifndef FTL_MAJOR
116 #define FTL_MAJOR 44
117 #endif
120 /* Funky stuff for setting up a block device */
121 #define MAJOR_NR FTL_MAJOR
122 #define DEVICE_NAME "ftl"
123 #define DEVICE_REQUEST do_ftl_request
124 #define DEVICE_ON(device)
125 #define DEVICE_OFF(device)
127 #define DEVICE_NR(minor) ((minor)>>5)
128 #define REGION_NR(minor) (((minor)>>3)&3)
129 #define PART_NR(minor) ((minor)&7)
130 #define MINOR_NR(dev,reg,part) (((dev)<<5)+((reg)<<3)+(part))
132 #include <linux/blk.h>
134 #ifdef FTL_DEBUG
135 static int debug = FTL_DEBUG;
136 MODULE_PARM(debug, "i");
137 #endif
139 /*====================================================================*/
141 #ifndef FTL_MAJOR
142 #define FTL_MAJOR 44
143 #endif
145 /* Maximum number of separate memory devices we'll allow */
146 #define MAX_DEV 4
148 /* Maximum number of regions per device */
149 #define MAX_REGION 4
151 /* Maximum number of partitions in an FTL region */
152 #define PART_BITS 3
153 #define MAX_PART 8
155 /* Maximum number of outstanding erase requests per socket */
156 #define MAX_ERASE 8
158 /* Sector size -- shouldn't need to change */
159 #define SECTOR_SIZE 512
162 /* Each memory region corresponds to a minor device */
163 typedef struct partition_t {
164 struct mtd_info *mtd;
165 u_int32_t state;
166 u_int32_t *VirtualBlockMap;
167 u_int32_t *VirtualPageMap;
168 u_int32_t FreeTotal;
169 struct eun_info_t {
170 u_int32_t Offset;
171 u_int32_t EraseCount;
172 u_int32_t Free;
173 u_int32_t Deleted;
174 } *EUNInfo;
175 struct xfer_info_t {
176 u_int32_t Offset;
177 u_int32_t EraseCount;
178 u_int16_t state;
179 } *XferInfo;
180 u_int16_t bam_index;
181 u_int32_t *bam_cache;
182 u_int16_t DataUnits;
183 u_int32_t BlocksPerUnit;
184 erase_unit_header_t header;
185 #if 0
186 region_info_t region;
187 memory_handle_t handle;
188 #endif
189 atomic_t open;
190 } partition_t;
192 partition_t *myparts[MAX_MTD_DEVICES];
194 static void ftl_notify_add(struct mtd_info *mtd);
195 static void ftl_notify_remove(struct mtd_info *mtd);
197 void ftl_freepart(partition_t *part);
199 static struct mtd_notifier ftl_notifier={ftl_notify_add, ftl_notify_remove, NULL};
201 /* Partition state flags */
202 #define FTL_FORMATTED 0x01
204 /* Transfer unit states */
205 #define XFER_UNKNOWN 0x00
206 #define XFER_ERASING 0x01
207 #define XFER_ERASED 0x02
208 #define XFER_PREPARED 0x03
209 #define XFER_FAILED 0x04
211 static struct hd_struct ftl_hd[MINOR_NR(MAX_DEV, 0, 0)];
212 static int ftl_sizes[MINOR_NR(MAX_DEV, 0, 0)];
213 static int ftl_blocksizes[MINOR_NR(MAX_DEV, 0, 0)];
215 static struct gendisk ftl_gendisk = {
216 major: FTL_MAJOR,
217 major_name: "ftl",
218 minor_shift: PART_BITS,
219 max_p: MAX_PART,
220 #if (LINUX_VERSION_CODE < 0x20328)
221 max_nr: MAX_DEV*MAX_PART,
222 #endif
223 part: ftl_hd,
224 sizes: ftl_sizes,
225 nr_real: 0
228 /*====================================================================*/
230 static int ftl_ioctl(struct inode *inode, struct file *file,
231 u_int cmd, u_long arg);
232 static int ftl_open(struct inode *inode, struct file *file);
233 static release_t ftl_close(struct inode *inode, struct file *file);
234 static int ftl_reread_partitions(int minor);
236 static void ftl_erase_callback(struct erase_info *done);
238 #if LINUX_VERSION_CODE < 0x20326
239 static struct file_operations ftl_blk_fops = {
240 open: ftl_open,
241 release: ftl_close,
242 ioctl: ftl_ioctl,
243 read: block_read,
244 write: block_write,
245 fsync: block_fsync
247 #else
248 static struct block_device_operations ftl_blk_fops = {
249 open: ftl_open,
250 release: ftl_close,
251 ioctl: ftl_ioctl,
253 #endif
255 /*======================================================================
257 Scan_header() checks to see if a memory region contains an FTL
258 partition. build_maps() reads all the erase unit headers, builds
259 the erase unit map, and then builds the virtual page map.
261 ======================================================================*/
263 static int scan_header(partition_t *part)
265 erase_unit_header_t header;
266 loff_t offset;
267 int ret;
268 part->header.FormattedSize = 0;
269 /* Search first megabyte for a valid FTL header */
270 for (offset = 0;
271 offset < 0x100000;
272 offset += part->mtd->erasesize?part->mtd->erasesize:0x2000) {
274 ret = part->mtd->read(part->mtd, offset, sizeof(header), &ret,
275 (unsigned char *)&header);
277 if (ret)
278 return ret;
280 if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break;
283 if (offset == 0x100000) {
284 printk(KERN_NOTICE "ftl_cs: FTL header not found.\n");
285 return -ENOENT;
287 if ((le16_to_cpu(header.NumEraseUnits) > 65536) || header.BlockSize != 9 ||
288 (header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) ||
289 (header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) {
290 printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n");
291 return -1;
293 if ((1 << header.EraseUnitSize) != part->mtd->erasesize) {
294 printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %lx\n",
295 1 << header.EraseUnitSize,part->mtd->erasesize);
296 return -1;
298 part->header = header;
299 return 0;
302 static int build_maps(partition_t *part)
304 erase_unit_header_t header;
305 u_int16_t xvalid, xtrans, i;
306 u_int blocks, j;
307 int hdr_ok, ret;
308 ssize_t retval;
309 loff_t offset;
311 /* Set up erase unit maps */
312 part->DataUnits = le16_to_cpu(part->header.NumEraseUnits) -
313 part->header.NumTransferUnits;
314 part->EUNInfo = kmalloc(part->DataUnits * sizeof(struct eun_info_t),
315 GFP_KERNEL);
316 if (!part->EUNInfo) return -1;
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) return -1;
324 xvalid = xtrans = 0;
325 for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) {
326 offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN))
327 << part->header.EraseUnitSize);
328 ret = part->mtd->read(part->mtd, offset, sizeof(header), &retval,
329 (unsigned char *)&header);
331 if (ret)
332 return ret;
334 /* Is this a transfer partition? */
335 hdr_ok = (strcmp(header.DataOrgTuple+3, "FTL100") == 0);
336 if (hdr_ok && (le16_to_cpu(header.LogicalEUN) < part->DataUnits) &&
337 (part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset == 0xffffffff)) {
338 part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset = offset;
339 part->EUNInfo[le16_to_cpu(header.LogicalEUN)].EraseCount =
340 le32_to_cpu(header.EraseCount);
341 xvalid++;
342 } else {
343 if (xtrans == part->header.NumTransferUnits) {
344 printk(KERN_NOTICE "ftl_cs: format error: too many "
345 "transfer units!\n");
346 return -1;
348 if (hdr_ok && (le16_to_cpu(header.LogicalEUN) == 0xffff)) {
349 part->XferInfo[xtrans].state = XFER_PREPARED;
350 part->XferInfo[xtrans].EraseCount = le32_to_cpu(header.EraseCount);
351 } else {
352 part->XferInfo[xtrans].state = XFER_UNKNOWN;
353 /* Pick anything reasonable for the erase count */
354 part->XferInfo[xtrans].EraseCount =
355 le32_to_cpu(part->header.EraseCount);
357 part->XferInfo[xtrans].Offset = offset;
358 xtrans++;
361 /* Check for format trouble */
362 header = part->header;
363 if ((xtrans != header.NumTransferUnits) ||
364 (xvalid+xtrans != le16_to_cpu(header.NumEraseUnits))) {
365 printk(KERN_NOTICE "ftl_cs: format error: erase units "
366 "don't add up!\n");
367 return -1;
370 /* Set up virtual page map */
371 blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize;
372 part->VirtualBlockMap = vmalloc(blocks * sizeof(u_int32_t));
373 memset(part->VirtualBlockMap, 0xff, blocks * sizeof(u_int32_t));
374 part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize;
376 part->bam_cache = kmalloc(part->BlocksPerUnit * sizeof(u_int32_t),
377 GFP_KERNEL);
378 if (!part->bam_cache) return -1;
380 part->bam_index = 0xffff;
381 part->FreeTotal = 0;
383 for (i = 0; i < part->DataUnits; i++) {
384 part->EUNInfo[i].Free = 0;
385 part->EUNInfo[i].Deleted = 0;
386 offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset);
388 ret = part->mtd->read(part->mtd, offset,
389 part->BlocksPerUnit * sizeof(u_int32_t), &retval,
390 (unsigned char *)part->bam_cache);
392 if (ret)
393 return ret;
395 for (j = 0; j < part->BlocksPerUnit; j++) {
396 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) {
397 part->EUNInfo[i].Free++;
398 part->FreeTotal++;
399 } else if ((BLOCK_TYPE(le32_to_cpu(part->bam_cache[j])) == BLOCK_DATA) &&
400 (BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j])) < blocks))
401 part->VirtualBlockMap[BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j]))] =
402 (i << header.EraseUnitSize) + (j << header.BlockSize);
403 else if (BLOCK_DELETED(le32_to_cpu(part->bam_cache[j])))
404 part->EUNInfo[i].Deleted++;
408 return 0;
410 } /* build_maps */
412 /*======================================================================
414 Erase_xfer() schedules an asynchronous erase operation for a
415 transfer unit.
417 ======================================================================*/
419 static int erase_xfer(partition_t *part,
420 u_int16_t xfernum)
422 int ret;
423 struct xfer_info_t *xfer;
424 struct erase_info *erase;
426 xfer = &part->XferInfo[xfernum];
427 DEBUG(1, "ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset);
428 xfer->state = XFER_ERASING;
430 /* Is there a free erase slot? Always in MTD. */
433 erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL);
434 if (!erase)
435 return -ENOMEM;
437 erase->callback = ftl_erase_callback;
438 erase->addr = xfer->Offset;
439 erase->len = 1 << part->header.EraseUnitSize;
440 erase->priv = (u_long)part;
442 ret = part->mtd->erase(part->mtd, erase);
444 if (!ret)
445 xfer->EraseCount++;
446 else
447 kfree(erase);
449 return ret;
450 } /* erase_xfer */
452 /*======================================================================
454 Prepare_xfer() takes a freshly erased transfer unit and gives
455 it an appropriate header.
457 ======================================================================*/
459 static void ftl_erase_callback(struct erase_info *erase)
461 partition_t *part;
462 struct xfer_info_t *xfer;
463 int i;
465 /* Look up the transfer unit */
466 part = (partition_t *)(erase->priv);
468 for (i = 0; i < part->header.NumTransferUnits; i++)
469 if (part->XferInfo[i].Offset == erase->addr) break;
471 if (i == part->header.NumTransferUnits) {
472 printk(KERN_NOTICE "ftl_cs: internal error: "
473 "erase lookup failed!\n");
474 return;
477 xfer = &part->XferInfo[i];
478 if (erase->state == MTD_ERASE_DONE)
479 xfer->state = XFER_ERASED;
480 else {
481 xfer->state = XFER_FAILED;
482 printk(KERN_NOTICE "ftl_cs: erase failed: state = %d\n",
483 erase->state);
486 kfree(erase);
488 } /* ftl_erase_callback */
490 static int prepare_xfer(partition_t *part, int i)
492 erase_unit_header_t header;
493 struct xfer_info_t *xfer;
494 int nbam, ret;
495 u_int32_t ctl;
496 ssize_t retlen;
497 loff_t offset;
499 xfer = &part->XferInfo[i];
500 xfer->state = XFER_FAILED;
502 DEBUG(1, "ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset);
504 /* Write the transfer unit header */
505 header = part->header;
506 header.LogicalEUN = cpu_to_le16(0xffff);
507 header.EraseCount = cpu_to_le32(xfer->EraseCount);
509 ret = part->mtd->write(part->mtd, xfer->Offset, sizeof(header),
510 &retlen, (u_char *)&header);
512 if (ret) {
513 return ret;
516 /* Write the BAM stub */
517 nbam = (part->BlocksPerUnit * sizeof(u_int32_t) +
518 le32_to_cpu(part->header.BAMOffset) + SECTOR_SIZE - 1) / SECTOR_SIZE;
520 offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
521 ctl = cpu_to_le32(BLOCK_CONTROL);
523 for (i = 0; i < nbam; i++, offset += sizeof(u_int32_t)) {
525 ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t),
526 &retlen, (u_char *)&ctl);
528 if (ret)
529 return ret;
531 xfer->state = XFER_PREPARED;
532 return 0;
534 } /* prepare_xfer */
536 /*======================================================================
538 Copy_erase_unit() takes a full erase block and a transfer unit,
539 copies everything to the transfer unit, then swaps the block
540 pointers.
542 All data blocks are copied to the corresponding blocks in the
543 target unit, so the virtual block map does not need to be
544 updated.
546 ======================================================================*/
548 static int copy_erase_unit(partition_t *part, u_int16_t srcunit,
549 u_int16_t xferunit)
551 u_char buf[SECTOR_SIZE];
552 struct eun_info_t *eun;
553 struct xfer_info_t *xfer;
554 u_int32_t src, dest, free, i;
555 u_int16_t unit;
556 int ret;
557 ssize_t retlen;
558 loff_t offset;
559 u_int16_t srcunitswap = cpu_to_le16(srcunit);
561 eun = &part->EUNInfo[srcunit];
562 xfer = &part->XferInfo[xferunit];
563 DEBUG(2, "ftl_cs: copying block 0x%x to 0x%x\n",
564 eun->Offset, xfer->Offset);
567 /* Read current BAM */
568 if (part->bam_index != srcunit) {
570 offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
572 ret = part->mtd->read(part->mtd, offset,
573 part->BlocksPerUnit * sizeof(u_int32_t),
574 &retlen, (u_char *) (part->bam_cache));
576 /* mark the cache bad, in case we get an error later */
577 part->bam_index = 0xffff;
579 if (ret) {
580 printk( KERN_WARNING "ftl: Failed to read BAM cache in copy_erase_unit()!\n");
581 return ret;
585 /* Write the LogicalEUN for the transfer unit */
586 xfer->state = XFER_UNKNOWN;
587 offset = xfer->Offset + 20; /* Bad! */
588 unit = cpu_to_le16(0x7fff);
590 ret = part->mtd->write(part->mtd, offset, sizeof(u_int16_t),
591 &retlen, (u_char *) &unit);
593 if (ret) {
594 printk( KERN_WARNING "ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
595 return ret;
598 /* Copy all data blocks from source unit to transfer unit */
599 src = eun->Offset; dest = xfer->Offset;
601 free = 0;
602 ret = 0;
603 for (i = 0; i < part->BlocksPerUnit; i++) {
604 switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) {
605 case BLOCK_CONTROL:
606 /* This gets updated later */
607 break;
608 case BLOCK_DATA:
609 case BLOCK_REPLACEMENT:
610 ret = part->mtd->read(part->mtd, src, SECTOR_SIZE,
611 &retlen, (u_char *) buf);
612 if (ret) {
613 printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n");
614 return ret;
618 ret = part->mtd->write(part->mtd, dest, SECTOR_SIZE,
619 &retlen, (u_char *) buf);
620 if (ret) {
621 printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n");
622 return ret;
625 break;
626 default:
627 /* All other blocks must be free */
628 part->bam_cache[i] = cpu_to_le32(0xffffffff);
629 free++;
630 break;
632 src += SECTOR_SIZE;
633 dest += SECTOR_SIZE;
636 /* Write the BAM to the transfer unit */
637 ret = part->mtd->write(part->mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset),
638 part->BlocksPerUnit * sizeof(int32_t), &retlen,
639 (u_char *)part->bam_cache);
640 if (ret) {
641 printk( KERN_WARNING "ftl: Error writing BAM in copy_erase_unit\n");
642 return ret;
646 /* All clear? Then update the LogicalEUN again */
647 ret = part->mtd->write(part->mtd, xfer->Offset + 20, sizeof(u_int16_t),
648 &retlen, (u_char *)&srcunitswap);
650 if (ret) {
651 printk(KERN_WARNING "ftl: Error writing new LogicalEUN in copy_erase_unit\n");
652 return ret;
656 /* Update the maps and usage stats*/
657 i = xfer->EraseCount;
658 xfer->EraseCount = eun->EraseCount;
659 eun->EraseCount = i;
660 i = xfer->Offset;
661 xfer->Offset = eun->Offset;
662 eun->Offset = i;
663 part->FreeTotal -= eun->Free;
664 part->FreeTotal += free;
665 eun->Free = free;
666 eun->Deleted = 0;
668 /* Now, the cache should be valid for the new block */
669 part->bam_index = srcunit;
671 return 0;
672 } /* copy_erase_unit */
674 /*======================================================================
676 reclaim_block() picks a full erase unit and a transfer unit and
677 then calls copy_erase_unit() to copy one to the other. Then, it
678 schedules an erase on the expired block.
680 What's a good way to decide which transfer unit and which erase
681 unit to use? Beats me. My way is to always pick the transfer
682 unit with the fewest erases, and usually pick the data unit with
683 the most deleted blocks. But with a small probability, pick the
684 oldest data unit instead. This means that we generally postpone
685 the next reclaimation as long as possible, but shuffle static
686 stuff around a bit for wear leveling.
688 ======================================================================*/
690 static int reclaim_block(partition_t *part)
692 u_int16_t i, eun, xfer;
693 u_int32_t best;
694 int queued, ret;
696 DEBUG(0, "ftl_cs: reclaiming space...\n");
697 DEBUG(4, "NumTransferUnits == %x\n", part->header.NumTransferUnits);
698 /* Pick the least erased transfer unit */
699 best = 0xffffffff; xfer = 0xffff;
700 do {
701 queued = 0;
702 for (i = 0; i < part->header.NumTransferUnits; i++) {
703 int n=0;
704 if (part->XferInfo[i].state == XFER_UNKNOWN) {
705 DEBUG(4,"XferInfo[%d].state == XFER_UNKNOWN\n",i);
706 n=1;
707 erase_xfer(part, i);
709 if (part->XferInfo[i].state == XFER_ERASING) {
710 DEBUG(4,"XferInfo[%d].state == XFER_ERASING\n",i);
711 n=1;
712 queued = 1;
714 else if (part->XferInfo[i].state == XFER_ERASED) {
715 DEBUG(4,"XferInfo[%d].state == XFER_ERASED\n",i);
716 n=1;
717 prepare_xfer(part, i);
719 if (part->XferInfo[i].state == XFER_PREPARED) {
720 DEBUG(4,"XferInfo[%d].state == XFER_PREPARED\n",i);
721 n=1;
722 if (part->XferInfo[i].EraseCount <= best) {
723 best = part->XferInfo[i].EraseCount;
724 xfer = i;
727 if (!n)
728 DEBUG(4,"XferInfo[%d].state == %x\n",i, part->XferInfo[i].state);
731 if (xfer == 0xffff) {
732 if (queued) {
733 DEBUG(1, "ftl_cs: waiting for transfer "
734 "unit to be prepared...\n");
735 if (part->mtd->sync)
736 part->mtd->sync(part->mtd);
737 } else {
738 static int ne = 0;
739 if (++ne < 5)
740 printk(KERN_NOTICE "ftl_cs: reclaim failed: no "
741 "suitable transfer units!\n");
742 else
743 DEBUG(1, "ftl_cs: reclaim failed: no "
744 "suitable transfer units!\n");
746 return -EIO;
749 } while (xfer == 0xffff);
751 eun = 0;
752 if ((jiffies % shuffle_freq) == 0) {
753 DEBUG(1, "ftl_cs: recycling freshest block...\n");
754 best = 0xffffffff;
755 for (i = 0; i < part->DataUnits; i++)
756 if (part->EUNInfo[i].EraseCount <= best) {
757 best = part->EUNInfo[i].EraseCount;
758 eun = i;
760 } else {
761 best = 0;
762 for (i = 0; i < part->DataUnits; i++)
763 if (part->EUNInfo[i].Deleted >= best) {
764 best = part->EUNInfo[i].Deleted;
765 eun = i;
767 if (best == 0) {
768 static int ne = 0;
769 if (++ne < 5)
770 printk(KERN_NOTICE "ftl_cs: reclaim failed: "
771 "no free blocks!\n");
772 else
773 DEBUG(1,"ftl_cs: reclaim failed: "
774 "no free blocks!\n");
776 return -EIO;
779 ret = copy_erase_unit(part, eun, xfer);
780 if (!ret)
781 erase_xfer(part, xfer);
782 else
783 printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed!\n");
784 return ret;
785 } /* reclaim_block */
787 /*======================================================================
789 Find_free() searches for a free block. If necessary, it updates
790 the BAM cache for the erase unit containing the free block. It
791 returns the block index -- the erase unit is just the currently
792 cached unit. If there are no free blocks, it returns 0 -- this
793 is never a valid data block because it contains the header.
795 ======================================================================*/
797 #ifdef PSYCHO_DEBUG
798 static void dump_lists(partition_t *part)
800 int i;
801 printk(KERN_DEBUG "ftl_cs: Free total = %d\n", part->FreeTotal);
802 for (i = 0; i < part->DataUnits; i++)
803 printk(KERN_DEBUG "ftl_cs: unit %d: %d phys, %d free, "
804 "%d deleted\n", i,
805 part->EUNInfo[i].Offset >> part->header.EraseUnitSize,
806 part->EUNInfo[i].Free, part->EUNInfo[i].Deleted);
808 #endif
810 static u_int32_t find_free(partition_t *part)
812 u_int16_t stop, eun;
813 u_int32_t blk;
814 size_t retlen;
815 int ret;
817 /* Find an erase unit with some free space */
818 stop = (part->bam_index == 0xffff) ? 0 : part->bam_index;
819 eun = stop;
820 do {
821 if (part->EUNInfo[eun].Free != 0) break;
822 /* Wrap around at end of table */
823 if (++eun == part->DataUnits) eun = 0;
824 } while (eun != stop);
826 if (part->EUNInfo[eun].Free == 0)
827 return 0;
829 /* Is this unit's BAM cached? */
830 if (eun != part->bam_index) {
831 /* Invalidate cache */
832 part->bam_index = 0xffff;
834 ret = part->mtd->read(part->mtd,
835 part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
836 part->BlocksPerUnit * sizeof(u_int32_t),
837 &retlen, (u_char *) (part->bam_cache));
839 if (ret) {
840 printk(KERN_WARNING"ftl: Error reading BAM in find_free\n");
841 return 0;
843 part->bam_index = eun;
846 /* Find a free block */
847 for (blk = 0; blk < part->BlocksPerUnit; blk++)
848 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break;
849 if (blk == part->BlocksPerUnit) {
850 #ifdef PSYCHO_DEBUG
851 static int ne = 0;
852 if (++ne == 1)
853 dump_lists(part);
854 #endif
855 printk(KERN_NOTICE "ftl_cs: bad free list!\n");
856 return 0;
858 DEBUG(2, "ftl_cs: found free block at %d in %d\n", blk, eun);
859 return blk;
861 } /* find_free */
863 /*======================================================================
865 This gets a memory handle for the region corresponding to the
866 minor device number.
868 ======================================================================*/
870 static int ftl_open(struct inode *inode, struct file *file)
872 int minor = MINOR(inode->i_rdev);
873 partition_t *partition;
875 if (minor>>4 >= MAX_MTD_DEVICES)
876 return -ENODEV;
878 partition = myparts[minor>>4];
880 if (!partition)
881 return -ENODEV;
883 if (partition->state != FTL_FORMATTED)
884 return -ENXIO;
886 if (ftl_gendisk.part[minor].nr_sects == 0)
887 return -ENXIO;
889 MOD_INC_USE_COUNT;
891 if (!get_mtd_device(partition->mtd, -1)) {
892 MOD_DEC_USE_COUNT;
893 return /* -E'SBUGGEREDOFF */ -ENXIO;
896 if ((file->f_mode & 2) && !(partition->mtd->flags & MTD_CLEAR_BITS) ) {
897 put_mtd_device(partition->mtd);
898 MOD_DEC_USE_COUNT;
899 return -EROFS;
902 DEBUG(0, "ftl_cs: ftl_open(%d)\n", minor);
904 atomic_inc(&partition->open);
906 return 0;
909 /*====================================================================*/
911 static release_t ftl_close(struct inode *inode, struct file *file)
913 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
914 struct super_block *sb = get_super(inode->i_rdev);
915 #endif
916 int minor = MINOR(inode->i_rdev);
917 partition_t *part = myparts[minor >> 4];
918 int i;
920 DEBUG(0, "ftl_cs: ftl_close(%d)\n", minor);
922 /* Flush all writes */
923 fsync_dev(inode->i_rdev);
924 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
925 if (sb) invalidate_inodes(sb);
926 #endif
927 invalidate_buffers(inode->i_rdev);
929 /* Wait for any pending erase operations to complete */
930 if (part->mtd->sync)
931 part->mtd->sync(part->mtd);
933 for (i = 0; i < part->header.NumTransferUnits; i++) {
934 if (part->XferInfo[i].state == XFER_ERASED)
935 prepare_xfer(part, i);
938 atomic_dec(&part->open);
940 put_mtd_device(part->mtd);
941 MOD_DEC_USE_COUNT;
942 release_return(0);
943 } /* ftl_close */
946 /*======================================================================
948 Read a series of sectors from an FTL partition.
950 ======================================================================*/
952 static int ftl_read(partition_t *part, caddr_t buffer,
953 u_long sector, u_long nblocks)
955 u_int32_t log_addr, bsize;
956 u_long i;
957 int ret;
958 size_t offset, retlen;
960 DEBUG(2, "ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
961 part, sector, nblocks);
962 if (!(part->state & FTL_FORMATTED)) {
963 printk(KERN_NOTICE "ftl_cs: bad partition\n");
964 return -EIO;
966 bsize = 1 << part->header.EraseUnitSize;
968 for (i = 0; i < nblocks; i++) {
969 if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) {
970 printk(KERN_NOTICE "ftl_cs: bad read offset\n");
971 return -EIO;
973 log_addr = part->VirtualBlockMap[sector+i];
974 if (log_addr == 0xffffffff)
975 memset(buffer, 0, SECTOR_SIZE);
976 else {
977 offset = (part->EUNInfo[log_addr / bsize].Offset
978 + (log_addr % bsize));
979 ret = part->mtd->read(part->mtd, offset, SECTOR_SIZE,
980 &retlen, (u_char *) buffer);
982 if (ret) {
983 printk(KERN_WARNING "Error reading MTD device in ftl_read()\n");
984 return ret;
987 buffer += SECTOR_SIZE;
989 return 0;
990 } /* ftl_read */
992 /*======================================================================
994 Write a series of sectors to an FTL partition
996 ======================================================================*/
998 static int set_bam_entry(partition_t *part, u_int32_t log_addr,
999 u_int32_t virt_addr)
1001 u_int32_t bsize, blk;
1002 #ifdef PSYCHO_DEBUG
1003 u_int32_t old_addr;
1004 #endif
1005 u_int16_t eun;
1006 int ret;
1007 size_t retlen, offset;
1009 DEBUG(2, "ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
1010 part, log_addr, virt_addr);
1011 bsize = 1 << part->header.EraseUnitSize;
1012 eun = log_addr / bsize;
1013 blk = (log_addr % bsize) / SECTOR_SIZE;
1014 offset = (part->EUNInfo[eun].Offset + blk * sizeof(u_int32_t) +
1015 le32_to_cpu(part->header.BAMOffset));
1017 #ifdef PSYCHO_DEBUG
1018 ret = part->mtd->read(part->mtd, offset, sizeof(u_int32_t),
1019 &retlen, (u_char *)&old_addr);
1020 if (ret) {
1021 printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
1022 return ret;
1024 old_addr = le32_to_cpu(old_addr);
1026 if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) ||
1027 ((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) ||
1028 (!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) {
1029 static int ne = 0;
1030 if (++ne < 5) {
1031 printk(KERN_NOTICE "ftl_cs: set_bam_entry() inconsistency!\n");
1032 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, old = 0x%x"
1033 ", new = 0x%x\n", log_addr, old_addr, virt_addr);
1035 return -EIO;
1037 #endif
1038 if (part->bam_index == eun) {
1039 #ifdef PSYCHO_DEBUG
1040 if (le32_to_cpu(part->bam_cache[blk]) != old_addr) {
1041 static int ne = 0;
1042 if (++ne < 5) {
1043 printk(KERN_NOTICE "ftl_cs: set_bam_entry() "
1044 "inconsistency!\n");
1045 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, cache"
1046 " = 0x%x\n",
1047 le32_to_cpu(part->bam_cache[blk]), old_addr);
1049 return -EIO;
1051 #endif
1052 part->bam_cache[blk] = cpu_to_le32(virt_addr);
1054 ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t),
1055 &retlen, (u_char *)&part->bam_cache[blk]);
1057 if (ret) {
1058 printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!\n");
1059 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, new = 0x%x\n",
1060 log_addr, virt_addr);
1062 return ret;
1063 } /* set_bam_entry */
1065 static int ftl_write(partition_t *part, caddr_t buffer,
1066 u_long sector, u_long nblocks)
1068 u_int32_t bsize, log_addr, virt_addr, old_addr, blk;
1069 u_long i;
1070 int ret;
1071 size_t retlen, offset;
1073 DEBUG(2, "ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
1074 part, sector, nblocks);
1075 if (!(part->state & FTL_FORMATTED)) {
1076 printk(KERN_NOTICE "ftl_cs: bad partition\n");
1077 return -EIO;
1079 /* See if we need to reclaim space, before we start */
1080 while (part->FreeTotal < nblocks) {
1081 ret = reclaim_block(part);
1082 if (ret)
1083 return ret;
1086 bsize = 1 << part->header.EraseUnitSize;
1088 virt_addr = sector * SECTOR_SIZE | BLOCK_DATA;
1089 for (i = 0; i < nblocks; i++) {
1090 if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) {
1091 printk(KERN_NOTICE "ftl_cs: bad write offset\n");
1092 return -EIO;
1095 /* Grab a free block */
1096 blk = find_free(part);
1097 if (blk == 0) {
1098 static int ne = 0;
1099 if (++ne < 5)
1100 printk(KERN_NOTICE "ftl_cs: internal error: "
1101 "no free blocks!\n");
1102 return -ENOSPC;
1105 /* Tag the BAM entry, and write the new block */
1106 log_addr = part->bam_index * bsize + blk * SECTOR_SIZE;
1107 part->EUNInfo[part->bam_index].Free--;
1108 part->FreeTotal--;
1109 if (set_bam_entry(part, log_addr, 0xfffffffe))
1110 return -EIO;
1111 part->EUNInfo[part->bam_index].Deleted++;
1112 offset = (part->EUNInfo[part->bam_index].Offset +
1113 blk * SECTOR_SIZE);
1114 ret = part->mtd->write(part->mtd, offset, SECTOR_SIZE, &retlen,
1115 buffer);
1117 if (ret) {
1118 printk(KERN_NOTICE "ftl_cs: block write failed!\n");
1119 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, virt_addr"
1120 " = 0x%x, Offset = 0x%x\n", log_addr, virt_addr,
1121 offset);
1122 return -EIO;
1125 /* Only delete the old entry when the new entry is ready */
1126 old_addr = part->VirtualBlockMap[sector+i];
1127 if (old_addr != 0xffffffff) {
1128 part->VirtualBlockMap[sector+i] = 0xffffffff;
1129 part->EUNInfo[old_addr/bsize].Deleted++;
1130 if (set_bam_entry(part, old_addr, 0))
1131 return -EIO;
1134 /* Finally, set up the new pointers */
1135 if (set_bam_entry(part, log_addr, virt_addr))
1136 return -EIO;
1137 part->VirtualBlockMap[sector+i] = log_addr;
1138 part->EUNInfo[part->bam_index].Deleted--;
1140 buffer += SECTOR_SIZE;
1141 virt_addr += SECTOR_SIZE;
1143 return 0;
1144 } /* ftl_write */
1146 /*======================================================================
1148 IOCTL calls for getting device parameters.
1150 ======================================================================*/
1152 static int ftl_ioctl(struct inode *inode, struct file *file,
1153 u_int cmd, u_long arg)
1155 struct hd_geometry *geo = (struct hd_geometry *)arg;
1156 int ret = 0, minor = MINOR(inode->i_rdev);
1157 partition_t *part= myparts[minor >> 4];
1158 u_long sect;
1160 if (!part)
1161 return -ENODEV; /* How? */
1163 switch (cmd) {
1164 case HDIO_GETGEO:
1165 ret = verify_area(VERIFY_WRITE, (long *)arg, sizeof(*geo));
1166 if (ret) return ret;
1167 /* Sort of arbitrary: round size down to 4K boundary */
1168 sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
1169 put_user(1, (char *)&geo->heads);
1170 put_user(8, (char *)&geo->sectors);
1171 put_user((sect>>3), (short *)&geo->cylinders);
1172 put_user(ftl_hd[minor].start_sect, (u_long *)&geo->start);
1173 break;
1174 case BLKGETSIZE:
1175 ret = verify_area(VERIFY_WRITE, (long *)arg, sizeof(long));
1176 if (ret) return ret;
1177 put_user(ftl_hd[minor].nr_sects,
1178 (long *)arg);
1179 break;
1180 case BLKRRPART:
1181 ret = ftl_reread_partitions(minor);
1182 break;
1183 #if (LINUX_VERSION_CODE < 0x20303)
1184 case BLKFLSBUF:
1185 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
1186 if (!capable(CAP_SYS_ADMIN)) return -EACCES;
1187 #endif
1188 fsync_dev(inode->i_rdev);
1189 invalidate_buffers(inode->i_rdev);
1190 break;
1191 RO_IOCTLS(inode->i_rdev, arg);
1192 #else
1193 case BLKROSET:
1194 case BLKROGET:
1195 case BLKFLSBUF:
1196 ret = blk_ioctl(inode->i_rdev, cmd, arg);
1197 break;
1198 #endif
1199 default:
1200 ret = -EINVAL;
1203 return ret;
1204 } /* ftl_ioctl */
1206 /*======================================================================
1208 Handler for block device requests
1210 ======================================================================*/
1212 static int ftl_reread_partitions(int minor)
1214 partition_t *part = myparts[minor >> 4];
1215 int i, whole;
1217 DEBUG(0, "ftl_cs: ftl_reread_partition(%d)\n", minor);
1218 if ((atomic_read(&part->open) > 1)) {
1219 return -EBUSY;
1221 whole = minor & ~(MAX_PART-1);
1223 for (i = 0; i < MAX_PART; i++) {
1224 if (ftl_hd[whole+i].nr_sects > 0) {
1225 kdev_t rdev = MKDEV(FTL_MAJOR, whole+i);
1226 sync_dev(rdev);
1227 invalidate_buffers(rdev);
1229 ftl_hd[whole+i].start_sect = 0;
1230 ftl_hd[whole+i].nr_sects = 0;
1233 scan_header(part);
1235 register_disk(&ftl_gendisk, whole >> PART_BITS, MAX_PART,
1236 &ftl_blk_fops, le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE);
1238 #ifdef PCMCIA_DEBUG
1239 for (i = 0; i < MAX_PART; i++) {
1240 if (ftl_hd[whole+i].nr_sects > 0)
1241 printk(KERN_INFO " %d: start %ld size %ld\n", i,
1242 ftl_hd[whole+i].start_sect,
1243 ftl_hd[whole+i].nr_sects);
1245 #endif
1246 return 0;
1249 /*======================================================================
1251 Handler for block device requests
1253 ======================================================================*/
1255 static void do_ftl_request(request_arg_t)
1257 int ret, minor;
1258 partition_t *part;
1260 do {
1261 // sti();
1262 INIT_REQUEST;
1264 minor = MINOR(CURRENT->rq_dev);
1266 part = myparts[minor >> 4];
1267 if (part) {
1268 ret = 0;
1270 switch (CURRENT->cmd) {
1271 case READ:
1272 ret = ftl_read(part, CURRENT->buffer,
1273 CURRENT->sector+ftl_hd[minor].start_sect,
1274 CURRENT->current_nr_sectors);
1275 if (ret) printk("ftl_read returned %d\n", ret);
1276 break;
1278 case WRITE:
1279 ret = ftl_write(part, CURRENT->buffer,
1280 CURRENT->sector+ftl_hd[minor].start_sect,
1281 CURRENT->current_nr_sectors);
1282 if (ret) printk("ftl_write returned %d\n", ret);
1283 break;
1285 default:
1286 panic("ftl_cs: unknown block command!\n");
1289 } else {
1290 ret = 1;
1291 printk("NULL part in ftl_request\n");
1294 if (!ret) {
1295 CURRENT->sector += CURRENT->current_nr_sectors;
1298 end_request((ret == 0) ? 1 : 0);
1299 } while (1);
1300 } /* do_ftl_request */
1302 /*====================================================================*/
1304 void ftl_freepart(partition_t *part)
1306 if (part->VirtualBlockMap) {
1307 vfree(part->VirtualBlockMap);
1308 part->VirtualBlockMap = NULL;
1310 if (part->VirtualPageMap) {
1311 kfree(part->VirtualPageMap);
1312 part->VirtualPageMap = NULL;
1314 if (part->EUNInfo) {
1315 kfree(part->EUNInfo);
1316 part->EUNInfo = NULL;
1318 if (part->XferInfo) {
1319 kfree(part->XferInfo);
1320 part->XferInfo = NULL;
1322 if (part->bam_cache) {
1323 kfree(part->bam_cache);
1324 part->bam_cache = NULL;
1327 } /* ftl_freepart */
1329 static void ftl_notify_add(struct mtd_info *mtd)
1331 partition_t *partition;
1332 int device;
1334 for (device=0; device < MAX_MTD_DEVICES && myparts[device]; device++)
1337 if (device == MAX_MTD_DEVICES) {
1338 printk(KERN_NOTICE "Maximum number of FTL partitions reached\n"
1339 "Not scanning <%s>\n", mtd->name);
1340 return;
1343 partition = kmalloc(sizeof(partition_t), GFP_KERNEL);
1345 if (!partition) {
1346 printk(KERN_WARNING "No memory to scan for FTL on %s\n",
1347 mtd->name);
1348 return;
1351 memset(partition, 0, sizeof(partition_t));
1353 partition->mtd = mtd;
1355 if ((scan_header(partition) == 0) &&
1356 (build_maps(partition) == 0)) {
1358 partition->state = FTL_FORMATTED;
1359 atomic_set(&partition->open, 0);
1360 myparts[device] = partition;
1361 ftl_reread_partitions(device << 4);
1362 #ifdef PCMCIA_DEBUG
1363 printk(KERN_INFO "ftl_cs: opening %d kb FTL partition\n",
1364 le32_to_cpu(partition->header.FormattedSize) >> 10);
1365 #endif
1369 static void ftl_notify_remove(struct mtd_info *mtd)
1371 int i,j;
1373 /* Q: What happens if you try to remove a device which has
1374 * a currently-open FTL partition on it?
1376 * A: You don't. The ftl_open routine is responsible for
1377 * increasing the use count of the driver module which
1378 * it uses.
1381 /* That's the theory, anyway :) */
1383 for (i=0; i< MAX_MTD_DEVICES; i++)
1384 if (myparts[i] && myparts[i]->mtd == mtd) {
1386 if (myparts[i]->state == FTL_FORMATTED)
1387 ftl_freepart(myparts[i]);
1389 myparts[i]->state = 0;
1390 for (j=0; j<16; j++) {
1391 ftl_gendisk.part[j].nr_sects=0;
1392 ftl_gendisk.part[j].start_sect=0;
1394 kfree(myparts[i]);
1395 myparts[i] = NULL;
1400 #if LINUX_VERSION_CODE < 0x20300
1401 #ifdef MODULE
1402 #define init_ftl init_module
1403 #define cleanup_ftl cleanup_module
1404 #endif
1405 #endif
1407 mod_init_t init_ftl(void)
1409 int i;
1411 memset(myparts, 0, sizeof(myparts));
1413 DEBUG(0, "$Id: ftl.c,v 1.20 2000/06/23 15:17:53 dwmw2 Exp $\n");
1415 if (register_blkdev(FTL_MAJOR, "ftl", &ftl_blk_fops)) {
1416 printk(KERN_NOTICE "ftl_cs: unable to grab major "
1417 "device number!\n");
1418 return -EAGAIN;
1421 for (i = 0; i < MINOR_NR(MAX_DEV, 0, 0); i++)
1422 ftl_blocksizes[i] = 1024;
1423 for (i = 0; i < MAX_DEV*MAX_PART; i++) {
1424 ftl_hd[i].nr_sects = 0;
1425 ftl_hd[i].start_sect = 0;
1427 blksize_size[FTL_MAJOR] = ftl_blocksizes;
1428 ftl_gendisk.major = FTL_MAJOR;
1429 blk_init_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR), &do_ftl_request);
1430 ftl_gendisk.next = gendisk_head;
1431 gendisk_head = &ftl_gendisk;
1433 register_mtd_user(&ftl_notifier);
1435 return 0;
1438 mod_exit_t cleanup_ftl(void)
1440 struct gendisk *gd, **gdp;
1442 unregister_mtd_user(&ftl_notifier);
1444 unregister_blkdev(FTL_MAJOR, "ftl");
1445 blk_cleanup_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR));
1446 blksize_size[FTL_MAJOR] = NULL;
1448 for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
1449 if (*gdp == &ftl_gendisk) {
1450 gd = *gdp; *gdp = gd->next;
1451 break;
1455 #if LINUX_VERSION_CODE > 0x20300
1456 module_init(init_ftl);
1457 module_exit(cleanup_ftl);
1458 #endif