2012-05-17 10:11:18 +00:00
|
|
|
/*-
|
|
|
|
* 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.
|
|
|
|
*/
|
2012-05-18 14:12:17 +00:00
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
2012-05-17 10:11:18 +00:00
|
|
|
__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 *)§ions;
|
|
|
|
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 *)§ions;
|
|
|
|
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);
|
|
|
|
}
|