1179187Sjb/* This version ported to the Linux-MTD system by dwmw2@infradead.org
2179187Sjb *
3179187Sjb * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
4179187Sjb * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
5179187Sjb *
6179187Sjb * Based on:
7179187Sjb */
8179187Sjb/*======================================================================
9179187Sjb
10179187Sjb    A Flash Translation Layer memory card driver
11179187Sjb
12179187Sjb    This driver implements a disk-like block device driver with an
13179187Sjb    apparent block size of 512 bytes for flash memory cards.
14179187Sjb
15179187Sjb    ftl_cs.c 1.62 2000/02/01 00:59:04
16179187Sjb
17179187Sjb    The contents of this file are subject to the Mozilla Public
18179187Sjb    License Version 1.1 (the "License"); you may not use this file
19179187Sjb    except in compliance with the License. You may obtain a copy of
20179187Sjb    the License at http://www.mozilla.org/MPL/
21179187Sjb
22179187Sjb    Software distributed under the License is distributed on an "AS
23179187Sjb    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
24179187Sjb    implied. See the License for the specific language governing
25179187Sjb    rights and limitations under the License.
26179187Sjb
27179187Sjb    The initial developer of the original code is David A. Hinds
28179187Sjb    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
29179187Sjb    are Copyright �� 1999 David A. Hinds.  All Rights Reserved.
30179187Sjb
31179187Sjb    Alternatively, the contents of this file may be used under the
32179187Sjb    terms of the GNU General Public License version 2 (the "GPL"), in
33179187Sjb    which case the provisions of the GPL are applicable instead of the
34179187Sjb    above.  If you wish to allow the use of your version of this file
35179187Sjb    only under the terms of the GPL and not to allow others to use
36179187Sjb    your version of this file under the MPL, indicate your decision
37179187Sjb    by deleting the provisions above and replace them with the notice
38179187Sjb    and other provisions required by the GPL.  If you do not delete
39179187Sjb    the provisions above, a recipient may use your version of this
40179187Sjb    file under either the MPL or the GPL.
41179187Sjb
42179187Sjb    LEGAL NOTE: The FTL format is patented by M-Systems.  They have
43179187Sjb    granted a license for its use with PCMCIA devices:
44179187Sjb
45179187Sjb     "M-Systems grants a royalty-free, non-exclusive license under
46179187Sjb      any presently existing M-Systems intellectual property rights
47179187Sjb      necessary for the design and development of FTL-compatible
48179187Sjb      drivers, file systems and utilities using the data formats with
49179187Sjb      PCMCIA PC Cards as described in the PCMCIA Flash Translation
50179187Sjb      Layer (FTL) Specification."
51179187Sjb
52179187Sjb    Use of the FTL format for non-PCMCIA applications may be an
53179187Sjb    infringement of these patents.  For additional information,
54179187Sjb    contact M-Systems directly. M-Systems since acquired by Sandisk.
55179187Sjb
56179187Sjb======================================================================*/
57179187Sjb#include <linux/mtd/blktrans.h>
58179187Sjb#include <linux/module.h>
59179187Sjb#include <linux/mtd/mtd.h>
60179187Sjb/*#define PSYCHO_DEBUG */
61179187Sjb
62179187Sjb#include <linux/kernel.h>
63179187Sjb#include <linux/ptrace.h>
64179187Sjb#include <linux/slab.h>
65179187Sjb#include <linux/string.h>
66179187Sjb#include <linux/timer.h>
67179187Sjb#include <linux/major.h>
68179187Sjb#include <linux/fs.h>
69179187Sjb#include <linux/init.h>
70179187Sjb#include <linux/hdreg.h>
71179187Sjb#include <linux/vmalloc.h>
72179187Sjb#include <linux/blkpg.h>
73179187Sjb#include <linux/uaccess.h>
74179187Sjb
75179187Sjb#include <linux/mtd/ftl.h>
76179187Sjb
77179187Sjb/*====================================================================*/
78179187Sjb
79179187Sjb/* Parameters that can be set with 'insmod' */
80179187Sjbstatic int shuffle_freq = 50;
81179187Sjbmodule_param(shuffle_freq, int, 0);
82179187Sjb
83179187Sjb/*====================================================================*/
84179187Sjb
85179187Sjb/* Major device # for FTL device */
86179187Sjb#ifndef FTL_MAJOR
87179187Sjb#define FTL_MAJOR	44
88179187Sjb#endif
89179187Sjb
90179187Sjb
91179187Sjb/*====================================================================*/
92179187Sjb
93179187Sjb/* Maximum number of separate memory devices we'll allow */
94179187Sjb#define MAX_DEV		4
95179187Sjb
96179187Sjb/* Maximum number of regions per device */
97179187Sjb#define MAX_REGION	4
98179187Sjb
99179187Sjb/* Maximum number of partitions in an FTL region */
100179187Sjb#define PART_BITS	4
101179187Sjb
102179187Sjb/* Maximum number of outstanding erase requests per socket */
103179187Sjb#define MAX_ERASE	8
104179187Sjb
105179187Sjb/* Sector size -- shouldn't need to change */
106179187Sjb#define SECTOR_SIZE	512
107179187Sjb
108179187Sjb
109179187Sjb/* Each memory region corresponds to a minor device */
110179187Sjbtypedef struct partition_t {
111179187Sjb    struct mtd_blktrans_dev mbd;
112179187Sjb    uint32_t		state;
113179187Sjb    uint32_t		*VirtualBlockMap;
114179187Sjb    uint32_t		FreeTotal;
115179187Sjb    struct eun_info_t {
116179187Sjb	uint32_t		Offset;
117179187Sjb	uint32_t		EraseCount;
118179187Sjb	uint32_t		Free;
119179187Sjb	uint32_t		Deleted;
120179187Sjb    } *EUNInfo;
121179187Sjb    struct xfer_info_t {
122179187Sjb	uint32_t		Offset;
123179187Sjb	uint32_t		EraseCount;
124179187Sjb	uint16_t		state;
125179187Sjb    } *XferInfo;
126179187Sjb    uint16_t		bam_index;
127179187Sjb    uint32_t		*bam_cache;
128179187Sjb    uint16_t		DataUnits;
129179187Sjb    uint32_t		BlocksPerUnit;
130179187Sjb    erase_unit_header_t	header;
131179187Sjb} partition_t;
132179187Sjb
133179187Sjb/* Partition state flags */
134179187Sjb#define FTL_FORMATTED	0x01
135179187Sjb
136179187Sjb/* Transfer unit states */
137179187Sjb#define XFER_UNKNOWN	0x00
138179187Sjb#define XFER_ERASING	0x01
139179187Sjb#define XFER_ERASED	0x02
140179187Sjb#define XFER_PREPARED	0x03
141179187Sjb#define XFER_FAILED	0x04
142179187Sjb
143179187Sjb/*======================================================================
144179187Sjb
145179187Sjb    Scan_header() checks to see if a memory region contains an FTL
146179187Sjb    partition.  build_maps() reads all the erase unit headers, builds
147179187Sjb    the erase unit map, and then builds the virtual page map.
148179187Sjb
149179187Sjb======================================================================*/
150179187Sjb
151179187Sjbstatic int scan_header(partition_t *part)
152179187Sjb{
153179187Sjb    erase_unit_header_t header;
154179187Sjb    loff_t offset, max_offset;
155179187Sjb    size_t ret;
156179187Sjb    int err;
157179187Sjb    part->header.FormattedSize = 0;
158179187Sjb    max_offset = (0x100000<part->mbd.mtd->size)?0x100000:part->mbd.mtd->size;
159179187Sjb    /* Search first megabyte for a valid FTL header */
160179187Sjb    for (offset = 0;
161179187Sjb	 (offset + sizeof(header)) < max_offset;
162179187Sjb	 offset += part->mbd.mtd->erasesize ? : 0x2000) {
163179187Sjb
164179187Sjb	err = mtd_read(part->mbd.mtd, offset, sizeof(header), &ret,
165179187Sjb                       (unsigned char *)&header);
166179187Sjb
167179187Sjb	if (err)
168179187Sjb	    return err;
169179187Sjb
170179187Sjb	if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break;
171179187Sjb    }
172179187Sjb
173179187Sjb    if (offset == max_offset) {
174179187Sjb	printk(KERN_NOTICE "ftl_cs: FTL header not found.\n");
175179187Sjb	return -ENOENT;
176179187Sjb    }
177179187Sjb    if (header.BlockSize != 9 ||
178179187Sjb	(header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) ||
179179187Sjb	(header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) {
180179187Sjb	printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n");
181179187Sjb	return -1;
182179187Sjb    }
183179187Sjb    if ((1 << header.EraseUnitSize) != part->mbd.mtd->erasesize) {
184179187Sjb	printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x\n",
185179187Sjb	       1 << header.EraseUnitSize,part->mbd.mtd->erasesize);
186179187Sjb	return -1;
187179187Sjb    }
188179187Sjb    part->header = header;
189179187Sjb    return 0;
190179187Sjb}
191179187Sjb
192179187Sjbstatic int build_maps(partition_t *part)
193179187Sjb{
194179187Sjb    erase_unit_header_t header;
195252430Skaiw    uint16_t xvalid, xtrans, i;
196179187Sjb    unsigned blocks, j;
197179187Sjb    int hdr_ok, ret = -1;
198179187Sjb    ssize_t retval;
199179187Sjb    loff_t offset;
200179187Sjb
201179187Sjb    /* Set up erase unit maps */
202179187Sjb    part->DataUnits = le16_to_cpu(part->header.NumEraseUnits) -
203179187Sjb	part->header.NumTransferUnits;
204179187Sjb    part->EUNInfo = kmalloc_array(part->DataUnits, sizeof(struct eun_info_t),
205179187Sjb                                  GFP_KERNEL);
206179187Sjb    if (!part->EUNInfo)
207179187Sjb	    goto out;
208179187Sjb    for (i = 0; i < part->DataUnits; i++)
209179187Sjb	part->EUNInfo[i].Offset = 0xffffffff;
210179187Sjb    part->XferInfo =
211179187Sjb	kmalloc_array(part->header.NumTransferUnits,
212179187Sjb                      sizeof(struct xfer_info_t),
213179187Sjb                      GFP_KERNEL);
214179187Sjb    if (!part->XferInfo)
215179187Sjb	    goto out_EUNInfo;
216179187Sjb
217179187Sjb    xvalid = xtrans = 0;
218179187Sjb    for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) {
219179187Sjb	offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN))
220179187Sjb		      << part->header.EraseUnitSize);
221179187Sjb	ret = mtd_read(part->mbd.mtd, offset, sizeof(header), &retval,
222179187Sjb                       (unsigned char *)&header);
223179187Sjb
224179187Sjb	if (ret)
225179187Sjb	    goto out_XferInfo;
226179187Sjb
227179187Sjb	ret = -1;
228179187Sjb	/* Is this a transfer partition? */
229179187Sjb	hdr_ok = (strcmp(header.DataOrgTuple+3, "FTL100") == 0);
230179187Sjb	if (hdr_ok && (le16_to_cpu(header.LogicalEUN) < part->DataUnits) &&
231179187Sjb	    (part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset == 0xffffffff)) {
232179187Sjb	    part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset = offset;
233179187Sjb	    part->EUNInfo[le16_to_cpu(header.LogicalEUN)].EraseCount =
234179187Sjb		le32_to_cpu(header.EraseCount);
235179187Sjb	    xvalid++;
236179187Sjb	} else {
237179187Sjb	    if (xtrans == part->header.NumTransferUnits) {
238179187Sjb		printk(KERN_NOTICE "ftl_cs: format error: too many "
239179187Sjb		       "transfer units!\n");
240179187Sjb		goto out_XferInfo;
241179187Sjb	    }
242179187Sjb	    if (hdr_ok && (le16_to_cpu(header.LogicalEUN) == 0xffff)) {
243179187Sjb		part->XferInfo[xtrans].state = XFER_PREPARED;
244179187Sjb		part->XferInfo[xtrans].EraseCount = le32_to_cpu(header.EraseCount);
245179187Sjb	    } else {
246179187Sjb		part->XferInfo[xtrans].state = XFER_UNKNOWN;
247179187Sjb		/* Pick anything reasonable for the erase count */
248179187Sjb		part->XferInfo[xtrans].EraseCount =
249179187Sjb		    le32_to_cpu(part->header.EraseCount);
250179187Sjb	    }
251179187Sjb	    part->XferInfo[xtrans].Offset = offset;
252179187Sjb	    xtrans++;
253179187Sjb	}
254179187Sjb    }
255179187Sjb    /* Check for format trouble */
256179187Sjb    header = part->header;
257179187Sjb    if ((xtrans != header.NumTransferUnits) ||
258179187Sjb	(xvalid+xtrans != le16_to_cpu(header.NumEraseUnits))) {
259179187Sjb	printk(KERN_NOTICE "ftl_cs: format error: erase units "
260179187Sjb	       "don't add up!\n");
261179187Sjb	goto out_XferInfo;
262179187Sjb    }
263179187Sjb
264179187Sjb    /* Set up virtual page map */
265179187Sjb    blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize;
266179187Sjb    part->VirtualBlockMap = vmalloc(array_size(blocks, sizeof(uint32_t)));
267179187Sjb    if (!part->VirtualBlockMap)
268179187Sjb	    goto out_XferInfo;
269179187Sjb
270179187Sjb    memset(part->VirtualBlockMap, 0xff, blocks * sizeof(uint32_t));
271179187Sjb    part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize;
272179187Sjb
273179187Sjb    part->bam_cache = kmalloc_array(part->BlocksPerUnit, sizeof(uint32_t),
274179187Sjb                                    GFP_KERNEL);
275179187Sjb    if (!part->bam_cache)
276179187Sjb	    goto out_VirtualBlockMap;
277179187Sjb
278179187Sjb    part->bam_index = 0xffff;
279179187Sjb    part->FreeTotal = 0;
280179187Sjb
281179187Sjb    for (i = 0; i < part->DataUnits; i++) {
282179187Sjb	part->EUNInfo[i].Free = 0;
283179187Sjb	part->EUNInfo[i].Deleted = 0;
284179187Sjb	offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset);
285179187Sjb
286179187Sjb	ret = mtd_read(part->mbd.mtd, offset,
287179187Sjb                       part->BlocksPerUnit * sizeof(uint32_t), &retval,
288179187Sjb                       (unsigned char *)part->bam_cache);
289179187Sjb
290179187Sjb	if (ret)
291179187Sjb		goto out_bam_cache;
292179187Sjb
293179187Sjb	for (j = 0; j < part->BlocksPerUnit; j++) {
294179187Sjb	    if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) {
295179187Sjb		part->EUNInfo[i].Free++;
296179187Sjb		part->FreeTotal++;
297179187Sjb	    } else if ((BLOCK_TYPE(le32_to_cpu(part->bam_cache[j])) == BLOCK_DATA) &&
298179187Sjb		     (BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j])) < blocks))
299179187Sjb		part->VirtualBlockMap[BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j]))] =
300179187Sjb		    (i << header.EraseUnitSize) + (j << header.BlockSize);
301179187Sjb	    else if (BLOCK_DELETED(le32_to_cpu(part->bam_cache[j])))
302179187Sjb		part->EUNInfo[i].Deleted++;
303179187Sjb	}
304179187Sjb    }
305179187Sjb
306179187Sjb    ret = 0;
307179187Sjb    goto out;
308179187Sjb
309179187Sjbout_bam_cache:
310179187Sjb    kfree(part->bam_cache);
311179187Sjbout_VirtualBlockMap:
312179187Sjb    vfree(part->VirtualBlockMap);
313179187Sjbout_XferInfo:
314179187Sjb    kfree(part->XferInfo);
315179187Sjbout_EUNInfo:
316179187Sjb    kfree(part->EUNInfo);
317179187Sjbout:
318179187Sjb    return ret;
319179187Sjb} /* build_maps */
320179187Sjb
321179187Sjb/*======================================================================
322179187Sjb
323179187Sjb    Erase_xfer() schedules an asynchronous erase operation for a
324179187Sjb    transfer unit.
325179187Sjb
326179187Sjb======================================================================*/
327179187Sjb
328179187Sjbstatic int erase_xfer(partition_t *part,
329179187Sjb		      uint16_t xfernum)
330179187Sjb{
331179187Sjb    int ret;
332179187Sjb    struct xfer_info_t *xfer;
333179187Sjb    struct erase_info *erase;
334179187Sjb
335179187Sjb    xfer = &part->XferInfo[xfernum];
336179187Sjb    pr_debug("ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset);
337179187Sjb    xfer->state = XFER_ERASING;
338179187Sjb
339179187Sjb    /* Is there a free erase slot? Always in MTD. */
340179187Sjb
341179187Sjb
342179187Sjb    erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL);
343179187Sjb    if (!erase)
344179187Sjb            return -ENOMEM;
345179187Sjb
346179187Sjb    erase->addr = xfer->Offset;
347179187Sjb    erase->len = 1 << part->header.EraseUnitSize;
348179187Sjb
349179187Sjb    ret = mtd_erase(part->mbd.mtd, erase);
350179187Sjb    if (!ret) {
351179187Sjb	xfer->state = XFER_ERASED;
352179187Sjb	xfer->EraseCount++;
353179187Sjb    } else {
354179187Sjb	xfer->state = XFER_FAILED;
355179187Sjb	pr_notice("ftl_cs: erase failed: err = %d\n", ret);
356179187Sjb    }
357179187Sjb
358179187Sjb    kfree(erase);
359179187Sjb
360179187Sjb    return ret;
361179187Sjb} /* erase_xfer */
362179187Sjb
363179187Sjb/*======================================================================
364179187Sjb
365179187Sjb    Prepare_xfer() takes a freshly erased transfer unit and gives
366179187Sjb    it an appropriate header.
367179187Sjb
368179187Sjb======================================================================*/
369179187Sjb
370179187Sjbstatic int prepare_xfer(partition_t *part, int i)
371179187Sjb{
372179187Sjb    erase_unit_header_t header;
373179187Sjb    struct xfer_info_t *xfer;
374179187Sjb    int nbam, ret;
375179187Sjb    uint32_t ctl;
376179187Sjb    ssize_t retlen;
377179187Sjb    loff_t offset;
378179187Sjb
379179187Sjb    xfer = &part->XferInfo[i];
380179187Sjb    xfer->state = XFER_FAILED;
381179187Sjb
382179187Sjb    pr_debug("ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset);
383179187Sjb
384179187Sjb    /* Write the transfer unit header */
385179187Sjb    header = part->header;
386179187Sjb    header.LogicalEUN = cpu_to_le16(0xffff);
387179187Sjb    header.EraseCount = cpu_to_le32(xfer->EraseCount);
388179187Sjb
389179187Sjb    ret = mtd_write(part->mbd.mtd, xfer->Offset, sizeof(header), &retlen,
390179187Sjb                    (u_char *)&header);
391179187Sjb
392179187Sjb    if (ret) {
393179187Sjb	return ret;
394179187Sjb    }
395179187Sjb
396179187Sjb    /* Write the BAM stub */
397179187Sjb    nbam = DIV_ROUND_UP(part->BlocksPerUnit * sizeof(uint32_t) +
398179187Sjb			le32_to_cpu(part->header.BAMOffset), SECTOR_SIZE);
399239872Sdim
400239872Sdim    offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
401239872Sdim    ctl = cpu_to_le32(BLOCK_CONTROL);
402239872Sdim
403179187Sjb    for (i = 0; i < nbam; i++, offset += sizeof(uint32_t)) {
404179187Sjb
405179187Sjb	ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
406179187Sjb                        (u_char *)&ctl);
407179187Sjb
408179187Sjb	if (ret)
409179187Sjb	    return ret;
410179187Sjb    }
411179187Sjb    xfer->state = XFER_PREPARED;
412179187Sjb    return 0;
413179187Sjb
414179187Sjb} /* prepare_xfer */
415179187Sjb
416179187Sjb/*======================================================================
417179187Sjb
418179187Sjb    Copy_erase_unit() takes a full erase block and a transfer unit,
419179187Sjb    copies everything to the transfer unit, then swaps the block
420179187Sjb    pointers.
421179187Sjb
422179187Sjb    All data blocks are copied to the corresponding blocks in the
423179187Sjb    target unit, so the virtual block map does not need to be
424179187Sjb    updated.
425179187Sjb
426179187Sjb======================================================================*/
427179187Sjb
428179187Sjbstatic int copy_erase_unit(partition_t *part, uint16_t srcunit,
429179187Sjb			   uint16_t xferunit)
430179187Sjb{
431179187Sjb    u_char buf[SECTOR_SIZE];
432179187Sjb    struct eun_info_t *eun;
433179187Sjb    struct xfer_info_t *xfer;
434179187Sjb    uint32_t src, dest, free, i;
435179187Sjb    uint16_t unit;
436179187Sjb    int ret;
437179187Sjb    ssize_t retlen;
438179187Sjb    loff_t offset;
439179187Sjb    uint16_t srcunitswap = cpu_to_le16(srcunit);
440179187Sjb
441179187Sjb    eun = &part->EUNInfo[srcunit];
442179187Sjb    xfer = &part->XferInfo[xferunit];
443179187Sjb    pr_debug("ftl_cs: copying block 0x%x to 0x%x\n",
444179187Sjb	  eun->Offset, xfer->Offset);
445179187Sjb
446179187Sjb
447179187Sjb    /* Read current BAM */
448179187Sjb    if (part->bam_index != srcunit) {
449179187Sjb
450179187Sjb	offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
451179187Sjb
452179187Sjb	ret = mtd_read(part->mbd.mtd, offset,
453179187Sjb                       part->BlocksPerUnit * sizeof(uint32_t), &retlen,
454179187Sjb                       (u_char *)(part->bam_cache));
455179187Sjb
456179187Sjb	/* mark the cache bad, in case we get an error later */
457179187Sjb	part->bam_index = 0xffff;
458179187Sjb
459179187Sjb	if (ret) {
460179187Sjb	    printk( KERN_WARNING "ftl: Failed to read BAM cache in copy_erase_unit()!\n");
461179187Sjb	    return ret;
462179187Sjb	}
463179187Sjb    }
464179187Sjb
465179187Sjb    /* Write the LogicalEUN for the transfer unit */
466179187Sjb    xfer->state = XFER_UNKNOWN;
467179187Sjb    offset = xfer->Offset + 20; /* Bad! */
468179187Sjb    unit = cpu_to_le16(0x7fff);
469179187Sjb
470179187Sjb    ret = mtd_write(part->mbd.mtd, offset, sizeof(uint16_t), &retlen,
471179187Sjb                    (u_char *)&unit);
472179187Sjb
473179187Sjb    if (ret) {
474179187Sjb	printk( KERN_WARNING "ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
475179187Sjb	return ret;
476179187Sjb    }
477179187Sjb
478179187Sjb    /* Copy all data blocks from source unit to transfer unit */
479179187Sjb    src = eun->Offset; dest = xfer->Offset;
480179187Sjb
481179187Sjb    free = 0;
482179187Sjb    ret = 0;
483179187Sjb    for (i = 0; i < part->BlocksPerUnit; i++) {
484179187Sjb	switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) {
485179187Sjb	case BLOCK_CONTROL:
486179187Sjb	    /* This gets updated later */
487179187Sjb	    break;
488179187Sjb	case BLOCK_DATA:
489179187Sjb	case BLOCK_REPLACEMENT:
490179187Sjb	    ret = mtd_read(part->mbd.mtd, src, SECTOR_SIZE, &retlen,
491179187Sjb                           (u_char *)buf);
492179187Sjb	    if (ret) {
493179187Sjb		printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n");
494179187Sjb		return ret;
495179187Sjb            }
496179187Sjb
497179187Sjb
498179187Sjb	    ret = mtd_write(part->mbd.mtd, dest, SECTOR_SIZE, &retlen,
499179187Sjb                            (u_char *)buf);
500179187Sjb	    if (ret)  {
501179187Sjb		printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n");
502179187Sjb		return ret;
503179187Sjb            }
504179187Sjb
505179187Sjb	    break;
506179187Sjb	default:
507179187Sjb	    /* All other blocks must be free */
508179187Sjb	    part->bam_cache[i] = cpu_to_le32(0xffffffff);
509179187Sjb	    free++;
510179187Sjb	    break;
511179187Sjb	}
512179187Sjb	src += SECTOR_SIZE;
513179187Sjb	dest += SECTOR_SIZE;
514179187Sjb    }
515179187Sjb
516179187Sjb    /* Write the BAM to the transfer unit */
517179187Sjb    ret = mtd_write(part->mbd.mtd,
518179187Sjb                    xfer->Offset + le32_to_cpu(part->header.BAMOffset),
519179187Sjb                    part->BlocksPerUnit * sizeof(int32_t),
520179187Sjb                    &retlen,
521179187Sjb                    (u_char *)part->bam_cache);
522179187Sjb    if (ret) {
523179187Sjb	printk( KERN_WARNING "ftl: Error writing BAM in copy_erase_unit\n");
524179187Sjb	return ret;
525179187Sjb    }
526179187Sjb
527179187Sjb
528179187Sjb    /* All clear? Then update the LogicalEUN again */
529179187Sjb    ret = mtd_write(part->mbd.mtd, xfer->Offset + 20, sizeof(uint16_t),
530179187Sjb                    &retlen, (u_char *)&srcunitswap);
531179187Sjb
532179187Sjb    if (ret) {
533179187Sjb	printk(KERN_WARNING "ftl: Error writing new LogicalEUN in copy_erase_unit\n");
534179187Sjb	return ret;
535179187Sjb    }
536179187Sjb
537179187Sjb
538179187Sjb    /* Update the maps and usage stats*/
539179187Sjb    swap(xfer->EraseCount, eun->EraseCount);
540179187Sjb    swap(xfer->Offset, eun->Offset);
541179187Sjb    part->FreeTotal -= eun->Free;
542179187Sjb    part->FreeTotal += free;
543179187Sjb    eun->Free = free;
544179187Sjb    eun->Deleted = 0;
545179187Sjb
546179187Sjb    /* Now, the cache should be valid for the new block */
547179187Sjb    part->bam_index = srcunit;
548179187Sjb
549179187Sjb    return 0;
550179187Sjb} /* copy_erase_unit */
551179187Sjb
552179187Sjb/*======================================================================
553179187Sjb
554241844Seadler    reclaim_block() picks a full erase unit and a transfer unit and
555179187Sjb    then calls copy_erase_unit() to copy one to the other.  Then, it
556179187Sjb    schedules an erase on the expired block.
557179187Sjb
558179187Sjb    What's a good way to decide which transfer unit and which erase
559179187Sjb    unit to use?  Beats me.  My way is to always pick the transfer
560179187Sjb    unit with the fewest erases, and usually pick the data unit with
561179187Sjb    the most deleted blocks.  But with a small probability, pick the
562179187Sjb    oldest data unit instead.  This means that we generally postpone
563179187Sjb    the next reclamation as long as possible, but shuffle static
564179187Sjb    stuff around a bit for wear leveling.
565179187Sjb
566179187Sjb======================================================================*/
567179187Sjb
568179187Sjbstatic int reclaim_block(partition_t *part)
569179187Sjb{
570179187Sjb    uint16_t i, eun, xfer;
571179187Sjb    uint32_t best;
572179187Sjb    int queued, ret;
573179187Sjb
574179187Sjb    pr_debug("ftl_cs: reclaiming space...\n");
575179187Sjb    pr_debug("NumTransferUnits == %x\n", part->header.NumTransferUnits);
576179187Sjb    /* Pick the least erased transfer unit */
577179187Sjb    best = 0xffffffff; xfer = 0xffff;
578179187Sjb    do {
579179187Sjb	queued = 0;
580179187Sjb	for (i = 0; i < part->header.NumTransferUnits; i++) {
581179187Sjb	    int n=0;
582179187Sjb	    if (part->XferInfo[i].state == XFER_UNKNOWN) {
583179187Sjb		pr_debug("XferInfo[%d].state == XFER_UNKNOWN\n",i);
584179187Sjb		n=1;
585221569Sobrien		erase_xfer(part, i);
586221569Sobrien	    }
587221569Sobrien	    if (part->XferInfo[i].state == XFER_ERASING) {
588179187Sjb		pr_debug("XferInfo[%d].state == XFER_ERASING\n",i);
589179187Sjb		n=1;
590179187Sjb		queued = 1;
591179187Sjb	    }
592179187Sjb	    else if (part->XferInfo[i].state == XFER_ERASED) {
593179187Sjb		pr_debug("XferInfo[%d].state == XFER_ERASED\n",i);
594179187Sjb		n=1;
595179187Sjb		prepare_xfer(part, i);
596179187Sjb	    }
597179187Sjb	    if (part->XferInfo[i].state == XFER_PREPARED) {
598179187Sjb		pr_debug("XferInfo[%d].state == XFER_PREPARED\n",i);
599179187Sjb		n=1;
600179187Sjb		if (part->XferInfo[i].EraseCount <= best) {
601179187Sjb		    best = part->XferInfo[i].EraseCount;
602179187Sjb		    xfer = i;
603179187Sjb		}
604179187Sjb	    }
605179187Sjb		if (!n)
606179187Sjb		    pr_debug("XferInfo[%d].state == %x\n",i, part->XferInfo[i].state);
607179187Sjb
608179187Sjb	}
609179187Sjb	if (xfer == 0xffff) {
610179187Sjb	    if (queued) {
611179187Sjb		pr_debug("ftl_cs: waiting for transfer "
612179187Sjb		      "unit to be prepared...\n");
613179187Sjb		mtd_sync(part->mbd.mtd);
614179187Sjb	    } else {
615179187Sjb		static int ne = 0;
616179187Sjb		if (++ne < 5)
617179187Sjb		    printk(KERN_NOTICE "ftl_cs: reclaim failed: no "
618179187Sjb			   "suitable transfer units!\n");
619179187Sjb		else
620179187Sjb		    pr_debug("ftl_cs: reclaim failed: no "
621179187Sjb			  "suitable transfer units!\n");
622179187Sjb
623179187Sjb		return -EIO;
624179187Sjb	    }
625179187Sjb	}
626179187Sjb    } while (xfer == 0xffff);
627179187Sjb
628179187Sjb    eun = 0;
629179187Sjb    if ((jiffies % shuffle_freq) == 0) {
630179187Sjb	pr_debug("ftl_cs: recycling freshest block...\n");
631179187Sjb	best = 0xffffffff;
632179187Sjb	for (i = 0; i < part->DataUnits; i++)
633179187Sjb	    if (part->EUNInfo[i].EraseCount <= best) {
634179187Sjb		best = part->EUNInfo[i].EraseCount;
635179187Sjb		eun = i;
636179187Sjb	    }
637179187Sjb    } else {
638179187Sjb	best = 0;
639179187Sjb	for (i = 0; i < part->DataUnits; i++)
640179187Sjb	    if (part->EUNInfo[i].Deleted >= best) {
641179187Sjb		best = part->EUNInfo[i].Deleted;
642179187Sjb		eun = i;
643179187Sjb	    }
644179187Sjb	if (best == 0) {
645179187Sjb	    static int ne = 0;
646179187Sjb	    if (++ne < 5)
647179187Sjb		printk(KERN_NOTICE "ftl_cs: reclaim failed: "
648179187Sjb		       "no free blocks!\n");
649179187Sjb	    else
650179187Sjb		pr_debug("ftl_cs: reclaim failed: "
651179187Sjb		       "no free blocks!\n");
652179187Sjb
653179187Sjb	    return -EIO;
654179187Sjb	}
655179187Sjb    }
656179187Sjb    ret = copy_erase_unit(part, eun, xfer);
657179187Sjb    if (!ret)
658179187Sjb	erase_xfer(part, xfer);
659179187Sjb    else
660179187Sjb	printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed!\n");
661179187Sjb    return ret;
662179187Sjb} /* reclaim_block */
663179187Sjb
664179187Sjb/*======================================================================
665179187Sjb
666179187Sjb    Find_free() searches for a free block.  If necessary, it updates
667179187Sjb    the BAM cache for the erase unit containing the free block.  It
668179187Sjb    returns the block index -- the erase unit is just the currently
669179187Sjb    cached unit.  If there are no free blocks, it returns 0 -- this
670179187Sjb    is never a valid data block because it contains the header.
671179187Sjb
672179187Sjb======================================================================*/
673179187Sjb
674179187Sjb#ifdef PSYCHO_DEBUG
675179187Sjbstatic void dump_lists(partition_t *part)
676179187Sjb{
677179187Sjb    int i;
678179187Sjb    printk(KERN_DEBUG "ftl_cs: Free total = %d\n", part->FreeTotal);
679179187Sjb    for (i = 0; i < part->DataUnits; i++)
680179187Sjb	printk(KERN_DEBUG "ftl_cs:   unit %d: %d phys, %d free, "
681179187Sjb	       "%d deleted\n", i,
682179187Sjb	       part->EUNInfo[i].Offset >> part->header.EraseUnitSize,
683179187Sjb	       part->EUNInfo[i].Free, part->EUNInfo[i].Deleted);
684179187Sjb}
685179187Sjb#endif
686179187Sjb
687179187Sjbstatic uint32_t find_free(partition_t *part)
688179187Sjb{
689179187Sjb    uint16_t stop, eun;
690179187Sjb    uint32_t blk;
691179187Sjb    size_t retlen;
692179187Sjb    int ret;
693179187Sjb
694179187Sjb    /* Find an erase unit with some free space */
695179187Sjb    stop = (part->bam_index == 0xffff) ? 0 : part->bam_index;
696221569Sobrien    eun = stop;
697179187Sjb    do {
698179187Sjb	if (part->EUNInfo[eun].Free != 0) break;
699179187Sjb	/* Wrap around at end of table */
700179187Sjb	if (++eun == part->DataUnits) eun = 0;
701179187Sjb    } while (eun != stop);
702179187Sjb
703179187Sjb    if (part->EUNInfo[eun].Free == 0)
704179187Sjb	return 0;
705179187Sjb
706179187Sjb    /* Is this unit's BAM cached? */
707179187Sjb    if (eun != part->bam_index) {
708179187Sjb	/* Invalidate cache */
709179187Sjb	part->bam_index = 0xffff;
710179187Sjb
711179187Sjb	ret = mtd_read(part->mbd.mtd,
712179187Sjb                       part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
713179187Sjb                       part->BlocksPerUnit * sizeof(uint32_t),
714179187Sjb                       &retlen,
715179187Sjb                       (u_char *)(part->bam_cache));
716179187Sjb
717179187Sjb	if (ret) {
718179187Sjb	    printk(KERN_WARNING"ftl: Error reading BAM in find_free\n");
719179187Sjb	    return 0;
720179187Sjb	}
721179187Sjb	part->bam_index = eun;
722179187Sjb    }
723179187Sjb
724179187Sjb    /* Find a free block */
725179187Sjb    for (blk = 0; blk < part->BlocksPerUnit; blk++)
726179187Sjb	if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break;
727179187Sjb    if (blk == part->BlocksPerUnit) {
728179187Sjb#ifdef PSYCHO_DEBUG
729179187Sjb	static int ne = 0;
730179187Sjb	if (++ne == 1)
731179187Sjb	    dump_lists(part);
732179187Sjb#endif
733179187Sjb	printk(KERN_NOTICE "ftl_cs: bad free list!\n");
734179187Sjb	return 0;
735179187Sjb    }
736179187Sjb    pr_debug("ftl_cs: found free block at %d in %d\n", blk, eun);
737179187Sjb    return blk;
738179187Sjb
739179187Sjb} /* find_free */
740179187Sjb
741179187Sjb
742179187Sjb/*======================================================================
743179187Sjb
744179187Sjb    Read a series of sectors from an FTL partition.
745179187Sjb
746179187Sjb======================================================================*/
747179187Sjb
748179187Sjbstatic int ftl_read(partition_t *part, caddr_t buffer,
749179187Sjb		    u_long sector, u_long nblocks)
750179187Sjb{
751179187Sjb    uint32_t log_addr, bsize;
752179187Sjb    u_long i;
753179187Sjb    int ret;
754179187Sjb    size_t offset, retlen;
755179187Sjb
756179187Sjb    pr_debug("ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
757	  part, sector, nblocks);
758    if (!(part->state & FTL_FORMATTED)) {
759	printk(KERN_NOTICE "ftl_cs: bad partition\n");
760	return -EIO;
761    }
762    bsize = 1 << part->header.EraseUnitSize;
763
764    for (i = 0; i < nblocks; i++) {
765	if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) {
766	    printk(KERN_NOTICE "ftl_cs: bad read offset\n");
767	    return -EIO;
768	}
769	log_addr = part->VirtualBlockMap[sector+i];
770	if (log_addr == 0xffffffff)
771	    memset(buffer, 0, SECTOR_SIZE);
772	else {
773	    offset = (part->EUNInfo[log_addr / bsize].Offset
774			  + (log_addr % bsize));
775	    ret = mtd_read(part->mbd.mtd, offset, SECTOR_SIZE, &retlen,
776                           (u_char *)buffer);
777
778	    if (ret) {
779		printk(KERN_WARNING "Error reading MTD device in ftl_read()\n");
780		return ret;
781	    }
782	}
783	buffer += SECTOR_SIZE;
784    }
785    return 0;
786} /* ftl_read */
787
788/*======================================================================
789
790    Write a series of sectors to an FTL partition
791
792======================================================================*/
793
794static int set_bam_entry(partition_t *part, uint32_t log_addr,
795			 uint32_t virt_addr)
796{
797    uint32_t bsize, blk, le_virt_addr;
798#ifdef PSYCHO_DEBUG
799    uint32_t old_addr;
800#endif
801    uint16_t eun;
802    int ret;
803    size_t retlen, offset;
804
805    pr_debug("ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
806	  part, log_addr, virt_addr);
807    bsize = 1 << part->header.EraseUnitSize;
808    eun = log_addr / bsize;
809    blk = (log_addr % bsize) / SECTOR_SIZE;
810    offset = (part->EUNInfo[eun].Offset + blk * sizeof(uint32_t) +
811		  le32_to_cpu(part->header.BAMOffset));
812
813#ifdef PSYCHO_DEBUG
814    ret = mtd_read(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
815                   (u_char *)&old_addr);
816    if (ret) {
817	printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
818	return ret;
819    }
820    old_addr = le32_to_cpu(old_addr);
821
822    if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) ||
823	((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) ||
824	(!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) {
825	static int ne = 0;
826	if (++ne < 5) {
827	    printk(KERN_NOTICE "ftl_cs: set_bam_entry() inconsistency!\n");
828	    printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, old = 0x%x"
829		   ", new = 0x%x\n", log_addr, old_addr, virt_addr);
830	}
831	return -EIO;
832    }
833#endif
834    le_virt_addr = cpu_to_le32(virt_addr);
835    if (part->bam_index == eun) {
836#ifdef PSYCHO_DEBUG
837	if (le32_to_cpu(part->bam_cache[blk]) != old_addr) {
838	    static int ne = 0;
839	    if (++ne < 5) {
840		printk(KERN_NOTICE "ftl_cs: set_bam_entry() "
841		       "inconsistency!\n");
842		printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, cache"
843		       " = 0x%x\n",
844		       le32_to_cpu(part->bam_cache[blk]), old_addr);
845	    }
846	    return -EIO;
847	}
848#endif
849	part->bam_cache[blk] = le_virt_addr;
850    }
851    ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
852                    (u_char *)&le_virt_addr);
853
854    if (ret) {
855	printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!\n");
856	printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, new = 0x%x\n",
857	       log_addr, virt_addr);
858    }
859    return ret;
860} /* set_bam_entry */
861
862static int ftl_write(partition_t *part, caddr_t buffer,
863		     u_long sector, u_long nblocks)
864{
865    uint32_t bsize, log_addr, virt_addr, old_addr, blk;
866    u_long i;
867    int ret;
868    size_t retlen, offset;
869
870    pr_debug("ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
871	  part, sector, nblocks);
872    if (!(part->state & FTL_FORMATTED)) {
873	printk(KERN_NOTICE "ftl_cs: bad partition\n");
874	return -EIO;
875    }
876    /* See if we need to reclaim space, before we start */
877    while (part->FreeTotal < nblocks) {
878	ret = reclaim_block(part);
879	if (ret)
880	    return ret;
881    }
882
883    bsize = 1 << part->header.EraseUnitSize;
884
885    virt_addr = sector * SECTOR_SIZE | BLOCK_DATA;
886    for (i = 0; i < nblocks; i++) {
887	if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) {
888	    printk(KERN_NOTICE "ftl_cs: bad write offset\n");
889	    return -EIO;
890	}
891
892	/* Grab a free block */
893	blk = find_free(part);
894	if (blk == 0) {
895	    static int ne = 0;
896	    if (++ne < 5)
897		printk(KERN_NOTICE "ftl_cs: internal error: "
898		       "no free blocks!\n");
899	    return -ENOSPC;
900	}
901
902	/* Tag the BAM entry, and write the new block */
903	log_addr = part->bam_index * bsize + blk * SECTOR_SIZE;
904	part->EUNInfo[part->bam_index].Free--;
905	part->FreeTotal--;
906	if (set_bam_entry(part, log_addr, 0xfffffffe))
907	    return -EIO;
908	part->EUNInfo[part->bam_index].Deleted++;
909	offset = (part->EUNInfo[part->bam_index].Offset +
910		      blk * SECTOR_SIZE);
911	ret = mtd_write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, buffer);
912
913	if (ret) {
914	    printk(KERN_NOTICE "ftl_cs: block write failed!\n");
915	    printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, virt_addr"
916		   " = 0x%x, Offset = 0x%zx\n", log_addr, virt_addr,
917		   offset);
918	    return -EIO;
919	}
920
921	/* Only delete the old entry when the new entry is ready */
922	old_addr = part->VirtualBlockMap[sector+i];
923	if (old_addr != 0xffffffff) {
924	    part->VirtualBlockMap[sector+i] = 0xffffffff;
925	    part->EUNInfo[old_addr/bsize].Deleted++;
926	    if (set_bam_entry(part, old_addr, 0))
927		return -EIO;
928	}
929
930	/* Finally, set up the new pointers */
931	if (set_bam_entry(part, log_addr, virt_addr))
932	    return -EIO;
933	part->VirtualBlockMap[sector+i] = log_addr;
934	part->EUNInfo[part->bam_index].Deleted--;
935
936	buffer += SECTOR_SIZE;
937	virt_addr += SECTOR_SIZE;
938    }
939    return 0;
940} /* ftl_write */
941
942static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
943{
944	partition_t *part = container_of(dev, struct partition_t, mbd);
945	u_long sect;
946
947	/* Sort of arbitrary: round size down to 4KiB boundary */
948	sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
949
950	geo->heads = 1;
951	geo->sectors = 8;
952	geo->cylinders = sect >> 3;
953
954	return 0;
955}
956
957static int ftl_readsect(struct mtd_blktrans_dev *dev,
958			      unsigned long block, char *buf)
959{
960	return ftl_read((void *)dev, buf, block, 1);
961}
962
963static int ftl_writesect(struct mtd_blktrans_dev *dev,
964			      unsigned long block, char *buf)
965{
966	return ftl_write((void *)dev, buf, block, 1);
967}
968
969static int ftl_discardsect(struct mtd_blktrans_dev *dev,
970			   unsigned long sector, unsigned nr_sects)
971{
972	partition_t *part = container_of(dev, struct partition_t, mbd);
973	uint32_t bsize = 1 << part->header.EraseUnitSize;
974
975	pr_debug("FTL erase sector %ld for %d sectors\n",
976	      sector, nr_sects);
977
978	while (nr_sects) {
979		uint32_t old_addr = part->VirtualBlockMap[sector];
980		if (old_addr != 0xffffffff) {
981			part->VirtualBlockMap[sector] = 0xffffffff;
982			part->EUNInfo[old_addr/bsize].Deleted++;
983			if (set_bam_entry(part, old_addr, 0))
984				return -EIO;
985		}
986		nr_sects--;
987		sector++;
988	}
989
990	return 0;
991}
992/*====================================================================*/
993
994static void ftl_freepart(partition_t *part)
995{
996	vfree(part->VirtualBlockMap);
997	part->VirtualBlockMap = NULL;
998	kfree(part->EUNInfo);
999	part->EUNInfo = NULL;
1000	kfree(part->XferInfo);
1001	part->XferInfo = NULL;
1002	kfree(part->bam_cache);
1003	part->bam_cache = NULL;
1004} /* ftl_freepart */
1005
1006static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
1007{
1008	partition_t *partition;
1009
1010	partition = kzalloc(sizeof(partition_t), GFP_KERNEL);
1011
1012	if (!partition) {
1013		printk(KERN_WARNING "No memory to scan for FTL on %s\n",
1014		       mtd->name);
1015		return;
1016	}
1017
1018	partition->mbd.mtd = mtd;
1019
1020	if ((scan_header(partition) == 0) &&
1021	    (build_maps(partition) == 0)) {
1022
1023		partition->state = FTL_FORMATTED;
1024#ifdef PCMCIA_DEBUG
1025		printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n",
1026		       le32_to_cpu(partition->header.FormattedSize) >> 10);
1027#endif
1028		partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9;
1029
1030		partition->mbd.tr = tr;
1031		partition->mbd.devnum = -1;
1032		if (!add_mtd_blktrans_dev(&partition->mbd))
1033			return;
1034	}
1035
1036	kfree(partition);
1037}
1038
1039static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
1040{
1041	del_mtd_blktrans_dev(dev);
1042	ftl_freepart((partition_t *)dev);
1043}
1044
1045static struct mtd_blktrans_ops ftl_tr = {
1046	.name		= "ftl",
1047	.major		= FTL_MAJOR,
1048	.part_bits	= PART_BITS,
1049	.blksize 	= SECTOR_SIZE,
1050	.readsect	= ftl_readsect,
1051	.writesect	= ftl_writesect,
1052	.discard	= ftl_discardsect,
1053	.getgeo		= ftl_getgeo,
1054	.add_mtd	= ftl_add_mtd,
1055	.remove_dev	= ftl_remove_dev,
1056	.owner		= THIS_MODULE,
1057};
1058
1059module_mtd_blktrans(ftl_tr);
1060
1061MODULE_LICENSE("Dual MPL/GPL");
1062MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1063MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");
1064