#include <stdio.h>
#include <stdint.h>
#include <string.h>

#define BLOCKSIZE	512

void linux_calc_ecc(const unsigned char *buf, unsigned char *ecc);
void wince_calc_ecc(const unsigned char *buf, unsigned char *ecc);

int main(int argc, char **argv)
{
	FILE *f;
	unsigned char ecc_buf[16];
	unsigned char data_buf[512];
	size_t count;
	int i;
	
	memset(ecc_buf, 0x00, 16);
	memset(data_buf, 0x00, 512);
	
	if(argc != 2) {
		printf("Wrong parameters supplied!\n");
		return -1;
	}
	
	f = fopen(argv[1], "r");
	if(f == NULL)
		return -1;
		
	count = fread(data_buf, 1, BLOCKSIZE, f);
	if(count != BLOCKSIZE) {
		printf("could not read exactly %d bytes! Read %d instead! Aborting!!\n", BLOCKSIZE, count);
	}
	else
		printf("Read okay, start ecc\n");

	for(i=0;i<512;i++) {
		if( ((i%16) == 0) && (i != 0) )
			printf("\n");
		printf("%02x ", data_buf[i]);
	}
	printf("\n");
	
	memset(ecc_buf, 0x00, 16);
	wince_calc_ecc(data_buf, ecc_buf);
	wince_calc_ecc(data_buf + 256, ecc_buf+3);
	printf("ECC: ");
	for(i=0; i<16; i++) {
		printf("%02x ", ecc_buf[i]);
	}
	printf("\n");

	memset(ecc_buf, 0x00, 16);
	linux_calc_ecc(data_buf, ecc_buf);
	linux_calc_ecc(data_buf + 256, ecc_buf+3);
	printf("ECC: ");
	for(i=0; i<16; i++) {
		printf("%02x ", ecc_buf[i]);
	}
	printf("\n");

	return 0;
}

/*
 * Pre-calculated 256-way 1 byte column parity
 */
static const unsigned char nand_ecc_precalc_table[] = {
	0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
	0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
	0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
	0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
	0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
	0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
	0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
	0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
	0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
	0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
	0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
	0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
	0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
	0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
	0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
	0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
};

/**
 * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256-byte block
 * @mtd:	MTD block structure
 * @dat:	raw data
 * @ecc_code:	buffer for ECC
 */
void linux_calc_ecc(const unsigned char *dat, unsigned char *ecc_code)
{
	uint8_t idx, reg1, reg2, reg3, tmp1, tmp2;
	int i;

	/* Initialize variables */
	reg1 = reg2 = reg3 = 0;

	/* Build up column parity */
	for(i = 0; i < 256; i++) {
		/* Get CP0 - CP5 from table */
		idx = nand_ecc_precalc_table[*dat++];
		reg1 ^= (idx & 0x3f);

		/* All bit XOR = 1 ? */
		if (idx & 0x40) {
			reg3 ^= (uint8_t) i;
			reg2 ^= ~((uint8_t) i);
		}
	}

	/* Create non-inverted ECC code from line parity */
	tmp1  = (reg3 & 0x80) >> 0; /* B7 -> B7 */
	tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */
	tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */
	tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */
	tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */
	tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */
	tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */
	tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */

	tmp2  = (reg3 & 0x08) << 4; /* B3 -> B7 */
	tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */
	tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */
	tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */
	tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */
	tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */
	tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */
	tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */

	/* Calculate final ECC code */
	ecc_code[0] = ~tmp1;
	ecc_code[1] = ~tmp2;
	ecc_code[2] = ((~reg1) << 2) | 0x03;
}


