/*
  Reed-Solomon encoder-decoder.
 
  Copyright (C) 2010 Federico Heinz <fheinz@vialibre.org.ar>

  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 3 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, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "ecc_rs.h"
#include "do_write.h"

#define CHUNKSIZE_NAND 2048
#define CHUNKSIZE_ECC  512

struct oob_data_s {
	unsigned char filler[24];
	unsigned char ecc_buffers[4][ECC_SIZE];
};

static const char *progname = NULL;
static const char *usage = NULL;
static const char *usage_nandrs_decode =
	"usage: %s <data+oob >rs_corrected_data\n"
	"typical usage: nanddump -n /dev/mtd0 | %s >image\n"
	"Performs Reed-Solomon error correction on partition dumps\n"
	"of 2048-byte erase size NAND memory using the embedded\n"
	"64-byte out-of-band information.\n"
	"Use only on mtd partitions encoded with 4-bit R-S ECC!\n"
	"On sheeva systems, this is only /dev/mtd0.\n";
static const char *usage_nandrs_encode =
	"usage: %s <data >data+oob_with_rs_ecc\n"
	"typical usage:"
        "  %s <image >image+oob && nandwrite -n -o /dev/mtd0 -f image+oob\n"
	"Adds 64 bytes of out-of-band data containing Reed-Solomon\n"
	"ECC for every 2048-byte block of the input.\n"
	"Use only on mtd partitions encoded with 4-bit R-S ECC!\n"
	"On sheeva systems, this is only /dev/mtd0.\n";
static const char *copyright =
	"This is %s %s\n"
	"Copyright (C) 2010 Federico Heinz <fheinz@vialibre.org.ar>\n"
	"This program comes with ABSOLUTELY NO WARRANTY;\n"
	"This is free software, and you are welcome to redistribute it\n"
	"under certain conditions (see the file COPYING).\n";


static int nandrs_decode() {
	unsigned int chunk_nr = 0;
	unsigned char chunk[CHUNKSIZE_NAND];

	while (fread(chunk, sizeof(chunk), 1, stdin) == 1) {
		struct oob_data_s oob_data;
		unsigned int ecc_chunk_nr = 0;

		if (fread(&oob_data, sizeof(oob_data), 1, stdin) != 1) {
			fprintf(stderr, "%s: unable to read oob for chunk #%d\n",
				progname, chunk_nr);
			return 3;
		}
		for (ecc_chunk_nr = 0; ecc_chunk_nr < 4; ecc_chunk_nr++) {
			unsigned char *ecc_chunk = chunk+ecc_chunk_nr*CHUNKSIZE_ECC;
			unsigned char computed_ecc[ECC_SIZE];

			calculate_ecc_rs(ecc_chunk, computed_ecc);
			if (correct_data_rs(ecc_chunk, oob_data.ecc_buffers[ecc_chunk_nr],
					    computed_ecc) < 0) {
				fprintf(stderr, "%s: too many errors in block #%d of chunk #%d\n",
					progname, ecc_chunk_nr, chunk_nr);
				return 4;
			}
		}
		if (do_write(chunk, sizeof(chunk)) != 0) {
			perror(progname);
			return -errno;
		}
		chunk_nr++;
	}
	return 0;
}

static int nandrs_encode() {
	unsigned char chunk[CHUNKSIZE_NAND];

	while (fread(chunk, sizeof(chunk), 1, stdin) == 1) {
		struct oob_data_s oob_data;
		unsigned int ecc_chunk_nr = 0;

		if (do_write(chunk, sizeof(chunk)) != 0) {
			perror(progname);
			return -errno;
		}
		memset(oob_data.filler, -1, sizeof(oob_data.filler));
		for (ecc_chunk_nr = 0; ecc_chunk_nr < 4; ecc_chunk_nr++)
			calculate_ecc_rs(chunk+ecc_chunk_nr*CHUNKSIZE_ECC,
					 oob_data.ecc_buffers[ecc_chunk_nr]);
		if (do_write(&oob_data, sizeof(oob_data)) != 0) {
			perror(progname);
			return -errno;
		}
	}
	return 0;
}

int main(int argc, const char *argv[]) {
	int (*mode)();
	progname = strrchr(argv[0], '/');

	if (progname != NULL)
		progname++;
	else
		progname = argv[0];
	if (!strcmp(progname, "sheeva-nandrs-decode")){
		mode = nandrs_decode;
		usage = usage_nandrs_decode;
	} else if (!strcmp(progname, "sheeva-nandrs-encode")) {
		mode = nandrs_encode;
		usage = usage_nandrs_encode;
	} else {
		fprintf(stderr, "%s: unknown operation mode\n", progname); 
		return 1;
	}
	if (argc != 1) {
		fprintf(stderr, usage, progname, progname);
		fprintf(stderr, copyright, progname, HGVERSION);
		return 1;
	}
	return (*mode)();
}
