~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/drivers/mtd/nftl.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.