/* * Copyright (c) International Business Machines Corp., 2007 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Oliver Lohmann */ /* * Create a flashable NAND image from a binary image * * History: * 1.0 Initial release (tglx) * 1.1 Understands hex and dec input parameters (tglx) * 1.2 Generates separated OOB data, if needed. (oloh) * 1.3 Padds data/oob to a given size. (oloh) * 1.4 Removed argp because we want to use uClibc. * 1.5 Minor cleanup * 1.6 written variable not initialized (-j did not work) (haver) */ #include <unistd.h> #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <getopt.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include "error.h" #include "config.h" #include "nandecc.h" #define PROGRAM_VERSION "1.6" #define CHECK_ENDP(option, endp) do { \ if (*endp) { \ fprintf(stderr, \ "Parse error option \'%s\'. " \ "No correct numeric value.\n" \ , option); \ exit(EXIT_FAILURE); \ } \ } while(0) typedef enum action_t { ACT_NORMAL = 0x00000001, } action_t; #define PAGESIZE 2048 #define PADDING 0 /* 0 means, do not adjust anything */ #define BUFSIZE 4096 static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" "bin2nand - a tool for adding OOB information to a " "binary input file.\n"; static const char *optionsstr = " -c, --copyright Print copyright informatoin.\n" " -j, --padding=<num> Padding in Byte/Mi/ki. Default = no padding\n" " -p, --pagesize=<num> Pagesize in Byte/Mi/ki. Default = 2048\n" " -o, --output=<fname> Output filename. Interleaved Data/OOB if\n" " output-oob not specified.\n" " -q, --output-oob=<fname> Write OOB data in separate file.\n" " -?, --help Give this help list\n" " --usage Give a short usage message\n" " -V, --version Print program version\n"; static const char *usage = "Usage: bin2nand [-c?V] [-j <num>] [-p <num>] [-o <fname>] [-q <fname>]\n" " [--copyright] [--padding=<num>] [--pagesize=<num>]\n" " [--output=<fname>] [--output-oob=<fname>] [--help] [--usage]\n" " [--version]\n"; struct option long_options[] = { { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, { .name = "padding", .has_arg = 1, .flag = NULL, .val = 'j' }, { .name = "pagesize", .has_arg = 1, .flag = NULL, .val = 'p' }, { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, { .name = "output-oob", .has_arg = 1, .flag = NULL, .val = 'q' }, { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, { NULL, 0, NULL, 0} }; static const char copyright [] __attribute__((unused)) = "Copyright IBM Corp. 2006"; typedef struct myargs { action_t action; size_t pagesize; size_t padding; FILE* fp_in; char *file_out_data; /* Either: Data and OOB interleaved or plain data */ char *file_out_oob; /* OOB Data only. */ /* special stuff needed to get additional arguments */ char *arg1; char **options; /* [STRING...] */ } myargs; static int ustrtoull(const char *cp, char **endp, unsigned int base) { unsigned long long res = strtoull(cp, endp, base); switch (**endp) { case 'G': res *= 1024; case 'M': res *= 1024; case 'k': case 'K': res *= 1024; /* "Ki", "ki", "Mi" or "Gi" are to be used. */ if ((*endp)[1] == 'i') (*endp) += 2; } return res; } static int parse_opt(int argc, char **argv, myargs *args) { char* endp; while (1) { int key; key = getopt_long(argc, argv, "cj:p:o:q:?V", long_options, NULL); if (key == -1) break; switch (key) { case 'p': /* pagesize */ args->pagesize = (size_t) ustrtoull(optarg, &endp, 0); CHECK_ENDP("p", endp); break; case 'j': /* padding */ args->padding = (size_t) ustrtoull(optarg, &endp, 0); CHECK_ENDP("j", endp); break; case 'o': /* output */ args->file_out_data = optarg; break; case 'q': /* output oob */ args->file_out_oob = optarg; break; case '?': /* help */ printf("%s", doc); printf("%s", optionsstr); exit(0); break; case 'V': printf("%s\n", PROGRAM_VERSION); exit(0); break; case 'c': printf("%s\n", copyright); exit(0); default: printf("%s", usage); exit(-1); } } if (optind < argc) { args->fp_in = fopen(argv[optind++], "rb"); if ((args->fp_in) == NULL) { err_quit("Cannot open file %s for input\n", argv[optind++]); } } return 0; } static int process_page(uint8_t* buf, size_t pagesize, FILE *fp_data, FILE* fp_oob, size_t* written) { int eccpoi, oobsize; size_t i; uint8_t oobbuf[64]; memset(oobbuf, 0xff, sizeof(oobbuf)); switch(pagesize) { case 2048: oobsize = 64; eccpoi = 64 / 2; break; case 512: oobsize = 16; eccpoi = 16 / 2; break; default: err_msg("Unsupported page size: %d\n", pagesize); return -EINVAL; } for (i = 0; i < pagesize; i += 256, eccpoi += 3) { oobbuf[eccpoi++] = 0x0; /* Calculate ECC */ nand_calculate_ecc(&buf[i], &oobbuf[eccpoi]); } /* write data */ *written += fwrite(buf, 1, pagesize, fp_data); /* either separate oob or interleave with data */ if (fp_oob) { i = fwrite(oobbuf, 1, oobsize, fp_oob); if (ferror(fp_oob)) { err_msg("IO error\n"); return -EIO; } } else { i = fwrite(oobbuf, 1, oobsize, fp_data); if (ferror(fp_data)) { err_msg("IO error\n"); return -EIO; } } return 0; } int main (int argc, char** argv) { int rc = -1; int res = 0; size_t written = 0, read; myargs args = { .action = ACT_NORMAL, .pagesize = PAGESIZE, .padding = PADDING, .fp_in = NULL, .file_out_data = NULL, .file_out_oob = NULL, }; FILE* fp_out_data = stdout; FILE* fp_out_oob = NULL; parse_opt(argc, argv, &args); uint8_t* buf = calloc(1, BUFSIZE); if (!buf) { err_quit("Cannot allocate page buffer.\n"); } if (!args.fp_in) { err_msg("No input image specified!\n"); goto err; } if (args.file_out_data) { fp_out_data = fopen(args.file_out_data, "wb"); if (fp_out_data == NULL) { err_sys("Cannot open file %s for output\n", args.file_out_data); goto err; } } if (args.file_out_oob) { fp_out_oob = fopen(args.file_out_oob, "wb"); if (fp_out_oob == NULL) { err_sys("Cannot open file %s for output\n", args.file_out_oob); goto err; } } while(1) { read = fread(buf, 1, args.pagesize, args.fp_in); if (feof(args.fp_in) && read == 0) break; if (read < args.pagesize) { err_msg("Image not page aligned\n"); goto err; } if (ferror(args.fp_in)) { err_msg("Read error\n"); goto err; } res = process_page(buf, args.pagesize, fp_out_data, fp_out_oob, &written); if (res != 0) goto err; } while (written < args.padding) { memset(buf, 0xff, args.pagesize); res = process_page(buf, args.pagesize, fp_out_data, fp_out_oob, &written); if (res != 0) goto err; } rc = 0; err: free(buf); if (args.fp_in) fclose(args.fp_in); if (fp_out_oob) fclose(fp_out_oob); if (fp_out_data && fp_out_data != stdout) fclose(fp_out_data); if (rc != 0) { err_msg("Error during conversion. rc: %d\n", rc); if (args.file_out_data) remove(args.file_out_data); if (args.file_out_oob) remove(args.file_out_oob); } return rc; }