static const unsigned char eccArray[] = 
{
	0,   1,   1,   2,   1,   2,   2,   3,   1,   2,   2,   3,   2,   3,   3,   4,
	1,   2,   2,   3,   2,   3,   3,   4,   2,   3,   3,   4,   3,   4,   4,   5,
	1,   2,   2,   3,   2,   3,   3,   4,   2,   3,   3,   4,   3,   4,   4,   5,
	2,   3,   3,   4,   3,   4,   4,   5,   3,   4,   4,   5,   4,   5,   5,   6,
	1,   2,   2,   3,   2,   3,   3,   4,   2,   3,   3,   4,   3,   4,   4,   5,
	2,   3,   3,   4,   3,   4,   4,   5,   3,   4,   4,   5,   4,   5,   5,   6,
	2,   3,   3,   4,   3,   4,   4,   5,   3,   4,   4,   5,   4,   5,   5,   6,
	3,   4,   4,   5,   4,   5,   5,   6,   4,   5,   5,   6,   5,   6,   6,   7,
	1,   2,   2,   3,   2,   3,   3,   4,   2,   3,   3,   4,   3,   4,   4,   5,
	2,   3,   3,   4,   3,   4,   4,   5,   3,   4,   4,   5,   4,   5,   5,   6,
	2,   3,   3,   4,   3,   4,   4,   5,   3,   4,   4,   5,   4,   5,   5,   6,
	3,   4,   4,   5,   4,   5,   5,   6,   4,   5,   5,   6,   5,   6,   6,   7,
	2,   3,   3,   4,   3,   4,   4,   5,   3,   4,   4,   5,   4,   5,   5,   6,
	3,   4,   4,   5,   4,   5,   5,   6,   4,   5,   5,   6,   5,   6,   6,   7,
	3,   4,   4,   5,   4,   5,   5,   6,   4,   5,   5,   6,   5,   6,   6,   7,
	4,   5,   5,   6,   5,   6,   6,   7,   5,   6,   6,   7,   6,   7,   7,   8,
	0,   1,   4,   5,0x10,0x11,0x14,0x15,0x40,0x41,0x44,0x45,0x50,0x51,0x54,0x55,
	0,   0,   1,   1,   0,   0,   1,   1,   2,   2,   3,   3,   2,   2,   3,   3,
	0,   0,   1,   1,   0,   0,   1,   1,   2,   2,   3,   3,   2,   2,   3,   3,
	4,   4,   5,   5,   4,   4,   5,   5,   6,   6,   7,   7,   6,   6,   7,   7,
	4,   4,   5,   5,   4,   4,   5,   5,   6,   6,   7,   7,   6,   6,   7,   7,
	0,   0,   1,   1,   0,   0,   1,   1,   2,   2,   3,   3,   2,   2,   3,   3,
	0,   0,   1,   1,   0,   0,   1,   1,   2,   2,   3,   3,   2,   2,   3,   3,
	4,   4,   5,   5,   4,   4,   5,   5,   6,   6,   7,   7,   6,   6,   7,   7,
	4,   4,   5,   5,   4,   4,   5,   5,   6,   6,   7,   7,   6,   6,   7,   7,
	8,   8,   9,   9,   8,   8,   9,   9, 0xA, 0xA, 0xB, 0xB, 0xA, 0xA, 0xB, 0xB,
	8,   8,   9,   9,   8,   8,   9,   9, 0xA, 0xA, 0xB, 0xB, 0xA, 0xA, 0xB, 0xB,
	0xC, 0xC, 0xD, 0xD, 0xC, 0xC, 0xD, 0xD, 0xE, 0xE, 0xF, 0xF, 0xE, 0xE, 0xF, 0xF,
	0xC, 0xC, 0xD, 0xD, 0xC, 0xC, 0xD, 0xD, 0xE, 0xE, 0xF, 0xF, 0xE, 0xE, 0xF, 0xF,
	8,   8,   9,   9,   8,   8,   9,   9, 0xA, 0xA, 0xB, 0xB, 0xA, 0xA, 0xB, 0xB,
	8,   8,   9,   9,   8,   8,   9,   9, 0xA, 0xA, 0xB, 0xB, 0xA, 0xA, 0xB, 0xB,
	0xC, 0xC, 0xD, 0xD, 0xC, 0xC, 0xD, 0xD, 0xE, 0xE, 0xF, 0xF, 0xE, 0xE, 0xF, 0xF,
	0xC, 0xC, 0xD, 0xD, 0xC, 0xC, 0xD, 0xD, 0xE, 0xE, 0xF, 0xF, 0xE, 0xE, 0xF, 0xF,
	0x52,0x53,0x44,0x53,0x67,0x98,0xE5,0x1F,0x71,0x94,0x75,0x45,0x9F,0x77,0x16,0x55,
	0xA8,0xCD,0x45,   6,0x11,   0,   0,   0,
};


