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
92typedef int (commandfunc_t)(int , char **);
93
94static struct nandsim_command *getcommand(char *);
95static int parse_devstring(char *, int *, int *);
96static void printchip(struct sim_chip *, uint8_t);
97static void printctrl(struct sim_ctrl *);
98static int opendev(int *);
99static commandfunc_t cmdstatus;
100static commandfunc_t cmdconf;
101static commandfunc_t cmdstart;
102static commandfunc_t cmdstop;
103static commandfunc_t cmdmod;
104static commandfunc_t cmderror;
105static commandfunc_t cmdbb;
106static commandfunc_t cmdfreeze;
107static commandfunc_t cmdlog;
108static commandfunc_t cmdstats;
109static commandfunc_t cmddump;
110static commandfunc_t cmdrestore;
111static commandfunc_t cmddestroy;
112static commandfunc_t cmdhelp;
113static int checkusage(int, int, char **);
114static int is_chip_created(int, int, int *);
115static int is_ctrl_created(int, int *);
116static int is_ctrl_running(int, int *);
117static int assert_chip_connected(int , int);
118static int printstats(int, int, uint32_t, int);
119
120struct 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
127static 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 */
163static struct nandsim_command*
164getcommand(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 */
184static int
185parse_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
238static int
239opendev(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
251static int
252opencdev(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 */
268static int
269checkusage(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
280static int
281cmdstatus(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
338static int
339cmdconf(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
350static int
351cmdstart(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
387static int
388cmdstop(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
417static int
418cmdmod(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
518static int
519cmderror(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
563static int
564cmdbb(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
661static int
662cmdfreeze(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
717static int
718cmdlog(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
769static int
770cmdstats(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
803static int
804cmddump(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
931static int
932cmdrestore(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
1072static int
1073cmddestroy(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
1150int
1151main(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
1184static int
1185cmdhelp(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
1199static void
1200printchip(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
1231static void
1232printctrl(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
1256static int
1257is_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
1277static int
1278is_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
1299static int
1300is_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
1322static int
1323assert_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
1354static int
1355printstats(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}
1400