File Index
Files and directories
Sign in
FILE

nftlcore.c

File preview with syntax highlighting.

linux-6.19.14/drivers/mtd/nftlcore.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Linux driver for NAND Flash Translation Layer
  4 *
  5 * Copyright © 1999 Machine Vision Holdings, Inc.
  6 * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org>
  7 */
  8
  9#define PRERELEASE
 10
 11#include <linux/kernel.h>
 12#include <linux/module.h>
 13#include <asm/errno.h>
 14#include <asm/io.h>
 15#include <linux/uaccess.h>
 16#include <linux/delay.h>
 17#include <linux/slab.h>
 18#include <linux/init.h>
 19#include <linux/hdreg.h>
 20#include <linux/blkdev.h>
 21
 22#include <linux/kmod.h>
 23#include <linux/mtd/mtd.h>
 24#include <linux/mtd/rawnand.h>
 25#include <linux/mtd/nftl.h>
 26#include <linux/mtd/blktrans.h>
 27
 28/* maximum number of loops while examining next block, to have a
 29   chance to detect consistency problems (they should never happen
 30   because of the checks done in the mounting */
 31
 32#define MAX_LOOPS 10000
 33
 34
 35static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 36{
 37	struct NFTLrecord *nftl;
 38	unsigned long temp;
 39
 40	if (!mtd_type_is_nand(mtd) || mtd->size > UINT_MAX)
 41		return;
 42	/* OK, this is moderately ugly.  But probably safe.  Alternatives? */
 43	if (memcmp(mtd->name, "DiskOnChip", 10))
 44		return;
 45
 46	pr_debug("NFTL: add_mtd for %s\n", mtd->name);
 47
 48	nftl = kzalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
 49
 50	if (!nftl)
 51		return;
 52
 53	nftl->mbd.mtd = mtd;
 54	nftl->mbd.devnum = -1;
 55
 56	nftl->mbd.tr = tr;
 57
 58        if (NFTL_mount(nftl) < 0) {
 59		printk(KERN_WARNING "NFTL: could not mount device\n");
 60		kfree(nftl);
 61		return;
 62        }
 63
 64	/* OK, it's a new one. Set up all the data structures. */
 65
 66	/* Calculate geometry */
 67	nftl->cylinders = 1024;
 68	nftl->heads = 16;
 69
 70	temp = nftl->cylinders * nftl->heads;
 71	nftl->sectors = nftl->mbd.size / temp;
 72	if (nftl->mbd.size % temp) {
 73		nftl->sectors++;
 74		temp = nftl->cylinders * nftl->sectors;
 75		nftl->heads = nftl->mbd.size / temp;
 76
 77		if (nftl->mbd.size % temp) {
 78			nftl->heads++;
 79			temp = nftl->heads * nftl->sectors;
 80			nftl->cylinders = nftl->mbd.size / temp;
 81		}
 82	}
 83
 84	if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) {
 85		/*
 86		  Oh no we don't have
 87		   mbd.size == heads * cylinders * sectors
 88		*/
 89		printk(KERN_WARNING "NFTL: cannot calculate a geometry to "
 90		       "match size of 0x%lx.\n", nftl->mbd.size);
 91		printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d "
 92			"(== 0x%lx sects)\n",
 93			nftl->cylinders, nftl->heads , nftl->sectors,
 94			(long)nftl->cylinders * (long)nftl->heads *
 95			(long)nftl->sectors );
 96	}
 97
 98	if (add_mtd_blktrans_dev(&nftl->mbd)) {
 99		kfree(nftl->ReplUnitTable);
100		kfree(nftl->EUNtable);
101		kfree(nftl);
102		return;
103	}
104#ifdef PSYCHO_DEBUG
105	printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
106#endif
107}
108
109static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
110{
111	struct NFTLrecord *nftl = (void *)dev;
112
113	pr_debug("NFTL: remove_dev (i=%d)\n", dev->devnum);
114
115	del_mtd_blktrans_dev(dev);
116	kfree(nftl->ReplUnitTable);
117	kfree(nftl->EUNtable);
118}
119
120/*
121 * Read oob data from flash
122 */
123int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
124		  size_t *retlen, uint8_t *buf)
125{
126	loff_t mask = mtd->writesize - 1;
127	struct mtd_oob_ops ops = { };
128	int res;
129
130	ops.mode = MTD_OPS_PLACE_OOB;
131	ops.ooboffs = offs & mask;
132	ops.ooblen = len;
133	ops.oobbuf = buf;
134	ops.datbuf = NULL;
135
136	res = mtd_read_oob(mtd, offs & ~mask, &ops);
137	*retlen = ops.oobretlen;
138	return res;
139}
140
141/*
142 * Write oob data to flash
143 */
144int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
145		   size_t *retlen, uint8_t *buf)
146{
147	loff_t mask = mtd->writesize - 1;
148	struct mtd_oob_ops ops = { };
149	int res;
150
151	ops.mode = MTD_OPS_PLACE_OOB;
152	ops.ooboffs = offs & mask;
153	ops.ooblen = len;
154	ops.oobbuf = buf;
155	ops.datbuf = NULL;
156
157	res = mtd_write_oob(mtd, offs & ~mask, &ops);
158	*retlen = ops.oobretlen;
159	return res;
160}
161
162#ifdef CONFIG_NFTL_RW
163
164/*
165 * Write data and oob to flash
166 */
167static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
168		      size_t *retlen, uint8_t *buf, uint8_t *oob)
169{
170	loff_t mask = mtd->writesize - 1;
171	struct mtd_oob_ops ops = { };
172	int res;
173
174	ops.mode = MTD_OPS_PLACE_OOB;
175	ops.ooboffs = offs & mask;
176	ops.ooblen = mtd->oobsize;
177	ops.oobbuf = oob;
178	ops.datbuf = buf;
179	ops.len = len;
180
181	res = mtd_write_oob(mtd, offs & ~mask, &ops);
182	*retlen = ops.retlen;
183	return res;
184}
185
186/* Actual NFTL access routines */
187/* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
188 *	when the give Virtual Unit Chain
189 */
190static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
191{
192	/* For a given Virtual Unit Chain: find or create a free block and
193	   add it to the chain */
194	/* We're passed the number of the last EUN in the chain, to save us from
195	   having to look it up again */
196	u16 pot = nftl->LastFreeEUN;
197	int silly = nftl->nb_blocks;
198
199	/* Normally, we force a fold to happen before we run out of free blocks completely */
200	if (!desperate && nftl->numfreeEUNs < 2) {
201		pr_debug("NFTL_findfreeblock: there are too few free EUNs\n");
202		return BLOCK_NIL;
203	}
204
205	/* Scan for a free block */
206	do {
207		if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
208			nftl->LastFreeEUN = pot;
209			nftl->numfreeEUNs--;
210			return pot;
211		}
212
213		/* This will probably point to the MediaHdr unit itself,
214		   right at the beginning of the partition. But that unit
215		   (and the backup unit too) should have the UCI set
216		   up so that it's not selected for overwriting */
217		if (++pot > nftl->lastEUN)
218			pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
219
220		if (!silly--) {
221			printk("Argh! No free blocks found! LastFreeEUN = %d, "
222			       "FirstEUN = %d\n", nftl->LastFreeEUN,
223			       le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
224			return BLOCK_NIL;
225		}
226	} while (pot != nftl->LastFreeEUN);
227
228	return BLOCK_NIL;
229}
230
231static noinline_for_stack void NFTL_move_block(struct mtd_info *mtd, loff_t src, loff_t dst)
232{
233	unsigned char movebuf[512];
234	struct nftl_oob oob;
235	size_t retlen;
236	int ret;
237
238	ret = mtd_read(mtd, src, 512, &retlen, movebuf);
239	if (ret < 0 && !mtd_is_bitflip(ret)) {
240		ret = mtd_read(mtd, src, 512, &retlen, movebuf);
241		if (ret != -EIO)
242			printk("Error went away on retry.\n");
243	}
244	memset(&oob, 0xff, sizeof(struct nftl_oob));
245	oob.b.Status = oob.b.Status1 = SECTOR_USED;
246
247	nftl_write(mtd, dst, 512, &retlen, movebuf, (char *)&oob);
248}
249
250static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
251{
252	struct mtd_info *mtd = nftl->mbd.mtd;
253	u16 BlockMap[MAX_SECTORS_PER_UNIT];
254	unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
255	unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
256	unsigned int thisEUN;
257	int block;
258	int silly;
259	unsigned int targetEUN;
260	struct nftl_oob oob;
261	int inplace = 1;
262	size_t retlen;
263
264	memset(BlockMap, 0xff, sizeof(BlockMap));
265	memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
266
267	thisEUN = nftl->EUNtable[thisVUC];
268
269	if (thisEUN == BLOCK_NIL) {
270		printk(KERN_WARNING "Trying to fold non-existent "
271		       "Virtual Unit Chain %d!\n", thisVUC);
272		return BLOCK_NIL;
273	}
274
275	/* Scan to find the Erase Unit which holds the actual data for each
276	   512-byte block within the Chain.
277	*/
278	silly = MAX_LOOPS;
279	targetEUN = BLOCK_NIL;
280	while (thisEUN <= nftl->lastEUN ) {
281		unsigned int status, foldmark;
282
283		targetEUN = thisEUN;
284		for (block = 0; block < nftl->EraseSize / 512; block ++) {
285			nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
286				      (block * 512), 16 , &retlen,
287				      (char *)&oob);
288			if (block == 2) {
289				foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
290				if (foldmark == FOLD_MARK_IN_PROGRESS) {
291					pr_debug("Write Inhibited on EUN %d\n", thisEUN);
292					inplace = 0;
293				} else {
294					/* There's no other reason not to do inplace,
295					   except ones that come later. So we don't need
296					   to preserve inplace */
297					inplace = 1;
298				}
299			}
300			status = oob.b.Status | oob.b.Status1;
301			BlockLastState[block] = status;
302
303			switch(status) {
304			case SECTOR_FREE:
305				BlockFreeFound[block] = 1;
306				break;
307
308			case SECTOR_USED:
309				if (!BlockFreeFound[block])
310					BlockMap[block] = thisEUN;
311				else
312					printk(KERN_WARNING
313					       "SECTOR_USED found after SECTOR_FREE "
314					       "in Virtual Unit Chain %d for block %d\n",
315					       thisVUC, block);
316				break;
317			case SECTOR_DELETED:
318				if (!BlockFreeFound[block])
319					BlockMap[block] = BLOCK_NIL;
320				else
321					printk(KERN_WARNING
322					       "SECTOR_DELETED found after SECTOR_FREE "
323					       "in Virtual Unit Chain %d for block %d\n",
324					       thisVUC, block);
325				break;
326
327			case SECTOR_IGNORE:
328				break;
329			default:
330				printk("Unknown status for block %d in EUN %d: %x\n",
331				       block, thisEUN, status);
332			}
333		}
334
335		if (!silly--) {
336			printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
337			       thisVUC);
338			return BLOCK_NIL;
339		}
340
341		thisEUN = nftl->ReplUnitTable[thisEUN];
342	}
343
344	if (inplace) {
345		/* We're being asked to be a fold-in-place. Check
346		   that all blocks which actually have data associated
347		   with them (i.e. BlockMap[block] != BLOCK_NIL) are
348		   either already present or SECTOR_FREE in the target
349		   block. If not, we're going to have to fold out-of-place
350		   anyway.
351		*/
352		for (block = 0; block < nftl->EraseSize / 512 ; block++) {
353			if (BlockLastState[block] != SECTOR_FREE &&
354			    BlockMap[block] != BLOCK_NIL &&
355			    BlockMap[block] != targetEUN) {
356				pr_debug("Setting inplace to 0. VUC %d, "
357				      "block %d was %x lastEUN, "
358				      "and is in EUN %d (%s) %d\n",
359				      thisVUC, block, BlockLastState[block],
360				      BlockMap[block],
361				      BlockMap[block]== targetEUN ? "==" : "!=",
362				      targetEUN);
363				inplace = 0;
364				break;
365			}
366		}
367
368		if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
369		    pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
370		    BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
371		    SECTOR_FREE) {
372			pr_debug("Pending write not free in EUN %d. "
373			      "Folding out of place.\n", targetEUN);
374			inplace = 0;
375		}
376	}
377
378	if (!inplace) {
379		pr_debug("Cannot fold Virtual Unit Chain %d in place. "
380		      "Trying out-of-place\n", thisVUC);
381		/* We need to find a targetEUN to fold into. */
382		targetEUN = NFTL_findfreeblock(nftl, 1);
383		if (targetEUN == BLOCK_NIL) {
384			/* Ouch. Now we're screwed. We need to do a
385			   fold-in-place of another chain to make room
386			   for this one. We need a better way of selecting
387			   which chain to fold, because makefreeblock will
388			   only ask us to fold the same one again.
389			*/
390			printk(KERN_WARNING
391			       "NFTL_findfreeblock(desperate) returns 0xffff.\n");
392			return BLOCK_NIL;
393		}
394	} else {
395		/* We put a fold mark in the chain we are folding only if we
396               fold in place to help the mount check code. If we do not fold in
397               place, it is possible to find the valid chain by selecting the
398               longer one */
399		oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
400		oob.u.c.unused = 0xffffffff;
401		nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
402			       8, &retlen, (char *)&oob.u);
403	}
404
405	/* OK. We now know the location of every block in the Virtual Unit Chain,
406	   and the Erase Unit into which we are supposed to be copying.
407	   Go for it.
408	*/
409	pr_debug("Folding chain %d into unit %d\n", thisVUC, targetEUN);
410	for (block = 0; block < nftl->EraseSize / 512 ; block++) {
411		/* If it's in the target EUN already, or if it's pending write, do nothing */
412		if (BlockMap[block] == targetEUN ||
413		    (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
414			continue;
415		}
416
417		/* copy only in non free block (free blocks can only
418                   happen in case of media errors or deleted blocks) */
419		if (BlockMap[block] == BLOCK_NIL)
420			continue;
421
422		NFTL_move_block(mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
423				(nftl->EraseSize * targetEUN) + (block * 512));
424	}
425
426	/* add the header so that it is now a valid chain */
427	oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
428	oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = BLOCK_NIL;
429
430	nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
431		       8, &retlen, (char *)&oob.u);
432
433	/* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
434
435	/* At this point, we have two different chains for this Virtual Unit, and no way to tell
436	   them apart. If we crash now, we get confused. However, both contain the same data, so we
437	   shouldn't actually lose data in this case. It's just that when we load up on a medium which
438	   has duplicate chains, we need to free one of the chains because it's not necessary any more.
439	*/
440	thisEUN = nftl->EUNtable[thisVUC];
441	pr_debug("Want to erase\n");
442
443	/* For each block in the old chain (except the targetEUN of course),
444	   free it and make it available for future use */
445	while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
446		unsigned int EUNtmp;
447
448		EUNtmp = nftl->ReplUnitTable[thisEUN];
449
450		if (NFTL_formatblock(nftl, thisEUN) < 0) {
451			/* could not erase : mark block as reserved
452			 */
453			nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
454		} else {
455			/* correctly erased : mark it as free */
456			nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
457			nftl->numfreeEUNs++;
458		}
459		thisEUN = EUNtmp;
460	}
461
462	/* Make this the new start of chain for thisVUC */
463	nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
464	nftl->EUNtable[thisVUC] = targetEUN;
465
466	return targetEUN;
467}
468
469static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
470{
471	/* This is the part that needs some cleverness applied.
472	   For now, I'm doing the minimum applicable to actually
473	   get the thing to work.
474	   Wear-levelling and other clever stuff needs to be implemented
475	   and we also need to do some assessment of the results when
476	   the system loses power half-way through the routine.
477	*/
478	u16 LongestChain = 0;
479	u16 ChainLength = 0, thislen;
480	u16 chain, EUN;
481
482	for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) {
483		EUN = nftl->EUNtable[chain];
484		thislen = 0;
485
486		while (EUN <= nftl->lastEUN) {
487			thislen++;
488			//printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
489			EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
490			if (thislen > 0xff00) {
491				printk("Endless loop in Virtual Chain %d: Unit %x\n",
492				       chain, EUN);
493			}
494			if (thislen > 0xff10) {
495				/* Actually, don't return failure. Just ignore this chain and
496				   get on with it. */
497				thislen = 0;
498				break;
499			}
500		}
501
502		if (thislen > ChainLength) {
503			//printk("New longest chain is %d with length %d\n", chain, thislen);
504			ChainLength = thislen;
505			LongestChain = chain;
506		}
507	}
508
509	if (ChainLength < 2) {
510		printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
511		       "Failing request\n");
512		return BLOCK_NIL;
513	}
514
515	return NFTL_foldchain (nftl, LongestChain, pendingblock);
516}
517
518/* NFTL_findwriteunit: Return the unit number into which we can write
519                       for this block. Make it available if it isn't already
520*/
521static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
522{
523	u16 lastEUN;
524	u16 thisVUC = block / (nftl->EraseSize / 512);
525	struct mtd_info *mtd = nftl->mbd.mtd;
526	unsigned int writeEUN;
527	unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
528	size_t retlen;
529	int silly, silly2 = 3;
530	struct nftl_oob oob;
531
532	do {
533		/* Scan the media to find a unit in the VUC which has
534		   a free space for the block in question.
535		*/
536
537		/* This condition catches the 0x[7f]fff cases, as well as
538		   being a sanity check for past-end-of-media access
539		*/
540		lastEUN = BLOCK_NIL;
541		writeEUN = nftl->EUNtable[thisVUC];
542		silly = MAX_LOOPS;
543		while (writeEUN <= nftl->lastEUN) {
544			struct nftl_bci bci;
545			size_t retlen;
546			unsigned int status;
547
548			lastEUN = writeEUN;
549
550			nftl_read_oob(mtd,
551				      (writeEUN * nftl->EraseSize) + blockofs,
552				      8, &retlen, (char *)&bci);
553
554			pr_debug("Status of block %d in EUN %d is %x\n",
555			      block , writeEUN, le16_to_cpu(bci.Status));
556
557			status = bci.Status | bci.Status1;
558			switch(status) {
559			case SECTOR_FREE:
560				return writeEUN;
561
562			case SECTOR_DELETED:
563			case SECTOR_USED:
564			case SECTOR_IGNORE:
565				break;
566			default:
567				// Invalid block. Don't use it any more. Must implement.
568				break;
569			}
570
571			if (!silly--) {
572				printk(KERN_WARNING
573				       "Infinite loop in Virtual Unit Chain 0x%x\n",
574				       thisVUC);
575				return BLOCK_NIL;
576			}
577
578			/* Skip to next block in chain */
579			writeEUN = nftl->ReplUnitTable[writeEUN];
580		}
581
582		/* OK. We didn't find one in the existing chain, or there
583		   is no existing chain. */
584
585		/* Try to find an already-free block */
586		writeEUN = NFTL_findfreeblock(nftl, 0);
587
588		if (writeEUN == BLOCK_NIL) {
589			/* That didn't work - there were no free blocks just
590			   waiting to be picked up. We're going to have to fold
591			   a chain to make room.
592			*/
593
594			/* First remember the start of this chain */
595			//u16 startEUN = nftl->EUNtable[thisVUC];
596
597			//printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
598			writeEUN = NFTL_makefreeblock(nftl, BLOCK_NIL);
599
600			if (writeEUN == BLOCK_NIL) {
601				/* OK, we accept that the above comment is
602				   lying - there may have been free blocks
603				   last time we called NFTL_findfreeblock(),
604				   but they are reserved for when we're
605				   desperate. Well, now we're desperate.
606				*/
607				pr_debug("Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
608				writeEUN = NFTL_findfreeblock(nftl, 1);
609			}
610			if (writeEUN == BLOCK_NIL) {
611				/* Ouch. This should never happen - we should
612				   always be able to make some room somehow.
613				   If we get here, we've allocated more storage
614				   space than actual media, or our makefreeblock
615				   routine is missing something.
616				*/
617				printk(KERN_WARNING "Cannot make free space.\n");
618				return BLOCK_NIL;
619			}
620			//printk("Restarting scan\n");
621			continue;
622		}
623
624		/* We've found a free block. Insert it into the chain. */
625
626		if (lastEUN != BLOCK_NIL) {
627			thisVUC |= 0x8000; /* It's a replacement block */
628		} else {
629			/* The first block in a new chain */
630			nftl->EUNtable[thisVUC] = writeEUN;
631		}
632
633		/* set up the actual EUN we're writing into */
634		/* Both in our cache... */
635		nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
636
637		/* ... and on the flash itself */
638		nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
639			      &retlen, (char *)&oob.u);
640
641		oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
642
643		nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
644			       &retlen, (char *)&oob.u);
645
646		/* we link the new block to the chain only after the
647                   block is ready. It avoids the case where the chain
648                   could point to a free block */
649		if (lastEUN != BLOCK_NIL) {
650			/* Both in our cache... */
651			nftl->ReplUnitTable[lastEUN] = writeEUN;
652			/* ... and on the flash itself */
653			nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
654				      8, &retlen, (char *)&oob.u);
655
656			oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
657				= cpu_to_le16(writeEUN);
658
659			nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
660				       8, &retlen, (char *)&oob.u);
661		}
662
663		return writeEUN;
664
665	} while (silly2--);
666
667	printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
668	       thisVUC);
669	return BLOCK_NIL;
670}
671
672static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
673			   char *buffer)
674{
675	struct NFTLrecord *nftl = (void *)mbd;
676	u16 writeEUN;
677	unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
678	size_t retlen;
679	struct nftl_oob oob;
680
681	writeEUN = NFTL_findwriteunit(nftl, block);
682
683	if (writeEUN == BLOCK_NIL) {
684		printk(KERN_WARNING
685		       "NFTL_writeblock(): Cannot find block to write to\n");
686		/* If we _still_ haven't got a block to use, we're screwed */
687		return 1;
688	}
689
690	memset(&oob, 0xff, sizeof(struct nftl_oob));
691	oob.b.Status = oob.b.Status1 = SECTOR_USED;
692
693	nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
694		   512, &retlen, (char *)buffer, (char *)&oob);
695	return 0;
696}
697#endif /* CONFIG_NFTL_RW */
698
699static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
700			  char *buffer)
701{
702	struct NFTLrecord *nftl = (void *)mbd;
703	struct mtd_info *mtd = nftl->mbd.mtd;
704	u16 lastgoodEUN;
705	u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
706	unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
707	unsigned int status;
708	int silly = MAX_LOOPS;
709	size_t retlen;
710	struct nftl_bci bci;
711
712	lastgoodEUN = BLOCK_NIL;
713
714	if (thisEUN != BLOCK_NIL) {
715		while (thisEUN < nftl->nb_blocks) {
716			if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
717					  blockofs, 8, &retlen,
718					  (char *)&bci) < 0)
719				status = SECTOR_IGNORE;
720			else
721				status = bci.Status | bci.Status1;
722
723			switch (status) {
724			case SECTOR_FREE:
725				/* no modification of a sector should follow a free sector */
726				goto the_end;
727			case SECTOR_DELETED:
728				lastgoodEUN = BLOCK_NIL;
729				break;
730			case SECTOR_USED:
731				lastgoodEUN = thisEUN;
732				break;
733			case SECTOR_IGNORE:
734				break;
735			default:
736				printk("Unknown status for block %ld in EUN %d: %x\n",
737				       block, thisEUN, status);
738				break;
739			}
740
741			if (!silly--) {
742				printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
743				       block / (nftl->EraseSize / 512));
744				return 1;
745			}
746			thisEUN = nftl->ReplUnitTable[thisEUN];
747		}
748	}
749
750 the_end:
751	if (lastgoodEUN == BLOCK_NIL) {
752		/* the requested block is not on the media, return all 0x00 */
753		memset(buffer, 0, 512);
754	} else {
755		loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
756		size_t retlen;
757		int res = mtd_read(mtd, ptr, 512, &retlen, buffer);
758
759		if (res < 0 && !mtd_is_bitflip(res))
760			return -EIO;
761	}
762	return 0;
763}
764
765static int nftl_getgeo(struct mtd_blktrans_dev *dev,  struct hd_geometry *geo)
766{
767	struct NFTLrecord *nftl = (void *)dev;
768
769	geo->heads = nftl->heads;
770	geo->sectors = nftl->sectors;
771	geo->cylinders = nftl->cylinders;
772
773	return 0;
774}
775
776/****************************************************************************
777 *
778 * Module stuff
779 *
780 ****************************************************************************/
781
782
783static struct mtd_blktrans_ops nftl_tr = {
784	.name		= "nftl",
785	.major		= NFTL_MAJOR,
786	.part_bits	= NFTL_PARTN_BITS,
787	.blksize 	= 512,
788	.getgeo		= nftl_getgeo,
789	.readsect	= nftl_readblock,
790#ifdef CONFIG_NFTL_RW
791	.writesect	= nftl_writeblock,
792#endif
793	.add_mtd	= nftl_add_mtd,
794	.remove_dev	= nftl_remove_dev,
795	.owner		= THIS_MODULE,
796};
797
798module_mtd_blktrans(nftl_tr);
799
800MODULE_LICENSE("GPL");
801MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
802MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");
803MODULE_ALIAS_BLOCKDEV_MAJOR(NFTL_MAJOR);
~/ Read-only file preview.