void wince_calc_ecc(const unsigned char *buf, unsigned char *ecc)
{
	uint32_t a0, a1, a2, a3;
	uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9;
	uint32_t v0, v1;

	a0 = (uint32_t)buf;
	a1 = (uint32_t)ecc;

	a3 = 0;
	a2 = 0;
	v1 = 0;
	t0 = 0;
	t1 = a0;
	v0 = (uint32_t)eccArray; 


	for(t0 = 0; t0 < 0x40; t0++)
	{
		a0 = *((uint32_t*)t1);
        	v1 = a0 ^ v1;
		a0 = (a0 >> 16) ^ a0;
		t4 = ((a0 >> 8) ^ a0);
		t7 = *(eccArray + (t4 & 0xFF));
		if(t7 & 1)
		{
			a3 ^= t0;
			a2 ^= ~t0;
		}
		t1 = t1 + 4;
	}	
	t1 = (a3 >> 4) & 3;
	t7 = (a2 >> 4) & 3;
	t4 = *(eccArray + 0x100 + t1);
	a0 = *(eccArray + 0x100 + t7);

	t1 = ((t4 << 1) | a0) << 7;
	t5 = *(eccArray + 0x100 + (a3 & 0xF));
	t6 = t1 | t5;

	a2 = *(eccArray + 0x100 + (a2 & 0xF));
	a3 = (t6 << 1) | a2;

	a0 = 0x55555555;
	a2 = v1 & a0;
	a2 = (a2 >> 16) ^ a2;
	t2 = (a2 >> 8) ^ a2;
	t6 = 0xAAAAAAAA;
	a2 = v1 & t6;
	a2 = (a2 >> 16) ^ a2;

	t5 = *(eccArray + (t2 & 0xFF));
	t9 = (a2 >> 8) ^ a2;

	t4 = 0x33333333;
	a2 = v1 & t4;
	a0 = t5 & 1;

	a2 = (a2 >> 16) ^ a2;
	t7 = (a2 >> 8) ^ a2;
	t1 = *(eccArray + (t9 & 0xFF));
	t3 = (t1 & 1) << 1;

	a2 = *(eccArray + (t7 & 0xFF));
	t2 = 0xCCCCCCCC;
	t0 = a2 & 1;
	a2 = v1 & t2;
	a0 = t3 | a0;
	a2 = (a2 >> 16) ^ a2;
	t5 = (a2 >> 8) ^ a2;
	t8 = *(eccArray + (t5 & 0xFF));
	t1 = t0 << 2;

	a0 = t1 | a0;
	a2 = (t8 & 1) << 3;
	t0 = 0xF0F0F0F;
	a0 = a2 | a0;
	a2 = v1 & t0;
	a2 = (a2 >> 16) ^ a2;
	t3 = (a2 >> 8) ^ a2;
	t9 = 0xF0F0F0F0;
	t6 = *(eccArray + (t3 & 0xFF));
	t7 = t6 & 1;
	a2 = v1 & t9;
	a2 = (a2 >> 16) ^ a2;
	t2 = (a2 >> 8) ^ a2;
	a0 = (t7 << 4) | a0;

	t5 = *(eccArray + (t2 & 0xFF));
	t8 = 0xFF00FF;
	a2 = v1 & t8;
	t6 = t5 & 1;
	a2 = (a2 >> 16) ^ a2;
	t5 = 0xFF00FF00;
	t0 = a2 & 0xFF;
	a2 = v1 & t5;
	a0 = (t6 << 5) | a0;
	t7 = (a2 >> 16) ^ a2;
	t2 = *(eccArray + t0);
	t9 = (t7 >> 8) & 0xFF;
	t0 = *(eccArray + t9);
	t3 = t2 & 1;
	a2 = v1 & 0xFFFF;
	a0 = (t3 << 6) | a0;
	t4 = (a2 >> 8) ^ a2;
	t7 = *(eccArray + (t4 & 0xFF));
	t2 = (t0 & 1) << 7;
	a0 = t2 | a0;
	t9 = (t7 & 1) << 8;
	v1 = v1 >> 16;
	a2 = t9 | a0;
	t0 = (v1 >> 8) ^ v1;
	t3 = *(eccArray + (t0 & 0xFF));
	*((uint8_t*)a1) = a3;
	t5 = (t3 & 1) << 9;
	t7 = (t5 | a2) << 4;
	t8 = (a3 >> 8) | t7;
	v0 = (v0 >> 4) | 0xC0;
	*((uint8_t*)a1+1) = t8;
	*((uint8_t*)a1+2) = v0;
}

