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