1235537Sgber/*-
2235537Sgber * Copyright (C) 2009-2012 Semihalf
3235537Sgber * All rights reserved.
4235537Sgber *
5235537Sgber * Redistribution and use in source and binary forms, with or without
6235537Sgber * modification, are permitted provided that the following conditions
7235537Sgber * are met:
8235537Sgber * 1. Redistributions of source code must retain the above copyright
9235537Sgber *    notice, this list of conditions and the following disclaimer.
10235537Sgber * 2. Redistributions in binary form must reproduce the above copyright
11235537Sgber *    notice, this list of conditions and the following disclaimer in the
12235537Sgber *    documentation and/or other materials provided with the distribution.
13235537Sgber *
14235537Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15235537Sgber * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16235537Sgber * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17235537Sgber * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
18235537Sgber * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19235537Sgber * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20235537Sgber * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21235537Sgber * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22235537Sgber * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23235537Sgber * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24235537Sgber * SUCH DAMAGE.
25235537Sgber */
26235537Sgber
27235537Sgber/*
28235537Sgber * Control application for the NAND simulator.
29235537Sgber */
30235537Sgber
31235537Sgber#include <sys/cdefs.h>
32235537Sgber__FBSDID("$FreeBSD$");
33235537Sgber
34235537Sgber#include <sys/errno.h>
35235537Sgber#include <sys/ioctl.h>
36235537Sgber#include <sys/mman.h>
37235537Sgber#include <sys/stat.h>
38235537Sgber#include <sys/types.h>
39235537Sgber
40235537Sgber#include <dev/nand/nandsim.h>
41235537Sgber#include <dev/nand/nand_dev.h>
42235537Sgber
43235537Sgber#include <ctype.h>
44235537Sgber#include <fcntl.h>
45235537Sgber#include <getopt.h>
46235537Sgber#include <stdio.h>
47235537Sgber#include <stdlib.h>
48235537Sgber#include <string.h>
49235537Sgber#include <stdarg.h>
50235537Sgber#include <unistd.h>
51235537Sgber#include <stdlib.h>
52235537Sgber#include <limits.h>
53235537Sgber#include <sysexits.h>
54235537Sgber
55235537Sgber#include "nandsim_cfgparse.h"
56235537Sgber
57235537Sgber#define SIMDEVICE	"/dev/nandsim.ioctl"
58235537Sgber
59235537Sgber#define error(fmt, args...) do { \
60235537Sgber    printf("ERROR: " fmt "\n", ##args); } while (0)
61235537Sgber
62235537Sgber#define warn(fmt, args...) do { \
63235537Sgber    printf("WARNING: " fmt "\n", ##args); } while (0)
64235537Sgber
65235537Sgber#define DEBUG
66235537Sgber#undef DEBUG
67235537Sgber
68235537Sgber#ifdef DEBUG
69235537Sgber#define debug(fmt, args...) do { \
70235537Sgber    printf("NANDSIM_CONF:" fmt "\n", ##args); } while (0)
71235537Sgber#else
72235537Sgber#define debug(fmt, args...) do {} while(0)
73235537Sgber#endif
74235537Sgber
75235537Sgber#define NANDSIM_RAM_LOG_SIZE 16384
76235537Sgber
77235537Sgber#define MSG_NOTRUNNING		"Controller#%d is not running.Please start" \
78235537Sgber    " it first."
79235537Sgber#define MSG_RUNNING		"Controller#%d is already running!"
80235537Sgber#define MSG_CTRLCHIPNEEDED	"You have to specify ctrl_no:cs_no pair!"
81235537Sgber#define MSG_STATUSACQCTRLCHIP	"Could not acquire status for ctrl#%d chip#%d"
82235537Sgber#define MSG_STATUSACQCTRL	"Could not acquire status for ctrl#%d"
83235537Sgber#define MSG_NOCHIP		"There is no such chip configured (chip#%d "\
84235537Sgber    "at ctrl#%d)!"
85235537Sgber
86235537Sgber#define MSG_NOCTRL		"Controller#%d is not configured!"
87235537Sgber#define MSG_NOTCONFIGDCTRLCHIP	"Chip connected to ctrl#%d at cs#%d " \
88235537Sgber    "is not configured."
89235537Sgber
90235537Sgbertypedef int (commandfunc_t)(int , char **);
91235537Sgber
92235537Sgberstatic struct nandsim_command *getcommand(char *);
93235537Sgberstatic int parse_devstring(char *, int *, int *);
94235537Sgberstatic void printchip(struct sim_chip *, uint8_t);
95235537Sgberstatic void printctrl(struct sim_ctrl *);
96235537Sgberstatic int opendev(int *);
97235537Sgberstatic commandfunc_t cmdstatus;
98235537Sgberstatic commandfunc_t cmdconf;
99235537Sgberstatic commandfunc_t cmdstart;
100235537Sgberstatic commandfunc_t cmdstop;
101235537Sgberstatic commandfunc_t cmdmod;
102235537Sgberstatic commandfunc_t cmderror;
103235537Sgberstatic commandfunc_t cmdbb;
104235537Sgberstatic commandfunc_t cmdfreeze;
105235537Sgberstatic commandfunc_t cmdlog;
106235537Sgberstatic commandfunc_t cmdstats;
107235537Sgberstatic commandfunc_t cmddump;
108235537Sgberstatic commandfunc_t cmdrestore;
109235537Sgberstatic commandfunc_t cmddestroy;
110235537Sgberstatic commandfunc_t cmdhelp;
111235537Sgberstatic int checkusage(int, int, char **);
112235537Sgberstatic int is_chip_created(int, int, int *);
113235537Sgberstatic int is_ctrl_created(int, int *);
114235537Sgberstatic int is_ctrl_running(int, int *);
115235537Sgberstatic int assert_chip_connected(int , int);
116235537Sgberstatic int printstats(int, int, uint32_t, int);
117235537Sgber
118235537Sgberstruct nandsim_command {
119235537Sgber	const char	*cmd_name;	/* Command name */
120235537Sgber	commandfunc_t	*commandfunc;	/* Ptr to command function */
121235537Sgber	uint8_t		req_argc;	/* Mandatory arguments count */
122235537Sgber	const char	*usagestring;	/* Usage string */
123235537Sgber};
124235537Sgber
125235537Sgberstatic struct nandsim_command commands[] = {
126235537Sgber	{"status", cmdstatus, 1,
127235537Sgber	    "status <ctl_no|--all|-a> [-v]\n" },
128235537Sgber	{"conf", cmdconf, 1,
129235537Sgber	    "conf <filename>\n" },
130235537Sgber	{"start", cmdstart, 1,
131235537Sgber	    "start <ctrl_no>\n" },
132235537Sgber	{"mod", cmdmod, 2,
133235537Sgber	    "mod [-l <loglevel>] | <ctl_no:cs_no> [-p <prog_time>]\n"
134235537Sgber	    "\t[-e <erase_time>] [-r <read_time>]\n"
135235537Sgber	    "\t[-E <error_ratio>] | [-h]\n" },
136235537Sgber	{"stop", cmdstop, 1,
137235537Sgber	    "stop <ctrl_no>\n" },
138235537Sgber	{"error", cmderror, 5,
139235537Sgber	    "error <ctrl_no:cs_no> <page_num> <column> <length> <pattern>\n" },
140235537Sgber	{"bb", cmdbb, 2,
141235537Sgber	    "bb <ctl_no:cs_no>  [blk_num1,blk_num2,..] [-U] [-L]\n" },
142235537Sgber	{"freeze", cmdfreeze, 1,
143235537Sgber	    "freeze [ctrl_no]\n" },
144235537Sgber	{"log", cmdlog, 1,
145235537Sgber	    "log <ctrl_no|--all|-a>\n" },
146235537Sgber	{"stats", cmdstats, 2,
147235537Sgber	    "stats <ctrl_no:cs_no> <pagenumber>\n" },
148235537Sgber	{"dump", cmddump, 2,
149235537Sgber	    "dump <ctrl_no:cs_no> <filename>\n" },
150235537Sgber	{"restore", cmdrestore, 2,
151235537Sgber	    "restore <ctrl_no:chip_no> <filename>\n" },
152235537Sgber	{"destroy", cmddestroy, 1,
153235537Sgber	    "destroy <ctrl_no[:cs_no]|--all|-a>\n" },
154235537Sgber	{"help", cmdhelp, 0,
155235537Sgber	    "help [-v]" },
156235537Sgber	{NULL, NULL, 0, NULL},
157235537Sgber};
158235537Sgber
159235537Sgber
160235537Sgber/* Parse command name, and start appropriate function */
161235537Sgberstatic struct nandsim_command*
162235537Sgbergetcommand(char *arg)
163235537Sgber{
164235537Sgber	struct nandsim_command *opts;
165235537Sgber
166235537Sgber	for (opts = commands; (opts != NULL) &&
167235537Sgber	    (opts->cmd_name != NULL); opts++) {
168235537Sgber		if (strcmp(opts->cmd_name, arg) == 0)
169235537Sgber			return (opts);
170235537Sgber	}
171235537Sgber	return (NULL);
172235537Sgber}
173235537Sgber
174235537Sgber/*
175235537Sgber * Parse given string in format <ctrl_no>:<cs_no>, if possible -- set
176235537Sgber * ctrl and/or cs, and return 0 (success) or 1 (in case of error).
177235537Sgber *
178235537Sgber * ctrl == 0xff && chip == 0xff  : '--all' flag specified
179235537Sgber * ctrl != 0xff && chip != 0xff  : both ctrl & chip were specified
180235537Sgber * ctrl != 0xff && chip == 0xff  : only ctrl was specified
181235537Sgber */
182235537Sgberstatic int
183235537Sgberparse_devstring(char *str, int *ctrl, int *cs)
184235537Sgber{
185235537Sgber	char *tmpstr;
186235537Sgber	unsigned int num = 0;
187235537Sgber
188235537Sgber	/* Ignore white spaces at the beginning */
189235537Sgber	while (isspace(*str) && (*str != '\0'))
190235537Sgber		str++;
191235537Sgber
192235537Sgber	*ctrl = 0xff;
193235537Sgber	*cs = 0xff;
194235537Sgber	if (strcmp(str, "--all") == 0 ||
195235537Sgber	    strcmp(str, "-a") == 0) {
196235537Sgber		/* If --all or -a is specified, ctl==chip==0xff */
197235537Sgber		debug("CTRL=%d CHIP=%d\n", *ctrl, *cs);
198235537Sgber		return (0);
199235537Sgber	}
200235537Sgber	/* Separate token and try to convert it to int */
201235537Sgber	tmpstr = (char *)strtok(str, ":");
202235537Sgber	if ((tmpstr != NULL) && (*tmpstr != '\0')) {
203235537Sgber		if (convert_arguint(tmpstr, &num) != 0)
204235537Sgber			return (1);
205235537Sgber
206235537Sgber		if (num > MAX_SIM_DEV - 1) {
207235537Sgber			error("Invalid ctrl_no supplied: %s. Valid ctrl_no "
208235537Sgber			    "value must lie between 0 and 3!", tmpstr);
209235537Sgber			return (1);
210235537Sgber		}
211235537Sgber
212235537Sgber		*ctrl = num;
213235537Sgber		tmpstr = (char *)strtok(NULL, ":");
214235537Sgber
215235537Sgber		if ((tmpstr != NULL) && (*tmpstr != '\0')) {
216235537Sgber			if (convert_arguint(tmpstr, &num) != 0)
217235537Sgber				return (1);
218235537Sgber
219235537Sgber			/* Check if chip_no is valid */
220235537Sgber			if (num > MAX_CTRL_CS - 1) {
221235537Sgber				error("Invalid chip_no supplied: %s. Valid "
222235537Sgber				    "chip_no value must lie between 0 and 3!",
223235537Sgber				    tmpstr);
224235537Sgber				return (1);
225235537Sgber			}
226235537Sgber			*cs = num;
227235537Sgber		}
228235537Sgber	} else
229235537Sgber		/* Empty devstring supplied */
230235537Sgber		return (1);
231235537Sgber
232235537Sgber	debug("CTRL=%d CHIP=%d\n", *ctrl, *cs);
233235537Sgber	return (0);
234235537Sgber}
235235537Sgber
236235537Sgberstatic int
237235537Sgberopendev(int *fd)
238235537Sgber{
239235537Sgber
240235537Sgber	*fd = open(SIMDEVICE, O_RDWR);
241235537Sgber	if (*fd == -1) {
242235537Sgber		error("Could not open simulator device file (%s)!",
243235537Sgber		    SIMDEVICE);
244235537Sgber		return (EX_OSFILE);
245235537Sgber	}
246235537Sgber	return (EX_OK);
247235537Sgber}
248235537Sgber
249235537Sgberstatic int
250235537Sgberopencdev(int *cdevd, int ctrl, int chip)
251235537Sgber{
252235537Sgber	char fname[255];
253235537Sgber
254235537Sgber	sprintf(fname, "/dev/nandsim%d.%d", ctrl, chip);
255235537Sgber	*cdevd = open(fname, O_RDWR);
256235537Sgber	if (*cdevd == -1)
257235537Sgber		return (EX_NOINPUT);
258235537Sgber
259235537Sgber	return (EX_OK);
260235537Sgber}
261235537Sgber
262235537Sgber/*
263235537Sgber * Check if given arguments count match requirements. If no, or
264235537Sgber * --help (-h) flag is specified -- return 1 (print usage)
265235537Sgber */
266235537Sgberstatic int
267235537Sgbercheckusage(int gargc, int argsreqd, char **gargv)
268235537Sgber{
269235537Sgber
270235537Sgber	if (gargc < argsreqd + 2 || (gargc >= (argsreqd + 2) &&
271235537Sgber	    (strcmp(gargv[1], "--help") == 0 ||
272235537Sgber	    strcmp(gargv[1], "-h") == 0)))
273235537Sgber		return (1);
274235537Sgber
275235537Sgber	return (0);
276235537Sgber}
277235537Sgber
278235537Sgberstatic int
279235537Sgbercmdstatus(int gargc, char **gargv)
280235537Sgber{
281235537Sgber	int chip = 0, ctl = 0, err = 0, fd, idx, idx2, start, stop;
282235537Sgber	uint8_t verbose = 0;
283235537Sgber	struct sim_ctrl ctrlconf;
284235537Sgber	struct sim_chip chipconf;
285235537Sgber
286235537Sgber	err = parse_devstring(gargv[2], &ctl, &chip);
287235537Sgber	if (err) {
288235537Sgber		return (EX_USAGE);
289235537Sgber	} else if (ctl == 0xff) {
290235537Sgber		/* Every controller */
291235537Sgber		start = 0;
292235537Sgber		stop = MAX_SIM_DEV-1;
293235537Sgber	} else {
294235537Sgber		/* Specified controller only */
295235537Sgber		start = ctl;
296235537Sgber		stop = ctl;
297235537Sgber	}
298235537Sgber
299235537Sgber	if (opendev(&fd) != EX_OK)
300235537Sgber		return (EX_OSFILE);
301235537Sgber
302235537Sgber	for (idx = 0; idx < gargc; idx ++)
303235537Sgber		if (strcmp(gargv[idx], "-v") == 0 ||
304235537Sgber		    strcmp(gargv[idx], "--verbose") == 0)
305235537Sgber			verbose = 1;
306235537Sgber
307235537Sgber	for (idx = start; idx <= stop; idx++) {
308235537Sgber		ctrlconf.num = idx;
309235537Sgber		err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrlconf);
310235537Sgber		if (err) {
311235537Sgber			err = EX_SOFTWARE;
312235537Sgber			error(MSG_STATUSACQCTRL, idx);
313235537Sgber			continue;
314235537Sgber		}
315235537Sgber
316235537Sgber		printctrl(&ctrlconf);
317235537Sgber
318235537Sgber		for (idx2 = 0; idx2 < MAX_CTRL_CS; idx2++) {
319235537Sgber			chipconf.num = idx2;
320235537Sgber			chipconf.ctrl_num = idx;
321235537Sgber
322235537Sgber			err = ioctl(fd, NANDSIM_STATUS_CHIP, &chipconf);
323235537Sgber			if (err) {
324235537Sgber				err = EX_SOFTWARE;
325235537Sgber				error(MSG_STATUSACQCTRL, idx);
326235537Sgber				continue;
327235537Sgber			}
328235537Sgber
329235537Sgber			printchip(&chipconf, verbose);
330235537Sgber		}
331235537Sgber	}
332235537Sgber	close(fd);
333235537Sgber	return (err);
334235537Sgber}
335235537Sgber
336235537Sgberstatic int
337235537Sgbercmdconf(int gargc __unused, char **gargv)
338235537Sgber{
339235537Sgber	int err;
340235537Sgber
341235537Sgber	err = parse_config(gargv[2], SIMDEVICE);
342235537Sgber	if (err)
343235537Sgber		return (EX_DATAERR);
344235537Sgber
345235537Sgber	return (EX_OK);
346235537Sgber}
347235537Sgber
348235537Sgberstatic int
349235537Sgbercmdstart(int gargc __unused, char **gargv)
350235537Sgber{
351235537Sgber	int chip = 0, ctl = 0, err = 0, fd, running, state;
352235537Sgber
353235537Sgber	err = parse_devstring(gargv[2], &ctl, &chip);
354235537Sgber	if (err)
355235537Sgber		return (EX_USAGE);
356235537Sgber
357235537Sgber	err = is_ctrl_created(ctl, &state);
358235537Sgber	if (err) {
359235537Sgber		return (EX_SOFTWARE);
360235537Sgber	} else if (state == 0) {
361235537Sgber		error(MSG_NOCTRL, ctl);
362235537Sgber		return (EX_SOFTWARE);
363235537Sgber	}
364235537Sgber
365235537Sgber	err = is_ctrl_running(ctl, &running);
366235537Sgber	if (err)
367235537Sgber		return (EX_SOFTWARE);
368235537Sgber
369235537Sgber	if (running) {
370235537Sgber		warn(MSG_RUNNING, ctl);
371235537Sgber	} else {
372235537Sgber		if (opendev(&fd) != EX_OK)
373235537Sgber			return (EX_OSFILE);
374235537Sgber
375235537Sgber		err = ioctl(fd, NANDSIM_START_CTRL, &ctl);
376235537Sgber		close(fd);
377235537Sgber		if (err) {
378235537Sgber			error("Cannot start controller#%d", ctl);
379235537Sgber			err = EX_SOFTWARE;
380235537Sgber		}
381235537Sgber	}
382235537Sgber	return (err);
383235537Sgber}
384235537Sgber
385235537Sgberstatic int
386235537Sgbercmdstop(int gargc __unused, char **gargv)
387235537Sgber{
388235537Sgber	int chip = 0, ctl = 0, err = 0, fd, running;
389235537Sgber
390235537Sgber	err = parse_devstring(gargv[2], &ctl, &chip);
391235537Sgber	if (err)
392235537Sgber		return (EX_USAGE);
393235537Sgber
394235537Sgber	err = is_ctrl_running(ctl, &running);
395235537Sgber	if (err)
396235537Sgber		return (EX_SOFTWARE);
397235537Sgber
398235537Sgber	if (!running) {
399235537Sgber		error(MSG_NOTRUNNING, ctl);
400235537Sgber	} else {
401235537Sgber		if (opendev(&fd) != EX_OK)
402235537Sgber			return (EX_OSFILE);
403235537Sgber
404235537Sgber		err = ioctl(fd, NANDSIM_STOP_CTRL, &ctl);
405235537Sgber		close(fd);
406235537Sgber		if (err) {
407235537Sgber			error("Cannot stop controller#%d", ctl);
408235537Sgber			err = EX_SOFTWARE;
409235537Sgber		}
410235537Sgber	}
411235537Sgber
412235537Sgber	return (err);
413235537Sgber}
414235537Sgber
415235537Sgberstatic int
416235537Sgbercmdmod(int gargc __unused, char **gargv)
417235537Sgber{
418235537Sgber	int chip, ctl, err = 0, fd = -1, i;
419235537Sgber	struct sim_mod mods;
420235537Sgber
421235537Sgber	if (gargc >= 4) {
422235537Sgber		if (strcmp(gargv[2], "--loglevel") == 0 || strcmp(gargv[2],
423235537Sgber		    "-l") == 0) {
424293290Sbdrewery			/* Set loglevel (ctrl:chip pair independent) */
425235537Sgber			mods.field = SIM_MOD_LOG_LEVEL;
426235537Sgber
427235537Sgber			if (convert_arguint(gargv[3], &mods.new_value) != 0)
428235537Sgber				return (EX_SOFTWARE);
429235537Sgber
430235537Sgber			if (opendev(&fd) != EX_OK)
431235537Sgber				return (EX_OSFILE);
432235537Sgber
433235537Sgber			err = ioctl(fd, NANDSIM_MODIFY, &mods);
434235537Sgber			if (err) {
435235537Sgber				error("simulator parameter %s could not be "
436235537Sgber				    "modified !", gargv[3]);
437235537Sgber				close(fd);
438235537Sgber				return (EX_SOFTWARE);
439235537Sgber			}
440235537Sgber
441235537Sgber			debug("request : loglevel = %d\n", mods.new_value);
442235537Sgber			close(fd);
443235537Sgber			return (EX_OK);
444235537Sgber		}
445235537Sgber	}
446235537Sgber
447235537Sgber	err = parse_devstring(gargv[2], &ctl, &chip);
448235537Sgber	if (err)
449235537Sgber		return (EX_USAGE);
450235537Sgber
451235537Sgber	else if (chip == 0xff) {
452235537Sgber		error(MSG_CTRLCHIPNEEDED);
453235537Sgber		return (EX_USAGE);
454235537Sgber	}
455235537Sgber
456235537Sgber	if (!assert_chip_connected(ctl, chip))
457235537Sgber		return (EX_SOFTWARE);
458235537Sgber
459235537Sgber	if (opendev(&fd) != EX_OK)
460235537Sgber		return (EX_OSFILE);
461235537Sgber
462235537Sgber	/* Find out which flags were passed */
463235537Sgber	for (i = 3; i < gargc; i++) {
464235537Sgber
465235537Sgber		if (convert_arguint(gargv[i + 1], &mods.new_value) != 0)
466235537Sgber			continue;
467235537Sgber
468235537Sgber		if (strcmp(gargv[i], "--prog-time") == 0 ||
469235537Sgber		    strcmp(gargv[i], "-p") == 0) {
470235537Sgber
471235537Sgber			mods.field = SIM_MOD_PROG_TIME;
472235537Sgber			debug("request : progtime = %d\n", mods.new_value);
473235537Sgber
474235537Sgber		} else if (strcmp(gargv[i], "--erase-time") == 0 ||
475235537Sgber		    strcmp(gargv[i], "-e") == 0) {
476235537Sgber
477235537Sgber			mods.field = SIM_MOD_ERASE_TIME;
478235537Sgber			debug("request : eraseime = %d\n", mods.new_value);
479235537Sgber
480235537Sgber		} else if (strcmp(gargv[i], "--read-time") == 0 ||
481235537Sgber		    strcmp(gargv[i], "-r") == 0) {
482235537Sgber
483235537Sgber			mods.field = SIM_MOD_READ_TIME;
484235537Sgber			debug("request : read_time = %d\n", mods.new_value);
485235537Sgber
486235537Sgber		} else if (strcmp(gargv[i], "--error-ratio") == 0 ||
487235537Sgber		    strcmp(gargv[i], "-E") == 0) {
488235537Sgber
489235537Sgber			mods.field = SIM_MOD_ERROR_RATIO;
490235537Sgber			debug("request : error_ratio = %d\n", mods.new_value);
491235537Sgber
492235537Sgber		} else {
493235537Sgber			/* Flag not recognized, or nothing specified. */
494235537Sgber			error("Unrecognized flag:%s\n", gargv[i]);
495235537Sgber			if (fd >= 0)
496235537Sgber				close(fd);
497235537Sgber			return (EX_USAGE);
498235537Sgber		}
499235537Sgber
500235537Sgber		mods.chip_num = chip;
501235537Sgber		mods.ctrl_num = ctl;
502235537Sgber
503235537Sgber		/* Call appropriate ioctl */
504235537Sgber		err = ioctl(fd, NANDSIM_MODIFY, &mods);
505235537Sgber		if (err) {
506235537Sgber			error("simulator parameter %s could not be modified! ",
507235537Sgber			    gargv[i]);
508235537Sgber			continue;
509235537Sgber		}
510235537Sgber		i++;
511235537Sgber	}
512235537Sgber	close(fd);
513235537Sgber	return (EX_OK);
514235537Sgber}
515235537Sgber
516235537Sgberstatic int
517235537Sgbercmderror(int gargc __unused, char **gargv)
518235537Sgber{
519235537Sgber	uint32_t page, column, len, pattern;
520235537Sgber	int chip = 0, ctl = 0, err = 0, fd;
521235537Sgber	struct sim_error sim_err;
522235537Sgber
523235537Sgber	err = parse_devstring(gargv[2], &ctl, &chip);
524235537Sgber	if (err)
525235537Sgber		return (EX_USAGE);
526235537Sgber
527235537Sgber	if (chip == 0xff) {
528235537Sgber		error(MSG_CTRLCHIPNEEDED);
529235537Sgber		return (EX_USAGE);
530235537Sgber	}
531235537Sgber
532235537Sgber	if (convert_arguint(gargv[3], &page) ||
533235537Sgber	    convert_arguint(gargv[4], &column) ||
534235537Sgber	    convert_arguint(gargv[5], &len) ||
535235537Sgber	    convert_arguint(gargv[6], &pattern))
536235537Sgber		return (EX_SOFTWARE);
537235537Sgber
538235537Sgber	if (!assert_chip_connected(ctl, chip))
539235537Sgber		return (EX_SOFTWARE);
540235537Sgber
541235537Sgber	sim_err.page_num = page;
542235537Sgber	sim_err.column = column;
543235537Sgber	sim_err.len = len;
544235537Sgber	sim_err.pattern = pattern;
545235537Sgber	sim_err.ctrl_num = ctl;
546235537Sgber	sim_err.chip_num = chip;
547235537Sgber
548235537Sgber	if (opendev(&fd) != EX_OK)
549235537Sgber		return (EX_OSFILE);
550235537Sgber
551235537Sgber	err = ioctl(fd, NANDSIM_INJECT_ERROR, &sim_err);
552235537Sgber
553235537Sgber	close(fd);
554235537Sgber	if (err) {
555235537Sgber		error("Could not inject error !");
556235537Sgber		return (EX_SOFTWARE);
557235537Sgber	}
558235537Sgber	return (EX_OK);
559235537Sgber}
560235537Sgber
561235537Sgberstatic int
562235537Sgbercmdbb(int gargc, char **gargv)
563235537Sgber{
564235537Sgber	struct sim_block_state bs;
565235537Sgber	struct chip_param_io cparams;
566235537Sgber	uint32_t blkidx;
567235537Sgber	int c, cdevd, chip = 0, ctl = 0, err = 0, fd, idx;
568235537Sgber	uint8_t flagL = 0, flagU = 0;
569235537Sgber	int *badblocks = NULL;
570235537Sgber
571235537Sgber	/* Check for --list/-L or --unmark/-U flags */
572235537Sgber	for (idx = 3; idx < gargc; idx++) {
573235537Sgber		if (strcmp(gargv[idx], "--list") == 0 ||
574235537Sgber		    strcmp(gargv[idx], "-L") == 0)
575235537Sgber			flagL = idx;
576235537Sgber		if (strcmp(gargv[idx], "--unmark") == 0 ||
577235537Sgber		    strcmp(gargv[idx], "-U") == 0)
578235537Sgber			flagU = idx;
579235537Sgber	}
580235537Sgber
581235537Sgber	if (flagL == 2 || flagU == 2 || flagU == 3)
582235537Sgber		return (EX_USAGE);
583235537Sgber
584235537Sgber	err = parse_devstring(gargv[2], &ctl, &chip);
585235537Sgber	if (err) {
586235537Sgber		return (EX_USAGE);
587235537Sgber	}
588235537Sgber	if (chip == 0xff || ctl == 0xff) {
589235537Sgber		error(MSG_CTRLCHIPNEEDED);
590235537Sgber		return (EX_USAGE);
591235537Sgber	}
592235537Sgber
593235537Sgber	bs.ctrl_num = ctl;
594235537Sgber	bs.chip_num = chip;
595235537Sgber
596235537Sgber	if (!assert_chip_connected(ctl, chip))
597235537Sgber		return (EX_SOFTWARE);
598235537Sgber
599235537Sgber	if (opencdev(&cdevd, ctl, chip) != EX_OK)
600235537Sgber		return (EX_OSFILE);
601235537Sgber
602235537Sgber	err = ioctl(cdevd, NAND_IO_GET_CHIP_PARAM, &cparams);
603235537Sgber	if (err)
604235537Sgber		return (EX_SOFTWARE);
605235537Sgber
606235537Sgber	close(cdevd);
607235537Sgber
608235537Sgber	bs.ctrl_num = ctl;
609235537Sgber	bs.chip_num = chip;
610235537Sgber
611235537Sgber	if (opendev(&fd) != EX_OK)
612235537Sgber		return (EX_OSFILE);
613235537Sgber
614235537Sgber	if (flagL != 3) {
615235537Sgber		/*
616235537Sgber		 * Flag -L was specified either after blocklist or was not
617235537Sgber		 * specified at all.
618235537Sgber		 */
619235537Sgber		c = parse_intarray(gargv[3], &badblocks);
620235537Sgber
621235537Sgber		for (idx = 0; idx < c; idx++) {
622235537Sgber			bs.block_num = badblocks[idx];
623235537Sgber			/* Do not change wearout */
624235537Sgber			bs.wearout = -1;
625235537Sgber			bs.state = (flagU == 0) ? NANDSIM_BAD_BLOCK :
626235537Sgber			    NANDSIM_GOOD_BLOCK;
627235537Sgber
628235537Sgber			err = ioctl(fd, NANDSIM_SET_BLOCK_STATE, &bs);
629235537Sgber			if (err) {
630235537Sgber				error("Could not set bad block(%d) for "
631235537Sgber				    "controller (%d)!",
632235537Sgber				    badblocks[idx], ctl);
633235537Sgber				err = EX_SOFTWARE;
634235537Sgber				break;
635235537Sgber			}
636235537Sgber		}
637235537Sgber	}
638235537Sgber	if (flagL != 0) {
639235537Sgber		/* If flag -L was specified (anywhere) */
640235537Sgber		for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
641235537Sgber			bs.block_num = blkidx;
642235537Sgber			/* Do not change the wearout */
643235537Sgber			bs.wearout = -1;
644235537Sgber			err = ioctl(fd, NANDSIM_GET_BLOCK_STATE, &bs);
645235537Sgber			if (err) {
646235537Sgber				error("Could not acquire block state");
647235537Sgber				err = EX_SOFTWARE;
648235537Sgber				continue;
649235537Sgber			}
650235537Sgber			printf("Block#%d: wear count: %d %s\n", blkidx,
651235537Sgber			    bs.wearout,
652235537Sgber			    (bs.state == NANDSIM_BAD_BLOCK) ? "BAD":"GOOD");
653235537Sgber		}
654235537Sgber	}
655235537Sgber	close(fd);
656235537Sgber	return (err);
657235537Sgber}
658235537Sgber
659235537Sgberstatic int
660235537Sgbercmdfreeze(int gargc __unused, char **gargv)
661235537Sgber{
662235537Sgber	int chip = 0, ctl = 0, err = 0, fd, i, start = 0, state, stop = 0;
663235537Sgber	struct sim_ctrl_chip ctrlchip;
664235537Sgber
665235537Sgber	err = parse_devstring(gargv[2], &ctl, &chip);
666235537Sgber	if (err)
667235537Sgber		return (EX_USAGE);
668235537Sgber
669235537Sgber	if (ctl == 0xff) {
670235537Sgber		error("You have to specify at least controller number");
671235537Sgber		return (EX_USAGE);
672235537Sgber	}
673235537Sgber
674235537Sgber	if (ctl != 0xff && chip == 0xff) {
675235537Sgber		start = 0;
676235537Sgber		stop = MAX_CTRL_CS - 1;
677235537Sgber	} else {
678235537Sgber		start = chip;
679235537Sgber		stop = chip;
680235537Sgber	}
681235537Sgber
682235537Sgber	ctrlchip.ctrl_num = ctl;
683235537Sgber
684235537Sgber	err = is_ctrl_running(ctl, &state);
685235537Sgber	if (err)
686235537Sgber		return (EX_SOFTWARE);
687235537Sgber	if (state == 0) {
688235537Sgber		error(MSG_NOTRUNNING, ctl);
689235537Sgber		return (EX_SOFTWARE);
690235537Sgber	}
691235537Sgber
692235537Sgber	if (opendev(&fd) != EX_OK)
693235537Sgber		return (EX_OSFILE);
694235537Sgber
695235537Sgber	for (i = start; i <= stop; i++) {
696235537Sgber		err = is_chip_created(ctl, i, &state);
697235537Sgber		if (err)
698235537Sgber			return (EX_SOFTWARE);
699235537Sgber		else if (state == 0) {
700235537Sgber			continue;
701235537Sgber		}
702235537Sgber
703235537Sgber		ctrlchip.chip_num = i;
704235537Sgber		err = ioctl(fd, NANDSIM_FREEZE, &ctrlchip);
705235537Sgber		if (err) {
706235537Sgber			error("Could not freeze ctrl#%d chip#%d", ctl, i);
707235537Sgber			close(fd);
708235537Sgber			return (EX_SOFTWARE);
709235537Sgber		}
710235537Sgber	}
711235537Sgber	close(fd);
712235537Sgber	return (EX_OK);
713235537Sgber}
714235537Sgber
715235537Sgberstatic int
716235537Sgbercmdlog(int gargc __unused, char **gargv)
717235537Sgber{
718235537Sgber	struct sim_log log;
719235537Sgber	int chip = 0, ctl = 0, err = 0, fd, idx, start = 0, stop = 0;
720235537Sgber	char *logbuf;
721235537Sgber
722235537Sgber	err = parse_devstring(gargv[2], &ctl, &chip);
723235537Sgber	if (err)
724235537Sgber		return (EX_USAGE);
725235537Sgber
726235537Sgber	logbuf = (char *)malloc(sizeof(char) * NANDSIM_RAM_LOG_SIZE);
727235537Sgber	if (logbuf == NULL) {
728235537Sgber		error("Not enough memory to create log buffer");
729235537Sgber		return (EX_SOFTWARE);
730235537Sgber	}
731235537Sgber
732235537Sgber	memset(logbuf, 0, NANDSIM_RAM_LOG_SIZE);
733235537Sgber	log.log = logbuf;
734235537Sgber	log.len = NANDSIM_RAM_LOG_SIZE;
735235537Sgber
736235537Sgber	if (ctl == 0xff) {
737235537Sgber		start = 0;
738235537Sgber		stop = MAX_SIM_DEV-1;
739235537Sgber	} else {
740235537Sgber		start = ctl;
741235537Sgber		stop = ctl;
742235537Sgber	}
743235537Sgber
744235537Sgber	if (opendev(&fd) != EX_OK) {
745235537Sgber		free(logbuf);
746235537Sgber		return (EX_OSFILE);
747235537Sgber	}
748235537Sgber
749235537Sgber	/* Print logs for selected controller(s) */
750235537Sgber	for (idx = start; idx <= stop; idx++) {
751235537Sgber		log.ctrl_num = idx;
752235537Sgber
753235537Sgber		err = ioctl(fd, NANDSIM_PRINT_LOG, &log);
754235537Sgber		if (err) {
755235537Sgber			error("Could not get log for controller %d!", idx);
756235537Sgber			continue;
757235537Sgber		}
758235537Sgber
759235537Sgber		printf("Logs for controller#%d:\n%s\n", idx, logbuf);
760235537Sgber	}
761235537Sgber
762235537Sgber	free(logbuf);
763235537Sgber	close(fd);
764235537Sgber	return (EX_OK);
765235537Sgber}
766235537Sgber
767235537Sgberstatic int
768235537Sgbercmdstats(int gargc __unused, char **gargv)
769235537Sgber{
770235537Sgber	int cdevd, chip = 0, ctl = 0, err = 0;
771235537Sgber	uint32_t pageno = 0;
772235537Sgber
773235537Sgber	err = parse_devstring(gargv[2], &ctl, &chip);
774235537Sgber
775235537Sgber	if (err)
776235537Sgber		return (EX_USAGE);
777235537Sgber
778235537Sgber	if (chip == 0xff) {
779235537Sgber		error(MSG_CTRLCHIPNEEDED);
780235537Sgber		return (EX_USAGE);
781235537Sgber	}
782235537Sgber
783235537Sgber	if (convert_arguint(gargv[3], &pageno) != 0)
784235537Sgber		return (EX_USAGE);
785235537Sgber
786235537Sgber	if (!assert_chip_connected(ctl, chip))
787235537Sgber		return (EX_SOFTWARE);
788235537Sgber
789235537Sgber	if (opencdev(&cdevd, ctl, chip) != EX_OK)
790235537Sgber		return (EX_OSFILE);
791235537Sgber
792235537Sgber	err = printstats(ctl, chip, pageno, cdevd);
793235537Sgber	if (err) {
794235537Sgber		close(cdevd);
795235537Sgber		return (EX_SOFTWARE);
796235537Sgber	}
797235537Sgber	close(cdevd);
798235537Sgber	return (EX_OK);
799235537Sgber}
800235537Sgber
801235537Sgberstatic int
802235537Sgbercmddump(int gargc __unused, char **gargv)
803235537Sgber{
804235537Sgber	struct sim_dump dump;
805235537Sgber	struct sim_block_state bs;
806235537Sgber	struct chip_param_io cparams;
807235537Sgber	int chip = 0, ctl = 0, err = EX_OK, fd, dumpfd;
808235537Sgber	uint32_t blkidx, bwritten = 0, totalwritten = 0;
809235537Sgber	void *buf;
810235537Sgber
811235537Sgber	err = parse_devstring(gargv[2], &ctl, &chip);
812235537Sgber	if (err)
813235537Sgber		return (EX_USAGE);
814235537Sgber
815235537Sgber	if (chip == 0xff || ctl == 0xff) {
816235537Sgber		error(MSG_CTRLCHIPNEEDED);
817235537Sgber		return (EX_USAGE);
818235537Sgber	}
819235537Sgber
820235537Sgber	if (!assert_chip_connected(ctl, chip))
821235537Sgber		return (EX_SOFTWARE);
822235537Sgber
823235537Sgber	if (opencdev(&fd, ctl, chip) != EX_OK)
824235537Sgber		return (EX_OSFILE);
825235537Sgber
826235537Sgber	err = ioctl(fd, NAND_IO_GET_CHIP_PARAM, &cparams);
827235537Sgber	if (err) {
828235537Sgber		error("Cannot get parameters for chip %d:%d", ctl, chip);
829235537Sgber		close(fd);
830235537Sgber		return (EX_SOFTWARE);
831235537Sgber	}
832235537Sgber	close(fd);
833235537Sgber
834235537Sgber	dump.ctrl_num = ctl;
835235537Sgber	dump.chip_num = chip;
836235537Sgber
837235537Sgber	dump.len = cparams.pages_per_block * (cparams.page_size +
838235537Sgber	    cparams.oob_size);
839235537Sgber
840235537Sgber	buf = malloc(dump.len);
841235537Sgber	if (buf == NULL) {
842235537Sgber		error("Could not allocate memory!");
843235537Sgber		return (EX_SOFTWARE);
844235537Sgber	}
845235537Sgber	dump.data = buf;
846235537Sgber
847235537Sgber	errno = 0;
848235537Sgber	dumpfd = open(gargv[3], O_WRONLY | O_CREAT, 0666);
849235537Sgber	if (dumpfd == -1) {
850235537Sgber		error("Cannot create dump file.");
851235537Sgber		free(buf);
852235537Sgber		return (EX_SOFTWARE);
853235537Sgber	}
854235537Sgber
855235537Sgber	if (opendev(&fd)) {
856235537Sgber		close(dumpfd);
857235537Sgber		free(buf);
858235537Sgber		return (EX_SOFTWARE);
859235537Sgber	}
860235537Sgber
861235537Sgber	bs.ctrl_num = ctl;
862235537Sgber	bs.chip_num = chip;
863235537Sgber
864235537Sgber	/* First uint32_t in file shall contain block count */
865235537Sgber	if (write(dumpfd, &cparams, sizeof(cparams)) < (int)sizeof(cparams)) {
866235537Sgber		error("Error writing to dumpfile!");
867235537Sgber		close(fd);
868235537Sgber		close(dumpfd);
869235537Sgber		free(buf);
870235537Sgber		return (EX_SOFTWARE);
871235537Sgber	}
872235537Sgber
873235537Sgber	/*
874235537Sgber	 * First loop acquires blocks states and writes them to
875235537Sgber	 * the dump file.
876235537Sgber	 */
877235537Sgber	for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
878235537Sgber		bs.block_num = blkidx;
879235537Sgber		err = ioctl(fd, NANDSIM_GET_BLOCK_STATE, &bs);
880235537Sgber		if (err) {
881235537Sgber			error("Could not get bad block(%d) for "
882235537Sgber			    "controller (%d)!", blkidx, ctl);
883235537Sgber			close(fd);
884235537Sgber			close(dumpfd);
885235537Sgber			free(buf);
886235537Sgber			return (EX_SOFTWARE);
887235537Sgber		}
888235537Sgber
889235537Sgber		bwritten = write(dumpfd, &bs, sizeof(bs));
890235537Sgber		if (bwritten != sizeof(bs)) {
891235537Sgber			error("Error writing to dumpfile");
892235537Sgber			close(fd);
893235537Sgber			close(dumpfd);
894235537Sgber			free(buf);
895235537Sgber			return (EX_SOFTWARE);
896235537Sgber		}
897235537Sgber	}
898235537Sgber
899235537Sgber	/* Second loop dumps the data */
900235537Sgber	for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
901235537Sgber		debug("Block#%d...", blkidx);
902235537Sgber		dump.block_num = blkidx;
903235537Sgber
904235537Sgber		err = ioctl(fd, NANDSIM_DUMP, &dump);
905235537Sgber		if (err) {
906235537Sgber			error("Could not dump ctrl#%d chip#%d "
907235537Sgber			    "block#%d", ctl, chip, blkidx);
908235537Sgber			err = EX_SOFTWARE;
909235537Sgber			break;
910235537Sgber		}
911235537Sgber
912235537Sgber		bwritten = write(dumpfd, dump.data, dump.len);
913235537Sgber		if (bwritten != dump.len) {
914235537Sgber			error("Error writing to dumpfile");
915235537Sgber			err = EX_SOFTWARE;
916235537Sgber			break;
917235537Sgber		}
918235537Sgber		debug("OK!\n");
919235537Sgber		totalwritten += bwritten;
920235537Sgber	}
921235537Sgber	printf("%d out of %d B written.\n", totalwritten, dump.len * blkidx);
922235537Sgber
923235537Sgber	close(fd);
924235537Sgber	close(dumpfd);
925235537Sgber	free(buf);
926235537Sgber	return (err);
927235537Sgber}
928235537Sgber
929235537Sgberstatic int
930235537Sgbercmdrestore(int gargc __unused, char **gargv)
931235537Sgber{
932235537Sgber	struct sim_dump dump;
933235537Sgber	struct sim_block_state bs;
934235537Sgber	struct stat filestat;
935235537Sgber	int chip = 0, ctl = 0, err = 0, fd, dumpfd = -1;
936235537Sgber	uint32_t blkidx, blksz, fsize = 0, expfilesz;
937235537Sgber	void *buf;
938235537Sgber	struct chip_param_io cparams, dumpcparams;
939235537Sgber
940235537Sgber	err = parse_devstring(gargv[2], &ctl, &chip);
941235537Sgber	if (err)
942235537Sgber		return (EX_USAGE);
943235537Sgber	else if (ctl == 0xff) {
944235537Sgber		error(MSG_CTRLCHIPNEEDED);
945235537Sgber		return (EX_USAGE);
946235537Sgber	}
947235537Sgber
948235537Sgber	if (!assert_chip_connected(ctl, chip))
949235537Sgber		return (EX_SOFTWARE);
950235537Sgber
951235537Sgber	/* Get chip geometry */
952235537Sgber	if (opencdev(&fd, ctl, chip) != EX_OK)
953235537Sgber		return (EX_OSFILE);
954235537Sgber
955235537Sgber	err = ioctl(fd, NAND_IO_GET_CHIP_PARAM, &cparams);
956235537Sgber	if (err) {
957235537Sgber		error("Cannot get parameters for chip %d:%d", ctl, chip);
958235537Sgber		close(fd);
959235537Sgber		return (err);
960235537Sgber	}
961235537Sgber	close(fd);
962235537Sgber
963235537Sgber	/* Obtain dump file size */
964235537Sgber	errno = 0;
965235537Sgber	if (stat(gargv[3], &filestat) != 0) {
966235537Sgber		error("Could not acquire file size! : %s",
967235537Sgber		    strerror(errno));
968235537Sgber		return (EX_IOERR);
969235537Sgber	}
970235537Sgber
971235537Sgber	fsize = filestat.st_size;
972235537Sgber	blksz = cparams.pages_per_block * (cparams.page_size +
973235537Sgber	    cparams.oob_size);
974235537Sgber
975235537Sgber	/* Expected dump file size for chip */
976235537Sgber	expfilesz = cparams.blocks * (blksz + sizeof(bs)) + sizeof(cparams);
977235537Sgber
978235537Sgber	if (fsize != expfilesz) {
979235537Sgber		error("File size does not match chip geometry (file size: %d"
980235537Sgber		    ", dump size: %d)", fsize, expfilesz);
981235537Sgber		return (EX_SOFTWARE);
982235537Sgber	}
983235537Sgber
984235537Sgber	dumpfd = open(gargv[3], O_RDONLY);
985235537Sgber	if (dumpfd == -1) {
986235537Sgber		error("Could not open dump file!");
987235537Sgber		return (EX_IOERR);
988235537Sgber	}
989235537Sgber
990235537Sgber	/* Read chip params saved in dumpfile */
991235537Sgber	read(dumpfd, &dumpcparams, sizeof(dumpcparams));
992235537Sgber
993235537Sgber	/* XXX */
994235537Sgber	if (bcmp(&dumpcparams, &cparams, sizeof(cparams)) != 0) {
995235537Sgber		error("Supplied dump is created for a chip with different "
996235537Sgber		    "chip configuration!");
997235537Sgber		close(dumpfd);
998235537Sgber		return (EX_SOFTWARE);
999235537Sgber	}
1000235537Sgber
1001235537Sgber	if (opendev(&fd) != EX_OK) {
1002235537Sgber		close(dumpfd);
1003235537Sgber		return (EX_OSFILE);
1004235537Sgber	}
1005235537Sgber
1006235537Sgber	buf = malloc(blksz);
1007235537Sgber	if (buf == NULL) {
1008235537Sgber		error("Could not allocate memory for block buffer");
1009235537Sgber		close(dumpfd);
1010235537Sgber		close(fd);
1011235537Sgber		return (EX_SOFTWARE);
1012235537Sgber	}
1013235537Sgber
1014235537Sgber	dump.ctrl_num = ctl;
1015235537Sgber	dump.chip_num = chip;
1016235537Sgber	dump.data = buf;
1017235537Sgber	/* Restore block states and wearouts */
1018235537Sgber	for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
1019235537Sgber		dump.block_num = blkidx;
1020235537Sgber		if (read(dumpfd, &bs, sizeof(bs)) != sizeof(bs)) {
1021235537Sgber			error("Error reading dumpfile");
1022235537Sgber			close(dumpfd);
1023235537Sgber			close(fd);
1024235537Sgber			free(buf);
1025235537Sgber			return (EX_SOFTWARE);
1026235537Sgber		}
1027235537Sgber		bs.ctrl_num = ctl;
1028235537Sgber		bs.chip_num = chip;
1029235537Sgber		debug("BLKIDX=%d BLOCKS=%d CTRL=%d CHIP=%d STATE=%d\n"
1030235537Sgber		    "WEAROUT=%d BS.CTRL_NUM=%d BS.CHIP_NUM=%d\n",
1031235537Sgber		    blkidx, cparams.blocks, dump.ctrl_num, dump.chip_num,
1032235537Sgber		    bs.state, bs.wearout, bs.ctrl_num, bs.chip_num);
1033235537Sgber
1034235537Sgber		err = ioctl(fd, NANDSIM_SET_BLOCK_STATE, &bs);
1035235537Sgber		if (err) {
1036235537Sgber			error("Could not set bad block(%d) for "
1037235537Sgber			    "controller: %d, chip: %d!", blkidx, ctl, chip);
1038235537Sgber			close(dumpfd);
1039235537Sgber			close(fd);
1040235537Sgber			free(buf);
1041235537Sgber			return (EX_SOFTWARE);
1042235537Sgber		}
1043235537Sgber	}
1044235537Sgber	/* Restore data */
1045235537Sgber	for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
1046235537Sgber		errno = 0;
1047235537Sgber		dump.len = read(dumpfd, buf, blksz);
1048235537Sgber		if (errno) {
1049235537Sgber			error("Failed to read block#%d from dumpfile.", blkidx);
1050235537Sgber			err = EX_SOFTWARE;
1051235537Sgber			break;
1052235537Sgber		}
1053235537Sgber		dump.block_num = blkidx;
1054235537Sgber		err = ioctl(fd, NANDSIM_RESTORE, &dump);
1055235537Sgber		if (err) {
1056235537Sgber			error("Could not restore block#%d of ctrl#%d chip#%d"
1057235537Sgber			    ": %s", blkidx, ctl, chip, strerror(errno));
1058235537Sgber			err = EX_SOFTWARE;
1059235537Sgber			break;
1060235537Sgber		}
1061235537Sgber	}
1062235537Sgber
1063235537Sgber	free(buf);
1064235537Sgber	close(dumpfd);
1065235537Sgber	close(fd);
1066235537Sgber	return (err);
1067235537Sgber
1068235537Sgber}
1069235537Sgber
1070235537Sgberstatic int
1071235537Sgbercmddestroy(int gargc __unused, char **gargv)
1072235537Sgber{
1073235537Sgber	int chip = 0, ctl = 0, err = 0, fd, idx, idx2, state;
1074235537Sgber	int chipstart, chipstop, ctrlstart, ctrlstop;
1075235537Sgber	struct sim_chip_destroy chip_destroy;
1076235537Sgber
1077235537Sgber	err = parse_devstring(gargv[2], &ctl, &chip);
1078235537Sgber
1079235537Sgber	if (err)
1080235537Sgber		return (EX_USAGE);
1081235537Sgber
1082235537Sgber	if (ctl == 0xff) {
1083235537Sgber		/* Every chip at every controller */
1084235537Sgber		ctrlstart = chipstart = 0;
1085235537Sgber		ctrlstop = MAX_SIM_DEV - 1;
1086235537Sgber		chipstop = MAX_CTRL_CS - 1;
1087235537Sgber	} else {
1088235537Sgber		ctrlstart = ctrlstop = ctl;
1089235537Sgber		if (chip == 0xff) {
1090235537Sgber			/* Every chip at selected controller */
1091235537Sgber			chipstart = 0;
1092235537Sgber			chipstop = MAX_CTRL_CS - 1;
1093235537Sgber		} else
1094235537Sgber			/* Selected chip at selected controller */
1095235537Sgber			chipstart = chipstop = chip;
1096235537Sgber	}
1097235537Sgber	debug("CTRLSTART=%d CTRLSTOP=%d CHIPSTART=%d CHIPSTOP=%d\n",
1098235537Sgber	    ctrlstart, ctrlstop, chipstart, chipstop);
1099235537Sgber	for (idx = ctrlstart; idx <= ctrlstop; idx++) {
1100235537Sgber		err = is_ctrl_created(idx, &state);
1101235537Sgber		if (err) {
1102235537Sgber			error("Could not acquire ctrl#%d state. Cannot "
1103235537Sgber			    "destroy controller.", idx);
1104235537Sgber			return (EX_SOFTWARE);
1105235537Sgber		}
1106235537Sgber		if (state == 0) {
1107235537Sgber			continue;
1108235537Sgber		}
1109235537Sgber		err = is_ctrl_running(idx, &state);
1110235537Sgber		if (err) {
1111235537Sgber			error(MSG_STATUSACQCTRL, idx);
1112235537Sgber			return (EX_SOFTWARE);
1113235537Sgber		}
1114235537Sgber		if (state != 0) {
1115235537Sgber			error(MSG_RUNNING, ctl);
1116235537Sgber			return (EX_SOFTWARE);
1117235537Sgber		}
1118235537Sgber		if (opendev(&fd) != EX_OK)
1119235537Sgber			return (EX_OSFILE);
1120235537Sgber
1121235537Sgber		for (idx2 = chipstart; idx2 <= chipstop; idx2++) {
1122235537Sgber			err = is_chip_created(idx, idx2, &state);
1123235537Sgber			if (err) {
1124235537Sgber				error(MSG_STATUSACQCTRLCHIP, idx2, idx);
1125235537Sgber				continue;
1126235537Sgber			}
1127235537Sgber			if (state == 0)
1128235537Sgber				/* There is no such chip running */
1129235537Sgber				continue;
1130235537Sgber			chip_destroy.ctrl_num = idx;
1131235537Sgber			chip_destroy.chip_num = idx2;
1132235537Sgber			ioctl(fd, NANDSIM_DESTROY_CHIP,
1133235537Sgber			    &chip_destroy);
1134235537Sgber		}
1135235537Sgber		/* If chip isn't explicitly specified -- destroy ctrl */
1136235537Sgber		if (chip == 0xff) {
1137235537Sgber			err = ioctl(fd, NANDSIM_DESTROY_CTRL, &idx);
1138235537Sgber			if (err) {
1139235537Sgber				error("Could not destroy ctrl#%d", idx);
1140235537Sgber				continue;
1141235537Sgber			}
1142235537Sgber		}
1143235537Sgber		close(fd);
1144235537Sgber	}
1145235537Sgber	return (err);
1146235537Sgber}
1147235537Sgber
1148235537Sgberint
1149235537Sgbermain(int argc, char **argv)
1150235537Sgber{
1151235537Sgber	struct nandsim_command *cmdopts;
1152235537Sgber	int retcode = 0;
1153235537Sgber
1154235537Sgber	if (argc < 2) {
1155235537Sgber		cmdhelp(argc, argv);
1156235537Sgber		retcode = EX_USAGE;
1157235537Sgber	} else {
1158235537Sgber		cmdopts = getcommand(argv[1]);
1159235537Sgber		if (cmdopts != NULL && cmdopts->commandfunc != NULL) {
1160235537Sgber			if (checkusage(argc, cmdopts->req_argc, argv) == 1) {
1161235537Sgber				/* Print command specific usage */
1162235537Sgber				printf("nandsim %s", cmdopts->usagestring);
1163235537Sgber				return (EX_USAGE);
1164235537Sgber			}
1165235537Sgber			retcode = cmdopts->commandfunc(argc, argv);
1166235537Sgber
1167235537Sgber			if (retcode == EX_USAGE) {
1168235537Sgber				/* Print command-specific usage */
1169235537Sgber				printf("nandsim %s", cmdopts->usagestring);
1170235537Sgber			} else if (retcode == EX_OSFILE) {
1171235537Sgber				error("Could not open device file");
1172235537Sgber			}
1173235537Sgber
1174235537Sgber		} else {
1175235537Sgber			error("Unknown command!");
1176235537Sgber			retcode = EX_USAGE;
1177235537Sgber		}
1178235537Sgber	}
1179235537Sgber	return (retcode);
1180235537Sgber}
1181235537Sgber
1182235537Sgberstatic int
1183235537Sgbercmdhelp(int gargc __unused, char **gargv __unused)
1184235537Sgber{
1185235537Sgber	struct nandsim_command *opts;
1186235537Sgber
1187235537Sgber	printf("usage:  nandsim <command> [command params] [params]\n\n");
1188235537Sgber
1189235537Sgber	for (opts = commands; (opts != NULL) &&
1190235537Sgber	    (opts->cmd_name != NULL); opts++)
1191235537Sgber		printf("nandsim %s", opts->usagestring);
1192235537Sgber
1193235537Sgber	printf("\n");
1194235537Sgber	return (EX_OK);
1195235537Sgber}
1196235537Sgber
1197235537Sgberstatic void
1198235537Sgberprintchip(struct sim_chip *chip, uint8_t verbose)
1199235537Sgber{
1200235537Sgber
1201235537Sgber	if (chip->created == 0)
1202235537Sgber		return;
1203235537Sgber	if (verbose > 0) {
1204235537Sgber		printf("\n[Chip info]\n");
1205235537Sgber		printf("num= %d\nctrl_num=%d\ndevice_id=%02x"
1206235537Sgber		    "\tmanufacturer_id=%02x\ndevice_model=%s\nmanufacturer="
1207235537Sgber		    "%s\ncol_addr_cycles=%d\nrow_addr_cycles=%d"
1208235537Sgber		    "\npage_size=%d\noob_size=%d\npages_per_block=%d\n"
1209235537Sgber		    "blocks_per_lun=%d\nluns=%d\n\nprog_time=%d\n"
1210235537Sgber		    "erase_time=%d\nread_time=%d\n"
1211235537Sgber		    "error_ratio=%d\nwear_level=%d\nwrite_protect=%c\n"
1212235537Sgber		    "chip_width=%db\n", chip->num, chip->ctrl_num,
1213235537Sgber		    chip->device_id, chip->manufact_id,chip->device_model,
1214235537Sgber		    chip->manufacturer, chip->col_addr_cycles,
1215235537Sgber		    chip->row_addr_cycles, chip->page_size,
1216235537Sgber		    chip->oob_size, chip->pgs_per_blk, chip->blks_per_lun,
1217235537Sgber		    chip->luns,chip->prog_time, chip->erase_time,
1218235537Sgber		    chip->read_time, chip->error_ratio, chip->wear_level,
1219235537Sgber		    (chip->is_wp == 0) ? 'N':'Y', chip->width);
1220235537Sgber	} else {
1221235537Sgber		printf("[Chip info]\n");
1222235537Sgber		printf("\tnum=%d\n\tdevice_model=%s\n\tmanufacturer=%s\n"
1223235537Sgber		    "\tpage_size=%d\n\twrite_protect=%s\n",
1224235537Sgber		    chip->num, chip->device_model, chip->manufacturer,
1225235537Sgber		    chip->page_size, (chip->is_wp == 0) ? "NO":"YES");
1226235537Sgber	}
1227235537Sgber}
1228235537Sgber
1229235537Sgberstatic void
1230235537Sgberprintctrl(struct sim_ctrl *ctrl)
1231235537Sgber{
1232235537Sgber	int i;
1233235537Sgber
1234235537Sgber	if (ctrl->created == 0) {
1235235537Sgber		printf(MSG_NOCTRL "\n", ctrl->num);
1236235537Sgber		return;
1237235537Sgber	}
1238235537Sgber	printf("\n[Controller info]\n");
1239235537Sgber	printf("\trunning: %s\n", ctrl->running ? "yes" : "no");
1240235537Sgber	printf("\tnum cs: %d\n", ctrl->num_cs);
1241235537Sgber	printf("\tecc: %d\n", ctrl->ecc);
1242235537Sgber	printf("\tlog_filename: %s\n", ctrl->filename);
1243235537Sgber	printf("\tecc_layout:");
1244235537Sgber	for (i = 0; i < MAX_ECC_BYTES; i++) {
1245235537Sgber		if (ctrl->ecc_layout[i] == 0xffff)
1246235537Sgber			break;
1247235537Sgber		else
1248235537Sgber			printf("%c%d", i%16 ? ' ' : '\n',
1249235537Sgber			    ctrl->ecc_layout[i]);
1250235537Sgber	}
1251235537Sgber	printf("\n");
1252235537Sgber}
1253235537Sgber
1254235537Sgberstatic int
1255235537Sgberis_ctrl_running(int ctrl_no, int *running)
1256235537Sgber{
1257235537Sgber	struct sim_ctrl ctrl;
1258235537Sgber	int err, fd;
1259235537Sgber
1260235537Sgber	ctrl.num = ctrl_no;
1261235537Sgber	if (opendev(&fd) != EX_OK)
1262235537Sgber		return (EX_OSFILE);
1263235537Sgber
1264235537Sgber	err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrl);
1265235537Sgber	if (err) {
1266235537Sgber		error(MSG_STATUSACQCTRL, ctrl_no);
1267235537Sgber		close(fd);
1268235537Sgber		return (err);
1269235537Sgber	}
1270235537Sgber	*running = ctrl.running;
1271235537Sgber	close(fd);
1272235537Sgber	return (0);
1273235537Sgber}
1274235537Sgber
1275235537Sgberstatic int
1276235537Sgberis_ctrl_created(int ctrl_no, int *created)
1277235537Sgber{
1278235537Sgber	struct sim_ctrl ctrl;
1279235537Sgber	int err, fd;
1280235537Sgber
1281235537Sgber	ctrl.num = ctrl_no;
1282235537Sgber
1283235537Sgber	if (opendev(&fd) != EX_OK)
1284235537Sgber		return (EX_OSFILE);
1285235537Sgber
1286235537Sgber	err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrl);
1287235537Sgber	if (err) {
1288235537Sgber		error("Could not acquire conf for ctrl#%d", ctrl_no);
1289235537Sgber		close(fd);
1290235537Sgber		return (err);
1291235537Sgber	}
1292235537Sgber	*created = ctrl.created;
1293235537Sgber	close(fd);
1294235537Sgber	return (0);
1295235537Sgber}
1296235537Sgber
1297235537Sgberstatic int
1298235537Sgberis_chip_created(int ctrl_no, int chip_no, int *created)
1299235537Sgber{
1300235537Sgber	struct sim_chip chip;
1301235537Sgber	int err, fd;
1302235537Sgber
1303235537Sgber	chip.ctrl_num = ctrl_no;
1304235537Sgber	chip.num = chip_no;
1305235537Sgber
1306235537Sgber	if (opendev(&fd) != EX_OK)
1307235537Sgber		return (EX_OSFILE);
1308235537Sgber
1309235537Sgber	err = ioctl(fd, NANDSIM_STATUS_CHIP, &chip);
1310235537Sgber	if (err) {
1311235537Sgber		error("Could not acquire conf for chip#%d", chip_no);
1312235537Sgber		close(fd);
1313235537Sgber		return (err);
1314235537Sgber	}
1315235537Sgber	*created = chip.created;
1316235537Sgber	close(fd);
1317235537Sgber	return (0);
1318235537Sgber}
1319235537Sgber
1320235537Sgberstatic int
1321235537Sgberassert_chip_connected(int ctrl_no, int chip_no)
1322235537Sgber{
1323235537Sgber	int created, running;
1324235537Sgber
1325235537Sgber	if (is_ctrl_created(ctrl_no, &created))
1326235537Sgber		return (0);
1327235537Sgber
1328235537Sgber	if (!created) {
1329235537Sgber		error(MSG_NOCTRL, ctrl_no);
1330235537Sgber		return (0);
1331235537Sgber	}
1332235537Sgber
1333235537Sgber	if (is_chip_created(ctrl_no, chip_no, &created))
1334235537Sgber		return (0);
1335235537Sgber
1336235537Sgber	if (!created) {
1337235537Sgber		error(MSG_NOTCONFIGDCTRLCHIP, ctrl_no, chip_no);
1338235537Sgber		return (0);
1339235537Sgber	}
1340235537Sgber
1341235537Sgber	if (is_ctrl_running(ctrl_no, &running))
1342235537Sgber		return (0);
1343235537Sgber
1344235537Sgber	if (!running) {
1345235537Sgber		error(MSG_NOTRUNNING, ctrl_no);
1346235537Sgber		return (0);
1347235537Sgber	}
1348235537Sgber
1349235537Sgber	return (1);
1350235537Sgber}
1351235537Sgber
1352235537Sgberstatic int
1353235537Sgberprintstats(int ctrlno, int chipno, uint32_t pageno, int cdevd)
1354235537Sgber{
1355235537Sgber	struct page_stat_io pstats;
1356235537Sgber	struct block_stat_io bstats;
1357235537Sgber	struct chip_param_io cparams;
1358235537Sgber	uint32_t blkidx;
1359235537Sgber	int err;
1360235537Sgber
1361235537Sgber	/* Gather information about chip */
1362235537Sgber	err = ioctl(cdevd, NAND_IO_GET_CHIP_PARAM, &cparams);
1363235537Sgber
1364235537Sgber	if (err) {
1365235537Sgber		error("Could not acquire chip info for chip attached to cs#"
1366235537Sgber		    "%d, ctrl#%d", chipno, ctrlno);
1367235537Sgber		return (EX_SOFTWARE);
1368235537Sgber	}
1369235537Sgber
1370235537Sgber	blkidx = (pageno / cparams.pages_per_block);
1371235537Sgber	bstats.block_num = blkidx;
1372235537Sgber
1373235537Sgber	err = ioctl(cdevd, NAND_IO_BLOCK_STAT, &bstats);
1374235537Sgber	if (err) {
1375235537Sgber		error("Could not acquire block#%d statistics!", blkidx);
1376235537Sgber		return (ENXIO);
1377235537Sgber	}
1378235537Sgber
1379235537Sgber	printf("Block #%d erased: %d\n", blkidx, bstats.block_erased);
1380235537Sgber	pstats.page_num = pageno;
1381235537Sgber
1382235537Sgber	err = ioctl(cdevd, NAND_IO_PAGE_STAT, &pstats);
1383235537Sgber	if (err) {
1384235537Sgber		error("Could not acquire page statistics!");
1385235537Sgber		return (ENXIO);
1386235537Sgber	}
1387235537Sgber
1388235537Sgber	debug("BLOCKIDX = %d PAGENO (REL. TO BLK) = %d\n", blkidx,
1389235537Sgber	    pstats.page_num);
1390235537Sgber
1391235537Sgber	printf("Page#%d : reads:%d writes:%d \n\traw reads:%d raw writes:%d "
1392235537Sgber	    "\n\tecc_succeeded:%d ecc_corrected:%d ecc_failed:%d\n",
1393235537Sgber	    pstats.page_num, pstats.page_read, pstats.page_written,
1394235537Sgber	    pstats.page_raw_read, pstats.page_raw_written,
1395235537Sgber	    pstats.ecc_succeded, pstats.ecc_corrected, pstats.ecc_failed);
1396235537Sgber	return (0);
1397235537Sgber}
1398