[PATCH] jiffies.h
[opensuse:kernel.git] / drivers / mtd / ftl.c
1 /* This version ported to the Linux-MTD system by dwmw2@infradead.org
2  * $Id: ftl.c,v 1.39 2001/10/02 15:05:11 dwmw2 Exp $
3  *
4  * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
5  * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
6  *
7  * Based on:
8  */
9 /*======================================================================
10
11     A Flash Translation Layer memory card driver
12
13     This driver implements a disk-like block device driver with an
14     apparent block size of 512 bytes for flash memory cards.
15
16     ftl_cs.c 1.62 2000/02/01 00:59:04
17
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/
22
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.
27
28     The initial developer of the original code is David A. Hinds
29     <dhinds@pcmcia.sourceforge.org>.  Portions created by David A. Hinds
30     are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
31
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.
42
43     LEGAL NOTE: The FTL format is patented by M-Systems.  They have
44     granted a license for its use with PCMCIA devices:
45
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."
52
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.
56       
57 ======================================================================*/
58 #include <linux/module.h>
59 #include <linux/mtd/compatmac.h>
60 #include <linux/mtd/mtd.h>
61 /*#define PSYCHO_DEBUG */
62
63 #include <linux/kernel.h>
64 #include <linux/jiffies.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 #include <stdarg.h>
74
75 #include <linux/vmalloc.h>
76 #include <linux/blkpg.h>
77
78 #include <linux/mtd/ftl.h>
79
80 #define request_arg_t           request_queue_t *q
81
82 /*====================================================================*/
83
84 /* Parameters that can be set with 'insmod' */
85 static int shuffle_freq = 50;
86 MODULE_PARM(shuffle_freq, "i");
87
88 /*====================================================================*/
89
90 /* Major device # for FTL device */
91 #ifndef FTL_MAJOR
92 #define FTL_MAJOR       44
93 #endif
94
95 /* Funky stuff for setting up a block device */
96 #define MAJOR_NR                FTL_MAJOR
97 #define DEVICE_NAME             "ftl"
98 #define DEVICE_OFF(device)
99
100 #define DEVICE_NR(minor)        ((minor)>>5)
101 #define REGION_NR(minor)        (((minor)>>3)&3)
102 #define PART_NR(minor)          ((minor)&7)
103 #define MINOR_NR(dev,reg,part)  (((dev)<<5)+((reg)<<3)+(part))
104
105 #include <linux/blk.h>
106
107 /*====================================================================*/
108
109 /* Maximum number of separate memory devices we'll allow */
110 #define MAX_DEV         4
111
112 /* Maximum number of regions per device */
113 #define MAX_REGION      4
114
115 /* Maximum number of partitions in an FTL region */
116 #define PART_BITS       3
117 #define MAX_PART        8
118
119 /* Maximum number of outstanding erase requests per socket */
120 #define MAX_ERASE       8
121
122 /* Sector size -- shouldn't need to change */
123 #define SECTOR_SIZE     512
124
125
126 /* Each memory region corresponds to a minor device */
127 typedef struct partition_t {
128     struct mtd_info     *mtd;
129     u_int32_t           state;
130     u_int32_t           *VirtualBlockMap;
131     u_int32_t           *VirtualPageMap;
132     u_int32_t           FreeTotal;
133     struct eun_info_t {
134         u_int32_t               Offset;
135         u_int32_t               EraseCount;
136         u_int32_t               Free;
137         u_int32_t               Deleted;
138     } *EUNInfo;
139     struct xfer_info_t {
140         u_int32_t               Offset;
141         u_int32_t               EraseCount;
142         u_int16_t               state;
143     } *XferInfo;
144     u_int16_t           bam_index;
145     u_int32_t           *bam_cache;
146     u_int16_t           DataUnits;
147     u_int32_t           BlocksPerUnit;
148     erase_unit_header_t header;
149 #if 0
150     region_info_t       region;
151     memory_handle_t     handle;
152 #endif
153     atomic_t            open;
154 } partition_t;
155
156 partition_t *myparts[MAX_MTD_DEVICES];
157
158 static void ftl_notify_add(struct mtd_info *mtd);
159 static void ftl_notify_remove(struct mtd_info *mtd);
160
161 void ftl_freepart(partition_t *part);
162
163 static struct mtd_notifier ftl_notifier = {
164         add:    ftl_notify_add,
165         remove: ftl_notify_remove,
166 };
167
168 /* Partition state flags */
169 #define FTL_FORMATTED   0x01
170
171 /* Transfer unit states */
172 #define XFER_UNKNOWN    0x00
173 #define XFER_ERASING    0x01
174 #define XFER_ERASED     0x02
175 #define XFER_PREPARED   0x03
176 #define XFER_FAILED     0x04
177
178 static struct hd_struct ftl_hd[MINOR_NR(MAX_DEV, 0, 0)];
179 static int ftl_sizes[MINOR_NR(MAX_DEV, 0, 0)];
180
181 static struct gendisk ftl_gendisk = {
182     major:              FTL_MAJOR,
183     major_name:         "ftl",
184     minor_shift:        PART_BITS,
185     part:               ftl_hd,
186     sizes:              ftl_sizes,
187 };
188
189 /*====================================================================*/
190
191 static int ftl_ioctl(struct inode *inode, struct file *file,
192                      u_int cmd, u_long arg);
193 static int ftl_open(struct inode *inode, struct file *file);
194 static release_t ftl_close(struct inode *inode, struct file *file);
195 static int ftl_reread_partitions(kdev_t dev);
196
197 static void ftl_erase_callback(struct erase_info *done);
198
199 static struct block_device_operations ftl_blk_fops = {
200     owner:      THIS_MODULE,
201     open:       ftl_open,
202     release:    ftl_close,
203     ioctl:      ftl_ioctl,
204 };
205
206 /*======================================================================
207
208     Scan_header() checks to see if a memory region contains an FTL
209     partition.  build_maps() reads all the erase unit headers, builds
210     the erase unit map, and then builds the virtual page map.
211     
212 ======================================================================*/
213
214 static int scan_header(partition_t *part)
215 {
216     erase_unit_header_t header;
217     loff_t offset, max_offset;
218     int ret;
219     part->header.FormattedSize = 0;
220     max_offset = (0x100000<part->mtd->size)?0x100000:part->mtd->size;
221     /* Search first megabyte for a valid FTL header */
222     for (offset = 0;
223          (offset + sizeof(header)) < max_offset;
224          offset += part->mtd->erasesize ? : 0x2000) {
225
226         ret = part->mtd->read(part->mtd, offset, sizeof(header), &ret, 
227                               (unsigned char *)&header);
228         
229         if (ret) 
230             return ret;
231
232         if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break;
233     }
234
235     if (offset == max_offset) {
236         printk(KERN_NOTICE "ftl_cs: FTL header not found.\n");
237         return -ENOENT;
238     }
239     if ((le16_to_cpu(header.NumEraseUnits) > 65536) || header.BlockSize != 9 ||
240         (header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) ||
241         (header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) {
242         printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n");
243         return -1;
244     }
245     if ((1 << header.EraseUnitSize) != part->mtd->erasesize) {
246         printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x\n",
247                1 << header.EraseUnitSize,part->mtd->erasesize);
248         return -1;
249     }
250     part->header = header;
251     return 0;
252 }
253
254 static int build_maps(partition_t *part)
255 {
256     erase_unit_header_t header;
257     u_int16_t xvalid, xtrans, i;
258     u_int blocks, j;
259     int hdr_ok, ret = -1;
260     ssize_t retval;
261     loff_t offset;
262
263     /* Set up erase unit maps */
264     part->DataUnits = le16_to_cpu(part->header.NumEraseUnits) -
265         part->header.NumTransferUnits;
266     part->EUNInfo = kmalloc(part->DataUnits * sizeof(struct eun_info_t),
267                             GFP_KERNEL);
268     if (!part->EUNInfo)
269             goto out;
270     for (i = 0; i < part->DataUnits; i++)
271         part->EUNInfo[i].Offset = 0xffffffff;
272     part->XferInfo =
273         kmalloc(part->header.NumTransferUnits * sizeof(struct xfer_info_t),
274                 GFP_KERNEL);
275     if (!part->XferInfo)
276             goto out_EUNInfo;
277
278     xvalid = xtrans = 0;
279     for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) {
280         offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN))
281                       << part->header.EraseUnitSize);
282         ret = part->mtd->read(part->mtd, offset, sizeof(header), &retval, 
283                               (unsigned char *)&header);
284         
285         if (ret) 
286             goto out_XferInfo;
287
288         ret = -1;
289         /* Is this a transfer partition? */
290         hdr_ok = (strcmp(header.DataOrgTuple+3, "FTL100") == 0);
291         if (hdr_ok && (le16_to_cpu(header.LogicalEUN) < part->DataUnits) &&
292             (part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset == 0xffffffff)) {
293             part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset = offset;
294             part->EUNInfo[le16_to_cpu(header.LogicalEUN)].EraseCount =
295                 le32_to_cpu(header.EraseCount);
296             xvalid++;
297         } else {
298             if (xtrans == part->header.NumTransferUnits) {
299                 printk(KERN_NOTICE "ftl_cs: format error: too many "
300                        "transfer units!\n");
301                 goto out_XferInfo;
302             }
303             if (hdr_ok && (le16_to_cpu(header.LogicalEUN) == 0xffff)) {
304                 part->XferInfo[xtrans].state = XFER_PREPARED;
305                 part->XferInfo[xtrans].EraseCount = le32_to_cpu(header.EraseCount);
306             } else {
307                 part->XferInfo[xtrans].state = XFER_UNKNOWN;
308                 /* Pick anything reasonable for the erase count */
309                 part->XferInfo[xtrans].EraseCount =
310                     le32_to_cpu(part->header.EraseCount);
311             }
312             part->XferInfo[xtrans].Offset = offset;
313             xtrans++;
314         }
315     }
316     /* Check for format trouble */
317     header = part->header;
318     if ((xtrans != header.NumTransferUnits) ||
319         (xvalid+xtrans != le16_to_cpu(header.NumEraseUnits))) {
320         printk(KERN_NOTICE "ftl_cs: format error: erase units "
321                "don't add up!\n");
322         goto out_XferInfo;
323     }
324     
325     /* Set up virtual page map */
326     blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize;
327     part->VirtualBlockMap = vmalloc(blocks * sizeof(u_int32_t));
328     if (!part->VirtualBlockMap)
329             goto out_XferInfo;
330
331     memset(part->VirtualBlockMap, 0xff, blocks * sizeof(u_int32_t));
332     part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize;
333
334     part->bam_cache = kmalloc(part->BlocksPerUnit * sizeof(u_int32_t),
335                               GFP_KERNEL);
336     if (!part->bam_cache)
337             goto out_VirtualBlockMap;
338
339     part->bam_index = 0xffff;
340     part->FreeTotal = 0;
341
342     for (i = 0; i < part->DataUnits; i++) {
343         part->EUNInfo[i].Free = 0;
344         part->EUNInfo[i].Deleted = 0;
345         offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset);
346         
347         ret = part->mtd->read(part->mtd, offset,  
348                               part->BlocksPerUnit * sizeof(u_int32_t), &retval, 
349                               (unsigned char *)part->bam_cache);
350         
351         if (ret) 
352                 goto out_bam_cache;
353
354         for (j = 0; j < part->BlocksPerUnit; j++) {
355             if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) {
356                 part->EUNInfo[i].Free++;
357                 part->FreeTotal++;
358             } else if ((BLOCK_TYPE(le32_to_cpu(part->bam_cache[j])) == BLOCK_DATA) &&
359                      (BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j])) < blocks))
360                 part->VirtualBlockMap[BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j]))] =
361                     (i << header.EraseUnitSize) + (j << header.BlockSize);
362             else if (BLOCK_DELETED(le32_to_cpu(part->bam_cache[j])))
363                 part->EUNInfo[i].Deleted++;
364         }
365     }
366     
367     ret = 0;
368     goto out;
369
370 out_bam_cache:
371     kfree(part->bam_cache);
372 out_VirtualBlockMap:
373     vfree(part->VirtualBlockMap);
374 out_XferInfo:
375     kfree(part->XferInfo);
376 out_EUNInfo:
377     kfree(part->EUNInfo);
378 out:
379     return ret;
380 } /* build_maps */
381
382 /*======================================================================
383
384     Erase_xfer() schedules an asynchronous erase operation for a
385     transfer unit.
386     
387 ======================================================================*/
388
389 static int erase_xfer(partition_t *part,
390                       u_int16_t xfernum)
391 {
392     int ret;
393     struct xfer_info_t *xfer;
394     struct erase_info *erase;
395
396     xfer = &part->XferInfo[xfernum];
397     DEBUG(1, "ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset);
398     xfer->state = XFER_ERASING;
399
400     /* Is there a free erase slot? Always in MTD. */
401     
402     
403     erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL);
404     if (!erase) 
405             return -ENOMEM;
406
407     erase->callback = ftl_erase_callback;
408     erase->addr = xfer->Offset;
409     erase->len = 1 << part->header.EraseUnitSize;
410     erase->priv = (u_long)part;
411     
412     ret = part->mtd->erase(part->mtd, erase);
413
414     if (!ret)
415             xfer->EraseCount++;
416     else
417             kfree(erase);
418
419     return ret;
420 } /* erase_xfer */
421
422 /*======================================================================
423
424     Prepare_xfer() takes a freshly erased transfer unit and gives
425     it an appropriate header.
426     
427 ======================================================================*/
428
429 static void ftl_erase_callback(struct erase_info *erase)
430 {
431     partition_t *part;
432     struct xfer_info_t *xfer;
433     int i;
434     
435     /* Look up the transfer unit */
436     part = (partition_t *)(erase->priv);
437
438     for (i = 0; i < part->header.NumTransferUnits; i++)
439         if (part->XferInfo[i].Offset == erase->addr) break;
440
441     if (i == part->header.NumTransferUnits) {
442         printk(KERN_NOTICE "ftl_cs: internal error: "
443                "erase lookup failed!\n");
444         return;
445     }
446
447     xfer = &part->XferInfo[i];
448     if (erase->state == MTD_ERASE_DONE)
449         xfer->state = XFER_ERASED;
450     else {
451         xfer->state = XFER_FAILED;
452         printk(KERN_NOTICE "ftl_cs: erase failed: state = %d\n",
453                erase->state);
454     }
455
456     kfree(erase);
457
458 } /* ftl_erase_callback */
459
460 static int prepare_xfer(partition_t *part, int i)
461 {
462     erase_unit_header_t header;
463     struct xfer_info_t *xfer;
464     int nbam, ret;
465     u_int32_t ctl;
466     ssize_t retlen;
467     loff_t offset;
468
469     xfer = &part->XferInfo[i];
470     xfer->state = XFER_FAILED;
471     
472     DEBUG(1, "ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset);
473
474     /* Write the transfer unit header */
475     header = part->header;
476     header.LogicalEUN = cpu_to_le16(0xffff);
477     header.EraseCount = cpu_to_le32(xfer->EraseCount);
478
479     ret = part->mtd->write(part->mtd, xfer->Offset, sizeof(header),
480                            &retlen, (u_char *)&header);
481
482     if (ret) {
483         return ret;
484     }
485
486     /* Write the BAM stub */
487     nbam = (part->BlocksPerUnit * sizeof(u_int32_t) +
488             le32_to_cpu(part->header.BAMOffset) + SECTOR_SIZE - 1) / SECTOR_SIZE;
489
490     offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
491     ctl = cpu_to_le32(BLOCK_CONTROL);
492
493     for (i = 0; i < nbam; i++, offset += sizeof(u_int32_t)) {
494
495         ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t), 
496                                &retlen, (u_char *)&ctl);
497
498         if (ret)
499             return ret;
500     }
501     xfer->state = XFER_PREPARED;
502     return 0;
503     
504 } /* prepare_xfer */
505
506 /*======================================================================
507
508     Copy_erase_unit() takes a full erase block and a transfer unit,
509     copies everything to the transfer unit, then swaps the block
510     pointers.
511
512     All data blocks are copied to the corresponding blocks in the
513     target unit, so the virtual block map does not need to be
514     updated.
515     
516 ======================================================================*/
517
518 static int copy_erase_unit(partition_t *part, u_int16_t srcunit,
519                            u_int16_t xferunit)
520 {
521     u_char buf[SECTOR_SIZE];
522     struct eun_info_t *eun;
523     struct xfer_info_t *xfer;
524     u_int32_t src, dest, free, i;
525     u_int16_t unit;
526     int ret;
527     ssize_t retlen;
528     loff_t offset;
529     u_int16_t srcunitswap = cpu_to_le16(srcunit);
530
531     eun = &part->EUNInfo[srcunit];
532     xfer = &part->XferInfo[xferunit];
533     DEBUG(2, "ftl_cs: copying block 0x%x to 0x%x\n",
534           eun->Offset, xfer->Offset);
535         
536     
537     /* Read current BAM */
538     if (part->bam_index != srcunit) {
539
540         offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
541
542         ret = part->mtd->read(part->mtd, offset, 
543                               part->BlocksPerUnit * sizeof(u_int32_t),
544                               &retlen, (u_char *) (part->bam_cache));
545
546         /* mark the cache bad, in case we get an error later */
547         part->bam_index = 0xffff;
548
549         if (ret) {
550             printk( KERN_WARNING "ftl: Failed to read BAM cache in copy_erase_unit()!\n");                
551             return ret;
552         }
553     }
554     
555     /* Write the LogicalEUN for the transfer unit */
556     xfer->state = XFER_UNKNOWN;
557     offset = xfer->Offset + 20; /* Bad! */
558     unit = cpu_to_le16(0x7fff);
559
560     ret = part->mtd->write(part->mtd, offset, sizeof(u_int16_t),
561                            &retlen, (u_char *) &unit);
562     
563     if (ret) {
564         printk( KERN_WARNING "ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
565         return ret;
566     }
567     
568     /* Copy all data blocks from source unit to transfer unit */
569     src = eun->Offset; dest = xfer->Offset;
570
571     free = 0;
572     ret = 0;
573     for (i = 0; i < part->BlocksPerUnit; i++) {
574         switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) {
575         case BLOCK_CONTROL:
576             /* This gets updated later */
577             break;
578         case BLOCK_DATA:
579         case BLOCK_REPLACEMENT:
580             ret = part->mtd->read(part->mtd, src, SECTOR_SIZE,
581                         &retlen, (u_char *) buf);
582             if (ret) {
583                 printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n");
584                 return ret;
585             }
586
587
588             ret = part->mtd->write(part->mtd, dest, SECTOR_SIZE,
589                         &retlen, (u_char *) buf);
590             if (ret)  {
591                 printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n");
592                 return ret;
593             }
594
595             break;
596         default:
597             /* All other blocks must be free */
598             part->bam_cache[i] = cpu_to_le32(0xffffffff);
599             free++;
600             break;
601         }
602         src += SECTOR_SIZE;
603         dest += SECTOR_SIZE;
604     }
605
606     /* Write the BAM to the transfer unit */
607     ret = part->mtd->write(part->mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset), 
608                     part->BlocksPerUnit * sizeof(int32_t), &retlen, 
609                     (u_char *)part->bam_cache);
610     if (ret) {
611         printk( KERN_WARNING "ftl: Error writing BAM in copy_erase_unit\n");
612         return ret;
613     }
614
615     
616     /* All clear? Then update the LogicalEUN again */
617     ret = part->mtd->write(part->mtd, xfer->Offset + 20, sizeof(u_int16_t),
618                            &retlen, (u_char *)&srcunitswap);
619
620     if (ret) {
621         printk(KERN_WARNING "ftl: Error writing new LogicalEUN in copy_erase_unit\n");
622         return ret;
623     }    
624     
625     
626     /* Update the maps and usage stats*/
627     i = xfer->EraseCount;
628     xfer->EraseCount = eun->EraseCount;
629     eun->EraseCount = i;
630     i = xfer->Offset;
631     xfer->Offset = eun->Offset;
632     eun->Offset = i;
633     part->FreeTotal -= eun->Free;
634     part->FreeTotal += free;
635     eun->Free = free;
636     eun->Deleted = 0;
637     
638     /* Now, the cache should be valid for the new block */
639     part->bam_index = srcunit;
640     
641     return 0;
642 } /* copy_erase_unit */
643
644 /*======================================================================
645
646     reclaim_block() picks a full erase unit and a transfer unit and
647     then calls copy_erase_unit() to copy one to the other.  Then, it
648     schedules an erase on the expired block.
649
650     What's a good way to decide which transfer unit and which erase
651     unit to use?  Beats me.  My way is to always pick the transfer
652     unit with the fewest erases, and usually pick the data unit with
653     the most deleted blocks.  But with a small probability, pick the
654     oldest data unit instead.  This means that we generally postpone
655     the next reclaimation as long as possible, but shuffle static
656     stuff around a bit for wear leveling.
657     
658 ======================================================================*/
659
660 static int reclaim_block(partition_t *part)
661 {
662     u_int16_t i, eun, xfer;
663     u_int32_t best;
664     int queued, ret;
665
666     DEBUG(0, "ftl_cs: reclaiming space...\n");
667     DEBUG(3, "NumTransferUnits == %x\n", part->header.NumTransferUnits);
668     /* Pick the least erased transfer unit */
669     best = 0xffffffff; xfer = 0xffff;
670     do {
671         queued = 0;
672         for (i = 0; i < part->header.NumTransferUnits; i++) {
673             int n=0;
674             if (part->XferInfo[i].state == XFER_UNKNOWN) {
675                 DEBUG(3,"XferInfo[%d].state == XFER_UNKNOWN\n",i);
676                 n=1;
677                 erase_xfer(part, i);
678             }
679             if (part->XferInfo[i].state == XFER_ERASING) {
680                 DEBUG(3,"XferInfo[%d].state == XFER_ERASING\n",i);
681                 n=1;
682                 queued = 1;
683             }
684             else if (part->XferInfo[i].state == XFER_ERASED) {
685                 DEBUG(3,"XferInfo[%d].state == XFER_ERASED\n",i);
686                 n=1;
687                 prepare_xfer(part, i);
688             }
689             if (part->XferInfo[i].state == XFER_PREPARED) {
690                 DEBUG(3,"XferInfo[%d].state == XFER_PREPARED\n",i);
691                 n=1;
692                 if (part->XferInfo[i].EraseCount <= best) {
693                     best = part->XferInfo[i].EraseCount;
694                     xfer = i;
695                 }
696             }
697                 if (!n)
698                     DEBUG(3,"XferInfo[%d].state == %x\n",i, part->XferInfo[i].state);
699
700         }
701         if (xfer == 0xffff) {
702             if (queued) {
703                 DEBUG(1, "ftl_cs: waiting for transfer "
704                       "unit to be prepared...\n");
705                 if (part->mtd->sync)
706                         part->mtd->sync(part->mtd);
707             } else {
708                 static int ne = 0;
709                 if (++ne < 5)
710                     printk(KERN_NOTICE "ftl_cs: reclaim failed: no "
711                            "suitable transfer units!\n");
712                 else
713                     DEBUG(1, "ftl_cs: reclaim failed: no "
714                           "suitable transfer units!\n");
715                         
716                 return -EIO;
717             }
718         }
719     } while (xfer == 0xffff);
720
721     eun = 0;
722     if ((jiffies % shuffle_freq) == 0) {
723         DEBUG(1, "ftl_cs: recycling freshest block...\n");
724         best = 0xffffffff;
725         for (i = 0; i < part->DataUnits; i++)
726             if (part->EUNInfo[i].EraseCount <= best) {
727                 best = part->EUNInfo[i].EraseCount;
728                 eun = i;
729             }
730     } else {
731         best = 0;
732         for (i = 0; i < part->DataUnits; i++)
733             if (part->EUNInfo[i].Deleted >= best) {
734                 best = part->EUNInfo[i].Deleted;
735                 eun = i;
736             }
737         if (best == 0) {
738             static int ne = 0;
739             if (++ne < 5)
740                 printk(KERN_NOTICE "ftl_cs: reclaim failed: "
741                        "no free blocks!\n");
742             else
743                 DEBUG(1,"ftl_cs: reclaim failed: "
744                        "no free blocks!\n");
745
746             return -EIO;
747         }
748     }
749     ret = copy_erase_unit(part, eun, xfer);
750     if (!ret)
751         erase_xfer(part, xfer);
752     else
753         printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed!\n");
754     return ret;
755 } /* reclaim_block */
756
757 /*======================================================================
758
759     Find_free() searches for a free block.  If necessary, it updates
760     the BAM cache for the erase unit containing the free block.  It
761     returns the block index -- the erase unit is just the currently
762     cached unit.  If there are no free blocks, it returns 0 -- this
763     is never a valid data block because it contains the header.
764     
765 ======================================================================*/
766
767 #ifdef PSYCHO_DEBUG
768 static void dump_lists(partition_t *part)
769 {
770     int i;
771     printk(KERN_DEBUG "ftl_cs: Free total = %d\n", part->FreeTotal);
772     for (i = 0; i < part->DataUnits; i++)
773         printk(KERN_DEBUG "ftl_cs:   unit %d: %d phys, %d free, "
774                "%d deleted\n", i,
775                part->EUNInfo[i].Offset >> part->header.EraseUnitSize,
776                part->EUNInfo[i].Free, part->EUNInfo[i].Deleted);
777 }
778 #endif
779
780 static u_int32_t find_free(partition_t *part)
781 {
782     u_int16_t stop, eun;
783     u_int32_t blk;
784     size_t retlen;
785     int ret;
786     
787     /* Find an erase unit with some free space */
788     stop = (part->bam_index == 0xffff) ? 0 : part->bam_index;
789     eun = stop;
790     do {
791         if (part->EUNInfo[eun].Free != 0) break;
792         /* Wrap around at end of table */
793         if (++eun == part->DataUnits) eun = 0;
794     } while (eun != stop);
795
796     if (part->EUNInfo[eun].Free == 0)
797         return 0;
798     
799     /* Is this unit's BAM cached? */
800     if (eun != part->bam_index) {
801         /* Invalidate cache */
802         part->bam_index = 0xffff;
803
804         ret = part->mtd->read(part->mtd, 
805                        part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
806                        part->BlocksPerUnit * sizeof(u_int32_t),
807                        &retlen, (u_char *) (part->bam_cache));
808         
809         if (ret) {
810             printk(KERN_WARNING"ftl: Error reading BAM in find_free\n");
811             return 0;
812         }
813         part->bam_index = eun;
814     }
815
816     /* Find a free block */
817     for (blk = 0; blk < part->BlocksPerUnit; blk++)
818         if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break;
819     if (blk == part->BlocksPerUnit) {
820 #ifdef PSYCHO_DEBUG
821         static int ne = 0;
822         if (++ne == 1)
823             dump_lists(part);
824 #endif
825         printk(KERN_NOTICE "ftl_cs: bad free list!\n");
826         return 0;
827     }
828     DEBUG(2, "ftl_cs: found free block at %d in %d\n", blk, eun);
829     return blk;
830     
831 } /* find_free */
832
833 /*======================================================================
834
835     This gets a memory handle for the region corresponding to the
836     minor device number.
837     
838 ======================================================================*/
839
840 static int ftl_open(struct inode *inode, struct file *file)
841 {
842     int minor = minor(inode->i_rdev);
843     partition_t *partition;
844
845     if (minor>>4 >= MAX_MTD_DEVICES)
846         return -ENODEV;
847
848     partition = myparts[minor>>4];
849
850     if (!partition)
851         return -ENODEV;
852
853     if (partition->state != FTL_FORMATTED)
854         return -ENXIO;
855     
856     if (ftl_gendisk.part[minor].nr_sects == 0)
857         return -ENXIO;
858
859     if (!get_mtd_device(partition->mtd, -1))
860             return /* -E'SBUGGEREDOFF */ -ENXIO;
861
862     if ((file->f_mode & 2) && !(partition->mtd->flags & MTD_CLEAR_BITS) ) {
863             put_mtd_device(partition->mtd);
864             return -EROFS;
865     }
866     
867     DEBUG(0, "ftl_cs: ftl_open(%d)\n", minor);
868
869     atomic_inc(&partition->open);
870
871     return 0;
872 }
873
874 /*====================================================================*/
875
876 static release_t ftl_close(struct inode *inode, struct file *file)
877 {
878     int minor = minor(inode->i_rdev);
879     partition_t *part = myparts[minor >> 4];
880     int i;
881     
882     DEBUG(0, "ftl_cs: ftl_close(%d)\n", minor);
883
884     /* Flush all writes */
885     invalidate_device(inode->i_rdev, 1);
886
887     /* Wait for any pending erase operations to complete */
888     if (part->mtd->sync)
889             part->mtd->sync(part->mtd);
890     
891     for (i = 0; i < part->header.NumTransferUnits; i++) {
892         if (part->XferInfo[i].state == XFER_ERASED)
893             prepare_xfer(part, i);
894     }
895
896     atomic_dec(&part->open);
897
898     put_mtd_device(part->mtd);
899     release_return(0);
900 } /* ftl_close */
901
902
903 /*======================================================================
904
905     Read a series of sectors from an FTL partition.
906     
907 ======================================================================*/
908
909 static int ftl_read(partition_t *part, caddr_t buffer,
910                     u_long sector, u_long nblocks)
911 {
912     u_int32_t log_addr, bsize;
913     u_long i;
914     int ret;
915     size_t offset, retlen;
916     
917     DEBUG(2, "ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
918           part, sector, nblocks);
919     if (!(part->state & FTL_FORMATTED)) {
920         printk(KERN_NOTICE "ftl_cs: bad partition\n");
921         return -EIO;
922     }
923     bsize = 1 << part->header.EraseUnitSize;
924
925     for (i = 0; i < nblocks; i++) {
926         if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) {
927             printk(KERN_NOTICE "ftl_cs: bad read offset\n");
928             return -EIO;
929         }
930         log_addr = part->VirtualBlockMap[sector+i];
931         if (log_addr == 0xffffffff)
932             memset(buffer, 0, SECTOR_SIZE);
933         else {
934             offset = (part->EUNInfo[log_addr / bsize].Offset
935                           + (log_addr % bsize));
936             ret = part->mtd->read(part->mtd, offset, SECTOR_SIZE,
937                            &retlen, (u_char *) buffer);
938
939             if (ret) {
940                 printk(KERN_WARNING "Error reading MTD device in ftl_read()\n");
941                 return ret;
942             }
943         }
944         buffer += SECTOR_SIZE;
945     }
946     return 0;
947 } /* ftl_read */
948
949 /*======================================================================
950
951     Write a series of sectors to an FTL partition
952     
953 ======================================================================*/
954
955 static int set_bam_entry(partition_t *part, u_int32_t log_addr,
956                          u_int32_t virt_addr)
957 {
958     u_int32_t bsize, blk, le_virt_addr;
959 #ifdef PSYCHO_DEBUG
960     u_int32_t old_addr;
961 #endif
962     u_int16_t eun;
963     int ret;
964     size_t retlen, offset;
965
966     DEBUG(2, "ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
967           part, log_addr, virt_addr);
968     bsize = 1 << part->header.EraseUnitSize;
969     eun = log_addr / bsize;
970     blk = (log_addr % bsize) / SECTOR_SIZE;
971     offset = (part->EUNInfo[eun].Offset + blk * sizeof(u_int32_t) +
972                   le32_to_cpu(part->header.BAMOffset));
973     
974 #ifdef PSYCHO_DEBUG
975     ret = part->mtd->read(part->mtd, offset, sizeof(u_int32_t),
976                         &retlen, (u_char *)&old_addr);
977     if (ret) {
978         printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
979         return ret;
980     }
981     old_addr = le32_to_cpu(old_addr);
982
983     if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) ||
984         ((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) ||
985         (!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) {
986         static int ne = 0;
987         if (++ne < 5) {
988             printk(KERN_NOTICE "ftl_cs: set_bam_entry() inconsistency!\n");
989             printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, old = 0x%x"
990                    ", new = 0x%x\n", log_addr, old_addr, virt_addr);
991         }
992         return -EIO;
993     }
994 #endif
995     le_virt_addr = cpu_to_le32(virt_addr);
996     if (part->bam_index == eun) {
997 #ifdef PSYCHO_DEBUG
998         if (le32_to_cpu(part->bam_cache[blk]) != old_addr) {
999             static int ne = 0;
1000             if (++ne < 5) {
1001                 printk(KERN_NOTICE "ftl_cs: set_bam_entry() "
1002                        "inconsistency!\n");
1003                 printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, cache"
1004                        " = 0x%x\n",
1005                        le32_to_cpu(part->bam_cache[blk]), old_addr);
1006             }
1007             return -EIO;
1008         }
1009 #endif
1010         part->bam_cache[blk] = le_virt_addr;
1011     }
1012     ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t),
1013                             &retlen, (u_char *)&le_virt_addr);
1014
1015     if (ret) {
1016         printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!\n");
1017         printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, new = 0x%x\n",
1018                log_addr, virt_addr);
1019     }
1020     return ret;
1021 } /* set_bam_entry */
1022
1023 static int ftl_write(partition_t *part, caddr_t buffer,
1024                      u_long sector, u_long nblocks)
1025 {
1026     u_int32_t bsize, log_addr, virt_addr, old_addr, blk;
1027     u_long i;
1028     int ret;
1029     size_t retlen, offset;
1030
1031     DEBUG(2, "ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
1032           part, sector, nblocks);
1033     if (!(part->state & FTL_FORMATTED)) {
1034         printk(KERN_NOTICE "ftl_cs: bad partition\n");
1035         return -EIO;
1036     }
1037     /* See if we need to reclaim space, before we start */
1038     while (part->FreeTotal < nblocks) {
1039         ret = reclaim_block(part);
1040         if (ret)
1041             return ret;
1042     }
1043     
1044     bsize = 1 << part->header.EraseUnitSize;
1045
1046     virt_addr = sector * SECTOR_SIZE | BLOCK_DATA;
1047     for (i = 0; i < nblocks; i++) {
1048         if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) {
1049             printk(KERN_NOTICE "ftl_cs: bad write offset\n");
1050             return -EIO;
1051         }
1052
1053         /* Grab a free block */
1054         blk = find_free(part);
1055         if (blk == 0) {
1056             static int ne = 0;
1057             if (++ne < 5)
1058                 printk(KERN_NOTICE "ftl_cs: internal error: "
1059                        "no free blocks!\n");
1060             return -ENOSPC;
1061         }
1062
1063         /* Tag the BAM entry, and write the new block */
1064         log_addr = part->bam_index * bsize + blk * SECTOR_SIZE;
1065         part->EUNInfo[part->bam_index].Free--;
1066         part->FreeTotal--;
1067         if (set_bam_entry(part, log_addr, 0xfffffffe)) 
1068             return -EIO;
1069         part->EUNInfo[part->bam_index].Deleted++;
1070         offset = (part->EUNInfo[part->bam_index].Offset +
1071                       blk * SECTOR_SIZE);
1072         ret = part->mtd->write(part->mtd, offset, SECTOR_SIZE, &retlen, 
1073                                      buffer);
1074
1075         if (ret) {
1076             printk(KERN_NOTICE "ftl_cs: block write failed!\n");
1077             printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, virt_addr"
1078                    " = 0x%x, Offset = 0x%x\n", log_addr, virt_addr,
1079                    offset);
1080             return -EIO;
1081         }
1082         
1083         /* Only delete the old entry when the new entry is ready */
1084         old_addr = part->VirtualBlockMap[sector+i];
1085         if (old_addr != 0xffffffff) {
1086             part->VirtualBlockMap[sector+i] = 0xffffffff;
1087             part->EUNInfo[old_addr/bsize].Deleted++;
1088             if (set_bam_entry(part, old_addr, 0))
1089                 return -EIO;
1090         }
1091
1092         /* Finally, set up the new pointers */
1093         if (set_bam_entry(part, log_addr, virt_addr))
1094             return -EIO;
1095         part->VirtualBlockMap[sector+i] = log_addr;
1096         part->EUNInfo[part->bam_index].Deleted--;
1097         
1098         buffer += SECTOR_SIZE;
1099         virt_addr += SECTOR_SIZE;
1100     }
1101     return 0;
1102 } /* ftl_write */
1103
1104 /*======================================================================
1105
1106     IOCTL calls for getting device parameters.
1107
1108 ======================================================================*/
1109
1110 static int ftl_ioctl(struct inode *inode, struct file *file,
1111                      u_int cmd, u_long arg)
1112 {
1113     struct hd_geometry *geo = (struct hd_geometry *)arg;
1114     int ret = 0, minor = minor(inode->i_rdev);
1115     partition_t *part= myparts[minor >> 4];
1116     u_long sect;
1117
1118     if (!part)
1119         return -ENODEV; /* How? */
1120
1121     switch (cmd) {
1122     case HDIO_GETGEO:
1123         ret = verify_area(VERIFY_WRITE, (long *)arg, sizeof(*geo));
1124         if (ret) return ret;
1125         /* Sort of arbitrary: round size down to 4K boundary */
1126         sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
1127         put_user(1, (char *)&geo->heads);
1128         put_user(8, (char *)&geo->sectors);
1129         put_user((sect>>3), (short *)&geo->cylinders);
1130         put_user(get_start_sect(inode->i_rdev), (u_long *)&geo->start);
1131         break;
1132     case BLKGETSIZE:
1133         ret = put_user(ftl_hd[minor].nr_sects, (unsigned long *)arg);
1134         break;
1135     case BLKGETSIZE64:
1136         ret = put_user((u64)ftl_hd[minor].nr_sects << 9, (u64 *)arg);
1137         break;
1138     case BLKRRPART:
1139         ret = ftl_reread_partitions(inode->i_rdev);
1140         break;
1141     case BLKROSET:
1142     case BLKROGET:
1143     case BLKFLSBUF:
1144         ret = blk_ioctl(inode->i_bdev, cmd, arg);
1145         break;
1146     default:
1147         ret = -EINVAL;
1148     }
1149
1150     return ret;
1151 } /* ftl_ioctl */
1152
1153 /*======================================================================
1154
1155     Handler for block device requests
1156
1157 ======================================================================*/
1158
1159 static int ftl_reread_partitions(kdev_t dev)
1160 {
1161     int minor = minor(dev);
1162     partition_t *part = myparts[minor >> 4];
1163     int res;
1164
1165     DEBUG(0, "ftl_cs: ftl_reread_partition(%d)\n", minor);
1166     if ((atomic_read(&part->open) > 1)) {
1167             return -EBUSY;
1168     }
1169
1170     res = wipe_partitions(dev);
1171     if (res)
1172         goto leave;
1173
1174     scan_header(part);
1175
1176     register_disk(&ftl_gendisk, whole >> PART_BITS, MAX_PART,
1177                   &ftl_blk_fops, le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE);
1178
1179     return res;
1180 }
1181
1182 /*======================================================================
1183
1184     Handler for block device requests
1185
1186 ======================================================================*/
1187
1188 static void do_ftl_request(request_arg_t)
1189 {
1190     int ret, minor;
1191     partition_t *part;
1192
1193     do {
1194         //          sti();
1195         if (blk_queue_empty(QUEUE)) {
1196                 CLEAR_INTR;
1197                 return;
1198         }
1199
1200         minor = minor(CURRENT->rq_dev);
1201
1202         part = myparts[minor >> 4];
1203         if (part) {
1204           ret = 0;
1205           
1206           switch (CURRENT->cmd) {
1207           case READ:
1208             ret = ftl_read(part, CURRENT->buffer,
1209                            CURRENT->sector+ftl_hd[minor].start_sect,
1210                            CURRENT->current_nr_sectors);
1211             if (ret) printk("ftl_read returned %d\n", ret);
1212             break;
1213             
1214           case WRITE:
1215             ret = ftl_write(part, CURRENT->buffer,
1216                             CURRENT->sector+ftl_hd[minor].start_sect,
1217                             CURRENT->current_nr_sectors);
1218             if (ret) printk("ftl_write returned %d\n", ret);
1219             break;
1220             
1221           default:
1222             panic("ftl_cs: unknown block command!\n");
1223             
1224           }
1225         } else {
1226           ret = 1;
1227           printk("NULL part in ftl_request\n");
1228         }
1229          
1230         if (!ret) {
1231           CURRENT->sector += CURRENT->current_nr_sectors;
1232         }
1233         
1234         end_request((ret == 0) ? 1 : 0);
1235     } while (1);
1236 } /* do_ftl_request */
1237
1238 /*====================================================================*/
1239
1240 void ftl_freepart(partition_t *part)
1241 {
1242     if (part->VirtualBlockMap) {
1243         vfree(part->VirtualBlockMap);
1244         part->VirtualBlockMap = NULL;
1245     }
1246     if (part->VirtualPageMap) {
1247         kfree(part->VirtualPageMap);
1248         part->VirtualPageMap = NULL;
1249     }
1250     if (part->EUNInfo) {
1251         kfree(part->EUNInfo);
1252         part->EUNInfo = NULL;
1253     }
1254     if (part->XferInfo) {
1255         kfree(part->XferInfo);
1256         part->XferInfo = NULL;
1257     }
1258     if (part->bam_cache) {
1259         kfree(part->bam_cache);
1260         part->bam_cache = NULL;
1261     }
1262     
1263 } /* ftl_freepart */
1264
1265 static void ftl_notify_add(struct mtd_info *mtd)
1266 {
1267         partition_t *partition;
1268         int device;
1269
1270         for (device=0; device < MAX_MTD_DEVICES && myparts[device]; device++)
1271                 ;
1272
1273         if (device == MAX_MTD_DEVICES) {
1274                 printk(KERN_NOTICE "Maximum number of FTL partitions reached\n"
1275                        "Not scanning <%s>\n", mtd->name);
1276                 return;
1277         }
1278
1279         partition = kmalloc(sizeof(partition_t), GFP_KERNEL);
1280                 
1281         if (!partition) {
1282                 printk(KERN_WARNING "No memory to scan for FTL on %s\n",
1283                        mtd->name);
1284                 return;
1285         }    
1286
1287         memset(partition, 0, sizeof(partition_t));
1288
1289         partition->mtd = mtd;
1290
1291         if ((scan_header(partition) == 0) && 
1292             (build_maps(partition) == 0)) {
1293                 
1294                 partition->state = FTL_FORMATTED;
1295                 atomic_set(&partition->open, 0);
1296                 myparts[device] = partition;
1297                 ftl_reread_partitions(device << 4);
1298 #ifdef PCMCIA_DEBUG
1299                 printk(KERN_INFO "ftl_cs: opening %d kb FTL partition\n",
1300                        le32_to_cpu(partition->header.FormattedSize) >> 10);
1301 #endif
1302         } else
1303                 kfree(partition);
1304 }
1305
1306 static void ftl_notify_remove(struct mtd_info *mtd)
1307 {
1308         int i,j;
1309
1310         /* Q: What happens if you try to remove a device which has
1311          *    a currently-open FTL partition on it?
1312          *
1313          * A: You don't. The ftl_open routine is responsible for
1314          *    increasing the use count of the driver module which
1315          *    it uses.
1316          */
1317
1318         /* That's the theory, anyway :) */
1319
1320         for (i=0; i< MAX_MTD_DEVICES; i++)
1321                 if (myparts[i] && myparts[i]->mtd == mtd) {
1322
1323                         if (myparts[i]->state == FTL_FORMATTED)
1324                                 ftl_freepart(myparts[i]);
1325                         
1326                         myparts[i]->state = 0;
1327                         for (j=0; j<16; j++) {
1328                                 ftl_gendisk.part[j].nr_sects=0;
1329                                 ftl_gendisk.part[j].start_sect=0;
1330                         }
1331                         kfree(myparts[i]);
1332                         myparts[i] = NULL;
1333                 }
1334 }
1335
1336 int init_ftl(void)
1337 {
1338     int i;
1339
1340     memset(myparts, 0, sizeof(myparts));
1341     
1342     DEBUG(0, "$Id: ftl.c,v 1.39 2001/10/02 15:05:11 dwmw2 Exp $\n");
1343     
1344     if (register_blkdev(FTL_MAJOR, "ftl", &ftl_blk_fops)) {
1345         printk(KERN_NOTICE "ftl_cs: unable to grab major "
1346                "device number!\n");
1347         return -EAGAIN;
1348     }
1349     
1350     for (i = 0; i < MAX_DEV*MAX_PART; i++) {
1351         ftl_hd[i].nr_sects = 0;
1352         ftl_hd[i].start_sect = 0;
1353     }
1354     ftl_gendisk.major = FTL_MAJOR;
1355     blk_init_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR), &do_ftl_request);
1356     add_gendisk(&ftl_gendisk);
1357     
1358     register_mtd_user(&ftl_notifier);
1359     
1360     return 0;
1361 }
1362
1363 static void __exit cleanup_ftl(void)
1364 {
1365     unregister_mtd_user(&ftl_notifier);
1366
1367     unregister_blkdev(FTL_MAJOR, "ftl");
1368     blk_cleanup_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR));
1369     blk_clear(FTL_MAJOR);
1370
1371     del_gendisk(&ftl_gendisk);
1372 }
1373
1374 module_init(init_ftl);
1375 module_exit(cleanup_ftl);
1376
1377
1378 MODULE_LICENSE("Dual MPL/GPL");
1379 MODULE_AUTHOR("David Hinds <dhinds@sonic.net>");
1380 MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices and M-Systems DiskOnChip 1000");