1249423Sdim/*-
2193323Sed * Copyright (C) 2009-2012 Semihalf
3353358Sdim * All rights reserved.
4353358Sdim *
5353358Sdim * Redistribution and use in source and binary forms, with or without
6193323Sed * modification, are permitted provided that the following conditions
7193323Sed * are met:
8327952Sdim * 1. Redistributions of source code must retain the above copyright
9296417Sdim *    notice, this list of conditions and the following disclaimer.
10296417Sdim * 2. Redistributions in binary form must reproduce the above copyright
11296417Sdim *    notice, this list of conditions and the following disclaimer in the
12327952Sdim *    documentation and/or other materials provided with the distribution.
13193323Sed *
14193323Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15309124Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16234353Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17327952Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
18243830Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19327952Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20327952Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21193323Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22249423Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23296417Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24296417Sdim * SUCH DAMAGE.
25327952Sdim */
26249423Sdim
27249423Sdim/*
28249423Sdim * Control application for the NAND simulator.
29327952Sdim */
30353358Sdim
31327952Sdim#include <sys/cdefs.h>
32296417Sdim__FBSDID("$FreeBSD$");
33327952Sdim
34327952Sdim#include <sys/errno.h>
35327952Sdim#include <sys/ioctl.h>
36327952Sdim#include <sys/mman.h>
37327952Sdim#include <sys/stat.h>
38327952Sdim#include <sys/types.h>
39327952Sdim
40276479Sdim#include <dev/nand/nandsim.h>
41327952Sdim#include <dev/nand/nand_dev.h>
42327952Sdim
43327952Sdim#include <ctype.h>
44344779Sdim#include <fcntl.h>
45327952Sdim#include <getopt.h>
46327952Sdim#include <stdio.h>
47327952Sdim#include <stdlib.h>
48327952Sdim#include <string.h>
49327952Sdim#include <stdarg.h>
50327952Sdim#include <unistd.h>
51360784Sdim#include <stdlib.h>
52327952Sdim#include <limits.h>
53327952Sdim#include <sysexits.h>
54327952Sdim
55327952Sdim#include "nandsim_cfgparse.h"
56296417Sdim
57327952Sdim#define SIMDEVICE	"/dev/nandsim.ioctl"
58296417Sdim
59321369Sdim#define error(fmt, args...) do { \
60327952Sdim    printf("ERROR: " fmt "\n", ##args); } while (0)
61327952Sdim
62327952Sdim#define warn(fmt, args...) do { \
63327952Sdim    printf("WARNING: " fmt "\n", ##args); } while (0)
64327952Sdim
65193323Sed#define DEBUG
66193323Sed#undef DEBUG
67276479Sdim
68276479Sdim#ifdef DEBUG
69193323Sed#define debug(fmt, args...) do { \
70193323Sed    printf("NANDSIM_CONF:" fmt "\n", ##args); } while (0)
71344779Sdim#else
72193323Sed#define debug(fmt, args...) do {} while(0)
73314564Sdim#endif
74261991Sdim
75261991Sdim#define NANDSIM_RAM_LOG_SIZE 16384
76193323Sed
77296417Sdim#define MSG_NOTRUNNING		"Controller#%d is not running.Please start" \
78296417Sdim    " it first."
79341825Sdim#define MSG_RUNNING		"Controller#%d is already running!"
80353358Sdim#define MSG_CTRLCHIPNEEDED	"You have to specify ctrl_no:cs_no pair!"
81193323Sed#define MSG_STATUSACQCTRLCHIP	"Could not acquire status for ctrl#%d chip#%d"
82321369Sdim#define MSG_STATUSACQCTRL	"Could not acquire status for ctrl#%d"
83360784Sdim#define MSG_NOCHIP		"There is no such chip configured (chip#%d "\
84321369Sdim    "at ctrl#%d)!"
85321369Sdim
86321369Sdim#define MSG_NOCTRL		"Controller#%d is not configured!"
87341825Sdim#define MSG_NOTCONFIGDCTRLCHIP	"Chip connected to ctrl#%d at cs#%d " \
88341825Sdim    "is not configured."
89341825Sdim
90341825Sdimtypedef int (commandfunc_t)(int , char **);
91353358Sdim
92353358Sdimstatic struct nandsim_command *getcommand(char *);
93353358Sdimstatic int parse_devstring(char *, int *, int *);
94353358Sdimstatic void printchip(struct sim_chip *, uint8_t);
95193323Sedstatic void printctrl(struct sim_ctrl *);
96193323Sedstatic int opendev(int *);
97327952Sdimstatic commandfunc_t cmdstatus;
98327952Sdimstatic commandfunc_t cmdconf;
99327952Sdimstatic commandfunc_t cmdstart;
100327952Sdimstatic commandfunc_t cmdstop;
101321369Sdimstatic commandfunc_t cmdmod;
102321369Sdimstatic commandfunc_t cmderror;
103321369Sdimstatic commandfunc_t cmdbb;
104321369Sdimstatic commandfunc_t cmdfreeze;
105321369Sdimstatic commandfunc_t cmdlog;
106321369Sdimstatic commandfunc_t cmdstats;
107321369Sdimstatic commandfunc_t cmddump;
108321369Sdimstatic commandfunc_t cmdrestore;
109321369Sdimstatic commandfunc_t cmddestroy;
110321369Sdimstatic commandfunc_t cmdhelp;
111296417Sdimstatic int checkusage(int, int, char **);
112296417Sdimstatic int is_chip_created(int, int, int *);
113296417Sdimstatic int is_ctrl_created(int, int *);
114296417Sdimstatic int is_ctrl_running(int, int *);
115296417Sdimstatic int assert_chip_connected(int , int);
116249423Sdimstatic int printstats(int, int, uint32_t, int);
117321369Sdim
118296417Sdimstruct nandsim_command {
119296417Sdim	const char	*cmd_name;	/* Command name */
120249423Sdim	commandfunc_t	*commandfunc;	/* Ptr to command function */
121344779Sdim	uint8_t		req_argc;	/* Mandatory arguments count */
122344779Sdim	const char	*usagestring;	/* Usage string */
123344779Sdim};
124344779Sdim
125296417Sdimstatic struct nandsim_command commands[] = {
126296417Sdim	{"status", cmdstatus, 1,
127249423Sdim	    "status <ctl_no|--all|-a> [-v]\n" },
128296417Sdim	{"conf", cmdconf, 1,
129296417Sdim	    "conf <filename>\n" },
130344779Sdim	{"start", cmdstart, 1,
131296417Sdim	    "start <ctrl_no>\n" },
132296417Sdim	{"mod", cmdmod, 2,
133261991Sdim	    "mod [-l <loglevel>] | <ctl_no:cs_no> [-p <prog_time>]\n"
134296417Sdim	    "\t[-e <erase_time>] [-r <read_time>]\n"
135296417Sdim	    "\t[-E <error_ratio>] | [-h]\n" },
136344779Sdim	{"stop", cmdstop, 1,
137309124Sdim	    "stop <ctrl_no>\n" },
138309124Sdim	{"error", cmderror, 5,
139309124Sdim	    "error <ctrl_no:cs_no> <page_num> <column> <length> <pattern>\n" },
140309124Sdim	{"bb", cmdbb, 2,
141344779Sdim	    "bb <ctl_no:cs_no>  [blk_num1,blk_num2,..] [-U] [-L]\n" },
142344779Sdim	{"freeze", cmdfreeze, 1,
143296417Sdim	    "freeze [ctrl_no]\n" },
144344779Sdim	{"log", cmdlog, 1,
145327952Sdim	    "log <ctrl_no|--all|-a>\n" },
146249423Sdim	{"stats", cmdstats, 2,
147296417Sdim	    "stats <ctrl_no:cs_no> <pagenumber>\n" },
148327952Sdim	{"dump", cmddump, 2,
149296417Sdim	    "dump <ctrl_no:cs_no> <filename>\n" },
150249423Sdim	{"restore", cmdrestore, 2,
151296417Sdim	    "restore <ctrl_no:chip_no> <filename>\n" },
152344779Sdim	{"destroy", cmddestroy, 1,
153327952Sdim	    "destroy <ctrl_no[:cs_no]|--all|-a>\n" },
154344779Sdim	{"help", cmdhelp, 0,
155296417Sdim	    "help [-v]" },
156327952Sdim	{NULL, NULL, 0, NULL},
157296417Sdim};
158296417Sdim
159296417Sdim
160249423Sdim/* Parse command name, and start appropriate function */
161296417Sdimstatic struct nandsim_command*
162296417Sdimgetcommand(char *arg)
163344779Sdim{
164296417Sdim	struct nandsim_command *opts;
165296417Sdim
166296417Sdim	for (opts = commands; (opts != NULL) &&
167296417Sdim	    (opts->cmd_name != NULL); opts++) {
168193323Sed		if (strcmp(opts->cmd_name, arg) == 0)
169296417Sdim			return (opts);
170296417Sdim	}
171344779Sdim	return (NULL);
172193323Sed}
173296417Sdim
174296417Sdim/*
175296417Sdim * Parse given string in format <ctrl_no>:<cs_no>, if possible -- set
176296417Sdim * ctrl and/or cs, and return 0 (success) or 1 (in case of error).
177193323Sed *
178327952Sdim * ctrl == 0xff && chip == 0xff  : '--all' flag specified
179344779Sdim * ctrl != 0xff && chip != 0xff  : both ctrl & chip were specified
180344779Sdim * ctrl != 0xff && chip == 0xff  : only ctrl was specified
181327952Sdim */
182296417Sdimstatic int
183296417Sdimparse_devstring(char *str, int *ctrl, int *cs)
184296417Sdim{
185296417Sdim	char *tmpstr;
186296417Sdim	unsigned int num = 0;
187296417Sdim
188296417Sdim	/* Ignore white spaces at the beginning */
189296417Sdim	while (isspace(*str) && (*str != '\0'))
190296417Sdim		str++;
191296417Sdim
192296417Sdim	*ctrl = 0xff;
193296417Sdim	*cs = 0xff;
194296417Sdim	if (strcmp(str, "--all") == 0 ||
195296417Sdim	    strcmp(str, "-a") == 0) {
196296417Sdim		/* If --all or -a is specified, ctl==chip==0xff */
197296417Sdim		debug("CTRL=%d CHIP=%d\n", *ctrl, *cs);
198296417Sdim		return (0);
199296417Sdim	}
200296417Sdim	/* Separate token and try to convert it to int */
201296417Sdim	tmpstr = (char *)strtok(str, ":");
202296417Sdim	if ((tmpstr != NULL) && (*tmpstr != '\0')) {
203296417Sdim		if (convert_arguint(tmpstr, &num) != 0)
204296417Sdim			return (1);
205296417Sdim
206193323Sed		if (num > MAX_SIM_DEV - 1) {
207296417Sdim			error("Invalid ctrl_no supplied: %s. Valid ctrl_no "
208296417Sdim			    "value must lie between 0 and 3!", tmpstr);
209344779Sdim			return (1);
210344779Sdim		}
211344779Sdim
212193323Sed		*ctrl = num;
213296417Sdim		tmpstr = (char *)strtok(NULL, ":");
214296417Sdim
215296417Sdim		if ((tmpstr != NULL) && (*tmpstr != '\0')) {
216193323Sed			if (convert_arguint(tmpstr, &num) != 0)
217344779Sdim				return (1);
218344779Sdim
219344779Sdim			/* Check if chip_no is valid */
220344779Sdim			if (num > MAX_CTRL_CS - 1) {
221344779Sdim				error("Invalid chip_no supplied: %s. Valid "
222344779Sdim				    "chip_no value must lie between 0 and 3!",
223344779Sdim				    tmpstr);
224296417Sdim				return (1);
225296417Sdim			}
226193323Sed			*cs = num;
227321369Sdim		}
228321369Sdim	} else
229321369Sdim		/* Empty devstring supplied */
230321369Sdim		return (1);
231321369Sdim
232296417Sdim	debug("CTRL=%d CHIP=%d\n", *ctrl, *cs);
233296417Sdim	return (0);
234321369Sdim}
235193323Sed
236193323Sedstatic int
237193323Sedopendev(int *fd)
238344779Sdim{
239296417Sdim
240296417Sdim	*fd = open(SIMDEVICE, O_RDWR);
241296417Sdim	if (*fd == -1) {
242193323Sed		error("Could not open simulator device file (%s)!",
243321369Sdim		    SIMDEVICE);
244321369Sdim		return (EX_OSFILE);
245321369Sdim	}
246321369Sdim	return (EX_OK);
247321369Sdim}
248296417Sdim
249193323Sedstatic int
250296417Sdimopencdev(int *cdevd, int ctrl, int chip)
251193323Sed{
252296417Sdim	char fname[255];
253344779Sdim
254344779Sdim	sprintf(fname, "/dev/nandsim%d.%d", ctrl, chip);
255344779Sdim	*cdevd = open(fname, O_RDWR);
256296417Sdim	if (*cdevd == -1)
257296417Sdim		return (EX_NOINPUT);
258296417Sdim
259193323Sed	return (EX_OK);
260193323Sed}
261193323Sed
262353358Sdim/*
263353358Sdim * Check if given arguments count match requirements. If no, or
264353358Sdim * --help (-h) flag is specified -- return 1 (print usage)
265353358Sdim */
266353358Sdimstatic int
267193323Sedcheckusage(int gargc, int argsreqd, char **gargv)
268193323Sed{
269193323Sed
270344779Sdim	if (gargc < argsreqd + 2 || (gargc >= (argsreqd + 2) &&
271296417Sdim	    (strcmp(gargv[1], "--help") == 0 ||
272193323Sed	    strcmp(gargv[1], "-h") == 0)))
273193323Sed		return (1);
274193323Sed
275193323Sed	return (0);
276193323Sed}
277193323Sed
278193323Sedstatic int
279193323Sedcmdstatus(int gargc, char **gargv)
280344779Sdim{
281344779Sdim	int chip = 0, ctl = 0, err = 0, fd, idx, idx2, start, stop;
282344779Sdim	uint8_t verbose = 0;
283193323Sed	struct sim_ctrl ctrlconf;
284193323Sed	struct sim_chip chipconf;
285193323Sed
286321369Sdim	err = parse_devstring(gargv[2], &ctl, &chip);
287321369Sdim	if (err) {
288344779Sdim		return (EX_USAGE);
289193323Sed	} else if (ctl == 0xff) {
290344779Sdim		/* Every controller */
291344779Sdim		start = 0;
292344779Sdim		stop = MAX_SIM_DEV-1;
293344779Sdim	} else {
294344779Sdim		/* Specified controller only */
295344779Sdim		start = ctl;
296344779Sdim		stop = ctl;
297193323Sed	}
298344779Sdim
299344779Sdim	if (opendev(&fd) != EX_OK)
300344779Sdim		return (EX_OSFILE);
301344779Sdim
302193323Sed	for (idx = 0; idx < gargc; idx ++)
303344779Sdim		if (strcmp(gargv[idx], "-v") == 0 ||
304344779Sdim		    strcmp(gargv[idx], "--verbose") == 0)
305344779Sdim			verbose = 1;
306193323Sed
307193323Sed	for (idx = start; idx <= stop; idx++) {
308193323Sed		ctrlconf.num = idx;
309193323Sed		err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrlconf);
310193323Sed		if (err) {
311193323Sed			err = EX_SOFTWARE;
312193323Sed			error(MSG_STATUSACQCTRL, idx);
313193323Sed			continue;
314234353Sdim		}
315327952Sdim
316296417Sdim		printctrl(&ctrlconf);
317296417Sdim
318296417Sdim		for (idx2 = 0; idx2 < MAX_CTRL_CS; idx2++) {
319296417Sdim			chipconf.num = idx2;
320296417Sdim			chipconf.ctrl_num = idx;
321296417Sdim
322296417Sdim			err = ioctl(fd, NANDSIM_STATUS_CHIP, &chipconf);
323234353Sdim			if (err) {
324296417Sdim				err = EX_SOFTWARE;
325296417Sdim				error(MSG_STATUSACQCTRL, idx);
326296417Sdim				continue;
327327952Sdim			}
328234353Sdim
329296417Sdim			printchip(&chipconf, verbose);
330234353Sdim		}
331296417Sdim	}
332296417Sdim	close(fd);
333296417Sdim	return (err);
334296417Sdim}
335296417Sdim
336296417Sdimstatic int
337296417Sdimcmdconf(int gargc __unused, char **gargv)
338234353Sdim{
339296417Sdim	int err;
340296417Sdim
341234353Sdim	err = parse_config(gargv[2], SIMDEVICE);
342327952Sdim	if (err)
343234353Sdim		return (EX_DATAERR);
344296417Sdim
345296417Sdim	return (EX_OK);
346296417Sdim}
347234353Sdim
348296417Sdimstatic int
349296417Sdimcmdstart(int gargc __unused, char **gargv)
350296417Sdim{
351296417Sdim	int chip = 0, ctl = 0, err = 0, fd, running, state;
352296417Sdim
353296417Sdim	err = parse_devstring(gargv[2], &ctl, &chip);
354296417Sdim	if (err)
355234353Sdim		return (EX_USAGE);
356296417Sdim
357296417Sdim	err = is_ctrl_created(ctl, &state);
358296417Sdim	if (err) {
359296417Sdim		return (EX_SOFTWARE);
360327952Sdim	} else if (state == 0) {
361234353Sdim		error(MSG_NOCTRL, ctl);
362296417Sdim		return (EX_SOFTWARE);
363234353Sdim	}
364296417Sdim
365296417Sdim	err = is_ctrl_running(ctl, &running);
366296417Sdim	if (err)
367296417Sdim		return (EX_SOFTWARE);
368296417Sdim
369296417Sdim	if (running) {
370234353Sdim		warn(MSG_RUNNING, ctl);
371296417Sdim	} else {
372309124Sdim		if (opendev(&fd) != EX_OK)
373296417Sdim			return (EX_OSFILE);
374296417Sdim
375296417Sdim		err = ioctl(fd, NANDSIM_START_CTRL, &ctl);
376234353Sdim		close(fd);
377296417Sdim		if (err) {
378296417Sdim			error("Cannot start controller#%d", ctl);
379296417Sdim			err = EX_SOFTWARE;
380296417Sdim		}
381296417Sdim	}
382296417Sdim	return (err);
383296417Sdim}
384296417Sdim
385296417Sdimstatic int
386296417Sdimcmdstop(int gargc __unused, char **gargv)
387296417Sdim{
388296417Sdim	int chip = 0, ctl = 0, err = 0, fd, running;
389296417Sdim
390296417Sdim	err = parse_devstring(gargv[2], &ctl, &chip);
391296417Sdim	if (err)
392296417Sdim		return (EX_USAGE);
393296417Sdim
394296417Sdim	err = is_ctrl_running(ctl, &running);
395296417Sdim	if (err)
396296417Sdim		return (EX_SOFTWARE);
397234353Sdim
398234353Sdim	if (!running) {
399296417Sdim		error(MSG_NOTRUNNING, ctl);
400296417Sdim	} else {
401296417Sdim		if (opendev(&fd) != EX_OK)
402296417Sdim			return (EX_OSFILE);
403296417Sdim
404234353Sdim		err = ioctl(fd, NANDSIM_STOP_CTRL, &ctl);
405296417Sdim		close(fd);
406296417Sdim		if (err) {
407296417Sdim			error("Cannot stop controller#%d", ctl);
408296417Sdim			err = EX_SOFTWARE;
409327952Sdim		}
410327952Sdim	}
411296417Sdim
412327952Sdim	return (err);
413327952Sdim}
414327952Sdim
415296417Sdimstatic int
416296417Sdimcmdmod(int gargc __unused, char **gargv)
417234353Sdim{
418327952Sdim	int chip, ctl, err = 0, fd = -1, i;
419327952Sdim	struct sim_mod mods;
420234353Sdim
421327952Sdim	if (gargc >= 4) {
422296417Sdim		if (strcmp(gargv[2], "--loglevel") == 0 || strcmp(gargv[2],
423327952Sdim		    "-l") == 0) {
424327952Sdim			/* Set loglevel (ctrl:chip pair independent) */
425234353Sdim			mods.field = SIM_MOD_LOG_LEVEL;
426314564Sdim
427314564Sdim			if (convert_arguint(gargv[3], &mods.new_value) != 0)
428314564Sdim				return (EX_SOFTWARE);
429296417Sdim
430327952Sdim			if (opendev(&fd) != EX_OK)
431296417Sdim				return (EX_OSFILE);
432296417Sdim
433314564Sdim			err = ioctl(fd, NANDSIM_MODIFY, &mods);
434327952Sdim			if (err) {
435296417Sdim				error("simulator parameter %s could not be "
436296417Sdim				    "modified !", gargv[3]);
437296417Sdim				close(fd);
438327952Sdim				return (EX_SOFTWARE);
439296417Sdim			}
440296417Sdim
441234353Sdim			debug("request : loglevel = %d\n", mods.new_value);
442327952Sdim			close(fd);
443327952Sdim			return (EX_OK);
444296417Sdim		}
445261991Sdim	}
446261991Sdim
447296417Sdim	err = parse_devstring(gargv[2], &ctl, &chip);
448296417Sdim	if (err)
449341825Sdim		return (EX_USAGE);
450296417Sdim
451276479Sdim	else if (chip == 0xff) {
452276479Sdim		error(MSG_CTRLCHIPNEEDED);
453276479Sdim		return (EX_USAGE);
454276479Sdim	}
455261991Sdim
456261991Sdim	if (!assert_chip_connected(ctl, chip))
457261991Sdim		return (EX_SOFTWARE);
458276479Sdim
459276479Sdim	if (opendev(&fd) != EX_OK)
460276479Sdim		return (EX_OSFILE);
461261991Sdim
462261991Sdim	/* Find out which flags were passed */
463261991Sdim	for (i = 3; i < gargc; i++) {
464261991Sdim
465261991Sdim		if (convert_arguint(gargv[i + 1], &mods.new_value) != 0)
466261991Sdim			continue;
467261991Sdim
468261991Sdim		if (strcmp(gargv[i], "--prog-time") == 0 ||
469261991Sdim		    strcmp(gargv[i], "-p") == 0) {
470261991Sdim
471261991Sdim			mods.field = SIM_MOD_PROG_TIME;
472276479Sdim			debug("request : progtime = %d\n", mods.new_value);
473261991Sdim
474276479Sdim		} else if (strcmp(gargv[i], "--erase-time") == 0 ||
475280031Sdim		    strcmp(gargv[i], "-e") == 0) {
476276479Sdim
477261991Sdim			mods.field = SIM_MOD_ERASE_TIME;
478261991Sdim			debug("request : eraseime = %d\n", mods.new_value);
479261991Sdim
480261991Sdim		} else if (strcmp(gargv[i], "--read-time") == 0 ||
481276479Sdim		    strcmp(gargv[i], "-r") == 0) {
482276479Sdim
483276479Sdim			mods.field = SIM_MOD_READ_TIME;
484276479Sdim			debug("request : read_time = %d\n", mods.new_value);
485276479Sdim
486276479Sdim		} else if (strcmp(gargv[i], "--error-ratio") == 0 ||
487276479Sdim		    strcmp(gargv[i], "-E") == 0) {
488276479Sdim
489280031Sdim			mods.field = SIM_MOD_ERROR_RATIO;
490276479Sdim			debug("request : error_ratio = %d\n", mods.new_value);
491276479Sdim
492276479Sdim		} else {
493261991Sdim			/* Flag not recognized, or nothing specified. */
494276479Sdim			error("Unrecognized flag:%s\n", gargv[i]);
495276479Sdim			if (fd >= 0)
496261991Sdim				close(fd);
497276479Sdim			return (EX_USAGE);
498261991Sdim		}
499261991Sdim
500261991Sdim		mods.chip_num = chip;
501261991Sdim		mods.ctrl_num = ctl;
502261991Sdim
503276479Sdim		/* Call appropriate ioctl */
504261991Sdim		err = ioctl(fd, NANDSIM_MODIFY, &mods);
505261991Sdim		if (err) {
506261991Sdim			error("simulator parameter %s could not be modified! ",
507261991Sdim			    gargv[i]);
508261991Sdim			continue;
509296417Sdim		}
510296417Sdim		i++;
511296417Sdim	}
512296417Sdim	close(fd);
513296417Sdim	return (EX_OK);
514296417Sdim}
515296417Sdim
516296417Sdimstatic int
517296417Sdimcmderror(int gargc __unused, char **gargv)
518296417Sdim{
519296417Sdim	uint32_t page, column, len, pattern;
520296417Sdim	int chip = 0, ctl = 0, err = 0, fd;
521296417Sdim	struct sim_error sim_err;
522296417Sdim
523296417Sdim	err = parse_devstring(gargv[2], &ctl, &chip);
524296417Sdim	if (err)
525296417Sdim		return (EX_USAGE);
526296417Sdim
527261991Sdim	if (chip == 0xff) {
528296417Sdim		error(MSG_CTRLCHIPNEEDED);
529296417Sdim		return (EX_USAGE);
530296417Sdim	}
531296417Sdim
532296417Sdim	if (convert_arguint(gargv[3], &page) ||
533296417Sdim	    convert_arguint(gargv[4], &column) ||
534296417Sdim	    convert_arguint(gargv[5], &len) ||
535314564Sdim	    convert_arguint(gargv[6], &pattern))
536314564Sdim		return (EX_SOFTWARE);
537296417Sdim
538296417Sdim	if (!assert_chip_connected(ctl, chip))
539296417Sdim		return (EX_SOFTWARE);
540296417Sdim
541296417Sdim	sim_err.page_num = page;
542296417Sdim	sim_err.column = column;
543296417Sdim	sim_err.len = len;
544296417Sdim	sim_err.pattern = pattern;
545296417Sdim	sim_err.ctrl_num = ctl;
546296417Sdim	sim_err.chip_num = chip;
547276479Sdim
548261991Sdim	if (opendev(&fd) != EX_OK)
549261991Sdim		return (EX_OSFILE);
550261991Sdim
551261991Sdim	err = ioctl(fd, NANDSIM_INJECT_ERROR, &sim_err);
552309124Sdim
553309124Sdim	close(fd);
554309124Sdim	if (err) {
555309124Sdim		error("Could not inject error !");
556309124Sdim		return (EX_SOFTWARE);
557261991Sdim	}
558261991Sdim	return (EX_OK);
559261991Sdim}
560261991Sdim
561261991Sdimstatic int
562261991Sdimcmdbb(int gargc, char **gargv)
563261991Sdim{
564261991Sdim	struct sim_block_state bs;
565261991Sdim	struct chip_param_io cparams;
566261991Sdim	uint32_t blkidx;
567261991Sdim	int c, cdevd, chip = 0, ctl = 0, err = 0, fd, idx;
568261991Sdim	uint8_t flagL = 0, flagU = 0;
569261991Sdim	int *badblocks = NULL;
570261991Sdim
571261991Sdim	/* Check for --list/-L or --unmark/-U flags */
572314564Sdim	for (idx = 3; idx < gargc; idx++) {
573314564Sdim		if (strcmp(gargv[idx], "--list") == 0 ||
574314564Sdim		    strcmp(gargv[idx], "-L") == 0)
575314564Sdim			flagL = idx;
576314564Sdim		if (strcmp(gargv[idx], "--unmark") == 0 ||
577314564Sdim		    strcmp(gargv[idx], "-U") == 0)
578314564Sdim			flagU = idx;
579314564Sdim	}
580314564Sdim
581314564Sdim	if (flagL == 2 || flagU == 2 || flagU == 3)
582314564Sdim		return (EX_USAGE);
583314564Sdim
584314564Sdim	err = parse_devstring(gargv[2], &ctl, &chip);
585314564Sdim	if (err) {
586314564Sdim		return (EX_USAGE);
587314564Sdim	}
588327952Sdim	if (chip == 0xff || ctl == 0xff) {
589327952Sdim		error(MSG_CTRLCHIPNEEDED);
590314564Sdim		return (EX_USAGE);
591314564Sdim	}
592314564Sdim
593314564Sdim	bs.ctrl_num = ctl;
594314564Sdim	bs.chip_num = chip;
595314564Sdim
596314564Sdim	if (!assert_chip_connected(ctl, chip))
597314564Sdim		return (EX_SOFTWARE);
598314564Sdim
599314564Sdim	if (opencdev(&cdevd, ctl, chip) != EX_OK)
600314564Sdim		return (EX_OSFILE);
601314564Sdim
602314564Sdim	err = ioctl(cdevd, NAND_IO_GET_CHIP_PARAM, &cparams);
603314564Sdim	if (err)
604314564Sdim		return (EX_SOFTWARE);
605314564Sdim
606314564Sdim	close(cdevd);
607314564Sdim
608314564Sdim	bs.ctrl_num = ctl;
609314564Sdim	bs.chip_num = chip;
610314564Sdim
611314564Sdim	if (opendev(&fd) != EX_OK)
612314564Sdim		return (EX_OSFILE);
613321369Sdim
614314564Sdim	if (flagL != 3) {
615314564Sdim		/*
616314564Sdim		 * Flag -L was specified either after blocklist or was not
617314564Sdim		 * specified at all.
618314564Sdim		 */
619314564Sdim		c = parse_intarray(gargv[3], &badblocks);
620314564Sdim
621314564Sdim		for (idx = 0; idx < c; idx++) {
622321369Sdim			bs.block_num = badblocks[idx];
623321369Sdim			/* Do not change wearout */
624321369Sdim			bs.wearout = -1;
625321369Sdim			bs.state = (flagU == 0) ? NANDSIM_BAD_BLOCK :
626321369Sdim			    NANDSIM_GOOD_BLOCK;
627321369Sdim
628321369Sdim			err = ioctl(fd, NANDSIM_SET_BLOCK_STATE, &bs);
629321369Sdim			if (err) {
630321369Sdim				error("Could not set bad block(%d) for "
631321369Sdim				    "controller (%d)!",
632321369Sdim				    badblocks[idx], ctl);
633321369Sdim				err = EX_SOFTWARE;
634321369Sdim				break;
635321369Sdim			}
636321369Sdim		}
637321369Sdim	}
638321369Sdim	if (flagL != 0) {
639321369Sdim		/* If flag -L was specified (anywhere) */
640321369Sdim		for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
641321369Sdim			bs.block_num = blkidx;
642321369Sdim			/* Do not change the wearout */
643321369Sdim			bs.wearout = -1;
644321369Sdim			err = ioctl(fd, NANDSIM_GET_BLOCK_STATE, &bs);
645321369Sdim			if (err) {
646321369Sdim				error("Could not acquire block state");
647321369Sdim				err = EX_SOFTWARE;
648321369Sdim				continue;
649321369Sdim			}
650321369Sdim			printf("Block#%d: wear count: %d %s\n", blkidx,
651321369Sdim			    bs.wearout,
652321369Sdim			    (bs.state == NANDSIM_BAD_BLOCK) ? "BAD":"GOOD");
653321369Sdim		}
654321369Sdim	}
655321369Sdim	close(fd);
656321369Sdim	return (err);
657321369Sdim}
658321369Sdim
659321369Sdimstatic int
660321369Sdimcmdfreeze(int gargc __unused, char **gargv)
661341825Sdim{
662321369Sdim	int chip = 0, ctl = 0, err = 0, fd, i, start = 0, state, stop = 0;
663321369Sdim	struct sim_ctrl_chip ctrlchip;
664321369Sdim
665360784Sdim	err = parse_devstring(gargv[2], &ctl, &chip);
666360784Sdim	if (err)
667360784Sdim		return (EX_USAGE);
668360784Sdim
669360784Sdim	if (ctl == 0xff) {
670360784Sdim		error("You have to specify at least controller number");
671360784Sdim		return (EX_USAGE);
672360784Sdim	}
673360784Sdim
674360784Sdim	if (ctl != 0xff && chip == 0xff) {
675360784Sdim		start = 0;
676360784Sdim		stop = MAX_CTRL_CS - 1;
677360784Sdim	} else {
678360784Sdim		start = chip;
679360784Sdim		stop = chip;
680360784Sdim	}
681360784Sdim
682360784Sdim	ctrlchip.ctrl_num = ctl;
683360784Sdim
684296417Sdim	err = is_ctrl_running(ctl, &state);
685296417Sdim	if (err)
686193323Sed		return (EX_SOFTWARE);
687193323Sed	if (state == 0) {
688234353Sdim		error(MSG_NOTRUNNING, ctl);
689234353Sdim		return (EX_SOFTWARE);
690193323Sed	}
691193323Sed
692296417Sdim	if (opendev(&fd) != EX_OK)
693309124Sdim		return (EX_OSFILE);
694309124Sdim
695309124Sdim	for (i = start; i <= stop; i++) {
696309124Sdim		err = is_chip_created(ctl, i, &state);
697193323Sed		if (err)
698193323Sed			return (EX_SOFTWARE);
699321369Sdim		else if (state == 0) {
700321369Sdim			continue;
701234353Sdim		}
702234353Sdim
703234353Sdim		ctrlchip.chip_num = i;
704234353Sdim		err = ioctl(fd, NANDSIM_FREEZE, &ctrlchip);
705296417Sdim		if (err) {
706296417Sdim			error("Could not freeze ctrl#%d chip#%d", ctl, i);
707234353Sdim			close(fd);
708321369Sdim			return (EX_SOFTWARE);
709234353Sdim		}
710234353Sdim	}
711234353Sdim	close(fd);
712234353Sdim	return (EX_OK);
713234353Sdim}
714234353Sdim
715234353Sdimstatic int
716296417Sdimcmdlog(int gargc __unused, char **gargv)
717296417Sdim{
718296417Sdim	struct sim_log log;
719296417Sdim	int chip = 0, ctl = 0, err = 0, fd, idx, start = 0, stop = 0;
720261991Sdim	char *logbuf;
721261991Sdim
722234353Sdim	err = parse_devstring(gargv[2], &ctl, &chip);
723296417Sdim	if (err)
724234353Sdim		return (EX_USAGE);
725234353Sdim
726234353Sdim	logbuf = (char *)malloc(sizeof(char) * NANDSIM_RAM_LOG_SIZE);
727321369Sdim	if (logbuf == NULL) {
728234353Sdim		error("Not enough memory to create log buffer");
729234353Sdim		return (EX_SOFTWARE);
730234353Sdim	}
731234353Sdim
732234353Sdim	memset(logbuf, 0, NANDSIM_RAM_LOG_SIZE);
733234353Sdim	log.log = logbuf;
734296417Sdim	log.len = NANDSIM_RAM_LOG_SIZE;
735309124Sdim
736309124Sdim	if (ctl == 0xff) {
737309124Sdim		start = 0;
738261991Sdim		stop = MAX_SIM_DEV-1;
739261991Sdim	} else {
740234353Sdim		start = ctl;
741234353Sdim		stop = ctl;
742234353Sdim	}
743234353Sdim
744261991Sdim	if (opendev(&fd) != EX_OK) {
745261991Sdim		free(logbuf);
746261991Sdim		return (EX_OSFILE);
747261991Sdim	}
748261991Sdim
749296417Sdim	/* Print logs for selected controller(s) */
750296417Sdim	for (idx = start; idx <= stop; idx++) {
751296417Sdim		log.ctrl_num = idx;
752360784Sdim
753360784Sdim		err = ioctl(fd, NANDSIM_PRINT_LOG, &log);
754261991Sdim		if (err) {
755261991Sdim			error("Could not get log for controller %d!", idx);
756234353Sdim			continue;
757234353Sdim		}
758234353Sdim
759234353Sdim		printf("Logs for controller#%d:\n%s\n", idx, logbuf);
760234353Sdim	}
761234353Sdim
762234353Sdim	free(logbuf);
763234353Sdim	close(fd);
764234353Sdim	return (EX_OK);
765296417Sdim}
766276479Sdim
767234353Sdimstatic int
768296417Sdimcmdstats(int gargc __unused, char **gargv)
769296417Sdim{
770234353Sdim	int cdevd, chip = 0, ctl = 0, err = 0;
771234353Sdim	uint32_t pageno = 0;
772234353Sdim
773234353Sdim	err = parse_devstring(gargv[2], &ctl, &chip);
774261991Sdim
775321369Sdim	if (err)
776193323Sed		return (EX_USAGE);
777193323Sed
778193323Sed	if (chip == 0xff) {
779234353Sdim		error(MSG_CTRLCHIPNEEDED);
780234353Sdim		return (EX_USAGE);
781234353Sdim	}
782234353Sdim
783276479Sdim	if (convert_arguint(gargv[3], &pageno) != 0)
784276479Sdim		return (EX_USAGE);
785234353Sdim
786234353Sdim	if (!assert_chip_connected(ctl, chip))
787234353Sdim		return (EX_SOFTWARE);
788234353Sdim
789234353Sdim	if (opencdev(&cdevd, ctl, chip) != EX_OK)
790234353Sdim		return (EX_OSFILE);
791296417Sdim
792296417Sdim	err = printstats(ctl, chip, pageno, cdevd);
793234353Sdim	if (err) {
794296417Sdim		close(cdevd);
795234353Sdim		return (EX_SOFTWARE);
796234353Sdim	}
797309124Sdim	close(cdevd);
798309124Sdim	return (EX_OK);
799234353Sdim}
800234353Sdim
801276479Sdimstatic int
802276479Sdimcmddump(int gargc __unused, char **gargv)
803234353Sdim{
804309124Sdim	struct sim_dump dump;
805309124Sdim	struct sim_block_state bs;
806234353Sdim	struct chip_param_io cparams;
807234353Sdim	int chip = 0, ctl = 0, err = EX_OK, fd, dumpfd;
808234353Sdim	uint32_t blkidx, bwritten = 0, totalwritten = 0;
809234353Sdim	void *buf;
810234353Sdim
811234353Sdim	err = parse_devstring(gargv[2], &ctl, &chip);
812296417Sdim	if (err)
813296417Sdim		return (EX_USAGE);
814234353Sdim
815234353Sdim	if (chip == 0xff || ctl == 0xff) {
816234353Sdim		error(MSG_CTRLCHIPNEEDED);
817321369Sdim		return (EX_USAGE);
818234353Sdim	}
819234353Sdim
820234353Sdim	if (!assert_chip_connected(ctl, chip))
821261991Sdim		return (EX_SOFTWARE);
822261991Sdim
823261991Sdim	if (opencdev(&fd, ctl, chip) != EX_OK)
824261991Sdim		return (EX_OSFILE);
825261991Sdim
826261991Sdim	err = ioctl(fd, NAND_IO_GET_CHIP_PARAM, &cparams);
827261991Sdim	if (err) {
828261991Sdim		error("Cannot get parameters for chip %d:%d", ctl, chip);
829261991Sdim		close(fd);
830261991Sdim		return (EX_SOFTWARE);
831261991Sdim	}
832261991Sdim	close(fd);
833261991Sdim
834261991Sdim	dump.ctrl_num = ctl;
835261991Sdim	dump.chip_num = chip;
836261991Sdim
837261991Sdim	dump.len = cparams.pages_per_block * (cparams.page_size +
838261991Sdim	    cparams.oob_size);
839261991Sdim
840261991Sdim	buf = malloc(dump.len);
841261991Sdim	if (buf == NULL) {
842261991Sdim		error("Could not allocate memory!");
843261991Sdim		return (EX_SOFTWARE);
844261991Sdim	}
845261991Sdim	dump.data = buf;
846261991Sdim
847261991Sdim	errno = 0;
848261991Sdim	dumpfd = open(gargv[3], O_WRONLY | O_CREAT, 0666);
849261991Sdim	if (dumpfd == -1) {
850360784Sdim		error("Cannot create dump file.");
851261991Sdim		free(buf);
852261991Sdim		return (EX_SOFTWARE);
853193323Sed	}
854193323Sed
855193323Sed	if (opendev(&fd)) {
856193323Sed		close(dumpfd);
857193323Sed		free(buf);
858296417Sdim		return (EX_SOFTWARE);
859296417Sdim	}
860296417Sdim
861296417Sdim	bs.ctrl_num = ctl;
862296417Sdim	bs.chip_num = chip;
863243830Sdim
864309124Sdim	/* First uint32_t in file shall contain block count */
865309124Sdim	if (write(dumpfd, &cparams, sizeof(cparams)) < (int)sizeof(cparams)) {
866193323Sed		error("Error writing to dumpfile!");
867193323Sed		close(fd);
868193323Sed		close(dumpfd);
869243830Sdim		free(buf);
870193323Sed		return (EX_SOFTWARE);
871193323Sed	}
872193323Sed
873193323Sed	/*
874193323Sed	 * First loop acquires blocks states and writes them to
875193323Sed	 * the dump file.
876193323Sed	 */
877193323Sed	for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
878193323Sed		bs.block_num = blkidx;
879193323Sed		err = ioctl(fd, NANDSIM_GET_BLOCK_STATE, &bs);
880193323Sed		if (err) {
881193323Sed			error("Could not get bad block(%d) for "
882193323Sed			    "controller (%d)!", blkidx, ctl);
883296417Sdim			close(fd);
884296417Sdim			close(dumpfd);
885296417Sdim			free(buf);
886296417Sdim			return (EX_SOFTWARE);
887296417Sdim		}
888296417Sdim
889296417Sdim		bwritten = write(dumpfd, &bs, sizeof(bs));
890296417Sdim		if (bwritten != sizeof(bs)) {
891296417Sdim			error("Error writing to dumpfile");
892296417Sdim			close(fd);
893296417Sdim			close(dumpfd);
894296417Sdim			free(buf);
895296417Sdim			return (EX_SOFTWARE);
896296417Sdim		}
897296417Sdim	}
898296417Sdim
899296417Sdim	/* Second loop dumps the data */
900296417Sdim	for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
901193323Sed		debug("Block#%d...", blkidx);
902296417Sdim		dump.block_num = blkidx;
903296417Sdim
904296417Sdim		err = ioctl(fd, NANDSIM_DUMP, &dump);
905296417Sdim		if (err) {
906296417Sdim			error("Could not dump ctrl#%d chip#%d "
907296417Sdim			    "block#%d", ctl, chip, blkidx);
908321369Sdim			err = EX_SOFTWARE;
909193323Sed			break;
910296417Sdim		}
911296417Sdim
912314564Sdim		bwritten = write(dumpfd, dump.data, dump.len);
913314564Sdim		if (bwritten != dump.len) {
914296417Sdim			error("Error writing to dumpfile");
915296417Sdim			err = EX_SOFTWARE;
916193323Sed			break;
917193323Sed		}
918199989Srdivacky		debug("OK!\n");
919193323Sed		totalwritten += bwritten;
920193323Sed	}
921193323Sed	printf("%d out of %d B written.\n", totalwritten, dump.len * blkidx);
922193323Sed
923193323Sed	close(fd);
924193323Sed	close(dumpfd);
925296417Sdim	free(buf);
926296417Sdim	return (err);
927193323Sed}
928193323Sed
929296417Sdimstatic int
930193323Sedcmdrestore(int gargc __unused, char **gargv)
931321369Sdim{
932193323Sed	struct sim_dump dump;
933193323Sed	struct sim_block_state bs;
934309124Sdim	struct stat filestat;
935309124Sdim	int chip = 0, ctl = 0, err = 0, fd, dumpfd = -1;
936309124Sdim	uint32_t blkidx, blksz, fsize = 0, expfilesz;
937309124Sdim	void *buf;
938193323Sed	struct chip_param_io cparams, dumpcparams;
939193323Sed
940296417Sdim	err = parse_devstring(gargv[2], &ctl, &chip);
941193323Sed	if (err)
942204642Srdivacky		return (EX_USAGE);
943193323Sed	else if (ctl == 0xff) {
944193323Sed		error(MSG_CTRLCHIPNEEDED);
945296417Sdim		return (EX_USAGE);
946193323Sed	}
947193323Sed
948193323Sed	if (!assert_chip_connected(ctl, chip))
949193323Sed		return (EX_SOFTWARE);
950296417Sdim
951321369Sdim	/* Get chip geometry */
952321369Sdim	if (opencdev(&fd, ctl, chip) != EX_OK)
953193323Sed		return (EX_OSFILE);
954193323Sed
955321369Sdim	err = ioctl(fd, NAND_IO_GET_CHIP_PARAM, &cparams);
956193323Sed	if (err) {
957193323Sed		error("Cannot get parameters for chip %d:%d", ctl, chip);
958193323Sed		close(fd);
959193323Sed		return (err);
960193323Sed	}
961193323Sed	close(fd);
962193323Sed
963296417Sdim	/* Obtain dump file size */
964296417Sdim	errno = 0;
965296417Sdim	if (stat(gargv[3], &filestat) != 0) {
966296417Sdim		error("Could not acquire file size! : %s",
967296417Sdim		    strerror(errno));
968296417Sdim		return (EX_IOERR);
969296417Sdim	}
970296417Sdim
971309124Sdim	fsize = filestat.st_size;
972296417Sdim	blksz = cparams.pages_per_block * (cparams.page_size +
973296417Sdim	    cparams.oob_size);
974296417Sdim
975280031Sdim	/* Expected dump file size for chip */
976296417Sdim	expfilesz = cparams.blocks * (blksz + sizeof(bs)) + sizeof(cparams);
977296417Sdim
978296417Sdim	if (fsize != expfilesz) {
979296417Sdim		error("File size does not match chip geometry (file size: %d"
980249423Sdim		    ", dump size: %d)", fsize, expfilesz);
981327952Sdim		return (EX_SOFTWARE);
982327952Sdim	}
983296417Sdim
984296417Sdim	dumpfd = open(gargv[3], O_RDONLY);
985296417Sdim	if (dumpfd == -1) {
986296417Sdim		error("Could not open dump file!");
987327952Sdim		return (EX_IOERR);
988296417Sdim	}
989296417Sdim
990296417Sdim	/* Read chip params saved in dumpfile */
991296417Sdim	read(dumpfd, &dumpcparams, sizeof(dumpcparams));
992296417Sdim
993296417Sdim	/* XXX */
994249423Sdim	if (bcmp(&dumpcparams, &cparams, sizeof(cparams)) != 0) {
995296417Sdim		error("Supplied dump is created for a chip with different "
996296417Sdim		    "chip configuration!");
997296417Sdim		close(dumpfd);
998296417Sdim		return (EX_SOFTWARE);
999296417Sdim	}
1000296417Sdim
1001296417Sdim	if (opendev(&fd) != EX_OK) {
1002296417Sdim		close(dumpfd);
1003296417Sdim		return (EX_OSFILE);
1004296417Sdim	}
1005296417Sdim
1006296417Sdim	buf = malloc(blksz);
1007296417Sdim	if (buf == NULL) {
1008296417Sdim		error("Could not allocate memory for block buffer");
1009296417Sdim		close(dumpfd);
1010296417Sdim		close(fd);
1011296417Sdim		return (EX_SOFTWARE);
1012296417Sdim	}
1013296417Sdim
1014296417Sdim	dump.ctrl_num = ctl;
1015296417Sdim	dump.chip_num = chip;
1016296417Sdim	dump.data = buf;
1017296417Sdim	/* Restore block states and wearouts */
1018296417Sdim	for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
1019296417Sdim		dump.block_num = blkidx;
1020296417Sdim		if (read(dumpfd, &bs, sizeof(bs)) != sizeof(bs)) {
1021296417Sdim			error("Error reading dumpfile");
1022296417Sdim			close(dumpfd);
1023296417Sdim			close(fd);
1024249423Sdim			free(buf);
1025296417Sdim			return (EX_SOFTWARE);
1026296417Sdim		}
1027296417Sdim		bs.ctrl_num = ctl;
1028296417Sdim		bs.chip_num = chip;
1029296417Sdim		debug("BLKIDX=%d BLOCKS=%d CTRL=%d CHIP=%d STATE=%d\n"
1030249423Sdim		    "WEAROUT=%d BS.CTRL_NUM=%d BS.CHIP_NUM=%d\n",
1031249423Sdim		    blkidx, cparams.blocks, dump.ctrl_num, dump.chip_num,
1032249423Sdim		    bs.state, bs.wearout, bs.ctrl_num, bs.chip_num);
1033249423Sdim
1034249423Sdim		err = ioctl(fd, NANDSIM_SET_BLOCK_STATE, &bs);
1035296417Sdim		if (err) {
1036309124Sdim			error("Could not set bad block(%d) for "
1037296417Sdim			    "controller: %d, chip: %d!", blkidx, ctl, chip);
1038296417Sdim			close(dumpfd);
1039296417Sdim			close(fd);
1040296417Sdim			free(buf);
1041249423Sdim			return (EX_SOFTWARE);
1042249423Sdim		}
1043296417Sdim	}
1044296417Sdim	/* Restore data */
1045296417Sdim	for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
1046296417Sdim		errno = 0;
1047321369Sdim		dump.len = read(dumpfd, buf, blksz);
1048296417Sdim		if (errno) {
1049296417Sdim			error("Failed to read block#%d from dumpfile.", blkidx);
1050249423Sdim			err = EX_SOFTWARE;
1051309124Sdim			break;
1052309124Sdim		}
1053309124Sdim		dump.block_num = blkidx;
1054309124Sdim		err = ioctl(fd, NANDSIM_RESTORE, &dump);
1055296417Sdim		if (err) {
1056296417Sdim			error("Could not restore block#%d of ctrl#%d chip#%d"
1057296417Sdim			    ": %s", blkidx, ctl, chip, strerror(errno));
1058296417Sdim			err = EX_SOFTWARE;
1059296417Sdim			break;
1060296417Sdim		}
1061296417Sdim	}
1062296417Sdim
1063309124Sdim	free(buf);
1064296417Sdim	close(dumpfd);
1065296417Sdim	close(fd);
1066296417Sdim	return (err);
1067341825Sdim
1068341825Sdim}
1069321369Sdim
1070296417Sdimstatic int
1071296417Sdimcmddestroy(int gargc __unused, char **gargv)
1072296417Sdim{
1073296417Sdim	int chip = 0, ctl = 0, err = 0, fd, idx, idx2, state;
1074296417Sdim	int chipstart, chipstop, ctrlstart, ctrlstop;
1075296417Sdim	struct sim_chip_destroy chip_destroy;
1076296417Sdim
1077296417Sdim	err = parse_devstring(gargv[2], &ctl, &chip);
1078249423Sdim
1079249423Sdim	if (err)
1080296417Sdim		return (EX_USAGE);
1081296417Sdim
1082321369Sdim	if (ctl == 0xff) {
1083296417Sdim		/* Every chip at every controller */
1084296417Sdim		ctrlstart = chipstart = 0;
1085296417Sdim		ctrlstop = MAX_SIM_DEV - 1;
1086296417Sdim		chipstop = MAX_CTRL_CS - 1;
1087341825Sdim	} else {
1088321369Sdim		ctrlstart = ctrlstop = ctl;
1089296417Sdim		if (chip == 0xff) {
1090296417Sdim			/* Every chip at selected controller */
1091296417Sdim			chipstart = 0;
1092296417Sdim			chipstop = MAX_CTRL_CS - 1;
1093296417Sdim		} else
1094249423Sdim			/* Selected chip at selected controller */
1095249423Sdim			chipstart = chipstop = chip;
1096249423Sdim	}
1097341825Sdim	debug("CTRLSTART=%d CTRLSTOP=%d CHIPSTART=%d CHIPSTOP=%d\n",
1098341825Sdim	    ctrlstart, ctrlstop, chipstart, chipstop);
1099341825Sdim	for (idx = ctrlstart; idx <= ctrlstop; idx++) {
1100341825Sdim		err = is_ctrl_created(idx, &state);
1101341825Sdim		if (err) {
1102341825Sdim			error("Could not acquire ctrl#%d state. Cannot "
1103341825Sdim			    "destroy controller.", idx);
1104341825Sdim			return (EX_SOFTWARE);
1105341825Sdim		}
1106341825Sdim		if (state == 0) {
1107341825Sdim			continue;
1108341825Sdim		}
1109341825Sdim		err = is_ctrl_running(idx, &state);
1110341825Sdim		if (err) {
1111341825Sdim			error(MSG_STATUSACQCTRL, idx);
1112341825Sdim			return (EX_SOFTWARE);
1113341825Sdim		}
1114341825Sdim		if (state != 0) {
1115341825Sdim			error(MSG_RUNNING, ctl);
1116341825Sdim			return (EX_SOFTWARE);
1117341825Sdim		}
1118341825Sdim		if (opendev(&fd) != EX_OK)
1119341825Sdim			return (EX_OSFILE);
1120341825Sdim
1121341825Sdim		for (idx2 = chipstart; idx2 <= chipstop; idx2++) {
1122341825Sdim			err = is_chip_created(idx, idx2, &state);
1123341825Sdim			if (err) {
1124341825Sdim				error(MSG_STATUSACQCTRLCHIP, idx2, idx);
1125341825Sdim				continue;
1126341825Sdim			}
1127341825Sdim			if (state == 0)
1128341825Sdim				/* There is no such chip running */
1129341825Sdim				continue;
1130341825Sdim			chip_destroy.ctrl_num = idx;
1131341825Sdim			chip_destroy.chip_num = idx2;
1132341825Sdim			ioctl(fd, NANDSIM_DESTROY_CHIP,
1133341825Sdim			    &chip_destroy);
1134341825Sdim		}
1135341825Sdim		/* If chip isn't explicitly specified -- destroy ctrl */
1136341825Sdim		if (chip == 0xff) {
1137341825Sdim			err = ioctl(fd, NANDSIM_DESTROY_CTRL, &idx);
1138341825Sdim			if (err) {
1139341825Sdim				error("Could not destroy ctrl#%d", idx);
1140341825Sdim				continue;
1141341825Sdim			}
1142341825Sdim		}
1143341825Sdim		close(fd);
1144341825Sdim	}
1145341825Sdim	return (err);
1146341825Sdim}
1147341825Sdim
1148341825Sdimint
1149341825Sdimmain(int argc, char **argv)
1150341825Sdim{
1151341825Sdim	struct nandsim_command *cmdopts;
1152341825Sdim	int retcode = 0;
1153341825Sdim
1154341825Sdim	if (argc < 2) {
1155341825Sdim		cmdhelp(argc, argv);
1156341825Sdim		retcode = EX_USAGE;
1157309124Sdim	} else {
1158309124Sdim		cmdopts = getcommand(argv[1]);
1159341825Sdim		if (cmdopts != NULL && cmdopts->commandfunc != NULL) {
1160341825Sdim			if (checkusage(argc, cmdopts->req_argc, argv) == 1) {
1161341825Sdim				/* Print command specific usage */
1162309124Sdim				printf("nandsim %s", cmdopts->usagestring);
1163341825Sdim				return (EX_USAGE);
1164341825Sdim			}
1165341825Sdim			retcode = cmdopts->commandfunc(argc, argv);
1166341825Sdim
1167341825Sdim			if (retcode == EX_USAGE) {
1168341825Sdim				/* Print command-specific usage */
1169341825Sdim				printf("nandsim %s", cmdopts->usagestring);
1170341825Sdim			} else if (retcode == EX_OSFILE) {
1171341825Sdim				error("Could not open device file");
1172341825Sdim			}
1173341825Sdim
1174341825Sdim		} else {
1175341825Sdim			error("Unknown command!");
1176341825Sdim			retcode = EX_USAGE;
1177341825Sdim		}
1178341825Sdim	}
1179341825Sdim	return (retcode);
1180341825Sdim}
1181341825Sdim
1182341825Sdimstatic int
1183341825Sdimcmdhelp(int gargc __unused, char **gargv __unused)
1184341825Sdim{
1185309124Sdim	struct nandsim_command *opts;
1186341825Sdim
1187341825Sdim	printf("usage:  nandsim <command> [command params] [params]\n\n");
1188341825Sdim
1189341825Sdim	for (opts = commands; (opts != NULL) &&
1190341825Sdim	    (opts->cmd_name != NULL); opts++)
1191341825Sdim		printf("nandsim %s", opts->usagestring);
1192341825Sdim
1193341825Sdim	printf("\n");
1194341825Sdim	return (EX_OK);
1195341825Sdim}
1196341825Sdim
1197341825Sdimstatic void
1198341825Sdimprintchip(struct sim_chip *chip, uint8_t verbose)
1199341825Sdim{
1200309124Sdim
1201309124Sdim	if (chip->created == 0)
1202309124Sdim		return;
1203341825Sdim	if (verbose > 0) {
1204341825Sdim		printf("\n[Chip info]\n");
1205309124Sdim		printf("num= %d\nctrl_num=%d\ndevice_id=%02x"
1206341825Sdim		    "\tmanufacturer_id=%02x\ndevice_model=%s\nmanufacturer="
1207341825Sdim		    "%s\ncol_addr_cycles=%d\nrow_addr_cycles=%d"
1208341825Sdim		    "\npage_size=%d\noob_size=%d\npages_per_block=%d\n"
1209341825Sdim		    "blocks_per_lun=%d\nluns=%d\n\nprog_time=%d\n"
1210341825Sdim		    "erase_time=%d\nread_time=%d\n"
1211341825Sdim		    "error_ratio=%d\nwear_level=%d\nwrite_protect=%c\n"
1212341825Sdim		    "chip_width=%db\n", chip->num, chip->ctrl_num,
1213341825Sdim		    chip->device_id, chip->manufact_id,chip->device_model,
1214341825Sdim		    chip->manufacturer, chip->col_addr_cycles,
1215341825Sdim		    chip->row_addr_cycles, chip->page_size,
1216341825Sdim		    chip->oob_size, chip->pgs_per_blk, chip->blks_per_lun,
1217341825Sdim		    chip->luns,chip->prog_time, chip->erase_time,
1218341825Sdim		    chip->read_time, chip->error_ratio, chip->wear_level,
1219341825Sdim		    (chip->is_wp == 0) ? 'N':'Y', chip->width);
1220309124Sdim	} else {
1221341825Sdim		printf("[Chip info]\n");
1222341825Sdim		printf("\tnum=%d\n\tdevice_model=%s\n\tmanufacturer=%s\n"
1223341825Sdim		    "\tpage_size=%d\n\twrite_protect=%s\n",
1224341825Sdim		    chip->num, chip->device_model, chip->manufacturer,
1225341825Sdim		    chip->page_size, (chip->is_wp == 0) ? "NO":"YES");
1226341825Sdim	}
1227341825Sdim}
1228341825Sdim
1229341825Sdimstatic void
1230341825Sdimprintctrl(struct sim_ctrl *ctrl)
1231341825Sdim{
1232341825Sdim	int i;
1233341825Sdim
1234341825Sdim	if (ctrl->created == 0) {
1235341825Sdim		printf(MSG_NOCTRL "\n", ctrl->num);
1236341825Sdim		return;
1237341825Sdim	}
1238341825Sdim	printf("\n[Controller info]\n");
1239341825Sdim	printf("\trunning: %s\n", ctrl->running ? "yes" : "no");
1240341825Sdim	printf("\tnum cs: %d\n", ctrl->num_cs);
1241341825Sdim	printf("\tecc: %d\n", ctrl->ecc);
1242341825Sdim	printf("\tlog_filename: %s\n", ctrl->filename);
1243341825Sdim	printf("\tecc_layout:");
1244309124Sdim	for (i = 0; i < MAX_ECC_BYTES; i++) {
1245309124Sdim		if (ctrl->ecc_layout[i] == 0xffff)
1246309124Sdim			break;
1247309124Sdim		else
1248353358Sdim			printf("%c%d", i%16 ? ' ' : '\n',
1249353358Sdim			    ctrl->ecc_layout[i]);
1250353358Sdim	}
1251353358Sdim	printf("\n");
1252353358Sdim}
1253353358Sdim
1254353358Sdimstatic int
1255353358Sdimis_ctrl_running(int ctrl_no, int *running)
1256353358Sdim{
1257353358Sdim	struct sim_ctrl ctrl;
1258353358Sdim	int err, fd;
1259353358Sdim
1260353358Sdim	ctrl.num = ctrl_no;
1261353358Sdim	if (opendev(&fd) != EX_OK)
1262353358Sdim		return (EX_OSFILE);
1263353358Sdim
1264353358Sdim	err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrl);
1265353358Sdim	if (err) {
1266353358Sdim		error(MSG_STATUSACQCTRL, ctrl_no);
1267341825Sdim		close(fd);
1268341825Sdim		return (err);
1269341825Sdim	}
1270341825Sdim	*running = ctrl.running;
1271341825Sdim	close(fd);
1272341825Sdim	return (0);
1273341825Sdim}
1274341825Sdim
1275341825Sdimstatic int
1276341825Sdimis_ctrl_created(int ctrl_no, int *created)
1277341825Sdim{
1278341825Sdim	struct sim_ctrl ctrl;
1279341825Sdim	int err, fd;
1280341825Sdim
1281341825Sdim	ctrl.num = ctrl_no;
1282341825Sdim
1283341825Sdim	if (opendev(&fd) != EX_OK)
1284341825Sdim		return (EX_OSFILE);
1285341825Sdim
1286341825Sdim	err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrl);
1287341825Sdim	if (err) {
1288341825Sdim		error("Could not acquire conf for ctrl#%d", ctrl_no);
1289341825Sdim		close(fd);
1290341825Sdim		return (err);
1291341825Sdim	}
1292341825Sdim	*created = ctrl.created;
1293341825Sdim	close(fd);
1294341825Sdim	return (0);
1295341825Sdim}
1296341825Sdim
1297341825Sdimstatic int
1298341825Sdimis_chip_created(int ctrl_no, int chip_no, int *created)
1299341825Sdim{
1300341825Sdim	struct sim_chip chip;
1301341825Sdim	int err, fd;
1302341825Sdim
1303341825Sdim	chip.ctrl_num = ctrl_no;
1304341825Sdim	chip.num = chip_no;
1305341825Sdim
1306341825Sdim	if (opendev(&fd) != EX_OK)
1307341825Sdim		return (EX_OSFILE);
1308341825Sdim
1309341825Sdim	err = ioctl(fd, NANDSIM_STATUS_CHIP, &chip);
1310341825Sdim	if (err) {
1311341825Sdim		error("Could not acquire conf for chip#%d", chip_no);
1312341825Sdim		close(fd);
1313341825Sdim		return (err);
1314341825Sdim	}
1315341825Sdim	*created = chip.created;
1316341825Sdim	close(fd);
1317341825Sdim	return (0);
1318341825Sdim}
1319341825Sdim
1320353358Sdimstatic int
1321353358Sdimassert_chip_connected(int ctrl_no, int chip_no)
1322353358Sdim{
1323353358Sdim	int created, running;
1324353358Sdim
1325353358Sdim	if (is_ctrl_created(ctrl_no, &created))
1326353358Sdim		return (0);
1327353358Sdim
1328353358Sdim	if (!created) {
1329353358Sdim		error(MSG_NOCTRL, ctrl_no);
1330353358Sdim		return (0);
1331353358Sdim	}
1332353358Sdim
1333353358Sdim	if (is_chip_created(ctrl_no, chip_no, &created))
1334353358Sdim		return (0);
1335353358Sdim
1336353358Sdim	if (!created) {
1337353358Sdim		error(MSG_NOTCONFIGDCTRLCHIP, ctrl_no, chip_no);
1338353358Sdim		return (0);
1339353358Sdim	}
1340353358Sdim
1341353358Sdim	if (is_ctrl_running(ctrl_no, &running))
1342353358Sdim		return (0);
1343341825Sdim
1344341825Sdim	if (!running) {
1345341825Sdim		error(MSG_NOTRUNNING, ctrl_no);
1346341825Sdim		return (0);
1347296417Sdim	}
1348296417Sdim
1349296417Sdim	return (1);
1350296417Sdim}
1351296417Sdim
1352296417Sdimstatic int
1353296417Sdimprintstats(int ctrlno, int chipno, uint32_t pageno, int cdevd)
1354296417Sdim{
1355309124Sdim	struct page_stat_io pstats;
1356296417Sdim	struct block_stat_io bstats;
1357296417Sdim	struct chip_param_io cparams;
1358296417Sdim	uint32_t blkidx;
1359309124Sdim	int err;
1360296417Sdim
1361296417Sdim	/* Gather information about chip */
1362309124Sdim	err = ioctl(cdevd, NAND_IO_GET_CHIP_PARAM, &cparams);
1363353358Sdim
1364296417Sdim	if (err) {
1365296417Sdim		error("Could not acquire chip info for chip attached to cs#"
1366296417Sdim		    "%d, ctrl#%d", chipno, ctrlno);
1367296417Sdim		return (EX_SOFTWARE);
1368296417Sdim	}
1369344779Sdim
1370344779Sdim	blkidx = (pageno / cparams.pages_per_block);
1371344779Sdim	bstats.block_num = blkidx;
1372344779Sdim
1373344779Sdim	err = ioctl(cdevd, NAND_IO_BLOCK_STAT, &bstats);
1374344779Sdim	if (err) {
1375344779Sdim		error("Could not acquire block#%d statistics!", blkidx);
1376344779Sdim		return (ENXIO);
1377296417Sdim	}
1378309124Sdim
1379309124Sdim	printf("Block #%d erased: %d\n", blkidx, bstats.block_erased);
1380309124Sdim	pstats.page_num = pageno;
1381309124Sdim
1382296417Sdim	err = ioctl(cdevd, NAND_IO_PAGE_STAT, &pstats);
1383296417Sdim	if (err) {
1384344779Sdim		error("Could not acquire page statistics!");
1385353358Sdim		return (ENXIO);
1386353358Sdim	}
1387344779Sdim
1388344779Sdim	debug("BLOCKIDX = %d PAGENO (REL. TO BLK) = %d\n", blkidx,
1389344779Sdim	    pstats.page_num);
1390344779Sdim
1391344779Sdim	printf("Page#%d : reads:%d writes:%d \n\traw reads:%d raw writes:%d "
1392344779Sdim	    "\n\tecc_succeeded:%d ecc_corrected:%d ecc_failed:%d\n",
1393344779Sdim	    pstats.page_num, pstats.page_read, pstats.page_written,
1394344779Sdim	    pstats.page_raw_read, pstats.page_raw_written,
1395344779Sdim	    pstats.ecc_succeded, pstats.ecc_corrected, pstats.ecc_failed);
1396344779Sdim	return (0);
1397344779Sdim}
1398344779Sdim