1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23#include <linux/kernel.h>
24#include <asm/errno.h>
25#include <linux/delay.h>
26#include <linux/slab.h>
27#include <linux/mtd/mtd.h>
28#include <linux/mtd/rawnand.h>
29#include <linux/mtd/nftl.h>
30
31#define SECTORSIZE 512
32
33
34
35
36
37
38static int find_boot_record(struct NFTLrecord *nftl)
39{
40 struct nftl_uci1 h1;
41 unsigned int block, boot_record_count = 0;
42 size_t retlen;
43 u8 buf[SECTORSIZE];
44 struct NFTLMediaHeader *mh = &nftl->MediaHdr;
45 struct mtd_info *mtd = nftl->mbd.mtd;
46 unsigned int i;
47
48
49
50
51
52
53
54 nftl->EraseSize = nftl->mbd.mtd->erasesize;
55 nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize;
56
57 nftl->MediaUnit = BLOCK_NIL;
58 nftl->SpareMediaUnit = BLOCK_NIL;
59
60
61 for (block = 0; block < nftl->nb_blocks; block++) {
62 int ret;
63
64
65
66 ret = mtd_read(mtd, block * nftl->EraseSize, SECTORSIZE,
67 &retlen, buf);
68
69
70 if (retlen != SECTORSIZE) {
71 static int warncount = 5;
72
73 if (warncount) {
74 printk(KERN_WARNING "Block read at 0x%x of mtd%d failed: %d\n",
75 block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
76 if (!--warncount)
77 printk(KERN_WARNING "Further failures for this block will not be printed\n");
78 }
79 continue;
80 }
81
82 if (retlen < 6 || memcmp(buf, "ANAND", 6)) {
83
84#if 0
85 printk(KERN_DEBUG "ANAND header not found at 0x%x in mtd%d\n",
86 block * nftl->EraseSize, nftl->mbd.mtd->index);
87#endif
88 continue;
89 }
90
91
92 ret = nftl_read_oob(mtd, block * nftl->EraseSize +
93 SECTORSIZE + 8, 8, &retlen,
94 (char *)&h1);
95 if (ret < 0) {
96 printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n",
97 block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
98 continue;
99 }
100
101#if 0
102
103
104
105 if (le16_to_cpu(h1.EraseMark | h1.EraseMark1) != ERASE_MARK) {
106 printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but erase mark not present (0x%04x,0x%04x instead)\n",
107 block * nftl->EraseSize, nftl->mbd.mtd->index,
108 le16_to_cpu(h1.EraseMark), le16_to_cpu(h1.EraseMark1));
109 continue;
110 }
111
112
113 ret = mtd->read(mtd, block * nftl->EraseSize, SECTORSIZE,
114 &retlen, buf);
115 if (ret < 0) {
116 printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n",
117 block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
118 continue;
119 }
120
121
122 if (memcmp(buf, "ANAND", 6)) {
123 printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but went away on reread!\n",
124 block * nftl->EraseSize, nftl->mbd.mtd->index);
125 printk(KERN_NOTICE "New data are: %6ph\n", buf);
126 continue;
127 }
128#endif
129
130
131 if (boot_record_count) {
132
133
134 if (memcmp(mh, buf, sizeof(struct NFTLMediaHeader))) {
135 printk(KERN_NOTICE "NFTL Media Headers at 0x%x and 0x%x disagree.\n",
136 nftl->MediaUnit * nftl->EraseSize, block * nftl->EraseSize);
137
138 if (boot_record_count < 2) {
139
140 return -1;
141 }
142 continue;
143 }
144 if (boot_record_count == 1)
145 nftl->SpareMediaUnit = block;
146
147
148 nftl->ReplUnitTable[block] = BLOCK_RESERVED;
149
150
151 boot_record_count++;
152 continue;
153 }
154
155
156 memcpy(mh, buf, sizeof(struct NFTLMediaHeader));
157
158
159#if 0
160The new DiskOnChip driver scans the MediaHeader itself, and presents a virtual
161erasesize based on UnitSizeFactor. So the erasesize we read from the mtd
162device is already correct.
163 if (mh->UnitSizeFactor == 0) {
164 printk(KERN_NOTICE "NFTL: UnitSizeFactor 0x00 detected. This violates the spec but we think we know what it means...\n");
165 } else if (mh->UnitSizeFactor < 0xfc) {
166 printk(KERN_NOTICE "Sorry, we don't support UnitSizeFactor 0x%02x\n",
167 mh->UnitSizeFactor);
168 return -1;
169 } else if (mh->UnitSizeFactor != 0xff) {
170 printk(KERN_NOTICE "WARNING: Support for NFTL with UnitSizeFactor 0x%02x is experimental\n",
171 mh->UnitSizeFactor);
172 nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor);
173 nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize;
174 }
175#endif
176 nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN);
177 if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) {
178 printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n");
179 printk(KERN_NOTICE "nb_boot_blocks (%d) + 2 > nb_blocks (%d)\n",
180 nftl->nb_boot_blocks, nftl->nb_blocks);
181 return -1;
182 }
183
184 nftl->numvunits = le32_to_cpu(mh->FormattedSize) / nftl->EraseSize;
185 if (nftl->numvunits > (nftl->nb_blocks - nftl->nb_boot_blocks - 2)) {
186 printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n");
187 printk(KERN_NOTICE "numvunits (%d) > nb_blocks (%d) - nb_boot_blocks(%d) - 2\n",
188 nftl->numvunits, nftl->nb_blocks, nftl->nb_boot_blocks);
189 return -1;
190 }
191
192 nftl->mbd.size = nftl->numvunits * (nftl->EraseSize / SECTORSIZE);
193
194
195
196 nftl->nb_blocks = le16_to_cpu(mh->NumEraseUnits) + le16_to_cpu(mh->FirstPhysicalEUN);
197
198
199 nftl->lastEUN = nftl->nb_blocks - 1;
200
201
202 nftl->EUNtable = kmalloc_array(nftl->nb_blocks, sizeof(u16),
203 GFP_KERNEL);
204 if (!nftl->EUNtable) {
205 printk(KERN_NOTICE "NFTL: allocation of EUNtable failed\n");
206 return -ENOMEM;
207 }
208
209 nftl->ReplUnitTable = kmalloc_array(nftl->nb_blocks,
210 sizeof(u16),
211 GFP_KERNEL);
212 if (!nftl->ReplUnitTable) {
213 kfree(nftl->EUNtable);
214 printk(KERN_NOTICE "NFTL: allocation of ReplUnitTable failed\n");
215 return -ENOMEM;
216 }
217
218
219 for (i = 0; i < nftl->nb_boot_blocks; i++)
220 nftl->ReplUnitTable[i] = BLOCK_RESERVED;
221
222 for (; i < nftl->nb_blocks; i++) {
223 nftl->ReplUnitTable[i] = BLOCK_NOTEXPLORED;
224 }
225
226
227 nftl->ReplUnitTable[block] = BLOCK_RESERVED;
228
229
230 for (i = 0; i < nftl->nb_blocks; i++) {
231#if 0
232The new DiskOnChip driver already scanned the bad block table. Just query it.
233 if ((i & (SECTORSIZE - 1)) == 0) {
234
235 ret = mtd->read(nftl->mbd.mtd,
236 block * nftl->EraseSize + i +
237 SECTORSIZE, SECTORSIZE,
238 &retlen, buf);
239 if (ret < 0) {
240 printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n",
241 ret);
242 kfree(nftl->ReplUnitTable);
243 kfree(nftl->EUNtable);
244 return -1;
245 }
246 }
247
248 if (buf[i & (SECTORSIZE - 1)] != 0xff)
249 nftl->ReplUnitTable[i] = BLOCK_RESERVED;
250#endif
251 if (mtd_block_isbad(nftl->mbd.mtd,
252 i * nftl->EraseSize))
253 nftl->ReplUnitTable[i] = BLOCK_RESERVED;
254 }
255
256 nftl->MediaUnit = block;
257 boot_record_count++;
258
259 }
260
261 return boot_record_count?0:-1;
262}
263
264static int memcmpb(void *a, int c, int n)
265{
266 int i;
267 for (i = 0; i < n; i++) {
268 if (c != ((unsigned char *)a)[i])
269 return 1;
270 }
271 return 0;
272}
273
274
275static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int len,
276 int check_oob)
277{
278 struct mtd_info *mtd = nftl->mbd.mtd;
279 size_t retlen;
280 int i, ret;
281 u8 *buf;
282
283 buf = kmalloc(SECTORSIZE + mtd->oobsize, GFP_KERNEL);
284 if (!buf)
285 return -1;
286
287 ret = -1;
288 for (i = 0; i < len; i += SECTORSIZE) {
289 if (mtd_read(mtd, address, SECTORSIZE, &retlen, buf))
290 goto out;
291 if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
292 goto out;
293
294 if (check_oob) {
295 if(nftl_read_oob(mtd, address, mtd->oobsize,
296 &retlen, &buf[SECTORSIZE]) < 0)
297 goto out;
298 if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0)
299 goto out;
300 }
301 address += SECTORSIZE;
302 }
303
304 ret = 0;
305
306out:
307 kfree(buf);
308 return ret;
309}
310
311
312
313
314
315
316
317
318int NFTL_formatblock(struct NFTLrecord *nftl, int block)
319{
320 size_t retlen;
321 unsigned int nb_erases, erase_mark;
322 struct nftl_uci1 uci;
323 struct erase_info *instr = &nftl->instr;
324 struct mtd_info *mtd = nftl->mbd.mtd;
325
326
327 if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8,
328 8, &retlen, (char *)&uci) < 0)
329 goto default_uci1;
330
331 erase_mark = le16_to_cpu ((uci.EraseMark | uci.EraseMark1));
332 if (erase_mark != ERASE_MARK) {
333 default_uci1:
334 uci.EraseMark = cpu_to_le16(ERASE_MARK);
335 uci.EraseMark1 = cpu_to_le16(ERASE_MARK);
336 uci.WearInfo = cpu_to_le32(0);
337 }
338
339 memset(instr, 0, sizeof(struct erase_info));
340
341
342 instr->addr = block * nftl->EraseSize;
343 instr->len = nftl->EraseSize;
344 if (mtd_erase(mtd, instr)) {
345 printk("Error while formatting block %d\n", block);
346 goto fail;
347 }
348
349
350 nb_erases = le32_to_cpu(uci.WearInfo);
351 nb_erases++;
352
353
354 if (nb_erases == 0)
355 nb_erases = 1;
356
357
358
359
360
361 if (check_free_sectors(nftl, instr->addr, nftl->EraseSize, 1) != 0)
362 goto fail;
363
364 uci.WearInfo = le32_to_cpu(nb_erases);
365 if (nftl_write_oob(mtd, block * nftl->EraseSize + SECTORSIZE +
366 8, 8, &retlen, (char *)&uci) < 0)
367 goto fail;
368 return 0;
369fail:
370
371
372 mtd_block_markbad(nftl->mbd.mtd, instr->addr);
373 return -1;
374}
375
376
377
378
379
380
381
382
383
384
385static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_block)
386{
387 struct mtd_info *mtd = nftl->mbd.mtd;
388 unsigned int block, i, status;
389 struct nftl_bci bci;
390 int sectors_per_block;
391 size_t retlen;
392
393 sectors_per_block = nftl->EraseSize / SECTORSIZE;
394 block = first_block;
395 for (;;) {
396 for (i = 0; i < sectors_per_block; i++) {
397 if (nftl_read_oob(mtd,
398 block * nftl->EraseSize + i * SECTORSIZE,
399 8, &retlen, (char *)&bci) < 0)
400 status = SECTOR_IGNORE;
401 else
402 status = bci.Status | bci.Status1;
403
404 switch(status) {
405 case SECTOR_FREE:
406
407
408 if (memcmpb(&bci, 0xff, 8) != 0 ||
409 check_free_sectors(nftl, block * nftl->EraseSize + i * SECTORSIZE,
410 SECTORSIZE, 0) != 0) {
411 printk("Incorrect free sector %d in block %d: "
412 "marking it as ignored\n",
413 i, block);
414
415
416 bci.Status = SECTOR_IGNORE;
417 bci.Status1 = SECTOR_IGNORE;
418 nftl_write_oob(mtd, block *
419 nftl->EraseSize +
420 i * SECTORSIZE, 8,
421 &retlen, (char *)&bci);
422 }
423 break;
424 default:
425 break;
426 }
427 }
428
429
430 block = nftl->ReplUnitTable[block];
431 if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
432 printk("incorrect ReplUnitTable[] : %d\n", block);
433 if (block == BLOCK_NIL || block >= nftl->nb_blocks)
434 break;
435 }
436}
437
438
439static int calc_chain_length(struct NFTLrecord *nftl, unsigned int first_block)
440{
441 unsigned int length = 0, block = first_block;
442
443 for (;;) {
444 length++;
445
446
447 if (length >= nftl->nb_blocks) {
448 printk("nftl: length too long %d !\n", length);
449 break;
450 }
451
452 block = nftl->ReplUnitTable[block];
453 if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
454 printk("incorrect ReplUnitTable[] : %d\n", block);
455 if (block == BLOCK_NIL || block >= nftl->nb_blocks)
456 break;
457 }
458 return length;
459}
460
461
462
463
464
465
466
467
468
469
470
471static void format_chain(struct NFTLrecord *nftl, unsigned int first_block)
472{
473 unsigned int block = first_block, block1;
474
475 printk("Formatting chain at block %d\n", first_block);
476
477 for (;;) {
478 block1 = nftl->ReplUnitTable[block];
479
480 printk("Formatting block %d\n", block);
481 if (NFTL_formatblock(nftl, block) < 0) {
482
483 nftl->ReplUnitTable[block] = BLOCK_RESERVED;
484 } else {
485 nftl->ReplUnitTable[block] = BLOCK_FREE;
486 }
487
488
489 block = block1;
490
491 if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
492 printk("incorrect ReplUnitTable[] : %d\n", block);
493 if (block == BLOCK_NIL || block >= nftl->nb_blocks)
494 break;
495 }
496}
497
498
499
500
501
502
503
504static int check_and_mark_free_block(struct NFTLrecord *nftl, int block)
505{
506 struct mtd_info *mtd = nftl->mbd.mtd;
507 struct nftl_uci1 h1;
508 unsigned int erase_mark;
509 size_t retlen;
510
511
512 if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
513 &retlen, (char *)&h1) < 0)
514 return -1;
515
516 erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
517 if (erase_mark != ERASE_MARK) {
518
519
520 if (check_free_sectors (nftl, block * nftl->EraseSize, nftl->EraseSize, 1) != 0)
521 return -1;
522
523
524 h1.EraseMark = cpu_to_le16(ERASE_MARK);
525 h1.EraseMark1 = cpu_to_le16(ERASE_MARK);
526 h1.WearInfo = cpu_to_le32(0);
527 if (nftl_write_oob(mtd,
528 block * nftl->EraseSize + SECTORSIZE + 8, 8,
529 &retlen, (char *)&h1) < 0)
530 return -1;
531 } else {
532#if 0
533
534 for (i = 0; i < nftl->EraseSize; i += SECTORSIZE) {
535
536 if (check_free_sectors (nftl, block * nftl->EraseSize + i,
537 SECTORSIZE, 0) != 0)
538 return -1;
539
540 if (nftl_read_oob(mtd, block * nftl->EraseSize + i,
541 16, &retlen, buf) < 0)
542 return -1;
543 if (i == SECTORSIZE) {
544
545 if (memcmpb(buf, 0xff, 8))
546 return -1;
547 } else {
548 if (memcmpb(buf, 0xff, 16))
549 return -1;
550 }
551 }
552#endif
553 }
554
555 return 0;
556}
557
558
559
560
561
562
563
564
565static int get_fold_mark(struct NFTLrecord *nftl, unsigned int block)
566{
567 struct mtd_info *mtd = nftl->mbd.mtd;
568 struct nftl_uci2 uci;
569 size_t retlen;
570
571 if (nftl_read_oob(mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8,
572 8, &retlen, (char *)&uci) < 0)
573 return 0;
574
575 return le16_to_cpu((uci.FoldMark | uci.FoldMark1));
576}
577
578int NFTL_mount(struct NFTLrecord *s)
579{
580 int i;
581 unsigned int first_logical_block, logical_block, rep_block, erase_mark;
582 unsigned int block, first_block, is_first_block;
583 int chain_length, do_format_chain;
584 struct nftl_uci0 h0;
585 struct nftl_uci1 h1;
586 struct mtd_info *mtd = s->mbd.mtd;
587 size_t retlen;
588
589
590 if (find_boot_record(s) < 0) {
591 printk("Could not find valid boot record\n");
592 return -1;
593 }
594
595
596 for (i = 0; i < s->nb_blocks; i++) {
597 s->EUNtable[i] = BLOCK_NIL;
598 }
599
600
601 first_logical_block = 0;
602 for (first_block = 0; first_block < s->nb_blocks; first_block++) {
603
604 if (s->ReplUnitTable[first_block] == BLOCK_NOTEXPLORED) {
605 block = first_block;
606 chain_length = 0;
607 do_format_chain = 0;
608
609 for (;;) {
610
611 if (nftl_read_oob(mtd,
612 block * s->EraseSize + 8, 8,
613 &retlen, (char *)&h0) < 0 ||
614 nftl_read_oob(mtd,
615 block * s->EraseSize +
616 SECTORSIZE + 8, 8,
617 &retlen, (char *)&h1) < 0) {
618 s->ReplUnitTable[block] = BLOCK_NIL;
619 do_format_chain = 1;
620 break;
621 }
622
623 logical_block = le16_to_cpu ((h0.VirtUnitNum | h0.SpareVirtUnitNum));
624 rep_block = le16_to_cpu ((h0.ReplUnitNum | h0.SpareReplUnitNum));
625 erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
626
627 is_first_block = !(logical_block >> 15);
628 logical_block = logical_block & 0x7fff;
629
630
631 if (erase_mark != ERASE_MARK || logical_block >= s->nb_blocks) {
632 if (chain_length == 0) {
633
634 if (check_and_mark_free_block(s, block) < 0) {
635
636 printk("Formatting block %d\n", block);
637 if (NFTL_formatblock(s, block) < 0) {
638
639 s->ReplUnitTable[block] = BLOCK_RESERVED;
640 } else {
641 s->ReplUnitTable[block] = BLOCK_FREE;
642 }
643 } else {
644
645 s->ReplUnitTable[block] = BLOCK_FREE;
646 }
647
648 goto examine_ReplUnitTable;
649 } else {
650
651
652 printk("Block %d: free but referenced in chain %d\n",
653 block, first_block);
654 s->ReplUnitTable[block] = BLOCK_NIL;
655 do_format_chain = 1;
656 break;
657 }
658 }
659
660
661 if (chain_length == 0) {
662
663
664
665 if (!is_first_block)
666 goto examine_ReplUnitTable;
667 first_logical_block = logical_block;
668 } else {
669 if (logical_block != first_logical_block) {
670 printk("Block %d: incorrect logical block: %d expected: %d\n",
671 block, logical_block, first_logical_block);
672
673
674 do_format_chain = 1;
675 }
676 if (is_first_block) {
677
678
679
680 if (get_fold_mark(s, block) != FOLD_MARK_IN_PROGRESS ||
681 rep_block != 0xffff) {
682 printk("Block %d: incorrectly marked as first block in chain\n",
683 block);
684
685
686 do_format_chain = 1;
687 } else {
688 printk("Block %d: folding in progress - ignoring first block flag\n",
689 block);
690 }
691 }
692 }
693 chain_length++;
694 if (rep_block == 0xffff) {
695
696 s->ReplUnitTable[block] = BLOCK_NIL;
697 break;
698 } else if (rep_block >= s->nb_blocks) {
699 printk("Block %d: referencing invalid block %d\n",
700 block, rep_block);
701 do_format_chain = 1;
702 s->ReplUnitTable[block] = BLOCK_NIL;
703 break;
704 } else if (s->ReplUnitTable[rep_block] != BLOCK_NOTEXPLORED) {
705
706
707
708
709
710 if (s->ReplUnitTable[rep_block] == BLOCK_NIL &&
711 s->EUNtable[first_logical_block] == rep_block &&
712 get_fold_mark(s, first_block) == FOLD_MARK_IN_PROGRESS) {
713
714 printk("Block %d: folding in progress - ignoring first block flag\n",
715 rep_block);
716 s->ReplUnitTable[block] = rep_block;
717 s->EUNtable[first_logical_block] = BLOCK_NIL;
718 } else {
719 printk("Block %d: referencing block %d already in another chain\n",
720 block, rep_block);
721
722 do_format_chain = 1;
723 s->ReplUnitTable[block] = BLOCK_NIL;
724 }
725 break;
726 } else {
727
728 s->ReplUnitTable[block] = rep_block;
729 block = rep_block;
730 }
731 }
732
733
734
735 if (do_format_chain) {
736
737 format_chain(s, first_block);
738 } else {
739 unsigned int first_block1, chain_to_format, chain_length1;
740 int fold_mark;
741
742
743 fold_mark = get_fold_mark(s, first_block);
744 if (fold_mark == 0) {
745
746 printk("Could read foldmark at block %d\n", first_block);
747 format_chain(s, first_block);
748 } else {
749 if (fold_mark == FOLD_MARK_IN_PROGRESS)
750 check_sectors_in_chain(s, first_block);
751
752
753
754
755
756 first_block1 = s->EUNtable[first_logical_block];
757 if (first_block1 != BLOCK_NIL) {
758
759 chain_length1 = calc_chain_length(s, first_block1);
760 printk("Two chains at blocks %d (len=%d) and %d (len=%d)\n",
761 first_block1, chain_length1, first_block, chain_length);
762
763 if (chain_length >= chain_length1) {
764 chain_to_format = first_block1;
765 s->EUNtable[first_logical_block] = first_block;
766 } else {
767 chain_to_format = first_block;
768 }
769 format_chain(s, chain_to_format);
770 } else {
771 s->EUNtable[first_logical_block] = first_block;
772 }
773 }
774 }
775 }
776 examine_ReplUnitTable:;
777 }
778
779
780 s->numfreeEUNs = 0;
781 s->LastFreeEUN = le16_to_cpu(s->MediaHdr.FirstPhysicalEUN);
782
783 for (block = 0; block < s->nb_blocks; block++) {
784 if (s->ReplUnitTable[block] == BLOCK_NOTEXPLORED) {
785 printk("Unreferenced block %d, formatting it\n", block);
786 if (NFTL_formatblock(s, block) < 0)
787 s->ReplUnitTable[block] = BLOCK_RESERVED;
788 else
789 s->ReplUnitTable[block] = BLOCK_FREE;
790 }
791 if (s->ReplUnitTable[block] == BLOCK_FREE) {
792 s->numfreeEUNs++;
793 s->LastFreeEUN = block;
794 }
795 }
796
797 return 0;
798}
799