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