| /* |
| * 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; |
| } |