/* For free support for VSIDE, please visit www.vsdsp-forum.com */

// Starting point template for creating VSOS3 libraries and device drivers.
// This will create a <projectname>.DL3 file, which you can copy to 
// your VS1005 Developer Board's system disk's SYS subdirectory.

// If init(), main() or fini() are not needed, remove them from the solution.
// There's no need to have any unneeded functions in the library.

#include <vo_stdio.h>
#include <volink.h>     // Linker directives like DLLENTRY
#include <apploader.h>  // RunLibraryFunction etc
#include <kernel.h>
#include <string.h>
#include <devSpiFlash.h>
#include <vo_fat.h>

#define RESERVED_SECTORS (2*65536/512) // Reserve 128KB from the beginning of flash for boot code

u_int16 verbatim = 0;
u_int16 fatonly = 0;
DiskGeometry g;
FILE *of = NULL;
FILE *vf = NULL;
DEVICE *bus = NULL;
DEVICE *flash = NULL;
u_int16 buf[256];
u_int16 buf2[256];
u_int32 sectors = 0;
u_int32 verifyErrors = 0;

void Backup(void) {
	u_int32 n;
	
	if (!fatonly) {
		for (n=0; n<128; n++) {
			flash->BlockRead(flash, n-RESERVED_SECTORS, 1, buf);
			fwrite(buf, 256, 1, of);
		}
		for (n=128; n<256; n++) {
			if (verbatim) {
				flash->BlockRead(flash, n-RESERVED_SECTORS, 1, buf);
			} else {
				flash->BlockRead(flash, n-(RESERVED_SECTORS + 128), 1, buf);
			}
			fwrite(buf, 256, 1, of);
		}
	}
	for (n=256; n<sectors; n++) {
		flash->BlockRead(flash, n-RESERVED_SECTORS, 1, buf);
		fwrite(buf, 256, 1, of);
		
		if ((n & 255) == 0) {
			printf("\r%ldK",n/2);
		}
 	
	}	
	printf("\r%ldK",n/2);
}

u_int16 CheckBuf(register const u_int16 *b1, register const u_int16 *b2, register s_int16 n) {
	s_int16 i;
	u_int16 nErr=0;
	for (i=0; i<n; i++) {
		nErr += (*b1++ != *b2++);
	}
	return nErr;
}

void Verify(void) {
	u_int32 n;
	
	if (!fatonly) {
		for (n=0; n<128; n++) {
			flash->BlockRead(flash, n-RESERVED_SECTORS, 1, buf);
			fread(buf2, 256, 1, vf);
			verifyErrors += CheckBuf(buf, buf2, 256);
		}
		for (n=128; n<256; n++) {
			if (verbatim) {
				flash->BlockRead(flash, n-RESERVED_SECTORS, 1, buf);
			} else {
				flash->BlockRead(flash, n-(RESERVED_SECTORS + 128), 1, buf);
			}
			fread(buf2, 256, 1, vf);
			verifyErrors += CheckBuf(buf, buf2, 256);
		}
	}
	for (n=256; n<sectors; n++) {
		flash->BlockRead(flash, n-RESERVED_SECTORS, 1, buf);
		fread(buf2, 256, 1, vf);
		verifyErrors += CheckBuf(buf, buf2, 256);
		if ((n & 255) == 0) {
			printf("\r%ldK",n/2);
		}
	}

	printf("\r%ldK",n/2);
}
	
	


// Startup code for each instance of the library
// If CONFIG.TXT has several instance of the same driver, this is called for each line.
ioresult main(char *parameters) {
	int nParam = RunProgram("ParamSpl",parameters);
	char *p = parameters;
	int i;
	char *fNamePtr = NULL;
	
	vo_fat_allocationSizeClusters = 16384;
	

	if (nParam == 0) {
		printf("Usage: sysbkup [-a] [-f] x:filename.bin\n");
		printf("Parameters:\n");
		printf("  -a  Create 1:1 backup. (Default: duplicate first 64K to second 64K)\n");
		printf("  -f  Extract FAT image only (skip first 128 KB)\n");
		//printf("  -i  Use internal flash (Default: use device S: for reading)\n");
		//printf("  -e  Use external flash (Default: use device S: for reading)\n");
		return S_ERROR;		
	}
	
	flash = VODEV('S');
	if (!flash || !flash->Identify) {
		printf("No S: flash\n");
		return S_ERROR;
	}
	printf("Sysbackup S: %s\n",flash->Identify(flash, NULL, 0));
	if (!strstr(flash->Identify(flash,NULL,0),"SPI")) {
		printf("Doesn't seem like SPI flash, failing.\n");
		return S_ERROR;
	}
	

	bus = (DEVICE*)(flash->hardwareInfo[0]);
	if (!flash->Identify || !bus || !bus->Identify) {
		printf("Cannot determine SPI flash/bus\n");
		return S_ERROR;
	}
	printf("Bus: %s\n",bus->Identify(bus, NULL, 0));
	ioctl(VODEV('S'), IOCTL_GET_GEOMETRY, &g);
	sectors = g.totalSectors + RESERVED_SECTORS; 
	printf("Size %ld sectors (%ldK)\n",sectors,sectors/2);
	flash->BlockRead(flash, 0-RESERVED_SECTORS, 1, buf);
	printf("Boot record: %04x%04x (%c%c%c%c)\n",buf[0],buf[1],buf[0]>>8,buf[0]&0xff,buf[1]>>8,buf[1]&0xff);
			
	if (((buf[0] & 0xfffe) != 0x564c) || (buf[1] != 0x5335)) {
		printf("Boot record validity check failed (VLS5 wanted), giving up.\n");
		printf("Maybe cannot read the boot record or size of reserved boot area is not 128K.\n");
		return S_ERROR;
	}
	if ((sectors < 256) || (sectors > 100000)) {
		printf("Size seems not correct (really small or really big)\n");
		return S_ERROR;
	}

	for (i=0; i<nParam; i++) {
		if (!strcmp(p, "-a")) {
			verbatim = 1;
		} else if (!strcmp(p, "-f")) {
			fatonly = 1;
		} else {
			if (of) {
				printf("Extraneous parameter %s.\n",p);
				fclose(of);
				return S_ERROR;
			}
			if ((strlen(p) < 3) || (p[1]!=':')) {
				printf("%s is not a valid full path file name\n",p);
				return S_ERROR;
			}
			of = fopen(p,"wb");
			fNamePtr = p;
			if (!of) {
				printf("Cannot open output file.\n");
				return S_ERROR;
			}
		}
		p += strlen(p)+1;
	}
	if (!of) {
		printf("No output file specified.\n");
		return S_ERROR;
	}
	printf("Backing up...\n");
	Backup();
	printf("\n");
	fclose(of);
	
	printf("Verifying...\n");
	vf = fopen(fNamePtr,"rb");
	if (!vf) {
		printf("Error: no file created!\n");
		return S_ERROR;
	}
	Verify();
	printf("\n");
	fclose(vf);
	
	//printf("%ld verify errors in file\n",verifyErrors);
	if (verifyErrors) {
		printf("Error: the backup failed.\n");
		printf("%ld verify errors in file.\n",verifyErrors);
	} else {
		printf("Verify OK. %s seems correct.\n",fNamePtr);
	}

	return S_OK;
}

