OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [mtd/] [ftl.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.