/*- * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #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}, }; static 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 necessary 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); }