Loading...
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");
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(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(part->header.NumTransferUnits * sizeof(struct xfer_info_t),
212 GFP_KERNEL);
213 if (!part->XferInfo)
214 goto out_EUNInfo;
215
216 xvalid = xtrans = 0;
217 for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) {
218 offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN))
219 << part->header.EraseUnitSize);
220 ret = mtd_read(part->mbd.mtd, offset, sizeof(header), &retval,
221 (unsigned char *)&header);
222
223 if (ret)
224 goto out_XferInfo;
225
226 ret = -1;
227 /* Is this a transfer partition? */
228 hdr_ok = (strcmp(header.DataOrgTuple+3, "FTL100") == 0);
229 if (hdr_ok && (le16_to_cpu(header.LogicalEUN) < part->DataUnits) &&
230 (part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset == 0xffffffff)) {
231 part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset = offset;
232 part->EUNInfo[le16_to_cpu(header.LogicalEUN)].EraseCount =
233 le32_to_cpu(header.EraseCount);
234 xvalid++;
235 } else {
236 if (xtrans == part->header.NumTransferUnits) {
237 printk(KERN_NOTICE "ftl_cs: format error: too many "
238 "transfer units!\n");
239 goto out_XferInfo;
240 }
241 if (hdr_ok && (le16_to_cpu(header.LogicalEUN) == 0xffff)) {
242 part->XferInfo[xtrans].state = XFER_PREPARED;
243 part->XferInfo[xtrans].EraseCount = le32_to_cpu(header.EraseCount);
244 } else {
245 part->XferInfo[xtrans].state = XFER_UNKNOWN;
246 /* Pick anything reasonable for the erase count */
247 part->XferInfo[xtrans].EraseCount =
248 le32_to_cpu(part->header.EraseCount);
249 }
250 part->XferInfo[xtrans].Offset = offset;
251 xtrans++;
252 }
253 }
254 /* Check for format trouble */
255 header = part->header;
256 if ((xtrans != header.NumTransferUnits) ||
257 (xvalid+xtrans != le16_to_cpu(header.NumEraseUnits))) {
258 printk(KERN_NOTICE "ftl_cs: format error: erase units "
259 "don't add up!\n");
260 goto out_XferInfo;
261 }
262
263 /* Set up virtual page map */
264 blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize;
265 part->VirtualBlockMap = vmalloc(blocks * sizeof(uint32_t));
266 if (!part->VirtualBlockMap)
267 goto out_XferInfo;
268
269 memset(part->VirtualBlockMap, 0xff, blocks * sizeof(uint32_t));
270 part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize;
271
272 part->bam_cache = kmalloc(part->BlocksPerUnit * sizeof(uint32_t),
273 GFP_KERNEL);
274 if (!part->bam_cache)
275 goto out_VirtualBlockMap;
276
277 part->bam_index = 0xffff;
278 part->FreeTotal = 0;
279
280 for (i = 0; i < part->DataUnits; i++) {
281 part->EUNInfo[i].Free = 0;
282 part->EUNInfo[i].Deleted = 0;
283 offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset);
284
285 ret = mtd_read(part->mbd.mtd, offset,
286 part->BlocksPerUnit * sizeof(uint32_t), &retval,
287 (unsigned char *)part->bam_cache);
288
289 if (ret)
290 goto out_bam_cache;
291
292 for (j = 0; j < part->BlocksPerUnit; j++) {
293 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) {
294 part->EUNInfo[i].Free++;
295 part->FreeTotal++;
296 } else if ((BLOCK_TYPE(le32_to_cpu(part->bam_cache[j])) == BLOCK_DATA) &&
297 (BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j])) < blocks))
298 part->VirtualBlockMap[BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j]))] =
299 (i << header.EraseUnitSize) + (j << header.BlockSize);
300 else if (BLOCK_DELETED(le32_to_cpu(part->bam_cache[j])))
301 part->EUNInfo[i].Deleted++;
302 }
303 }
304
305 ret = 0;
306 goto out;
307
308out_bam_cache:
309 kfree(part->bam_cache);
310out_VirtualBlockMap:
311 vfree(part->VirtualBlockMap);
312out_XferInfo:
313 kfree(part->XferInfo);
314out_EUNInfo:
315 kfree(part->EUNInfo);
316out:
317 return ret;
318} /* build_maps */
319
320/*======================================================================
321
322 Erase_xfer() schedules an asynchronous erase operation for a
323 transfer unit.
324
325======================================================================*/
326
327static int erase_xfer(partition_t *part,
328 uint16_t xfernum)
329{
330 int ret;
331 struct xfer_info_t *xfer;
332 struct erase_info *erase;
333
334 xfer = &part->XferInfo[xfernum];
335 pr_debug("ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset);
336 xfer->state = XFER_ERASING;
337
338 /* Is there a free erase slot? Always in MTD. */
339
340
341 erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL);
342 if (!erase)
343 return -ENOMEM;
344
345 erase->addr = xfer->Offset;
346 erase->len = 1 << part->header.EraseUnitSize;
347
348 ret = mtd_erase(part->mbd.mtd, erase);
349 if (!ret) {
350 xfer->state = XFER_ERASED;
351 xfer->EraseCount++;
352 } else {
353 xfer->state = XFER_FAILED;
354 pr_notice("ftl_cs: erase failed: err = %d\n", ret);
355 }
356
357 kfree(erase);
358
359 return ret;
360} /* erase_xfer */
361
362/*======================================================================
363
364 Prepare_xfer() takes a freshly erased transfer unit and gives
365 it an appropriate header.
366
367======================================================================*/
368
369static int prepare_xfer(partition_t *part, int i)
370{
371 erase_unit_header_t header;
372 struct xfer_info_t *xfer;
373 int nbam, ret;
374 uint32_t ctl;
375 ssize_t retlen;
376 loff_t offset;
377
378 xfer = &part->XferInfo[i];
379 xfer->state = XFER_FAILED;
380
381 pr_debug("ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset);
382
383 /* Write the transfer unit header */
384 header = part->header;
385 header.LogicalEUN = cpu_to_le16(0xffff);
386 header.EraseCount = cpu_to_le32(xfer->EraseCount);
387
388 ret = mtd_write(part->mbd.mtd, xfer->Offset, sizeof(header), &retlen,
389 (u_char *)&header);
390
391 if (ret) {
392 return ret;
393 }
394
395 /* Write the BAM stub */
396 nbam = DIV_ROUND_UP(part->BlocksPerUnit * sizeof(uint32_t) +
397 le32_to_cpu(part->header.BAMOffset), SECTOR_SIZE);
398
399 offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
400 ctl = cpu_to_le32(BLOCK_CONTROL);
401
402 for (i = 0; i < nbam; i++, offset += sizeof(uint32_t)) {
403
404 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
405 (u_char *)&ctl);
406
407 if (ret)
408 return ret;
409 }
410 xfer->state = XFER_PREPARED;
411 return 0;
412
413} /* prepare_xfer */
414
415/*======================================================================
416
417 Copy_erase_unit() takes a full erase block and a transfer unit,
418 copies everything to the transfer unit, then swaps the block
419 pointers.
420
421 All data blocks are copied to the corresponding blocks in the
422 target unit, so the virtual block map does not need to be
423 updated.
424
425======================================================================*/
426
427static int copy_erase_unit(partition_t *part, uint16_t srcunit,
428 uint16_t xferunit)
429{
430 u_char buf[SECTOR_SIZE];
431 struct eun_info_t *eun;
432 struct xfer_info_t *xfer;
433 uint32_t src, dest, free, i;
434 uint16_t unit;
435 int ret;
436 ssize_t retlen;
437 loff_t offset;
438 uint16_t srcunitswap = cpu_to_le16(srcunit);
439
440 eun = &part->EUNInfo[srcunit];
441 xfer = &part->XferInfo[xferunit];
442 pr_debug("ftl_cs: copying block 0x%x to 0x%x\n",
443 eun->Offset, xfer->Offset);
444
445
446 /* Read current BAM */
447 if (part->bam_index != srcunit) {
448
449 offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
450
451 ret = mtd_read(part->mbd.mtd, offset,
452 part->BlocksPerUnit * sizeof(uint32_t), &retlen,
453 (u_char *)(part->bam_cache));
454
455 /* mark the cache bad, in case we get an error later */
456 part->bam_index = 0xffff;
457
458 if (ret) {
459 printk( KERN_WARNING "ftl: Failed to read BAM cache in copy_erase_unit()!\n");
460 return ret;
461 }
462 }
463
464 /* Write the LogicalEUN for the transfer unit */
465 xfer->state = XFER_UNKNOWN;
466 offset = xfer->Offset + 20; /* Bad! */
467 unit = cpu_to_le16(0x7fff);
468
469 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint16_t), &retlen,
470 (u_char *)&unit);
471
472 if (ret) {
473 printk( KERN_WARNING "ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
474 return ret;
475 }
476
477 /* Copy all data blocks from source unit to transfer unit */
478 src = eun->Offset; dest = xfer->Offset;
479
480 free = 0;
481 ret = 0;
482 for (i = 0; i < part->BlocksPerUnit; i++) {
483 switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) {
484 case BLOCK_CONTROL:
485 /* This gets updated later */
486 break;
487 case BLOCK_DATA:
488 case BLOCK_REPLACEMENT:
489 ret = mtd_read(part->mbd.mtd, src, SECTOR_SIZE, &retlen,
490 (u_char *)buf);
491 if (ret) {
492 printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n");
493 return ret;
494 }
495
496
497 ret = mtd_write(part->mbd.mtd, dest, SECTOR_SIZE, &retlen,
498 (u_char *)buf);
499 if (ret) {
500 printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n");
501 return ret;
502 }
503
504 break;
505 default:
506 /* All other blocks must be free */
507 part->bam_cache[i] = cpu_to_le32(0xffffffff);
508 free++;
509 break;
510 }
511 src += SECTOR_SIZE;
512 dest += SECTOR_SIZE;
513 }
514
515 /* Write the BAM to the transfer unit */
516 ret = mtd_write(part->mbd.mtd,
517 xfer->Offset + le32_to_cpu(part->header.BAMOffset),
518 part->BlocksPerUnit * sizeof(int32_t),
519 &retlen,
520 (u_char *)part->bam_cache);
521 if (ret) {
522 printk( KERN_WARNING "ftl: Error writing BAM in copy_erase_unit\n");
523 return ret;
524 }
525
526
527 /* All clear? Then update the LogicalEUN again */
528 ret = mtd_write(part->mbd.mtd, xfer->Offset + 20, sizeof(uint16_t),
529 &retlen, (u_char *)&srcunitswap);
530
531 if (ret) {
532 printk(KERN_WARNING "ftl: Error writing new LogicalEUN in copy_erase_unit\n");
533 return ret;
534 }
535
536
537 /* Update the maps and usage stats*/
538 swap(xfer->EraseCount, eun->EraseCount);
539 swap(xfer->Offset, eun->Offset);
540 part->FreeTotal -= eun->Free;
541 part->FreeTotal += free;
542 eun->Free = free;
543 eun->Deleted = 0;
544
545 /* Now, the cache should be valid for the new block */
546 part->bam_index = srcunit;
547
548 return 0;
549} /* copy_erase_unit */
550
551/*======================================================================
552
553 reclaim_block() picks a full erase unit and a transfer unit and
554 then calls copy_erase_unit() to copy one to the other. Then, it
555 schedules an erase on the expired block.
556
557 What's a good way to decide which transfer unit and which erase
558 unit to use? Beats me. My way is to always pick the transfer
559 unit with the fewest erases, and usually pick the data unit with
560 the most deleted blocks. But with a small probability, pick the
561 oldest data unit instead. This means that we generally postpone
562 the next reclamation as long as possible, but shuffle static
563 stuff around a bit for wear leveling.
564
565======================================================================*/
566
567static int reclaim_block(partition_t *part)
568{
569 uint16_t i, eun, xfer;
570 uint32_t best;
571 int queued, ret;
572
573 pr_debug("ftl_cs: reclaiming space...\n");
574 pr_debug("NumTransferUnits == %x\n", part->header.NumTransferUnits);
575 /* Pick the least erased transfer unit */
576 best = 0xffffffff; xfer = 0xffff;
577 do {
578 queued = 0;
579 for (i = 0; i < part->header.NumTransferUnits; i++) {
580 int n=0;
581 if (part->XferInfo[i].state == XFER_UNKNOWN) {
582 pr_debug("XferInfo[%d].state == XFER_UNKNOWN\n",i);
583 n=1;
584 erase_xfer(part, i);
585 }
586 if (part->XferInfo[i].state == XFER_ERASING) {
587 pr_debug("XferInfo[%d].state == XFER_ERASING\n",i);
588 n=1;
589 queued = 1;
590 }
591 else if (part->XferInfo[i].state == XFER_ERASED) {
592 pr_debug("XferInfo[%d].state == XFER_ERASED\n",i);
593 n=1;
594 prepare_xfer(part, i);
595 }
596 if (part->XferInfo[i].state == XFER_PREPARED) {
597 pr_debug("XferInfo[%d].state == XFER_PREPARED\n",i);
598 n=1;
599 if (part->XferInfo[i].EraseCount <= best) {
600 best = part->XferInfo[i].EraseCount;
601 xfer = i;
602 }
603 }
604 if (!n)
605 pr_debug("XferInfo[%d].state == %x\n",i, part->XferInfo[i].state);
606
607 }
608 if (xfer == 0xffff) {
609 if (queued) {
610 pr_debug("ftl_cs: waiting for transfer "
611 "unit to be prepared...\n");
612 mtd_sync(part->mbd.mtd);
613 } else {
614 static int ne = 0;
615 if (++ne < 5)
616 printk(KERN_NOTICE "ftl_cs: reclaim failed: no "
617 "suitable transfer units!\n");
618 else
619 pr_debug("ftl_cs: reclaim failed: no "
620 "suitable transfer units!\n");
621
622 return -EIO;
623 }
624 }
625 } while (xfer == 0xffff);
626
627 eun = 0;
628 if ((jiffies % shuffle_freq) == 0) {
629 pr_debug("ftl_cs: recycling freshest block...\n");
630 best = 0xffffffff;
631 for (i = 0; i < part->DataUnits; i++)
632 if (part->EUNInfo[i].EraseCount <= best) {
633 best = part->EUNInfo[i].EraseCount;
634 eun = i;
635 }
636 } else {
637 best = 0;
638 for (i = 0; i < part->DataUnits; i++)
639 if (part->EUNInfo[i].Deleted >= best) {
640 best = part->EUNInfo[i].Deleted;
641 eun = i;
642 }
643 if (best == 0) {
644 static int ne = 0;
645 if (++ne < 5)
646 printk(KERN_NOTICE "ftl_cs: reclaim failed: "
647 "no free blocks!\n");
648 else
649 pr_debug("ftl_cs: reclaim failed: "
650 "no free blocks!\n");
651
652 return -EIO;
653 }
654 }
655 ret = copy_erase_unit(part, eun, xfer);
656 if (!ret)
657 erase_xfer(part, xfer);
658 else
659 printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed!\n");
660 return ret;
661} /* reclaim_block */
662
663/*======================================================================
664
665 Find_free() searches for a free block. If necessary, it updates
666 the BAM cache for the erase unit containing the free block. It
667 returns the block index -- the erase unit is just the currently
668 cached unit. If there are no free blocks, it returns 0 -- this
669 is never a valid data block because it contains the header.
670
671======================================================================*/
672
673#ifdef PSYCHO_DEBUG
674static void dump_lists(partition_t *part)
675{
676 int i;
677 printk(KERN_DEBUG "ftl_cs: Free total = %d\n", part->FreeTotal);
678 for (i = 0; i < part->DataUnits; i++)
679 printk(KERN_DEBUG "ftl_cs: unit %d: %d phys, %d free, "
680 "%d deleted\n", i,
681 part->EUNInfo[i].Offset >> part->header.EraseUnitSize,
682 part->EUNInfo[i].Free, part->EUNInfo[i].Deleted);
683}
684#endif
685
686static uint32_t find_free(partition_t *part)
687{
688 uint16_t stop, eun;
689 uint32_t blk;
690 size_t retlen;
691 int ret;
692
693 /* Find an erase unit with some free space */
694 stop = (part->bam_index == 0xffff) ? 0 : part->bam_index;
695 eun = stop;
696 do {
697 if (part->EUNInfo[eun].Free != 0) break;
698 /* Wrap around at end of table */
699 if (++eun == part->DataUnits) eun = 0;
700 } while (eun != stop);
701
702 if (part->EUNInfo[eun].Free == 0)
703 return 0;
704
705 /* Is this unit's BAM cached? */
706 if (eun != part->bam_index) {
707 /* Invalidate cache */
708 part->bam_index = 0xffff;
709
710 ret = mtd_read(part->mbd.mtd,
711 part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
712 part->BlocksPerUnit * sizeof(uint32_t),
713 &retlen,
714 (u_char *)(part->bam_cache));
715
716 if (ret) {
717 printk(KERN_WARNING"ftl: Error reading BAM in find_free\n");
718 return 0;
719 }
720 part->bam_index = eun;
721 }
722
723 /* Find a free block */
724 for (blk = 0; blk < part->BlocksPerUnit; blk++)
725 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break;
726 if (blk == part->BlocksPerUnit) {
727#ifdef PSYCHO_DEBUG
728 static int ne = 0;
729 if (++ne == 1)
730 dump_lists(part);
731#endif
732 printk(KERN_NOTICE "ftl_cs: bad free list!\n");
733 return 0;
734 }
735 pr_debug("ftl_cs: found free block at %d in %d\n", blk, eun);
736 return blk;
737
738} /* find_free */
739
740
741/*======================================================================
742
743 Read a series of sectors from an FTL partition.
744
745======================================================================*/
746
747static int ftl_read(partition_t *part, caddr_t buffer,
748 u_long sector, u_long nblocks)
749{
750 uint32_t log_addr, bsize;
751 u_long i;
752 int ret;
753 size_t offset, retlen;
754
755 pr_debug("ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
756 part, sector, nblocks);
757 if (!(part->state & FTL_FORMATTED)) {
758 printk(KERN_NOTICE "ftl_cs: bad partition\n");
759 return -EIO;
760 }
761 bsize = 1 << part->header.EraseUnitSize;
762
763 for (i = 0; i < nblocks; i++) {
764 if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) {
765 printk(KERN_NOTICE "ftl_cs: bad read offset\n");
766 return -EIO;
767 }
768 log_addr = part->VirtualBlockMap[sector+i];
769 if (log_addr == 0xffffffff)
770 memset(buffer, 0, SECTOR_SIZE);
771 else {
772 offset = (part->EUNInfo[log_addr / bsize].Offset
773 + (log_addr % bsize));
774 ret = mtd_read(part->mbd.mtd, offset, SECTOR_SIZE, &retlen,
775 (u_char *)buffer);
776
777 if (ret) {
778 printk(KERN_WARNING "Error reading MTD device in ftl_read()\n");
779 return ret;
780 }
781 }
782 buffer += SECTOR_SIZE;
783 }
784 return 0;
785} /* ftl_read */
786
787/*======================================================================
788
789 Write a series of sectors to an FTL partition
790
791======================================================================*/
792
793static int set_bam_entry(partition_t *part, uint32_t log_addr,
794 uint32_t virt_addr)
795{
796 uint32_t bsize, blk, le_virt_addr;
797#ifdef PSYCHO_DEBUG
798 uint32_t old_addr;
799#endif
800 uint16_t eun;
801 int ret;
802 size_t retlen, offset;
803
804 pr_debug("ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
805 part, log_addr, virt_addr);
806 bsize = 1 << part->header.EraseUnitSize;
807 eun = log_addr / bsize;
808 blk = (log_addr % bsize) / SECTOR_SIZE;
809 offset = (part->EUNInfo[eun].Offset + blk * sizeof(uint32_t) +
810 le32_to_cpu(part->header.BAMOffset));
811
812#ifdef PSYCHO_DEBUG
813 ret = mtd_read(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
814 (u_char *)&old_addr);
815 if (ret) {
816 printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
817 return ret;
818 }
819 old_addr = le32_to_cpu(old_addr);
820
821 if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) ||
822 ((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) ||
823 (!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) {
824 static int ne = 0;
825 if (++ne < 5) {
826 printk(KERN_NOTICE "ftl_cs: set_bam_entry() inconsistency!\n");
827 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, old = 0x%x"
828 ", new = 0x%x\n", log_addr, old_addr, virt_addr);
829 }
830 return -EIO;
831 }
832#endif
833 le_virt_addr = cpu_to_le32(virt_addr);
834 if (part->bam_index == eun) {
835#ifdef PSYCHO_DEBUG
836 if (le32_to_cpu(part->bam_cache[blk]) != old_addr) {
837 static int ne = 0;
838 if (++ne < 5) {
839 printk(KERN_NOTICE "ftl_cs: set_bam_entry() "
840 "inconsistency!\n");
841 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, cache"
842 " = 0x%x\n",
843 le32_to_cpu(part->bam_cache[blk]), old_addr);
844 }
845 return -EIO;
846 }
847#endif
848 part->bam_cache[blk] = le_virt_addr;
849 }
850 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
851 (u_char *)&le_virt_addr);
852
853 if (ret) {
854 printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!\n");
855 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, new = 0x%x\n",
856 log_addr, virt_addr);
857 }
858 return ret;
859} /* set_bam_entry */
860
861static int ftl_write(partition_t *part, caddr_t buffer,
862 u_long sector, u_long nblocks)
863{
864 uint32_t bsize, log_addr, virt_addr, old_addr, blk;
865 u_long i;
866 int ret;
867 size_t retlen, offset;
868
869 pr_debug("ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
870 part, sector, nblocks);
871 if (!(part->state & FTL_FORMATTED)) {
872 printk(KERN_NOTICE "ftl_cs: bad partition\n");
873 return -EIO;
874 }
875 /* See if we need to reclaim space, before we start */
876 while (part->FreeTotal < nblocks) {
877 ret = reclaim_block(part);
878 if (ret)
879 return ret;
880 }
881
882 bsize = 1 << part->header.EraseUnitSize;
883
884 virt_addr = sector * SECTOR_SIZE | BLOCK_DATA;
885 for (i = 0; i < nblocks; i++) {
886 if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) {
887 printk(KERN_NOTICE "ftl_cs: bad write offset\n");
888 return -EIO;
889 }
890
891 /* Grab a free block */
892 blk = find_free(part);
893 if (blk == 0) {
894 static int ne = 0;
895 if (++ne < 5)
896 printk(KERN_NOTICE "ftl_cs: internal error: "
897 "no free blocks!\n");
898 return -ENOSPC;
899 }
900
901 /* Tag the BAM entry, and write the new block */
902 log_addr = part->bam_index * bsize + blk * SECTOR_SIZE;
903 part->EUNInfo[part->bam_index].Free--;
904 part->FreeTotal--;
905 if (set_bam_entry(part, log_addr, 0xfffffffe))
906 return -EIO;
907 part->EUNInfo[part->bam_index].Deleted++;
908 offset = (part->EUNInfo[part->bam_index].Offset +
909 blk * SECTOR_SIZE);
910 ret = mtd_write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, buffer);
911
912 if (ret) {
913 printk(KERN_NOTICE "ftl_cs: block write failed!\n");
914 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, virt_addr"
915 " = 0x%x, Offset = 0x%zx\n", log_addr, virt_addr,
916 offset);
917 return -EIO;
918 }
919
920 /* Only delete the old entry when the new entry is ready */
921 old_addr = part->VirtualBlockMap[sector+i];
922 if (old_addr != 0xffffffff) {
923 part->VirtualBlockMap[sector+i] = 0xffffffff;
924 part->EUNInfo[old_addr/bsize].Deleted++;
925 if (set_bam_entry(part, old_addr, 0))
926 return -EIO;
927 }
928
929 /* Finally, set up the new pointers */
930 if (set_bam_entry(part, log_addr, virt_addr))
931 return -EIO;
932 part->VirtualBlockMap[sector+i] = log_addr;
933 part->EUNInfo[part->bam_index].Deleted--;
934
935 buffer += SECTOR_SIZE;
936 virt_addr += SECTOR_SIZE;
937 }
938 return 0;
939} /* ftl_write */
940
941static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
942{
943 partition_t *part = (void *)dev;
944 u_long sect;
945
946 /* Sort of arbitrary: round size down to 4KiB boundary */
947 sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
948
949 geo->heads = 1;
950 geo->sectors = 8;
951 geo->cylinders = sect >> 3;
952
953 return 0;
954}
955
956static int ftl_readsect(struct mtd_blktrans_dev *dev,
957 unsigned long block, char *buf)
958{
959 return ftl_read((void *)dev, buf, block, 1);
960}
961
962static int ftl_writesect(struct mtd_blktrans_dev *dev,
963 unsigned long block, char *buf)
964{
965 return ftl_write((void *)dev, buf, block, 1);
966}
967
968static int ftl_discardsect(struct mtd_blktrans_dev *dev,
969 unsigned long sector, unsigned nr_sects)
970{
971 partition_t *part = (void *)dev;
972 uint32_t bsize = 1 << part->header.EraseUnitSize;
973
974 pr_debug("FTL erase sector %ld for %d sectors\n",
975 sector, nr_sects);
976
977 while (nr_sects) {
978 uint32_t old_addr = part->VirtualBlockMap[sector];
979 if (old_addr != 0xffffffff) {
980 part->VirtualBlockMap[sector] = 0xffffffff;
981 part->EUNInfo[old_addr/bsize].Deleted++;
982 if (set_bam_entry(part, old_addr, 0))
983 return -EIO;
984 }
985 nr_sects--;
986 sector++;
987 }
988
989 return 0;
990}
991/*====================================================================*/
992
993static void ftl_freepart(partition_t *part)
994{
995 vfree(part->VirtualBlockMap);
996 part->VirtualBlockMap = NULL;
997 kfree(part->EUNInfo);
998 part->EUNInfo = NULL;
999 kfree(part->XferInfo);
1000 part->XferInfo = NULL;
1001 kfree(part->bam_cache);
1002 part->bam_cache = NULL;
1003} /* ftl_freepart */
1004
1005static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
1006{
1007 partition_t *partition;
1008
1009 partition = kzalloc(sizeof(partition_t), GFP_KERNEL);
1010
1011 if (!partition) {
1012 printk(KERN_WARNING "No memory to scan for FTL on %s\n",
1013 mtd->name);
1014 return;
1015 }
1016
1017 partition->mbd.mtd = mtd;
1018
1019 if ((scan_header(partition) == 0) &&
1020 (build_maps(partition) == 0)) {
1021
1022 partition->state = FTL_FORMATTED;
1023#ifdef PCMCIA_DEBUG
1024 printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n",
1025 le32_to_cpu(partition->header.FormattedSize) >> 10);
1026#endif
1027 partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9;
1028
1029 partition->mbd.tr = tr;
1030 partition->mbd.devnum = -1;
1031 if (!add_mtd_blktrans_dev((void *)partition))
1032 return;
1033 }
1034
1035 kfree(partition);
1036}
1037
1038static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
1039{
1040 del_mtd_blktrans_dev(dev);
1041 ftl_freepart((partition_t *)dev);
1042}
1043
1044static struct mtd_blktrans_ops ftl_tr = {
1045 .name = "ftl",
1046 .major = FTL_MAJOR,
1047 .part_bits = PART_BITS,
1048 .blksize = SECTOR_SIZE,
1049 .readsect = ftl_readsect,
1050 .writesect = ftl_writesect,
1051 .discard = ftl_discardsect,
1052 .getgeo = ftl_getgeo,
1053 .add_mtd = ftl_add_mtd,
1054 .remove_dev = ftl_remove_dev,
1055 .owner = THIS_MODULE,
1056};
1057
1058static int __init init_ftl(void)
1059{
1060 return register_mtd_blktrans(&ftl_tr);
1061}
1062
1063static void __exit cleanup_ftl(void)
1064{
1065 deregister_mtd_blktrans(&ftl_tr);
1066}
1067
1068module_init(init_ftl);
1069module_exit(cleanup_ftl);
1070
1071
1072MODULE_LICENSE("Dual MPL/GPL");
1073MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1074MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");