/*
 * $RCSfile: download.c,v $  $Revision: 1.9 $  $Name:  $
 * $Id: download.c,v 1.9 2007/09/09 18:16:03 bpaauwe Exp $
 * $Author: bpaauwe $
 * $Date: 2007/09/09 18:16:03 $
 * ----------------------------------------------------------------------------
 *
 *  Copyright (c) Bob Paauwe (2006)
 *
 *  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.,
 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * ----------------------------------------------------------------------------
 *
 *  Description:
 *    Program the PLC with the timercore SALAD application.
 *
 * ----------------------------------------------------------------------------
 */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <memory.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include "ilib.h"


#define VERSION "download version 0.2.20a Copyright (C) 2006, Bob Paauwe"
#define BLKSIZE 256
#define SALAD_ADDR 0x0210   /* Start address in PLC for salad code */

void download_app(ilib_t *iplc, char *fname);
void verify_app(ilib_t *iplc, char *fname);

void usage(char *prog)
{
	printf("%s\n\n", VERSION);
	printf("This program will download a binary timercoreapp file to the\n");
	printf("PLC. It does no error checking or validation that the file was\n");
	printf("correctly downloaded.  Once complete, all timers will be reset\n");
	printf("and must be reloaded.\n");
	printf("\n");
	printf("usage: %s\n", prog);
	printf("       [-p <timercoreapp file>] download program to PLC.\n");
	printf("       [-v <timercoreapp file>] verify PLC core app.\n");
	printf("       [-d <start addr> <length>] dump PLC memory.\n");
	printf("       [-r] reset PLC.\n");
}

int main (int argc, char **argv)
{
	ilib_t *iplc;
	unsigned char test[100];
	unsigned char buffer[65536];
	char device_id[10];
	unsigned int start_addr;
	int dump = 0;
	char *fname = NULL;
	int i;
	int verify = 0;
	int reset = 0;

	memset(device_id, 0, 10);

	if (argc > 0) {
		for (i = 1; i < argc; i++) {
			if (argv[i][0] == '-') {
				switch (argv[i][1]) {
					case 'V':
						printf("%s\n", VERSION);

						printf("arg = %d\n", alpha_to_i(argv[++i]));
						return 0;
						break;
					case 'h':
						usage(argv[0]);
						return 0;
						break;
					case 'd': /* do memory dump */
						dump = 1;
						start_addr = alpha_to_i(argv[++i]);
						dump = atoi(argv[++i]);
						break;
					case 'p': /* Program PLC */
						fname = argv[++i];
						break;
					case 'r': /* reset PLC */
						reset = 1;
						break;
					case 'v': /* Program PLC */
						fname = argv[++i];
						verify = 1;
						break;
					default:
						usage(argv[0]);
						return 0;
						break;
				}
			}
		}
	}

	printf("%s\n", VERSION);
	/* if ((iplc = ilib_open(USE_IPLC, "/dev/usb/iplc0")) == NULL) { */
	if ((iplc = ilib_open(USE_HIDDEV, "/dev/hiddev0")) == NULL) {
		perror("download:");
		return -1;
		if ((iplc = ilib_open(USE_LIBHID, NULL)) == NULL) {
			if ((iplc = ilib_open(USE_LIBUSB, NULL)) == NULL) {
				printf("Failed to connect to PLC\n");
				return -1;
			}
		}
	}

	/* Send some test commands */
	if (ibios_get_version(iplc, &test[0], buffer) == 0) {
		printf("PLC ID: %s   Firmware version %02x\n", buffer, test[0]);
	} else {
		printf("Command failed\n");
	}

	if(dump) {
		printf("Dump %d bytes starting at 0x%04x\n", dump, start_addr);
		ibios_read_memory(iplc, start_addr, dump, buffer);
		for (i = 0; i < dump; i++) {
			if ((i % 16) == 0) {
				printf("\n0x%04x: ", (start_addr + i));
			}
			printf("%02x ", (buffer[i] & 0xff));
		}
		printf("\n");
	}

	if (fname) {
		if (verify) {
			verify_app(iplc, fname);
		} else {
			download_app(iplc, fname);
		}
	}

	if (reset) {
		printf("Resetting the PLC....\n");
		ibios_reset(iplc);
	}

	ilib_close(iplc);
	return 0;
}


/*
 * download_app
 *
 * Read the data from the file and download it to the PLC.  The SALAD
 * applications starts at address 0x0210 and is 4096 bytes long
 * (subject to change).
 *
 * This area of memory is write protected so it needs to be write
 * enabled before the writing can take place.
 */
void download_app(ilib_t *iplc, char *fname)
{
	unsigned char buffer[BLKSIZE];
	int fd;
	int blk, blks = 0;
	unsigned short addr = SALAD_ADDR;
	unsigned char wp;
	struct stat sbuf;

	/* Get file status info */
	stat (fname, &sbuf);
	printf("File is %d bytes\n", (int)sbuf.st_size);

	/* open binary file with timer core application */
	fd = open (fname, O_RDONLY);
	if (fd < 0) {
		printf("Failed to open %s\n", fname);
		return;
	}

	/* Turn off write protect of salad application */
	ibios_read_memory(iplc, 0x016b, 1, &wp);
	wp |= 0x08;
	ibios_write_memory(iplc, 0x016b, 1, &wp);

	/* Read from file, write to PLC */
	while ( (blk = read(fd, buffer, BLKSIZE)) != 0) {
		ibios_write_memory(iplc, addr, blk, buffer);
		blks += blk;
		printf("Wrote %d%%\n", (100 * blks) / (int)sbuf.st_size);
		addr += blk;
	}

	close (fd);

	/* Turn write protect back on */
	wp &= ~0x08;
	ibios_write_memory(iplc, 0x016b, 1, &wp);

	/* Reset the PLC */
	printf("Request system reset....\n");
	sleep(2);
	ibios_read_memory(iplc, 0x0154, 1, &wp);
	wp |= 0x80;
	ibios_write_memory(iplc, 0x0154, 1, &wp);
	sleep(2);
	printf("Resetting the PLC....\n");
	ibios_reset(iplc);
}


/*
 * verify_app
 *
 * Read the data from the file and verify it with what's in the PLC. 
 * The SALAD applications starts at address 0x0210 and is 4096 bytes long
 * (subject to change).
 *
 */
void verify_app(ilib_t *iplc, char *fname)
{
	unsigned char buffer[BLKSIZE];
	unsigned char plc[BLKSIZE];
	int fd;
	int i;
	int blk, blks = 0;
	unsigned short addr = SALAD_ADDR;
	struct stat sbuf;

	/* Get file status info */
	stat (fname, &sbuf);
	printf("File is %d bytes\n", (int)sbuf.st_size);

	/* open binary file with timer core application */
	fd = open (fname, O_RDONLY);
	if (fd < 0) {
		printf("Failed to open %s\n", fname);
		return;
	}


	/* Read from file, write to PLC */
	while ( (blk = read(fd, buffer, BLKSIZE)) != 0) {
		ibios_read_memory(iplc, addr, blk, plc);
		for (i = 0; i < blk; i++) {
			if (buffer[i] != plc[i]) {
				printf("Verify failed at address 0x%08x "
						"(plc = 0x%02x vs. 0x%02x)\n",
						addr + i, plc[i], buffer[i]);
			}
		}
		blks += blk;
		printf("Verified %d%%\n", (100 * blks) / (int)sbuf.st_size);
		addr += blk;
	}

	close (fd);
}
