summaryrefslogtreecommitdiffstats
path: root/usr.sbin
diff options
context:
space:
mode:
authorgber <gber@FreeBSD.org>2012-05-17 10:11:18 +0000
committergber <gber@FreeBSD.org>2012-05-17 10:11:18 +0000
commit6f7c7353004e2ff9709b326a4008ce8ea63d9270 (patch)
treea325137a898341311de8641f7212e28b7d87950e /usr.sbin
parent661b9d94414ea6d11d5b7960aef1f172975ce52b (diff)
downloadFreeBSD-src-6f7c7353004e2ff9709b326a4008ce8ea63d9270.zip
FreeBSD-src-6f7c7353004e2ff9709b326a4008ce8ea63d9270.tar.gz
Import work done under project/nand (@235533) into head.
The NAND Flash environment consists of several distinct components: - NAND framework (drivers harness for NAND controllers and NAND chips) - NAND simulator (NANDsim) - NAND file system (NAND FS) - Companion tools and utilities - Documentation (manual pages) This work is still experimental. Please use with caution. Obtained from: Semihalf Supported by: FreeBSD Foundation, Juniper Networks
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/Makefile5
-rw-r--r--usr.sbin/nandsim/Makefile8
-rw-r--r--usr.sbin/nandsim/nandsim.8230
-rw-r--r--usr.sbin/nandsim/nandsim.c1397
-rw-r--r--usr.sbin/nandsim/nandsim_cfgparse.c957
-rw-r--r--usr.sbin/nandsim/nandsim_cfgparse.h86
-rw-r--r--usr.sbin/nandsim/nandsim_rcfile.c440
-rw-r--r--usr.sbin/nandsim/nandsim_rcfile.h70
-rw-r--r--usr.sbin/nandsim/sample.conf174
-rw-r--r--usr.sbin/nandtool/Makefile11
-rw-r--r--usr.sbin/nandtool/nand_erase.c114
-rw-r--r--usr.sbin/nandtool/nand_info.c86
-rw-r--r--usr.sbin/nandtool/nand_read.c139
-rw-r--r--usr.sbin/nandtool/nand_readoob.c111
-rw-r--r--usr.sbin/nandtool/nand_write.c143
-rw-r--r--usr.sbin/nandtool/nand_writeoob.c113
-rw-r--r--usr.sbin/nandtool/nandtool.8188
-rw-r--r--usr.sbin/nandtool/nandtool.c283
-rw-r--r--usr.sbin/nandtool/nandtool.h57
-rw-r--r--usr.sbin/nandtool/usage.h112
20 files changed, 4724 insertions, 0 deletions
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
index 68b82e7..ded1725 100644
--- a/usr.sbin/Makefile
+++ b/usr.sbin/Makefile
@@ -223,6 +223,11 @@ SUBDIR+= lpr
SUBDIR+= manctl
.endif
+.if ${MK_NAND} != "no"
+SUBDIR+= nandsim
+SUBDIR+= nandtool
+.endif
+
.if ${MK_NETGRAPH} != "no"
SUBDIR+= flowctl
SUBDIR+= lmcconfig
diff --git a/usr.sbin/nandsim/Makefile b/usr.sbin/nandsim/Makefile
new file mode 100644
index 0000000..9269ab5
--- /dev/null
+++ b/usr.sbin/nandsim/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= nandsim
+SRCS= nandsim.c nandsim_rcfile.c nandsim_cfgparse.c
+BINDIR= /usr/sbin
+MAN= nandsim.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nandsim/nandsim.8 b/usr.sbin/nandsim/nandsim.8
new file mode 100644
index 0000000..5e86299
--- /dev/null
+++ b/usr.sbin/nandsim/nandsim.8
@@ -0,0 +1,230 @@
+.\" Copyright (c) 2010 Semihalf
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 10, 2010
+.Dt NANDSIM 8
+.Os
+.Sh NAME
+.Nm nandsim
+.Nd NAND simulator control program
+.Sh SYNOPSIS
+.Nm
+.Ic status
+.Aq ctrl_no | Fl -all | Fl a
+.Op Fl v
+.Nm
+.Ic conf
+.Aq filename
+.Nm
+.Ic start
+.Aq ctrl_no
+.Nm
+.Ic mod
+.Aq ctrl_no:cs_no | Fl l Aq loglevel
+.Op Fl p Aq prog_time
+.Op Fl e Aq erase_time
+.Op Fl r Aq read_time
+.Op Fl E Aq error_ratio
+.Op Fl h
+.Nm
+.Ic stop
+.Aq ctrl_no
+.Nm
+.Ic error
+.Aq ctrl_no:cs_no
+.Aq page_num
+.Aq column
+.Aq length
+.Aq pattern
+.Nm
+.Ic bb
+.Aq ctrl_no:cs_no
+.Op blk_num,blk_num2,...
+.Op Fl U
+.Op Fl L
+.Nm
+.Ic freeze
+.Op ctrl_no
+.Nm
+.Ic log
+.Aq ctrl_no | Fl -all | Fl a
+.Nm
+.Ic stats
+.Aq ctrl_no:cs_no
+.Aq page_num
+.Nm
+.Ic dump
+.Aq ctrl_no:cs_no
+.Aq filename
+.Nm
+.Ic restore
+.Aq ctrl_no:chip_no
+.Aq filename
+.Nm
+.Ic destroy
+.Aq ctrl_no[:cs_no] | Fl -all | Fl a
+.Nm
+.Ic help
+.Op Fl v
+.Sh COMMAND DESCRIPTION
+Controllers and chips are arranged into a simple hierarchy.
+There can be up to 4 controllers configured, each with 4 chip select (CS) lines.
+A given chip is connected to one of the chip selects.
+.Pp
+Controllers are specified as
+.Aq ctrl_no ;
+chip selects are specified as
+.Aq cs_no .
+.Bl -tag -width periphlist
+.It Ic status
+Gets controller(s) status. If
+.Fl a
+or
+.Fl -all
+flag is specified - command will print status of every controller
+currently available.
+Optional flag
+.Fl v
+causes printing complete information about the controller, and all
+chips attached to it.
+.It Ic conf
+Reads simulator configuration from a specified file (this includes
+the simulation "layout" i.e. controllers-chips assignments).
+Configuration changes for an already started simulation require a
+full stop-start cycle in order to take effect i.e.:
+.Pp
+.Bl -column
+.It nandsim stop ...
+.It nandsim destroy ...
+.Pp
+.It << edit config file >>
+.Pp
+.It nandsim conf ...
+.It nandsim start ...
+.El
+.It Ic mod
+Alters simulator parameters on-the-fly.
+If controller number and CS pair is not specified, the general
+simulator parameters (not specific to a controller or a chip) will be modified.
+Changing chip's parameters requires specifying both controller number and CS
+to which the given chip is connected.
+Parameters which can be altered:
+.Pp
+General simulator related:
+.Bl -tag -width flag
+.It Fl l Aq log_level
+change logging level to
+.Aq log_level
+.El
+.Pp
+Chip related:
+.Bl -tag -width flag
+.It Fl p Aq prog_time
+change prog time for specified chip to
+.Aq prog_time
+.It Fl e Aq erase_time
+change erase time for specified chip to
+.Aq erase_time
+.It Fl r Aq read_time
+change read time for specified chip to
+.Aq read_time
+.It Fl E Aq error_ratio
+change error ratio for specified chip to
+.Aq error_ratio .
+Error ratio is a number of errors per million read/write bytes.
+.El
+.Pp
+Additionally, flag
+.Fl h
+will list parameters which can be altered.
+.El
+.Bl -tag -width periphlist
+.It Ic bb
+Marks/unmarks a specified block as bad.
+To mark/unmark the bad condition an a block, the following parameters
+have to be supplied: controller number, CS number, and at least one
+block number.
+It is possible to specify multiple blocks, by separating blocks numbers
+with a comma.
+The following options can be used for the 'bb' command:
+.Bl -tag -width flag
+.It Fl U
+unmark the bad previously marked block as bad.
+.It Fl L
+list all blocks marked as bad on a given chip.
+.El
+.It Ic log
+Prints activity log of the specified controller to stdout; if
+controller number is not specifed, logs for all available
+controllers are printed.
+.It Ic stats
+Print statistics of the selected controller, chip and page.
+Statistics includes read count, write count, raw read count, raw
+write count, ECC stats (succeeded corrections, failed correction).
+.It Ic dump
+Dumps a snaphot of a single chip (including data and bad blocks
+information, wearout level) into the file.
+.It Ic restore
+Restores chip state from a dump-file snapshot (produced previously
+with the 'dump' command).
+.It Ic start
+Starts a controller i.e. the simulation.
+.It Ic stop
+Stops an already started controller; if the controller number is not
+supplied, attempts to stop all currently working controllers.
+.It Ic destroy
+Removes existing active chip/controller and its configuration from
+memory and releases the resources.
+Specifying flag
+.Fl a
+or
+.Fl -all
+causes removal of every chip and controller.
+Controller must be stopped in order to be destroyed.
+.It Ic error
+Directly overwrites a certain number of bytes in the specified page
+at a given offset with a supplied pattern (which mimics the
+corruption of flash contents).
+.It Ic help
+Prints synopsis,
+.Fl v
+gives more verbose output.
+.It Ic freeze
+Stops simulation of given controller (simulates power-loss).
+All commands issues to any chip on this controller are ignored.
+.El
+.Sh SEE ALSO
+.Xr nand 4 ,
+.Xr nandsim 4
+.Xr nandsim.conf 5
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 10.0 .
+.Sh AUTHOR
+This utility was written by
+.An Lukasz Wojcik .
diff --git a/usr.sbin/nandsim/nandsim.c b/usr.sbin/nandsim/nandsim.c
new file mode 100644
index 0000000..082085f
--- /dev/null
+++ b/usr.sbin/nandsim/nandsim.c
@@ -0,0 +1,1397 @@
+/*-
+ * Copyright (C) 2009-2012 Semihalf
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Control application for the NAND simulator.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <dev/nand/nandsim.h>
+#include <dev/nand/nand_dev.h>
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sysexits.h>
+
+#include "nandsim_cfgparse.h"
+
+#define SIMDEVICE "/dev/nandsim.ioctl"
+
+#define error(fmt, args...) do { \
+ printf("ERROR: " fmt "\n", ##args); } while (0)
+
+#define warn(fmt, args...) do { \
+ printf("WARNING: " fmt "\n", ##args); } while (0)
+
+#define DEBUG
+#undef DEBUG
+
+#ifdef DEBUG
+#define debug(fmt, args...) do { \
+ printf("NANDSIM_CONF:" fmt "\n", ##args); } while (0)
+#else
+#define debug(fmt, args...) do {} while(0)
+#endif
+
+#define NANDSIM_RAM_LOG_SIZE 16384
+
+#define MSG_NOTRUNNING "Controller#%d is not running.Please start" \
+ " it first."
+#define MSG_RUNNING "Controller#%d is already running!"
+#define MSG_CTRLCHIPNEEDED "You have to specify ctrl_no:cs_no pair!"
+#define MSG_STATUSACQCTRLCHIP "Could not acquire status for ctrl#%d chip#%d"
+#define MSG_STATUSACQCTRL "Could not acquire status for ctrl#%d"
+#define MSG_NOCHIP "There is no such chip configured (chip#%d "\
+ "at ctrl#%d)!"
+
+#define MSG_NOCTRL "Controller#%d is not configured!"
+#define MSG_NOTCONFIGDCTRLCHIP "Chip connected to ctrl#%d at cs#%d " \
+ "is not configured."
+
+typedef int (commandfunc_t)(int , char **);
+
+static struct nandsim_command *getcommand(char *);
+static int parse_devstring(char *, int *, int *);
+static void printchip(struct sim_chip *, uint8_t);
+static void printctrl(struct sim_ctrl *);
+static int opendev(int *);
+static commandfunc_t cmdstatus;
+static commandfunc_t cmdconf;
+static commandfunc_t cmdstart;
+static commandfunc_t cmdstop;
+static commandfunc_t cmdmod;
+static commandfunc_t cmderror;
+static commandfunc_t cmdbb;
+static commandfunc_t cmdfreeze;
+static commandfunc_t cmdlog;
+static commandfunc_t cmdstats;
+static commandfunc_t cmddump;
+static commandfunc_t cmdrestore;
+static commandfunc_t cmddestroy;
+static commandfunc_t cmdhelp;
+static int checkusage(int, int, char **);
+static int is_chip_created(int, int, int *);
+static int is_ctrl_created(int, int *);
+static int is_ctrl_running(int, int *);
+static int assert_chip_connected(int , int);
+static int printstats(int, int, uint32_t, int);
+
+struct nandsim_command {
+ const char *cmd_name; /* Command name */
+ commandfunc_t *commandfunc; /* Ptr to command function */
+ uint8_t req_argc; /* Mandatory arguments count */
+ const char *usagestring; /* Usage string */
+};
+
+static struct nandsim_command commands[] = {
+ {"status", cmdstatus, 1,
+ "status <ctl_no|--all|-a> [-v]\n" },
+ {"conf", cmdconf, 1,
+ "conf <filename>\n" },
+ {"start", cmdstart, 1,
+ "start <ctrl_no>\n" },
+ {"mod", cmdmod, 2,
+ "mod [-l <loglevel>] | <ctl_no:cs_no> [-p <prog_time>]\n"
+ "\t[-e <erase_time>] [-r <read_time>]\n"
+ "\t[-E <error_ratio>] | [-h]\n" },
+ {"stop", cmdstop, 1,
+ "stop <ctrl_no>\n" },
+ {"error", cmderror, 5,
+ "error <ctrl_no:cs_no> <page_num> <column> <length> <pattern>\n" },
+ {"bb", cmdbb, 2,
+ "bb <ctl_no:cs_no> [blk_num1,blk_num2,..] [-U] [-L]\n" },
+ {"freeze", cmdfreeze, 1,
+ "freeze [ctrl_no]\n" },
+ {"log", cmdlog, 1,
+ "log <ctrl_no|--all|-a>\n" },
+ {"stats", cmdstats, 2,
+ "stats <ctrl_no:cs_no> <pagenumber>\n" },
+ {"dump", cmddump, 2,
+ "dump <ctrl_no:cs_no> <filename>\n" },
+ {"restore", cmdrestore, 2,
+ "restore <ctrl_no:chip_no> <filename>\n" },
+ {"destroy", cmddestroy, 1,
+ "destroy <ctrl_no[:cs_no]|--all|-a>\n" },
+ {"help", cmdhelp, 0,
+ "help [-v]" },
+ {NULL, NULL, 0, NULL},
+};
+
+
+/* Parse command name, and start appropriate function */
+static struct nandsim_command*
+getcommand(char *arg)
+{
+ struct nandsim_command *opts;
+
+ for (opts = commands; (opts != NULL) &&
+ (opts->cmd_name != NULL); opts++) {
+ if (strcmp(opts->cmd_name, arg) == 0)
+ return (opts);
+ }
+ return (NULL);
+}
+
+/*
+ * Parse given string in format <ctrl_no>:<cs_no>, if possible -- set
+ * ctrl and/or cs, and return 0 (success) or 1 (in case of error).
+ *
+ * ctrl == 0xff && chip == 0xff : '--all' flag specified
+ * ctrl != 0xff && chip != 0xff : both ctrl & chip were specified
+ * ctrl != 0xff && chip == 0xff : only ctrl was specified
+ */
+static int
+parse_devstring(char *str, int *ctrl, int *cs)
+{
+ char *tmpstr;
+ unsigned int num = 0;
+
+ /* Ignore white spaces at the beginning */
+ while (isspace(*str) && (*str != '\0'))
+ str++;
+
+ *ctrl = 0xff;
+ *cs = 0xff;
+ if (strcmp(str, "--all") == 0 ||
+ strcmp(str, "-a") == 0) {
+ /* If --all or -a is specified, ctl==chip==0xff */
+ debug("CTRL=%d CHIP=%d\n", *ctrl, *cs);
+ return (0);
+ }
+ /* Separate token and try to convert it to int */
+ tmpstr = (char *)strtok(str, ":");
+ if ((tmpstr != NULL) && (*tmpstr != '\0')) {
+ if (convert_arguint(tmpstr, &num) != 0)
+ return (1);
+
+ if (num > MAX_SIM_DEV - 1) {
+ error("Invalid ctrl_no supplied: %s. Valid ctrl_no "
+ "value must lie between 0 and 3!", tmpstr);
+ return (1);
+ }
+
+ *ctrl = num;
+ tmpstr = (char *)strtok(NULL, ":");
+
+ if ((tmpstr != NULL) && (*tmpstr != '\0')) {
+ if (convert_arguint(tmpstr, &num) != 0)
+ return (1);
+
+ /* Check if chip_no is valid */
+ if (num > MAX_CTRL_CS - 1) {
+ error("Invalid chip_no supplied: %s. Valid "
+ "chip_no value must lie between 0 and 3!",
+ tmpstr);
+ return (1);
+ }
+ *cs = num;
+ }
+ } else
+ /* Empty devstring supplied */
+ return (1);
+
+ debug("CTRL=%d CHIP=%d\n", *ctrl, *cs);
+ return (0);
+}
+
+static int
+opendev(int *fd)
+{
+
+ *fd = open(SIMDEVICE, O_RDWR);
+ if (*fd == -1) {
+ error("Could not open simulator device file (%s)!",
+ SIMDEVICE);
+ return (EX_OSFILE);
+ }
+ return (EX_OK);
+}
+
+static int
+opencdev(int *cdevd, int ctrl, int chip)
+{
+ char fname[255];
+
+ sprintf(fname, "/dev/nandsim%d.%d", ctrl, chip);
+ *cdevd = open(fname, O_RDWR);
+ if (*cdevd == -1)
+ return (EX_NOINPUT);
+
+ return (EX_OK);
+}
+
+/*
+ * Check if given arguments count match requirements. If no, or
+ * --help (-h) flag is specified -- return 1 (print usage)
+ */
+static int
+checkusage(int gargc, int argsreqd, char **gargv)
+{
+
+ if (gargc < argsreqd + 2 || (gargc >= (argsreqd + 2) &&
+ (strcmp(gargv[1], "--help") == 0 ||
+ strcmp(gargv[1], "-h") == 0)))
+ return (1);
+
+ return (0);
+}
+
+static int
+cmdstatus(int gargc, char **gargv)
+{
+ int chip = 0, ctl = 0, err = 0, fd, idx, idx2, start, stop;
+ uint8_t verbose = 0;
+ struct sim_ctrl ctrlconf;
+ struct sim_chip chipconf;
+
+ err = parse_devstring(gargv[2], &ctl, &chip);
+ if (err) {
+ return (EX_USAGE);
+ } else if (ctl == 0xff) {
+ /* Every controller */
+ start = 0;
+ stop = MAX_SIM_DEV-1;
+ } else {
+ /* Specified controller only */
+ start = ctl;
+ stop = ctl;
+ }
+
+ if (opendev(&fd) != EX_OK)
+ return (EX_OSFILE);
+
+ for (idx = 0; idx < gargc; idx ++)
+ if (strcmp(gargv[idx], "-v") == 0 ||
+ strcmp(gargv[idx], "--verbose") == 0)
+ verbose = 1;
+
+ for (idx = start; idx <= stop; idx++) {
+ ctrlconf.num = idx;
+ err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrlconf);
+ if (err) {
+ err = EX_SOFTWARE;
+ error(MSG_STATUSACQCTRL, idx);
+ continue;
+ }
+
+ printctrl(&ctrlconf);
+
+ for (idx2 = 0; idx2 < MAX_CTRL_CS; idx2++) {
+ chipconf.num = idx2;
+ chipconf.ctrl_num = idx;
+
+ err = ioctl(fd, NANDSIM_STATUS_CHIP, &chipconf);
+ if (err) {
+ err = EX_SOFTWARE;
+ error(MSG_STATUSACQCTRL, idx);
+ continue;
+ }
+
+ printchip(&chipconf, verbose);
+ }
+ }
+ close(fd);
+ return (err);
+}
+
+static int
+cmdconf(int gargc __unused, char **gargv)
+{
+ int err;
+
+ err = parse_config(gargv[2], SIMDEVICE);
+ if (err)
+ return (EX_DATAERR);
+
+ return (EX_OK);
+}
+
+static int
+cmdstart(int gargc __unused, char **gargv)
+{
+ int chip = 0, ctl = 0, err = 0, fd, running, state;
+
+ err = parse_devstring(gargv[2], &ctl, &chip);
+ if (err)
+ return (EX_USAGE);
+
+ err = is_ctrl_created(ctl, &state);
+ if (err) {
+ return (EX_SOFTWARE);
+ } else if (state == 0) {
+ error(MSG_NOCTRL, ctl);
+ return (EX_SOFTWARE);
+ }
+
+ err = is_ctrl_running(ctl, &running);
+ if (err)
+ return (EX_SOFTWARE);
+
+ if (running) {
+ warn(MSG_RUNNING, ctl);
+ } else {
+ if (opendev(&fd) != EX_OK)
+ return (EX_OSFILE);
+
+ err = ioctl(fd, NANDSIM_START_CTRL, &ctl);
+ close(fd);
+ if (err) {
+ error("Cannot start controller#%d", ctl);
+ err = EX_SOFTWARE;
+ }
+ }
+ return (err);
+}
+
+static int
+cmdstop(int gargc __unused, char **gargv)
+{
+ int chip = 0, ctl = 0, err = 0, fd, running;
+
+ err = parse_devstring(gargv[2], &ctl, &chip);
+ if (err)
+ return (EX_USAGE);
+
+ err = is_ctrl_running(ctl, &running);
+ if (err)
+ return (EX_SOFTWARE);
+
+ if (!running) {
+ error(MSG_NOTRUNNING, ctl);
+ } else {
+ if (opendev(&fd) != EX_OK)
+ return (EX_OSFILE);
+
+ err = ioctl(fd, NANDSIM_STOP_CTRL, &ctl);
+ close(fd);
+ if (err) {
+ error("Cannot stop controller#%d", ctl);
+ err = EX_SOFTWARE;
+ }
+ }
+
+ return (err);
+}
+
+static int
+cmdmod(int gargc __unused, char **gargv)
+{
+ int chip, ctl, err = 0, fd = -1, i;
+ struct sim_mod mods;
+
+ if (gargc >= 4) {
+ if (strcmp(gargv[2], "--loglevel") == 0 || strcmp(gargv[2],
+ "-l") == 0) {
+ /* Set loglevel (ctrl:chip pair independant) */
+ mods.field = SIM_MOD_LOG_LEVEL;
+
+ if (convert_arguint(gargv[3], &mods.new_value) != 0)
+ return (EX_SOFTWARE);
+
+ if (opendev(&fd) != EX_OK)
+ return (EX_OSFILE);
+
+ err = ioctl(fd, NANDSIM_MODIFY, &mods);
+ if (err) {
+ error("simulator parameter %s could not be "
+ "modified !", gargv[3]);
+ close(fd);
+ return (EX_SOFTWARE);
+ }
+
+ debug("request : loglevel = %d\n", mods.new_value);
+ close(fd);
+ return (EX_OK);
+ }
+ }
+
+ err = parse_devstring(gargv[2], &ctl, &chip);
+ if (err)
+ return (EX_USAGE);
+
+ else if (chip == 0xff) {
+ error(MSG_CTRLCHIPNEEDED);
+ return (EX_USAGE);
+ }
+
+ if (!assert_chip_connected(ctl, chip))
+ return (EX_SOFTWARE);
+
+ if (opendev(&fd) != EX_OK)
+ return (EX_OSFILE);
+
+ /* Find out which flags were passed */
+ for (i = 3; i < gargc; i++) {
+
+ if (convert_arguint(gargv[i + 1], &mods.new_value) != 0)
+ continue;
+
+ if (strcmp(gargv[i], "--prog-time") == 0 ||
+ strcmp(gargv[i], "-p") == 0) {
+
+ mods.field = SIM_MOD_PROG_TIME;
+ debug("request : progtime = %d\n", mods.new_value);
+
+ } else if (strcmp(gargv[i], "--erase-time") == 0 ||
+ strcmp(gargv[i], "-e") == 0) {
+
+ mods.field = SIM_MOD_ERASE_TIME;
+ debug("request : eraseime = %d\n", mods.new_value);
+
+ } else if (strcmp(gargv[i], "--read-time") == 0 ||
+ strcmp(gargv[i], "-r") == 0) {
+
+ mods.field = SIM_MOD_READ_TIME;
+ debug("request : read_time = %d\n", mods.new_value);
+
+ } else if (strcmp(gargv[i], "--error-ratio") == 0 ||
+ strcmp(gargv[i], "-E") == 0) {
+
+ mods.field = SIM_MOD_ERROR_RATIO;
+ debug("request : error_ratio = %d\n", mods.new_value);
+
+ } else {
+ /* Flag not recognized, or nothing specified. */
+ error("Unrecognized flag:%s\n", gargv[i]);
+ if (fd >= 0)
+ close(fd);
+ return (EX_USAGE);
+ }
+
+ mods.chip_num = chip;
+ mods.ctrl_num = ctl;
+
+ /* Call appropriate ioctl */
+ err = ioctl(fd, NANDSIM_MODIFY, &mods);
+ if (err) {
+ error("simulator parameter %s could not be modified! ",
+ gargv[i]);
+ continue;
+ }
+ i++;
+ }
+ close(fd);
+ return (EX_OK);
+}
+
+static int
+cmderror(int gargc __unused, char **gargv)
+{
+ uint32_t page, column, len, pattern;
+ int chip = 0, ctl = 0, err = 0, fd;
+ struct sim_error sim_err;
+
+ err = parse_devstring(gargv[2], &ctl, &chip);
+ if (err)
+ return (EX_USAGE);
+
+ if (chip == 0xff) {
+ error(MSG_CTRLCHIPNEEDED);
+ return (EX_USAGE);
+ }
+
+ if (convert_arguint(gargv[3], &page) ||
+ convert_arguint(gargv[4], &column) ||
+ convert_arguint(gargv[5], &len) ||
+ convert_arguint(gargv[6], &pattern))
+ return (EX_SOFTWARE);
+
+ if (!assert_chip_connected(ctl, chip))
+ return (EX_SOFTWARE);
+
+ sim_err.page_num = page;
+ sim_err.column = column;
+ sim_err.len = len;
+ sim_err.pattern = pattern;
+ sim_err.ctrl_num = ctl;
+ sim_err.chip_num = chip;
+
+ if (opendev(&fd) != EX_OK)
+ return (EX_OSFILE);
+
+ err = ioctl(fd, NANDSIM_INJECT_ERROR, &sim_err);
+
+ close(fd);
+ if (err) {
+ error("Could not inject error !");
+ return (EX_SOFTWARE);
+ }
+ return (EX_OK);
+}
+
+static int
+cmdbb(int gargc, char **gargv)
+{
+ struct sim_block_state bs;
+ struct chip_param_io cparams;
+ uint32_t blkidx;
+ int c, cdevd, chip = 0, ctl = 0, err = 0, fd, idx;
+ uint8_t flagL = 0, flagU = 0;
+ int *badblocks = NULL;
+
+ /* Check for --list/-L or --unmark/-U flags */
+ for (idx = 3; idx < gargc; idx++) {
+ if (strcmp(gargv[idx], "--list") == 0 ||
+ strcmp(gargv[idx], "-L") == 0)
+ flagL = idx;
+ if (strcmp(gargv[idx], "--unmark") == 0 ||
+ strcmp(gargv[idx], "-U") == 0)
+ flagU = idx;
+ }
+
+ if (flagL == 2 || flagU == 2 || flagU == 3)
+ return (EX_USAGE);
+
+ err = parse_devstring(gargv[2], &ctl, &chip);
+ if (err) {
+ return (EX_USAGE);
+ }
+ if (chip == 0xff || ctl == 0xff) {
+ error(MSG_CTRLCHIPNEEDED);
+ return (EX_USAGE);
+ }
+
+ bs.ctrl_num = ctl;
+ bs.chip_num = chip;
+
+ if (!assert_chip_connected(ctl, chip))
+ return (EX_SOFTWARE);
+
+ if (opencdev(&cdevd, ctl, chip) != EX_OK)
+ return (EX_OSFILE);
+
+ err = ioctl(cdevd, NAND_IO_GET_CHIP_PARAM, &cparams);
+ if (err)
+ return (EX_SOFTWARE);
+
+ close(cdevd);
+
+ bs.ctrl_num = ctl;
+ bs.chip_num = chip;
+
+ if (opendev(&fd) != EX_OK)
+ return (EX_OSFILE);
+
+ if (flagL != 3) {
+ /*
+ * Flag -L was specified either after blocklist or was not
+ * specified at all.
+ */
+ c = parse_intarray(gargv[3], &badblocks);
+
+ for (idx = 0; idx < c; idx++) {
+ bs.block_num = badblocks[idx];
+ /* Do not change wearout */
+ bs.wearout = -1;
+ bs.state = (flagU == 0) ? NANDSIM_BAD_BLOCK :
+ NANDSIM_GOOD_BLOCK;
+
+ err = ioctl(fd, NANDSIM_SET_BLOCK_STATE, &bs);
+ if (err) {
+ error("Could not set bad block(%d) for "
+ "controller (%d)!",
+ badblocks[idx], ctl);
+ err = EX_SOFTWARE;
+ break;
+ }
+ }
+ }
+ if (flagL != 0) {
+ /* If flag -L was specified (anywhere) */
+ for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
+ bs.block_num = blkidx;
+ /* Do not change the wearout */
+ bs.wearout = -1;
+ err = ioctl(fd, NANDSIM_GET_BLOCK_STATE, &bs);
+ if (err) {
+ error("Could not acquire block state");
+ err = EX_SOFTWARE;
+ continue;
+ }
+ printf("Block#%d: wear count: %d %s\n", blkidx,
+ bs.wearout,
+ (bs.state == NANDSIM_BAD_BLOCK) ? "BAD":"GOOD");
+ }
+ }
+ close(fd);
+ return (err);
+}
+
+static int
+cmdfreeze(int gargc __unused, char **gargv)
+{
+ int chip = 0, ctl = 0, err = 0, fd, i, start = 0, state, stop = 0;
+ struct sim_ctrl_chip ctrlchip;
+
+ err = parse_devstring(gargv[2], &ctl, &chip);
+ if (err)
+ return (EX_USAGE);
+
+ if (ctl == 0xff) {
+ error("You have to specify at least controller number");
+ return (EX_USAGE);
+ }
+
+ if (ctl != 0xff && chip == 0xff) {
+ start = 0;
+ stop = MAX_CTRL_CS - 1;
+ } else {
+ start = chip;
+ stop = chip;
+ }
+
+ ctrlchip.ctrl_num = ctl;
+
+ err = is_ctrl_running(ctl, &state);
+ if (err)
+ return (EX_SOFTWARE);
+ if (state == 0) {
+ error(MSG_NOTRUNNING, ctl);
+ return (EX_SOFTWARE);
+ }
+
+ if (opendev(&fd) != EX_OK)
+ return (EX_OSFILE);
+
+ for (i = start; i <= stop; i++) {
+ err = is_chip_created(ctl, i, &state);
+ if (err)
+ return (EX_SOFTWARE);
+ else if (state == 0) {
+ continue;
+ }
+
+ ctrlchip.chip_num = i;
+ err = ioctl(fd, NANDSIM_FREEZE, &ctrlchip);
+ if (err) {
+ error("Could not freeze ctrl#%d chip#%d", ctl, i);
+ close(fd);
+ return (EX_SOFTWARE);
+ }
+ }
+ close(fd);
+ return (EX_OK);
+}
+
+static int
+cmdlog(int gargc __unused, char **gargv)
+{
+ struct sim_log log;
+ int chip = 0, ctl = 0, err = 0, fd, idx, start = 0, stop = 0;
+ char *logbuf;
+
+ err = parse_devstring(gargv[2], &ctl, &chip);
+ if (err)
+ return (EX_USAGE);
+
+ logbuf = (char *)malloc(sizeof(char) * NANDSIM_RAM_LOG_SIZE);
+ if (logbuf == NULL) {
+ error("Not enough memory to create log buffer");
+ return (EX_SOFTWARE);
+ }
+
+ memset(logbuf, 0, NANDSIM_RAM_LOG_SIZE);
+ log.log = logbuf;
+ log.len = NANDSIM_RAM_LOG_SIZE;
+
+ if (ctl == 0xff) {
+ start = 0;
+ stop = MAX_SIM_DEV-1;
+ } else {
+ start = ctl;
+ stop = ctl;
+ }
+
+ if (opendev(&fd) != EX_OK) {
+ free(logbuf);
+ return (EX_OSFILE);
+ }
+
+ /* Print logs for selected controller(s) */
+ for (idx = start; idx <= stop; idx++) {
+ log.ctrl_num = idx;
+
+ err = ioctl(fd, NANDSIM_PRINT_LOG, &log);
+ if (err) {
+ error("Could not get log for controller %d!", idx);
+ continue;
+ }
+
+ printf("Logs for controller#%d:\n%s\n", idx, logbuf);
+ }
+
+ free(logbuf);
+ close(fd);
+ return (EX_OK);
+}
+
+static int
+cmdstats(int gargc __unused, char **gargv)
+{
+ int cdevd, chip = 0, ctl = 0, err = 0;
+ uint32_t pageno = 0;
+
+ err = parse_devstring(gargv[2], &ctl, &chip);
+
+ if (err)
+ return (EX_USAGE);
+
+ if (chip == 0xff) {
+ error(MSG_CTRLCHIPNEEDED);
+ return (EX_USAGE);
+ }
+
+ if (convert_arguint(gargv[3], &pageno) != 0)
+ return (EX_USAGE);
+
+ if (!assert_chip_connected(ctl, chip))
+ return (EX_SOFTWARE);
+
+ if (opencdev(&cdevd, ctl, chip) != EX_OK)
+ return (EX_OSFILE);
+
+ err = printstats(ctl, chip, pageno, cdevd);
+ if (err) {
+ close(cdevd);
+ return (EX_SOFTWARE);
+ }
+ close(cdevd);
+ return (EX_OK);
+}
+
+static int
+cmddump(int gargc __unused, char **gargv)
+{
+ struct sim_dump dump;
+ struct sim_block_state bs;
+ struct chip_param_io cparams;
+ int chip = 0, ctl = 0, err = EX_OK, fd, dumpfd;
+ uint32_t blkidx, bwritten = 0, totalwritten = 0;
+ void *buf;
+
+ err = parse_devstring(gargv[2], &ctl, &chip);
+ if (err)
+ return (EX_USAGE);
+
+ if (chip == 0xff || ctl == 0xff) {
+ error(MSG_CTRLCHIPNEEDED);
+ return (EX_USAGE);
+ }
+
+ if (!assert_chip_connected(ctl, chip))
+ return (EX_SOFTWARE);
+
+ if (opencdev(&fd, ctl, chip) != EX_OK)
+ return (EX_OSFILE);
+
+ err = ioctl(fd, NAND_IO_GET_CHIP_PARAM, &cparams);
+ if (err) {
+ error("Cannot get parameters for chip %d:%d", ctl, chip);
+ close(fd);
+ return (EX_SOFTWARE);
+ }
+ close(fd);
+
+ dump.ctrl_num = ctl;
+ dump.chip_num = chip;
+
+ dump.len = cparams.pages_per_block * (cparams.page_size +
+ cparams.oob_size);
+
+ buf = malloc(dump.len);
+ if (buf == NULL) {
+ error("Could not allocate memory!");
+ return (EX_SOFTWARE);
+ }
+ dump.data = buf;
+
+ errno = 0;
+ dumpfd = open(gargv[3], O_WRONLY | O_CREAT, 0666);
+ if (dumpfd == -1) {
+ error("Cannot create dump file.");
+ free(buf);
+ return (EX_SOFTWARE);
+ }
+
+ if (opendev(&fd)) {
+ close(dumpfd);
+ free(buf);
+ return (EX_SOFTWARE);
+ }
+
+ bs.ctrl_num = ctl;
+ bs.chip_num = chip;
+
+ /* First uint32_t in file shall contain block count */
+ if (write(dumpfd, &cparams, sizeof(cparams)) < (int)sizeof(cparams)) {
+ error("Error writing to dumpfile!");
+ close(fd);
+ close(dumpfd);
+ free(buf);
+ return (EX_SOFTWARE);
+ }
+
+ /*
+ * First loop acquires blocks states and writes them to
+ * the dump file.
+ */
+ for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
+ bs.block_num = blkidx;
+ err = ioctl(fd, NANDSIM_GET_BLOCK_STATE, &bs);
+ if (err) {
+ error("Could not get bad block(%d) for "
+ "controller (%d)!", blkidx, ctl);
+ close(fd);
+ close(dumpfd);
+ free(buf);
+ return (EX_SOFTWARE);
+ }
+
+ bwritten = write(dumpfd, &bs, sizeof(bs));
+ if (bwritten != sizeof(bs)) {
+ error("Error writing to dumpfile");
+ close(fd);
+ close(dumpfd);
+ free(buf);
+ return (EX_SOFTWARE);
+ }
+ }
+
+ /* Second loop dumps the data */
+ for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
+ debug("Block#%d...", blkidx);
+ dump.block_num = blkidx;
+
+ err = ioctl(fd, NANDSIM_DUMP, &dump);
+ if (err) {
+ error("Could not dump ctrl#%d chip#%d "
+ "block#%d", ctl, chip, blkidx);
+ err = EX_SOFTWARE;
+ break;
+ }
+
+ bwritten = write(dumpfd, dump.data, dump.len);
+ if (bwritten != dump.len) {
+ error("Error writing to dumpfile");
+ err = EX_SOFTWARE;
+ break;
+ }
+ debug("OK!\n");
+ totalwritten += bwritten;
+ }
+ printf("%d out of %d B written.\n", totalwritten, dump.len * blkidx);
+
+ close(fd);
+ close(dumpfd);
+ free(buf);
+ return (err);
+}
+
+static int
+cmdrestore(int gargc __unused, char **gargv)
+{
+ struct sim_dump dump;
+ struct sim_block_state bs;
+ struct stat filestat;
+ int chip = 0, ctl = 0, err = 0, fd, dumpfd = -1;
+ uint32_t blkidx, blksz, fsize = 0, expfilesz;
+ void *buf;
+ struct chip_param_io cparams, dumpcparams;
+
+ err = parse_devstring(gargv[2], &ctl, &chip);
+ if (err)
+ return (EX_USAGE);
+ else if (ctl == 0xff) {
+ error(MSG_CTRLCHIPNEEDED);
+ return (EX_USAGE);
+ }
+
+ if (!assert_chip_connected(ctl, chip))
+ return (EX_SOFTWARE);
+
+ /* Get chip geometry */
+ if (opencdev(&fd, ctl, chip) != EX_OK)
+ return (EX_OSFILE);
+
+ err = ioctl(fd, NAND_IO_GET_CHIP_PARAM, &cparams);
+ if (err) {
+ error("Cannot get parameters for chip %d:%d", ctl, chip);
+ close(fd);
+ return (err);
+ }
+ close(fd);
+
+ /* Obtain dump file size */
+ errno = 0;
+ if (stat(gargv[3], &filestat) != 0) {
+ error("Could not acquire file size! : %s",
+ strerror(errno));
+ return (EX_IOERR);
+ }
+
+ fsize = filestat.st_size;
+ blksz = cparams.pages_per_block * (cparams.page_size +
+ cparams.oob_size);
+
+ /* Expected dump file size for chip */
+ expfilesz = cparams.blocks * (blksz + sizeof(bs)) + sizeof(cparams);
+
+ if (fsize != expfilesz) {
+ error("File size does not match chip geometry (file size: %d"
+ ", dump size: %d)", fsize, expfilesz);
+ return (EX_SOFTWARE);
+ }
+
+ dumpfd = open(gargv[3], O_RDONLY);
+ if (dumpfd == -1) {
+ error("Could not open dump file!");
+ return (EX_IOERR);
+ }
+
+ /* Read chip params saved in dumpfile */
+ read(dumpfd, &dumpcparams, sizeof(dumpcparams));
+
+ /* XXX */
+ if (bcmp(&dumpcparams, &cparams, sizeof(cparams)) != 0) {
+ error("Supplied dump is created for a chip with different "
+ "chip configuration!");
+ close(dumpfd);
+ return (EX_SOFTWARE);
+ }
+
+ if (opendev(&fd) != EX_OK) {
+ close(dumpfd);
+ return (EX_OSFILE);
+ }
+
+ buf = malloc(blksz);
+ if (buf == NULL) {
+ error("Could not allocate memory for block buffer");
+ close(dumpfd);
+ close(fd);
+ return (EX_SOFTWARE);
+ }
+
+ dump.ctrl_num = ctl;
+ dump.chip_num = chip;
+ dump.data = buf;
+ /* Restore block states and wearouts */
+ for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
+ dump.block_num = blkidx;
+ if (read(dumpfd, &bs, sizeof(bs)) != sizeof(bs)) {
+ error("Error reading dumpfile");
+ close(dumpfd);
+ close(fd);
+ free(buf);
+ return (EX_SOFTWARE);
+ }
+ bs.ctrl_num = ctl;
+ bs.chip_num = chip;
+ debug("BLKIDX=%d BLOCKS=%d CTRL=%d CHIP=%d STATE=%d\n"
+ "WEAROUT=%d BS.CTRL_NUM=%d BS.CHIP_NUM=%d\n",
+ blkidx, cparams.blocks, dump.ctrl_num, dump.chip_num,
+ bs.state, bs.wearout, bs.ctrl_num, bs.chip_num);
+
+ err = ioctl(fd, NANDSIM_SET_BLOCK_STATE, &bs);
+ if (err) {
+ error("Could not set bad block(%d) for "
+ "controller: %d, chip: %d!", blkidx, ctl, chip);
+ close(dumpfd);
+ close(fd);
+ free(buf);
+ return (EX_SOFTWARE);
+ }
+ }
+ /* Restore data */
+ for (blkidx = 0; blkidx < cparams.blocks; blkidx++) {
+ errno = 0;
+ dump.len = read(dumpfd, buf, blksz);
+ if (errno) {
+ error("Failed to read block#%d from dumpfile.", blkidx);
+ err = EX_SOFTWARE;
+ break;
+ }
+ dump.block_num = blkidx;
+ err = ioctl(fd, NANDSIM_RESTORE, &dump);
+ if (err) {
+ error("Could not restore block#%d of ctrl#%d chip#%d"
+ ": %s", blkidx, ctl, chip, strerror(errno));
+ err = EX_SOFTWARE;
+ break;
+ }
+ }
+
+ free(buf);
+ close(dumpfd);
+ close(fd);
+ return (err);
+
+}
+
+static int
+cmddestroy(int gargc __unused, char **gargv)
+{
+ int chip = 0, ctl = 0, err = 0, fd, idx, idx2, state;
+ int chipstart, chipstop, ctrlstart, ctrlstop;
+ struct sim_chip_destroy chip_destroy;
+
+ err = parse_devstring(gargv[2], &ctl, &chip);
+
+ if (err)
+ return (EX_USAGE);
+
+ if (ctl == 0xff) {
+ /* Every chip at every controller */
+ ctrlstart = chipstart = 0;
+ ctrlstop = MAX_SIM_DEV - 1;
+ chipstop = MAX_CTRL_CS - 1;
+ } else {
+ ctrlstart = ctrlstop = ctl;
+ if (chip == 0xff) {
+ /* Every chip at selected controller */
+ chipstart = 0;
+ chipstop = MAX_CTRL_CS - 1;
+ } else
+ /* Selected chip at selected controller */
+ chipstart = chipstop = chip;
+ }
+ debug("CTRLSTART=%d CTRLSTOP=%d CHIPSTART=%d CHIPSTOP=%d\n",
+ ctrlstart, ctrlstop, chipstart, chipstop);
+ for (idx = ctrlstart; idx <= ctrlstop; idx++) {
+ err = is_ctrl_created(idx, &state);
+ if (err) {
+ error("Could not acquire ctrl#%d state. Cannot "
+ "destroy controller.", idx);
+ return (EX_SOFTWARE);
+ }
+ if (state == 0) {
+ continue;
+ }
+ err = is_ctrl_running(idx, &state);
+ if (err) {
+ error(MSG_STATUSACQCTRL, idx);
+ return (EX_SOFTWARE);
+ }
+ if (state != 0) {
+ error(MSG_RUNNING, ctl);
+ return (EX_SOFTWARE);
+ }
+ if (opendev(&fd) != EX_OK)
+ return (EX_OSFILE);
+
+ for (idx2 = chipstart; idx2 <= chipstop; idx2++) {
+ err = is_chip_created(idx, idx2, &state);
+ if (err) {
+ error(MSG_STATUSACQCTRLCHIP, idx2, idx);
+ continue;
+ }
+ if (state == 0)
+ /* There is no such chip running */
+ continue;
+ chip_destroy.ctrl_num = idx;
+ chip_destroy.chip_num = idx2;
+ ioctl(fd, NANDSIM_DESTROY_CHIP,
+ &chip_destroy);
+ }
+ /* If chip isn't explicitly specified -- destroy ctrl */
+ if (chip == 0xff) {
+ err = ioctl(fd, NANDSIM_DESTROY_CTRL, &idx);
+ if (err) {
+ error("Could not destroy ctrl#%d", idx);
+ continue;
+ }
+ }
+ close(fd);
+ }
+ return (err);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct nandsim_command *cmdopts;
+ int retcode = 0;
+
+ if (argc < 2) {
+ cmdhelp(argc, argv);
+ retcode = EX_USAGE;
+ } else {
+ cmdopts = getcommand(argv[1]);
+ if (cmdopts != NULL && cmdopts->commandfunc != NULL) {
+ if (checkusage(argc, cmdopts->req_argc, argv) == 1) {
+ /* Print command specific usage */
+ printf("nandsim %s", cmdopts->usagestring);
+ return (EX_USAGE);
+ }
+ retcode = cmdopts->commandfunc(argc, argv);
+
+ if (retcode == EX_USAGE) {
+ /* Print command-specific usage */
+ printf("nandsim %s", cmdopts->usagestring);
+ } else if (retcode == EX_OSFILE) {
+ error("Could not open device file");
+ }
+
+ } else {
+ error("Unknown command!");
+ retcode = EX_USAGE;
+ }
+ }
+ return (retcode);
+}
+
+static int
+cmdhelp(int gargc __unused, char **gargv __unused)
+{
+ struct nandsim_command *opts;
+
+ printf("usage: nandsim <command> [command params] [params]\n\n");
+
+ for (opts = commands; (opts != NULL) &&
+ (opts->cmd_name != NULL); opts++)
+ printf("nandsim %s", opts->usagestring);
+
+ printf("\n");
+ return (EX_OK);
+}
+
+static void
+printchip(struct sim_chip *chip, uint8_t verbose)
+{
+
+ if (chip->created == 0)
+ return;
+ if (verbose > 0) {
+ printf("\n[Chip info]\n");
+ printf("num= %d\nctrl_num=%d\ndevice_id=%02x"
+ "\tmanufacturer_id=%02x\ndevice_model=%s\nmanufacturer="
+ "%s\ncol_addr_cycles=%d\nrow_addr_cycles=%d"
+ "\npage_size=%d\noob_size=%d\npages_per_block=%d\n"
+ "blocks_per_lun=%d\nluns=%d\n\nprog_time=%d\n"
+ "erase_time=%d\nread_time=%d\n"
+ "error_ratio=%d\nwear_level=%d\nwrite_protect=%c\n"
+ "chip_width=%db\n", chip->num, chip->ctrl_num,
+ chip->device_id, chip->manufact_id,chip->device_model,
+ chip->manufacturer, chip->col_addr_cycles,
+ chip->row_addr_cycles, chip->page_size,
+ chip->oob_size, chip->pgs_per_blk, chip->blks_per_lun,
+ chip->luns,chip->prog_time, chip->erase_time,
+ chip->read_time, chip->error_ratio, chip->wear_level,
+ (chip->is_wp == 0) ? 'N':'Y', chip->width);
+ } else {
+ printf("[Chip info]\n");
+ printf("\tnum=%d\n\tdevice_model=%s\n\tmanufacturer=%s\n"
+ "\tpage_size=%d\n\twrite_protect=%s\n",
+ chip->num, chip->device_model, chip->manufacturer,
+ chip->page_size, (chip->is_wp == 0) ? "NO":"YES");
+ }
+}
+
+static void
+printctrl(struct sim_ctrl *ctrl)
+{
+ int i;
+
+ if (ctrl->created == 0) {
+ printf(MSG_NOCTRL "\n", ctrl->num);
+ return;
+ }
+ printf("\n[Controller info]\n");
+ printf("\trunning: %s\n", ctrl->running ? "yes" : "no");
+ printf("\tnum cs: %d\n", ctrl->num_cs);
+ printf("\tecc: %d\n", ctrl->ecc);
+ printf("\tlog_filename: %s\n", ctrl->filename);
+ printf("\tecc_layout:");
+ for (i = 0; i < MAX_ECC_BYTES; i++) {
+ if (ctrl->ecc_layout[i] == 0xffff)
+ break;
+ else
+ printf("%c%d", i%16 ? ' ' : '\n',
+ ctrl->ecc_layout[i]);
+ }
+ printf("\n");
+}
+
+static int
+is_ctrl_running(int ctrl_no, int *running)
+{
+ struct sim_ctrl ctrl;
+ int err, fd;
+
+ ctrl.num = ctrl_no;
+ if (opendev(&fd) != EX_OK)
+ return (EX_OSFILE);
+
+ err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrl);
+ if (err) {
+ error(MSG_STATUSACQCTRL, ctrl_no);
+ close(fd);
+ return (err);
+ }
+ *running = ctrl.running;
+ close(fd);
+ return (0);
+}
+
+static int
+is_ctrl_created(int ctrl_no, int *created)
+{
+ struct sim_ctrl ctrl;
+ int err, fd;
+
+ ctrl.num = ctrl_no;
+
+ if (opendev(&fd) != EX_OK)
+ return (EX_OSFILE);
+
+ err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrl);
+ if (err) {
+ error("Could not acquire conf for ctrl#%d", ctrl_no);
+ close(fd);
+ return (err);
+ }
+ *created = ctrl.created;
+ close(fd);
+ return (0);
+}
+
+static int
+is_chip_created(int ctrl_no, int chip_no, int *created)
+{
+ struct sim_chip chip;
+ int err, fd;
+
+ chip.ctrl_num = ctrl_no;
+ chip.num = chip_no;
+
+ if (opendev(&fd) != EX_OK)
+ return (EX_OSFILE);
+
+ err = ioctl(fd, NANDSIM_STATUS_CHIP, &chip);
+ if (err) {
+ error("Could not acquire conf for chip#%d", chip_no);
+ close(fd);
+ return (err);
+ }
+ *created = chip.created;
+ close(fd);
+ return (0);
+}
+
+static int
+assert_chip_connected(int ctrl_no, int chip_no)
+{
+ int created, running;
+
+ if (is_ctrl_created(ctrl_no, &created))
+ return (0);
+
+ if (!created) {
+ error(MSG_NOCTRL, ctrl_no);
+ return (0);
+ }
+
+ if (is_chip_created(ctrl_no, chip_no, &created))
+ return (0);
+
+ if (!created) {
+ error(MSG_NOTCONFIGDCTRLCHIP, ctrl_no, chip_no);
+ return (0);
+ }
+
+ if (is_ctrl_running(ctrl_no, &running))
+ return (0);
+
+ if (!running) {
+ error(MSG_NOTRUNNING, ctrl_no);
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+printstats(int ctrlno, int chipno, uint32_t pageno, int cdevd)
+{
+ struct page_stat_io pstats;
+ struct block_stat_io bstats;
+ struct chip_param_io cparams;
+ uint32_t blkidx;
+ int err;
+
+ /* Gather information about chip */
+ err = ioctl(cdevd, NAND_IO_GET_CHIP_PARAM, &cparams);
+
+ if (err) {
+ error("Could not acquire chip info for chip attached to cs#"
+ "%d, ctrl#%d", chipno, ctrlno);
+ return (EX_SOFTWARE);
+ }
+
+ blkidx = (pageno / cparams.pages_per_block);
+ bstats.block_num = blkidx;
+
+ err = ioctl(cdevd, NAND_IO_BLOCK_STAT, &bstats);
+ if (err) {
+ error("Could not acquire block#%d statistics!", blkidx);
+ return (ENXIO);
+ }
+
+ printf("Block #%d erased: %d\n", blkidx, bstats.block_erased);
+ pstats.page_num = pageno;
+
+ err = ioctl(cdevd, NAND_IO_PAGE_STAT, &pstats);
+ if (err) {
+ error("Could not acquire page statistics!");
+ return (ENXIO);
+ }
+
+ debug("BLOCKIDX = %d PAGENO (REL. TO BLK) = %d\n", blkidx,
+ pstats.page_num);
+
+ printf("Page#%d : reads:%d writes:%d \n\traw reads:%d raw writes:%d "
+ "\n\tecc_succeeded:%d ecc_corrected:%d ecc_failed:%d\n",
+ pstats.page_num, pstats.page_read, pstats.page_written,
+ pstats.page_raw_read, pstats.page_raw_written,
+ pstats.ecc_succeded, pstats.ecc_corrected, pstats.ecc_failed);
+ return (0);
+}
diff --git a/usr.sbin/nandsim/nandsim_cfgparse.c b/usr.sbin/nandsim/nandsim_cfgparse.c
new file mode 100644
index 0000000..40e8d4c
--- /dev/null
+++ b/usr.sbin/nandsim/nandsim_cfgparse.c
@@ -0,0 +1,957 @@
+/*-
+ * Copyright (C) 2009-2012 Semihalf
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+__FBSDID("$FreeBSD$");
+
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include <dev/nand/nandsim.h>
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "nandsim_cfgparse.h"
+
+#define warn(fmt, args...) do { \
+ printf("WARNING: " fmt "\n", ##args); } while (0)
+
+#define error(fmt, args...) do { \
+ printf("ERROR: " fmt "\n", ##args); } while (0)
+
+#define MSG_MANDATORYKEYMISSING "mandatory key \"%s\" value belonging to " \
+ "section \"%s\" is missing!\n"
+
+#define DEBUG
+#undef DEBUG
+
+#ifdef DEBUG
+#define debug(fmt, args...) do { \
+ printf("NANDSIM_CONF:" fmt "\n", ##args); } while (0)
+#else
+#define debug(fmt, args...) do {} while(0)
+#endif
+
+#define STRBUFSIZ 2000
+
+/* Macros extracts type and type size */
+#define TYPE(x) ((x) & 0xf8)
+#define SIZE(x) (((x) & 0x07))
+
+/* Erase/Prog/Read time max and min values */
+#define DELAYTIME_MIN 10000
+#define DELAYTIME_MAX 10000000
+
+/* Structure holding configuration for controller. */
+static struct sim_ctrl ctrl_conf;
+/* Structure holding configuration for chip. */
+static struct sim_chip chip_conf;
+
+static struct nandsim_key nandsim_ctrl_keys[] = {
+ {"num_cs", 1, VALUE_UINT | SIZE_8, (void *)&ctrl_conf.num_cs, 0},
+ {"ctrl_num", 1, VALUE_UINT | SIZE_8, (void *)&ctrl_conf.num, 0},
+
+ {"ecc_layout", 1, VALUE_UINTARRAY | SIZE_16,
+ (void *)&ctrl_conf.ecc_layout, MAX_ECC_BYTES},
+
+ {"filename", 0, VALUE_STRING,
+ (void *)&ctrl_conf.filename, FILENAME_SIZE},
+
+ {"ecc", 0, VALUE_BOOL, (void *)&ctrl_conf.ecc, 0},
+ {NULL, 0, 0, NULL, 0},
+};
+
+static struct nandsim_key nandsim_chip_keys[] = {
+ {"chip_cs", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.num, 0},
+ {"chip_ctrl", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.ctrl_num,
+ 0},
+ {"device_id", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.device_id,
+ 0},
+ {"manufacturer_id", 1, VALUE_UINT | SIZE_8,
+ (void *)&chip_conf.manufact_id, 0},
+ {"model", 0, VALUE_STRING, (void *)&chip_conf.device_model,
+ DEV_MODEL_STR_SIZE},
+ {"manufacturer", 0, VALUE_STRING, (void *)&chip_conf.manufacturer,
+ MAN_STR_SIZE},
+ {"page_size", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.page_size,
+ 0},
+ {"oob_size", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.oob_size,
+ 0},
+ {"pages_per_block", 1, VALUE_UINT | SIZE_32,
+ (void *)&chip_conf.pgs_per_blk, 0},
+ {"blocks_per_lun", 1, VALUE_UINT | SIZE_32,
+ (void *)&chip_conf.blks_per_lun, 0},
+ {"luns", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.luns, 0},
+ {"column_addr_cycle", 1,VALUE_UINT | SIZE_8,
+ (void *)&chip_conf.col_addr_cycles, 0},
+ {"row_addr_cycle", 1, VALUE_UINT | SIZE_8,
+ (void *)&chip_conf.row_addr_cycles, 0},
+ {"program_time", 0, VALUE_UINT | SIZE_32,
+ (void *)&chip_conf.prog_time, 0},
+ {"erase_time", 0, VALUE_UINT | SIZE_32,
+ (void *)&chip_conf.erase_time, 0},
+ {"read_time", 0, VALUE_UINT | SIZE_32,
+ (void *)&chip_conf.read_time, 0},
+ {"width", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.width, 0},
+ {"wear_out", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.wear_level,
+ 0},
+ {"bad_block_map", 0, VALUE_UINTARRAY | SIZE_32,
+ (void *)&chip_conf.bad_block_map, MAX_BAD_BLOCKS},
+ {NULL, 0, 0, NULL, 0},
+};
+
+struct nandsim_section sections[] = {
+ {"ctrl", (struct nandsim_key *)&nandsim_ctrl_keys},
+ {"chip", (struct nandsim_key *)&nandsim_chip_keys},
+ {NULL, NULL},
+};
+
+static uint8_t logoutputtoint(char *, int *);
+static uint8_t validate_chips(struct sim_chip *, int, struct sim_ctrl *, int);
+static uint8_t validate_ctrls(struct sim_ctrl *, int);
+static int configure_sim(const char *, struct rcfile *);
+static int create_ctrls(struct rcfile *, struct sim_ctrl **, int *);
+static int create_chips(struct rcfile *, struct sim_chip **, int *);
+static void destroy_ctrls(struct sim_ctrl *);
+static void destroy_chips(struct sim_chip *);
+static int validate_section_config(struct rcfile *, const char *, int);
+
+int
+convert_argint(char *arg, int *value)
+{
+
+ if (arg == NULL || value == NULL)
+ return (EINVAL);
+
+ errno = 0;
+ *value = (int)strtol(arg, NULL, 0);
+ if (*value == 0 && errno != 0) {
+ error("Cannot convert to number argument \'%s\'", arg);
+ return (EINVAL);
+ }
+ return (0);
+}
+
+int
+convert_arguint(char *arg, unsigned int *value)
+{
+
+ if (arg == NULL || value == NULL)
+ return (EINVAL);
+
+ errno = 0;
+ *value = (unsigned int)strtol(arg, NULL, 0);
+ if (*value == 0 && errno != 0) {
+ error("Cannot convert to number argument \'%s\'", arg);
+ return (EINVAL);
+ }
+ return (0);
+}
+
+/* Parse given ',' separated list of bytes into buffer. */
+int
+parse_intarray(char *array, int **buffer)
+{
+ char *tmp, *tmpstr, *origstr;
+ unsigned int currbufp = 0, i;
+ unsigned int count = 0, from = 0, to = 0;
+
+ /* Remove square braces */
+ if (array[0] == '[')
+ array ++;
+ if (array[strlen(array)-1] == ']')
+ array[strlen(array)-1] = ',';
+
+ from = strlen(array);
+ origstr = (char *)malloc(sizeof(char) * from);
+ strcpy(origstr, array);
+
+ tmpstr = (char *)strtok(array, ",");
+ /* First loop checks for how big int array we need to allocate */
+ while (tmpstr != NULL) {
+ errno = 0;
+ if ((tmp = strchr(tmpstr, '-')) != NULL) {
+ *tmp = ' ';
+ if (convert_arguint(tmpstr, &from) ||
+ convert_arguint(tmp, &to)) {
+ free(origstr);
+ return (EINVAL);
+ }
+
+ count += to - from + 1;
+ } else {
+ if (convert_arguint(tmpstr, &from)) {
+ free(origstr);
+ return (EINVAL);
+ }
+ count++;
+ }
+ tmpstr = (char *)strtok(NULL, ",");
+ }
+
+ if (count == 0)
+ goto out;
+
+ /* Allocate buffer of ints */
+ tmpstr = (char *)strtok(origstr, ",");
+ *buffer = malloc(count * sizeof(int));
+
+ /* Second loop is just inserting converted values into int array */
+ while (tmpstr != NULL) {
+ errno = 0;
+ if ((tmp = strchr(tmpstr, '-')) != NULL) {
+ *tmp = ' ';
+ from = strtol(tmpstr, NULL, 0);
+ to = strtol(tmp, NULL, 0);
+ tmpstr = strtok(NULL, ",");
+ for (i = from; i <= to; i ++)
+ (*buffer)[currbufp++] = i;
+ continue;
+ }
+ errno = 0;
+ from = (int)strtol(tmpstr, NULL, 0);
+ (*buffer)[currbufp++] = from;
+ tmpstr = (char *)strtok(NULL, ",");
+ }
+out:
+ free(origstr);
+ return (count);
+}
+
+/* Convert logoutput strings literals into appropriate ints. */
+static uint8_t
+logoutputtoint(char *logoutput, int *output)
+{
+ int out;
+
+ if (strcmp(logoutput, "file") == 0)
+ out = NANDSIM_OUTPUT_FILE;
+
+ else if (strcmp(logoutput, "console") == 0)
+ out = NANDSIM_OUTPUT_CONSOLE;
+
+ else if (strcmp(logoutput, "ram") == 0)
+ out = NANDSIM_OUTPUT_RAM;
+
+ else if (strcmp(logoutput, "none") == 0)
+ out = NANDSIM_OUTPUT_NONE;
+ else
+ out = -1;
+
+ *output = out;
+
+ if (out == -1)
+ return (EINVAL);
+ else
+ return (0);
+}
+
+static int
+configure_sim(const char *devfname, struct rcfile *f)
+{
+ struct sim_param sim_conf;
+ char buf[255];
+ int err, tmpv, fd;
+
+ err = rc_getint(f, "sim", 0, "log_level", &tmpv);
+
+ if (tmpv < 0 || tmpv > 255 || err) {
+ error("Bad log level specified (%d)\n", tmpv);
+ return (ENOTSUP);
+ } else
+ sim_conf.log_level = tmpv;
+
+ rc_getstring(f, "sim", 0, "log_output", 255, (char *)&buf);
+
+ tmpv = -1;
+ err = logoutputtoint((char *)&buf, &tmpv);
+ if (err) {
+ error("Log output specified in config file does not seem to "
+ "be valid (%s)!", (char *)&buf);
+ return (ENOTSUP);
+ }
+
+ sim_conf.log_output = tmpv;
+
+ fd = open(devfname, O_RDWR);
+ if (fd == -1) {
+ error("could not open simulator device file (%s)!",
+ devfname);
+ return (EX_OSFILE);
+ }
+
+ err = ioctl(fd, NANDSIM_SIM_PARAM, &sim_conf);
+ if (err) {
+ error("simulator parameters could not be modified: %s",
+ strerror(errno));
+ close(fd);
+ return (ENXIO);
+ }
+
+ close(fd);
+ return (EX_OK);
+}
+
+static int
+create_ctrls(struct rcfile *f, struct sim_ctrl **ctrls, int *cnt)
+{
+ int count, i;
+ struct sim_ctrl *ctrlsptr;
+
+ count = rc_getsectionscount(f, "ctrl");
+ if (count > MAX_SIM_DEV) {
+ error("Too many CTRL sections specified(%d)", count);
+ return (ENOTSUP);
+ } else if (count == 0) {
+ error("No ctrl sections specified");
+ return (ENOENT);
+ }
+
+ ctrlsptr = (struct sim_ctrl *)malloc(sizeof(struct sim_ctrl) * count);
+ if (ctrlsptr == NULL) {
+ error("Could not allocate memory for ctrl configuration");
+ return (ENOMEM);
+ }
+
+ for (i = 0; i < count; i++) {
+ bzero((void *)&ctrl_conf, sizeof(ctrl_conf));
+
+ /*
+ * ECC layout have to end up with 0xffff, so
+ * we're filling buffer with 0xff. If ecc_layout is
+ * defined in config file, values will be overriden.
+ */
+ memset((void *)&ctrl_conf.ecc_layout, 0xff,
+ sizeof(ctrl_conf.ecc_layout));
+
+ if (validate_section_config(f, "ctrl", i) != 0) {
+ free(ctrlsptr);
+ return (EINVAL);
+ }
+
+ if (parse_section(f, "ctrl", i) != 0) {
+ free(ctrlsptr);
+ return (EINVAL);
+ }
+
+ memcpy(&ctrlsptr[i], &ctrl_conf, sizeof(ctrl_conf));
+ /* Try to create ctrl with config parsed */
+ debug("NUM=%d\nNUM_CS=%d\nECC=%d\nFILENAME=%s\nECC_LAYOUT[0]"
+ "=%d\nECC_LAYOUT[1]=%d\n\n",
+ ctrlsptr[i].num, ctrlsptr[i].num_cs, ctrlsptr[i].ecc,
+ ctrlsptr[i].filename, ctrlsptr[i].ecc_layout[0],
+ ctrlsptr[i].ecc_layout[1]);
+ }
+ *cnt = count;
+ *ctrls = ctrlsptr;
+ return (0);
+}
+
+static void
+destroy_ctrls(struct sim_ctrl *ctrls)
+{
+
+ free(ctrls);
+}
+
+static int
+create_chips(struct rcfile *f, struct sim_chip **chips, int *cnt)
+{
+ struct sim_chip *chipsptr;
+ int count, i;
+
+ count = rc_getsectionscount(f, "chip");
+ if (count > (MAX_CTRL_CS * MAX_SIM_DEV)) {
+ error("Too many chip sections specified(%d)", count);
+ return (ENOTSUP);
+ } else if (count == 0) {
+ error("No chip sections specified");
+ return (ENOENT);
+ }
+
+ chipsptr = (struct sim_chip *)malloc(sizeof(struct sim_chip) * count);
+ if (chipsptr == NULL) {
+ error("Could not allocate memory for chip configuration");
+ return (ENOMEM);
+ }
+
+ for (i = 0; i < count; i++) {
+ bzero((void *)&chip_conf, sizeof(chip_conf));
+
+ /*
+ * Bad block map have to end up with 0xffff, so
+ * we're filling array with 0xff. If bad block map is
+ * defined in config file, values will be overriden.
+ */
+ memset((void *)&chip_conf.bad_block_map, 0xff,
+ sizeof(chip_conf.bad_block_map));
+
+ if (validate_section_config(f, "chip", i) != 0) {
+ free(chipsptr);
+ return (EINVAL);
+ }
+
+ if (parse_section(f, "chip", i) != 0) {
+ free(chipsptr);
+ return (EINVAL);
+ }
+
+ memcpy(&chipsptr[i], &chip_conf, sizeof(chip_conf));
+
+ /* Try to create chip with config parsed */
+ debug("CHIP:\nNUM=%d\nCTRL_NUM=%d\nDEVID=%d\nMANID=%d\n"
+ "PAGE_SZ=%d\nOOBSZ=%d\nREAD_T=%d\nDEVMODEL=%s\n"
+ "MAN=%s\nCOLADDRCYCLES=%d\nROWADDRCYCLES=%d\nCHWIDTH=%d\n"
+ "PGS/BLK=%d\nBLK/LUN=%d\nLUNS=%d\nERR_RATIO=%d\n"
+ "WEARLEVEL=%d\nISWP=%d\n\n\n\n",
+ chipsptr[i].num, chipsptr[i].ctrl_num,
+ chipsptr[i].device_id, chipsptr[i].manufact_id,
+ chipsptr[i].page_size, chipsptr[i].oob_size,
+ chipsptr[i].read_time, chipsptr[i].device_model,
+ chipsptr[i].manufacturer, chipsptr[i].col_addr_cycles,
+ chipsptr[i].row_addr_cycles, chipsptr[i].width,
+ chipsptr[i].pgs_per_blk, chipsptr[i].blks_per_lun,
+ chipsptr[i].luns, chipsptr[i].error_ratio,
+ chipsptr[i].wear_level, chipsptr[i].is_wp);
+ }
+ *cnt = count;
+ *chips = chipsptr;
+ return (0);
+}
+
+static void
+destroy_chips(struct sim_chip *chips)
+{
+
+ free(chips);
+}
+
+int
+parse_config(char *cfgfname, const char *devfname)
+{
+ int err = 0, fd;
+ unsigned int chipsectionscnt, ctrlsectionscnt, i;
+ struct rcfile *f;
+ struct sim_chip *chips;
+ struct sim_ctrl *ctrls;
+
+ err = rc_open(cfgfname, "r", &f);
+ if (err) {
+ error("could not open configuration file (%s)", cfgfname);
+ return (EX_NOINPUT);
+ }
+
+ /* First, try to configure simulator itself. */
+ if (configure_sim(devfname, f) != EX_OK) {
+ rc_close(f);
+ return (EINVAL);
+ }
+
+ debug("SIM CONFIGURED!\n");
+ /* Then create controllers' configs */
+ if (create_ctrls(f, &ctrls, &ctrlsectionscnt) != 0) {
+ rc_close(f);
+ return (ENXIO);
+ }
+ debug("CTRLS CONFIG READ!\n");
+
+ /* Then create chips' configs */
+ if (create_chips(f, &chips, &chipsectionscnt) != 0) {
+ destroy_ctrls(ctrls);
+ rc_close(f);
+ return (ENXIO);
+ }
+ debug("CHIPS CONFIG READ!\n");
+
+ if (validate_ctrls(ctrls, ctrlsectionscnt) != 0) {
+ destroy_ctrls(ctrls);
+ destroy_chips(chips);
+ rc_close(f);
+ return (EX_SOFTWARE);
+ }
+ if (validate_chips(chips, chipsectionscnt, ctrls,
+ ctrlsectionscnt) != 0) {
+ destroy_ctrls(ctrls);
+ destroy_chips(chips);
+ rc_close(f);
+ return (EX_SOFTWARE);
+ }
+
+ /* Open device */
+ fd = open(devfname, O_RDWR);
+ if (fd == -1) {
+ error("could not open simulator device file (%s)!",
+ devfname);
+ rc_close(f);
+ destroy_chips(chips);
+ destroy_ctrls(ctrls);
+ return (EX_OSFILE);
+ }
+
+ debug("SIM CONFIG STARTED!\n");
+
+ /* At this stage, both ctrls' and chips' configs should be valid */
+ for (i = 0; i < ctrlsectionscnt; i++) {
+ err = ioctl(fd, NANDSIM_CREATE_CTRL, &ctrls[i]);
+ if (err) {
+ if (err == EEXIST)
+ error("Controller#%d already created\n",
+ ctrls[i].num);
+ else if (err == EINVAL)
+ error("Incorrect controler number (%d)\n",
+ ctrls[i].num);
+ else
+ error("Could not created controller#%d\n",
+ ctrls[i].num);
+ /* Errors during controller creation stops parsing */
+ close(fd);
+ rc_close(f);
+ destroy_ctrls(ctrls);
+ destroy_chips(chips);
+ return (ENXIO);
+ }
+ debug("CTRL#%d CONFIG STARTED!\n", i);
+ }
+
+ for (i = 0; i < chipsectionscnt; i++) {
+ err = ioctl(fd, NANDSIM_CREATE_CHIP, &chips[i]);
+ if (err) {
+ if (err == EEXIST)
+ error("Chip#%d for controller#%d already "
+ "created\n", chips[i].num,
+ chips[i].ctrl_num);
+ else if (err == EINVAL)
+ error("Incorrect chip number (%d:%d)\n",
+ chips[i].num, chips[i].ctrl_num);
+ else
+ error("Could not create chip (%d:%d)\n",
+ chips[i].num, chips[i].ctrl_num);
+ error("Could not start chip#%d\n", i);
+ destroy_chips(chips);
+ destroy_ctrls(ctrls);
+ close(fd);
+ rc_close(f);
+ return (ENXIO);
+ }
+ }
+ debug("CHIPS CONFIG STARTED!\n");
+
+ close(fd);
+ rc_close(f);
+ destroy_chips(chips);
+ destroy_ctrls(ctrls);
+ return (0);
+}
+
+/*
+ * Function tries to get appropriate value for given key, convert it to
+ * array of ints (of given size), and perform all the neccesary checks and
+ * conversions.
+ */
+static int
+get_argument_intarray(const char *sect_name, int sectno,
+ struct nandsim_key *key, struct rcfile *f)
+{
+ char strbuf[STRBUFSIZ];
+ int *intbuf;
+ int getres;
+ uint32_t cnt, i = 0;
+
+ getres = rc_getstring(f, sect_name, sectno, key->keyname, STRBUFSIZ,
+ (char *)&strbuf);
+
+ if (getres != 0) {
+ if (key->mandatory != 0) {
+ error(MSG_MANDATORYKEYMISSING, key->keyname,
+ sect_name);
+ return (EINVAL);
+ } else
+ /* Non-mandatory key, not present -- skip */
+ return (0);
+ }
+ cnt = parse_intarray((char *)&strbuf, &intbuf);
+ cnt = (cnt <= key->maxlength) ? cnt : key->maxlength;
+
+ for (i = 0; i < cnt; i++) {
+ if (SIZE(key->valuetype) == SIZE_8)
+ *((uint8_t *)(key->field) + i) =
+ (uint8_t)intbuf[i];
+ else if (SIZE(key->valuetype) == SIZE_16)
+ *((uint16_t *)(key->field) + i) =
+ (uint16_t)intbuf[i];
+ else
+ *((uint32_t *)(key->field) + i) =
+ (uint32_t)intbuf[i];
+ }
+ free(intbuf);
+ return (0);
+}
+
+/*
+ * Function tries to get appropriate value for given key, convert it to
+ * int of certain length.
+ */
+static int
+get_argument_int(const char *sect_name, int sectno, struct nandsim_key *key,
+ struct rcfile *f)
+{
+ int getres;
+ uint32_t val;
+
+ getres = rc_getint(f, sect_name, sectno, key->keyname, &val);
+ if (getres != 0) {
+
+ if (key->mandatory != 0) {
+ error(MSG_MANDATORYKEYMISSING, key->keyname,
+ sect_name);
+
+ return (EINVAL);
+ } else
+ /* Non-mandatory key, not present -- skip */
+ return (0);
+ }
+ if (SIZE(key->valuetype) == SIZE_8)
+ *(uint8_t *)(key->field) = (uint8_t)val;
+ else if (SIZE(key->valuetype) == SIZE_16)
+ *(uint16_t *)(key->field) = (uint16_t)val;
+ else
+ *(uint32_t *)(key->field) = (uint32_t)val;
+ return (0);
+}
+
+/* Function tries to get string value for given key */
+static int
+get_argument_string(const char *sect_name, int sectno,
+ struct nandsim_key *key, struct rcfile *f)
+{
+ char strbuf[STRBUFSIZ];
+ int getres;
+
+ getres = rc_getstring(f, sect_name, sectno, key->keyname, STRBUFSIZ,
+ strbuf);
+
+ if (getres != 0) {
+ if (key->mandatory != 0) {
+ error(MSG_MANDATORYKEYMISSING, key->keyname,
+ sect_name);
+ return (1);
+ } else
+ /* Non-mandatory key, not present -- skip */
+ return (0);
+ }
+ strncpy(key->field, (char *)&strbuf, (size_t)(key->maxlength - 1));
+ return (0);
+}
+
+/* Function tries to get on/off value for given key */
+static int
+get_argument_bool(const char *sect_name, int sectno, struct nandsim_key *key,
+ struct rcfile *f)
+{
+ int getres, val;
+
+ getres = rc_getbool(f, sect_name, sectno, key->keyname, &val);
+ if (getres != 0) {
+ if (key->mandatory != 0) {
+ error(MSG_MANDATORYKEYMISSING, key->keyname,
+ sect_name);
+ return (1);
+ } else
+ /* Non-mandatory key, not present -- skip */
+ return (0);
+ }
+ *(uint8_t *)key->field = (uint8_t)val;
+ return (0);
+}
+
+int
+parse_section(struct rcfile *f, const char *sect_name, int sectno)
+{
+ struct nandsim_key *key;
+ struct nandsim_section *sect = (struct nandsim_section *)&sections;
+ int getres = 0;
+
+ while (1) {
+ if (sect == NULL)
+ return (EINVAL);
+
+ if (strcmp(sect->name, sect_name) == 0)
+ break;
+ else
+ sect++;
+ }
+ key = sect->keys;
+ do {
+ debug("->Section: %s, Key: %s, type: %d, size: %d",
+ sect_name, key->keyname, TYPE(key->valuetype),
+ SIZE(key->valuetype)/2);
+
+ switch (TYPE(key->valuetype)) {
+ case VALUE_UINT:
+ /* Single int value */
+ getres = get_argument_int(sect_name, sectno, key, f);
+
+ if (getres != 0)
+ return (getres);
+
+ break;
+ case VALUE_UINTARRAY:
+ /* Array of ints */
+ getres = get_argument_intarray(sect_name,
+ sectno, key, f);
+
+ if (getres != 0)
+ return (getres);
+
+ break;
+ case VALUE_STRING:
+ /* Array of chars */
+ getres = get_argument_string(sect_name, sectno, key,
+ f);
+
+ if (getres != 0)
+ return (getres);
+
+ break;
+ case VALUE_BOOL:
+ /* Boolean value (true/false/on/off/yes/no) */
+ getres = get_argument_bool(sect_name, sectno, key,
+ f);
+
+ if (getres != 0)
+ return (getres);
+
+ break;
+ }
+ } while ((++key)->keyname != NULL);
+
+ return (0);
+}
+
+static uint8_t
+validate_chips(struct sim_chip *chips, int chipcnt,
+ struct sim_ctrl *ctrls, int ctrlcnt)
+{
+ int cchipcnt, i, width, j, id, max;
+
+ cchipcnt = chipcnt;
+ for (chipcnt -= 1; chipcnt >= 0; chipcnt--) {
+ if (chips[chipcnt].num >= MAX_CTRL_CS) {
+ error("chip no. too high (%d)!!\n",
+ chips[chipcnt].num);
+ return (EINVAL);
+ }
+
+ if (chips[chipcnt].ctrl_num >= MAX_SIM_DEV) {
+ error("controller no. too high (%d)!!\n",
+ chips[chipcnt].ctrl_num);
+ return (EINVAL);
+ }
+
+ if (chips[chipcnt].width != 8 &&
+ chips[chipcnt].width != 16) {
+ error("invalid width:%d for chip#%d",
+ chips[chipcnt].width, chips[chipcnt].num);
+ return (EINVAL);
+ }
+
+ /* Check if page size is > 512 and if its power of 2 */
+ if (chips[chipcnt].page_size < 512 ||
+ (chips[chipcnt].page_size &
+ (chips[chipcnt].page_size - 1)) != 0) {
+ error("invalid page size:%d for chip#%d at ctrl#%d!!"
+ "\n", chips[chipcnt].page_size,
+ chips[chipcnt].num,
+ chips[chipcnt].ctrl_num);
+ return (EINVAL);
+ }
+
+ /* Check if controller no. ctrl_num is configured */
+ for (i = 0, id = -1; i < ctrlcnt && id == -1; i++)
+ if (ctrls[i].num == chips[chipcnt].ctrl_num)
+ id = i;
+
+ if (i == ctrlcnt && id == -1) {
+ error("Missing configuration for controller %d"
+ " (at least one chip is connected to it)",
+ chips[chipcnt].ctrl_num);
+ return (EINVAL);
+ } else {
+ /*
+ * Controller is configured -> check oob_size
+ * validity
+ */
+ i = 0;
+ max = ctrls[id].ecc_layout[0];
+ while (i < MAX_ECC_BYTES &&
+ ctrls[id].ecc_layout[i] != 0xffff) {
+
+ if (ctrls[id].ecc_layout[i] > max)
+ max = ctrls[id].ecc_layout[i];
+ i++;
+ }
+
+ if (chips[chipcnt].oob_size < (unsigned)i) {
+ error("OOB size for chip#%d at ctrl#%d is "
+ "smaller than ecc layout length!",
+ chips[chipcnt].num,
+ chips[chipcnt].ctrl_num);
+ exit(EINVAL);
+ }
+
+ if (chips[chipcnt].oob_size < (unsigned)max) {
+ error("OOB size for chip#%d at ctrl#%d is "
+ "smaller than maximal ecc position in "
+ "defined layout!", chips[chipcnt].num,
+ chips[chipcnt].ctrl_num);
+ exit(EINVAL);
+ }
+
+
+ }
+
+ if ((chips[chipcnt].erase_time < DELAYTIME_MIN ||
+ chips[chipcnt].erase_time > DELAYTIME_MAX) &&
+ chips[chipcnt].erase_time != 0) {
+ error("Invalid erase time value for chip#%d at "
+ "ctrl#%d",
+ chips[chipcnt].num,
+ chips[chipcnt].ctrl_num);
+ return (EINVAL);
+ }
+
+ if ((chips[chipcnt].prog_time < DELAYTIME_MIN ||
+ chips[chipcnt].prog_time > DELAYTIME_MAX) &&
+ chips[chipcnt].prog_time != 0) {
+ error("Invalid prog time value for chip#%d at "
+ "ctr#%d!",
+ chips[chipcnt].num,
+ chips[chipcnt].ctrl_num);
+ return (EINVAL);
+ }
+
+ if ((chips[chipcnt].read_time < DELAYTIME_MIN ||
+ chips[chipcnt].read_time > DELAYTIME_MAX) &&
+ chips[chipcnt].read_time != 0) {
+ error("Invalid read time value for chip#%d at "
+ "ctrl#%d!",
+ chips[chipcnt].num,
+ chips[chipcnt].ctrl_num);
+ return (EINVAL);
+ }
+ }
+ /* Check if chips attached to the same controller, have same width */
+ for (i = 0; i < ctrlcnt; i++) {
+ width = -1;
+ for (j = 0; j < cchipcnt; j++) {
+ if (chips[j].ctrl_num == i) {
+ if (width == -1) {
+ width = chips[j].width;
+ } else {
+ if (width != chips[j].width) {
+ error("Chips attached to "
+ "ctrl#%d have different "
+ "widths!\n", i);
+ return (EINVAL);
+ }
+ }
+ }
+ }
+ }
+
+ return (0);
+}
+
+static uint8_t
+validate_ctrls(struct sim_ctrl *ctrl, int ctrlcnt)
+{
+ for (ctrlcnt -= 1; ctrlcnt >= 0; ctrlcnt--) {
+ if (ctrl[ctrlcnt].num > MAX_SIM_DEV) {
+ error("Controller no. too high (%d)!!\n",
+ ctrl[ctrlcnt].num);
+ return (EINVAL);
+ }
+ if (ctrl[ctrlcnt].num_cs > MAX_CTRL_CS) {
+ error("Too many CS (%d)!!\n", ctrl[ctrlcnt].num_cs);
+ return (EINVAL);
+ }
+ if (ctrl[ctrlcnt].ecc != 0 && ctrl[ctrlcnt].ecc != 1) {
+ error("ECC is set to neither 0 nor 1 !\n");
+ return (EINVAL);
+ }
+ }
+
+ return (0);
+}
+
+static int validate_section_config(struct rcfile *f, const char *sect_name,
+ int sectno)
+{
+ struct nandsim_key *key;
+ struct nandsim_section *sect;
+ char **keys_tbl;
+ int i, match;
+
+ for (match = 0, sect = (struct nandsim_section *)&sections;
+ sect != NULL; sect++) {
+ if (strcmp(sect->name, sect_name) == 0) {
+ match = 1;
+ break;
+ }
+ }
+
+ if (match == 0)
+ return (EINVAL);
+
+ keys_tbl = rc_getkeys(f, sect_name, sectno);
+ if (keys_tbl == NULL)
+ return (ENOMEM);
+
+ for (i = 0; keys_tbl[i] != NULL; i++) {
+ key = sect->keys;
+ match = 0;
+ do {
+ if (strcmp(keys_tbl[i], key->keyname) == 0) {
+ match = 1;
+ break;
+ }
+ } while ((++key)->keyname != NULL);
+
+ if (match == 0) {
+ error("Invalid key in config file: %s\n", keys_tbl[i]);
+ free(keys_tbl);
+ return (EINVAL);
+ }
+ }
+
+ free(keys_tbl);
+ return (0);
+}
diff --git a/usr.sbin/nandsim/nandsim_cfgparse.h b/usr.sbin/nandsim/nandsim_cfgparse.h
new file mode 100644
index 0000000..b9c642a
--- /dev/null
+++ b/usr.sbin/nandsim/nandsim_cfgparse.h
@@ -0,0 +1,86 @@
+/*-
+ * Copyright (C) 2009-2012 Semihalf
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NANDSIM_CONFPARSER_H_
+#define _NANDSIM_CONFPARSER_H_
+
+#define VALUE_UINT 0x08
+#define VALUE_INT 0x10
+#define VALUE_UINTARRAY 0x18
+#define VALUE_INTARRAY 0x20
+#define VALUE_STRING 0x28
+#define VALUE_CHAR 0x40
+#define VALUE_BOOL 0x48
+
+#define SIZE_8 0x01
+#define SIZE_16 0x02
+#define SIZE_32 0x04
+
+#include "nandsim_rcfile.h"
+
+/*
+ * keyname = name of a key,
+ * mandatory = is key mandatory in section belonging to, 0=false 1=true
+ * valuetype = what kind of value is assigned to that key, e.g.
+ * VALUE_UINT | SIZE_8 -- unsigned uint size 8 bits;
+ * VALUE_UINTARRAY | SIZE_8 -- array of uints 8-bit long;
+ * VALUE_BOOL -- 'on', 'off','true','false','yes' or 'no'
+ * literals;
+ * VALUE_STRING -- strings
+ * field = ptr to the field that should hold value for parsed value
+ * maxlength = contains maximum length of an array (used only with either
+ * VALUE_STRING or VALUE_(U)INTARRAY value types.
+ */
+struct nandsim_key {
+ const char *keyname;
+ uint8_t mandatory;
+ uint8_t valuetype;
+ void *field;
+ uint32_t maxlength;
+};
+struct nandsim_section {
+ const char *name;
+ struct nandsim_key *keys;
+};
+
+struct nandsim_config {
+ struct sim_param **simparams;
+ struct sim_chip **simchips;
+ struct sim_ctrl **simctrls;
+ int chipcnt;
+ int ctrlcnt;
+};
+
+int parse_intarray(char *, int **);
+int parse_config(char *, const char *);
+int parse_section(struct rcfile *, const char *, int);
+int compare_configs(struct nandsim_config *, struct nandsim_config *);
+int convert_argint(char *, int *);
+int convert_arguint(char *, unsigned int *);
+
+#endif /* _NANDSIM_CONFPARSER_H_ */
diff --git a/usr.sbin/nandsim/nandsim_rcfile.c b/usr.sbin/nandsim/nandsim_rcfile.c
new file mode 100644
index 0000000..0f99e7b
--- /dev/null
+++ b/usr.sbin/nandsim/nandsim_rcfile.c
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 1999, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: FreeBSD: src/lib/libncp/ncpl_rcfile.c,v 1.5 2007/01/09 23:27:39 imp Exp
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <unistd.h>
+
+#include "nandsim_rcfile.h"
+
+SLIST_HEAD(rcfile_head, rcfile);
+static struct rcfile_head pf_head = {NULL};
+static struct rcsection *rc_findsect(struct rcfile *rcp,
+ const char *sectname, int sect_id);
+static struct rcsection *rc_addsect(struct rcfile *rcp,
+ const char *sectname);
+static int rc_sect_free(struct rcsection *rsp);
+static struct rckey *rc_sect_findkey(struct rcsection *rsp,
+ const char *keyname);
+static struct rckey *rc_sect_addkey(struct rcsection *rsp, const char *name,
+ char *value);
+static void rc_key_free(struct rckey *p);
+static void rc_parse(struct rcfile *rcp);
+
+static struct rcfile* rc_find(const char *filename);
+
+/*
+ * open rcfile and load its content, if already open - return previous handle
+ */
+int
+rc_open(const char *filename, const char *mode,struct rcfile **rcfile)
+{
+ struct rcfile *rcp;
+ FILE *f;
+ rcp = rc_find(filename);
+ if (rcp) {
+ *rcfile = rcp;
+ return (0);
+ }
+ f = fopen (filename, mode);
+ if (f == NULL)
+ return errno;
+ rcp = malloc(sizeof(struct rcfile));
+ if (rcp == NULL) {
+ fclose(f);
+ return ENOMEM;
+ }
+ bzero(rcp, sizeof(struct rcfile));
+ rcp->rf_name = strdup(filename);
+ rcp->rf_f = f;
+ SLIST_INSERT_HEAD(&pf_head, rcp, rf_next);
+ rc_parse(rcp);
+ *rcfile = rcp;
+ return (0);
+}
+
+int
+rc_close(struct rcfile *rcp)
+{
+ struct rcsection *p,*n;
+
+ fclose(rcp->rf_f);
+ for (p = SLIST_FIRST(&rcp->rf_sect); p; ) {
+ n = p;
+ p = SLIST_NEXT(p,rs_next);
+ rc_sect_free(n);
+ }
+ free(rcp->rf_name);
+ SLIST_REMOVE(&pf_head, rcp, rcfile, rf_next);
+ free(rcp);
+ return (0);
+}
+
+static struct rcfile*
+rc_find(const char *filename)
+{
+ struct rcfile *p;
+
+ SLIST_FOREACH(p, &pf_head, rf_next)
+ if (strcmp (filename, p->rf_name) == 0)
+ return (p);
+ return (0);
+}
+
+/* Find section with given name and id */
+static struct rcsection *
+rc_findsect(struct rcfile *rcp, const char *sectname, int sect_id)
+{
+ struct rcsection *p;
+
+ SLIST_FOREACH(p, &rcp->rf_sect, rs_next)
+ if (strcmp(p->rs_name, sectname) == 0 && p->rs_id == sect_id)
+ return (p);
+ return (NULL);
+}
+
+static struct rcsection *
+rc_addsect(struct rcfile *rcp, const char *sectname)
+{
+ struct rcsection *p;
+ int id = 0;
+ p = rc_findsect(rcp, sectname, 0);
+ if (p) {
+ /*
+ * If section with that name already exists -- add one more,
+ * same named, but with different id (higher by one)
+ */
+ while (p != NULL) {
+ id = p->rs_id + 1;
+ p = rc_findsect(rcp, sectname, id);
+ }
+ }
+ p = malloc(sizeof(*p));
+ if (!p)
+ return (NULL);
+ p->rs_name = strdup(sectname);
+ p->rs_id = id;
+ SLIST_INIT(&p->rs_keys);
+ SLIST_INSERT_HEAD(&rcp->rf_sect, p, rs_next);
+ return (p);
+}
+
+static int
+rc_sect_free(struct rcsection *rsp)
+{
+ struct rckey *p,*n;
+
+ for (p = SLIST_FIRST(&rsp->rs_keys); p; ) {
+ n = p;
+ p = SLIST_NEXT(p,rk_next);
+ rc_key_free(n);
+ }
+ free(rsp->rs_name);
+ free(rsp);
+ return (0);
+}
+
+static struct rckey *
+rc_sect_findkey(struct rcsection *rsp, const char *keyname)
+{
+ struct rckey *p;
+
+ SLIST_FOREACH(p, &rsp->rs_keys, rk_next)
+ if (strcmp(p->rk_name, keyname)==0)
+ return (p);
+ return (NULL);
+}
+
+static struct rckey *
+rc_sect_addkey(struct rcsection *rsp, const char *name, char *value)
+{
+ struct rckey *p;
+ p = rc_sect_findkey(rsp, name);
+ if (p) {
+ free(p->rk_value);
+ } else {
+ p = malloc(sizeof(*p));
+ if (!p)
+ return (NULL);
+ SLIST_INSERT_HEAD(&rsp->rs_keys, p, rk_next);
+ p->rk_name = strdup(name);
+ }
+ p->rk_value = value ? strdup(value) : strdup("");
+ return (p);
+}
+
+static void
+rc_key_free(struct rckey *p)
+{
+ free(p->rk_value);
+ free(p->rk_name);
+ free(p);
+}
+
+enum { stNewLine, stHeader, stSkipToEOL, stGetKey, stGetValue};
+
+static void
+rc_parse(struct rcfile *rcp)
+{
+ FILE *f = rcp->rf_f;
+ int state = stNewLine, c;
+ struct rcsection *rsp = NULL;
+ struct rckey *rkp = NULL;
+ char buf[2048];
+ char *next = buf, *last = &buf[sizeof(buf)-1];
+
+ while ((c = getc (f)) != EOF) {
+ if (c == '\r')
+ continue;
+ if (state == stNewLine) {
+ next = buf;
+ if (isspace(c))
+ continue; /* skip leading junk */
+ if (c == '[') {
+ state = stHeader;
+ rsp = NULL;
+ continue;
+ }
+ if (c == '#' || c == ';') {
+ state = stSkipToEOL;
+ } else { /* something meaningful */
+ state = stGetKey;
+ }
+ }
+ if (state == stSkipToEOL || next == last) {/* ignore long lines */
+ if (c == '\n') {
+ state = stNewLine;
+ next = buf;
+ }
+ continue;
+ }
+ if (state == stHeader) {
+ if (c == ']') {
+ *next = 0;
+ next = buf;
+ rsp = rc_addsect(rcp, buf);
+ state = stSkipToEOL;
+ } else
+ *next++ = c;
+ continue;
+ }
+ if (state == stGetKey) {
+ if (c == ' ' || c == '\t')/* side effect: 'key name='*/
+ continue; /* become 'keyname=' */
+ if (c == '\n') { /* silently ignore ... */
+ state = stNewLine;
+ continue;
+ }
+ if (c != '=') {
+ *next++ = c;
+ continue;
+ }
+ *next = 0;
+ if (rsp == NULL) {
+ fprintf(stderr, "Key '%s' defined before "
+ "section\n", buf);
+ state = stSkipToEOL;
+ continue;
+ }
+ rkp = rc_sect_addkey(rsp, buf, NULL);
+ next = buf;
+ state = stGetValue;
+ continue;
+ }
+ /* only stGetValue left */
+ if (state != stGetValue) {
+ fprintf(stderr, "Well, I can't parse file "
+ "'%s'\n",rcp->rf_name);
+ state = stSkipToEOL;
+ }
+ if (c != '\n') {
+ *next++ = c;
+ continue;
+ }
+ *next = 0;
+ rkp->rk_value = strdup(buf);
+ state = stNewLine;
+ rkp = NULL;
+ } /* while */
+ if (c == EOF && state == stGetValue) {
+ *next = 0;
+ rkp->rk_value = strdup(buf);
+ }
+}
+
+int
+rc_getstringptr(struct rcfile *rcp, const char *section, int sect_id,
+ const char *key, char **dest)
+{
+ struct rcsection *rsp;
+ struct rckey *rkp;
+
+ *dest = NULL;
+ rsp = rc_findsect(rcp, section, sect_id);
+ if (!rsp)
+ return (ENOENT);
+ rkp = rc_sect_findkey(rsp,key);
+ if (!rkp)
+ return (ENOENT);
+ *dest = rkp->rk_value;
+ return (0);
+}
+
+int
+rc_getstring(struct rcfile *rcp, const char *section, int sect_id,
+ const char *key, unsigned int maxlen, char *dest)
+{
+ char *value;
+ int error;
+
+ error = rc_getstringptr(rcp, section, sect_id, key, &value);
+ if (error)
+ return (error);
+ if (strlen(value) >= maxlen) {
+ fprintf(stderr, "line too long for key '%s' in section '%s',"
+ "max = %d\n",key, section, maxlen);
+ return (EINVAL);
+ }
+ strcpy(dest,value);
+ return (0);
+}
+
+int
+rc_getint(struct rcfile *rcp, const char *section, int sect_id,
+ const char *key, int *value)
+{
+ struct rcsection *rsp;
+ struct rckey *rkp;
+
+ rsp = rc_findsect(rcp, section, sect_id);
+ if (!rsp)
+ return (ENOENT);
+ rkp = rc_sect_findkey(rsp,key);
+ if (!rkp)
+ return (ENOENT);
+ errno = 0;
+ *value = strtol(rkp->rk_value,NULL,0);
+ if (errno) {
+ fprintf(stderr, "invalid int value '%s' for key '%s' in "
+ "section '%s'\n",rkp->rk_value,key,section);
+ return (errno);
+ }
+ return (0);
+}
+
+/*
+ * 1,yes,true
+ * 0,no,false
+ */
+int
+rc_getbool(struct rcfile *rcp, const char *section, int sect_id,
+ const char *key, int *value)
+{
+ struct rcsection *rsp;
+ struct rckey *rkp;
+ char *p;
+
+ rsp = rc_findsect(rcp, section, sect_id);
+ if (!rsp)
+ return (ENOENT);
+ rkp = rc_sect_findkey(rsp,key);
+ if (!rkp)
+ return (ENOENT);
+ p = rkp->rk_value;
+ while (*p && isspace(*p)) p++;
+ if (*p == '0' || strcasecmp(p,"no") == 0 ||
+ strcasecmp(p, "false") == 0 ||
+ strcasecmp(p, "off") == 0) {
+ *value = 0;
+ return (0);
+ }
+ if (*p == '1' || strcasecmp(p,"yes") == 0 ||
+ strcasecmp(p, "true") == 0 ||
+ strcasecmp(p, "on") == 0) {
+ *value = 1;
+ return (0);
+ }
+ fprintf(stderr, "invalid boolean value '%s' for key '%s' in section "
+ "'%s' \n",p, key, section);
+ return (EINVAL);
+}
+
+/* Count how many sections with given name exists in configuration. */
+int rc_getsectionscount(struct rcfile *f, const char *sectname)
+{
+ struct rcsection *p;
+ int count = 0;
+
+ p = rc_findsect(f, sectname, 0);
+ if (p) {
+ while (p != NULL) {
+ count = p->rs_id + 1;
+ p = rc_findsect(f, sectname, count);
+ }
+ return (count);
+ } else
+ return (0);
+}
+
+char **
+rc_getkeys(struct rcfile *rcp, const char *sectname, int sect_id)
+{
+ struct rcsection *rsp;
+ struct rckey *p;
+ char **names_tbl;
+ int i = 0, count = 0;
+
+ rsp = rc_findsect(rcp, sectname, sect_id);
+ if (rsp == NULL)
+ return (NULL);
+
+ SLIST_FOREACH(p, &rsp->rs_keys, rk_next)
+ count++;
+
+ names_tbl = malloc(sizeof(char *) * (count + 1));
+ if (names_tbl == NULL)
+ return (NULL);
+
+ SLIST_FOREACH(p, &rsp->rs_keys, rk_next)
+ names_tbl[i++] = p->rk_name;
+
+ names_tbl[i] = NULL;
+ return (names_tbl);
+}
+
diff --git a/usr.sbin/nandsim/nandsim_rcfile.h b/usr.sbin/nandsim/nandsim_rcfile.h
new file mode 100644
index 0000000..f5c3ce9
--- /dev/null
+++ b/usr.sbin/nandsim/nandsim_rcfile.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1999, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ * from: FreeBSD: src/lib/libncp/ncpl_rcfile.c,v 1.5 2007/01/09 23:27:39 imp Exp
+ */
+
+#ifndef _SIMRC_H_
+#define _SIMRC_H_
+
+#include <sys/queue.h>
+
+struct rckey {
+ SLIST_ENTRY(rckey) rk_next;
+ char *rk_name; /* key name */
+ char *rk_value; /* key value */
+};
+
+struct rcsection {
+ SLIST_ENTRY(rcsection) rs_next;
+ SLIST_HEAD(rckey_head,rckey) rs_keys; /* key list */
+ char *rs_name; /* section name */
+ int rs_id; /* allow few same named */
+};
+
+struct rcfile {
+ SLIST_ENTRY(rcfile) rf_next;
+ SLIST_HEAD(rcsec_head, rcsection) rf_sect; /* sections list */
+ char *rf_name; /* file name */
+ FILE *rf_f; /* file desc */
+};
+
+int rc_open(const char *, const char *,struct rcfile **);
+int rc_close(struct rcfile *);
+int rc_getstringptr(struct rcfile *, const char *, int, const char *,
+ char **);
+int rc_getstring(struct rcfile *, const char *, int, const char *,
+ unsigned int, char *);
+int rc_getint(struct rcfile *, const char *, int, const char *, int *);
+int rc_getbool(struct rcfile *, const char *, int, const char *, int *);
+int rc_getsectionscount(struct rcfile *, const char *);
+char **rc_getkeys(struct rcfile *, const char *, int);
+
+#endif /* _SIMRC_H_ */
diff --git a/usr.sbin/nandsim/sample.conf b/usr.sbin/nandsim/sample.conf
new file mode 100644
index 0000000..bc534e1
--- /dev/null
+++ b/usr.sbin/nandsim/sample.conf
@@ -0,0 +1,174 @@
+#-
+# Copyright (C) 2009-2012 Semihalf
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+
+#
+# Sample NANDsim configuration file.
+#
+
+#############################################################################
+#
+# [sim] General (common) simulator configuration section.
+#
+[sim]
+# log_level=0..255
+log_level=11
+
+# log_output=[none, console, ram, file]
+#
+# When log_output=file is specified, each [ctrl] section must have a
+# corresponding 'log_filename' field provided, which specifies log file name
+# to be used.
+log_output=none
+
+#############################################################################
+#
+# [ctrl] Controller configuration section.
+#
+# There can be a number of controllers defined for simulation, each has a
+# dedicated [ctrl] section. With a given controller there are associated
+# subordinate NAND chips, which are tied to chip select lines.
+#
+[ctrl]
+# The number of this controller.
+# ctrl_num=0..3
+ctrl_num=0
+
+# The number of chip selects available at this controller.
+# num_cs=1..4
+num_cs=1
+
+# ECC enable flag.
+# ecc=[on|off]
+ecc=on
+
+# ECC layout. This is the list of byte offsets within OOB area, which comprise
+# the ECC contents set.
+#
+# ecc_layout=[byte1, byte2-byte3, ..byten]
+ecc_layout=[0-53]
+
+# Absolute path to the log file for this controller.
+#log_filename=/var/log/nandsim-ctl0.log
+
+
+#############################################################################
+#
+# [chip] Chip configuration section.
+#
+# There can be a number of individual NAND chip devices defined for
+# simulation, and each has a dedicated [chip] section.
+#
+# A particular chip needs to be associated with its parent NAND controller by
+# specifying the following fields: controller number (chip_ctrl) and the chip
+# select line it is connected to (chip_cs). The chip can be connected to only
+# a single (and unique) controller:cs pair.
+#
+[chip]
+# The number of parent controller. This has to fit one of the controller
+# instance number (ctrl_num from [ctrl] section).
+# chip_ctrl=0..3
+chip_ctrl=0
+
+# Chip select line.
+# chip_cs=0..3
+chip_cs=0
+
+# ONFI device identifier.
+# device_id=0x00..0xff
+device_id=0xd3
+
+# ONFI manufacturer identifier.
+# manufacturer_id=0x00..0xff
+manufacturer_id=0xec
+
+# Textual description of the chip.
+# model="model_name"
+model="k9xxg08uxM:1GiB 3,3V 8-bit"
+
+# Textual name of the chip manufacturer.
+# manufacturer="manufacturer name"
+manufacturer="SAMSUNG"
+
+# page_size=[must be power of 2 and >= 512] (in bytes)
+page_size=2048
+# oob_size=[>0]
+oob_size=64
+# pages_per_block=n*32
+pages_per_block=64
+# blocks_per_lun=[>0]
+blocks_per_lun=4096
+# luns=1..N
+luns=1
+# column_addr_cycle=[1,2]
+column_addr_cycle=2
+# row_addr_cycle=[1,2,3]
+row_addr_cycle=3
+
+# program_time= (in us)
+program_time=0
+# erase_time= (in us)
+erase_time=0
+# read_time= (in us)
+read_time=0
+# ccs_time= (in us)
+#ccs_time=200
+
+# Simulate write-protect on the chip.
+# write_protect=[yes|no]
+#write_protect=no
+
+# Blocks wear-out threshold. Each block has a counter of program-erase cycles;
+# when this counter reaches 'wear_out' value a given block is treated as a bad
+# block (access will report error).
+#
+# Setting wear_out to 0 means that blocks will never wear out.
+#
+# wear_out=0..100000
+wear_out=50000
+
+# Errors per million read/write bytes. This simulates an accidental read/write
+# block error, which can happen in real devices with certain probability. Note
+# this isn't a bad block condition i.e. the block at which the read/write
+# operation is simulated to fail here remains usable, only the operation has
+# not succeeded (this is where ECC comes into play and is supposed to correct
+# such problems).
+#
+# error_ratio=0..1000000
+#error_ratio=50
+
+# Chip data bus width. All chips connected to the same controller must have
+# the same bus width.
+#
+# width=[8|16]
+width=8
+
+# Bad block map. NANDsim emulates bad block behavior upon accessing a block
+# with number from the specified list.
+#
+# bad_block_map=[bad_block1, bad_block2-bad_block3, ..bad_blockn]
+bad_block_map=[100-200]
+
diff --git a/usr.sbin/nandtool/Makefile b/usr.sbin/nandtool/Makefile
new file mode 100644
index 0000000..ae9de2d
--- /dev/null
+++ b/usr.sbin/nandtool/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+PROG= nandtool
+SRCS= nandtool.c nand_read.c nand_write.c nand_erase.c nand_info.c
+SRCS+= nand_readoob.c nand_writeoob.c
+BINDIR= /usr/sbin
+DPADD= ${LIBGEOM}
+LDADD= -lgeom
+MAN= nandtool.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nandtool/nand_erase.c b/usr.sbin/nandtool/nand_erase.c
new file mode 100644
index 0000000..50bfaa6
--- /dev/null
+++ b/usr.sbin/nandtool/nand_erase.c
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/disk.h>
+#include <libgeom.h>
+#include <dev/nand/nand_dev.h>
+#include "nandtool.h"
+
+int nand_erase(struct cmd_param *params)
+{
+ struct chip_param_io chip_params;
+ char *dev;
+ int fd = -1, ret = 0;
+ off_t pos, count;
+ off_t start, nblocks, i;
+ int block_size, mult;
+
+ if (!(dev = param_get_string(params, "dev"))) {
+ fprintf(stderr, "Please supply valid 'dev' parameter.\n");
+ return (1);
+ }
+
+ if (param_has_value(params, "count"))
+ count = param_get_intx(params, "count");
+ else
+ count = 1;
+
+ if ((fd = g_open(dev, 1)) < 0) {
+ perrorf("Cannot open %s", dev);
+ return (1);
+ }
+
+ if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) {
+ perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)");
+ ret = 1;
+ goto out;
+ }
+
+ block_size = chip_params.page_size * chip_params.pages_per_block;
+
+ if (param_has_value(params, "page")) {
+ pos = chip_params.page_size * param_get_intx(params, "page");
+ mult = chip_params.page_size;
+ } else if (param_has_value(params, "block")) {
+ pos = block_size * param_get_intx(params, "block");
+ mult = block_size;
+ } else if (param_has_value(params, "pos")) {
+ pos = param_get_intx(params, "pos");
+ mult = 1;
+ } else {
+ /* Erase whole chip */
+ if (ioctl(fd, DIOCGMEDIASIZE, &count) == -1) {
+ ret = 1;
+ goto out;
+ }
+
+ pos = 0;
+ mult = 1;
+ }
+
+ if (pos % block_size) {
+ fprintf(stderr, "Position must be block-size aligned!\n");
+ ret = 1;
+ goto out;
+ }
+
+ count *= mult;
+ start = pos / block_size;
+ nblocks = count / block_size;
+
+ for (i = 0; i < nblocks; i++) {
+ if (g_delete(fd, (start + i) * block_size, block_size) == -1) {
+ perrorf("Cannot erase block %d - probably a bad block",
+ start + i);
+ ret = 1;
+ }
+ }
+
+out:
+ g_close(fd);
+
+ return (ret);
+}
+
diff --git a/usr.sbin/nandtool/nand_info.c b/usr.sbin/nandtool/nand_info.c
new file mode 100644
index 0000000..38fe010
--- /dev/null
+++ b/usr.sbin/nandtool/nand_info.c
@@ -0,0 +1,86 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <libgeom.h>
+#include <sys/disk.h>
+#include <dev/nand/nand_dev.h>
+#include "nandtool.h"
+
+int nand_info(struct cmd_param *params)
+{
+ struct chip_param_io chip_params;
+ int fd = -1, ret = 0;
+ int block_size;
+ off_t chip_size, media_size;
+ const char *dev;
+
+ if ((dev = param_get_string(params, "dev")) == NULL) {
+ fprintf(stderr, "Please supply 'dev' parameter, eg. "
+ "'dev=/dev/gnand0'\n");
+ return (1);
+ }
+
+ if ((fd = g_open(dev, 1)) == -1) {
+ perrorf("Cannot open %s", dev);
+ return (1);
+ }
+
+ if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) {
+ perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)");
+ ret = 1;
+ goto out;
+ }
+
+ if (ioctl(fd, DIOCGMEDIASIZE, &media_size) == -1) {
+ perrorf("Cannot ioctl(DIOCGMEDIASIZE)");
+ ret = 1;
+ goto out;
+ }
+
+ block_size = chip_params.page_size * chip_params.pages_per_block;
+ chip_size = block_size * chip_params.blocks;
+
+ printf("Device:\t\t\t%s\n", dev);
+ printf("Page size:\t\t%d bytes\n", chip_params.page_size);
+ printf("Block size:\t\t%d bytes (%d KB)\n", block_size,
+ block_size / 1024);
+ printf("OOB size per page:\t%d bytes\n", chip_params.oob_size);
+ printf("Chip size:\t\t%jd MB\n", (uintmax_t)(chip_size / 1024 / 1024));
+ printf("Slice size:\t\t%jd MB\n",
+ (uintmax_t)(media_size / 1024 / 1024));
+
+out:
+ g_close(fd);
+
+ return (ret);
+}
diff --git a/usr.sbin/nandtool/nand_read.c b/usr.sbin/nandtool/nand_read.c
new file mode 100644
index 0000000..5267b7d
--- /dev/null
+++ b/usr.sbin/nandtool/nand_read.c
@@ -0,0 +1,139 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libgeom.h>
+#include <sys/disk.h>
+#include <dev/nand/nand_dev.h>
+#include "nandtool.h"
+
+int nand_read(struct cmd_param *params)
+{
+ struct chip_param_io chip_params;
+ int fd = -1, out_fd = -1, done = 0, ret = 0;
+ char *dev, *out;
+ int pos, count, mult, block_size;
+ uint8_t *buf = NULL;
+
+ if (!(dev = param_get_string(params, "dev"))) {
+ fprintf(stderr, "You must specify 'dev' parameter\n");
+ return (1);
+ }
+
+ if ((out = param_get_string(params, "out"))) {
+ out_fd = open(out, O_WRONLY|O_CREAT);
+ if (out_fd == -1) {
+ perrorf("Cannot open %s for writing", out);
+ return (1);
+ }
+ }
+
+ if ((fd = g_open(dev, 1)) == -1) {
+ perrorf("Cannot open %s", dev);
+ ret = 1;
+ goto out;
+ }
+
+ if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) {
+ perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)");
+ ret = 1;
+ goto out;
+ }
+
+ block_size = chip_params.page_size * chip_params.pages_per_block;
+
+ if (param_has_value(params, "page")) {
+ pos = chip_params.page_size * param_get_int(params, "page");
+ mult = chip_params.page_size;
+ } else if (param_has_value(params, "block")) {
+ pos = block_size * param_get_int(params, "block");
+ mult = block_size;
+ } else if (param_has_value(params, "pos")) {
+ pos = param_get_int(params, "pos");
+ mult = 1;
+ if (pos % chip_params.page_size) {
+ fprintf(stderr, "Position must be page-size aligned!\n");
+ ret = 1;
+ goto out;
+ }
+ } else {
+ fprintf(stderr, "You must specify one of: 'block', 'page',"
+ "'pos' arguments\n");
+ ret = 1;
+ goto out;
+ }
+
+ if (!(param_has_value(params, "count")))
+ count = mult;
+ else
+ count = param_get_int(params, "count") * mult;
+
+ if (!(buf = malloc(chip_params.page_size))) {
+ perrorf("Cannot allocate buffer [size %x]",
+ chip_params.page_size);
+ ret = 1;
+ goto out;
+ }
+
+ lseek(fd, pos, SEEK_SET);
+
+ while (done < count) {
+ if ((ret = read(fd, buf, chip_params.page_size)) !=
+ (int32_t)chip_params.page_size) {
+ perrorf("read error (read %d bytes)", ret);
+ goto out;
+ }
+
+ if (out_fd != -1) {
+ done += ret;
+ if ((ret = write(out_fd, buf, chip_params.page_size)) !=
+ (int32_t)chip_params.page_size) {
+ perrorf("write error (written %d bytes)", ret);
+ ret = 1;
+ goto out;
+ }
+ } else {
+ hexdumpoffset(buf, chip_params.page_size, done);
+ done += ret;
+ }
+ }
+
+out:
+ g_close(fd);
+ if (out_fd != -1)
+ close(out_fd);
+ if (buf)
+ free(buf);
+
+ return (ret);
+}
+
diff --git a/usr.sbin/nandtool/nand_readoob.c b/usr.sbin/nandtool/nand_readoob.c
new file mode 100644
index 0000000..37fd14b
--- /dev/null
+++ b/usr.sbin/nandtool/nand_readoob.c
@@ -0,0 +1,111 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libgeom.h>
+#include <sys/types.h>
+#include <sys/disk.h>
+#include <dev/nand/nand_dev.h>
+#include "nandtool.h"
+
+int nand_read_oob(struct cmd_param *params)
+{
+ struct chip_param_io chip_params;
+ struct nand_oob_rw req;
+ char *dev, *out;
+ int fd = -1, fd_out = -1, ret = 0;
+ int page;
+ uint8_t *buf = NULL;
+
+ if ((page = param_get_int(params, "page")) < 0) {
+ fprintf(stderr, "You must supply valid 'page' argument.\n");
+ return (1);
+ }
+
+ if (!(dev = param_get_string(params, "dev"))) {
+ fprintf(stderr, "You must supply 'dev' argument.\n");
+ return (1);
+ }
+
+ if ((out = param_get_string(params, "out"))) {
+ if ((fd_out = open(out, O_WRONLY | O_CREAT)) == -1) {
+ perrorf("Cannot open %s", out);
+ ret = 1;
+ goto out;
+ }
+ }
+
+ if ((fd = g_open(dev, 1)) == -1) {
+ perrorf("Cannot open %s", dev);
+ ret = 1;
+ goto out;
+ }
+
+ if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) {
+ perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)");
+ ret = 1;
+ goto out;
+ }
+
+ buf = malloc(chip_params.oob_size);
+ if (buf == NULL) {
+ perrorf("Cannot allocate %d bytes\n", chip_params.oob_size);
+ ret = 1;
+ goto out;
+ }
+
+ req.page = page;
+ req.len = chip_params.oob_size;
+ req.data = buf;
+
+ if (ioctl(fd, NAND_IO_OOB_READ, &req) == -1) {
+ perrorf("Cannot read OOB from %s", dev);
+ ret = 1;
+ goto out;
+ }
+
+ if (fd_out != -1)
+ write(fd_out, buf, chip_params.oob_size);
+ else
+ hexdump(buf, chip_params.oob_size);
+
+out:
+ close(fd_out);
+
+ if (fd != -1)
+ g_close(fd);
+ if (buf)
+ free(buf);
+
+ return (ret);
+}
+
diff --git a/usr.sbin/nandtool/nand_write.c b/usr.sbin/nandtool/nand_write.c
new file mode 100644
index 0000000..157c6aa
--- /dev/null
+++ b/usr.sbin/nandtool/nand_write.c
@@ -0,0 +1,143 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libgeom.h>
+#include <sys/disk.h>
+#include <dev/nand/nand_dev.h>
+#include "nandtool.h"
+
+int nand_write(struct cmd_param *params)
+{
+ struct chip_param_io chip_params;
+ char *dev, *file;
+ int in_fd = -1, ret = 0, done = 0;
+ int fd, block_size, mult, pos, count;
+ uint8_t *buf = NULL;
+
+ if (!(dev = param_get_string(params, "dev"))) {
+ fprintf(stderr, "Please supply 'dev' argument.\n");
+ return (1);
+ }
+
+ if (!(file = param_get_string(params, "in"))) {
+ fprintf(stderr, "Please supply 'in' argument.\n");
+ return (1);
+ }
+
+ if ((fd = g_open(dev, 1)) == -1) {
+ perrorf("Cannot open %s", dev);
+ return (1);
+ }
+
+ if ((in_fd = open(file, O_RDONLY)) == -1) {
+ perrorf("Cannot open file %s", file);
+ ret = 1;
+ goto out;
+ }
+
+ if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) {
+ perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)");
+ ret = 1;
+ goto out;
+ }
+
+ block_size = chip_params.page_size * chip_params.pages_per_block;
+
+ if (param_has_value(params, "page")) {
+ pos = chip_params.page_size * param_get_int(params, "page");
+ mult = chip_params.page_size;
+ } else if (param_has_value(params, "block")) {
+ pos = block_size * param_get_int(params, "block");
+ mult = block_size;
+ } else if (param_has_value(params, "pos")) {
+ pos = param_get_int(params, "pos");
+ mult = 1;
+ if (pos % chip_params.page_size) {
+ fprintf(stderr, "Position must be page-size "
+ "aligned!\n");
+ ret = 1;
+ goto out;
+ }
+ } else {
+ fprintf(stderr, "You must specify one of: 'block', 'page',"
+ "'pos' arguments\n");
+ ret = 1;
+ goto out;
+ }
+
+ if (!(param_has_value(params, "count")))
+ count = mult;
+ else
+ count = param_get_int(params, "count") * mult;
+
+ if (!(buf = malloc(chip_params.page_size))) {
+ perrorf("Cannot allocate buffer [size %x]",
+ chip_params.page_size);
+ ret = 1;
+ goto out;
+ }
+
+ lseek(fd, pos, SEEK_SET);
+
+ while (done < count) {
+ if ((ret = read(in_fd, buf, chip_params.page_size)) !=
+ (int32_t)chip_params.page_size) {
+ if (ret > 0) {
+ /* End of file ahead, truncate here */
+ break;
+ } else {
+ perrorf("Cannot read from %s", file);
+ ret = 1;
+ goto out;
+ }
+ }
+
+ if ((ret = write(fd, buf, chip_params.page_size)) !=
+ (int32_t)chip_params.page_size) {
+ ret = 1;
+ goto out;
+ }
+
+ done += ret;
+ }
+
+out:
+ g_close(fd);
+ if (in_fd != -1)
+ close(in_fd);
+ if (buf)
+ free(buf);
+
+ return (ret);
+}
+
diff --git a/usr.sbin/nandtool/nand_writeoob.c b/usr.sbin/nandtool/nand_writeoob.c
new file mode 100644
index 0000000..53cb32a
--- /dev/null
+++ b/usr.sbin/nandtool/nand_writeoob.c
@@ -0,0 +1,113 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libgeom.h>
+#include <sys/disk.h>
+#include <dev/nand/nand_dev.h>
+#include "nandtool.h"
+
+int nand_write_oob(struct cmd_param *params)
+{
+ struct chip_param_io chip_params;
+ struct nand_oob_rw req;
+ char *dev, *in;
+ int fd = -1, fd_in = -1, ret = 0;
+ uint8_t *buf = NULL;
+ int page;
+
+ if (!(dev = param_get_string(params, "dev"))) {
+ fprintf(stderr, "Please supply valid 'dev' parameter.\n");
+ return (1);
+ }
+
+ if (!(in = param_get_string(params, "in"))) {
+ fprintf(stderr, "Please supply valid 'in' parameter.\n");
+ return (1);
+ }
+
+ if ((page = param_get_int(params, "page")) < 0) {
+ fprintf(stderr, "Please supply valid 'page' parameter.\n");
+ return (1);
+ }
+
+ if ((fd = g_open(dev, 1)) == -1) {
+ perrorf("Cannot open %s", dev);
+ return (1);
+ }
+
+ if ((fd_in = open(in, O_RDONLY)) == -1) {
+ perrorf("Cannot open %s", in);
+ ret = 1;
+ goto out;
+ }
+
+ if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) {
+ perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)");
+ ret = 1;
+ goto out;
+ }
+
+ buf = malloc(chip_params.oob_size);
+ if (buf == NULL) {
+ perrorf("Cannot allocate %d bytes\n", chip_params.oob_size);
+ ret = 1;
+ goto out;
+ }
+
+ if (read(fd_in, buf, chip_params.oob_size) == -1) {
+ perrorf("Cannot read from %s", in);
+ ret = 1;
+ goto out;
+ }
+
+ req.page = page;
+ req.len = chip_params.oob_size;
+ req.data = buf;
+
+ if (ioctl(fd, NAND_IO_OOB_PROG, &req) == -1) {
+ perrorf("Cannot write OOB to %s", dev);
+ ret = 1;
+ goto out;
+ }
+
+out:
+ g_close(fd);
+ if (fd_in != -1)
+ close(fd_in);
+ if (buf)
+ free(buf);
+
+ return (ret);
+}
+
+
diff --git a/usr.sbin/nandtool/nandtool.8 b/usr.sbin/nandtool/nandtool.8
new file mode 100644
index 0000000..2cc4588
--- /dev/null
+++ b/usr.sbin/nandtool/nandtool.8
@@ -0,0 +1,188 @@
+.\" Copyright (c) 2010 Semihalf
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 10, 2012
+.Dt NANDTOOL 8
+.Os
+.Sh NAME
+.Nm nandtool
+.Nd NAND devices swiss army knife
+.Sh SYNOPSIS
+.Nm
+.Ar command
+.Op Ar operands ...
+.Sh DESCRIPTION
+The
+.Nm
+utility can be used to perform various operations on
+.Xr gnand 4
+devices (read, write, erase,
+read and write OOB area and to get info about NAND flash chip).
+.Pp
+The following commands are available:
+.Bl -tag -width ".Cm of Ns = Ns Ar file"
+.It Cm read Ns
+Read pages from NAND device.
+.It Cm write Ns
+Write pages to NAND device.
+.It Cm erase Ns
+Erase blocks.
+Requires offset aligned to block granularity.
+.It Cm info Ns
+Get information about NAND chip (page size, block size, OOB area size, chip size
+and media size)
+.It Cm readoob Ns
+Read OOB area from specified page.
+.It Cm writeoob Ns
+Write OOB area bound to specified page.
+.It Cm help Ns
+Get usage info.
+.El
+.Sh COMMAND read
+.Pp
+The following operands are available for
+.Nm
+.Cm read
+command:
+.Bl -tag -width ".Cm of Ns = Ns Ar file"
+.It Cm dev Ns = Ns Ar <path>
+Path to a
+.Xr gnand 4
+device node, required for all operations.
+.It Cm out Ns = Ns Ar <file>
+Output file path. If not specified, page contents
+will be dumped to stdout in format similar to
+.Xr hexdump 1
+.It Cm page Ns = Ns Ar <n>
+Offset on device, expressed as page number.
+.It Cm block Ns = Ns Ar <n>
+Offset on device, expressed as block number.
+.It Cm pos Ns = Ns Ar <n>
+Offset on device, expressed in bytes (however, must be aligned
+to page granularity).
+.It Cm count Ns = Ns Ar <n>
+Count of objects (pages, blocks, bytes).
+.El
+.Sh COMMAND readoob
+.Bl -tag -width ".Cm of Ns = Ns Ar file"
+.Pp
+The following operands are available for
+.Nm
+.Cm readoob
+command:
+.Pp
+.It Cm dev Ns = Ns Ar <path>
+Path to NAND device node.
+.It Cm page Ns = Ns Ar <n>
+Offset on device, expressed as page number.
+.It Cm out Ns = Ns Ar <file>
+Output file path, optional.
+.El
+.Sh COMMAND write
+.Bl -tag -width ".Cm of Ns = Ns Ar file"
+The following operands are available for
+.Nm
+.Cm write
+command:
+.It Cm dev Ns = Ns Ar <path>
+Path to NAND device node.
+.It Cm page Ns = Ns Ar <n>
+Offset on device, expressed as page number.
+.It Cm block Ns = Ns Ar <n>
+Offset on device, expressed as block number.
+.It Cm pos Ns = Ns Ar <n>
+Offset on device, expressed in bytes (however, must be aligned
+to page granularity).
+.It Cm in Ns = Ns Ar <file>
+Input file path.
+.El
+.Sh COMMAND writeoob
+.Bl -tag -width ".Cm of Ns = Ns Ar file"
+The following operands are available for
+.Nm
+.Cm writeoob
+command:
+.It Cm dev Ns = Ns Ar <path>
+Path to NAND device node.
+.It Cm page Ns = Ns Ar <n>
+Offset on device, expressed as page number.
+.It Cm in Ns = Ns Ar <file>
+Input file path.
+.El
+.Sh COMMAND erase
+.Bl -tag -width ".Cm of Ns = Ns Ar file"
+The following operands are available for
+.Nm
+.Cm erase
+command:
+.It Cm dev Ns = Ns Ar <path>
+Path to NAND device node.
+.It Cm page Ns = Ns Ar <n>
+Offset on device, expressed as page number.
+.It Cm block Ns = Ns Ar <n>
+Offset on device, expressed as block number.
+.It Cm pos Ns = Ns Ar <n>
+Offset on device, epressed in bytes (however, must be aligned
+to block granularity).
+.It Cm count Ns = Ns Ar <n>
+Count of objects (pages, blocks, bytes).
+.El
+.Pp
+WARNING: The only required parameter for the \fBerase\fP command is
+.Ar dev .
+When no other arguments are provided the whole device is erased!
+.Sh COMMAND info
+.Bl -tag -width ".Cm of Ns = Ns Ar file"
+There is only one operand available for
+.Nm
+.Cm info
+command:
+.It Cm dev Ns = Ns Ar <path>
+Path to NAND device node.
+.El
+.Sh COMMAND help
+.Bl -tag -width ".Cm of Ns = Ns Ar file"
+There is only one operand available for
+.Nm
+.Cm help
+command:
+.Pp
+.It Cm topic Ns = Ns Ar <name>
+Help topic.
+.El
+.Sh EXIT STATUS
+.Ex -std
+If the supplied argument
+.Ar dev
+points to a device node other than gnand<num> or gnand.raw<num> both
+.Nm
+.Cm readoob
+and
+.Nm
+.Cm writeoob
+return error.
+.Sh SEE ALSO
+.Xr gnand 4
diff --git a/usr.sbin/nandtool/nandtool.c b/usr.sbin/nandtool/nandtool.c
new file mode 100644
index 0000000..bd7075b
--- /dev/null
+++ b/usr.sbin/nandtool/nandtool.c
@@ -0,0 +1,283 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <sysexits.h>
+#include <libgeom.h>
+#include "nandtool.h"
+#include "usage.h"
+
+int usage(struct cmd_param *);
+
+static const struct {
+ const char *name;
+ const char *usage;
+ int (*handler)(struct cmd_param *);
+} commands[] = {
+ { "help", nand_help_usage, usage },
+ { "read", nand_read_usage, nand_read },
+ { "write", nand_write_usage, nand_write },
+ { "erase", nand_erase_usage, nand_erase },
+ { "readoob", nand_read_oob_usage, nand_read_oob },
+ { "writeoob", nand_write_oob_usage, nand_write_oob },
+ { "info", nand_info_usage, nand_info },
+ { NULL, NULL, NULL },
+};
+
+static char *
+_param_get_stringx(struct cmd_param *params, const char *name, int doexit)
+{
+ int i;
+
+ for (i = 0; params[i].name[0] != '\0'; i++) {
+ if (!strcmp(params[i].name, name))
+ return params[i].value;
+ }
+
+ if (doexit) {
+ perrorf("Missing parameter %s", name);
+ exit(1);
+ }
+ return (NULL);
+}
+
+char *
+param_get_string(struct cmd_param *params, const char *name)
+{
+
+ return (_param_get_stringx(params, name, 0));
+}
+
+static int
+_param_get_intx(struct cmd_param *params, const char *name, int doexit)
+{
+ int ret;
+ char *str = _param_get_stringx(params, name, doexit);
+
+ if (!str)
+ return (-1);
+
+ errno = 0;
+ ret = (int)strtol(str, (char **)NULL, 10);
+ if (errno) {
+ if (doexit) {
+ perrorf("Invalid value for parameter %s", name);
+ exit(1);
+ }
+ return (-1);
+ }
+
+ return (ret);
+}
+
+int
+param_get_intx(struct cmd_param *params, const char *name)
+{
+
+ return (_param_get_intx(params, name, 1));
+}
+
+int
+param_get_int(struct cmd_param *params, const char *name)
+{
+
+ return (_param_get_intx(params, name, 0));
+}
+
+int
+param_get_boolean(struct cmd_param *params, const char *name)
+{
+ char *str = param_get_string(params, name);
+
+ if (!str)
+ return (0);
+
+ if (!strcmp(str, "true") || !strcmp(str, "yes"))
+ return (1);
+
+ return (0);
+}
+
+int
+param_has_value(struct cmd_param *params, const char *name)
+{
+ int i;
+
+ for (i = 0; params[i].name[0] != '\0'; i++) {
+ if (!strcmp(params[i].name, name))
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+param_get_count(struct cmd_param *params)
+{
+ int i;
+
+ for (i = 0; params[i].name[0] != '\0'; i++);
+
+ return (i);
+}
+
+void
+hexdumpoffset(uint8_t *buf, int length, int off)
+{
+ int i, j;
+ for (i = 0; i < length; i += 16) {
+ printf("%08x: ", off + i);
+
+ for (j = 0; j < 16; j++)
+ printf("%02x ", buf[i+j]);
+
+ printf("| ");
+
+ for (j = 0; j < 16; j++) {
+ printf("%c", isalnum(buf[i+j])
+ ? buf[i+j]
+ : '.');
+ }
+
+ printf("\n");
+ }
+}
+
+void
+hexdump(uint8_t *buf, int length)
+{
+
+ hexdumpoffset(buf, length, 0);
+}
+
+void *
+xmalloc(size_t len)
+{
+ void *ret = malloc(len);
+
+ if (!ret) {
+ fprintf(stderr, "Cannot allocate buffer of %zd bytes. "
+ "Exiting.\n", len);
+ exit(EX_OSERR);
+ }
+
+ return (ret);
+}
+
+void
+perrorf(const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+ fprintf(stderr, ": %s\n", strerror(errno));
+}
+
+int
+usage(struct cmd_param *params)
+{
+ int i;
+
+ if (!params || !param_get_count(params)) {
+ fprintf(stderr, "Usage: nandtool <command> [arguments...]\n");
+ fprintf(stderr, "Arguments are in form 'name=value'.\n\n");
+ fprintf(stderr, "Available commands:\n");
+
+ for (i = 0; commands[i].name != NULL; i++)
+ fprintf(stderr, "\t%s\n", commands[i].name);
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "For information about particular command, "
+ "type:\n");
+ fprintf(stderr, "'nandtool help topic=<command>'\n");
+ } else if (param_has_value(params, "topic")) {
+ for (i = 0; commands[i].name != NULL; i++) {
+ if (!strcmp(param_get_string(params, "topic"),
+ commands[i].name)) {
+ fprintf(stderr, commands[i].usage, "nandtool");
+ return (0);
+ }
+ }
+
+ fprintf(stderr, "No such command\n");
+ return (EX_SOFTWARE);
+ } else {
+ fprintf(stderr, "Wrong arguments given. Try: 'nandtool help'\n");
+ }
+
+ return (EX_USAGE);
+}
+
+int
+main(int argc, const char *argv[])
+{
+ struct cmd_param *params;
+ int i, ret, idx;
+
+ if (argc < 2) {
+ usage(NULL);
+ return (0);
+ }
+
+ params = malloc(sizeof(struct cmd_param) * (argc - 1));
+
+ for (i = 2, idx = 0; i < argc; i++, idx++) {
+ if (sscanf(argv[i], "%63[^=]=%63s", params[idx].name,
+ params[idx].value) < 2) {
+ fprintf(stderr, "Syntax error in argument %d. "
+ "Argument should be in form 'name=value'.\n", i);
+ free(params);
+ return (-1);
+ }
+ }
+
+ params[idx].name[0] = '\0';
+ params[idx].value[0] = '\0';
+
+ for (i = 0; commands[i].name != NULL; i++) {
+ if (!strcmp(commands[i].name, argv[1])) {
+ ret = commands[i].handler(params);
+ free(params);
+ return (ret);
+ }
+ }
+
+ free(params);
+ fprintf(stderr, "Unknown command. Try '%s help'\n", argv[0]);
+
+ return (-1);
+}
+
diff --git a/usr.sbin/nandtool/nandtool.h b/usr.sbin/nandtool/nandtool.h
new file mode 100644
index 0000000..639f95e
--- /dev/null
+++ b/usr.sbin/nandtool/nandtool.h
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __UTILS_H
+#define __UTILS_H
+
+struct cmd_param
+{
+ char name[64];
+ char value[64];
+};
+
+char *param_get_string(struct cmd_param *, const char *);
+int param_get_int(struct cmd_param *, const char *);
+int param_get_intx(struct cmd_param *, const char *);
+int param_get_boolean(struct cmd_param *, const char *);
+int param_has_value(struct cmd_param *, const char *);
+int param_get_count(struct cmd_param *);
+void perrorf(const char *, ...);
+void hexdumpoffset(uint8_t *, int, int);
+void hexdump(uint8_t *, int);
+void *xmalloc(size_t);
+
+/* Command handlers */
+int nand_read(struct cmd_param *);
+int nand_write(struct cmd_param *);
+int nand_read_oob(struct cmd_param *);
+int nand_write_oob(struct cmd_param *);
+int nand_erase(struct cmd_param *);
+int nand_info(struct cmd_param *);
+
+#endif /* __UTILS_H */
diff --git a/usr.sbin/nandtool/usage.h b/usr.sbin/nandtool/usage.h
new file mode 100644
index 0000000..74e8543
--- /dev/null
+++ b/usr.sbin/nandtool/usage.h
@@ -0,0 +1,112 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __USAGE_H
+#define __USAGE_H
+
+static const char nand_help_usage[] =
+ "Usage: %s help topic=<cmd>\n"
+ "\n"
+ "Arguments:\n"
+ "\tcmd\t- [help|read|write|erase|readoob|writeoob|info]\n"
+ "\n";
+
+static const char nand_read_usage[] =
+ "Usage: %s read dev=<gnand_device> (block|page|pos)=n [count=n]\n"
+ "\n"
+ "Arguments:\n"
+ "\tdev\t- path to gnand device node\n"
+ "\tblock\t- starting block or\n"
+ "\tpage\t- starting page or\n"
+ "\tpos\t- starting position (in bytes, must be page-aligned)\n"
+ "\tout\t- output file (hexdump to stdout if not supplied)\n"
+ "\n"
+ "Note that you can only specify only one of: 'block', 'page', 'pos'\n"
+ "parameters at once. 'count' parameter is meaningful in terms of used\n"
+ "unit (page, block or byte).\n";
+
+static const char nand_write_usage[] =
+ "Usage: %s write dev=<gnand_device> in=<file> (block|page|pos)=n [count=n]\n"
+ "\n"
+ "Arguments:\n"
+ "\tdev\t- path to gnand device node\n"
+ "\tin\t- path to input file which be writed to gnand\n"
+ "\tblock\t- starting block or\n"
+ "\tpage\t- starting page or\n"
+ "\tpos\t- starting position (in bytes, must be page-aligned)\n"
+ "\tcount\t- byte/page/block count\n"
+ "\n"
+ "";
+
+static const char nand_erase_usage[] =
+ "Usage: %s erase dev=<gnand_device> (block|page|pos)=n [count=n]\n"
+ "\n"
+ "Arguments:\n"
+ "\tdev\t- path to gnand device node\n"
+ "\tblock\t- starting block or\n"
+ "\tpage\t- starting page or\n"
+ "\tpos\t- starting position (in bytes, muse be block-aligned)\n"
+ "\tcount\t- byte/page/block count\n"
+ "\n"
+ "NOTE: position and count for erase operation MUST be block-aligned\n";
+
+static const char nand_read_oob_usage[] =
+ "Usage: %s readoob dev=<gnand_device> page=n [out=file] [count=n]\n"
+ "\n"
+ "Arguments:\n"
+ "\tdev\t- path to gnand device node\n"
+ "\tpage\t- page (page) number\n"
+ "\tout\t- outut file (hexdump to stdout if not supplied)\n"
+ "\tcount\t- page count (default is 1)\n"
+ "\n"
+ "If you supply count parameter with value other than 1, data will be\n"
+ "read from subsequent page's OOB areas\n";
+
+static const char nand_write_oob_usage[] =
+ "Usage: %s writeoob dev=<gnand_device> in=<file> page=n [count=n]\n"
+ "\n"
+ "\tdev\t- path to gnand device node\n"
+ "\tin\t- path to file containing data which will be written\n"
+ "\tpage\t- page (page) number\n"
+ "\n"
+ "If you supply count parameter with value other than 1, data will be\n"
+ "written to subsequent page's OOB areas\n";
+
+static const char nand_info_usage[] =
+ "Usage: %s info dev=<gnand_device>\n"
+ "\n"
+ "Arguments:\n"
+ "\tdev\t- path to gnand device node\n";
+
+static const char nand_stats_usage[] =
+ "Usage: %s stats dev=<gnand_device> (page|block)=<n>\n"
+ "\n"
+ "Arguments:\n"
+ "\tdev\t- path to gnand device node\n";
+
+#endif /* __USAGE_H */
OpenPOWER on IntegriCloud