1 /* Linux driver for NAND Flash Translation Layer */
2 /* (c) 1999 Machine Vision Holdings, Inc. */
3 /* Author: David Woodhouse <dwmw2@infradead.org> */
4 /* $Id: nftl.c,v 1.57 2000/12/01 17:51:54 dwmw2 Exp $ */
5
6 /*
7 The contents of this file are distributed under the GNU General
8 Public License version 2 ("GPL"). The author places no additional
9 restrictions of any kind on it. However, local legislation in some
10 countries may restrict the use of the algorithms implemented by this
11 code in certain circumstances.
12
13 The legal note below refers only to the _use_ of the code in the
14 affected jurisdictions, and does not in any way affect the copying,
15 distribution and modification of this code, which are permitted, and
16 indeed required, under the terms of the GPL.
17
18 Section 0 of the GPL says:
19 "Activities other than copying, distribution and modification are not
20 covered by this License; they are outside its scope."
21
22 You may copy, distribute and modify this code to your hearts'
23 content - it's just that in some jurisdictions, you may only _use_
24 it under the terms of the patent grant below. This puts it in a
25 similar situation to the ISDN code, which you may need telco
26 approval to use, and indeed any code which has uses that may be
27 restricted in law. For example, certain malicious uses of the
28 networking stack may be illegal, but that doesn't prevent the
29 networking code from being under GPL.
30
31 In fact the ISDN case is worse than this, because modification of
32 the code automatically invalidates its approval. Modification,
33 unlike usage, _is_ one of the rights which is protected by the
34 GPL. Happily, the law in those places where approval is required
35 doesn't actually prevent you from modifying the code - it's just
36 that you may not be allowed to _use_ it once you've done so - and
37 because usage isn't addressed by the GPL, that's just fine.
38
39 dwmw2@infradead.org
40 30/10/0
41
42 LEGAL NOTE: The NFTL format is patented by M-Systems. They have
43 granted a licence for its use with their DiskOnChip products:
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 NFTL-compatible
48 drivers, file systems and utilities to use the data formats with,
49 and solely to support, M-Systems' DiskOnChip products"
50
51 A signed copy of this agreement from M-Systems is kept on file by
52 Red Hat UK Limited. In the unlikely event that you need access to it,
53 please contact dwmw2@redhat.com for assistance. */
54
55 #define PRERELEASE
56
57 #include <linux/config.h>
58 #include <linux/kernel.h>
59 #include <linux/module.h>
60 #include <asm/errno.h>
61 #include <asm/io.h>
62 #include <asm/uaccess.h>
63 #include <linux/miscdevice.h>
64 #include <linux/pci.h>
65 #include <linux/delay.h>
66 #include <linux/malloc.h>
67 #include <linux/sched.h>
68 #include <linux/init.h>
69 #include <linux/blkpg.h>
70 #ifdef CONFIG_KMOD
71 #include <linux/kmod.h>
72 #endif
73 #include <linux/mtd/mtd.h>
74 #include <linux/mtd/nftl.h>
75 #include <linux/mtd/compatmac.h>
76
77 /* maximum number of loops while examining next block, to have a
78 chance to detect consistency problems (they should never happen
79 because of the checks done in the mounting */
80
81 #define MAX_LOOPS 10000
82
83 /* NFTL block device stuff */
84 #define MAJOR_NR NFTL_MAJOR
85 #define DEVICE_REQUEST nftl_request
86 #define DEVICE_OFF(device)
87
88
89 #include <linux/blk.h>
90 #include <linux/hdreg.h>
91
92 /* Linux-specific block device functions */
93
94 /* I _HATE_ the Linux block device setup more than anything else I've ever
95 * encountered, except ...
96 */
97
98 static int nftl_sizes[256] = {0,};
99 static int nftl_blocksizes[256] = {0,};
100
101 /* .. for the Linux partition table handling. */
102 struct hd_struct part_table[256] = {{0,0},};
103
104 #if LINUX_VERSION_CODE < 0x20328
105 static void dummy_init (struct gendisk *crap)
106 {}
107 #endif
108
109 static struct gendisk nftl_gendisk = {
110 MAJOR_NR, /* Major number */
111 "nftl", /* Major name */
112 4, /* Bits to shift to get real from partition */
113 15, /* Number of partitions per real */
114 #if LINUX_VERSION_CODE < 0x20328
115 MAX_NFTLS, /* maximum number of real */
116 dummy_init, /* init function */
117 #endif
118 part_table, /* hd struct */
119 nftl_sizes, /* block sizes */
120 0, /* number */
121 NULL, /* internal use, not presently used */
122 NULL /* next */
123 };
124
125 struct NFTLrecord *NFTLs[MAX_NFTLS] = {NULL};
126
127 static void NFTL_setup(struct mtd_info *mtd)
128 {
129 int i;
130 struct NFTLrecord *nftl;
131 unsigned long temp;
132 int firstfree = -1;
133
134 DEBUG(MTD_DEBUG_LEVEL1,"NFTL_setup\n");
135
136 for (i = 0; i < MAX_NFTLS; i++) {
137 if (!NFTLs[i] && firstfree == -1)
138 firstfree = i;
139 else if (NFTLs[i] && NFTLs[i]->mtd == mtd) {
140 /* This is a Spare Media Header for an NFTL we've already found */
141 DEBUG(MTD_DEBUG_LEVEL1, "MTD already mounted as NFTL\n");
142 return;
143 }
144 }
145 if (firstfree == -1) {
146 printk(KERN_WARNING "No more NFTL slot available\n");
147 return;
148 }
149
150 nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
151 if (!nftl) {
152 printk(KERN_WARNING "Out of memory for NFTL data structures\n");
153 return;
154 }
155
156 init_MUTEX(&nftl->mutex);
157
158 /* get physical parameters */
159 nftl->EraseSize = mtd->erasesize;
160 nftl->nb_blocks = mtd->size / mtd->erasesize;
161 nftl->mtd = mtd;
162
163 if (NFTL_mount(nftl) < 0) {
164 printk(KERN_WARNING "Could not mount NFTL device\n");
165 kfree(nftl);
166 return;
167 }
168
169 /* OK, it's a new one. Set up all the data structures. */
170 #ifdef PSYCHO_DEBUG
171 printk("Found new NFTL nftl%c\n", firstfree + 'a');
172 #endif
173
174 /* linux stuff */
175 nftl->usecount = 0;
176 nftl->cylinders = 1024;
177 nftl->heads = 16;
178
179 temp = nftl->cylinders * nftl->heads;
180 nftl->sectors = nftl->nr_sects / temp;
181 if (nftl->nr_sects % temp) {
182 nftl->sectors++;
183 temp = nftl->cylinders * nftl->sectors;
184 nftl->heads = nftl->nr_sects / temp;
185
186 if (nftl->nr_sects % temp) {
187 nftl->heads++;
188 temp = nftl->heads * nftl->sectors;
189 nftl->cylinders = nftl->nr_sects / temp;
190 }
191 }
192
193 if (nftl->nr_sects != nftl->heads * nftl->cylinders * nftl->sectors) {
194 printk(KERN_WARNING "Cannot calculate an NFTL geometry to "
195 "match size of 0x%lx.\n", nftl->nr_sects);
196 printk(KERN_WARNING "Using C:%d H:%d S:%d (== 0x%lx sects)\n",
197 nftl->cylinders, nftl->heads , nftl->sectors,
198 (long)nftl->cylinders * (long)nftl->heads * (long)nftl->sectors );
199
200 /* Oh no we don't have nftl->nr_sects = nftl->heads * nftl->cylinders * nftl->sectors; */
201 }
202 NFTLs[firstfree] = nftl;
203 /* Finally, set up the block device sizes */
204 nftl_sizes[firstfree * 16] = nftl->nr_sects;
205 //nftl_blocksizes[firstfree*16] = 512;
206 part_table[firstfree * 16].nr_sects = nftl->nr_sects;
207
208 /* partition check ... */
209 #if LINUX_VERSION_CODE < 0x20328
210 resetup_one_dev(&nftl_gendisk, firstfree);
211 #else
212 grok_partitions(&nftl_gendisk, firstfree, 1<<4, nftl->nr_sects);
213 #endif
214 }
215
216 static void NFTL_unsetup(int i)
217 {
218 struct NFTLrecord *nftl = NFTLs[i];
219
220 DEBUG(MTD_DEBUG_LEVEL1, "NFTL_unsetup %d\n", i);
221
222 NFTLs[i] = NULL;
223
224 if (nftl->ReplUnitTable)
225 kfree(nftl->ReplUnitTable);
226 if (nftl->EUNtable)
227 kfree(nftl->EUNtable);
228
229 kfree(nftl);
230 }
231
232 /* Search the MTD device for NFTL partitions */
233 static void NFTL_notify_add(struct mtd_info *mtd)
234 {
235 DEBUG(MTD_DEBUG_LEVEL1, "NFTL_notify_add for %s\n", mtd->name);
236
237 if (mtd) {
238 if (!mtd->read_oob) {
239 /* If this MTD doesn't have out-of-band data,
240 then there's no point continuing */
241 DEBUG(MTD_DEBUG_LEVEL1, "No OOB data, quitting\n");
242 return;
243 }
244 DEBUG(MTD_DEBUG_LEVEL3, "mtd->read = %p, size = %d, erasesize = %d\n",
245 mtd->read, mtd->size, mtd->erasesize);
246
247 NFTL_setup(mtd);
248 }
249 }
250
251 static void NFTL_notify_remove(struct mtd_info *mtd)
252 {
253 int i;
254
255 for (i = 0; i < MAX_NFTLS; i++) {
256 if (NFTLs[i] && NFTLs[i]->mtd == mtd)
257 NFTL_unsetup(i);
258 }
259 }
260
261 #ifdef CONFIG_NFTL_RW
262
263 /* Actual NFTL access routines */
264 /* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
265 * when the give Virtual Unit Chain
266 */
267 static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
268 {
269 /* For a given Virtual Unit Chain: find or create a free block and
270 add it to the chain */
271 /* We're passed the number of the last EUN in the chain, to save us from
272 having to look it up again */
273 u16 pot = nftl->LastFreeEUN;
274 int silly = -1;
275
276 /* Normally, we force a fold to happen before we run out of free blocks completely */
277 if (!desperate && nftl->numfreeEUNs < 2) {
278 DEBUG(MTD_DEBUG_LEVEL1, "NFTL_findfreeblock: there are too few free EUNs\n");
279 return 0xffff;
280 }
281
282 /* Scan for a free block */
283 do {
284 if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
285 nftl->LastFreeEUN = pot;
286 nftl->numfreeEUNs--;
287 return pot;
288 }
289
290 /* This will probably point to the MediaHdr unit itself,
291 right at the beginning of the partition. But that unit
292 (and the backup unit too) should have the UCI set
293 up so that it's not selected for overwriting */
294 if (++pot > nftl->lastEUN)
295 pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
296
297 if (!silly--) {
298 printk("Argh! No free blocks found! LastFreeEUN = %d, "
299 "FirstEUN = %d\n", nftl->LastFreeEUN,
300 le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
301 return 0xffff;
302 }
303 } while (pot != nftl->LastFreeEUN);
304
305 return 0xffff;
306 }
307
308 static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
309 {
310 u16 BlockMap[MAX_SECTORS_PER_UNIT];
311 unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
312 unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
313 unsigned int thisEUN;
314 int block;
315 int silly;
316 unsigned int targetEUN;
317 struct nftl_oob oob;
318 int inplace = 1;
319 size_t retlen;
320
321 memset(BlockMap, 0xff, sizeof(BlockMap));
322 memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
323
324 thisEUN = nftl->EUNtable[thisVUC];
325
326 if (thisEUN == BLOCK_NIL) {
327 printk(KERN_WARNING "Trying to fold non-existent "
328 "Virtual Unit Chain %d!\n", thisVUC);
329 return BLOCK_NIL;
330 }
331
332 /* Scan to find the Erase Unit which holds the actual data for each
333 512-byte block within the Chain.
334 */
335 silly = MAX_LOOPS;
336 targetEUN = BLOCK_NIL;
337 while (thisEUN <= nftl->lastEUN ) {
338 unsigned int status, foldmark;
339
340 targetEUN = thisEUN;
341 for (block = 0; block < nftl->EraseSize / 512; block ++) {
342 MTD_READOOB(nftl->mtd,
343 (thisEUN * nftl->EraseSize) + (block * 512),
344 16 , &retlen, (char *)&oob);
345 if (block == 2) {
346 foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
347 if (foldmark == FOLD_MARK_IN_PROGRESS) {
348 DEBUG(MTD_DEBUG_LEVEL1,
349 "Write Inhibited on EUN %d\n", thisEUN);
350 inplace = 0;
351 } else {
352 /* There's no other reason not to do inplace,
353 except ones that come later. So we don't need
354 to preserve inplace */
355 inplace = 1;
356 }
357 }
358 status = oob.b.Status | oob.b.Status1;
359 BlockLastState[block] = status;
360
361 switch(status) {
362 case SECTOR_FREE:
363 BlockFreeFound[block] = 1;
364 break;
365
366 case SECTOR_USED:
367 if (!BlockFreeFound[block])
368 BlockMap[block] = thisEUN;
369 else
370 printk(KERN_WARNING
371 "SECTOR_USED found after SECTOR_FREE "
372 "in Virtual Unit Chain %d for block %d\n",
373 thisVUC, block);
374 break;
375 case SECTOR_IGNORE:
376 case SECTOR_DELETED:
377 break;
378 default:
379 printk("Unknown status for block %d in EUN %d: %x\n",
380 block, thisEUN, status);
381 }
382 }
383
384 if (!silly--) {
385 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
386 thisVUC);
387 return BLOCK_NIL;
388 }
389
390 thisEUN = nftl->ReplUnitTable[thisEUN];
391 }
392
393 if (inplace) {
394 /* We're being asked to be a fold-in-place. Check
395 that all blocks are either present or SECTOR_FREE
396 in the target block. If not, we're going to have
397 to fold out-of-place anyway.
398 */
399 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
400 if (BlockLastState[block] != SECTOR_FREE &&
401 BlockMap[block] != targetEUN) {
402 DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, "
403 "block %d was %x lastEUN, "
404 "and is in EUN %d (%s) %d\n",
405 thisVUC, block, BlockLastState[block],
406 BlockMap[block],
407 BlockMap[block]== targetEUN ? "==" : "!=",
408 targetEUN);
409 inplace = 0;
410 break;
411 }
412 }
413
414 if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
415 pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
416 BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
417 SECTOR_FREE) {
418 DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. "
419 "Folding out of place.\n", targetEUN);
420 inplace = 0;
421 }
422 }
423
424 if (!inplace) {
425 DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. "
426 "Trying out-of-place\n", thisVUC);
427 /* We need to find a targetEUN to fold into. */
428 targetEUN = NFTL_findfreeblock(nftl, 1);
429 if (targetEUN == BLOCK_NIL) {
430 /* Ouch. Now we're screwed. We need to do a
431 fold-in-place of another chain to make room
432 for this one. We need a better way of selecting
433 which chain to fold, because makefreeblock will
434 only ask us to fold the same one again.
435 */
436 printk(KERN_WARNING
437 "NFTL_findfreeblock(desperate) returns 0xffff.\n");
438 return BLOCK_NIL;
439 }
440 } else {
441 /* We put a fold mark in the chain we are folding only if
442 we fold in place to help the mount check code. If we do
443 not fold in place, it is possible to find the valid
444 chain by selecting the longer one */
445 oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
446 oob.u.c.unused = 0xffffffff;
447 MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
448 8, &retlen, (char *)&oob.u);
449 }
450
451 /* OK. We now know the location of every block in the Virtual Unit Chain,
452 and the Erase Unit into which we are supposed to be copying.
453 Go for it.
454 */
455 DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
456 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
457 unsigned char movebuf[512];
458 int ret;
459
460 /* If it's in the target EUN already, or if it's pending write, do nothing */
461 if (BlockMap[block] == targetEUN ||
462 (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
463 continue;
464 }
465
466 /* copy only in non free block (free blocks can only
467 happen in case of media errors or deleted blocks) */
468 if (BlockMap[block] == BLOCK_NIL)
469 continue;
470
471 ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block])
472 + (block * 512), 512, &retlen, movebuf, (char *)&oob);
473 if (ret < 0) {
474 ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block])
475 + (block * 512), 512, &retlen,
476 movebuf, (char *)&oob);
477 if (ret != -EIO)
478 printk("Error went away on retry.\n");
479 }
480 MTD_WRITEECC(nftl->mtd, (nftl->EraseSize * targetEUN) + (block * 512),
481 512, &retlen, movebuf, (char *)&oob);
482 }
483
484 /* add the header so that it is now a valid chain */
485 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum
486 = cpu_to_le16(thisVUC);
487 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
488
489 MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 8,
490 8, &retlen, (char *)&oob.u);
491
492 /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
493
494 /* At this point, we have two different chains for this Virtual Unit, and no way to tell
495 them apart. If we crash now, we get confused. However, both contain the same data, so we
496 shouldn't actually lose data in this case. It's just that when we load up on a medium which
497 has duplicate chains, we need to free one of the chains because it's not necessary any more.
498 */
499 thisEUN = nftl->EUNtable[thisVUC];
500 DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n");
501
502 /* For each block in the old chain (except the targetEUN of course),
503 free it and make it available for future use */
504 while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
505 unsigned int EUNtmp;
506
507 EUNtmp = nftl->ReplUnitTable[thisEUN];
508
509 if (NFTL_formatblock(nftl, thisEUN) < 0) {
510 /* could not erase : mark block as reserved
511 * FixMe: Update Bad Unit Table on disk
512 */
513 nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
514 } else {
515 /* correctly erased : mark it as free */
516 nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
517 nftl->numfreeEUNs++;
518 }
519 thisEUN = EUNtmp;
520 }
521
522 /* Make this the new start of chain for thisVUC */
523 nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
524 nftl->EUNtable[thisVUC] = targetEUN;
525
526 return targetEUN;
527 }
528
529 u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
530 {
531 /* This is the part that needs some cleverness applied.
532 For now, I'm doing the minimum applicable to actually
533 get the thing to work.
534 Wear-levelling and other clever stuff needs to be implemented
535 and we also need to do some assessment of the results when
536 the system loses power half-way through the routine.
537 */
538 u16 LongestChain = 0;
539 u16 ChainLength = 0, thislen;
540 u16 chain, EUN;
541
542 for (chain = 0; chain < nftl->MediaHdr.FormattedSize / nftl->EraseSize; chain++) {
543 EUN = nftl->EUNtable[chain];
544 thislen = 0;
545
546 while (EUN <= nftl->lastEUN) {
547 thislen++;
548 //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
549 EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
550 if (thislen > 0xff00) {
551 printk("Endless loop in Virtual Chain %d: Unit %x\n",
552 chain, EUN);
553 }
554 if (thislen > 0xff10) {
555 /* Actually, don't return failure. Just ignore this chain and
556 get on with it. */
557 thislen = 0;
558 break;
559 }
560 }
561
562 if (thislen > ChainLength) {
563 //printk("New longest chain is %d with length %d\n", chain, thislen);
564 ChainLength = thislen;
565 LongestChain = chain;
566 }
567 }
568
569 if (ChainLength < 2) {
570 printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
571 "Failing request\n");
572 return 0xffff;
573 }
574
575 return NFTL_foldchain (nftl, LongestChain, pendingblock);
576 }
577
578 /* NFTL_findwriteunit: Return the unit number into which we can write
579 for this block. Make it available if it isn't already
580 */
581 static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
582 {
583 u16 lastEUN;
584 u16 thisVUC = block / (nftl->EraseSize / 512);
585 unsigned int writeEUN;
586 unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
587 size_t retlen;
588 int silly, silly2 = 3;
589 struct nftl_oob oob;
590
591 do {
592 /* Scan the media to find a unit in the VUC which has
593 a free space for the block in question.
594 */
595
596 /* This condition catches the 0x[7f]fff cases, as well as
597 being a sanity check for past-end-of-media access
598 */
599 lastEUN = BLOCK_NIL;
600 writeEUN = nftl->EUNtable[thisVUC];
601 silly = MAX_LOOPS;
602 while (writeEUN <= nftl->lastEUN) {
603 struct nftl_bci bci;
604 size_t retlen;
605 unsigned int status;
606
607 lastEUN = writeEUN;
608
609 MTD_READOOB(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs,
610 8, &retlen, (char *)&bci);
611
612 DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
613 block , writeEUN, le16_to_cpu(bci.Status));
614
615 status = bci.Status | bci.Status1;
616 switch(status) {
617 case SECTOR_FREE:
618 return writeEUN;
619
620 case SECTOR_DELETED:
621 case SECTOR_USED:
622 case SECTOR_IGNORE:
623 break;
624 default:
625 // Invalid block. Don't use it any more. Must implement.
626 break;
627 }
628
629 if (!silly--) {
630 printk(KERN_WARNING
631 "Infinite loop in Virtual Unit Chain 0x%x\n",
632 thisVUC);
633 return 0xffff;
634 }
635
636 /* Skip to next block in chain */
637 writeEUN = nftl->ReplUnitTable[writeEUN];
638 }
639
640 /* OK. We didn't find one in the existing chain, or there
641 is no existing chain. */
642
643 /* Try to find an already-free block */
644 writeEUN = NFTL_findfreeblock(nftl, 0);
645
646 if (writeEUN == BLOCK_NIL) {
647 /* That didn't work - there were no free blocks just
648 waiting to be picked up. We're going to have to fold
649 a chain to make room.
650 */
651
652 /* First remember the start of this chain */
653 //u16 startEUN = nftl->EUNtable[thisVUC];
654
655 //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
656 writeEUN = NFTL_makefreeblock(nftl, 0xffff);
657
658 if (writeEUN == BLOCK_NIL) {
659 /* Ouch. This should never happen - we should
660 always be able to make some room somehow.
661 If we get here, we've allocated more storage
662 space than actual media, or our makefreeblock
663 routine is missing something.
664 */
665 printk(KERN_WARNING "Cannot make free space.\n");
666 return BLOCK_NIL;
667 }
668 //printk("Restarting scan\n");
669 lastEUN = BLOCK_NIL;
670 continue;
671 }
672
673 /* We've found a free block. Insert it into the chain. */
674
675 if (lastEUN != BLOCK_NIL) {
676 thisVUC |= 0x8000; /* It's a replacement block */
677 } else {
678 /* The first block in a new chain */
679 nftl->EUNtable[thisVUC] = writeEUN;
680 }
681
682 /* set up the actual EUN we're writing into */
683 /* Both in our cache... */
684 nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
685
686 /* ... and on the flash itself */
687 MTD_READOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8,
688 &retlen, (char *)&oob.u);
689
690 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
691
692 MTD_WRITEOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8,
693 &retlen, (char *)&oob.u);
694
695 /* we link the new block to the chain only after the
696 block is ready. It avoids the case where the chain
697 could point to a free block */
698 if (lastEUN != BLOCK_NIL) {
699 /* Both in our cache... */
700 nftl->ReplUnitTable[lastEUN] = writeEUN;
701 /* ... and on the flash itself */
702 MTD_READOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8,
703 8, &retlen, (char *)&oob.u);
704
705 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
706 = cpu_to_le16(writeEUN);
707
708 MTD_WRITEOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8,
709 8, &retlen, (char *)&oob.u);
710 }
711
712 return writeEUN;
713
714 } while (silly2--);
715
716 printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
717 thisVUC);
718 return 0xffff;
719 }
720
721 static int NFTL_writeblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
722 {
723 u16 writeEUN;
724 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
725 size_t retlen;
726 u8 eccbuf[6];
727
728 writeEUN = NFTL_findwriteunit(nftl, block);
729
730 if (writeEUN == BLOCK_NIL) {
731 printk(KERN_WARNING
732 "NFTL_writeblock(): Cannot find block to write to\n");
733 /* If we _still_ haven't got a block to use, we're screwed */
734 return 1;
735 }
736
737 MTD_WRITEECC(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs,
738 512, &retlen, (char *)buffer, (char *)eccbuf);
739 /* no need to write SECTOR_USED flags since they are written in mtd_writeecc */
740
741 return 0;
742 }
743 #endif /* CONFIG_NFTL_RW */
744
745 static int NFTL_readblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
746 {
747 u16 lastgoodEUN;
748 u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
749 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
750 unsigned int status;
751 int silly = MAX_LOOPS;
752 size_t retlen;
753 struct nftl_bci bci;
754
755 lastgoodEUN = BLOCK_NIL;
756
757 if (thisEUN != BLOCK_NIL) {
758 while (thisEUN < nftl->nb_blocks) {
759 if (MTD_READOOB(nftl->mtd, (thisEUN * nftl->EraseSize) + blockofs,
760 8, &retlen, (char *)&bci) < 0)
761 status = SECTOR_IGNORE;
762 else
763 status = bci.Status | bci.Status1;
764
765 switch (status) {
766 case SECTOR_FREE:
767 /* no modification of a sector should follow a free sector */
768 goto the_end;
769 case SECTOR_DELETED:
770 lastgoodEUN = BLOCK_NIL;
771 break;
772 case SECTOR_USED:
773 lastgoodEUN = thisEUN;
774 break;
775 case SECTOR_IGNORE:
776 break;
777 default:
778 printk("Unknown status for block %d in EUN %d: %x\n",
779 block, thisEUN, status);
780 break;
781 }
782
783 if (!silly--) {
784 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
785 block / (nftl->EraseSize / 512));
786 return 1;
787 }
788 thisEUN = nftl->ReplUnitTable[thisEUN];
789 }
790 }
791
792 the_end:
793 if (lastgoodEUN == BLOCK_NIL) {
794 /* the requested block is not on the media, return all 0x00 */
795 memset(buffer, 0, 512);
796 } else {
797 loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
798 size_t retlen;
799 u_char eccbuf[6];
800 if (MTD_READECC(nftl->mtd, ptr, 512, &retlen, buffer, eccbuf))
801 return -EIO;
802 }
803 return 0;
804 }
805
806 static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
807 {
808 struct NFTLrecord *nftl;
809
810 nftl = NFTLs[MINOR(inode->i_rdev) / 16];
811
812 if (!nftl) return -EINVAL;
813
814 switch (cmd) {
815 case HDIO_GETGEO: {
816 struct hd_geometry g;
817
818 g.heads = nftl->heads;
819 g.sectors = nftl->sectors;
820 g.cylinders = nftl->cylinders;
821 g.start = part_table[MINOR(inode->i_rdev)].start_sect;
822 return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0;
823 }
824 case BLKGETSIZE: /* Return device size */
825 if (!arg) return -EINVAL;
826 return put_user(part_table[MINOR(inode->i_rdev)].nr_sects,
827 (long *) arg);
828
829 case BLKFLSBUF:
830 if (!capable(CAP_SYS_ADMIN)) return -EACCES;
831 fsync_dev(inode->i_rdev);
832 invalidate_buffers(inode->i_rdev);
833 if (nftl->mtd->sync)
834 nftl->mtd->sync(nftl->mtd);
835 return 0;
836
837 case BLKRRPART:
838 if (!capable(CAP_SYS_ADMIN)) return -EACCES;
839 if (nftl->usecount > 1) return -EBUSY;
840 #if LINUX_VERSION_CODE < 0x20328
841 resetup_one_dev(&nftl_gendisk, MINOR(inode->i_rdev) / 16);
842 #else
843 grok_partitions(&nftl_gendisk, MINOR(inode->i_rdev) / 16,
844 1<<4, nftl->nr_sects);
845 #endif
846 return 0;
847
848 #if (LINUX_VERSION_CODE < 0x20303)
849 RO_IOCTLS(inode->i_rdev, arg); /* ref. linux/blk.h */
850 #else
851 case BLKROSET:
852 case BLKROGET:
853 case BLKSSZGET:
854 return blk_ioctl(inode->i_rdev, cmd, arg);
855 #endif
856
857 default:
858 return -EINVAL;
859 }
860 }
861
862 void nftl_request(RQFUNC_ARG)
863 {
864 unsigned int dev, block, nsect;
865 struct NFTLrecord *nftl;
866 char *buffer;
867 struct request *req;
868 int res;
869
870 while (1) {
871 INIT_REQUEST; /* blk.h */
872 req = CURRENT;
873
874 /* We can do this because the generic code knows not to
875 touch the request at the head of the queue */
876 spin_unlock_irq(&io_request_lock);
877
878 DEBUG(MTD_DEBUG_LEVEL2, "NFTL_request\n");
879 DEBUG(MTD_DEBUG_LEVEL3, "NFTL %s request, from sector 0x%04lx for 0x%04lx sectors\n",
880 (req->cmd == READ) ? "Read " : "Write",
881 req->sector, req->current_nr_sectors);
882
883 dev = MINOR(req->rq_dev);
884 block = req->sector;
885 nsect = req->current_nr_sectors;
886 buffer = req->buffer;
887 res = 1; /* succeed */
888
889 if (dev >= MAX_NFTLS * 16) {
890 /* there is no such partition */
891 printk("nftl: bad minor number: device = %s\n",
892 kdevname(req->rq_dev));
893 res = 0; /* fail */
894 goto repeat;
895 }
896
897 nftl = NFTLs[dev / 16];
898 DEBUG(MTD_DEBUG_LEVEL3, "Waiting for mutex\n");
899 down(&nftl->mutex);
900 DEBUG(MTD_DEBUG_LEVEL3, "Got mutex\n");
901
902 if (block + nsect > part_table[dev].nr_sects) {
903 /* access past the end of device */
904 printk("nftl%c%d: bad access: block = %d, count = %d\n",
905 (MINOR(req->rq_dev)>>6)+'a', dev & 0xf, block, nsect);
906 up(&nftl->mutex);
907 res = 0; /* fail */
908 goto repeat;
909 }
910
911 block += part_table[dev].start_sect;
912
913 if (req->cmd == READ) {
914 DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request of 0x%x sectors @ %x "
915 "(req->nr_sectors == %lx)\n", nsect, block, req->nr_sectors);
916
917 for ( ; nsect > 0; nsect-- , block++, buffer += 512) {
918 /* Read a single sector to req->buffer + (512 * i) */
919 if (NFTL_readblock(nftl, block, buffer)) {
920 DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request failed\n");
921 up(&nftl->mutex);
922 res = 0;
923 goto repeat;
924 }
925 }
926
927 DEBUG(MTD_DEBUG_LEVEL2,"NFTL read request completed OK\n");
928 up(&nftl->mutex);
929 goto repeat;
930 } else if (req->cmd == WRITE) {
931 DEBUG(MTD_DEBUG_LEVEL2, "NFTL write request of 0x%x sectors @ %x "
932 "(req->nr_sectors == %lx)\n", nsect, block,
933 req->nr_sectors);
934 #ifdef CONFIG_NFTL_RW
935 for ( ; nsect > 0; nsect-- , block++, buffer += 512) {
936 /* Read a single sector to req->buffer + (512 * i) */
937 if (NFTL_writeblock(nftl, block, buffer)) {
938 DEBUG(MTD_DEBUG_LEVEL1,"NFTL write request failed\n");
939 up(&nftl->mutex);
940 res = 0;
941 goto repeat;
942 }
943 }
944 DEBUG(MTD_DEBUG_LEVEL2,"NFTL write request completed OK\n");
945 #else
946 res = 0; /* Writes always fail */
947 #endif /* CONFIG_NFTL_RW */
948 up(&nftl->mutex);
949 goto repeat;
950 } else {
951 DEBUG(MTD_DEBUG_LEVEL0, "NFTL unknown request\n");
952 up(&nftl->mutex);
953 res = 0;
954 goto repeat;
955 }
956 repeat:
957 DEBUG(MTD_DEBUG_LEVEL3, "end_request(%d)\n", res);
958 spin_lock_irq(&io_request_lock);
959 end_request(res);
960 }
961 }
962
963 static int nftl_open(struct inode *ip, struct file *fp)
964 {
965 int nftlnum = MINOR(ip->i_rdev) / 16;
966 struct NFTLrecord *thisNFTL;
967 thisNFTL = NFTLs[nftlnum];
968
969 DEBUG(MTD_DEBUG_LEVEL2,"NFTL_open\n");
970
971 #ifdef CONFIG_KMOD
972 if (!thisNFTL && nftlnum == 0) {
973 request_module("docprobe");
974 thisNFTL = NFTLs[nftlnum];
975 }
976 #endif
977 if (!thisNFTL) {
978 DEBUG(MTD_DEBUG_LEVEL2,"ENODEV: thisNFTL = %d, minor = %d, ip = %p, fp = %p\n",
979 nftlnum, ip->i_rdev, ip, fp);
980 return -ENODEV;
981 }
982
983 #ifndef CONFIG_NFTL_RW
984 if (fp->f_mode & FMODE_WRITE)
985 return -EROFS;
986 #endif /* !CONFIG_NFTL_RW */
987
988 thisNFTL->usecount++;
989 MOD_INC_USE_COUNT;
990 if (!get_mtd_device(thisNFTL->mtd, -1)) {
991 MOD_DEC_USE_COUNT;
992 return /* -E'SBUGGEREDOFF */ -ENXIO;
993 }
994
995 return 0;
996 }
997
998 static int nftl_release(struct inode *inode, struct file *fp)
999 {
1000 struct super_block *sb = get_super(inode->i_rdev);
1001 struct NFTLrecord *thisNFTL;
1002
1003 thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16];
1004
1005 DEBUG(MTD_DEBUG_LEVEL2, "NFTL_release\n");
1006
1007 fsync_dev(inode->i_rdev);
1008 if (sb)
1009 invalidate_inodes(sb);
1010 invalidate_buffers(inode->i_rdev);
1011
1012 if (thisNFTL->mtd->sync)
1013 thisNFTL->mtd->sync(thisNFTL->mtd);
1014 thisNFTL->usecount--;
1015 MOD_DEC_USE_COUNT;
1016
1017 put_mtd_device(thisNFTL->mtd);
1018
1019 return 0;
1020 }
1021 #if LINUX_VERSION_CODE < 0x20326
1022 static struct file_operations nftl_fops = {
1023 read: block_read,
1024 write: block_write,
1025 ioctl: nftl_ioctl,
1026 open: nftl_open,
1027 release: nftl_release,
1028 fsync: block_fsync,
1029 };
1030 #else
1031 static struct block_device_operations nftl_fops =
1032 {
1033 open: nftl_open,
1034 release: nftl_release,
1035 ioctl: nftl_ioctl
1036 };
1037 #endif
1038
1039
1040
1041 /****************************************************************************
1042 *
1043 * Module stuff
1044 *
1045 ****************************************************************************/
1046
1047 #if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
1048 #define init_nftl init_module
1049 #define cleanup_nftl cleanup_module
1050 #endif
1051
1052 static struct mtd_notifier nftl_notifier = {NFTL_notify_add, NFTL_notify_remove, NULL};
1053
1054 /* static int __init init_nftl(void) */
1055 int __init init_nftl(void)
1056 {
1057 int i;
1058
1059 printk(KERN_NOTICE
1060 "M-Systems NAND Flash Translation Layer driver. (C) 1999 MVHI\n");
1061 #ifdef PRERELEASE
1062 printk(KERN_INFO"$Id: nftl.c,v 1.57 2000/12/01 17:51:54 dwmw2 Exp $\n");
1063 #endif
1064
1065 if (register_blkdev(MAJOR_NR, "nftl", &nftl_fops)){
1066 printk("unable to register NFTL block device on major %d\n", MAJOR_NR);
1067 return -EBUSY;
1068 } else {
1069 #if LINUX_VERSION_CODE < 0x20320
1070 blk_dev[MAJOR_NR].request_fn = nftl_request;
1071 #else
1072 blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request);
1073 #endif
1074 /* set block size to 1kB each */
1075 for (i = 0; i < 256; i++) {
1076 nftl_blocksizes[i] = 1024;
1077 }
1078 blksize_size[MAJOR_NR] = nftl_blocksizes;
1079
1080 nftl_gendisk.next = gendisk_head;
1081 gendisk_head = &nftl_gendisk;
1082 }
1083
1084 register_mtd_user(&nftl_notifier);
1085
1086 return 0;
1087 }
1088
1089 static void __exit cleanup_nftl(void)
1090 {
1091 struct gendisk *gd, **gdp;
1092
1093 unregister_mtd_user(&nftl_notifier);
1094 unregister_blkdev(MAJOR_NR, "nftl");
1095
1096 #if LINUX_VERSION_CODE < 0x20320
1097 blk_dev[MAJOR_NR].request_fn = 0;
1098 #else
1099 blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
1100 #endif
1101
1102 /* remove ourself from generic harddisk list
1103 FIXME: why can't I found this partition on /proc/partition */
1104 for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
1105 if (*gdp == &nftl_gendisk) {
1106 gd = *gdp; *gdp = gd->next;
1107 break;
1108 }
1109 }
1110
1111 module_init(init_nftl);
1112 module_exit(cleanup_nftl);
1113
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.