Merge with Linux 2.4.0-test4-pre3.
[linux-2.6/linux-mips.git] / drivers / mtd / nftl.c
blob182ec5a96f6c3518a131d0e68bd452bea597917b
2 /* Linux driver for NAND Flash Translation Layer */
3 /* (c) 1999 Machine Vision Holdings, Inc. */
4 /* Author: David Woodhouse <dwmw2@infradead.org> */
5 /* $Id: nftl.c,v 1.35 2000/07/06 14:35:01 dwmw2 Exp $ */
7 /*
8 The contents of this file are distributed under the GNU Public
9 Licence version 2 ("GPL"). The legal note below refers only to the
10 _use_ of the code in some jurisdictions, and does not in any way
11 affect the copying, distribution and modification of this code,
12 which is permitted under the terms of the GPL.
14 Section 0 of the GPL says:
16 "Activities other than copying, distribution and modification are not
17 covered by this License; they are outside its scope."
19 You may copy, distribute and modify this code to your hearts'
20 content - it's just that in some jurisdictions, you may only _use_
21 it under the terms of the licence below. This puts it in a similar
22 situation to the ISDN code, which you may need telco approval to
23 use, and indeed any code which has uses that may be restricted in
24 law. For example, certain malicious uses of the networking stack
25 may be illegal, but that doesn't prevent the networking code from
26 being under GPL.
28 In fact the ISDN case is worse than this, because modification of
29 the code automatically invalidates its approval. Modificiation,
30 unlike usage, _is_ one of the rights which is protected by the
31 GPL. Happily, the law in those places where approval is required
32 doesn't actually prevent you from modifying the code - it's just
33 that you may not be allowed to _use_ it once you've done so - and
34 because usage isn't addressed by the GPL, that's just fine.
36 dwmw2@infradead.org
37 6/7/0
39 LEGAL NOTE: The NFTL format is patented by M-Systems. They have
40 granted a licence for its use with their DiskOnChip products:
42 "M-Systems grants a royalty-free, non-exclusive license under
43 any presently existing M-Systems intellectual property rights
44 necessary for the design and development of NFTL-compatible
45 drivers, file systems and utilities to use the data formats with,
46 and solely to support, M-Systems' DiskOnChip products"
48 A signed copy of this agreement from M-Systems is kept on file by
49 Red Hat UK Limited. In the unlikely event that you need access to it,
50 please contact dwmw2@redhat.com for assistance. */
52 #define PRERELEASE
54 #ifdef NFTL_DEBUG
55 #define DEBUGLVL debug
56 #endif
58 #include <linux/config.h>
59 #include <linux/kernel.h>
60 #include <linux/module.h>
61 #include <asm/errno.h>
62 #include <asm/io.h>
63 #include <asm/uaccess.h>
64 #include <linux/miscdevice.h>
65 #include <linux/pci.h>
66 #include <linux/delay.h>
67 #include <linux/malloc.h>
68 #include <linux/sched.h>
69 #include <linux/init.h>
70 #include <linux/mtd/mtd.h>
71 #include <linux/mtd/nftl.h>
72 #include <linux/mtd/compatmac.h>
74 #undef WE_KNOW_WTF_THIS_DOES_NOT_WORK
76 /* NFTL block device stuff */
77 #define MAJOR_NR NFTL_MAJOR
78 #define DEVICE_REQUEST nftl_request
79 #define DEVICE_OFF(device)
80 #ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK
81 #define LOCAL_END_REQUEST
82 #endif
83 #include <linux/blk.h>
84 #include <linux/hdreg.h>
87 #ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK
89 static void nftl_end_request(struct request *req, int res)
91 req->sector += req->current_nr_sectors;
92 req->nr_sectors -= req->current_nr_sectors;
94 if (end_that_request_first( req, res, "nftl" ))
95 return;
96 end_that_request_last( req );
98 #endif
100 #ifdef NFTL_DEBUG
101 static int debug = NFTL_DEBUG;
102 MODULE_PARM(debug, "i");
103 #endif
107 /* Linux-specific block device functions */
109 /* I _HATE_ the Linux block device setup more than anything else I've ever
110 * encountered, except ...
113 static int nftl_sizes[256]={0,};
114 static int nftl_blocksizes[256] = {0,};
116 /* .. for the Linux partition table handling. */
118 struct hd_struct part_table[256] = {{0,0},};
120 #if LINUX_VERSION_CODE < 0x20328
121 static void dummy_init (struct gendisk *crap)
123 #endif
125 static struct gendisk nftl_gendisk = {
126 NFTL_MAJOR, /* Major number */
127 "nftl", /* Major name */
128 4, /* Bits to shift to get real from partition */
129 15, /* Number of partitions per real */
130 #if LINUX_VERSION_CODE < 0x20328
131 MAX_NFTLS, /* maximum number of real */
132 dummy_init, /* init function */
133 #endif
134 part_table, /* hd struct */
135 nftl_sizes, /* block sizes */
136 0, /* number */
137 NULL, /* internal use, not presently used */
138 NULL /* next */
142 struct NFTLrecord *NFTLs[MAX_NFTLS] = {NULL};
144 static void NFTL_setup(struct mtd_info *mtd, unsigned long ofs,
145 struct NFTLMediaHeader *hdr)
147 int i;
148 struct NFTLrecord *thisNFTL;
149 unsigned long temp;
150 int firstfree = -1;
152 DEBUG(1,"NFTL_setup\n");
154 for (i=0; i < MAX_NFTLS; i++) {
155 if (!NFTLs[i] && firstfree==-1)
156 firstfree = i;
157 else if (NFTLs[i] && NFTLs[i]->mtd == mtd &&
158 NFTLs[i]->MediaHdr.FirstPhysicalEUN == hdr->FirstPhysicalEUN) {
159 /* This is a Spare Media Header for an NFTL we've already found */
160 DEBUG(1, "Spare Media Header for NFTL %d found at %lx\n",i, ofs);
161 NFTLs[i]->SpareMediaUnit = ofs / mtd->erasesize;
162 return;
167 /* OK, it's a new one. Set up all the data structures. */
168 #ifdef PSYCHO_DEBUG
169 printk("Found new NFTL nftl%c at offset %lx\n",firstfree + 'a', ofs);
170 #endif
171 if (hdr->UnitSizeFactor != 0xff) {
172 printk("Sorry, we don't support UnitSizeFactor of != 1 yet\n");
173 return;
176 thisNFTL = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
177 if (!thisNFTL) {
178 printk(KERN_WARNING "Out of memory for NFTL data structures\n");
179 return;
181 init_MUTEX(&thisNFTL->mutex);
182 thisNFTL->EraseSize = mtd->erasesize;
183 memcpy(&thisNFTL->MediaHdr, hdr, sizeof(*hdr));
184 thisNFTL->mtd = mtd;
185 thisNFTL->MediaUnit = ofs / mtd->erasesize;
186 thisNFTL->SpareMediaUnit = 0xffff;
187 thisNFTL->numvunits = le32_to_cpu(thisNFTL->MediaHdr.FormattedSize) / 8192;
188 thisNFTL->nr_sects = thisNFTL->numvunits * (thisNFTL->EraseSize / 512);
189 thisNFTL->usecount = 0;
191 thisNFTL->cylinders = 1024;
192 thisNFTL->heads = 16;
194 temp = thisNFTL->cylinders * thisNFTL->heads;
195 thisNFTL->sectors = thisNFTL->nr_sects / temp;
197 if (thisNFTL->nr_sects % temp) {
199 thisNFTL->sectors++;
200 temp = thisNFTL->cylinders * thisNFTL->sectors;
201 thisNFTL->heads = thisNFTL->nr_sects / temp;
203 if (thisNFTL->nr_sects & temp) {
204 thisNFTL->heads++;
205 temp = thisNFTL->heads * thisNFTL->sectors;
207 thisNFTL->cylinders = thisNFTL->nr_sects / temp;
210 if (thisNFTL->nr_sects != thisNFTL->heads * thisNFTL->cylinders *
211 thisNFTL->sectors) {
212 printk(KERN_WARNING "Cannot calculate an NFTL geometry to match size of 0x%lx.\n", thisNFTL->nr_sects);
213 printk(KERN_WARNING "Using C:%d H:%d S:%d (== %lx sects)\n",
214 thisNFTL->cylinders, thisNFTL->heads ,
215 thisNFTL->sectors,
216 (long)thisNFTL->cylinders * (long)thisNFTL->heads *
217 (long)thisNFTL->sectors );
219 /* Oh no we don't
220 * thisNFTL->nr_sects = thisNFTL->heads * thisNFTL->cylinders * thisNFTL->sectors;
225 thisNFTL->EUNtable = kmalloc( 2 * thisNFTL->numvunits,
226 GFP_KERNEL);
227 if (!thisNFTL->EUNtable) {
228 printk("ENOMEM\n");
229 kfree(thisNFTL);
230 return;
232 memset(thisNFTL->EUNtable, 0xff, 2 * thisNFTL->numvunits);
234 thisNFTL->VirtualUnitTable = kmalloc( 2 * le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) , GFP_KERNEL);
235 if (!thisNFTL->VirtualUnitTable) {
236 printk("ENOMEM\n");
237 kfree(thisNFTL->EUNtable);
238 kfree(thisNFTL);
239 return;
241 memset(thisNFTL->VirtualUnitTable, 0xff, 2 * le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits));
243 thisNFTL->ReplUnitTable = kmalloc( 2 * le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) , GFP_KERNEL);
244 if (!thisNFTL->ReplUnitTable) {
245 printk("ENOMEM\n");
246 kfree(thisNFTL->VirtualUnitTable);
247 kfree(thisNFTL->EUNtable);
248 kfree(thisNFTL);
249 return;
251 memset(thisNFTL->ReplUnitTable, 0xff, 2 *le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) );
253 /* Ought to check the media header for bad blocks */
254 thisNFTL->lastEUN = le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) +
255 le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN) - 1;
256 thisNFTL->numfreeEUNs = 0;
258 /* Scan each physical Erase Unit for validity and to find the
259 Virtual Erase Unit Chain to which it belongs */
261 for (i=le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN);
262 i <= thisNFTL->lastEUN; i++) {
264 union nftl_uci uci;
265 unsigned long ofs;
266 size_t retlen;
267 ofs = i * thisNFTL->EraseSize;
269 MTD_READOOB(mtd, (i * thisNFTL->EraseSize) + 512 + 8, 8, &retlen, (char *)&uci);
271 if (uci.b.EraseMark != cpu_to_le16(0x3c69) ||
272 uci.b.EraseMark1 != cpu_to_le16(0x3c69)) {
273 printk("EUN %d: EraseMark not 0x3c69 (0x%4.4x 0x%4.4x instead)\n",
274 i, le16_to_cpu(uci.b.EraseMark), le16_to_cpu(uci.b.EraseMark1));
275 thisNFTL->VirtualUnitTable[i] = 0x7fff;
276 thisNFTL->ReplUnitTable[i] = 0xffff;
277 continue;
280 MTD_READOOB(mtd, (i * thisNFTL->EraseSize) + 8, 8, &retlen, (u_char *)&uci);
282 if (uci.a.VirtUnitNum != uci.a.SpareVirtUnitNum)
283 printk("EUN %d: VirtualUnitNumber (%x) != SpareVirtualUnitNumber (%x)\n",
284 i, le16_to_cpu(uci.a.VirtUnitNum),
285 le16_to_cpu(uci.a.SpareVirtUnitNum));
287 if (uci.a.ReplUnitNum != uci.a.SpareReplUnitNum)
288 printk("EUN %d: ReplacementUnitNumber (%x) != SpareReplacementUnitNumber (%x)\n",
289 i, le16_to_cpu(uci.a.ReplUnitNum),
290 le16_to_cpu(uci.a.SpareReplUnitNum));
292 /* We don't actually _do_ anything about the above, just whinge */
294 thisNFTL->VirtualUnitTable[i] = le16_to_cpu(uci.a.VirtUnitNum);
295 thisNFTL->ReplUnitTable[i] = le16_to_cpu(uci.a.ReplUnitNum);
297 /* if (!(VUN & 0x8000) && VUN < (arraybounds)).. optimises to: */
298 if (le16_to_cpu(uci.a.VirtUnitNum) < thisNFTL->numvunits)
299 thisNFTL->EUNtable[le16_to_cpu(uci.a.VirtUnitNum) & 0x7fff] = i;
301 if (uci.a.VirtUnitNum == 0xffff) {
302 /* Free block */
303 thisNFTL->LastFreeEUN = i;
304 thisNFTL->numfreeEUNs++;
308 NFTLs[firstfree] = thisNFTL;
309 thisNFTL->LastFreeEUN = le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN);
311 //#define PSYCHO_DEBUG
312 #ifdef PSYCHO_DEBUG
313 for (i=0; i < 10/* thisNFTL->numvunits*/; i++) {
314 u16 curEUN = thisNFTL->EUNtable[i];
315 int sillycount=100;
317 printk("Virtual Unit #%d: ",i);
318 if (!curEUN || curEUN == 0xffff) {
319 printk("Not present\n");
320 continue;
322 printk("%d", curEUN);
324 while ((curEUN = thisNFTL->ReplUnitTable[curEUN]) != 0xffff && --sillycount) {
325 printk(", %d", curEUN & 0xffff);
328 printk("\n");
330 #endif
332 /* OK. Now we deal with the fact that we're in the real world. Sometimes
333 things don't actually happen the way they're supposed to. Find, fix,
334 and whinge about the most common deviations from spec that we have
335 been known to encounter.
337 /* Except that I haven't implemented that bit yet :) */
339 /* Finally, set up the block device sizes */
340 nftl_sizes[firstfree * 16]=thisNFTL->nr_sects;
341 // nftl_blocksizes[firstfree*16] = 512;
342 part_table[firstfree * 16].nr_sects = thisNFTL->nr_sects;
343 #if LINUX_VERSION_CODE < 0x20328
344 resetup_one_dev(&nftl_gendisk, firstfree);
345 #else
346 grok_partitions(&nftl_gendisk, firstfree, 1<<4, thisNFTL->nr_sects);
347 #endif
352 static void NFTL_unsetup(int i)
354 struct NFTLrecord *thisNFTL = NFTLs[i];
356 DEBUG(1, "NFTL_unsetup %d\n", i);
358 NFTLs[i] = NULL;
360 if (thisNFTL->VirtualUnitTable)
361 kfree(thisNFTL->VirtualUnitTable);
362 if (thisNFTL->ReplUnitTable)
363 kfree(thisNFTL->ReplUnitTable);
364 if (thisNFTL->EUNtable)
365 kfree(thisNFTL->EUNtable);
367 kfree(thisNFTL);
373 /* Search the MTD device for NFTL partitions */
374 static void NFTL_notify_add(struct mtd_info *mtd)
376 int i;
377 unsigned long ofs;
378 struct NFTLMediaHeader hdr;
380 DEBUG(1, "NFTL_notify_add for %s\n", mtd->name);
382 if (mtd) {
383 if (!mtd->read_oob) /* If this MTD doesn't have out-of-band data,
384 then there's no point continuing */
386 DEBUG(1, "No OOB data, quitting\n");
387 return;
389 DEBUG(3, "mtd->read = %p,size = %d, erasesize = %d\n",
390 mtd->read, mtd->size, mtd->erasesize);
391 for (ofs = 0; ofs < mtd->size ; ofs += mtd->erasesize) {
392 size_t retlen = 0;
393 MTD_READ(mtd, ofs, sizeof(hdr), &retlen, (u_char *)&hdr);
395 if (retlen < sizeof(hdr))
397 continue;
400 if (!strncmp(hdr.DataOrgID, "ANAND", 6)) {
401 DEBUG(2, "Valid NFTL partition at ofs %ld\n", ofs);
402 NFTL_setup(mtd, ofs, &hdr);
404 else {
405 DEBUG(3,"No valid NFTL Partition at ofs %d\n", ofs);
406 for(i = 0; i < 6; i++) {
407 DEBUG(3,"%x, ", hdr.DataOrgID[i]);
409 DEBUG(3," = %s\n", hdr.DataOrgID);
410 DEBUG(3,"%d, %d, %d, %d\n", hdr.NumEraseUnits, hdr.FirstPhysicalEUN,
411 hdr.FormattedSize, hdr.UnitSizeFactor);
415 return;
419 static void NFTL_notify_remove(struct mtd_info *mtd)
421 int i;
423 for (i=0; i< MAX_NFTLS; i++) {
424 if (NFTLs[i] && NFTLs[i]->mtd == mtd)
425 NFTL_unsetup(i);
430 #ifdef CONFIG_NFTL_RW
432 /* Actual NFTL access routines */
435 static u16 NFTL_findfreeblock( struct NFTLrecord *thisNFTL, int desperate )
437 /* For a given Virtual Unit Chain: find or create a free block and
438 add it to the chain */
439 /* We're passed the number of the last EUN in the chain, to save us from
440 having to look it up again */
442 u16 pot = thisNFTL->LastFreeEUN;
443 int silly = -1;
445 /* Normally, we force a fold to happen before we run out of free blocks completely */
447 if (!desperate && thisNFTL->numfreeEUNs < 2) {
448 // printk("NFTL_findfreeblock: there are too few free EUNs\n");
449 return 0xffff;
452 /* Scan for a free block */
454 do {
455 if (thisNFTL->VirtualUnitTable[pot] == 0xffff) {
456 thisNFTL->LastFreeEUN = pot;
457 thisNFTL->numfreeEUNs--;
458 return pot;
461 if (++pot > thisNFTL->lastEUN)
462 pot = le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN);
464 if (!silly--) {
465 printk("Tell Dave he fucked up. LastFreeEUN = %d, FirstEUN = %d\n",
466 thisNFTL->LastFreeEUN, le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN));
467 return 0xffff;
470 } while (pot != thisNFTL->LastFreeEUN);
472 return 0xffff;
479 static u16 NFTL_foldchain (struct NFTLrecord *thisNFTL, u16 thisVUC, unsigned pendingblock )
481 u16 BlockMap[thisNFTL->EraseSize / 512];
482 unsigned char BlockLastState[thisNFTL->EraseSize / 512];
483 unsigned char BlockFreeFound[thisNFTL->EraseSize / 512];
484 u16 thisEUN;
485 int block;
486 int silly = -1;
487 u16 targetEUN = 0xffff;
488 struct nftl_oob oob;
489 int inplace = 1;
491 memset(BlockMap, 0xff, sizeof(BlockMap));
492 memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
494 thisEUN = thisNFTL->EUNtable[thisVUC];
496 if (thisEUN == 0xffff) {
497 printk(KERN_WARNING "Trying to fold non-existent Virtual Unit Chain %d!\n", thisVUC);
498 return 0xffff;
501 /* Scan to find the Erase Unit which holds the actual data for each
502 512-byte block within the Chain.
505 while( thisEUN <= thisNFTL->lastEUN ) {
506 size_t retlen;
508 targetEUN = thisEUN;
510 for (block = 0 ; block < thisNFTL->EraseSize / 512; block ++) {
512 MTD_READOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + (block * 512),16 , &retlen, (char *)&oob);
514 if (block == 2) {
515 if (oob.u.c.WriteInh != 0xffffffff) {
516 printk("Write Inhibited on EUN %d\n", thisEUN);
517 inplace = 0;
518 } else {
519 /* There's no other reason not to do inplace,
520 except ones that come later. So we don't need
521 to preserve inplace */
522 inplace = 1;
526 BlockLastState[block] = (unsigned char) oob.b.Status & 0xff;
528 switch(oob.b.Status) {
529 case __constant_cpu_to_le16(BLOCK_FREE):
530 BlockFreeFound[block]=1;
531 break;
533 case __constant_cpu_to_le16(BLOCK_USED):
534 if (!BlockFreeFound[block])
535 BlockMap[block] = thisEUN;
536 else
537 printk(KERN_WARNING "BLOCK_USED found after BLOCK_FREE in Virtual Unit Chain %d for block %d\n", thisVUC, block);
538 break;
539 case __constant_cpu_to_le16(BLOCK_IGNORE):
540 case __constant_cpu_to_le16(BLOCK_DELETED):
541 break;
542 default:
543 printk("Unknown status for block %d in EUN %d: %x\n",block,thisEUN, oob.b.Status);
547 if (!silly--) {
548 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", thisVUC);
549 return 0xffff;
552 thisEUN = thisNFTL->ReplUnitTable[thisEUN] & 0x7fff;
555 if (inplace) {
556 /* We're being asked to be a fold-in-place. Check
557 that all blocks are either present or BLOCK_FREE
558 in the target block. If not, we're going to have
559 to fold out-of-place anyway.
562 for (block = 0; block < thisNFTL->EraseSize / 512 ; block++) {
564 if (BlockLastState[block] != (unsigned char) (cpu_to_le16(BLOCK_FREE) & 0xff) &&
565 BlockMap[block] != targetEUN) {
566 DEBUG(1, "Setting inplace to 0. VUC %d, block %d was %x lastEUN, and is in EUN %d (%s) %d\n",
567 thisVUC, block, BlockLastState[block], BlockMap[block] , BlockMap[block]==targetEUN?"==":"!=", targetEUN);
569 inplace = 0;
570 break;
574 if ( pendingblock >= (thisVUC * (thisNFTL->EraseSize / 512)) &&
575 pendingblock < ((thisVUC + 1)* (thisNFTL->EraseSize / 512)) &&
576 BlockLastState[ pendingblock - (thisVUC * (thisNFTL->EraseSize / 512))] !=
577 (unsigned char) (cpu_to_le16(BLOCK_FREE) & 0xff)) {
578 DEBUG(1, "Pending write not free in EUN %d. Folding out of place.\n", targetEUN);
579 inplace = 0;
584 if (!inplace) {
585 DEBUG(1, "Cannot fold Virtual Unit Chain %d in place. Trying out-of-place\n", thisVUC);
586 /* We need to find a targetEUN to fold into. */
587 targetEUN = NFTL_findfreeblock(thisNFTL, 1);
588 if (targetEUN == 0xffff) {
589 /* Ouch. Now we're screwed. We need to do a
590 fold-in-place of another chain to make room
591 for this one. We need a better way of selecting
592 which chain to fold, because makefreeblock will
593 only ask us to fold the same one again.
595 printk(KERN_WARNING"NFTL_findfreeblock(desperate) returns 0xffff.\n");
596 return 0xffff;
602 /* OK. We now know the location of every block in the Virtual Unit Chain,
603 and the Erase Unit into which we are supposed to be copying.
604 Go for it.
607 DEBUG(1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
609 for (block = 0; block < thisNFTL->EraseSize / 512 ; block++) {
610 unsigned char movebuf[512];
611 struct nftl_oob oob;
612 size_t retlen;
614 memset(&oob, 0xff, sizeof(oob));
616 /* If it's in the target EUN already, or if it's pending write, do nothing */
617 if (BlockMap[block] == targetEUN ||(pendingblock == (thisVUC * (thisNFTL->EraseSize / 512) + block))) {
618 /* Except if it's the first block, in which case we have to
619 set the UnitNumbers */
620 if (block == 0) {
622 thisNFTL->mtd->read_oob(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) ,
623 16, &retlen, (char *)&oob);
625 // printk("Setting VirtUnitNum on EUN %d to %x, was %x\n", targetEUN, thisVUC,
626 // le16_to_cpu(oob.u.a.VirtUnitNum));
628 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC & 0x7fff);
630 thisNFTL->mtd->write_oob(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) ,
631 16, &retlen, (char *)&oob);
633 continue;
636 oob.b.Status = BLOCK_USED;
638 switch(block) {
639 case 0:
640 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC & 0x7fff);
641 // printk("Setting VirtUnitNum on EUN %d to %x\n", targetEUN, thisVUC);
643 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
644 break;
646 case 1:
647 oob.u.b.WearInfo = cpu_to_le32(3); // We don't use this, but M-Systems' drivers do
648 oob.u.b.EraseMark = oob.u.b.EraseMark1 = cpu_to_le16(0x3c69);
649 break;
651 case 2:
652 default:
653 oob.u.c.WriteInh = 0xffffffff;
654 oob.u.c.unused = 0xffffffff;
656 if (thisNFTL->mtd->read_ecc(thisNFTL->mtd, (thisNFTL->EraseSize * BlockMap[block]) + (block * 512),
657 512, &retlen, movebuf, (char *)&oob) == -EIO) {
658 if (thisNFTL->mtd->read_ecc(thisNFTL->mtd, (thisNFTL->EraseSize * BlockMap[block]) + (block * 512),
659 512, &retlen, movebuf, (char *)&oob) != -EIO)
660 printk("Error went away on retry.\n");
663 thisNFTL->mtd->write_ecc(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) + (block * 512),
664 512, &retlen, movebuf, (char *)&oob);
667 /* FIXME: Add some error checking.... */
668 thisNFTL->mtd->write_oob(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) + (block * 512),
669 16, &retlen, (char *)&oob);
673 /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
675 /* At this point, we have two different chains for this Virtual Unit, and no way to tell
676 them apart. If we crash now, we get confused. However, both contain the same data, so we
677 shouldn't actually lose data in this case. It's just that when we load up on a medium which
678 has duplicate chains, we need to free one of the chains because it's not necessary any more.
682 thisEUN = thisNFTL->EUNtable[thisVUC];
684 DEBUG(1,"Want to erase\n");
685 /* For each block in the old chain (except the targetEUN of course),
686 free it and make it available for future use */
688 while( thisEUN <= thisNFTL->lastEUN && thisEUN != targetEUN) {
689 size_t retlen;
690 struct erase_info *instr;
691 u16 EUNtmp;
693 instr = kmalloc(sizeof(struct erase_info), GFP_KERNEL);
694 if (!instr) {
695 printk(KERN_WARNING "Out of memory for struct erase_info\n");
697 EUNtmp = thisEUN;
699 thisEUN = thisNFTL->ReplUnitTable[EUNtmp] & 0x7fff;
700 thisNFTL->VirtualUnitTable[EUNtmp] = 0x7fff;
701 thisNFTL->ReplUnitTable[EUNtmp] = 0xffff;
702 } else {
703 memset(instr, 0, sizeof(struct erase_info));
704 instr->addr = thisEUN * thisNFTL->EraseSize;
705 instr->len = thisNFTL->EraseSize;
707 MTD_ERASE(thisNFTL->mtd, instr);
708 /* This is an async interface. Or will be. At which point
709 this code will break. */
711 #if 0
712 MTD_READOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + 512, 16, &retlen, (char *)&oob);
714 printk("After erasing, EUN %d contains: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
715 thisEUN, oob.b.ECCSig[0],
716 oob.b.ECCSig[1],
717 oob.b.ECCSig[2],
718 oob.b.ECCSig[3],
719 oob.b.ECCSig[4],
720 oob.b.ECCSig[5]);
721 #endif
722 memset(&oob, 0xff, sizeof(oob));
723 oob.u.b.WearInfo = cpu_to_le32(3);
724 oob.u.b.EraseMark = oob.u.b.EraseMark1 = cpu_to_le16(0x3c69);
726 MTD_WRITEOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + 512, 16, &retlen, (char *)&oob);
728 EUNtmp = thisEUN;
730 thisEUN = thisNFTL->ReplUnitTable[EUNtmp] & 0x7fff;
731 thisNFTL->VirtualUnitTable[EUNtmp] = 0xffff;
732 thisNFTL->ReplUnitTable[EUNtmp] = 0xffff;
734 thisNFTL->numfreeEUNs++;
738 // shifted upwards: thisEUN = thisNFTL->ReplUnitTable[thisEUN] & 0x7fff;
742 /* Make this the new start of chain for thisVUC */
743 thisNFTL->VirtualUnitTable[targetEUN] = thisVUC;
744 thisNFTL->ReplUnitTable[targetEUN] = 0xffff;
746 thisNFTL->EUNtable[thisVUC] = targetEUN;
747 return targetEUN;
751 u16 NFTL_makefreeblock( struct NFTLrecord *thisNFTL , unsigned pendingblock)
753 /* This is the part that needs some cleverness applied.
754 For now, I'm doing the minimum applicable to actually
755 get the thing to work.
756 Wear-levelling and other clever stuff needs to be implemented
757 and we also need to do some assessment of the results when
758 the system loses power half-way through the routine.
761 u16 LongestChain = 0;
762 u16 ChainLength = 0, thislen;
763 u16 chain, EUN;
766 for (chain=0; chain < thisNFTL->MediaHdr.FormattedSize / thisNFTL->EraseSize; chain++) {
767 EUN = thisNFTL->EUNtable[chain];
769 thislen = 0;
771 while (EUN <= thisNFTL->lastEUN) {
772 thislen++;
773 // printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
774 EUN = thisNFTL->ReplUnitTable[EUN] & 0x7fff;
775 if (thislen > 0xff00) {
776 printk("Endless loop in Virtual Chain %d: Unit %x\n", chain, EUN);
778 if (thislen > 0xff10) {
779 /* Actually, don't return failure. Just ignore this chain and
780 get on with it. */
781 thislen = 0;
782 break;
788 if (thislen > ChainLength) {
789 // printk("New longest chain is %d with length %d\n", chain, thislen);
790 ChainLength = thislen;
791 LongestChain = chain;
795 if (ChainLength < 2) {
796 printk(KERN_WARNING "No Virtual Unit Chains available for folding. Failing request\n");
797 return 0xffff;
800 return NFTL_foldchain (thisNFTL, LongestChain, pendingblock);
803 /* NFTL_findwriteunit: Return the unit number into which we can write
804 for this block. Make it available if it isn't already
807 static inline u16 NFTL_findwriteunit(struct NFTLrecord *thisNFTL, unsigned block)
809 u16 lastEUN;
810 u16 thisVUC = block / (thisNFTL->EraseSize / 512);
811 u16 writeEUN;
812 unsigned long blockofs = (block * 512) & (thisNFTL->EraseSize -1);
813 size_t retlen;
814 int silly = 0x10000, silly2 = 3;
815 struct nftl_oob oob;
816 int debug=0;
818 do {
820 /* Scan the media to find a unit in the VUC which has
821 a free space for the block in question.
824 /* This condition catches the 0x[7f]fff cases, as well as
825 being a sanity check for past-end-of-media access
827 lastEUN = 0xffff;
828 writeEUN = thisNFTL->EUNtable[thisVUC];
830 while(writeEUN <= thisNFTL->lastEUN) {
831 struct nftl_bci bci;
832 size_t retlen;
834 lastEUN = writeEUN;
836 MTD_READOOB(thisNFTL->mtd, (writeEUN * thisNFTL->EraseSize)
837 + blockofs,8, &retlen, (char *)&bci);
839 if (debug)
840 printk("Status of block %d in EUN %d is %x\n", block , writeEUN, le16_to_cpu(bci.Status));
842 switch(bci.Status) {
843 case __constant_cpu_to_le16(BLOCK_FREE):
844 return writeEUN;
846 case __constant_cpu_to_le16(BLOCK_DELETED):
847 case __constant_cpu_to_le16(BLOCK_USED):
848 case __constant_cpu_to_le16(BLOCK_IGNORE):
849 break;
850 default:
851 // Invalid block. Don't use it any more. Must implement.
852 break;
855 if (!silly--) {
856 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", thisVUC);
857 return 0xffff;
860 /* Skip to next block in chain */
862 writeEUN = thisNFTL->ReplUnitTable[writeEUN] & 0x7fff;
865 /* OK. We didn't find one in the existing chain, or there
866 is no existing chain. */
868 /* Try to find an already-free block */
870 writeEUN = NFTL_findfreeblock(thisNFTL, 0);
872 if (writeEUN == 0xffff) {
873 /* That didn't work - there were no free blocks just
874 waiting to be picked up. We're going to have to fold
875 a chain to make room.
878 /* First remember the start of this chain */
879 // u16 startEUN = thisNFTL->EUNtable[thisVUC];
881 //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
882 writeEUN = NFTL_makefreeblock(thisNFTL, block);
884 if (writeEUN == 0xffff) {
885 /* Ouch. This should never happen - we should
886 always be able to make some room somehow.
887 If we get here, we've allocated more storage
888 space than actual media, or our makefreeblock
889 routine is missing something.
891 printk(KERN_WARNING "Cannot make free space.\n");
892 return 0xffff;
894 // printk("Restarting scan\n");
895 lastEUN = 0xffff;
896 // debug = 1;
897 continue;
898 #if 0
899 if (startEUN != thisNFTL->EUNtable[thisVUC]) {
900 /* The fold operation has moved the chain
901 that we're looking at. Start the scan again.
903 continue;
905 #endif
908 /* We've found a free block. Insert it into the chain. */
910 if (lastEUN != 0xffff) {
911 /* Addition to an existing chain. Make the previous
912 last block in the chain point to this one.
915 //printk("Linking EUN %d to EUN %d in VUC %d\n",
916 // lastEUN, writeEUN, thisVUC);
917 /* Both in our cache... */
918 thisNFTL->ReplUnitTable[lastEUN] = writeEUN;
921 /* ... and on the flash itself */
922 MTD_READOOB(thisNFTL->mtd, (lastEUN * thisNFTL->EraseSize), 16, &retlen,
923 (char *)&oob);
925 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = cpu_to_le16(writeEUN);
927 MTD_WRITEOOB(thisNFTL->mtd, (lastEUN * thisNFTL->EraseSize), 16, &retlen,
928 (char *)&oob);
930 thisVUC |= 0x8000; /* It's a replacement block */
931 } else {
932 /* The first block in a new chain */
933 thisNFTL->EUNtable[thisVUC] = writeEUN;
936 /* Now set up the actual EUN we're writing into */
938 /* Both in our cache... */
939 thisNFTL->VirtualUnitTable[writeEUN] = thisVUC;
940 thisNFTL->ReplUnitTable[writeEUN] = 0xffff;
942 /* ... and on the flash itself */
943 MTD_READOOB(thisNFTL->mtd, writeEUN * thisNFTL->EraseSize, 16,
944 &retlen, (char *)&oob);
946 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
948 MTD_WRITEOOB(thisNFTL->mtd, writeEUN * thisNFTL->EraseSize, 16,
949 &retlen, (char *)&oob);
951 return writeEUN;
953 } while (silly2--);
955 printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n", thisVUC);
956 return 0xffff;
959 static int NFTL_writeblock(struct NFTLrecord *thisNFTL, unsigned block,
960 char *buffer)
962 u16 writeEUN;
963 unsigned long blockofs = (block * 512) & (thisNFTL->EraseSize -1);
964 size_t retlen;
965 u16 eccbuf[8];
967 // if (thisEUN == 0xffff) thisEUN = 0;
969 writeEUN = NFTL_findwriteunit(thisNFTL, block);
971 // printk("writeblock(%d): Write to Unit %d\n", block, writeEUN);
973 if (writeEUN == 0xffff) {
974 printk(KERN_WARNING "NFTL_writeblock(): Cannot find block to write to\n");
975 /* If we _still_ haven't got a block to use, we're screwed */
976 return 1;
978 // printk("Writing block %lx to EUN %x\n",block, writeEUN);
981 thisNFTL->mtd->write_ecc(thisNFTL->mtd,
982 (writeEUN * thisNFTL->EraseSize) + blockofs,
983 512, &retlen, (char *)buffer, (char *)eccbuf);
984 eccbuf[3] = BLOCK_USED;
985 eccbuf[4] = eccbuf[5] = eccbuf[6] = eccbuf[7] = 0xffff;
987 thisNFTL->mtd->write_oob(thisNFTL->mtd,
988 (writeEUN * thisNFTL->EraseSize) + blockofs,
989 16, &retlen, (char *)eccbuf);
991 return 0;
994 #endif /* CONFIG_NFTL_RW */
996 static int NFTL_readblock(struct NFTLrecord *thisNFTL,
997 unsigned block, char *buffer)
999 u16 lastgoodEUN = 0xffff;
1000 u16 thisEUN = thisNFTL->EUNtable[block / (thisNFTL->EraseSize / 512)];
1001 unsigned long blockofs = (block * 512) & (thisNFTL->EraseSize -1);
1003 int silly = -1;
1005 if (thisEUN == 0xffff) thisEUN = 0;
1007 while(thisEUN && (thisEUN & 0x7fff) != 0x7fff) {
1008 struct nftl_bci bci;
1009 size_t retlen;
1011 MTD_READOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + blockofs,8, &retlen, (char *)&bci);
1013 switch(bci.Status) {
1014 case __constant_cpu_to_le16(BLOCK_FREE):
1015 thisEUN = 0;
1016 break;
1017 case __constant_cpu_to_le16(BLOCK_USED):
1018 lastgoodEUN = thisEUN;
1019 break;
1020 case __constant_cpu_to_le16(BLOCK_IGNORE):
1021 case __constant_cpu_to_le16(BLOCK_DELETED):
1022 break;
1023 default:
1024 printk("Unknown status for block %d in EUN %d: %x\n",block,thisEUN, bci.Status);
1027 if (!silly--) {
1028 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",block / (thisNFTL->EraseSize / 512));
1029 return 1;
1031 if (thisEUN)
1032 thisEUN = thisNFTL->ReplUnitTable[thisEUN] & 0x7fff;
1034 if (lastgoodEUN == 0xffff) {
1035 memset(buffer, 0, 512);
1036 } else {
1037 loff_t ptr = (lastgoodEUN * thisNFTL->EraseSize) + blockofs;
1038 size_t retlen;
1039 u_char eccbuf[6];
1040 thisNFTL->mtd->read_ecc(thisNFTL->mtd, ptr, 512, &retlen, buffer, eccbuf);
1042 return 0;
1046 static int nftl_ioctl(struct inode * inode, struct file * file,
1047 unsigned int cmd, unsigned long arg)
1049 struct NFTLrecord *thisNFTL;
1051 thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16];
1053 if (!thisNFTL) return -EINVAL;
1056 switch (cmd) {
1057 case HDIO_GETGEO: {
1058 struct hd_geometry g;
1060 g.heads = thisNFTL->heads;
1061 g.sectors = thisNFTL->sectors;
1062 g.cylinders = thisNFTL->cylinders;
1063 g.start = part_table[MINOR(inode->i_rdev)].start_sect;
1064 return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0;
1066 case BLKGETSIZE: /* Return device size */
1067 if (!arg) return -EINVAL;
1068 return put_user(part_table[MINOR(inode->i_rdev)].nr_sects,
1069 (long *) arg);
1071 case BLKFLSBUF:
1072 if(!capable(CAP_SYS_ADMIN)) return -EACCES;
1073 fsync_dev(inode->i_rdev);
1074 invalidate_buffers(inode->i_rdev);
1075 if (thisNFTL->mtd->sync)
1076 thisNFTL->mtd->sync(thisNFTL->mtd);
1077 return 0;
1079 case BLKRRPART:
1080 if (!capable(CAP_SYS_ADMIN)) return -EACCES;
1081 if (thisNFTL->usecount > 1) {
1082 // printk("Use count %d\n", thisNFTL->usecount);
1083 return -EBUSY;
1085 #if LINUX_VERSION_CODE < 0x20328
1086 resetup_one_dev(&nftl_gendisk, MINOR(inode->i_dev) / 16);
1087 #else
1088 grok_partitions(&nftl_gendisk, MINOR(inode->i_dev) / 16, 1<<4, thisNFTL->nr_sects);
1089 #endif
1090 return 0;
1092 // RO_IOCTLS(inode->i_rdev, arg); /* ref. linux/blk.h */
1093 default:
1094 return -EINVAL;
1099 void nftl_request(RQFUNC_ARG)
1101 unsigned int dev, block, nsect;
1102 struct NFTLrecord *thisNFTL;
1103 char *buffer;
1104 struct request *req;
1105 int res;
1107 while (1) {
1108 INIT_REQUEST; /* blk.h */
1110 req = CURRENT;
1111 #ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK
1112 blkdev_dequeue_request(req);
1113 spin_unlock_irq(&io_request_lock);
1114 #else
1115 req = CURRENT;
1116 #endif
1118 DEBUG(2,"NFTL_request\n");
1119 DEBUG(3,"NFTL %d request, %lx, %lx", req->cmd,
1120 req->sector, req->current_nr_sectors);
1121 dev = MINOR(req->rq_dev);
1122 block = req->sector;
1123 nsect = req->current_nr_sectors;
1124 buffer = req->buffer;
1125 res = 1; /* succeed */
1127 if (dev >= MAX_NFTLS * 16) {
1128 printk("fl: bad minor number: device=%s\n",
1129 kdevname(req->rq_dev));
1130 res = 0; /* fail */
1131 goto repeat;
1134 thisNFTL = NFTLs[dev / 16];
1135 DEBUG(3,"Waiting for mutex\n");
1136 down(&thisNFTL->mutex);
1137 DEBUG(3,"Got mutex\n");
1139 if (block + nsect >= part_table[dev].nr_sects) {
1140 printk("nftl%c%d: bad access: block=%d, count=%d\n",
1141 (MINOR(req->rq_dev)>>6)+'a', dev & 0xf, block, nsect);
1142 up(&thisNFTL->mutex);
1143 res = 0; /* fail */
1144 goto repeat;
1147 block += part_table[dev].start_sect;
1149 if (req->cmd == READ) {
1150 DEBUG(2,"NFTL read\n");
1151 for ( ; nsect > 0; nsect-- , block++, buffer+= 512) {
1152 /* Read a single sector to req->buffer + (512 * i) */
1154 if (NFTL_readblock(thisNFTL, block, buffer)) {
1155 DEBUG(2,"NFTL read request failed\n");
1156 up(&thisNFTL->mutex);
1157 res = 0;
1158 goto repeat;
1161 DEBUG(2,"NFTL read request completed OK\n");
1162 up(&thisNFTL->mutex);
1163 goto repeat;
1165 else if (req->cmd == WRITE) {
1166 DEBUG(2,"NFTL write request of 0x%x sectors @ %x (req->nr_sectors == %lx\n",nsect, block, req->nr_sectors);
1167 #ifdef CONFIG_NFTL_RW
1168 for ( ; nsect > 0; nsect-- , block++, buffer+= 512) {
1169 /* Read a single sector to req->buffer + (512 * i) */
1171 if (NFTL_writeblock(thisNFTL, block, buffer)) {
1172 DEBUG(1,"NFTL write request failed\n");
1174 up(&thisNFTL->mutex);
1175 res = 0;
1176 goto repeat;
1179 DEBUG(2,"NFTL write request completed OK\n");
1180 #else
1181 res=0; /* Writes always fail */
1182 #endif /* CONFIG_NFTL_RW */
1183 up(&thisNFTL->mutex);
1184 goto repeat;
1186 else {
1187 DEBUG(0,"NFTL ??? request\n");
1188 up(&thisNFTL->mutex);
1189 res = 0;
1190 goto repeat;
1192 repeat:
1193 DEBUG(3,"end_request(%d)\n", res);
1194 #ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK
1195 spin_lock_irq(&io_request_lock);
1196 nftl_end_request(req, res);
1197 #else
1198 end_request(res);
1199 #endif
1203 static int nftl_open(struct inode *ip, struct file *fp)
1205 struct NFTLrecord *thisNFTL;
1206 thisNFTL = NFTLs[MINOR(ip->i_rdev) / 16];
1208 DEBUG(2,"NFTL_open\n");
1210 if (!thisNFTL) {
1211 DEBUG(2,"ENODEV: thisNFTL = %d, minor = %d, ip = %p, fp = %p\n",
1212 MINOR(ip->i_rdev) / 16,ip->i_rdev,ip, fp);
1213 return -ENODEV;
1215 #ifndef CONFIG_NFTL_RW
1216 if (fp->f_mode & FMODE_WRITE)
1217 return -EROFS;
1218 #endif /* !CONFIG_NFTL_RW */
1219 thisNFTL->usecount++;
1220 MOD_INC_USE_COUNT;
1221 if (!get_mtd_device(thisNFTL->mtd, -1)) {
1222 MOD_DEC_USE_COUNT;
1223 return /* -E'SBUGGEREDOFF */ -ENXIO;
1226 return 0;
1229 static int nftl_release(struct inode *inode, struct file *fp)
1231 struct super_block *sb = get_super(inode->i_rdev);
1232 struct NFTLrecord *thisNFTL;
1234 thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16];
1236 DEBUG(2, "NFTL_release\n");
1238 fsync_dev(inode->i_rdev);
1239 if (sb)
1240 invalidate_inodes(sb);
1241 invalidate_buffers(inode->i_rdev);
1243 if (thisNFTL->mtd->sync)
1244 thisNFTL->mtd->sync(thisNFTL->mtd);
1245 thisNFTL->usecount--;
1246 MOD_DEC_USE_COUNT;
1248 put_mtd_device(thisNFTL->mtd);
1250 return 0;
1252 #if LINUX_VERSION_CODE < 0x20326
1253 static struct file_operations nftl_fops = {
1254 read: block_read,
1255 write: block_write,
1256 ioctl: nftl_ioctl,
1257 open: nftl_open,
1258 release: nftl_release,
1259 fsync: block_fsync,
1261 #else
1262 static struct block_device_operations nftl_fops =
1264 open: nftl_open,
1265 release: nftl_release,
1266 ioctl: nftl_ioctl
1268 #endif
1272 /****************************************************************************
1274 * Module stuff
1276 ****************************************************************************/
1278 #if LINUX_VERSION_CODE < 0x20300
1279 #ifdef MODULE
1280 #define init_nftl init_module
1281 #define cleanup_nftl cleanup_module
1282 #endif
1283 #define __exit
1284 #endif
1286 static struct mtd_notifier nftl_notifier = {NFTL_notify_add, NFTL_notify_remove, NULL};
1289 /* static int __init init_nftl(void) */
1290 int __init init_nftl(void)
1292 int i;
1294 printk(KERN_NOTICE "M-Systems NAND Flash Translation Layer driver. (C) 1999 MVHI\n");
1295 #ifdef PRERELEASE
1296 printk(KERN_INFO"$Id: nftl.c,v 1.35 2000/07/06 14:35:01 dwmw2 Exp $\n");
1297 #endif
1299 if (register_blkdev(NFTL_MAJOR, "nftl", &nftl_fops)){
1300 printk("unable to register NFTL block device\n");
1301 } else {
1302 #if LINUX_VERSION_CODE < 0x20320
1303 blk_dev[MAJOR_NR].request_fn = nftl_request;
1304 #else
1305 blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request);
1306 #endif
1307 for (i=0; i < 256 ; i++) {
1308 nftl_blocksizes[i] = 1024;
1310 blksize_size[NFTL_MAJOR] = nftl_blocksizes;
1311 nftl_gendisk.next = gendisk_head;
1312 gendisk_head = &nftl_gendisk;
1315 register_mtd_user(&nftl_notifier);
1317 return 0;
1320 static void __exit cleanup_nftl(void)
1322 struct gendisk *gd, **gdp;
1324 unregister_mtd_user(&nftl_notifier);
1326 unregister_blkdev(NFTL_MAJOR, "nftl");
1327 #if LINUX_VERSION_CODE < 0x20320
1328 blk_dev[MAJOR_NR].request_fn = 0;
1329 #else
1330 blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
1331 #endif
1332 for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
1333 if (*gdp == &nftl_gendisk) {
1334 gd = *gdp; *gdp = gd->next;
1335 break;
1340 #if LINUX_VERSION_CODE > 0x20300
1341 module_init(init_nftl);
1342 module_exit(cleanup_nftl);
1343 #endif