/*
 *  xsvftool-xpcu - An (X)SVF player for the Xilinx Platform Cable USB
 *
 *  Copyright (C) 2011  RIEGL Research ForschungsGmbH
 *  Copyright (C) 2011  Clifford Wolf <clifford@clifford.at>
 *  
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *  
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#define _GNU_SOURCE

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>

#include "libxsvf.h"
#include "fx2usb-interface.h"

#include "filedata.h"

char *correct_cksum =
#include "hardware_cksum_c.inc"
;

#define UNUSED __attribute__((unused))

/**** BEGIN: http://svn.clifford.at/tools/trunk/examples/check.h ****/

// This is to not confuse the VIM syntax highlighting
#define CHECK_VAL_OPEN (
#define CHECK_VAL_CLOSE )

#define CHECK(result, check)                                         \
   CHECK_VAL_OPEN{                                                   \
     typeof(result) _R = (result);                                   \
     if (!(_R check)) {                                              \
       fprintf(stderr, "Error from '%s' (%d %s) in %s:%d.\n",        \
               #result, (int)_R, #check, __FILE__, __LINE__);        \
       fprintf(stderr, "ERRNO(%d): %s\n", errno, strerror(errno));   \
       abort();                                                      \
     }                                                               \
     _R;                                                             \
   }CHECK_VAL_CLOSE

#define CHECK_PTR(result, check)                                         \
   CHECK_VAL_OPEN{                                                   \
     typeof(result) _R = (result);                                   \
     if (!(_R check)) {                                              \
       fprintf(stderr, "Error from '%s' (%p %s) in %s:%d.\n",        \
               #result, (void*)_R, #check, __FILE__, __LINE__);        \
       fprintf(stderr, "ERRNO(%d): %s\n", errno, strerror(errno));   \
       abort();                                                      \
     }                                                               \
     _R;                                                             \
   }CHECK_VAL_CLOSE

/**** END: http://svn.clifford.at/tools/trunk/examples/check.h ****/

FILE *file_fp = NULL;

int usb_vendor_id = 0;
int usb_device_id = 0;
char *usb_device_file = NULL;

int mode_frequency = 6000;
int mode_async_check = 0;
int mode_internal_cpld = 0;
int mode_8bit_per_cycle = 0;
int mode_hex_rmask = 0;

usb_dev_handle *fx2usb;
int internal_jtag_scan_test = 0;

int sync_count;
int tck_cycle_count;
int blocks_without_sync;

int tdo_check_period_100;
int tdo_check_thisperiod;

int rmask_bits = 0, rmask_bytes = 0;
unsigned char *rmask_data = NULL;

/* This constant are used to determine when the error status should be synced.
 * Syncinc to often would slow things down, syncinc not often enough might cause
 * errors beeing reported by far to late.
 */
#define FORCE_SYNC_AFTER_N_BLOCKS 100
#define FORCE_SYNC_MIN_PERIOD   10000
#define FORCE_SYNC_INIT_PERIOD 100000

// send larger junks to USB stack and let the kernel split it up
// #define MAXBUF() (mode_internal_cpld ? 50 : mode_8bit_per_cycle ? 500 : 1000)
#define MAXBUF() (mode_internal_cpld ? 50 : 4000)

unsigned char fx2usb_retbuf[65];
int fx2usb_retlen;

unsigned char commandbuf[4096];
int commandbuf_len;

static void shrink_8bit_to_4bit()
{
	int i;
	if ((commandbuf_len & 1) != 0)
		commandbuf[commandbuf_len++] = 0;
	for (i = 0; i<commandbuf_len; i++) {
		if ((i & 1) == 0)
			commandbuf[i >> 1] = (commandbuf[i >> 1] & 0xf0) | commandbuf[i];
		else
			commandbuf[i >> 1] = (commandbuf[i >> 1] & 0x0f) | (commandbuf[i] << 4);
	}
	commandbuf_len = commandbuf_len >> 1;
}

void fx2usb_command(const char *cmd)
{
	// fprintf(stderr, "Sending FX2USB Command: '%s' => ", cmd);
	fx2usb_send_chunk(fx2usb, 1, cmd, strlen(cmd));
	fx2usb_recv_chunk(fx2usb, 1, fx2usb_retbuf, sizeof(fx2usb_retbuf)-1, &fx2usb_retlen);
	fx2usb_retbuf[fx2usb_retlen] = 0;
	// fprintf(stderr, "'%s'\n", fx2usb_retbuf);
	
	if (strchr((char*)fx2usb_retbuf, '!') != NULL) {
		fprintf(stderr, "Internal ERROR in communication with probe: '%s' => '%s'\n", cmd, fx2usb_retbuf);
		abort();
	}
}

static int xpcu_set_frequency(struct libxsvf_host *h UNUSED, int v);
static int xpcu_pulse_tck(struct libxsvf_host *h UNUSED, int tms, int tdi, int tdo, int rmask UNUSED, int sync);

static int xpcu_setup(struct libxsvf_host *h UNUSED)
{
	sync_count = 0;
	blocks_without_sync = 0;
	commandbuf_len = 0;
	fx2usb_command("R");

	if (!mode_internal_cpld) {
		fx2usb_command("B1");
	}

	if (mode_frequency)
		xpcu_set_frequency(h, mode_frequency * 1000);

	tdo_check_period_100 = FORCE_SYNC_INIT_PERIOD * 100;
	tdo_check_thisperiod = 0;

	return 0;
}

static int xpcu_shutdown(struct libxsvf_host *h UNUSED)
{
	int rc = 0;
	if (commandbuf_len != 0) {
		fprintf(stderr, "Found %d unsynced commands in command buffer on interface shutdown!\n", commandbuf_len);
		commandbuf_len = 0;
		rc = -1;
	}
	fx2usb_command("S");
	if (fx2usb_retbuf[mode_internal_cpld ? 1 : 0] == '1') {
		fprintf(stderr, "Found pending errors in interface status on shutdown!\n");
		rc = -1;
	}
	fx2usb_command("R");
	return rc;
}

static void xpcu_udelay(struct libxsvf_host *h UNUSED, long usecs, int tms, long num_tck)
{
	struct timeval tv1, tv2;
	long rem_usecs;

	if (mode_internal_cpld)
	{
		if (!mode_8bit_per_cycle)
			shrink_8bit_to_4bit();
		unsigned char tempbuf[64];
		tempbuf[0] = 'J';
		memcpy(tempbuf+1, commandbuf, commandbuf_len);
		fx2usb_send_chunk(fx2usb, 1, tempbuf, commandbuf_len + 1);

		fx2usb_command("P");
		commandbuf_len = 0;
	}
	else
	{
		sync_count = 0x08 | ((sync_count+1) & 0x0f);
		commandbuf[commandbuf_len++] = 0x01;
		commandbuf[commandbuf_len++] = sync_count;
		if (!mode_8bit_per_cycle)
			shrink_8bit_to_4bit();
		fx2usb_send_chunk(fx2usb, 2, commandbuf, commandbuf_len);
		commandbuf_len = 0;

		char cmd[3];
		snprintf(cmd, 3, "W%x", sync_count);
		fx2usb_command(cmd);
	}

	gettimeofday(&tv1, NULL);

	while (num_tck > 0) {
		xpcu_pulse_tck(h, tms, 0, -1, 0, 0);
		num_tck--;
	}

	if (mode_internal_cpld)
	{
		if (!mode_8bit_per_cycle)
			shrink_8bit_to_4bit();
		unsigned char tempbuf[64];
		tempbuf[0] = 'J';
		memcpy(tempbuf+1, commandbuf, commandbuf_len);
		fx2usb_send_chunk(fx2usb, 1, tempbuf, commandbuf_len + 1);

		fx2usb_command("P");
		commandbuf_len = 0;
	}
	else
	{
		sync_count = 0x08 | ((sync_count+1) & 0x0f);
		commandbuf[commandbuf_len++] = 0x01;
		commandbuf[commandbuf_len++] = sync_count;
		if (!mode_8bit_per_cycle)
			shrink_8bit_to_4bit();
		fx2usb_send_chunk(fx2usb, 2, commandbuf, commandbuf_len);
		commandbuf_len = 0;

		char cmd[3];
		snprintf(cmd, 3, "W%x", sync_count);
		fx2usb_command(cmd);
	}

	while (usecs > 0) {
		gettimeofday(&tv2, NULL);
		rem_usecs = usecs - ((tv2.tv_sec - tv1.tv_sec)*1000000 + (tv2.tv_usec - tv1.tv_usec));
		if (rem_usecs <= 0)
			break;
		usleep(rem_usecs);
	}
}

static int xpcu_getbyte(struct libxsvf_host *h UNUSED)
{
	return fgetc(file_fp);
}

static int xpcu_pulse_tck(struct libxsvf_host *h UNUSED, int tms, int tdi, int tdo, int rmask, int sync)
{
	int dummy_sync = 0;

	tck_cycle_count++;

	if (tdo >= 0) {
		commandbuf[commandbuf_len++] = 0x08 | ((tdo & 1) << 2) | ((tms & 1) << 1) | ((tdi & 1) << 0);
		tdo_check_period_100 = (tdo_check_period_100 * 99) / 100 + tdo_check_thisperiod;
		tdo_check_thisperiod = 0;
	} else {
		commandbuf[commandbuf_len++] = 0x04 | ((tms & 1) << 1) | ((tdi & 1) << 0);
	}

	if (mode_async_check == 0)
	{
		if (!sync && tdo >= 0 && (blocks_without_sync > FORCE_SYNC_AFTER_N_BLOCKS || tdo_check_period_100 > FORCE_SYNC_MIN_PERIOD))
			sync = 1;
		if (!sync && !mode_internal_cpld && blocks_without_sync > 10*FORCE_SYNC_AFTER_N_BLOCKS && commandbuf_len >= (MAXBUF() - 10))
			dummy_sync = 1;
	}

	if (rmask && !sync)
		dummy_sync = 1;

	if ((dummy_sync || sync) && !mode_internal_cpld) {
		sync_count = 0x08 | ((sync_count+1) & 0x0f);
		commandbuf[commandbuf_len++] = 0x01;
		commandbuf[commandbuf_len++] = sync_count;
	}

	if (commandbuf_len >= (MAXBUF() - 4) || sync || dummy_sync) {
		if (!mode_8bit_per_cycle)
			shrink_8bit_to_4bit();
		if (mode_internal_cpld) {
			unsigned char tempbuf[64];
			tempbuf[0] = 'J';
			memcpy(tempbuf+1, commandbuf, commandbuf_len);
			fx2usb_send_chunk(fx2usb, 1, tempbuf, commandbuf_len + 1);
		} else {
			fx2usb_send_chunk(fx2usb, 2, commandbuf, commandbuf_len);
		}
		blocks_without_sync++;
		commandbuf_len = 0;
	}

	if ((sync || dummy_sync) && !mode_internal_cpld) {
		char cmd[3];
		snprintf(cmd, 3, "W%x", sync_count);
		fx2usb_command(cmd);
	}

	if (sync) {
		fx2usb_command("S");
		blocks_without_sync = 0;
	}

	if (dummy_sync) {
		fx2usb_command("P");
		blocks_without_sync = 0;
	}

	if (rmask) {
		if (rmask_bits >= 8*rmask_bytes) {
			int old_rmask_bytes = rmask_bytes;
			rmask_bytes = rmask_bytes ? rmask_bytes*2 : 64;
			rmask_data = realloc(rmask_data, rmask_bytes);
			memset(rmask_data + old_rmask_bytes, 0, rmask_bytes-old_rmask_bytes);
		}
		if (fx2usb_retbuf[mode_internal_cpld ? 5 : 4] == '1')
			rmask_data[rmask_bits/8] |= 1 << (rmask_bits%8);
		rmask_bits++;
	}

	if (sync) {
		if (fx2usb_retbuf[mode_internal_cpld ? 1 : 0] == '1')
			return -1;
		return fx2usb_retbuf[mode_internal_cpld ? 5 : 4] == '1';
	}
	return tdo < 0 ? 1 : tdo;
}

static int xpcu_sync(struct libxsvf_host *h UNUSED)
{
	if (!mode_internal_cpld) {
		sync_count = 0x08 | ((sync_count+1) & 0x0f);
		commandbuf[commandbuf_len++] = 0x01;
		commandbuf[commandbuf_len++] = sync_count;
	}

	if (!mode_8bit_per_cycle)
		shrink_8bit_to_4bit();
	if (mode_internal_cpld) {
		unsigned char tempbuf[64];
		tempbuf[0] = 'J';
		memcpy(tempbuf+1, commandbuf, commandbuf_len);
		fx2usb_send_chunk(fx2usb, 1, tempbuf, commandbuf_len + 1);
	} else {
		fx2usb_send_chunk(fx2usb, 2, commandbuf, commandbuf_len);
	}
	commandbuf_len = 0;

	if (!mode_internal_cpld) {
		char cmd[3];
		snprintf(cmd, 3, "W%x", sync_count);
		fx2usb_command(cmd);
	}

	fx2usb_command("S");
	blocks_without_sync = 0;
	if (fx2usb_retbuf[mode_internal_cpld ? 1 : 0] == '1')
		return -1;

	return 0;
}

static int xpcu_set_frequency(struct libxsvf_host *h UNUSED, int v)
{
	int freq = 24000000, delay = 0;

	if (mode_internal_cpld)
		return 0;

	if (!mode_internal_cpld) {
		sync_count = 0x08 | ((sync_count+1) & 0x0f);
		commandbuf[commandbuf_len++] = 0x01;
		commandbuf[commandbuf_len++] = sync_count;
	}

	if (!mode_8bit_per_cycle)
		shrink_8bit_to_4bit();
	if (mode_internal_cpld) {
		unsigned char tempbuf[64];
		tempbuf[0] = 'J';
		memcpy(tempbuf+1, commandbuf, commandbuf_len);
		fx2usb_send_chunk(fx2usb, 1, tempbuf, commandbuf_len + 1);
	} else {
		fx2usb_send_chunk(fx2usb, 2, commandbuf, commandbuf_len);
	}
	commandbuf_len = 0;

	if (!mode_internal_cpld) {
		char cmd[3];
		snprintf(cmd, 3, "W%x", sync_count);
		fx2usb_command(cmd);
	}

	while (delay < 250 && v < freq) {
		delay++;
		freq = 48000000 / (2*delay + 2);
	}

	if (v < freq)
		fprintf(stderr, "Requested FREQUENCY %dHz is to low! Using minimum value %dHz instead.\n", v, freq);
	else if (v-freq > 10)
		fprintf(stderr, "Requested FREQUENCY is %dHz. Using %dHz (24MHz/%d) instead.\n", v, freq, delay+1);

	char cmd[4];
	snprintf(cmd, 4, "T%02x", delay);
	fx2usb_command(cmd);

	mode_8bit_per_cycle = delay != 0;

	if (!mode_internal_cpld)
	{
		sync_count = 0x08 | ((sync_count+1) & 0x0f);
		commandbuf[commandbuf_len++] = 0x01;
		commandbuf[commandbuf_len++] = sync_count;
		if (!mode_8bit_per_cycle)
			shrink_8bit_to_4bit();
		fx2usb_send_chunk(fx2usb, 2, commandbuf, commandbuf_len);
		commandbuf_len = 0;

		char cmd[3];
		snprintf(cmd, 3, "W%x", sync_count);
		fx2usb_command(cmd);
	}

	return 0;
}

static void xpcu_report_tapstate(struct libxsvf_host *h UNUSED)
{
	// fprintf(stderr, "[%s]\n", libxsvf_state2str(h->tap_state));
}

static void xpcu_report_device(struct libxsvf_host *h UNUSED, unsigned long idcode)
{
	if (internal_jtag_scan_test != 0) {
		// CPLD should be: idcode=0x16d4a093, revision=0x1, part=0x6d4a, manufactor=0x049
		if (((idcode >> 12) & 0xffff) == 0x6d4a && ((idcode >> 1) & 0x7ff) == 0x049)
			internal_jtag_scan_test += 1;
		else
			internal_jtag_scan_test += 2;
	} else {
		printf("idcode=0x%08lx, revision=0x%01lx, part=0x%04lx, manufactor=0x%03lx\n", idcode,
				(idcode >> 28) & 0xf, (idcode >> 12) & 0xffff, (idcode >> 1) & 0x7ff);
	}
}

static void xpcu_report_status(struct libxsvf_host *h UNUSED, const char *message UNUSED)
{
	// fprintf(stderr, "[STATUS] %s\n", message);
}

static void xpcu_report_error(struct libxsvf_host *h UNUSED, const char *file, int line, const char *message)
{
	fprintf(stderr, "[%s:%d] %s\n", file, line, message);
}

static void *xpcu_realloc(struct libxsvf_host *h UNUSED, void *ptr, int size, enum libxsvf_mem which UNUSED)
{
	return realloc(ptr, size);
}

static struct libxsvf_host h = {
	.udelay = xpcu_udelay,
	.setup = xpcu_setup,
	.shutdown = xpcu_shutdown,
	.getbyte = xpcu_getbyte,
	.pulse_tck = xpcu_pulse_tck,
	.sync = xpcu_sync,
	.set_frequency = xpcu_set_frequency,
	.report_tapstate = xpcu_report_tapstate,
	.report_device = xpcu_report_device,
	.report_status = xpcu_report_status,
	.report_error = xpcu_report_error,
	.realloc = xpcu_realloc
};

const char *progname;

static void help()
{
	fprintf(stderr, "\n");
	fprintf(stderr, "A JTAG SVF/XSVF Player based on libxsvf for the Xilinx Platform Cable USB\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "xsvftool-xpcu, part of Lib(X)SVF (http://www.clifford.at/libxsvf/).\n");
	fprintf(stderr, "Copyright (C) 2011  RIEGL Research ForschungsGmbH\n");
	fprintf(stderr, "Copyright (C) 2011  Clifford Wolf <clifford@clifford.at>\n");
	fprintf(stderr, "Lib(X)SVF is free software licensed under the ISC license.\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "Usage: %s [ -L | -B ] [ -d <vendor>:<device> | -D <device_file> ] [ -f kHz ] [ -A ] [ -P ]\n", progname);
	fprintf(stderr, "       %*s { -E | -p | -s svf-file | -x xsvf-file | -c } ...\n", (int)strlen(progname), "");
	fprintf(stderr, "\n");
	fprintf(stderr, "   -L, -B\n");
	fprintf(stderr, "          Print RMASK bits as hex value (little or big endian)\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "   -d <vendor>:<device>\n");
	fprintf(stderr, "          Open the device with this USB vendor and device ID.\n");
	fprintf(stderr, "          (default: 03fd:000d and 04b4:8613\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "   -D <device_file>\n");
	fprintf(stderr, "          Open this USB device (usually something like /dev/bus/usb/012/345)\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "   -f kHz\n");
	fprintf(stderr, "          Run probe with the specified frequency (default=6000, max=24000)\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "   -A\n");
	fprintf(stderr, "          Use full asynchonous error checking\n");
	fprintf(stderr, "          (very fast but error reporting might be delayed)\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "   -P\n");
	fprintf(stderr, "          Use CPLD on probe as target device\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "   -p\n");
	fprintf(stderr, "          Force (re-)programming the CPLD on the probe\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "   -E\n");
	fprintf(stderr, "          Erase the CPLD on the probe\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "   -s svf-file\n");
	fprintf(stderr, "          Play the specified SVF file\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "   -x xsvf-file\n");
	fprintf(stderr, "          Play the specified XSVF file\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "   -c\n");
	fprintf(stderr, "          List devices in JTAG chain\n");
	fprintf(stderr, "\n");
	exit(1);
}

int main(int argc, char **argv)
{
	int rc = 0;
	int gotaction = 0;
	int opt, i, j;

	int done_initialization = 0;

	progname = argc >= 1 ? argv[0] : "xsvftool-xpcu";
	while ((opt = getopt(argc, argv, "LBd:D:f:APpEs:x:c")) != -1)
	{
		if (!done_initialization && (opt == 'p' || opt == 'E' || opt == 's' || opt == 'x' || opt == 'c'))
		{
			usb_init();
			usb_find_busses();
			usb_find_devices();

			fx2usb = fx2usb_open(usb_vendor_id, usb_device_id, usb_device_file);
			if (fx2usb == NULL) {
				fprintf(stderr, "Failed to find or open USB device!\n");
				exit(1);
			}
			CHECK(fx2usb_claim(fx2usb), == 0);

			FILE *ihexf = CHECK_PTR(fmemopen(firmware_ihx, sizeof(firmware_ihx), "r"), != NULL);
			CHECK(fx2usb_upload_ihex(fx2usb, ihexf), == 0);
			CHECK(fclose(ihexf), == 0);

			i = mode_internal_cpld;
			mode_internal_cpld = 1;
			internal_jtag_scan_test = 1;
			libxsvf_play(&h, LIBXSVF_MODE_SCAN);

			if (internal_jtag_scan_test != 2) {
				fprintf(stderr, "Probe (device %s on bus %s) failed internal JTAG scan test!\n",
					usb_device(fx2usb)->filename, usb_device(fx2usb)->bus->dirname);
				exit(1);
			}
			mode_internal_cpld = i;
			internal_jtag_scan_test = 0;

			fprintf(stderr, "Connected to probe (device %s on bus %s) and passed internal JTAG scan test.\n",
				usb_device(fx2usb)->filename, usb_device(fx2usb)->bus->dirname);

			if (opt != 'p' && opt != 'E' && !mode_internal_cpld) {
				fx2usb_command("C");
				if (memcmp(correct_cksum, fx2usb_retbuf, 6)) {
					fprintf(stderr, "Mismatch in CPLD checksum (is=%.6s, should=%s): reprogramming CPLD on probe..\n",
							fx2usb_retbuf, correct_cksum);
					i = mode_internal_cpld;
					mode_internal_cpld = 1;
					file_fp = CHECK_PTR(fmemopen(hardware_svf, sizeof(hardware_svf), "r"), != NULL);
					libxsvf_play(&h, LIBXSVF_MODE_SVF);
					mode_internal_cpld = i;
					fclose(file_fp);
				}
			}

			done_initialization = 1;
		}

		switch (opt)
		{
		case 'L':
			mode_hex_rmask = 1;
			break;
		case 'B':
			mode_hex_rmask = 2;
			break;
		case 'd':
			if (usb_vendor_id || usb_device_id || usb_device_file || fx2usb)
				help();
			if (sscanf(optarg, "%x:%x", &usb_vendor_id, &usb_device_id) != 2)
				help();
			break;
		case 'D':
			if (usb_vendor_id || usb_device_id || usb_device_file || fx2usb)
				help();
			usb_device_file = strdup(optarg);
			break;
		case 'f':
			mode_frequency = atoi(optarg);
			break;
		case 'P':
			mode_internal_cpld = 1;
			break;
		case 'A':
			mode_async_check = 1;
			break;
		case 'p':
		case 'E':
			gotaction = 1;
			i = mode_internal_cpld;
			mode_internal_cpld = 1;
			if (opt == 'p') {
				file_fp = CHECK_PTR(fmemopen(hardware_svf, sizeof(hardware_svf), "r"), != NULL);
				fprintf(stderr, "(Re-)programming CPLD on the probe..\n");
			} else {
				file_fp = CHECK_PTR(fmemopen(erasecpld_svf, sizeof(erasecpld_svf), "r"), != NULL);
				fprintf(stderr, "Erasing CPLD on the probe..\n");
			}
			libxsvf_play(&h, LIBXSVF_MODE_SVF);
			mode_internal_cpld = i;
			fclose(file_fp);
			break;
		case 'x':
		case 's':
			gotaction = 1;
			if (!strcmp(optarg, "-"))
				file_fp = stdin;
			else
				file_fp = fopen(optarg, "rb");
			if (file_fp == NULL) {
				fprintf(stderr, "Can't open %s file `%s': %s\n", opt == 's' ? "SVF" : "XSVF", optarg, strerror(errno));
				rc = 1;
				break;
			}
			fprintf(stderr, "Playing %s file `%s'..\n", opt == 's' ? "SVF" : "XSVF", optarg);
			if (libxsvf_play(&h, opt == 's' ? LIBXSVF_MODE_SVF : LIBXSVF_MODE_XSVF) < 0) {
				fprintf(stderr, "Error while playing %s file `%s'.\n", opt == 's' ? "SVF" : "XSVF", optarg);
				rc = 1;
			}
			if (strcmp(optarg, "-"))
				fclose(file_fp);
			break;
		case 'c':
			gotaction = 1;
			fprintf(stderr, "Scanning JTAG chain..\n");
			if (libxsvf_play(&h, LIBXSVF_MODE_SCAN) < 0) {
				fprintf(stderr, "Error while scanning JTAG chain.\n");
				rc = 1;
			}
			break;
		default:
			help();
			break;
		}
	}

	if (!gotaction)
		help();

	if (rmask_bits > 0) {
		fprintf(stderr, "Total number of rmask bits acquired: %d\n", rmask_bits);
		if (mode_hex_rmask) {
			printf("0x");
			for (i = 0; i < rmask_bits; i += 4) {
				int val = 0;
				for (j = i; j < i + 4; j++) {
					int pos = mode_hex_rmask > 1 ? j : rmask_bits - j - 1;
					val = (val << 1) | (rmask_data[pos/8] & (1 << (pos%8)) ? 1 : 0);
				}
				printf("%x", val);
			}
		} else {
			for (i = 0; i < rmask_bits; i++)
				putchar((rmask_data[i / 8] & (1 << (i % 8))) != 0 ? '1' : '0');
		}
		putchar('\n');
	}

	if (done_initialization) {
		fx2usb_command("X");
		fx2usb_release(fx2usb);
		usb_close(fx2usb);
	}

	fprintf(stderr, "Total number of JTAG clock cycles performed: %d\n", tck_cycle_count);
	if (rc == 0)
		fprintf(stderr, "READY.\n");
	else
		fprintf(stderr, "TERMINATED WITH ERROR(s)! (see above)\n");

	return rc;
}

