Linux Audio

Check our new training course

Loading...
v6.8
   1/* This version ported to the Linux-MTD system by dwmw2@infradead.org
   2 *
   3 * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
   4 * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
   5 *
   6 * Based on:
   7 */
   8/*======================================================================
   9
  10    A Flash Translation Layer memory card driver
  11
  12    This driver implements a disk-like block device driver with an
  13    apparent block size of 512 bytes for flash memory cards.
  14
  15    ftl_cs.c 1.62 2000/02/01 00:59:04
  16
  17    The contents of this file are subject to the Mozilla Public
  18    License Version 1.1 (the "License"); you may not use this file
  19    except in compliance with the License. You may obtain a copy of
  20    the License at http://www.mozilla.org/MPL/
  21
  22    Software distributed under the License is distributed on an "AS
  23    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  24    implied. See the License for the specific language governing
  25    rights and limitations under the License.
  26
  27    The initial developer of the original code is David A. Hinds
  28    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
  29    are Copyright © 1999 David A. Hinds.  All Rights Reserved.
  30
  31    Alternatively, the contents of this file may be used under the
  32    terms of the GNU General Public License version 2 (the "GPL"), in
  33    which case the provisions of the GPL are applicable instead of the
  34    above.  If you wish to allow the use of your version of this file
  35    only under the terms of the GPL and not to allow others to use
  36    your version of this file under the MPL, indicate your decision
  37    by deleting the provisions above and replace them with the notice
  38    and other provisions required by the GPL.  If you do not delete
  39    the provisions above, a recipient may use your version of this
  40    file under either the MPL or the GPL.
  41
  42    LEGAL NOTE: The FTL format is patented by M-Systems.  They have
  43    granted a license for its use with PCMCIA devices:
  44
  45     "M-Systems grants a royalty-free, non-exclusive license under
  46      any presently existing M-Systems intellectual property rights
  47      necessary for the design and development of FTL-compatible
  48      drivers, file systems and utilities using the data formats with
  49      PCMCIA PC Cards as described in the PCMCIA Flash Translation
  50      Layer (FTL) Specification."
  51
  52    Use of the FTL format for non-PCMCIA applications may be an
  53    infringement of these patents.  For additional information,
  54    contact M-Systems directly. M-Systems since acquired by Sandisk. 
  55
  56======================================================================*/
  57#include <linux/mtd/blktrans.h>
  58#include <linux/module.h>
  59#include <linux/mtd/mtd.h>
  60/*#define PSYCHO_DEBUG */
  61
  62#include <linux/kernel.h>
  63#include <linux/ptrace.h>
  64#include <linux/slab.h>
  65#include <linux/string.h>
  66#include <linux/timer.h>
  67#include <linux/major.h>
  68#include <linux/fs.h>
  69#include <linux/init.h>
  70#include <linux/hdreg.h>
  71#include <linux/vmalloc.h>
  72#include <linux/blkpg.h>
  73#include <linux/uaccess.h>
  74
  75#include <linux/mtd/ftl.h>
  76
  77/*====================================================================*/
  78
  79/* Parameters that can be set with 'insmod' */
  80static int shuffle_freq = 50;
  81module_param(shuffle_freq, int, 0);
  82
  83/*====================================================================*/
  84
  85/* Major device # for FTL device */
  86#ifndef FTL_MAJOR
  87#define FTL_MAJOR	44
  88#endif
  89
  90
  91/*====================================================================*/
  92
  93/* Maximum number of separate memory devices we'll allow */
  94#define MAX_DEV		4
  95
  96/* Maximum number of regions per device */
  97#define MAX_REGION	4
  98
  99/* Maximum number of partitions in an FTL region */
 100#define PART_BITS	4
 101
 102/* Maximum number of outstanding erase requests per socket */
 103#define MAX_ERASE	8
 104
 105/* Sector size -- shouldn't need to change */
 106#define SECTOR_SIZE	512
 107
 108
 109/* Each memory region corresponds to a minor device */
 110typedef struct partition_t {
 111    struct mtd_blktrans_dev mbd;
 112    uint32_t		state;
 113    uint32_t		*VirtualBlockMap;
 
 114    uint32_t		FreeTotal;
 115    struct eun_info_t {
 116	uint32_t		Offset;
 117	uint32_t		EraseCount;
 118	uint32_t		Free;
 119	uint32_t		Deleted;
 120    } *EUNInfo;
 121    struct xfer_info_t {
 122	uint32_t		Offset;
 123	uint32_t		EraseCount;
 124	uint16_t		state;
 125    } *XferInfo;
 126    uint16_t		bam_index;
 127    uint32_t		*bam_cache;
 128    uint16_t		DataUnits;
 129    uint32_t		BlocksPerUnit;
 130    erase_unit_header_t	header;
 131} partition_t;
 132
 133/* Partition state flags */
 134#define FTL_FORMATTED	0x01
 135
 136/* Transfer unit states */
 137#define XFER_UNKNOWN	0x00
 138#define XFER_ERASING	0x01
 139#define XFER_ERASED	0x02
 140#define XFER_PREPARED	0x03
 141#define XFER_FAILED	0x04
 142
 
 
 
 
 
 
 143/*======================================================================
 144
 145    Scan_header() checks to see if a memory region contains an FTL
 146    partition.  build_maps() reads all the erase unit headers, builds
 147    the erase unit map, and then builds the virtual page map.
 148
 149======================================================================*/
 150
 151static int scan_header(partition_t *part)
 152{
 153    erase_unit_header_t header;
 154    loff_t offset, max_offset;
 155    size_t ret;
 156    int err;
 157    part->header.FormattedSize = 0;
 158    max_offset = (0x100000<part->mbd.mtd->size)?0x100000:part->mbd.mtd->size;
 159    /* Search first megabyte for a valid FTL header */
 160    for (offset = 0;
 161	 (offset + sizeof(header)) < max_offset;
 162	 offset += part->mbd.mtd->erasesize ? : 0x2000) {
 163
 164	err = mtd_read(part->mbd.mtd, offset, sizeof(header), &ret,
 165                       (unsigned char *)&header);
 166
 167	if (err)
 168	    return err;
 169
 170	if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break;
 171    }
 172
 173    if (offset == max_offset) {
 174	printk(KERN_NOTICE "ftl_cs: FTL header not found.\n");
 175	return -ENOENT;
 176    }
 177    if (header.BlockSize != 9 ||
 178	(header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) ||
 179	(header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) {
 180	printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n");
 181	return -1;
 182    }
 183    if ((1 << header.EraseUnitSize) != part->mbd.mtd->erasesize) {
 184	printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x\n",
 185	       1 << header.EraseUnitSize,part->mbd.mtd->erasesize);
 186	return -1;
 187    }
 188    part->header = header;
 189    return 0;
 190}
 191
 192static int build_maps(partition_t *part)
 193{
 194    erase_unit_header_t header;
 195    uint16_t xvalid, xtrans, i;
 196    unsigned blocks, j;
 197    int hdr_ok, ret = -1;
 198    ssize_t retval;
 199    loff_t offset;
 200
 201    /* Set up erase unit maps */
 202    part->DataUnits = le16_to_cpu(part->header.NumEraseUnits) -
 203	part->header.NumTransferUnits;
 204    part->EUNInfo = kmalloc_array(part->DataUnits, sizeof(struct eun_info_t),
 205                                  GFP_KERNEL);
 206    if (!part->EUNInfo)
 207	    goto out;
 208    for (i = 0; i < part->DataUnits; i++)
 209	part->EUNInfo[i].Offset = 0xffffffff;
 210    part->XferInfo =
 211	kmalloc_array(part->header.NumTransferUnits,
 212                      sizeof(struct xfer_info_t),
 213                      GFP_KERNEL);
 214    if (!part->XferInfo)
 215	    goto out_EUNInfo;
 216
 217    xvalid = xtrans = 0;
 218    for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) {
 219	offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN))
 220		      << part->header.EraseUnitSize);
 221	ret = mtd_read(part->mbd.mtd, offset, sizeof(header), &retval,
 222                       (unsigned char *)&header);
 223
 224	if (ret)
 225	    goto out_XferInfo;
 226
 227	ret = -1;
 228	/* Is this a transfer partition? */
 229	hdr_ok = (strcmp(header.DataOrgTuple+3, "FTL100") == 0);
 230	if (hdr_ok && (le16_to_cpu(header.LogicalEUN) < part->DataUnits) &&
 231	    (part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset == 0xffffffff)) {
 232	    part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset = offset;
 233	    part->EUNInfo[le16_to_cpu(header.LogicalEUN)].EraseCount =
 234		le32_to_cpu(header.EraseCount);
 235	    xvalid++;
 236	} else {
 237	    if (xtrans == part->header.NumTransferUnits) {
 238		printk(KERN_NOTICE "ftl_cs: format error: too many "
 239		       "transfer units!\n");
 240		goto out_XferInfo;
 241	    }
 242	    if (hdr_ok && (le16_to_cpu(header.LogicalEUN) == 0xffff)) {
 243		part->XferInfo[xtrans].state = XFER_PREPARED;
 244		part->XferInfo[xtrans].EraseCount = le32_to_cpu(header.EraseCount);
 245	    } else {
 246		part->XferInfo[xtrans].state = XFER_UNKNOWN;
 247		/* Pick anything reasonable for the erase count */
 248		part->XferInfo[xtrans].EraseCount =
 249		    le32_to_cpu(part->header.EraseCount);
 250	    }
 251	    part->XferInfo[xtrans].Offset = offset;
 252	    xtrans++;
 253	}
 254    }
 255    /* Check for format trouble */
 256    header = part->header;
 257    if ((xtrans != header.NumTransferUnits) ||
 258	(xvalid+xtrans != le16_to_cpu(header.NumEraseUnits))) {
 259	printk(KERN_NOTICE "ftl_cs: format error: erase units "
 260	       "don't add up!\n");
 261	goto out_XferInfo;
 262    }
 263
 264    /* Set up virtual page map */
 265    blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize;
 266    part->VirtualBlockMap = vmalloc(array_size(blocks, sizeof(uint32_t)));
 267    if (!part->VirtualBlockMap)
 268	    goto out_XferInfo;
 269
 270    memset(part->VirtualBlockMap, 0xff, blocks * sizeof(uint32_t));
 271    part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize;
 272
 273    part->bam_cache = kmalloc_array(part->BlocksPerUnit, sizeof(uint32_t),
 274                                    GFP_KERNEL);
 275    if (!part->bam_cache)
 276	    goto out_VirtualBlockMap;
 277
 278    part->bam_index = 0xffff;
 279    part->FreeTotal = 0;
 280
 281    for (i = 0; i < part->DataUnits; i++) {
 282	part->EUNInfo[i].Free = 0;
 283	part->EUNInfo[i].Deleted = 0;
 284	offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset);
 285
 286	ret = mtd_read(part->mbd.mtd, offset,
 287                       part->BlocksPerUnit * sizeof(uint32_t), &retval,
 288                       (unsigned char *)part->bam_cache);
 289
 290	if (ret)
 291		goto out_bam_cache;
 292
 293	for (j = 0; j < part->BlocksPerUnit; j++) {
 294	    if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) {
 295		part->EUNInfo[i].Free++;
 296		part->FreeTotal++;
 297	    } else if ((BLOCK_TYPE(le32_to_cpu(part->bam_cache[j])) == BLOCK_DATA) &&
 298		     (BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j])) < blocks))
 299		part->VirtualBlockMap[BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j]))] =
 300		    (i << header.EraseUnitSize) + (j << header.BlockSize);
 301	    else if (BLOCK_DELETED(le32_to_cpu(part->bam_cache[j])))
 302		part->EUNInfo[i].Deleted++;
 303	}
 304    }
 305
 306    ret = 0;
 307    goto out;
 308
 309out_bam_cache:
 310    kfree(part->bam_cache);
 311out_VirtualBlockMap:
 312    vfree(part->VirtualBlockMap);
 313out_XferInfo:
 314    kfree(part->XferInfo);
 315out_EUNInfo:
 316    kfree(part->EUNInfo);
 317out:
 318    return ret;
 319} /* build_maps */
 320
 321/*======================================================================
 322
 323    Erase_xfer() schedules an asynchronous erase operation for a
 324    transfer unit.
 325
 326======================================================================*/
 327
 328static int erase_xfer(partition_t *part,
 329		      uint16_t xfernum)
 330{
 331    int ret;
 332    struct xfer_info_t *xfer;
 333    struct erase_info *erase;
 334
 335    xfer = &part->XferInfo[xfernum];
 336    pr_debug("ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset);
 337    xfer->state = XFER_ERASING;
 338
 339    /* Is there a free erase slot? Always in MTD. */
 340
 341
 342    erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL);
 343    if (!erase)
 344            return -ENOMEM;
 345
 
 
 346    erase->addr = xfer->Offset;
 347    erase->len = 1 << part->header.EraseUnitSize;
 
 348
 349    ret = mtd_erase(part->mbd.mtd, erase);
 350    if (!ret) {
 351	xfer->state = XFER_ERASED;
 352	xfer->EraseCount++;
 353    } else {
 354	xfer->state = XFER_FAILED;
 355	pr_notice("ftl_cs: erase failed: err = %d\n", ret);
 356    }
 357
 358    kfree(erase);
 
 
 
 359
 360    return ret;
 361} /* erase_xfer */
 362
 363/*======================================================================
 364
 365    Prepare_xfer() takes a freshly erased transfer unit and gives
 366    it an appropriate header.
 367
 368======================================================================*/
 369
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 370static int prepare_xfer(partition_t *part, int i)
 371{
 372    erase_unit_header_t header;
 373    struct xfer_info_t *xfer;
 374    int nbam, ret;
 375    uint32_t ctl;
 376    ssize_t retlen;
 377    loff_t offset;
 378
 379    xfer = &part->XferInfo[i];
 380    xfer->state = XFER_FAILED;
 381
 382    pr_debug("ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset);
 383
 384    /* Write the transfer unit header */
 385    header = part->header;
 386    header.LogicalEUN = cpu_to_le16(0xffff);
 387    header.EraseCount = cpu_to_le32(xfer->EraseCount);
 388
 389    ret = mtd_write(part->mbd.mtd, xfer->Offset, sizeof(header), &retlen,
 390                    (u_char *)&header);
 391
 392    if (ret) {
 393	return ret;
 394    }
 395
 396    /* Write the BAM stub */
 397    nbam = DIV_ROUND_UP(part->BlocksPerUnit * sizeof(uint32_t) +
 398			le32_to_cpu(part->header.BAMOffset), SECTOR_SIZE);
 399
 400    offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
 401    ctl = cpu_to_le32(BLOCK_CONTROL);
 402
 403    for (i = 0; i < nbam; i++, offset += sizeof(uint32_t)) {
 404
 405	ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
 406                        (u_char *)&ctl);
 407
 408	if (ret)
 409	    return ret;
 410    }
 411    xfer->state = XFER_PREPARED;
 412    return 0;
 413
 414} /* prepare_xfer */
 415
 416/*======================================================================
 417
 418    Copy_erase_unit() takes a full erase block and a transfer unit,
 419    copies everything to the transfer unit, then swaps the block
 420    pointers.
 421
 422    All data blocks are copied to the corresponding blocks in the
 423    target unit, so the virtual block map does not need to be
 424    updated.
 425
 426======================================================================*/
 427
 428static int copy_erase_unit(partition_t *part, uint16_t srcunit,
 429			   uint16_t xferunit)
 430{
 431    u_char buf[SECTOR_SIZE];
 432    struct eun_info_t *eun;
 433    struct xfer_info_t *xfer;
 434    uint32_t src, dest, free, i;
 435    uint16_t unit;
 436    int ret;
 437    ssize_t retlen;
 438    loff_t offset;
 439    uint16_t srcunitswap = cpu_to_le16(srcunit);
 440
 441    eun = &part->EUNInfo[srcunit];
 442    xfer = &part->XferInfo[xferunit];
 443    pr_debug("ftl_cs: copying block 0x%x to 0x%x\n",
 444	  eun->Offset, xfer->Offset);
 445
 446
 447    /* Read current BAM */
 448    if (part->bam_index != srcunit) {
 449
 450	offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
 451
 452	ret = mtd_read(part->mbd.mtd, offset,
 453                       part->BlocksPerUnit * sizeof(uint32_t), &retlen,
 454                       (u_char *)(part->bam_cache));
 455
 456	/* mark the cache bad, in case we get an error later */
 457	part->bam_index = 0xffff;
 458
 459	if (ret) {
 460	    printk( KERN_WARNING "ftl: Failed to read BAM cache in copy_erase_unit()!\n");
 461	    return ret;
 462	}
 463    }
 464
 465    /* Write the LogicalEUN for the transfer unit */
 466    xfer->state = XFER_UNKNOWN;
 467    offset = xfer->Offset + 20; /* Bad! */
 468    unit = cpu_to_le16(0x7fff);
 469
 470    ret = mtd_write(part->mbd.mtd, offset, sizeof(uint16_t), &retlen,
 471                    (u_char *)&unit);
 472
 473    if (ret) {
 474	printk( KERN_WARNING "ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
 475	return ret;
 476    }
 477
 478    /* Copy all data blocks from source unit to transfer unit */
 479    src = eun->Offset; dest = xfer->Offset;
 480
 481    free = 0;
 482    ret = 0;
 483    for (i = 0; i < part->BlocksPerUnit; i++) {
 484	switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) {
 485	case BLOCK_CONTROL:
 486	    /* This gets updated later */
 487	    break;
 488	case BLOCK_DATA:
 489	case BLOCK_REPLACEMENT:
 490	    ret = mtd_read(part->mbd.mtd, src, SECTOR_SIZE, &retlen,
 491                           (u_char *)buf);
 492	    if (ret) {
 493		printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n");
 494		return ret;
 495            }
 496
 497
 498	    ret = mtd_write(part->mbd.mtd, dest, SECTOR_SIZE, &retlen,
 499                            (u_char *)buf);
 500	    if (ret)  {
 501		printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n");
 502		return ret;
 503            }
 504
 505	    break;
 506	default:
 507	    /* All other blocks must be free */
 508	    part->bam_cache[i] = cpu_to_le32(0xffffffff);
 509	    free++;
 510	    break;
 511	}
 512	src += SECTOR_SIZE;
 513	dest += SECTOR_SIZE;
 514    }
 515
 516    /* Write the BAM to the transfer unit */
 517    ret = mtd_write(part->mbd.mtd,
 518                    xfer->Offset + le32_to_cpu(part->header.BAMOffset),
 519                    part->BlocksPerUnit * sizeof(int32_t),
 520                    &retlen,
 521                    (u_char *)part->bam_cache);
 522    if (ret) {
 523	printk( KERN_WARNING "ftl: Error writing BAM in copy_erase_unit\n");
 524	return ret;
 525    }
 526
 527
 528    /* All clear? Then update the LogicalEUN again */
 529    ret = mtd_write(part->mbd.mtd, xfer->Offset + 20, sizeof(uint16_t),
 530                    &retlen, (u_char *)&srcunitswap);
 531
 532    if (ret) {
 533	printk(KERN_WARNING "ftl: Error writing new LogicalEUN in copy_erase_unit\n");
 534	return ret;
 535    }
 536
 537
 538    /* Update the maps and usage stats*/
 539    swap(xfer->EraseCount, eun->EraseCount);
 540    swap(xfer->Offset, eun->Offset);
 
 
 
 
 541    part->FreeTotal -= eun->Free;
 542    part->FreeTotal += free;
 543    eun->Free = free;
 544    eun->Deleted = 0;
 545
 546    /* Now, the cache should be valid for the new block */
 547    part->bam_index = srcunit;
 548
 549    return 0;
 550} /* copy_erase_unit */
 551
 552/*======================================================================
 553
 554    reclaim_block() picks a full erase unit and a transfer unit and
 555    then calls copy_erase_unit() to copy one to the other.  Then, it
 556    schedules an erase on the expired block.
 557
 558    What's a good way to decide which transfer unit and which erase
 559    unit to use?  Beats me.  My way is to always pick the transfer
 560    unit with the fewest erases, and usually pick the data unit with
 561    the most deleted blocks.  But with a small probability, pick the
 562    oldest data unit instead.  This means that we generally postpone
 563    the next reclamation as long as possible, but shuffle static
 564    stuff around a bit for wear leveling.
 565
 566======================================================================*/
 567
 568static int reclaim_block(partition_t *part)
 569{
 570    uint16_t i, eun, xfer;
 571    uint32_t best;
 572    int queued, ret;
 573
 574    pr_debug("ftl_cs: reclaiming space...\n");
 575    pr_debug("NumTransferUnits == %x\n", part->header.NumTransferUnits);
 576    /* Pick the least erased transfer unit */
 577    best = 0xffffffff; xfer = 0xffff;
 578    do {
 579	queued = 0;
 580	for (i = 0; i < part->header.NumTransferUnits; i++) {
 581	    int n=0;
 582	    if (part->XferInfo[i].state == XFER_UNKNOWN) {
 583		pr_debug("XferInfo[%d].state == XFER_UNKNOWN\n",i);
 584		n=1;
 585		erase_xfer(part, i);
 586	    }
 587	    if (part->XferInfo[i].state == XFER_ERASING) {
 588		pr_debug("XferInfo[%d].state == XFER_ERASING\n",i);
 589		n=1;
 590		queued = 1;
 591	    }
 592	    else if (part->XferInfo[i].state == XFER_ERASED) {
 593		pr_debug("XferInfo[%d].state == XFER_ERASED\n",i);
 594		n=1;
 595		prepare_xfer(part, i);
 596	    }
 597	    if (part->XferInfo[i].state == XFER_PREPARED) {
 598		pr_debug("XferInfo[%d].state == XFER_PREPARED\n",i);
 599		n=1;
 600		if (part->XferInfo[i].EraseCount <= best) {
 601		    best = part->XferInfo[i].EraseCount;
 602		    xfer = i;
 603		}
 604	    }
 605		if (!n)
 606		    pr_debug("XferInfo[%d].state == %x\n",i, part->XferInfo[i].state);
 607
 608	}
 609	if (xfer == 0xffff) {
 610	    if (queued) {
 611		pr_debug("ftl_cs: waiting for transfer "
 612		      "unit to be prepared...\n");
 613		mtd_sync(part->mbd.mtd);
 
 614	    } else {
 615		static int ne = 0;
 616		if (++ne < 5)
 617		    printk(KERN_NOTICE "ftl_cs: reclaim failed: no "
 618			   "suitable transfer units!\n");
 619		else
 620		    pr_debug("ftl_cs: reclaim failed: no "
 621			  "suitable transfer units!\n");
 622
 623		return -EIO;
 624	    }
 625	}
 626    } while (xfer == 0xffff);
 627
 628    eun = 0;
 629    if ((jiffies % shuffle_freq) == 0) {
 630	pr_debug("ftl_cs: recycling freshest block...\n");
 631	best = 0xffffffff;
 632	for (i = 0; i < part->DataUnits; i++)
 633	    if (part->EUNInfo[i].EraseCount <= best) {
 634		best = part->EUNInfo[i].EraseCount;
 635		eun = i;
 636	    }
 637    } else {
 638	best = 0;
 639	for (i = 0; i < part->DataUnits; i++)
 640	    if (part->EUNInfo[i].Deleted >= best) {
 641		best = part->EUNInfo[i].Deleted;
 642		eun = i;
 643	    }
 644	if (best == 0) {
 645	    static int ne = 0;
 646	    if (++ne < 5)
 647		printk(KERN_NOTICE "ftl_cs: reclaim failed: "
 648		       "no free blocks!\n");
 649	    else
 650		pr_debug("ftl_cs: reclaim failed: "
 651		       "no free blocks!\n");
 652
 653	    return -EIO;
 654	}
 655    }
 656    ret = copy_erase_unit(part, eun, xfer);
 657    if (!ret)
 658	erase_xfer(part, xfer);
 659    else
 660	printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed!\n");
 661    return ret;
 662} /* reclaim_block */
 663
 664/*======================================================================
 665
 666    Find_free() searches for a free block.  If necessary, it updates
 667    the BAM cache for the erase unit containing the free block.  It
 668    returns the block index -- the erase unit is just the currently
 669    cached unit.  If there are no free blocks, it returns 0 -- this
 670    is never a valid data block because it contains the header.
 671
 672======================================================================*/
 673
 674#ifdef PSYCHO_DEBUG
 675static void dump_lists(partition_t *part)
 676{
 677    int i;
 678    printk(KERN_DEBUG "ftl_cs: Free total = %d\n", part->FreeTotal);
 679    for (i = 0; i < part->DataUnits; i++)
 680	printk(KERN_DEBUG "ftl_cs:   unit %d: %d phys, %d free, "
 681	       "%d deleted\n", i,
 682	       part->EUNInfo[i].Offset >> part->header.EraseUnitSize,
 683	       part->EUNInfo[i].Free, part->EUNInfo[i].Deleted);
 684}
 685#endif
 686
 687static uint32_t find_free(partition_t *part)
 688{
 689    uint16_t stop, eun;
 690    uint32_t blk;
 691    size_t retlen;
 692    int ret;
 693
 694    /* Find an erase unit with some free space */
 695    stop = (part->bam_index == 0xffff) ? 0 : part->bam_index;
 696    eun = stop;
 697    do {
 698	if (part->EUNInfo[eun].Free != 0) break;
 699	/* Wrap around at end of table */
 700	if (++eun == part->DataUnits) eun = 0;
 701    } while (eun != stop);
 702
 703    if (part->EUNInfo[eun].Free == 0)
 704	return 0;
 705
 706    /* Is this unit's BAM cached? */
 707    if (eun != part->bam_index) {
 708	/* Invalidate cache */
 709	part->bam_index = 0xffff;
 710
 711	ret = mtd_read(part->mbd.mtd,
 712                       part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
 713                       part->BlocksPerUnit * sizeof(uint32_t),
 714                       &retlen,
 715                       (u_char *)(part->bam_cache));
 716
 717	if (ret) {
 718	    printk(KERN_WARNING"ftl: Error reading BAM in find_free\n");
 719	    return 0;
 720	}
 721	part->bam_index = eun;
 722    }
 723
 724    /* Find a free block */
 725    for (blk = 0; blk < part->BlocksPerUnit; blk++)
 726	if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break;
 727    if (blk == part->BlocksPerUnit) {
 728#ifdef PSYCHO_DEBUG
 729	static int ne = 0;
 730	if (++ne == 1)
 731	    dump_lists(part);
 732#endif
 733	printk(KERN_NOTICE "ftl_cs: bad free list!\n");
 734	return 0;
 735    }
 736    pr_debug("ftl_cs: found free block at %d in %d\n", blk, eun);
 737    return blk;
 738
 739} /* find_free */
 740
 741
 742/*======================================================================
 743
 744    Read a series of sectors from an FTL partition.
 745
 746======================================================================*/
 747
 748static int ftl_read(partition_t *part, caddr_t buffer,
 749		    u_long sector, u_long nblocks)
 750{
 751    uint32_t log_addr, bsize;
 752    u_long i;
 753    int ret;
 754    size_t offset, retlen;
 755
 756    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");
v3.1
   1/* This version ported to the Linux-MTD system by dwmw2@infradead.org
   2 *
   3 * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
   4 * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
   5 *
   6 * Based on:
   7 */
   8/*======================================================================
   9
  10    A Flash Translation Layer memory card driver
  11
  12    This driver implements a disk-like block device driver with an
  13    apparent block size of 512 bytes for flash memory cards.
  14
  15    ftl_cs.c 1.62 2000/02/01 00:59:04
  16
  17    The contents of this file are subject to the Mozilla Public
  18    License Version 1.1 (the "License"); you may not use this file
  19    except in compliance with the License. You may obtain a copy of
  20    the License at http://www.mozilla.org/MPL/
  21
  22    Software distributed under the License is distributed on an "AS
  23    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  24    implied. See the License for the specific language governing
  25    rights and limitations under the License.
  26
  27    The initial developer of the original code is David A. Hinds
  28    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
  29    are Copyright © 1999 David A. Hinds.  All Rights Reserved.
  30
  31    Alternatively, the contents of this file may be used under the
  32    terms of the GNU General Public License version 2 (the "GPL"), in
  33    which case the provisions of the GPL are applicable instead of the
  34    above.  If you wish to allow the use of your version of this file
  35    only under the terms of the GPL and not to allow others to use
  36    your version of this file under the MPL, indicate your decision
  37    by deleting the provisions above and replace them with the notice
  38    and other provisions required by the GPL.  If you do not delete
  39    the provisions above, a recipient may use your version of this
  40    file under either the MPL or the GPL.
  41
  42    LEGAL NOTE: The FTL format is patented by M-Systems.  They have
  43    granted a license for its use with PCMCIA devices:
  44
  45     "M-Systems grants a royalty-free, non-exclusive license under
  46      any presently existing M-Systems intellectual property rights
  47      necessary for the design and development of FTL-compatible
  48      drivers, file systems and utilities using the data formats with
  49      PCMCIA PC Cards as described in the PCMCIA Flash Translation
  50      Layer (FTL) Specification."
  51
  52    Use of the FTL format for non-PCMCIA applications may be an
  53    infringement of these patents.  For additional information,
  54    contact M-Systems directly. M-Systems since acquired by Sandisk. 
  55
  56======================================================================*/
  57#include <linux/mtd/blktrans.h>
  58#include <linux/module.h>
  59#include <linux/mtd/mtd.h>
  60/*#define PSYCHO_DEBUG */
  61
  62#include <linux/kernel.h>
  63#include <linux/ptrace.h>
  64#include <linux/slab.h>
  65#include <linux/string.h>
  66#include <linux/timer.h>
  67#include <linux/major.h>
  68#include <linux/fs.h>
  69#include <linux/init.h>
  70#include <linux/hdreg.h>
  71#include <linux/vmalloc.h>
  72#include <linux/blkpg.h>
  73#include <asm/uaccess.h>
  74
  75#include <linux/mtd/ftl.h>
  76
  77/*====================================================================*/
  78
  79/* Parameters that can be set with 'insmod' */
  80static int shuffle_freq = 50;
  81module_param(shuffle_freq, int, 0);
  82
  83/*====================================================================*/
  84
  85/* Major device # for FTL device */
  86#ifndef FTL_MAJOR
  87#define FTL_MAJOR	44
  88#endif
  89
  90
  91/*====================================================================*/
  92
  93/* Maximum number of separate memory devices we'll allow */
  94#define MAX_DEV		4
  95
  96/* Maximum number of regions per device */
  97#define MAX_REGION	4
  98
  99/* Maximum number of partitions in an FTL region */
 100#define PART_BITS	4
 101
 102/* Maximum number of outstanding erase requests per socket */
 103#define MAX_ERASE	8
 104
 105/* Sector size -- shouldn't need to change */
 106#define SECTOR_SIZE	512
 107
 108
 109/* Each memory region corresponds to a minor device */
 110typedef struct partition_t {
 111    struct mtd_blktrans_dev mbd;
 112    uint32_t		state;
 113    uint32_t		*VirtualBlockMap;
 114    uint32_t		*VirtualPageMap;
 115    uint32_t		FreeTotal;
 116    struct eun_info_t {
 117	uint32_t		Offset;
 118	uint32_t		EraseCount;
 119	uint32_t		Free;
 120	uint32_t		Deleted;
 121    } *EUNInfo;
 122    struct xfer_info_t {
 123	uint32_t		Offset;
 124	uint32_t		EraseCount;
 125	uint16_t		state;
 126    } *XferInfo;
 127    uint16_t		bam_index;
 128    uint32_t		*bam_cache;
 129    uint16_t		DataUnits;
 130    uint32_t		BlocksPerUnit;
 131    erase_unit_header_t	header;
 132} partition_t;
 133
 134/* Partition state flags */
 135#define FTL_FORMATTED	0x01
 136
 137/* Transfer unit states */
 138#define XFER_UNKNOWN	0x00
 139#define XFER_ERASING	0x01
 140#define XFER_ERASED	0x02
 141#define XFER_PREPARED	0x03
 142#define XFER_FAILED	0x04
 143
 144/*====================================================================*/
 145
 146
 147static void ftl_erase_callback(struct erase_info *done);
 148
 149
 150/*======================================================================
 151
 152    Scan_header() checks to see if a memory region contains an FTL
 153    partition.  build_maps() reads all the erase unit headers, builds
 154    the erase unit map, and then builds the virtual page map.
 155
 156======================================================================*/
 157
 158static int scan_header(partition_t *part)
 159{
 160    erase_unit_header_t header;
 161    loff_t offset, max_offset;
 162    size_t ret;
 163    int err;
 164    part->header.FormattedSize = 0;
 165    max_offset = (0x100000<part->mbd.mtd->size)?0x100000:part->mbd.mtd->size;
 166    /* Search first megabyte for a valid FTL header */
 167    for (offset = 0;
 168	 (offset + sizeof(header)) < max_offset;
 169	 offset += part->mbd.mtd->erasesize ? : 0x2000) {
 170
 171	err = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &ret,
 172			      (unsigned char *)&header);
 173
 174	if (err)
 175	    return err;
 176
 177	if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break;
 178    }
 179
 180    if (offset == max_offset) {
 181	printk(KERN_NOTICE "ftl_cs: FTL header not found.\n");
 182	return -ENOENT;
 183    }
 184    if (header.BlockSize != 9 ||
 185	(header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) ||
 186	(header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) {
 187	printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n");
 188	return -1;
 189    }
 190    if ((1 << header.EraseUnitSize) != part->mbd.mtd->erasesize) {
 191	printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x\n",
 192	       1 << header.EraseUnitSize,part->mbd.mtd->erasesize);
 193	return -1;
 194    }
 195    part->header = header;
 196    return 0;
 197}
 198
 199static int build_maps(partition_t *part)
 200{
 201    erase_unit_header_t header;
 202    uint16_t xvalid, xtrans, i;
 203    unsigned blocks, j;
 204    int hdr_ok, ret = -1;
 205    ssize_t retval;
 206    loff_t offset;
 207
 208    /* Set up erase unit maps */
 209    part->DataUnits = le16_to_cpu(part->header.NumEraseUnits) -
 210	part->header.NumTransferUnits;
 211    part->EUNInfo = kmalloc(part->DataUnits * sizeof(struct eun_info_t),
 212			    GFP_KERNEL);
 213    if (!part->EUNInfo)
 214	    goto out;
 215    for (i = 0; i < part->DataUnits; i++)
 216	part->EUNInfo[i].Offset = 0xffffffff;
 217    part->XferInfo =
 218	kmalloc(part->header.NumTransferUnits * sizeof(struct xfer_info_t),
 219		GFP_KERNEL);
 
 220    if (!part->XferInfo)
 221	    goto out_EUNInfo;
 222
 223    xvalid = xtrans = 0;
 224    for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) {
 225	offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN))
 226		      << part->header.EraseUnitSize);
 227	ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &retval,
 228			      (unsigned char *)&header);
 229
 230	if (ret)
 231	    goto out_XferInfo;
 232
 233	ret = -1;
 234	/* Is this a transfer partition? */
 235	hdr_ok = (strcmp(header.DataOrgTuple+3, "FTL100") == 0);
 236	if (hdr_ok && (le16_to_cpu(header.LogicalEUN) < part->DataUnits) &&
 237	    (part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset == 0xffffffff)) {
 238	    part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset = offset;
 239	    part->EUNInfo[le16_to_cpu(header.LogicalEUN)].EraseCount =
 240		le32_to_cpu(header.EraseCount);
 241	    xvalid++;
 242	} else {
 243	    if (xtrans == part->header.NumTransferUnits) {
 244		printk(KERN_NOTICE "ftl_cs: format error: too many "
 245		       "transfer units!\n");
 246		goto out_XferInfo;
 247	    }
 248	    if (hdr_ok && (le16_to_cpu(header.LogicalEUN) == 0xffff)) {
 249		part->XferInfo[xtrans].state = XFER_PREPARED;
 250		part->XferInfo[xtrans].EraseCount = le32_to_cpu(header.EraseCount);
 251	    } else {
 252		part->XferInfo[xtrans].state = XFER_UNKNOWN;
 253		/* Pick anything reasonable for the erase count */
 254		part->XferInfo[xtrans].EraseCount =
 255		    le32_to_cpu(part->header.EraseCount);
 256	    }
 257	    part->XferInfo[xtrans].Offset = offset;
 258	    xtrans++;
 259	}
 260    }
 261    /* Check for format trouble */
 262    header = part->header;
 263    if ((xtrans != header.NumTransferUnits) ||
 264	(xvalid+xtrans != le16_to_cpu(header.NumEraseUnits))) {
 265	printk(KERN_NOTICE "ftl_cs: format error: erase units "
 266	       "don't add up!\n");
 267	goto out_XferInfo;
 268    }
 269
 270    /* Set up virtual page map */
 271    blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize;
 272    part->VirtualBlockMap = vmalloc(blocks * sizeof(uint32_t));
 273    if (!part->VirtualBlockMap)
 274	    goto out_XferInfo;
 275
 276    memset(part->VirtualBlockMap, 0xff, blocks * sizeof(uint32_t));
 277    part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize;
 278
 279    part->bam_cache = kmalloc(part->BlocksPerUnit * sizeof(uint32_t),
 280			      GFP_KERNEL);
 281    if (!part->bam_cache)
 282	    goto out_VirtualBlockMap;
 283
 284    part->bam_index = 0xffff;
 285    part->FreeTotal = 0;
 286
 287    for (i = 0; i < part->DataUnits; i++) {
 288	part->EUNInfo[i].Free = 0;
 289	part->EUNInfo[i].Deleted = 0;
 290	offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset);
 291
 292	ret = part->mbd.mtd->read(part->mbd.mtd, offset,
 293			      part->BlocksPerUnit * sizeof(uint32_t), &retval,
 294			      (unsigned char *)part->bam_cache);
 295
 296	if (ret)
 297		goto out_bam_cache;
 298
 299	for (j = 0; j < part->BlocksPerUnit; j++) {
 300	    if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) {
 301		part->EUNInfo[i].Free++;
 302		part->FreeTotal++;
 303	    } else if ((BLOCK_TYPE(le32_to_cpu(part->bam_cache[j])) == BLOCK_DATA) &&
 304		     (BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j])) < blocks))
 305		part->VirtualBlockMap[BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j]))] =
 306		    (i << header.EraseUnitSize) + (j << header.BlockSize);
 307	    else if (BLOCK_DELETED(le32_to_cpu(part->bam_cache[j])))
 308		part->EUNInfo[i].Deleted++;
 309	}
 310    }
 311
 312    ret = 0;
 313    goto out;
 314
 315out_bam_cache:
 316    kfree(part->bam_cache);
 317out_VirtualBlockMap:
 318    vfree(part->VirtualBlockMap);
 319out_XferInfo:
 320    kfree(part->XferInfo);
 321out_EUNInfo:
 322    kfree(part->EUNInfo);
 323out:
 324    return ret;
 325} /* build_maps */
 326
 327/*======================================================================
 328
 329    Erase_xfer() schedules an asynchronous erase operation for a
 330    transfer unit.
 331
 332======================================================================*/
 333
 334static int erase_xfer(partition_t *part,
 335		      uint16_t xfernum)
 336{
 337    int ret;
 338    struct xfer_info_t *xfer;
 339    struct erase_info *erase;
 340
 341    xfer = &part->XferInfo[xfernum];
 342    DEBUG(1, "ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset);
 343    xfer->state = XFER_ERASING;
 344
 345    /* Is there a free erase slot? Always in MTD. */
 346
 347
 348    erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL);
 349    if (!erase)
 350            return -ENOMEM;
 351
 352    erase->mtd = part->mbd.mtd;
 353    erase->callback = ftl_erase_callback;
 354    erase->addr = xfer->Offset;
 355    erase->len = 1 << part->header.EraseUnitSize;
 356    erase->priv = (u_long)part;
 357
 358    ret = part->mbd.mtd->erase(part->mbd.mtd, erase);
 
 
 
 
 
 
 
 359
 360    if (!ret)
 361	    xfer->EraseCount++;
 362    else
 363	    kfree(erase);
 364
 365    return ret;
 366} /* erase_xfer */
 367
 368/*======================================================================
 369
 370    Prepare_xfer() takes a freshly erased transfer unit and gives
 371    it an appropriate header.
 372
 373======================================================================*/
 374
 375static void ftl_erase_callback(struct erase_info *erase)
 376{
 377    partition_t *part;
 378    struct xfer_info_t *xfer;
 379    int i;
 380
 381    /* Look up the transfer unit */
 382    part = (partition_t *)(erase->priv);
 383
 384    for (i = 0; i < part->header.NumTransferUnits; i++)
 385	if (part->XferInfo[i].Offset == erase->addr) break;
 386
 387    if (i == part->header.NumTransferUnits) {
 388	printk(KERN_NOTICE "ftl_cs: internal error: "
 389	       "erase lookup failed!\n");
 390	return;
 391    }
 392
 393    xfer = &part->XferInfo[i];
 394    if (erase->state == MTD_ERASE_DONE)
 395	xfer->state = XFER_ERASED;
 396    else {
 397	xfer->state = XFER_FAILED;
 398	printk(KERN_NOTICE "ftl_cs: erase failed: state = %d\n",
 399	       erase->state);
 400    }
 401
 402    kfree(erase);
 403
 404} /* ftl_erase_callback */
 405
 406static int prepare_xfer(partition_t *part, int i)
 407{
 408    erase_unit_header_t header;
 409    struct xfer_info_t *xfer;
 410    int nbam, ret;
 411    uint32_t ctl;
 412    ssize_t retlen;
 413    loff_t offset;
 414
 415    xfer = &part->XferInfo[i];
 416    xfer->state = XFER_FAILED;
 417
 418    DEBUG(1, "ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset);
 419
 420    /* Write the transfer unit header */
 421    header = part->header;
 422    header.LogicalEUN = cpu_to_le16(0xffff);
 423    header.EraseCount = cpu_to_le32(xfer->EraseCount);
 424
 425    ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset, sizeof(header),
 426			   &retlen, (u_char *)&header);
 427
 428    if (ret) {
 429	return ret;
 430    }
 431
 432    /* Write the BAM stub */
 433    nbam = (part->BlocksPerUnit * sizeof(uint32_t) +
 434	    le32_to_cpu(part->header.BAMOffset) + SECTOR_SIZE - 1) / SECTOR_SIZE;
 435
 436    offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
 437    ctl = cpu_to_le32(BLOCK_CONTROL);
 438
 439    for (i = 0; i < nbam; i++, offset += sizeof(uint32_t)) {
 440
 441	ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(uint32_t),
 442			       &retlen, (u_char *)&ctl);
 443
 444	if (ret)
 445	    return ret;
 446    }
 447    xfer->state = XFER_PREPARED;
 448    return 0;
 449
 450} /* prepare_xfer */
 451
 452/*======================================================================
 453
 454    Copy_erase_unit() takes a full erase block and a transfer unit,
 455    copies everything to the transfer unit, then swaps the block
 456    pointers.
 457
 458    All data blocks are copied to the corresponding blocks in the
 459    target unit, so the virtual block map does not need to be
 460    updated.
 461
 462======================================================================*/
 463
 464static int copy_erase_unit(partition_t *part, uint16_t srcunit,
 465			   uint16_t xferunit)
 466{
 467    u_char buf[SECTOR_SIZE];
 468    struct eun_info_t *eun;
 469    struct xfer_info_t *xfer;
 470    uint32_t src, dest, free, i;
 471    uint16_t unit;
 472    int ret;
 473    ssize_t retlen;
 474    loff_t offset;
 475    uint16_t srcunitswap = cpu_to_le16(srcunit);
 476
 477    eun = &part->EUNInfo[srcunit];
 478    xfer = &part->XferInfo[xferunit];
 479    DEBUG(2, "ftl_cs: copying block 0x%x to 0x%x\n",
 480	  eun->Offset, xfer->Offset);
 481
 482
 483    /* Read current BAM */
 484    if (part->bam_index != srcunit) {
 485
 486	offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
 487
 488	ret = part->mbd.mtd->read(part->mbd.mtd, offset,
 489			      part->BlocksPerUnit * sizeof(uint32_t),
 490			      &retlen, (u_char *) (part->bam_cache));
 491
 492	/* mark the cache bad, in case we get an error later */
 493	part->bam_index = 0xffff;
 494
 495	if (ret) {
 496	    printk( KERN_WARNING "ftl: Failed to read BAM cache in copy_erase_unit()!\n");
 497	    return ret;
 498	}
 499    }
 500
 501    /* Write the LogicalEUN for the transfer unit */
 502    xfer->state = XFER_UNKNOWN;
 503    offset = xfer->Offset + 20; /* Bad! */
 504    unit = cpu_to_le16(0x7fff);
 505
 506    ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(uint16_t),
 507			   &retlen, (u_char *) &unit);
 508
 509    if (ret) {
 510	printk( KERN_WARNING "ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
 511	return ret;
 512    }
 513
 514    /* Copy all data blocks from source unit to transfer unit */
 515    src = eun->Offset; dest = xfer->Offset;
 516
 517    free = 0;
 518    ret = 0;
 519    for (i = 0; i < part->BlocksPerUnit; i++) {
 520	switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) {
 521	case BLOCK_CONTROL:
 522	    /* This gets updated later */
 523	    break;
 524	case BLOCK_DATA:
 525	case BLOCK_REPLACEMENT:
 526	    ret = part->mbd.mtd->read(part->mbd.mtd, src, SECTOR_SIZE,
 527                        &retlen, (u_char *) buf);
 528	    if (ret) {
 529		printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n");
 530		return ret;
 531            }
 532
 533
 534	    ret = part->mbd.mtd->write(part->mbd.mtd, dest, SECTOR_SIZE,
 535                        &retlen, (u_char *) buf);
 536	    if (ret)  {
 537		printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n");
 538		return ret;
 539            }
 540
 541	    break;
 542	default:
 543	    /* All other blocks must be free */
 544	    part->bam_cache[i] = cpu_to_le32(0xffffffff);
 545	    free++;
 546	    break;
 547	}
 548	src += SECTOR_SIZE;
 549	dest += SECTOR_SIZE;
 550    }
 551
 552    /* Write the BAM to the transfer unit */
 553    ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset),
 554                    part->BlocksPerUnit * sizeof(int32_t), &retlen,
 555		    (u_char *)part->bam_cache);
 
 
 556    if (ret) {
 557	printk( KERN_WARNING "ftl: Error writing BAM in copy_erase_unit\n");
 558	return ret;
 559    }
 560
 561
 562    /* All clear? Then update the LogicalEUN again */
 563    ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + 20, sizeof(uint16_t),
 564			   &retlen, (u_char *)&srcunitswap);
 565
 566    if (ret) {
 567	printk(KERN_WARNING "ftl: Error writing new LogicalEUN in copy_erase_unit\n");
 568	return ret;
 569    }
 570
 571
 572    /* Update the maps and usage stats*/
 573    i = xfer->EraseCount;
 574    xfer->EraseCount = eun->EraseCount;
 575    eun->EraseCount = i;
 576    i = xfer->Offset;
 577    xfer->Offset = eun->Offset;
 578    eun->Offset = i;
 579    part->FreeTotal -= eun->Free;
 580    part->FreeTotal += free;
 581    eun->Free = free;
 582    eun->Deleted = 0;
 583
 584    /* Now, the cache should be valid for the new block */
 585    part->bam_index = srcunit;
 586
 587    return 0;
 588} /* copy_erase_unit */
 589
 590/*======================================================================
 591
 592    reclaim_block() picks a full erase unit and a transfer unit and
 593    then calls copy_erase_unit() to copy one to the other.  Then, it
 594    schedules an erase on the expired block.
 595
 596    What's a good way to decide which transfer unit and which erase
 597    unit to use?  Beats me.  My way is to always pick the transfer
 598    unit with the fewest erases, and usually pick the data unit with
 599    the most deleted blocks.  But with a small probability, pick the
 600    oldest data unit instead.  This means that we generally postpone
 601    the next reclaimation as long as possible, but shuffle static
 602    stuff around a bit for wear leveling.
 603
 604======================================================================*/
 605
 606static int reclaim_block(partition_t *part)
 607{
 608    uint16_t i, eun, xfer;
 609    uint32_t best;
 610    int queued, ret;
 611
 612    DEBUG(0, "ftl_cs: reclaiming space...\n");
 613    DEBUG(3, "NumTransferUnits == %x\n", part->header.NumTransferUnits);
 614    /* Pick the least erased transfer unit */
 615    best = 0xffffffff; xfer = 0xffff;
 616    do {
 617	queued = 0;
 618	for (i = 0; i < part->header.NumTransferUnits; i++) {
 619	    int n=0;
 620	    if (part->XferInfo[i].state == XFER_UNKNOWN) {
 621		DEBUG(3,"XferInfo[%d].state == XFER_UNKNOWN\n",i);
 622		n=1;
 623		erase_xfer(part, i);
 624	    }
 625	    if (part->XferInfo[i].state == XFER_ERASING) {
 626		DEBUG(3,"XferInfo[%d].state == XFER_ERASING\n",i);
 627		n=1;
 628		queued = 1;
 629	    }
 630	    else if (part->XferInfo[i].state == XFER_ERASED) {
 631		DEBUG(3,"XferInfo[%d].state == XFER_ERASED\n",i);
 632		n=1;
 633		prepare_xfer(part, i);
 634	    }
 635	    if (part->XferInfo[i].state == XFER_PREPARED) {
 636		DEBUG(3,"XferInfo[%d].state == XFER_PREPARED\n",i);
 637		n=1;
 638		if (part->XferInfo[i].EraseCount <= best) {
 639		    best = part->XferInfo[i].EraseCount;
 640		    xfer = i;
 641		}
 642	    }
 643		if (!n)
 644		    DEBUG(3,"XferInfo[%d].state == %x\n",i, part->XferInfo[i].state);
 645
 646	}
 647	if (xfer == 0xffff) {
 648	    if (queued) {
 649		DEBUG(1, "ftl_cs: waiting for transfer "
 650		      "unit to be prepared...\n");
 651		if (part->mbd.mtd->sync)
 652			part->mbd.mtd->sync(part->mbd.mtd);
 653	    } else {
 654		static int ne = 0;
 655		if (++ne < 5)
 656		    printk(KERN_NOTICE "ftl_cs: reclaim failed: no "
 657			   "suitable transfer units!\n");
 658		else
 659		    DEBUG(1, "ftl_cs: reclaim failed: no "
 660			  "suitable transfer units!\n");
 661
 662		return -EIO;
 663	    }
 664	}
 665    } while (xfer == 0xffff);
 666
 667    eun = 0;
 668    if ((jiffies % shuffle_freq) == 0) {
 669	DEBUG(1, "ftl_cs: recycling freshest block...\n");
 670	best = 0xffffffff;
 671	for (i = 0; i < part->DataUnits; i++)
 672	    if (part->EUNInfo[i].EraseCount <= best) {
 673		best = part->EUNInfo[i].EraseCount;
 674		eun = i;
 675	    }
 676    } else {
 677	best = 0;
 678	for (i = 0; i < part->DataUnits; i++)
 679	    if (part->EUNInfo[i].Deleted >= best) {
 680		best = part->EUNInfo[i].Deleted;
 681		eun = i;
 682	    }
 683	if (best == 0) {
 684	    static int ne = 0;
 685	    if (++ne < 5)
 686		printk(KERN_NOTICE "ftl_cs: reclaim failed: "
 687		       "no free blocks!\n");
 688	    else
 689		DEBUG(1,"ftl_cs: reclaim failed: "
 690		       "no free blocks!\n");
 691
 692	    return -EIO;
 693	}
 694    }
 695    ret = copy_erase_unit(part, eun, xfer);
 696    if (!ret)
 697	erase_xfer(part, xfer);
 698    else
 699	printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed!\n");
 700    return ret;
 701} /* reclaim_block */
 702
 703/*======================================================================
 704
 705    Find_free() searches for a free block.  If necessary, it updates
 706    the BAM cache for the erase unit containing the free block.  It
 707    returns the block index -- the erase unit is just the currently
 708    cached unit.  If there are no free blocks, it returns 0 -- this
 709    is never a valid data block because it contains the header.
 710
 711======================================================================*/
 712
 713#ifdef PSYCHO_DEBUG
 714static void dump_lists(partition_t *part)
 715{
 716    int i;
 717    printk(KERN_DEBUG "ftl_cs: Free total = %d\n", part->FreeTotal);
 718    for (i = 0; i < part->DataUnits; i++)
 719	printk(KERN_DEBUG "ftl_cs:   unit %d: %d phys, %d free, "
 720	       "%d deleted\n", i,
 721	       part->EUNInfo[i].Offset >> part->header.EraseUnitSize,
 722	       part->EUNInfo[i].Free, part->EUNInfo[i].Deleted);
 723}
 724#endif
 725
 726static uint32_t find_free(partition_t *part)
 727{
 728    uint16_t stop, eun;
 729    uint32_t blk;
 730    size_t retlen;
 731    int ret;
 732
 733    /* Find an erase unit with some free space */
 734    stop = (part->bam_index == 0xffff) ? 0 : part->bam_index;
 735    eun = stop;
 736    do {
 737	if (part->EUNInfo[eun].Free != 0) break;
 738	/* Wrap around at end of table */
 739	if (++eun == part->DataUnits) eun = 0;
 740    } while (eun != stop);
 741
 742    if (part->EUNInfo[eun].Free == 0)
 743	return 0;
 744
 745    /* Is this unit's BAM cached? */
 746    if (eun != part->bam_index) {
 747	/* Invalidate cache */
 748	part->bam_index = 0xffff;
 749
 750	ret = part->mbd.mtd->read(part->mbd.mtd,
 751		       part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
 752		       part->BlocksPerUnit * sizeof(uint32_t),
 753		       &retlen, (u_char *) (part->bam_cache));
 
 754
 755	if (ret) {
 756	    printk(KERN_WARNING"ftl: Error reading BAM in find_free\n");
 757	    return 0;
 758	}
 759	part->bam_index = eun;
 760    }
 761
 762    /* Find a free block */
 763    for (blk = 0; blk < part->BlocksPerUnit; blk++)
 764	if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break;
 765    if (blk == part->BlocksPerUnit) {
 766#ifdef PSYCHO_DEBUG
 767	static int ne = 0;
 768	if (++ne == 1)
 769	    dump_lists(part);
 770#endif
 771	printk(KERN_NOTICE "ftl_cs: bad free list!\n");
 772	return 0;
 773    }
 774    DEBUG(2, "ftl_cs: found free block at %d in %d\n", blk, eun);
 775    return blk;
 776
 777} /* find_free */
 778
 779
 780/*======================================================================
 781
 782    Read a series of sectors from an FTL partition.
 783
 784======================================================================*/
 785
 786static int ftl_read(partition_t *part, caddr_t buffer,
 787		    u_long sector, u_long nblocks)
 788{
 789    uint32_t log_addr, bsize;
 790    u_long i;
 791    int ret;
 792    size_t offset, retlen;
 793
 794    DEBUG(2, "ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
 795	  part, sector, nblocks);
 796    if (!(part->state & FTL_FORMATTED)) {
 797	printk(KERN_NOTICE "ftl_cs: bad partition\n");
 798	return -EIO;
 799    }
 800    bsize = 1 << part->header.EraseUnitSize;
 801
 802    for (i = 0; i < nblocks; i++) {
 803	if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) {
 804	    printk(KERN_NOTICE "ftl_cs: bad read offset\n");
 805	    return -EIO;
 806	}
 807	log_addr = part->VirtualBlockMap[sector+i];
 808	if (log_addr == 0xffffffff)
 809	    memset(buffer, 0, SECTOR_SIZE);
 810	else {
 811	    offset = (part->EUNInfo[log_addr / bsize].Offset
 812			  + (log_addr % bsize));
 813	    ret = part->mbd.mtd->read(part->mbd.mtd, offset, SECTOR_SIZE,
 814			   &retlen, (u_char *) buffer);
 815
 816	    if (ret) {
 817		printk(KERN_WARNING "Error reading MTD device in ftl_read()\n");
 818		return ret;
 819	    }
 820	}
 821	buffer += SECTOR_SIZE;
 822    }
 823    return 0;
 824} /* ftl_read */
 825
 826/*======================================================================
 827
 828    Write a series of sectors to an FTL partition
 829
 830======================================================================*/
 831
 832static int set_bam_entry(partition_t *part, uint32_t log_addr,
 833			 uint32_t virt_addr)
 834{
 835    uint32_t bsize, blk, le_virt_addr;
 836#ifdef PSYCHO_DEBUG
 837    uint32_t old_addr;
 838#endif
 839    uint16_t eun;
 840    int ret;
 841    size_t retlen, offset;
 842
 843    DEBUG(2, "ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
 844	  part, log_addr, virt_addr);
 845    bsize = 1 << part->header.EraseUnitSize;
 846    eun = log_addr / bsize;
 847    blk = (log_addr % bsize) / SECTOR_SIZE;
 848    offset = (part->EUNInfo[eun].Offset + blk * sizeof(uint32_t) +
 849		  le32_to_cpu(part->header.BAMOffset));
 850
 851#ifdef PSYCHO_DEBUG
 852    ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(uint32_t),
 853                        &retlen, (u_char *)&old_addr);
 854    if (ret) {
 855	printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
 856	return ret;
 857    }
 858    old_addr = le32_to_cpu(old_addr);
 859
 860    if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) ||
 861	((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) ||
 862	(!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) {
 863	static int ne = 0;
 864	if (++ne < 5) {
 865	    printk(KERN_NOTICE "ftl_cs: set_bam_entry() inconsistency!\n");
 866	    printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, old = 0x%x"
 867		   ", new = 0x%x\n", log_addr, old_addr, virt_addr);
 868	}
 869	return -EIO;
 870    }
 871#endif
 872    le_virt_addr = cpu_to_le32(virt_addr);
 873    if (part->bam_index == eun) {
 874#ifdef PSYCHO_DEBUG
 875	if (le32_to_cpu(part->bam_cache[blk]) != old_addr) {
 876	    static int ne = 0;
 877	    if (++ne < 5) {
 878		printk(KERN_NOTICE "ftl_cs: set_bam_entry() "
 879		       "inconsistency!\n");
 880		printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, cache"
 881		       " = 0x%x\n",
 882		       le32_to_cpu(part->bam_cache[blk]), old_addr);
 883	    }
 884	    return -EIO;
 885	}
 886#endif
 887	part->bam_cache[blk] = le_virt_addr;
 888    }
 889    ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(uint32_t),
 890                            &retlen, (u_char *)&le_virt_addr);
 891
 892    if (ret) {
 893	printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!\n");
 894	printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, new = 0x%x\n",
 895	       log_addr, virt_addr);
 896    }
 897    return ret;
 898} /* set_bam_entry */
 899
 900static int ftl_write(partition_t *part, caddr_t buffer,
 901		     u_long sector, u_long nblocks)
 902{
 903    uint32_t bsize, log_addr, virt_addr, old_addr, blk;
 904    u_long i;
 905    int ret;
 906    size_t retlen, offset;
 907
 908    DEBUG(2, "ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
 909	  part, sector, nblocks);
 910    if (!(part->state & FTL_FORMATTED)) {
 911	printk(KERN_NOTICE "ftl_cs: bad partition\n");
 912	return -EIO;
 913    }
 914    /* See if we need to reclaim space, before we start */
 915    while (part->FreeTotal < nblocks) {
 916	ret = reclaim_block(part);
 917	if (ret)
 918	    return ret;
 919    }
 920
 921    bsize = 1 << part->header.EraseUnitSize;
 922
 923    virt_addr = sector * SECTOR_SIZE | BLOCK_DATA;
 924    for (i = 0; i < nblocks; i++) {
 925	if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) {
 926	    printk(KERN_NOTICE "ftl_cs: bad write offset\n");
 927	    return -EIO;
 928	}
 929
 930	/* Grab a free block */
 931	blk = find_free(part);
 932	if (blk == 0) {
 933	    static int ne = 0;
 934	    if (++ne < 5)
 935		printk(KERN_NOTICE "ftl_cs: internal error: "
 936		       "no free blocks!\n");
 937	    return -ENOSPC;
 938	}
 939
 940	/* Tag the BAM entry, and write the new block */
 941	log_addr = part->bam_index * bsize + blk * SECTOR_SIZE;
 942	part->EUNInfo[part->bam_index].Free--;
 943	part->FreeTotal--;
 944	if (set_bam_entry(part, log_addr, 0xfffffffe))
 945	    return -EIO;
 946	part->EUNInfo[part->bam_index].Deleted++;
 947	offset = (part->EUNInfo[part->bam_index].Offset +
 948		      blk * SECTOR_SIZE);
 949	ret = part->mbd.mtd->write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen,
 950                                     buffer);
 951
 952	if (ret) {
 953	    printk(KERN_NOTICE "ftl_cs: block write failed!\n");
 954	    printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, virt_addr"
 955		   " = 0x%x, Offset = 0x%zx\n", log_addr, virt_addr,
 956		   offset);
 957	    return -EIO;
 958	}
 959
 960	/* Only delete the old entry when the new entry is ready */
 961	old_addr = part->VirtualBlockMap[sector+i];
 962	if (old_addr != 0xffffffff) {
 963	    part->VirtualBlockMap[sector+i] = 0xffffffff;
 964	    part->EUNInfo[old_addr/bsize].Deleted++;
 965	    if (set_bam_entry(part, old_addr, 0))
 966		return -EIO;
 967	}
 968
 969	/* Finally, set up the new pointers */
 970	if (set_bam_entry(part, log_addr, virt_addr))
 971	    return -EIO;
 972	part->VirtualBlockMap[sector+i] = log_addr;
 973	part->EUNInfo[part->bam_index].Deleted--;
 974
 975	buffer += SECTOR_SIZE;
 976	virt_addr += SECTOR_SIZE;
 977    }
 978    return 0;
 979} /* ftl_write */
 980
 981static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
 982{
 983	partition_t *part = (void *)dev;
 984	u_long sect;
 985
 986	/* Sort of arbitrary: round size down to 4KiB boundary */
 987	sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
 988
 989	geo->heads = 1;
 990	geo->sectors = 8;
 991	geo->cylinders = sect >> 3;
 992
 993	return 0;
 994}
 995
 996static int ftl_readsect(struct mtd_blktrans_dev *dev,
 997			      unsigned long block, char *buf)
 998{
 999	return ftl_read((void *)dev, buf, block, 1);
1000}
1001
1002static int ftl_writesect(struct mtd_blktrans_dev *dev,
1003			      unsigned long block, char *buf)
1004{
1005	return ftl_write((void *)dev, buf, block, 1);
1006}
1007
1008static int ftl_discardsect(struct mtd_blktrans_dev *dev,
1009			   unsigned long sector, unsigned nr_sects)
1010{
1011	partition_t *part = (void *)dev;
1012	uint32_t bsize = 1 << part->header.EraseUnitSize;
1013
1014	DEBUG(1, "FTL erase sector %ld for %d sectors\n",
1015	      sector, nr_sects);
1016
1017	while (nr_sects) {
1018		uint32_t old_addr = part->VirtualBlockMap[sector];
1019		if (old_addr != 0xffffffff) {
1020			part->VirtualBlockMap[sector] = 0xffffffff;
1021			part->EUNInfo[old_addr/bsize].Deleted++;
1022			if (set_bam_entry(part, old_addr, 0))
1023				return -EIO;
1024		}
1025		nr_sects--;
1026		sector++;
1027	}
1028
1029	return 0;
1030}
1031/*====================================================================*/
1032
1033static void ftl_freepart(partition_t *part)
1034{
1035	vfree(part->VirtualBlockMap);
1036	part->VirtualBlockMap = NULL;
1037	kfree(part->VirtualPageMap);
1038	part->VirtualPageMap = NULL;
1039	kfree(part->EUNInfo);
1040	part->EUNInfo = NULL;
1041	kfree(part->XferInfo);
1042	part->XferInfo = NULL;
1043	kfree(part->bam_cache);
1044	part->bam_cache = NULL;
1045} /* ftl_freepart */
1046
1047static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
1048{
1049	partition_t *partition;
1050
1051	partition = kzalloc(sizeof(partition_t), GFP_KERNEL);
1052
1053	if (!partition) {
1054		printk(KERN_WARNING "No memory to scan for FTL on %s\n",
1055		       mtd->name);
1056		return;
1057	}
1058
1059	partition->mbd.mtd = mtd;
1060
1061	if ((scan_header(partition) == 0) &&
1062	    (build_maps(partition) == 0)) {
1063
1064		partition->state = FTL_FORMATTED;
1065#ifdef PCMCIA_DEBUG
1066		printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n",
1067		       le32_to_cpu(partition->header.FormattedSize) >> 10);
1068#endif
1069		partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9;
1070
1071		partition->mbd.tr = tr;
1072		partition->mbd.devnum = -1;
1073		if (!add_mtd_blktrans_dev((void *)partition))
1074			return;
1075	}
1076
1077	ftl_freepart(partition);
1078	kfree(partition);
1079}
1080
1081static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
1082{
1083	del_mtd_blktrans_dev(dev);
1084	ftl_freepart((partition_t *)dev);
1085}
1086
1087static struct mtd_blktrans_ops ftl_tr = {
1088	.name		= "ftl",
1089	.major		= FTL_MAJOR,
1090	.part_bits	= PART_BITS,
1091	.blksize 	= SECTOR_SIZE,
1092	.readsect	= ftl_readsect,
1093	.writesect	= ftl_writesect,
1094	.discard	= ftl_discardsect,
1095	.getgeo		= ftl_getgeo,
1096	.add_mtd	= ftl_add_mtd,
1097	.remove_dev	= ftl_remove_dev,
1098	.owner		= THIS_MODULE,
1099};
1100
1101static int __init init_ftl(void)
1102{
1103	return register_mtd_blktrans(&ftl_tr);
1104}
1105
1106static void __exit cleanup_ftl(void)
1107{
1108	deregister_mtd_blktrans(&ftl_tr);
1109}
1110
1111module_init(init_ftl);
1112module_exit(cleanup_ftl);
1113
1114
1115MODULE_LICENSE("Dual MPL/GPL");
1116MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1117MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");