]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/nandsim/nandsim.c
powerpc: Transition to Secure-PLT, like most other OSs (Toolchain part)
[FreeBSD/FreeBSD.git] / usr.sbin / nandsim / nandsim.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (C) 2009-2012 Semihalf
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 /*
30  * Control application for the NAND simulator.
31  */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/errno.h>
37 #include <sys/ioctl.h>
38 #include <sys/mman.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41
42 #include <dev/nand/nandsim.h>
43 #include <dev/nand/nand_dev.h>
44
45 #include <ctype.h>
46 #include <fcntl.h>
47 #include <getopt.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <stdarg.h>
52 #include <unistd.h>
53 #include <stdlib.h>
54 #include <limits.h>
55 #include <sysexits.h>
56
57 #include "nandsim_cfgparse.h"
58
59 #define SIMDEVICE       "/dev/nandsim.ioctl"
60
61 #define error(fmt, args...) do { \
62     printf("ERROR: " fmt "\n", ##args); } while (0)
63
64 #define warn(fmt, args...) do { \
65     printf("WARNING: " fmt "\n", ##args); } while (0)
66
67 #define DEBUG
68 #undef DEBUG
69
70 #ifdef DEBUG
71 #define debug(fmt, args...) do { \
72     printf("NANDSIM_CONF:" fmt "\n", ##args); } while (0)
73 #else
74 #define debug(fmt, args...) do {} while(0)
75 #endif
76
77 #define NANDSIM_RAM_LOG_SIZE 16384
78
79 #define MSG_NOTRUNNING          "Controller#%d is not running.Please start" \
80     " it first."
81 #define MSG_RUNNING             "Controller#%d is already running!"
82 #define MSG_CTRLCHIPNEEDED      "You have to specify ctrl_no:cs_no pair!"
83 #define MSG_STATUSACQCTRLCHIP   "Could not acquire status for ctrl#%d chip#%d"
84 #define MSG_STATUSACQCTRL       "Could not acquire status for ctrl#%d"
85 #define MSG_NOCHIP              "There is no such chip configured (chip#%d "\
86     "at ctrl#%d)!"
87
88 #define MSG_NOCTRL              "Controller#%d is not configured!"
89 #define MSG_NOTCONFIGDCTRLCHIP  "Chip connected to ctrl#%d at cs#%d " \
90     "is not configured."
91
92 typedef int (commandfunc_t)(int , char **);
93
94 static struct nandsim_command *getcommand(char *);
95 static int parse_devstring(char *, int *, int *);
96 static void printchip(struct sim_chip *, uint8_t);
97 static void printctrl(struct sim_ctrl *);
98 static int opendev(int *);
99 static commandfunc_t cmdstatus;
100 static commandfunc_t cmdconf;
101 static commandfunc_t cmdstart;
102 static commandfunc_t cmdstop;
103 static commandfunc_t cmdmod;
104 static commandfunc_t cmderror;
105 static commandfunc_t cmdbb;
106 static commandfunc_t cmdfreeze;
107 static commandfunc_t cmdlog;
108 static commandfunc_t cmdstats;
109 static commandfunc_t cmddump;
110 static commandfunc_t cmdrestore;
111 static commandfunc_t cmddestroy;
112 static commandfunc_t cmdhelp;
113 static int checkusage(int, int, char **);
114 static int is_chip_created(int, int, int *);
115 static int is_ctrl_created(int, int *);
116 static int is_ctrl_running(int, int *);
117 static int assert_chip_connected(int , int);
118 static int printstats(int, int, uint32_t, int);
119
120 struct nandsim_command {
121         const char      *cmd_name;      /* Command name */
122         commandfunc_t   *commandfunc;   /* Ptr to command function */
123         uint8_t         req_argc;       /* Mandatory arguments count */
124         const char      *usagestring;   /* Usage string */
125 };
126
127 static struct nandsim_command commands[] = {
128         {"status", cmdstatus, 1,
129             "status <ctl_no|--all|-a> [-v]\n" },
130         {"conf", cmdconf, 1,
131             "conf <filename>\n" },
132         {"start", cmdstart, 1,
133             "start <ctrl_no>\n" },
134         {"mod", cmdmod, 2,
135             "mod [-l <loglevel>] | <ctl_no:cs_no> [-p <prog_time>]\n"
136             "\t[-e <erase_time>] [-r <read_time>]\n"
137             "\t[-E <error_ratio>] | [-h]\n" },
138         {"stop", cmdstop, 1,
139             "stop <ctrl_no>\n" },
140         {"error", cmderror, 5,
141             "error <ctrl_no:cs_no> <page_num> <column> <length> <pattern>\n" },
142         {"bb", cmdbb, 2,
143             "bb <ctl_no:cs_no>  [blk_num1,blk_num2,..] [-U] [-L]\n" },
144         {"freeze", cmdfreeze, 1,
145             "freeze [ctrl_no]\n" },
146         {"log", cmdlog, 1,
147             "log <ctrl_no|--all|-a>\n" },
148         {"stats", cmdstats, 2,
149             "stats <ctrl_no:cs_no> <pagenumber>\n" },
150         {"dump", cmddump, 2,
151             "dump <ctrl_no:cs_no> <filename>\n" },
152         {"restore", cmdrestore, 2,
153             "restore <ctrl_no:chip_no> <filename>\n" },
154         {"destroy", cmddestroy, 1,
155             "destroy <ctrl_no[:cs_no]|--all|-a>\n" },
156         {"help", cmdhelp, 0,
157             "help [-v]" },
158         {NULL, NULL, 0, NULL},
159 };
160
161
162 /* Parse command name, and start appropriate function */
163 static struct nandsim_command*
164 getcommand(char *arg)
165 {
166         struct nandsim_command *opts;
167
168         for (opts = commands; (opts != NULL) &&
169             (opts->cmd_name != NULL); opts++) {
170                 if (strcmp(opts->cmd_name, arg) == 0)
171                         return (opts);
172         }
173         return (NULL);
174 }
175
176 /*
177  * Parse given string in format <ctrl_no>:<cs_no>, if possible -- set
178  * ctrl and/or cs, and return 0 (success) or 1 (in case of error).
179  *
180  * ctrl == 0xff && chip == 0xff  : '--all' flag specified
181  * ctrl != 0xff && chip != 0xff  : both ctrl & chip were specified
182  * ctrl != 0xff && chip == 0xff  : only ctrl was specified
183  */
184 static int
185 parse_devstring(char *str, int *ctrl, int *cs)
186 {
187         char *tmpstr;
188         unsigned int num = 0;
189
190         /* Ignore white spaces at the beginning */
191         while (isspace(*str) && (*str != '\0'))
192                 str++;
193
194         *ctrl = 0xff;
195         *cs = 0xff;
196         if (strcmp(str, "--all") == 0 ||
197             strcmp(str, "-a") == 0) {
198                 /* If --all or -a is specified, ctl==chip==0xff */
199                 debug("CTRL=%d CHIP=%d\n", *ctrl, *cs);
200                 return (0);
201         }
202         /* Separate token and try to convert it to int */
203         tmpstr = (char *)strtok(str, ":");
204         if ((tmpstr != NULL) && (*tmpstr != '\0')) {
205                 if (convert_arguint(tmpstr, &num) != 0)
206                         return (1);
207
208                 if (num > MAX_SIM_DEV - 1) {
209                         error("Invalid ctrl_no supplied: %s. Valid ctrl_no "
210                             "value must lie between 0 and 3!", tmpstr);
211                         return (1);
212                 }
213
214                 *ctrl = num;
215                 tmpstr = (char *)strtok(NULL, ":");
216
217                 if ((tmpstr != NULL) && (*tmpstr != '\0')) {
218                         if (convert_arguint(tmpstr, &num) != 0)
219                                 return (1);
220
221                         /* Check if chip_no is valid */
222                         if (num > MAX_CTRL_CS - 1) {
223                                 error("Invalid chip_no supplied: %s. Valid "
224                                     "chip_no value must lie between 0 and 3!",
225                                     tmpstr);
226                                 return (1);
227                         }
228                         *cs = num;
229                 }
230         } else
231                 /* Empty devstring supplied */
232                 return (1);
233
234         debug("CTRL=%d CHIP=%d\n", *ctrl, *cs);
235         return (0);
236 }
237
238 static int
239 opendev(int *fd)
240 {
241
242         *fd = open(SIMDEVICE, O_RDWR);
243         if (*fd == -1) {
244                 error("Could not open simulator device file (%s)!",
245                     SIMDEVICE);
246                 return (EX_OSFILE);
247         }
248         return (EX_OK);
249 }
250
251 static int
252 opencdev(int *cdevd, int ctrl, int chip)
253 {
254         char fname[255];
255
256         sprintf(fname, "/dev/nandsim%d.%d", ctrl, chip);
257         *cdevd = open(fname, O_RDWR);
258         if (*cdevd == -1)
259                 return (EX_NOINPUT);
260
261         return (EX_OK);
262 }
263
264 /*
265  * Check if given arguments count match requirements. If no, or
266  * --help (-h) flag is specified -- return 1 (print usage)
267  */
268 static int
269 checkusage(int gargc, int argsreqd, char **gargv)
270 {
271
272         if (gargc < argsreqd + 2 || (gargc >= (argsreqd + 2) &&
273             (strcmp(gargv[1], "--help") == 0 ||
274             strcmp(gargv[1], "-h") == 0)))
275                 return (1);
276
277         return (0);
278 }
279
280 static int
281 cmdstatus(int gargc, char **gargv)
282 {
283         int chip = 0, ctl = 0, err = 0, fd, idx, idx2, start, stop;
284         uint8_t verbose = 0;
285         struct sim_ctrl ctrlconf;
286         struct sim_chip chipconf;
287
288         err = parse_devstring(gargv[2], &ctl, &chip);
289         if (err) {
290                 return (EX_USAGE);
291         } else if (ctl == 0xff) {
292                 /* Every controller */
293                 start = 0;
294                 stop = MAX_SIM_DEV-1;
295         } else {
296                 /* Specified controller only */
297                 start = ctl;
298                 stop = ctl;
299         }
300
301         if (opendev(&fd) != EX_OK)
302                 return (EX_OSFILE);
303
304         for (idx = 0; idx < gargc; idx ++)
305                 if (strcmp(gargv[idx], "-v") == 0 ||
306                     strcmp(gargv[idx], "--verbose") == 0)
307                         verbose = 1;
308
309         for (idx = start; idx <= stop; idx++) {
310                 ctrlconf.num = idx;
311                 err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrlconf);
312                 if (err) {
313                         err = EX_SOFTWARE;
314                         error(MSG_STATUSACQCTRL, idx);
315                         continue;
316                 }
317
318                 printctrl(&ctrlconf);
319
320                 for (idx2 = 0; idx2 < MAX_CTRL_CS; idx2++) {
321                         chipconf.num = idx2;
322                         chipconf.ctrl_num = idx;
323
324                         err = ioctl(fd, NANDSIM_STATUS_CHIP, &chipconf);
325                         if (err) {
326                                 err = EX_SOFTWARE;
327                                 error(MSG_STATUSACQCTRL, idx);
328                                 continue;
329                         }
330
331                         printchip(&chipconf, verbose);
332                 }
333         }
334         close(fd);
335         return (err);
336 }
337
338 static int
339 cmdconf(int gargc __unused, char **gargv)
340 {
341         int err;
342
343         err = parse_config(gargv[2], SIMDEVICE);
344         if (err)
345                 return (EX_DATAERR);
346
347         return (EX_OK);
348 }
349
350 static int
351 cmdstart(int gargc __unused, char **gargv)
352 {
353         int chip = 0, ctl = 0, err = 0, fd, running, state;
354
355         err = parse_devstring(gargv[2], &ctl, &chip);
356         if (err)
357                 return (EX_USAGE);
358
359         err = is_ctrl_created(ctl, &state);
360         if (err) {
361                 return (EX_SOFTWARE);
362         } else if (state == 0) {
363                 error(MSG_NOCTRL, ctl);
364                 return (EX_SOFTWARE);
365         }
366
367         err = is_ctrl_running(ctl, &running);
368         if (err)
369                 return (EX_SOFTWARE);
370
371         if (running) {
372                 warn(MSG_RUNNING, ctl);
373         } else {
374                 if (opendev(&fd) != EX_OK)
375                         return (EX_OSFILE);
376
377                 err = ioctl(fd, NANDSIM_START_CTRL, &ctl);
378                 close(fd);
379                 if (err) {
380                         error("Cannot start controller#%d", ctl);
381                         err = EX_SOFTWARE;
382                 }
383         }
384         return (err);
385 }
386
387 static int
388 cmdstop(int gargc __unused, char **gargv)
389 {
390         int chip = 0, ctl = 0, err = 0, fd, running;
391
392         err = parse_devstring(gargv[2], &ctl, &chip);
393         if (err)
394                 return (EX_USAGE);
395
396         err = is_ctrl_running(ctl, &running);
397         if (err)
398                 return (EX_SOFTWARE);
399
400         if (!running) {
401                 error(MSG_NOTRUNNING, ctl);
402         } else {
403                 if (opendev(&fd) != EX_OK)
404                         return (EX_OSFILE);
405
406                 err = ioctl(fd, NANDSIM_STOP_CTRL, &ctl);
407                 close(fd);
408                 if (err) {
409                         error("Cannot stop controller#%d", ctl);
410                         err = EX_SOFTWARE;
411                 }
412         }
413
414         return (err);
415 }
416
417 static int
418 cmdmod(int gargc __unused, char **gargv)
419 {
420         int chip, ctl, err = 0, fd = -1, i;
421         struct sim_mod mods;
422
423         if (gargc >= 4) {
424                 if (strcmp(gargv[2], "--loglevel") == 0 || strcmp(gargv[2],
425                     "-l") == 0) {
426                         /* Set loglevel (ctrl:chip pair independent) */
427                         mods.field = SIM_MOD_LOG_LEVEL;
428
429                         if (convert_arguint(gargv[3], &mods.new_value) != 0)
430                                 return (EX_SOFTWARE);
431
432                         if (opendev(&fd) != EX_OK)
433                                 return (EX_OSFILE);
434
435                         err = ioctl(fd, NANDSIM_MODIFY, &mods);
436                         if (err) {
437                                 error("simulator parameter %s could not be "
438                                     "modified !", gargv[3]);
439                                 close(fd);
440                                 return (EX_SOFTWARE);
441                         }
442
443                         debug("request : loglevel = %d\n", mods.new_value);
444                         close(fd);
445                         return (EX_OK);
446                 }
447         }
448
449         err = parse_devstring(gargv[2], &ctl, &chip);
450         if (err)
451                 return (EX_USAGE);
452
453         else if (chip == 0xff) {
454                 error(MSG_CTRLCHIPNEEDED);
455                 return (EX_USAGE);
456         }
457
458         if (!assert_chip_connected(ctl, chip))
459                 return (EX_SOFTWARE);
460
461         if (opendev(&fd) != EX_OK)
462                 return (EX_OSFILE);
463
464         /* Find out which flags were passed */
465         for (i = 3; i < gargc; i++) {
466
467                 if (convert_arguint(gargv[i + 1], &mods.new_value) != 0)
468                         continue;
469
470                 if (strcmp(gargv[i], "--prog-time") == 0 ||
471                     strcmp(gargv[i], "-p") == 0) {
472
473                         mods.field = SIM_MOD_PROG_TIME;
474                         debug("request : progtime = %d\n", mods.new_value);
475
476                 } else if (strcmp(gargv[i], "--erase-time") == 0 ||
477                     strcmp(gargv[i], "-e") == 0) {
478
479                         mods.field = SIM_MOD_ERASE_TIME;
480                         debug("request : eraseime = %d\n", mods.new_value);
481
482                 } else if (strcmp(gargv[i], "--read-time") == 0 ||
483                     strcmp(gargv[i], "-r") == 0) {
484
485                         mods.field = SIM_MOD_READ_TIME;
486                         debug("request : read_time = %d\n", mods.new_value);
487
488                 } else if (strcmp(gargv[i], "--error-ratio") == 0 ||
489                     strcmp(gargv[i], "-E") == 0) {
490
491                         mods.field = SIM_MOD_ERROR_RATIO;
492                         debug("request : error_ratio = %d\n", mods.new_value);
493
494                 } else {
495                         /* Flag not recognized, or nothing specified. */
496                         error("Unrecognized flag:%s\n", gargv[i]);
497                         if (fd >= 0)
498                                 close(fd);
499                         return (EX_USAGE);
500                 }
501
502                 mods.chip_num = chip;
503                 mods.ctrl_num = ctl;
504
505                 /* Call appropriate ioctl */
506                 err = ioctl(fd, NANDSIM_MODIFY, &mods);
507                 if (err) {
508                         error("simulator parameter %s could not be modified! ",
509                             gargv[i]);
510                         continue;
511                 }
512                 i++;
513         }
514         close(fd);
515         return (EX_OK);
516 }
517
518 static int
519 cmderror(int gargc __unused, char **gargv)
520 {
521         uint32_t page, column, len, pattern;
522         int chip = 0, ctl = 0, err = 0, fd;
523         struct sim_error sim_err;
524
525         err = parse_devstring(gargv[2], &ctl, &chip);
526         if (err)
527                 return (EX_USAGE);
528
529         if (chip == 0xff) {
530                 error(MSG_CTRLCHIPNEEDED);
531                 return (EX_USAGE);
532         }
533
534         if (convert_arguint(gargv[3], &page) ||
535             convert_arguint(gargv[4], &column) ||
536             convert_arguint(gargv[5], &len) ||
537             convert_arguint(gargv[6], &pattern))
538                 return (EX_SOFTWARE);
539
540         if (!assert_chip_connected(ctl, chip))
541                 return (EX_SOFTWARE);
542
543         sim_err.page_num = page;
544         sim_err.column = column;
545         sim_err.len = len;
546         sim_err.pattern = pattern;
547         sim_err.ctrl_num = ctl;
548         sim_err.chip_num = chip;
549
550         if (opendev(&fd) != EX_OK)
551                 return (EX_OSFILE);
552
553         err = ioctl(fd, NANDSIM_INJECT_ERROR, &sim_err);
554
555         close(fd);
556         if (err) {
557                 error("Could not inject error !");
558                 return (EX_SOFTWARE);
559         }
560         return (EX_OK);
561 }
562
563 static int
564 cmdbb(int gargc, char **gargv)
565 {
566         struct sim_block_state bs;
567         struct chip_param_io cparams;
568         uint32_t blkidx;
569         int c, cdevd, chip = 0, ctl = 0, err = 0, fd, idx;
570         uint8_t flagL = 0, flagU = 0;
571         int *badblocks = NULL;
572
573         /* Check for --list/-L or --unmark/-U flags */
574         for (idx = 3; idx < gargc; idx++) {
575                 if (strcmp(gargv[idx], "--list") == 0 ||
576                     strcmp(gargv[idx], "-L") == 0)
577                         flagL = idx;
578                 if (strcmp(gargv[idx], "--unmark") == 0 ||
579                     strcmp(gargv[idx], "-U") == 0)
580                         flagU = idx;
581         }
582
583         if (flagL == 2 || flagU == 2 || flagU == 3)
584                 return (EX_USAGE);
585
586         err = parse_devstring(gargv[2], &ctl, &chip);
587         if (err) {
588                 return (EX_USAGE);
589         }
590         if (chip == 0xff || ctl == 0xff) {
591                 error(MSG_CTRLCHIPNEEDED);
592                 return (EX_USAGE);
593         }
594
595         bs.ctrl_num = ctl;
596         bs.chip_num = chip;
597
598         if (!assert_chip_connected(ctl, chip))
599                 return (EX_SOFTWARE);
600
601         if (opencdev(&cdevd, ctl, chip) != EX_OK)
602                 return (EX_OSFILE);
603
604         err = ioctl(cdevd, NAND_IO_GET_CHIP_PARAM, &cparams);
605         if (err)
606                 return (EX_SOFTWARE);
607
608         close(cdevd);
609
610         bs.ctrl_num = ctl;
611         bs.chip_num = chip;
612
613         if (opendev(&fd) != EX_OK)
614                 return (EX_OSFILE);
615
616         if (flagL != 3) {
617                 /*
618                  * Flag -L was specified either after blocklist or was not
619                  * specified at all.
620                  */
621                 c = parse_intarray(gargv[3], &badblocks);
622
623                 for (idx = 0; idx < c; idx++) {
624                         bs.block_num = badblocks[idx];
625                         /* Do not change wearout */
626                         bs.wearout = -1;
627                         bs.state = (flagU == 0) ? NANDSIM_BAD_BLOCK :
628                             NANDSIM_GOOD_BLOCK;
629
630                         err = ioctl(fd, NANDSIM_SET_BLOCK_STATE, &bs);
631                         if (err) {
632                                 error("Could not set bad block(%d) for "
633                                     "controller (%d)!",
634                                     badblocks[idx], ctl);
635                                 err = EX_SOFTWARE;
636                                 break;
637                         }
638                 }
639         }
640         if (flagL != 0) {
641                 /* If flag -L was specified (anywhere) */
642                 for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
643                         bs.block_num = blkidx;
644                         /* Do not change the wearout */
645                         bs.wearout = -1;
646                         err = ioctl(fd, NANDSIM_GET_BLOCK_STATE, &bs);
647                         if (err) {
648                                 error("Could not acquire block state");
649                                 err = EX_SOFTWARE;
650                                 continue;
651                         }
652                         printf("Block#%d: wear count: %d %s\n", blkidx,
653                             bs.wearout,
654                             (bs.state == NANDSIM_BAD_BLOCK) ? "BAD":"GOOD");
655                 }
656         }
657         close(fd);
658         return (err);
659 }
660
661 static int
662 cmdfreeze(int gargc __unused, char **gargv)
663 {
664         int chip = 0, ctl = 0, err = 0, fd, i, start = 0, state, stop = 0;
665         struct sim_ctrl_chip ctrlchip;
666
667         err = parse_devstring(gargv[2], &ctl, &chip);
668         if (err)
669                 return (EX_USAGE);
670
671         if (ctl == 0xff) {
672                 error("You have to specify at least controller number");
673                 return (EX_USAGE);
674         }
675
676         if (ctl != 0xff && chip == 0xff) {
677                 start = 0;
678                 stop = MAX_CTRL_CS - 1;
679         } else {
680                 start = chip;
681                 stop = chip;
682         }
683
684         ctrlchip.ctrl_num = ctl;
685
686         err = is_ctrl_running(ctl, &state);
687         if (err)
688                 return (EX_SOFTWARE);
689         if (state == 0) {
690                 error(MSG_NOTRUNNING, ctl);
691                 return (EX_SOFTWARE);
692         }
693
694         if (opendev(&fd) != EX_OK)
695                 return (EX_OSFILE);
696
697         for (i = start; i <= stop; i++) {
698                 err = is_chip_created(ctl, i, &state);
699                 if (err)
700                         return (EX_SOFTWARE);
701                 else if (state == 0) {
702                         continue;
703                 }
704
705                 ctrlchip.chip_num = i;
706                 err = ioctl(fd, NANDSIM_FREEZE, &ctrlchip);
707                 if (err) {
708                         error("Could not freeze ctrl#%d chip#%d", ctl, i);
709                         close(fd);
710                         return (EX_SOFTWARE);
711                 }
712         }
713         close(fd);
714         return (EX_OK);
715 }
716
717 static int
718 cmdlog(int gargc __unused, char **gargv)
719 {
720         struct sim_log log;
721         int chip = 0, ctl = 0, err = 0, fd, idx, start = 0, stop = 0;
722         char *logbuf;
723
724         err = parse_devstring(gargv[2], &ctl, &chip);
725         if (err)
726                 return (EX_USAGE);
727
728         logbuf = (char *)malloc(sizeof(char) * NANDSIM_RAM_LOG_SIZE);
729         if (logbuf == NULL) {
730                 error("Not enough memory to create log buffer");
731                 return (EX_SOFTWARE);
732         }
733
734         memset(logbuf, 0, NANDSIM_RAM_LOG_SIZE);
735         log.log = logbuf;
736         log.len = NANDSIM_RAM_LOG_SIZE;
737
738         if (ctl == 0xff) {
739                 start = 0;
740                 stop = MAX_SIM_DEV-1;
741         } else {
742                 start = ctl;
743                 stop = ctl;
744         }
745
746         if (opendev(&fd) != EX_OK) {
747                 free(logbuf);
748                 return (EX_OSFILE);
749         }
750
751         /* Print logs for selected controller(s) */
752         for (idx = start; idx <= stop; idx++) {
753                 log.ctrl_num = idx;
754
755                 err = ioctl(fd, NANDSIM_PRINT_LOG, &log);
756                 if (err) {
757                         error("Could not get log for controller %d!", idx);
758                         continue;
759                 }
760
761                 printf("Logs for controller#%d:\n%s\n", idx, logbuf);
762         }
763
764         free(logbuf);
765         close(fd);
766         return (EX_OK);
767 }
768
769 static int
770 cmdstats(int gargc __unused, char **gargv)
771 {
772         int cdevd, chip = 0, ctl = 0, err = 0;
773         uint32_t pageno = 0;
774
775         err = parse_devstring(gargv[2], &ctl, &chip);
776
777         if (err)
778                 return (EX_USAGE);
779
780         if (chip == 0xff) {
781                 error(MSG_CTRLCHIPNEEDED);
782                 return (EX_USAGE);
783         }
784
785         if (convert_arguint(gargv[3], &pageno) != 0)
786                 return (EX_USAGE);
787
788         if (!assert_chip_connected(ctl, chip))
789                 return (EX_SOFTWARE);
790
791         if (opencdev(&cdevd, ctl, chip) != EX_OK)
792                 return (EX_OSFILE);
793
794         err = printstats(ctl, chip, pageno, cdevd);
795         if (err) {
796                 close(cdevd);
797                 return (EX_SOFTWARE);
798         }
799         close(cdevd);
800         return (EX_OK);
801 }
802
803 static int
804 cmddump(int gargc __unused, char **gargv)
805 {
806         struct sim_dump dump;
807         struct sim_block_state bs;
808         struct chip_param_io cparams;
809         int chip = 0, ctl = 0, err = EX_OK, fd, dumpfd;
810         uint32_t blkidx, bwritten = 0, totalwritten = 0;
811         void *buf;
812
813         err = parse_devstring(gargv[2], &ctl, &chip);
814         if (err)
815                 return (EX_USAGE);
816
817         if (chip == 0xff || ctl == 0xff) {
818                 error(MSG_CTRLCHIPNEEDED);
819                 return (EX_USAGE);
820         }
821
822         if (!assert_chip_connected(ctl, chip))
823                 return (EX_SOFTWARE);
824
825         if (opencdev(&fd, ctl, chip) != EX_OK)
826                 return (EX_OSFILE);
827
828         err = ioctl(fd, NAND_IO_GET_CHIP_PARAM, &cparams);
829         if (err) {
830                 error("Cannot get parameters for chip %d:%d", ctl, chip);
831                 close(fd);
832                 return (EX_SOFTWARE);
833         }
834         close(fd);
835
836         dump.ctrl_num = ctl;
837         dump.chip_num = chip;
838
839         dump.len = cparams.pages_per_block * (cparams.page_size +
840             cparams.oob_size);
841
842         buf = malloc(dump.len);
843         if (buf == NULL) {
844                 error("Could not allocate memory!");
845                 return (EX_SOFTWARE);
846         }
847         dump.data = buf;
848
849         errno = 0;
850         dumpfd = open(gargv[3], O_WRONLY | O_CREAT, 0666);
851         if (dumpfd == -1) {
852                 error("Cannot create dump file.");
853                 free(buf);
854                 return (EX_SOFTWARE);
855         }
856
857         if (opendev(&fd)) {
858                 close(dumpfd);
859                 free(buf);
860                 return (EX_SOFTWARE);
861         }
862
863         bs.ctrl_num = ctl;
864         bs.chip_num = chip;
865
866         /* First uint32_t in file shall contain block count */
867         if (write(dumpfd, &cparams, sizeof(cparams)) < (int)sizeof(cparams)) {
868                 error("Error writing to dumpfile!");
869                 close(fd);
870                 close(dumpfd);
871                 free(buf);
872                 return (EX_SOFTWARE);
873         }
874
875         /*
876          * First loop acquires blocks states and writes them to
877          * the dump file.
878          */
879         for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
880                 bs.block_num = blkidx;
881                 err = ioctl(fd, NANDSIM_GET_BLOCK_STATE, &bs);
882                 if (err) {
883                         error("Could not get bad block(%d) for "
884                             "controller (%d)!", blkidx, ctl);
885                         close(fd);
886                         close(dumpfd);
887                         free(buf);
888                         return (EX_SOFTWARE);
889                 }
890
891                 bwritten = write(dumpfd, &bs, sizeof(bs));
892                 if (bwritten != sizeof(bs)) {
893                         error("Error writing to dumpfile");
894                         close(fd);
895                         close(dumpfd);
896                         free(buf);
897                         return (EX_SOFTWARE);
898                 }
899         }
900
901         /* Second loop dumps the data */
902         for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
903                 debug("Block#%d...", blkidx);
904                 dump.block_num = blkidx;
905
906                 err = ioctl(fd, NANDSIM_DUMP, &dump);
907                 if (err) {
908                         error("Could not dump ctrl#%d chip#%d "
909                             "block#%d", ctl, chip, blkidx);
910                         err = EX_SOFTWARE;
911                         break;
912                 }
913
914                 bwritten = write(dumpfd, dump.data, dump.len);
915                 if (bwritten != dump.len) {
916                         error("Error writing to dumpfile");
917                         err = EX_SOFTWARE;
918                         break;
919                 }
920                 debug("OK!\n");
921                 totalwritten += bwritten;
922         }
923         printf("%d out of %d B written.\n", totalwritten, dump.len * blkidx);
924
925         close(fd);
926         close(dumpfd);
927         free(buf);
928         return (err);
929 }
930
931 static int
932 cmdrestore(int gargc __unused, char **gargv)
933 {
934         struct sim_dump dump;
935         struct sim_block_state bs;
936         struct stat filestat;
937         int chip = 0, ctl = 0, err = 0, fd, dumpfd = -1;
938         uint32_t blkidx, blksz, fsize = 0, expfilesz;
939         void *buf;
940         struct chip_param_io cparams, dumpcparams;
941
942         err = parse_devstring(gargv[2], &ctl, &chip);
943         if (err)
944                 return (EX_USAGE);
945         else if (ctl == 0xff) {
946                 error(MSG_CTRLCHIPNEEDED);
947                 return (EX_USAGE);
948         }
949
950         if (!assert_chip_connected(ctl, chip))
951                 return (EX_SOFTWARE);
952
953         /* Get chip geometry */
954         if (opencdev(&fd, ctl, chip) != EX_OK)
955                 return (EX_OSFILE);
956
957         err = ioctl(fd, NAND_IO_GET_CHIP_PARAM, &cparams);
958         if (err) {
959                 error("Cannot get parameters for chip %d:%d", ctl, chip);
960                 close(fd);
961                 return (err);
962         }
963         close(fd);
964
965         /* Obtain dump file size */
966         errno = 0;
967         if (stat(gargv[3], &filestat) != 0) {
968                 error("Could not acquire file size! : %s",
969                     strerror(errno));
970                 return (EX_IOERR);
971         }
972
973         fsize = filestat.st_size;
974         blksz = cparams.pages_per_block * (cparams.page_size +
975             cparams.oob_size);
976
977         /* Expected dump file size for chip */
978         expfilesz = cparams.blocks * (blksz + sizeof(bs)) + sizeof(cparams);
979
980         if (fsize != expfilesz) {
981                 error("File size does not match chip geometry (file size: %d"
982                     ", dump size: %d)", fsize, expfilesz);
983                 return (EX_SOFTWARE);
984         }
985
986         dumpfd = open(gargv[3], O_RDONLY);
987         if (dumpfd == -1) {
988                 error("Could not open dump file!");
989                 return (EX_IOERR);
990         }
991
992         /* Read chip params saved in dumpfile */
993         read(dumpfd, &dumpcparams, sizeof(dumpcparams));
994
995         /* XXX */
996         if (bcmp(&dumpcparams, &cparams, sizeof(cparams)) != 0) {
997                 error("Supplied dump is created for a chip with different "
998                     "chip configuration!");
999                 close(dumpfd);
1000                 return (EX_SOFTWARE);
1001         }
1002
1003         if (opendev(&fd) != EX_OK) {
1004                 close(dumpfd);
1005                 return (EX_OSFILE);
1006         }
1007
1008         buf = malloc(blksz);
1009         if (buf == NULL) {
1010                 error("Could not allocate memory for block buffer");
1011                 close(dumpfd);
1012                 close(fd);
1013                 return (EX_SOFTWARE);
1014         }
1015
1016         dump.ctrl_num = ctl;
1017         dump.chip_num = chip;
1018         dump.data = buf;
1019         /* Restore block states and wearouts */
1020         for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
1021                 dump.block_num = blkidx;
1022                 if (read(dumpfd, &bs, sizeof(bs)) != sizeof(bs)) {
1023                         error("Error reading dumpfile");
1024                         close(dumpfd);
1025                         close(fd);
1026                         free(buf);
1027                         return (EX_SOFTWARE);
1028                 }
1029                 bs.ctrl_num = ctl;
1030                 bs.chip_num = chip;
1031                 debug("BLKIDX=%d BLOCKS=%d CTRL=%d CHIP=%d STATE=%d\n"
1032                     "WEAROUT=%d BS.CTRL_NUM=%d BS.CHIP_NUM=%d\n",
1033                     blkidx, cparams.blocks, dump.ctrl_num, dump.chip_num,
1034                     bs.state, bs.wearout, bs.ctrl_num, bs.chip_num);
1035
1036                 err = ioctl(fd, NANDSIM_SET_BLOCK_STATE, &bs);
1037                 if (err) {
1038                         error("Could not set bad block(%d) for "
1039                             "controller: %d, chip: %d!", blkidx, ctl, chip);
1040                         close(dumpfd);
1041                         close(fd);
1042                         free(buf);
1043                         return (EX_SOFTWARE);
1044                 }
1045         }
1046         /* Restore data */
1047         for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
1048                 errno = 0;
1049                 dump.len = read(dumpfd, buf, blksz);
1050                 if (errno) {
1051                         error("Failed to read block#%d from dumpfile.", blkidx);
1052                         err = EX_SOFTWARE;
1053                         break;
1054                 }
1055                 dump.block_num = blkidx;
1056                 err = ioctl(fd, NANDSIM_RESTORE, &dump);
1057                 if (err) {
1058                         error("Could not restore block#%d of ctrl#%d chip#%d"
1059                             ": %s", blkidx, ctl, chip, strerror(errno));
1060                         err = EX_SOFTWARE;
1061                         break;
1062                 }
1063         }
1064
1065         free(buf);
1066         close(dumpfd);
1067         close(fd);
1068         return (err);
1069
1070 }
1071
1072 static int
1073 cmddestroy(int gargc __unused, char **gargv)
1074 {
1075         int chip = 0, ctl = 0, err = 0, fd, idx, idx2, state;
1076         int chipstart, chipstop, ctrlstart, ctrlstop;
1077         struct sim_chip_destroy chip_destroy;
1078
1079         err = parse_devstring(gargv[2], &ctl, &chip);
1080
1081         if (err)
1082                 return (EX_USAGE);
1083
1084         if (ctl == 0xff) {
1085                 /* Every chip at every controller */
1086                 ctrlstart = chipstart = 0;
1087                 ctrlstop = MAX_SIM_DEV - 1;
1088                 chipstop = MAX_CTRL_CS - 1;
1089         } else {
1090                 ctrlstart = ctrlstop = ctl;
1091                 if (chip == 0xff) {
1092                         /* Every chip at selected controller */
1093                         chipstart = 0;
1094                         chipstop = MAX_CTRL_CS - 1;
1095                 } else
1096                         /* Selected chip at selected controller */
1097                         chipstart = chipstop = chip;
1098         }
1099         debug("CTRLSTART=%d CTRLSTOP=%d CHIPSTART=%d CHIPSTOP=%d\n",
1100             ctrlstart, ctrlstop, chipstart, chipstop);
1101         for (idx = ctrlstart; idx <= ctrlstop; idx++) {
1102                 err = is_ctrl_created(idx, &state);
1103                 if (err) {
1104                         error("Could not acquire ctrl#%d state. Cannot "
1105                             "destroy controller.", idx);
1106                         return (EX_SOFTWARE);
1107                 }
1108                 if (state == 0) {
1109                         continue;
1110                 }
1111                 err = is_ctrl_running(idx, &state);
1112                 if (err) {
1113                         error(MSG_STATUSACQCTRL, idx);
1114                         return (EX_SOFTWARE);
1115                 }
1116                 if (state != 0) {
1117                         error(MSG_RUNNING, ctl);
1118                         return (EX_SOFTWARE);
1119                 }
1120                 if (opendev(&fd) != EX_OK)
1121                         return (EX_OSFILE);
1122
1123                 for (idx2 = chipstart; idx2 <= chipstop; idx2++) {
1124                         err = is_chip_created(idx, idx2, &state);
1125                         if (err) {
1126                                 error(MSG_STATUSACQCTRLCHIP, idx2, idx);
1127                                 continue;
1128                         }
1129                         if (state == 0)
1130                                 /* There is no such chip running */
1131                                 continue;
1132                         chip_destroy.ctrl_num = idx;
1133                         chip_destroy.chip_num = idx2;
1134                         ioctl(fd, NANDSIM_DESTROY_CHIP,
1135                             &chip_destroy);
1136                 }
1137                 /* If chip isn't explicitly specified -- destroy ctrl */
1138                 if (chip == 0xff) {
1139                         err = ioctl(fd, NANDSIM_DESTROY_CTRL, &idx);
1140                         if (err) {
1141                                 error("Could not destroy ctrl#%d", idx);
1142                                 continue;
1143                         }
1144                 }
1145                 close(fd);
1146         }
1147         return (err);
1148 }
1149
1150 int
1151 main(int argc, char **argv)
1152 {
1153         struct nandsim_command *cmdopts;
1154         int retcode = 0;
1155
1156         if (argc < 2) {
1157                 cmdhelp(argc, argv);
1158                 retcode = EX_USAGE;
1159         } else {
1160                 cmdopts = getcommand(argv[1]);
1161                 if (cmdopts != NULL && cmdopts->commandfunc != NULL) {
1162                         if (checkusage(argc, cmdopts->req_argc, argv) == 1) {
1163                                 /* Print command specific usage */
1164                                 printf("nandsim %s", cmdopts->usagestring);
1165                                 return (EX_USAGE);
1166                         }
1167                         retcode = cmdopts->commandfunc(argc, argv);
1168
1169                         if (retcode == EX_USAGE) {
1170                                 /* Print command-specific usage */
1171                                 printf("nandsim %s", cmdopts->usagestring);
1172                         } else if (retcode == EX_OSFILE) {
1173                                 error("Could not open device file");
1174                         }
1175
1176                 } else {
1177                         error("Unknown command!");
1178                         retcode = EX_USAGE;
1179                 }
1180         }
1181         return (retcode);
1182 }
1183
1184 static int
1185 cmdhelp(int gargc __unused, char **gargv __unused)
1186 {
1187         struct nandsim_command *opts;
1188
1189         printf("usage:  nandsim <command> [command params] [params]\n\n");
1190
1191         for (opts = commands; (opts != NULL) &&
1192             (opts->cmd_name != NULL); opts++)
1193                 printf("nandsim %s", opts->usagestring);
1194
1195         printf("\n");
1196         return (EX_OK);
1197 }
1198
1199 static void
1200 printchip(struct sim_chip *chip, uint8_t verbose)
1201 {
1202
1203         if (chip->created == 0)
1204                 return;
1205         if (verbose > 0) {
1206                 printf("\n[Chip info]\n");
1207                 printf("num= %d\nctrl_num=%d\ndevice_id=%02x"
1208                     "\tmanufacturer_id=%02x\ndevice_model=%s\nmanufacturer="
1209                     "%s\ncol_addr_cycles=%d\nrow_addr_cycles=%d"
1210                     "\npage_size=%d\noob_size=%d\npages_per_block=%d\n"
1211                     "blocks_per_lun=%d\nluns=%d\n\nprog_time=%d\n"
1212                     "erase_time=%d\nread_time=%d\n"
1213                     "error_ratio=%d\nwear_level=%d\nwrite_protect=%c\n"
1214                     "chip_width=%db\n", chip->num, chip->ctrl_num,
1215                     chip->device_id, chip->manufact_id,chip->device_model,
1216                     chip->manufacturer, chip->col_addr_cycles,
1217                     chip->row_addr_cycles, chip->page_size,
1218                     chip->oob_size, chip->pgs_per_blk, chip->blks_per_lun,
1219                     chip->luns,chip->prog_time, chip->erase_time,
1220                     chip->read_time, chip->error_ratio, chip->wear_level,
1221                     (chip->is_wp == 0) ? 'N':'Y', chip->width);
1222         } else {
1223                 printf("[Chip info]\n");
1224                 printf("\tnum=%d\n\tdevice_model=%s\n\tmanufacturer=%s\n"
1225                     "\tpage_size=%d\n\twrite_protect=%s\n",
1226                     chip->num, chip->device_model, chip->manufacturer,
1227                     chip->page_size, (chip->is_wp == 0) ? "NO":"YES");
1228         }
1229 }
1230
1231 static void
1232 printctrl(struct sim_ctrl *ctrl)
1233 {
1234         int i;
1235
1236         if (ctrl->created == 0) {
1237                 printf(MSG_NOCTRL "\n", ctrl->num);
1238                 return;
1239         }
1240         printf("\n[Controller info]\n");
1241         printf("\trunning: %s\n", ctrl->running ? "yes" : "no");
1242         printf("\tnum cs: %d\n", ctrl->num_cs);
1243         printf("\tecc: %d\n", ctrl->ecc);
1244         printf("\tlog_filename: %s\n", ctrl->filename);
1245         printf("\tecc_layout:");
1246         for (i = 0; i < MAX_ECC_BYTES; i++) {
1247                 if (ctrl->ecc_layout[i] == 0xffff)
1248                         break;
1249                 else
1250                         printf("%c%d", i%16 ? ' ' : '\n',
1251                             ctrl->ecc_layout[i]);
1252         }
1253         printf("\n");
1254 }
1255
1256 static int
1257 is_ctrl_running(int ctrl_no, int *running)
1258 {
1259         struct sim_ctrl ctrl;
1260         int err, fd;
1261
1262         ctrl.num = ctrl_no;
1263         if (opendev(&fd) != EX_OK)
1264                 return (EX_OSFILE);
1265
1266         err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrl);
1267         if (err) {
1268                 error(MSG_STATUSACQCTRL, ctrl_no);
1269                 close(fd);
1270                 return (err);
1271         }
1272         *running = ctrl.running;
1273         close(fd);
1274         return (0);
1275 }
1276
1277 static int
1278 is_ctrl_created(int ctrl_no, int *created)
1279 {
1280         struct sim_ctrl ctrl;
1281         int err, fd;
1282
1283         ctrl.num = ctrl_no;
1284
1285         if (opendev(&fd) != EX_OK)
1286                 return (EX_OSFILE);
1287
1288         err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrl);
1289         if (err) {
1290                 error("Could not acquire conf for ctrl#%d", ctrl_no);
1291                 close(fd);
1292                 return (err);
1293         }
1294         *created = ctrl.created;
1295         close(fd);
1296         return (0);
1297 }
1298
1299 static int
1300 is_chip_created(int ctrl_no, int chip_no, int *created)
1301 {
1302         struct sim_chip chip;
1303         int err, fd;
1304
1305         chip.ctrl_num = ctrl_no;
1306         chip.num = chip_no;
1307
1308         if (opendev(&fd) != EX_OK)
1309                 return (EX_OSFILE);
1310
1311         err = ioctl(fd, NANDSIM_STATUS_CHIP, &chip);
1312         if (err) {
1313                 error("Could not acquire conf for chip#%d", chip_no);
1314                 close(fd);
1315                 return (err);
1316         }
1317         *created = chip.created;
1318         close(fd);
1319         return (0);
1320 }
1321
1322 static int
1323 assert_chip_connected(int ctrl_no, int chip_no)
1324 {
1325         int created, running;
1326
1327         if (is_ctrl_created(ctrl_no, &created))
1328                 return (0);
1329
1330         if (!created) {
1331                 error(MSG_NOCTRL, ctrl_no);
1332                 return (0);
1333         }
1334
1335         if (is_chip_created(ctrl_no, chip_no, &created))
1336                 return (0);
1337
1338         if (!created) {
1339                 error(MSG_NOTCONFIGDCTRLCHIP, ctrl_no, chip_no);
1340                 return (0);
1341         }
1342
1343         if (is_ctrl_running(ctrl_no, &running))
1344                 return (0);
1345
1346         if (!running) {
1347                 error(MSG_NOTRUNNING, ctrl_no);
1348                 return (0);
1349         }
1350
1351         return (1);
1352 }
1353
1354 static int
1355 printstats(int ctrlno, int chipno, uint32_t pageno, int cdevd)
1356 {
1357         struct page_stat_io pstats;
1358         struct block_stat_io bstats;
1359         struct chip_param_io cparams;
1360         uint32_t blkidx;
1361         int err;
1362
1363         /* Gather information about chip */
1364         err = ioctl(cdevd, NAND_IO_GET_CHIP_PARAM, &cparams);
1365
1366         if (err) {
1367                 error("Could not acquire chip info for chip attached to cs#"
1368                     "%d, ctrl#%d", chipno, ctrlno);
1369                 return (EX_SOFTWARE);
1370         }
1371
1372         blkidx = (pageno / cparams.pages_per_block);
1373         bstats.block_num = blkidx;
1374
1375         err = ioctl(cdevd, NAND_IO_BLOCK_STAT, &bstats);
1376         if (err) {
1377                 error("Could not acquire block#%d statistics!", blkidx);
1378                 return (ENXIO);
1379         }
1380
1381         printf("Block #%d erased: %d\n", blkidx, bstats.block_erased);
1382         pstats.page_num = pageno;
1383
1384         err = ioctl(cdevd, NAND_IO_PAGE_STAT, &pstats);
1385         if (err) {
1386                 error("Could not acquire page statistics!");
1387                 return (ENXIO);
1388         }
1389
1390         debug("BLOCKIDX = %d PAGENO (REL. TO BLK) = %d\n", blkidx,
1391             pstats.page_num);
1392
1393         printf("Page#%d : reads:%d writes:%d \n\traw reads:%d raw writes:%d "
1394             "\n\tecc_succeeded:%d ecc_corrected:%d ecc_failed:%d\n",
1395             pstats.page_num, pstats.page_read, pstats.page_written,
1396             pstats.page_raw_read, pstats.page_raw_written,
1397             pstats.ecc_succeded, pstats.ecc_corrected, pstats.ecc_failed);
1398         return (0);
1399 }