1 /* Linux driver for NAND Flash Translation Layer */
2 /* (c) 1999 Machine Vision Holdings, Inc. */
3 /* Author: David Woodhouse <dwmw2@infradead.org> */
4 /* $Id: nftlcore.c,v 1.98 2005/11/07 11:14:21 gleixner Exp $ */
7 The contents of this file are distributed under the GNU General
8 Public License version 2. The author places no additional
9 restrictions of any kind on it.
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <asm/errno.h>
18 #include <asm/uaccess.h>
19 #include <linux/miscdevice.h>
20 #include <linux/delay.h>
21 #include <linux/slab.h>
22 #include <linux/init.h>
23 #include <linux/hdreg.h>
25 #include <linux/kmod.h>
26 #include <linux/mtd/mtd.h>
27 #include <linux/mtd/nand.h>
28 #include <linux/mtd/nftl.h>
29 #include <linux/mtd/blktrans.h>
31 /* maximum number of loops while examining next block, to have a
32 chance to detect consistency problems (they should never happen
33 because of the checks done in the mounting */
35 #define MAX_LOOPS 10000
38 static void nftl_add_mtd(struct mtd_blktrans_ops
*tr
, struct mtd_info
*mtd
)
40 struct NFTLrecord
*nftl
;
43 if (mtd
->type
!= MTD_NANDFLASH
)
45 /* OK, this is moderately ugly. But probably safe. Alternatives? */
46 if (memcmp(mtd
->name
, "DiskOnChip", 10))
49 if (!mtd
->block_isbad
) {
51 "NFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
52 "Please use the new diskonchip driver under the NAND subsystem.\n");
56 DEBUG(MTD_DEBUG_LEVEL1
, "NFTL: add_mtd for %s\n", mtd
->name
);
58 nftl
= kzalloc(sizeof(struct NFTLrecord
), GFP_KERNEL
);
61 printk(KERN_WARNING
"NFTL: out of memory for data structures\n");
66 nftl
->mbd
.devnum
= -1;
70 if (NFTL_mount(nftl
) < 0) {
71 printk(KERN_WARNING
"NFTL: could not mount device\n");
76 /* OK, it's a new one. Set up all the data structures. */
78 /* Calculate geometry */
79 nftl
->cylinders
= 1024;
82 temp
= nftl
->cylinders
* nftl
->heads
;
83 nftl
->sectors
= nftl
->mbd
.size
/ temp
;
84 if (nftl
->mbd
.size
% temp
) {
86 temp
= nftl
->cylinders
* nftl
->sectors
;
87 nftl
->heads
= nftl
->mbd
.size
/ temp
;
89 if (nftl
->mbd
.size
% temp
) {
91 temp
= nftl
->heads
* nftl
->sectors
;
92 nftl
->cylinders
= nftl
->mbd
.size
/ temp
;
96 if (nftl
->mbd
.size
!= nftl
->heads
* nftl
->cylinders
* nftl
->sectors
) {
99 mbd.size == heads * cylinders * sectors
101 printk(KERN_WARNING
"NFTL: cannot calculate a geometry to "
102 "match size of 0x%lx.\n", nftl
->mbd
.size
);
103 printk(KERN_WARNING
"NFTL: using C:%d H:%d S:%d "
104 "(== 0x%lx sects)\n",
105 nftl
->cylinders
, nftl
->heads
, nftl
->sectors
,
106 (long)nftl
->cylinders
* (long)nftl
->heads
*
107 (long)nftl
->sectors
);
110 if (add_mtd_blktrans_dev(&nftl
->mbd
)) {
111 kfree(nftl
->ReplUnitTable
);
112 kfree(nftl
->EUNtable
);
117 printk(KERN_INFO
"NFTL: Found new nftl%c\n", nftl
->mbd
.devnum
+ 'a');
121 static void nftl_remove_dev(struct mtd_blktrans_dev
*dev
)
123 struct NFTLrecord
*nftl
= (void *)dev
;
125 DEBUG(MTD_DEBUG_LEVEL1
, "NFTL: remove_dev (i=%d)\n", dev
->devnum
);
127 del_mtd_blktrans_dev(dev
);
128 kfree(nftl
->ReplUnitTable
);
129 kfree(nftl
->EUNtable
);
134 * Read oob data from flash
136 int nftl_read_oob(struct mtd_info
*mtd
, loff_t offs
, size_t len
,
137 size_t *retlen
, uint8_t *buf
)
139 struct mtd_oob_ops ops
;
142 ops
.mode
= MTD_OOB_PLACE
;
143 ops
.ooboffs
= offs
& (mtd
->writesize
- 1);
148 res
= mtd
->read_oob(mtd
, offs
& ~(mtd
->writesize
- 1), &ops
);
149 *retlen
= ops
.oobretlen
;
154 * Write oob data to flash
156 int nftl_write_oob(struct mtd_info
*mtd
, loff_t offs
, size_t len
,
157 size_t *retlen
, uint8_t *buf
)
159 struct mtd_oob_ops ops
;
162 ops
.mode
= MTD_OOB_PLACE
;
163 ops
.ooboffs
= offs
& (mtd
->writesize
- 1);
168 res
= mtd
->write_oob(mtd
, offs
& ~(mtd
->writesize
- 1), &ops
);
169 *retlen
= ops
.oobretlen
;
173 #ifdef CONFIG_NFTL_RW
176 * Write data and oob to flash
178 static int nftl_write(struct mtd_info
*mtd
, loff_t offs
, size_t len
,
179 size_t *retlen
, uint8_t *buf
, uint8_t *oob
)
181 struct mtd_oob_ops ops
;
184 ops
.mode
= MTD_OOB_PLACE
;
186 ops
.ooblen
= mtd
->oobsize
;
191 res
= mtd
->write_oob(mtd
, offs
& ~(mtd
->writesize
- 1), &ops
);
192 *retlen
= ops
.retlen
;
196 /* Actual NFTL access routines */
197 /* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
198 * when the give Virtual Unit Chain
200 static u16
NFTL_findfreeblock(struct NFTLrecord
*nftl
, int desperate
)
202 /* For a given Virtual Unit Chain: find or create a free block and
203 add it to the chain */
204 /* We're passed the number of the last EUN in the chain, to save us from
205 having to look it up again */
206 u16 pot
= nftl
->LastFreeEUN
;
207 int silly
= nftl
->nb_blocks
;
209 /* Normally, we force a fold to happen before we run out of free blocks completely */
210 if (!desperate
&& nftl
->numfreeEUNs
< 2) {
211 DEBUG(MTD_DEBUG_LEVEL1
, "NFTL_findfreeblock: there are too few free EUNs\n");
215 /* Scan for a free block */
217 if (nftl
->ReplUnitTable
[pot
] == BLOCK_FREE
) {
218 nftl
->LastFreeEUN
= pot
;
223 /* This will probably point to the MediaHdr unit itself,
224 right at the beginning of the partition. But that unit
225 (and the backup unit too) should have the UCI set
226 up so that it's not selected for overwriting */
227 if (++pot
> nftl
->lastEUN
)
228 pot
= le16_to_cpu(nftl
->MediaHdr
.FirstPhysicalEUN
);
231 printk("Argh! No free blocks found! LastFreeEUN = %d, "
232 "FirstEUN = %d\n", nftl
->LastFreeEUN
,
233 le16_to_cpu(nftl
->MediaHdr
.FirstPhysicalEUN
));
236 } while (pot
!= nftl
->LastFreeEUN
);
241 static u16
NFTL_foldchain (struct NFTLrecord
*nftl
, unsigned thisVUC
, unsigned pendingblock
)
243 struct mtd_info
*mtd
= nftl
->mbd
.mtd
;
244 u16 BlockMap
[MAX_SECTORS_PER_UNIT
];
245 unsigned char BlockLastState
[MAX_SECTORS_PER_UNIT
];
246 unsigned char BlockFreeFound
[MAX_SECTORS_PER_UNIT
];
247 unsigned int thisEUN
;
250 unsigned int targetEUN
;
255 memset(BlockMap
, 0xff, sizeof(BlockMap
));
256 memset(BlockFreeFound
, 0, sizeof(BlockFreeFound
));
258 thisEUN
= nftl
->EUNtable
[thisVUC
];
260 if (thisEUN
== BLOCK_NIL
) {
261 printk(KERN_WARNING
"Trying to fold non-existent "
262 "Virtual Unit Chain %d!\n", thisVUC
);
266 /* Scan to find the Erase Unit which holds the actual data for each
267 512-byte block within the Chain.
270 targetEUN
= BLOCK_NIL
;
271 while (thisEUN
<= nftl
->lastEUN
) {
272 unsigned int status
, foldmark
;
275 for (block
= 0; block
< nftl
->EraseSize
/ 512; block
++) {
276 nftl_read_oob(mtd
, (thisEUN
* nftl
->EraseSize
) +
277 (block
* 512), 16 , &retlen
,
280 foldmark
= oob
.u
.c
.FoldMark
| oob
.u
.c
.FoldMark1
;
281 if (foldmark
== FOLD_MARK_IN_PROGRESS
) {
282 DEBUG(MTD_DEBUG_LEVEL1
,
283 "Write Inhibited on EUN %d\n", thisEUN
);
286 /* There's no other reason not to do inplace,
287 except ones that come later. So we don't need
288 to preserve inplace */
292 status
= oob
.b
.Status
| oob
.b
.Status1
;
293 BlockLastState
[block
] = status
;
297 BlockFreeFound
[block
] = 1;
301 if (!BlockFreeFound
[block
])
302 BlockMap
[block
] = thisEUN
;
305 "SECTOR_USED found after SECTOR_FREE "
306 "in Virtual Unit Chain %d for block %d\n",
310 if (!BlockFreeFound
[block
])
311 BlockMap
[block
] = BLOCK_NIL
;
314 "SECTOR_DELETED found after SECTOR_FREE "
315 "in Virtual Unit Chain %d for block %d\n",
322 printk("Unknown status for block %d in EUN %d: %x\n",
323 block
, thisEUN
, status
);
328 printk(KERN_WARNING
"Infinite loop in Virtual Unit Chain 0x%x\n",
333 thisEUN
= nftl
->ReplUnitTable
[thisEUN
];
337 /* We're being asked to be a fold-in-place. Check
338 that all blocks which actually have data associated
339 with them (i.e. BlockMap[block] != BLOCK_NIL) are
340 either already present or SECTOR_FREE in the target
341 block. If not, we're going to have to fold out-of-place
344 for (block
= 0; block
< nftl
->EraseSize
/ 512 ; block
++) {
345 if (BlockLastState
[block
] != SECTOR_FREE
&&
346 BlockMap
[block
] != BLOCK_NIL
&&
347 BlockMap
[block
] != targetEUN
) {
348 DEBUG(MTD_DEBUG_LEVEL1
, "Setting inplace to 0. VUC %d, "
349 "block %d was %x lastEUN, "
350 "and is in EUN %d (%s) %d\n",
351 thisVUC
, block
, BlockLastState
[block
],
353 BlockMap
[block
]== targetEUN
? "==" : "!=",
360 if (pendingblock
>= (thisVUC
* (nftl
->EraseSize
/ 512)) &&
361 pendingblock
< ((thisVUC
+ 1)* (nftl
->EraseSize
/ 512)) &&
362 BlockLastState
[pendingblock
- (thisVUC
* (nftl
->EraseSize
/ 512))] !=
364 DEBUG(MTD_DEBUG_LEVEL1
, "Pending write not free in EUN %d. "
365 "Folding out of place.\n", targetEUN
);
371 DEBUG(MTD_DEBUG_LEVEL1
, "Cannot fold Virtual Unit Chain %d in place. "
372 "Trying out-of-place\n", thisVUC
);
373 /* We need to find a targetEUN to fold into. */
374 targetEUN
= NFTL_findfreeblock(nftl
, 1);
375 if (targetEUN
== BLOCK_NIL
) {
376 /* Ouch. Now we're screwed. We need to do a
377 fold-in-place of another chain to make room
378 for this one. We need a better way of selecting
379 which chain to fold, because makefreeblock will
380 only ask us to fold the same one again.
383 "NFTL_findfreeblock(desperate) returns 0xffff.\n");
387 /* We put a fold mark in the chain we are folding only if we
388 fold in place to help the mount check code. If we do not fold in
389 place, it is possible to find the valid chain by selecting the
391 oob
.u
.c
.FoldMark
= oob
.u
.c
.FoldMark1
= cpu_to_le16(FOLD_MARK_IN_PROGRESS
);
392 oob
.u
.c
.unused
= 0xffffffff;
393 nftl_write_oob(mtd
, (nftl
->EraseSize
* targetEUN
) + 2 * 512 + 8,
394 8, &retlen
, (char *)&oob
.u
);
397 /* OK. We now know the location of every block in the Virtual Unit Chain,
398 and the Erase Unit into which we are supposed to be copying.
401 DEBUG(MTD_DEBUG_LEVEL1
,"Folding chain %d into unit %d\n", thisVUC
, targetEUN
);
402 for (block
= 0; block
< nftl
->EraseSize
/ 512 ; block
++) {
403 unsigned char movebuf
[512];
406 /* If it's in the target EUN already, or if it's pending write, do nothing */
407 if (BlockMap
[block
] == targetEUN
||
408 (pendingblock
== (thisVUC
* (nftl
->EraseSize
/ 512) + block
))) {
412 /* copy only in non free block (free blocks can only
413 happen in case of media errors or deleted blocks) */
414 if (BlockMap
[block
] == BLOCK_NIL
)
417 ret
= mtd
->read(mtd
, (nftl
->EraseSize
* BlockMap
[block
]) + (block
* 512),
418 512, &retlen
, movebuf
);
419 if (ret
< 0 && ret
!= -EUCLEAN
) {
420 ret
= mtd
->read(mtd
, (nftl
->EraseSize
* BlockMap
[block
])
421 + (block
* 512), 512, &retlen
,
424 printk("Error went away on retry.\n");
426 memset(&oob
, 0xff, sizeof(struct nftl_oob
));
427 oob
.b
.Status
= oob
.b
.Status1
= SECTOR_USED
;
429 nftl_write(nftl
->mbd
.mtd
, (nftl
->EraseSize
* targetEUN
) +
430 (block
* 512), 512, &retlen
, movebuf
, (char *)&oob
);
433 /* add the header so that it is now a valid chain */
434 oob
.u
.a
.VirtUnitNum
= oob
.u
.a
.SpareVirtUnitNum
= cpu_to_le16(thisVUC
);
435 oob
.u
.a
.ReplUnitNum
= oob
.u
.a
.SpareReplUnitNum
= 0xffff;
437 nftl_write_oob(mtd
, (nftl
->EraseSize
* targetEUN
) + 8,
438 8, &retlen
, (char *)&oob
.u
);
440 /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
442 /* At this point, we have two different chains for this Virtual Unit, and no way to tell
443 them apart. If we crash now, we get confused. However, both contain the same data, so we
444 shouldn't actually lose data in this case. It's just that when we load up on a medium which
445 has duplicate chains, we need to free one of the chains because it's not necessary any more.
447 thisEUN
= nftl
->EUNtable
[thisVUC
];
448 DEBUG(MTD_DEBUG_LEVEL1
,"Want to erase\n");
450 /* For each block in the old chain (except the targetEUN of course),
451 free it and make it available for future use */
452 while (thisEUN
<= nftl
->lastEUN
&& thisEUN
!= targetEUN
) {
455 EUNtmp
= nftl
->ReplUnitTable
[thisEUN
];
457 if (NFTL_formatblock(nftl
, thisEUN
) < 0) {
458 /* could not erase : mark block as reserved
460 nftl
->ReplUnitTable
[thisEUN
] = BLOCK_RESERVED
;
462 /* correctly erased : mark it as free */
463 nftl
->ReplUnitTable
[thisEUN
] = BLOCK_FREE
;
469 /* Make this the new start of chain for thisVUC */
470 nftl
->ReplUnitTable
[targetEUN
] = BLOCK_NIL
;
471 nftl
->EUNtable
[thisVUC
] = targetEUN
;
476 static u16
NFTL_makefreeblock( struct NFTLrecord
*nftl
, unsigned pendingblock
)
478 /* This is the part that needs some cleverness applied.
479 For now, I'm doing the minimum applicable to actually
480 get the thing to work.
481 Wear-levelling and other clever stuff needs to be implemented
482 and we also need to do some assessment of the results when
483 the system loses power half-way through the routine.
485 u16 LongestChain
= 0;
486 u16 ChainLength
= 0, thislen
;
489 for (chain
= 0; chain
< le32_to_cpu(nftl
->MediaHdr
.FormattedSize
) / nftl
->EraseSize
; chain
++) {
490 EUN
= nftl
->EUNtable
[chain
];
493 while (EUN
<= nftl
->lastEUN
) {
495 //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
496 EUN
= nftl
->ReplUnitTable
[EUN
] & 0x7fff;
497 if (thislen
> 0xff00) {
498 printk("Endless loop in Virtual Chain %d: Unit %x\n",
501 if (thislen
> 0xff10) {
502 /* Actually, don't return failure. Just ignore this chain and
509 if (thislen
> ChainLength
) {
510 //printk("New longest chain is %d with length %d\n", chain, thislen);
511 ChainLength
= thislen
;
512 LongestChain
= chain
;
516 if (ChainLength
< 2) {
517 printk(KERN_WARNING
"No Virtual Unit Chains available for folding. "
518 "Failing request\n");
522 return NFTL_foldchain (nftl
, LongestChain
, pendingblock
);
525 /* NFTL_findwriteunit: Return the unit number into which we can write
526 for this block. Make it available if it isn't already
528 static inline u16
NFTL_findwriteunit(struct NFTLrecord
*nftl
, unsigned block
)
531 u16 thisVUC
= block
/ (nftl
->EraseSize
/ 512);
532 struct mtd_info
*mtd
= nftl
->mbd
.mtd
;
533 unsigned int writeEUN
;
534 unsigned long blockofs
= (block
* 512) & (nftl
->EraseSize
-1);
536 int silly
, silly2
= 3;
540 /* Scan the media to find a unit in the VUC which has
541 a free space for the block in question.
544 /* This condition catches the 0x[7f]fff cases, as well as
545 being a sanity check for past-end-of-media access
548 writeEUN
= nftl
->EUNtable
[thisVUC
];
550 while (writeEUN
<= nftl
->lastEUN
) {
558 (writeEUN
* nftl
->EraseSize
) + blockofs
,
559 8, &retlen
, (char *)&bci
);
561 DEBUG(MTD_DEBUG_LEVEL2
, "Status of block %d in EUN %d is %x\n",
562 block
, writeEUN
, le16_to_cpu(bci
.Status
));
564 status
= bci
.Status
| bci
.Status1
;
574 // Invalid block. Don't use it any more. Must implement.
580 "Infinite loop in Virtual Unit Chain 0x%x\n",
585 /* Skip to next block in chain */
586 writeEUN
= nftl
->ReplUnitTable
[writeEUN
];
589 /* OK. We didn't find one in the existing chain, or there
590 is no existing chain. */
592 /* Try to find an already-free block */
593 writeEUN
= NFTL_findfreeblock(nftl
, 0);
595 if (writeEUN
== BLOCK_NIL
) {
596 /* That didn't work - there were no free blocks just
597 waiting to be picked up. We're going to have to fold
598 a chain to make room.
601 /* First remember the start of this chain */
602 //u16 startEUN = nftl->EUNtable[thisVUC];
604 //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
605 writeEUN
= NFTL_makefreeblock(nftl
, 0xffff);
607 if (writeEUN
== BLOCK_NIL
) {
608 /* OK, we accept that the above comment is
609 lying - there may have been free blocks
610 last time we called NFTL_findfreeblock(),
611 but they are reserved for when we're
612 desperate. Well, now we're desperate.
614 DEBUG(MTD_DEBUG_LEVEL1
, "Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC
);
615 writeEUN
= NFTL_findfreeblock(nftl
, 1);
617 if (writeEUN
== BLOCK_NIL
) {
618 /* Ouch. This should never happen - we should
619 always be able to make some room somehow.
620 If we get here, we've allocated more storage
621 space than actual media, or our makefreeblock
622 routine is missing something.
624 printk(KERN_WARNING
"Cannot make free space.\n");
627 //printk("Restarting scan\n");
632 /* We've found a free block. Insert it into the chain. */
634 if (lastEUN
!= BLOCK_NIL
) {
635 thisVUC
|= 0x8000; /* It's a replacement block */
637 /* The first block in a new chain */
638 nftl
->EUNtable
[thisVUC
] = writeEUN
;
641 /* set up the actual EUN we're writing into */
642 /* Both in our cache... */
643 nftl
->ReplUnitTable
[writeEUN
] = BLOCK_NIL
;
645 /* ... and on the flash itself */
646 nftl_read_oob(mtd
, writeEUN
* nftl
->EraseSize
+ 8, 8,
647 &retlen
, (char *)&oob
.u
);
649 oob
.u
.a
.VirtUnitNum
= oob
.u
.a
.SpareVirtUnitNum
= cpu_to_le16(thisVUC
);
651 nftl_write_oob(mtd
, writeEUN
* nftl
->EraseSize
+ 8, 8,
652 &retlen
, (char *)&oob
.u
);
654 /* we link the new block to the chain only after the
655 block is ready. It avoids the case where the chain
656 could point to a free block */
657 if (lastEUN
!= BLOCK_NIL
) {
658 /* Both in our cache... */
659 nftl
->ReplUnitTable
[lastEUN
] = writeEUN
;
660 /* ... and on the flash itself */
661 nftl_read_oob(mtd
, (lastEUN
* nftl
->EraseSize
) + 8,
662 8, &retlen
, (char *)&oob
.u
);
664 oob
.u
.a
.ReplUnitNum
= oob
.u
.a
.SpareReplUnitNum
665 = cpu_to_le16(writeEUN
);
667 nftl_write_oob(mtd
, (lastEUN
* nftl
->EraseSize
) + 8,
668 8, &retlen
, (char *)&oob
.u
);
675 printk(KERN_WARNING
"Error folding to make room for Virtual Unit Chain 0x%x\n",
680 static int nftl_writeblock(struct mtd_blktrans_dev
*mbd
, unsigned long block
,
683 struct NFTLrecord
*nftl
= (void *)mbd
;
685 unsigned long blockofs
= (block
* 512) & (nftl
->EraseSize
- 1);
689 writeEUN
= NFTL_findwriteunit(nftl
, block
);
691 if (writeEUN
== BLOCK_NIL
) {
693 "NFTL_writeblock(): Cannot find block to write to\n");
694 /* If we _still_ haven't got a block to use, we're screwed */
698 memset(&oob
, 0xff, sizeof(struct nftl_oob
));
699 oob
.b
.Status
= oob
.b
.Status1
= SECTOR_USED
;
701 nftl_write(nftl
->mbd
.mtd
, (writeEUN
* nftl
->EraseSize
) + blockofs
,
702 512, &retlen
, (char *)buffer
, (char *)&oob
);
705 #endif /* CONFIG_NFTL_RW */
707 static int nftl_readblock(struct mtd_blktrans_dev
*mbd
, unsigned long block
,
710 struct NFTLrecord
*nftl
= (void *)mbd
;
711 struct mtd_info
*mtd
= nftl
->mbd
.mtd
;
713 u16 thisEUN
= nftl
->EUNtable
[block
/ (nftl
->EraseSize
/ 512)];
714 unsigned long blockofs
= (block
* 512) & (nftl
->EraseSize
- 1);
716 int silly
= MAX_LOOPS
;
720 lastgoodEUN
= BLOCK_NIL
;
722 if (thisEUN
!= BLOCK_NIL
) {
723 while (thisEUN
< nftl
->nb_blocks
) {
724 if (nftl_read_oob(mtd
, (thisEUN
* nftl
->EraseSize
) +
725 blockofs
, 8, &retlen
,
727 status
= SECTOR_IGNORE
;
729 status
= bci
.Status
| bci
.Status1
;
733 /* no modification of a sector should follow a free sector */
736 lastgoodEUN
= BLOCK_NIL
;
739 lastgoodEUN
= thisEUN
;
744 printk("Unknown status for block %ld in EUN %d: %x\n",
745 block
, thisEUN
, status
);
750 printk(KERN_WARNING
"Infinite loop in Virtual Unit Chain 0x%lx\n",
751 block
/ (nftl
->EraseSize
/ 512));
754 thisEUN
= nftl
->ReplUnitTable
[thisEUN
];
759 if (lastgoodEUN
== BLOCK_NIL
) {
760 /* the requested block is not on the media, return all 0x00 */
761 memset(buffer
, 0, 512);
763 loff_t ptr
= (lastgoodEUN
* nftl
->EraseSize
) + blockofs
;
765 int res
= mtd
->read(mtd
, ptr
, 512, &retlen
, buffer
);
767 if (res
< 0 && res
!= -EUCLEAN
)
773 static int nftl_getgeo(struct mtd_blktrans_dev
*dev
, struct hd_geometry
*geo
)
775 struct NFTLrecord
*nftl
= (void *)dev
;
777 geo
->heads
= nftl
->heads
;
778 geo
->sectors
= nftl
->sectors
;
779 geo
->cylinders
= nftl
->cylinders
;
784 /****************************************************************************
788 ****************************************************************************/
791 static struct mtd_blktrans_ops nftl_tr
= {
794 .part_bits
= NFTL_PARTN_BITS
,
796 .getgeo
= nftl_getgeo
,
797 .readsect
= nftl_readblock
,
798 #ifdef CONFIG_NFTL_RW
799 .writesect
= nftl_writeblock
,
801 .add_mtd
= nftl_add_mtd
,
802 .remove_dev
= nftl_remove_dev
,
803 .owner
= THIS_MODULE
,
806 extern char nftlmountrev
[];
808 static int __init
init_nftl(void)
810 printk(KERN_INFO
"NFTL driver: nftlcore.c $Revision: 1.98 $, nftlmount.c %s\n", nftlmountrev
);
812 return register_mtd_blktrans(&nftl_tr
);
815 static void __exit
cleanup_nftl(void)
817 deregister_mtd_blktrans(&nftl_tr
);
820 module_init(init_nftl
);
821 module_exit(cleanup_nftl
);
823 MODULE_LICENSE("GPL");
824 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
825 MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");
This page took 0.046638 seconds and 5 git commands to generate.