1
2
3
4
5
6
7
8
9
10
11
12#define PRERELEASE
13
14#include <linux/config.h>
15#include <linux/kernel.h>
16#include <linux/module.h>
17#include <asm/errno.h>
18#include <asm/io.h>
19#include <asm/uaccess.h>
20#include <linux/miscdevice.h>
21#include <linux/pci.h>
22#include <linux/delay.h>
23#include <linux/slab.h>
24#include <linux/sched.h>
25#include <linux/init.h>
26#include <linux/blkpg.h>
27
28#ifdef CONFIG_KMOD
29#include <linux/kmod.h>
30#endif
31#include <linux/mtd/mtd.h>
32#include <linux/mtd/nand.h>
33#include <linux/mtd/nftl.h>
34#include <linux/mtd/compatmac.h>
35
36
37
38
39
40#define MAX_LOOPS 10000
41
42
43#define MAJOR_NR NFTL_MAJOR
44#define DEVICE_REQUEST nftl_request
45#define DEVICE_OFF(device)
46
47
48#include <linux/blk.h>
49#include <linux/hdreg.h>
50
51
52
53
54
55
56
57static int nftl_sizes[256];
58static int nftl_blocksizes[256];
59
60
61struct hd_struct part_table[256];
62
63#if LINUX_VERSION_CODE < 0x20328
64static void dummy_init (struct gendisk *crap)
65{}
66#endif
67
68static struct gendisk nftl_gendisk = {
69 major: MAJOR_NR,
70 major_name: "nftl",
71 minor_shift: NFTL_PARTN_BITS,
72 max_p: (1<<NFTL_PARTN_BITS)-1,
73#if LINUX_VERSION_CODE < 0x20328
74 max_nr: MAX_NFTLS,
75 init: dummy_init,
76#endif
77 part: part_table,
78 sizes: nftl_sizes,
79};
80
81#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14)
82#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT
83#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT
84#else
85#define BLK_INC_USE_COUNT do {} while(0)
86#define BLK_DEC_USE_COUNT do {} while(0)
87#endif
88
89struct NFTLrecord *NFTLs[MAX_NFTLS];
90
91static void NFTL_setup(struct mtd_info *mtd)
92{
93 int i;
94 struct NFTLrecord *nftl;
95 unsigned long temp;
96 int firstfree = -1;
97
98 DEBUG(MTD_DEBUG_LEVEL1,"NFTL_setup\n");
99
100 for (i = 0; i < MAX_NFTLS; i++) {
101 if (!NFTLs[i] && firstfree == -1)
102 firstfree = i;
103 else if (NFTLs[i] && NFTLs[i]->mtd == mtd) {
104
105 DEBUG(MTD_DEBUG_LEVEL1, "MTD already mounted as NFTL\n");
106 return;
107 }
108 }
109 if (firstfree == -1) {
110 printk(KERN_WARNING "No more NFTL slot available\n");
111 return;
112 }
113
114 nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
115 if (!nftl) {
116 printk(KERN_WARNING "Out of memory for NFTL data structures\n");
117 return;
118 }
119
120 init_MUTEX(&nftl->mutex);
121
122 nftl->mtd = mtd;
123
124 if (NFTL_mount(nftl) < 0) {
125 printk(KERN_WARNING "Could not mount NFTL device\n");
126 kfree(nftl);
127 return;
128 }
129
130
131#ifdef PSYCHO_DEBUG
132 printk("Found new NFTL nftl%c\n", firstfree + 'a');
133#endif
134
135
136 nftl->usecount = 0;
137 nftl->cylinders = 1024;
138 nftl->heads = 16;
139
140 temp = nftl->cylinders * nftl->heads;
141 nftl->sectors = nftl->nr_sects / temp;
142 if (nftl->nr_sects % temp) {
143 nftl->sectors++;
144 temp = nftl->cylinders * nftl->sectors;
145 nftl->heads = nftl->nr_sects / temp;
146
147 if (nftl->nr_sects % temp) {
148 nftl->heads++;
149 temp = nftl->heads * nftl->sectors;
150 nftl->cylinders = nftl->nr_sects / temp;
151 }
152 }
153
154 if (nftl->nr_sects != nftl->heads * nftl->cylinders * nftl->sectors) {
155 printk(KERN_WARNING "Cannot calculate an NFTL geometry to "
156 "match size of 0x%x.\n", nftl->nr_sects);
157 printk(KERN_WARNING "Using C:%d H:%d S:%d (== 0x%lx sects)\n",
158 nftl->cylinders, nftl->heads , nftl->sectors,
159 (long)nftl->cylinders * (long)nftl->heads * (long)nftl->sectors );
160
161
162 }
163 NFTLs[firstfree] = nftl;
164
165 nftl_sizes[firstfree * 16] = nftl->nr_sects;
166
167 part_table[firstfree * 16].nr_sects = nftl->nr_sects;
168
169 nftl_gendisk.nr_real++;
170
171
172#if LINUX_VERSION_CODE < 0x20328
173 resetup_one_dev(&nftl_gendisk, firstfree);
174#else
175 grok_partitions(&nftl_gendisk, firstfree, 1<<NFTL_PARTN_BITS, nftl->nr_sects);
176#endif
177}
178
179static void NFTL_unsetup(int i)
180{
181 struct NFTLrecord *nftl = NFTLs[i];
182
183 DEBUG(MTD_DEBUG_LEVEL1, "NFTL_unsetup %d\n", i);
184
185 NFTLs[i] = NULL;
186
187 if (nftl->ReplUnitTable)
188 kfree(nftl->ReplUnitTable);
189 if (nftl->EUNtable)
190 kfree(nftl->EUNtable);
191
192 nftl_gendisk.nr_real--;
193 kfree(nftl);
194}
195
196
197static void NFTL_notify_add(struct mtd_info *mtd)
198{
199 DEBUG(MTD_DEBUG_LEVEL1, "NFTL_notify_add for %s\n", mtd->name);
200
201 if (mtd) {
202 if (!mtd->read_oob) {
203
204
205 DEBUG(MTD_DEBUG_LEVEL1, "No OOB data, quitting\n");
206 return;
207 }
208 DEBUG(MTD_DEBUG_LEVEL3, "mtd->read = %p, size = %d, erasesize = %d\n",
209 mtd->read, mtd->size, mtd->erasesize);
210
211 NFTL_setup(mtd);
212 }
213}
214
215static void NFTL_notify_remove(struct mtd_info *mtd)
216{
217 int i;
218
219 for (i = 0; i < MAX_NFTLS; i++) {
220 if (NFTLs[i] && NFTLs[i]->mtd == mtd)
221 NFTL_unsetup(i);
222 }
223}
224
225#ifdef CONFIG_NFTL_RW
226
227
228
229
230
231static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
232{
233
234
235
236
237 u16 pot = nftl->LastFreeEUN;
238 int silly = nftl->nb_blocks;
239
240
241 if (!desperate && nftl->numfreeEUNs < 2) {
242 DEBUG(MTD_DEBUG_LEVEL1, "NFTL_findfreeblock: there are too few free EUNs\n");
243 return 0xffff;
244 }
245
246
247 do {
248 if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
249 nftl->LastFreeEUN = pot;
250 nftl->numfreeEUNs--;
251 return pot;
252 }
253
254
255
256
257
258 if (++pot > nftl->lastEUN)
259 pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
260
261 if (!silly--) {
262 printk("Argh! No free blocks found! LastFreeEUN = %d, "
263 "FirstEUN = %d\n", nftl->LastFreeEUN,
264 le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
265 return 0xffff;
266 }
267 } while (pot != nftl->LastFreeEUN);
268
269 return 0xffff;
270}
271
272static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
273{
274 u16 BlockMap[MAX_SECTORS_PER_UNIT];
275 unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
276 unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
277 unsigned int thisEUN;
278 int block;
279 int silly;
280 unsigned int targetEUN;
281 struct nftl_oob oob;
282 int inplace = 1;
283 size_t retlen;
284
285 memset(BlockMap, 0xff, sizeof(BlockMap));
286 memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
287
288 thisEUN = nftl->EUNtable[thisVUC];
289
290 if (thisEUN == BLOCK_NIL) {
291 printk(KERN_WARNING "Trying to fold non-existent "
292 "Virtual Unit Chain %d!\n", thisVUC);
293 return BLOCK_NIL;
294 }
295
296
297
298
299 silly = MAX_LOOPS;
300 targetEUN = BLOCK_NIL;
301 while (thisEUN <= nftl->lastEUN ) {
302 unsigned int status, foldmark;
303
304 targetEUN = thisEUN;
305 for (block = 0; block < nftl->EraseSize / 512; block ++) {
306 MTD_READOOB(nftl->mtd,
307 (thisEUN * nftl->EraseSize) + (block * 512),
308 16 , &retlen, (char *)&oob);
309 if (block == 2) {
310 foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
311 if (foldmark == FOLD_MARK_IN_PROGRESS) {
312 DEBUG(MTD_DEBUG_LEVEL1,
313 "Write Inhibited on EUN %d\n", thisEUN);
314 inplace = 0;
315 } else {
316
317
318
319 inplace = 1;
320 }
321 }
322 status = oob.b.Status | oob.b.Status1;
323 BlockLastState[block] = status;
324
325 switch(status) {
326 case SECTOR_FREE:
327 BlockFreeFound[block] = 1;
328 break;
329
330 case SECTOR_USED:
331 if (!BlockFreeFound[block])
332 BlockMap[block] = thisEUN;
333 else
334 printk(KERN_WARNING
335 "SECTOR_USED found after SECTOR_FREE "
336 "in Virtual Unit Chain %d for block %d\n",
337 thisVUC, block);
338 break;
339 case SECTOR_DELETED:
340 if (!BlockFreeFound[block])
341 BlockMap[block] = BLOCK_NIL;
342 else
343 printk(KERN_WARNING
344 "SECTOR_DELETED found after SECTOR_FREE "
345 "in Virtual Unit Chain %d for block %d\n",
346 thisVUC, block);
347 break;
348
349 case SECTOR_IGNORE:
350 break;
351 default:
352 printk("Unknown status for block %d in EUN %d: %x\n",
353 block, thisEUN, status);
354 }
355 }
356
357 if (!silly--) {
358 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
359 thisVUC);
360 return BLOCK_NIL;
361 }
362
363 thisEUN = nftl->ReplUnitTable[thisEUN];
364 }
365
366 if (inplace) {
367
368
369
370
371
372
373
374 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
375 if (BlockLastState[block] != SECTOR_FREE &&
376 BlockMap[block] != BLOCK_NIL &&
377 BlockMap[block] != targetEUN) {
378 DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, "
379 "block %d was %x lastEUN, "
380 "and is in EUN %d (%s) %d\n",
381 thisVUC, block, BlockLastState[block],
382 BlockMap[block],
383 BlockMap[block]== targetEUN ? "==" : "!=",
384 targetEUN);
385 inplace = 0;
386 break;
387 }
388 }
389
390 if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
391 pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
392 BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
393 SECTOR_FREE) {
394 DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. "
395 "Folding out of place.\n", targetEUN);
396 inplace = 0;
397 }
398 }
399
400 if (!inplace) {
401 DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. "
402 "Trying out-of-place\n", thisVUC);
403
404 targetEUN = NFTL_findfreeblock(nftl, 1);
405 if (targetEUN == BLOCK_NIL) {
406
407
408
409
410
411
412 printk(KERN_WARNING
413 "NFTL_findfreeblock(desperate) returns 0xffff.\n");
414 return BLOCK_NIL;
415 }
416 } else {
417
418
419
420
421 oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
422 oob.u.c.unused = 0xffffffff;
423 MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
424 8, &retlen, (char *)&oob.u);
425 }
426
427
428
429
430
431 DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
432 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
433 unsigned char movebuf[512];
434 int ret;
435
436
437 if (BlockMap[block] == targetEUN ||
438 (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
439 continue;
440 }
441
442
443
444 if (BlockMap[block] == BLOCK_NIL)
445 continue;
446
447 ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
448 512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP);
449 if (ret < 0) {
450 ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block])
451 + (block * 512), 512, &retlen,
452 movebuf, (char *)&oob, NAND_ECC_DISKONCHIP);
453 if (ret != -EIO)
454 printk("Error went away on retry.\n");
455 }
456 MTD_WRITEECC(nftl->mtd, (nftl->EraseSize * targetEUN) + (block * 512),
457 512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP);
458 }
459
460
461 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum
462 = cpu_to_le16(thisVUC);
463 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
464
465 MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 8,
466 8, &retlen, (char *)&oob.u);
467
468
469
470
471
472
473
474
475 thisEUN = nftl->EUNtable[thisVUC];
476 DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n");
477
478
479
480 while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
481 unsigned int EUNtmp;
482
483 EUNtmp = nftl->ReplUnitTable[thisEUN];
484
485 if (NFTL_formatblock(nftl, thisEUN) < 0) {
486
487
488
489 nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
490 } else {
491
492 nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
493 nftl->numfreeEUNs++;
494 }
495 thisEUN = EUNtmp;
496 }
497
498
499 nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
500 nftl->EUNtable[thisVUC] = targetEUN;
501
502 return targetEUN;
503}
504
505u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
506{
507
508
509
510
511
512
513
514 u16 LongestChain = 0;
515 u16 ChainLength = 0, thislen;
516 u16 chain, EUN;
517
518 for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) {
519 EUN = nftl->EUNtable[chain];
520 thislen = 0;
521
522 while (EUN <= nftl->lastEUN) {
523 thislen++;
524
525 EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
526 if (thislen > 0xff00) {
527 printk("Endless loop in Virtual Chain %d: Unit %x\n",
528 chain, EUN);
529 }
530 if (thislen > 0xff10) {
531
532
533 thislen = 0;
534 break;
535 }
536 }
537
538 if (thislen > ChainLength) {
539
540 ChainLength = thislen;
541 LongestChain = chain;
542 }
543 }
544
545 if (ChainLength < 2) {
546 printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
547 "Failing request\n");
548 return 0xffff;
549 }
550
551 return NFTL_foldchain (nftl, LongestChain, pendingblock);
552}
553
554
555
556
557static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
558{
559 u16 lastEUN;
560 u16 thisVUC = block / (nftl->EraseSize / 512);
561 unsigned int writeEUN;
562 unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
563 size_t retlen;
564 int silly, silly2 = 3;
565 struct nftl_oob oob;
566
567 do {
568
569
570
571
572
573
574
575 lastEUN = BLOCK_NIL;
576 writeEUN = nftl->EUNtable[thisVUC];
577 silly = MAX_LOOPS;
578 while (writeEUN <= nftl->lastEUN) {
579 struct nftl_bci bci;
580 size_t retlen;
581 unsigned int status;
582
583 lastEUN = writeEUN;
584
585 MTD_READOOB(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs,
586 8, &retlen, (char *)&bci);
587
588 DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
589 block , writeEUN, le16_to_cpu(bci.Status));
590
591 status = bci.Status | bci.Status1;
592 switch(status) {
593 case SECTOR_FREE:
594 return writeEUN;
595
596 case SECTOR_DELETED:
597 case SECTOR_USED:
598 case SECTOR_IGNORE:
599 break;
600 default:
601
602 break;
603 }
604
605 if (!silly--) {
606 printk(KERN_WARNING
607 "Infinite loop in Virtual Unit Chain 0x%x\n",
608 thisVUC);
609 return 0xffff;
610 }
611
612
613 writeEUN = nftl->ReplUnitTable[writeEUN];
614 }
615
616
617
618
619
620 writeEUN = NFTL_findfreeblock(nftl, 0);
621
622 if (writeEUN == BLOCK_NIL) {
623
624
625
626
627
628
629
630
631
632 writeEUN = NFTL_makefreeblock(nftl, 0xffff);
633
634 if (writeEUN == BLOCK_NIL) {
635
636
637
638
639
640
641 DEBUG(MTD_DEBUG_LEVEL1, "Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
642 writeEUN = NFTL_findfreeblock(nftl, 1);
643 }
644 if (writeEUN == BLOCK_NIL) {
645
646
647
648
649
650
651 printk(KERN_WARNING "Cannot make free space.\n");
652 return BLOCK_NIL;
653 }
654
655 lastEUN = BLOCK_NIL;
656 continue;
657 }
658
659
660
661 if (lastEUN != BLOCK_NIL) {
662 thisVUC |= 0x8000;
663 } else {
664
665 nftl->EUNtable[thisVUC] = writeEUN;
666 }
667
668
669
670 nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
671
672
673 MTD_READOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8,
674 &retlen, (char *)&oob.u);
675
676 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
677
678 MTD_WRITEOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8,
679 &retlen, (char *)&oob.u);
680
681
682
683
684 if (lastEUN != BLOCK_NIL) {
685
686 nftl->ReplUnitTable[lastEUN] = writeEUN;
687
688 MTD_READOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8,
689 8, &retlen, (char *)&oob.u);
690
691 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
692 = cpu_to_le16(writeEUN);
693
694 MTD_WRITEOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8,
695 8, &retlen, (char *)&oob.u);
696 }
697
698 return writeEUN;
699
700 } while (silly2--);
701
702 printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
703 thisVUC);
704 return 0xffff;
705}
706
707static int NFTL_writeblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
708{
709 u16 writeEUN;
710 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
711 size_t retlen;
712 u8 eccbuf[6];
713
714 writeEUN = NFTL_findwriteunit(nftl, block);
715
716 if (writeEUN == BLOCK_NIL) {
717 printk(KERN_WARNING
718 "NFTL_writeblock(): Cannot find block to write to\n");
719
720 return 1;
721 }
722
723 MTD_WRITEECC(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs,
724 512, &retlen, (char *)buffer, (char *)eccbuf, NAND_ECC_DISKONCHIP);
725
726
727 return 0;
728}
729#endif
730
731static int NFTL_readblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
732{
733 u16 lastgoodEUN;
734 u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
735 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
736 unsigned int status;
737 int silly = MAX_LOOPS;
738 size_t retlen;
739 struct nftl_bci bci;
740
741 lastgoodEUN = BLOCK_NIL;
742
743 if (thisEUN != BLOCK_NIL) {
744 while (thisEUN < nftl->nb_blocks) {
745 if (MTD_READOOB(nftl->mtd, (thisEUN * nftl->EraseSize) + blockofs,
746 8, &retlen, (char *)&bci) < 0)
747 status = SECTOR_IGNORE;
748 else
749 status = bci.Status | bci.Status1;
750
751 switch (status) {
752 case SECTOR_FREE:
753
754 goto the_end;
755 case SECTOR_DELETED:
756 lastgoodEUN = BLOCK_NIL;
757 break;
758 case SECTOR_USED:
759 lastgoodEUN = thisEUN;
760 break;
761 case SECTOR_IGNORE:
762 break;
763 default:
764 printk("Unknown status for block %d in EUN %d: %x\n",
765 block, thisEUN, status);
766 break;
767 }
768
769 if (!silly--) {
770 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
771 block / (nftl->EraseSize / 512));
772 return 1;
773 }
774 thisEUN = nftl->ReplUnitTable[thisEUN];
775 }
776 }
777
778 the_end:
779 if (lastgoodEUN == BLOCK_NIL) {
780
781 memset(buffer, 0, 512);
782 } else {
783 loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
784 size_t retlen;
785 u_char eccbuf[6];
786 if (MTD_READECC(nftl->mtd, ptr, 512, &retlen, buffer, eccbuf, NAND_ECC_DISKONCHIP))
787 return -EIO;
788 }
789 return 0;
790}
791
792static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
793{
794 struct NFTLrecord *nftl;
795 int p;
796
797 nftl = NFTLs[MINOR(inode->i_rdev) >> NFTL_PARTN_BITS];
798
799 if (!nftl) return -EINVAL;
800
801 switch (cmd) {
802 case HDIO_GETGEO: {
803 struct hd_geometry g;
804
805 g.heads = nftl->heads;
806 g.sectors = nftl->sectors;
807 g.cylinders = nftl->cylinders;
808 g.start = part_table[MINOR(inode->i_rdev)].start_sect;
809 return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0;
810 }
811 case BLKGETSIZE:
812 return put_user(part_table[MINOR(inode->i_rdev)].nr_sects,
813 (unsigned long *) arg);
814
815#ifdef BLKGETSIZE64
816 case BLKGETSIZE64:
817 return put_user((u64)part_table[MINOR(inode->i_rdev)].nr_sects << 9,
818 (u64 *)arg);
819#endif
820
821 case BLKFLSBUF:
822 if (!capable(CAP_SYS_ADMIN)) return -EACCES;
823 fsync_dev(inode->i_rdev);
824 invalidate_buffers(inode->i_rdev);
825 if (nftl->mtd->sync)
826 nftl->mtd->sync(nftl->mtd);
827 return 0;
828
829 case BLKRRPART:
830 if (!capable(CAP_SYS_ADMIN)) return -EACCES;
831 if (nftl->usecount > 1) return -EBUSY;
832
833
834
835
836
837 p = (1<<NFTL_PARTN_BITS) - 1;
838 while (p-- > 0) {
839 kdev_t devp = MKDEV(MAJOR(inode->i_dev), MINOR(inode->i_dev)+p);
840 if (part_table[p].nr_sects > 0)
841 invalidate_device (devp, 1);
842
843 part_table[MINOR(inode->i_dev)+p].start_sect = 0;
844 part_table[MINOR(inode->i_dev)+p].nr_sects = 0;
845 }
846
847#if LINUX_VERSION_CODE < 0x20328
848 resetup_one_dev(&nftl_gendisk, MINOR(inode->i_rdev) >> NFTL_PARTN_BITS);
849#else
850 grok_partitions(&nftl_gendisk, MINOR(inode->i_rdev) >> NFTL_PARTN_BITS,
851 1<<NFTL_PARTN_BITS, nftl->nr_sects);
852#endif
853 return 0;
854
855#if (LINUX_VERSION_CODE < 0x20303)
856 RO_IOCTLS(inode->i_rdev, arg);
857#else
858 case BLKROSET:
859 case BLKROGET:
860 case BLKSSZGET:
861 return blk_ioctl(inode->i_rdev, cmd, arg);
862#endif
863
864 default:
865 return -EINVAL;
866 }
867}
868
869void nftl_request(RQFUNC_ARG)
870{
871 unsigned int dev, block, nsect;
872 struct NFTLrecord *nftl;
873 char *buffer;
874 struct request *req;
875 int res;
876
877 while (1) {
878 INIT_REQUEST;
879 req = CURRENT;
880
881
882
883 spin_unlock_irq(&io_request_lock);
884
885 DEBUG(MTD_DEBUG_LEVEL2, "NFTL_request\n");
886 DEBUG(MTD_DEBUG_LEVEL3, "NFTL %s request, from sector 0x%04lx for 0x%04lx sectors\n",
887 (req->cmd == READ) ? "Read " : "Write",
888 req->sector, req->current_nr_sectors);
889
890 dev = MINOR(req->rq_dev);
891 block = req->sector;
892 nsect = req->current_nr_sectors;
893 buffer = req->buffer;
894 res = 1;
895
896 if (dev >= MAX_NFTLS * (1<<NFTL_PARTN_BITS)) {
897
898 printk("nftl: bad minor number: device = %s\n",
899 kdevname(req->rq_dev));
900 res = 0;
901 goto repeat;
902 }
903
904 nftl = NFTLs[dev / (1<<NFTL_PARTN_BITS)];
905 DEBUG(MTD_DEBUG_LEVEL3, "Waiting for mutex\n");
906 down(&nftl->mutex);
907 DEBUG(MTD_DEBUG_LEVEL3, "Got mutex\n");
908
909 if (block + nsect > part_table[dev].nr_sects) {
910
911 printk("nftl%c%d: bad access: block = %d, count = %d\n",
912 (MINOR(req->rq_dev)>>6)+'a', dev & 0xf, block, nsect);
913 up(&nftl->mutex);
914 res = 0;
915 goto repeat;
916 }
917
918 block += part_table[dev].start_sect;
919
920 if (req->cmd == READ) {
921 DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request of 0x%x sectors @ %x "
922 "(req->nr_sectors == %lx)\n", nsect, block, req->nr_sectors);
923
924 for ( ; nsect > 0; nsect-- , block++, buffer += 512) {
925
926 if (NFTL_readblock(nftl, block, buffer)) {
927 DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request failed\n");
928 up(&nftl->mutex);
929 res = 0;
930 goto repeat;
931 }
932 }
933
934 DEBUG(MTD_DEBUG_LEVEL2,"NFTL read request completed OK\n");
935 up(&nftl->mutex);
936 goto repeat;
937 } else if (req->cmd == WRITE) {
938 DEBUG(MTD_DEBUG_LEVEL2, "NFTL write request of 0x%x sectors @ %x "
939 "(req->nr_sectors == %lx)\n", nsect, block,
940 req->nr_sectors);
941#ifdef CONFIG_NFTL_RW
942 for ( ; nsect > 0; nsect-- , block++, buffer += 512) {
943
944 if (NFTL_writeblock(nftl, block, buffer)) {
945 DEBUG(MTD_DEBUG_LEVEL1,"NFTL write request failed\n");
946 up(&nftl->mutex);
947 res = 0;
948 goto repeat;
949 }
950 }
951 DEBUG(MTD_DEBUG_LEVEL2,"NFTL write request completed OK\n");
952#else
953 res = 0;
954#endif
955 up(&nftl->mutex);
956 goto repeat;
957 } else {
958 DEBUG(MTD_DEBUG_LEVEL0, "NFTL unknown request\n");
959 up(&nftl->mutex);
960 res = 0;
961 goto repeat;
962 }
963 repeat:
964 DEBUG(MTD_DEBUG_LEVEL3, "end_request(%d)\n", res);
965 spin_lock_irq(&io_request_lock);
966 end_request(res);
967 }
968}
969
970static int nftl_open(struct inode *ip, struct file *fp)
971{
972 int nftlnum = MINOR(ip->i_rdev) >> NFTL_PARTN_BITS;
973 struct NFTLrecord *thisNFTL;
974 thisNFTL = NFTLs[nftlnum];
975
976 DEBUG(MTD_DEBUG_LEVEL2,"NFTL_open\n");
977
978#ifdef CONFIG_KMOD
979 if (!thisNFTL && nftlnum == 0) {
980 request_module("docprobe");
981 thisNFTL = NFTLs[nftlnum];
982 }
983#endif
984 if (!thisNFTL) {
985 DEBUG(MTD_DEBUG_LEVEL2,"ENODEV: thisNFTL = %d, minor = %d, ip = %p, fp = %p\n",
986 nftlnum, ip->i_rdev, ip, fp);
987 return -ENODEV;
988 }
989
990#ifndef CONFIG_NFTL_RW
991 if (fp->f_mode & FMODE_WRITE)
992 return -EROFS;
993#endif
994
995 thisNFTL->usecount++;
996 BLK_INC_USE_COUNT;
997 if (!get_mtd_device(thisNFTL->mtd, -1)) {
998 BLK_DEC_USE_COUNT;
999 return -ENXIO;
1000 }
1001
1002 return 0;
1003}
1004
1005static int nftl_release(struct inode *inode, struct file *fp)
1006{
1007 struct NFTLrecord *thisNFTL;
1008
1009 thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16];
1010
1011 DEBUG(MTD_DEBUG_LEVEL2, "NFTL_release\n");
1012
1013 if (thisNFTL->mtd->sync)
1014 thisNFTL->mtd->sync(thisNFTL->mtd);
1015 thisNFTL->usecount--;
1016 BLK_DEC_USE_COUNT;
1017
1018 put_mtd_device(thisNFTL->mtd);
1019
1020 return 0;
1021}
1022#if LINUX_VERSION_CODE < 0x20326
1023static struct file_operations nftl_fops = {
1024 read: block_read,
1025 write: block_write,
1026 ioctl: nftl_ioctl,
1027 open: nftl_open,
1028 release: nftl_release,
1029 fsync: block_fsync,
1030};
1031#else
1032static struct block_device_operations nftl_fops =
1033{
1034#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14)
1035 owner: THIS_MODULE,
1036#endif
1037 open: nftl_open,
1038 release: nftl_release,
1039 ioctl: nftl_ioctl
1040};
1041#endif
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051static struct mtd_notifier nftl_notifier = {
1052 add: NFTL_notify_add,
1053 remove: NFTL_notify_remove
1054};
1055
1056extern char nftlmountrev[];
1057
1058int __init init_nftl(void)
1059{
1060 int i;
1061
1062#ifdef PRERELEASE
1063 printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.87 $, nftlmount.c %s\n", nftlmountrev);
1064#endif
1065
1066 if (register_blkdev(MAJOR_NR, "nftl", &nftl_fops)){
1067 printk("unable to register NFTL block device on major %d\n", MAJOR_NR);
1068 return -EBUSY;
1069 } else {
1070 blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request);
1071
1072
1073 for (i = 0; i < 256; i++) {
1074 nftl_blocksizes[i] = 1024;
1075 }
1076 blksize_size[MAJOR_NR] = nftl_blocksizes;
1077
1078 add_gendisk(&nftl_gendisk);
1079 }
1080
1081 register_mtd_user(&nftl_notifier);
1082
1083 return 0;
1084}
1085
1086static void __exit cleanup_nftl(void)
1087{
1088 unregister_mtd_user(&nftl_notifier);
1089 unregister_blkdev(MAJOR_NR, "nftl");
1090
1091 blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
1092
1093 del_gendisk(&nftl_gendisk);
1094}
1095
1096module_init(init_nftl);
1097module_exit(cleanup_nftl);
1098
1099MODULE_LICENSE("GPL");
1100MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
1101MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");
1102