CIexample.c

/*****************************************************************************

	CIexample.c			Source for BitFlow CI lib example program

	Sep 28,		2008	CIW/SJT

	� Copyright 2008, BitFlow, Inc. All rights reserved.

	Tabstops are 4

	$Author: steve $

	$Date: 2020/10/02 23:30:12 $

	$Id: CIexample.c,v 1.42 2020/10/02 23:30:12 steve Exp $

*****************************************************************************/

/*==========================================================================*/
/*
**	For access to command line display.
*/
#include	<stdio.h>
#include	<stdarg.h>
#include	<string.h>
#include	<errno.h>
/*
**	For checking for keypress
*/
#include	<sys/time.h>
#include	<sys/types.h>
#include	<unistd.h>
/*
**	For getting timing information.
*/
#include	<time.h>
#include	<sys/timeb.h>
/*
**	For access to BitFlow camera interface library.
*/
#include	"BFciLib.h"
/*==========================================================================*/
static int sExitAns = 0;		/* program exit code */
static int sLibDebug = 0;		/* enable library display */
static tCIp sCIp = NULL;		/* device open token */
static int sNdx = 0;			/* device index */
static int sBGok = 0;			/* ok to be in background */
static int sNframes = 0;		/* number of DMA frames */
static int sSkipFrames = 0;		/* skip frames between display */
static int sLines = 1;			/* lines to display */
static int sTimeout = -1;		/* timeout (mSec) */
static int sDelay = -1;			/* delay for, e.g., taskset */
static int sMaxFrames = 0;		/* total frames to display */
static int sNoClear = 0;		/* do not modify frame lines */
static int sSecondsOA = 0;		/* overall timeout */
static int sPixShow = -1;		/* size of display */
static int sHiFrameRate = 0;	/* high frame rate polling */

static int sNoRule = 0;			/* DMA as fast as possible */
/*
**	Normal behavior is to use the default config file
*/
static char sCfgFN[1024] = "";	/* CiVFGinitialize() cfg file */
/*--------------------------------------------------------------------------*/
#define	SHOW(x)	{ (void)printf x ; (void)fflush(stdout); }
#define	ERR(x)	{ SHOW(("ERR: ")); SHOW(x); }
/*--------------------------------------------------------------------------*/
static void LDBdisplay(char *format, ...)
/*
**	Library debug display function.
**
**	This will display progress/information/error strings from BFciLib
*/
{
	va_list val;
	char buff[8192];

	va_start(val, format);
	(void)vsprintf(buff, format, val);
	va_end(val);
	/*
	 ** buff now contains the library display string
	 **
	 ** This function simply prints it to the console.
	 */
	(void)printf("LDB: %s\n", buff);
	(void)fflush(stdout);
}

/*--------------------------------------------------------------------------*/
static char *sArgv0 = NULL;
static void ShowHelp(void)
{
	SHOW(("%s of " __DATE__ " at " __TIME__ "\n", sArgv0));
	SHOW(("   -h           display this message and exit\n"));
	SHOW(("   -i           show info on system and exit\n"));
	SHOW(("   -I           show more info on system and exit\n"));
	SHOW(("\n"));
	SHOW(("   -x ndx       choose available device ndx (default 0)\n"));
	SHOW(("   -n nFrames   reconfigure buffers for nFrames\n"));
	SHOW(("   -N           set DMA_NO_RULE and ignore OVSTEP\n"));
	SHOW(("   -D           display library debug info ('LDB' prefix)\n"));
	SHOW(("   -b           program is backgrounded (no newline exit)\n"));
	SHOW(("   -c cfgFN     CiVFGinitialize() with this config file\n"));
	SHOW(("   -s nSkip     skip frames between display frames (0 default)\n"));
	SHOW(("   -l nLines    display n lines from each frame (1 default)\n"));
	SHOW(("   -t mSec      timeout when waiting for new data (-1 default)\n"));
	SHOW(("   -w mSec      delay after setup, before start aq (-1 default)\n"));
	SHOW(("   -W nPixShow  show this many pixels (default auto size)\n"));
	SHOW(("   -T sec       timeout overall\n"));
	SHOW(("   -m maxFrames max frames to display (default infinite)\n"));
	SHOW(("   -C           do not clear each line after display\n"));
	SHOW(("   -P           start acquisition for High Frame Rate Polling\n"));
	SHOW(("\n"));
	SHOW(("  CIexample     show access to library display and VFG status\n"));
	SHOW(("   <noarg || -x || -n || -D>\n"));
	SHOW(("      initialize frame grabber\n"));
	SHOW(("      configure DMA\n"));
	SHOW(("      start continuous acquisition\n"));
	SHOW(("      wait for frames; display 1st 16 pix of 1st row of frame\n"));
	SHOW(("          <terminate on newline>\n"));
	SHOW(("\n"));
}

/*==========================================================================*/
static void ShowCITDP(const char *str, tCITDP citdp)
/*
**	Tool to dump typed data.
**
**	This illustrates how to access the tCITDP information.
**
**	DO NOT CHANGE ANY OF THE INFORMATION!
*/
{
	int x = 0, y;
	tCIU32 *ptr;

	SHOW(("%s\n", str));
	if (NULL == citdp)
	{
		return;
	}
	while (citdp[x].descriptor != NULL)
	{
		SHOW(("  %-20s: (%3d) (", citdp[x].descriptor, citdp[x].dataID));
		switch (citdp[x].dataType)
		{
		case kBF_tCIU32:
			SHOW(("0x%08X", (*((tCIU32 *) (citdp[x].datap)))));
			break;
		case kBF_tCIU32_pair:
			SHOW(("0x%08X", (((tCIU32 *) (citdp[x].datap)))[0]));
			SHOW((" 0x%08X", (((tCIU32 *) (citdp[x].datap)))[1]));
			break;
		case kBF_tCIDOUBLE:
			SHOW(("%.3lf", (*((tCIDOUBLE *) (citdp[x].datap)))));
			break;
		case kBF_tCISTRZ:
			SHOW(("'%s'", (((tCISTRZ) (citdp[x].datap)))));
			break;
		case kBF_tCIU32_list_pair:
			ptr = (tCIU32 *) (citdp[x].datap);
			y = *ptr++;
			SHOW(("%d:\n", y));
			while (y-- > 0)
			{
				SHOW(("                      0x%08X 0x%08X\n", ptr[0], ptr[1]));
				ptr += 2;
			}
			break;
		case kBF_tCIU32_tCISTRZ_pair:
			SHOW(("0x%08X/'%s'", (*((tCIU32 *) (citdp[x].datap))), (((tCISTRZ) (citdp[x].datap)) + sizeof(tCIU32))));
			break;
		default:
			SHOW(("--err-- do not know %d dataType", citdp[x].dataType));
		}
		SHOW((")\n"));
		x += 1;
	}
}

/*--------------------------------------------------------------------------*/
static void GetRegAndProc(int ndx)
/*
**	Read all registers and then dump the /proc file for this VFG
*/
{
	tCIRC circ;
	tCIU32 count, x, y;
	char fname[64], line[1024];
	FILE *fp;

	circ = CiRegGetCount(sCIp, &count);
	if (kCIEnoErr != circ)
	{
		ERR(("CiRegGetCount gave '%s'\n", CiErrStr(circ)));
		return;
	}
	/*
	 ** Read the registers, forcing an update of the /proc info
	 */
	for (x = 0; x < count; x++)
	{
		(void)CiRegPeek(sCIp, x, &y);
	}
	/*
	 ** Read the /proc file
	 */
	(void)sprintf(fname, "/proc/bitflow%02d", ndx);
	fp = fopen(fname, "r");
	if (NULL == fp)
	{
		ERR(("GetRegAndProc: fopen of '%s' gave '%s'\n", fname, CiErrStr(errno)));
		return;
	}
	SHOW(("contents of '%s'\n", fname));
	while (NULL != fgets(line, 1024, fp))
	{
		SHOW(("%s", line));
	}
	SHOW(("\n"));
	(void)fclose(fp);

	return;
}

/*--------------------------------------------------------------------------*/
#define	CASE_STATE_STR(label)	\
			case kCIBQ_ ## label : (void)sprintf(str,"%s", #label); break;

#define	CASE_AQSTATE_STR(label)	\
			case kCIAQS ## label : (void)sprintf(str,"%s", #label); break;

static void DoShowInfo(int doAll)
/*
**	Query all possible information from all installed frame grabbers.
*/
{
	static const char *sBoardNames[kCICT_VFGmask + 1] = {
		"--none--", "Neon", "NeonDIF", "Karbon",
		"Alta", "KbnCXP (deprecated)", "CtnCXP", "AxnCL",
		"AonCXP", "ClxCXP", "ClxFXP", "--none--",
		"--none--", "--none--", "--none--", "--none--",
		"--none--", "--none--", "--none--", "--none--",
		"--none--", "--none--", "--none--", "--none--",
		"--none--", "--none--", "--none--", "--none--",
		"--none--", "--none--", "--none--", "--none--"
	};
	tCIRC circ;
	tCITDP cfgFN, cfg, camFN, cam, firmFN, firm, serial, cxp, spare;
	tCIU32 lv, dv, nb, i, stt, ih = 0, il = 0, node;
	tCIU32 ctab[kCIszCTAB];
	tCIU32 ift, sw, ndx, db, par, sb, baud;
	char name[256], *str, vfg[64], *fmt = NULL;
	tCIU32 ic[kCINinterruptCounters];
	tCIU32 nf, bpp, h0, hsz, v0, vsz, stride, nerr;
	tCIdiagStruct ds;
	char hwRev[32];
	/*
	 ** Get library/driver version information.
	 */
	switch (circ = CiSysGetVersions(&lv, &dv))
	{
	case kCIEnoErr:
		SHOW(("CiSysGetVersions: 0x%08X library, 0x%08X driver\n", lv, dv));
		break;
	case kCIEnoAccessibleVideo:
	case kCIEnullArg:
	case kCIEversionErr:
	default:
		ERR(("CiSysGetVersions gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	/*
	 ** Get a count of frame grabbers.
	 */
	circ = CiSysVFGenum(&nb);
	if (kCIEnoErr != circ)
	{
		ERR(("CiSysVFGenum gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	SHOW(("CiSysBrdEnum returns %d frame grabbers installed\n", nb));
	/*
	 ** For all frame grabbers: show all information.
	 */
	for (i = 0; i < nb; i++)
	{
		/*
		 **   Get basic info about this frame grabber.
		 */
		circ = CiSysVFGinfo2(i, &ift, &sw, &ndx, name, &ih, &il, vfg, &node);
		if (kCIEnoErr != circ)
		{
			ERR(("CiSysVFGinfo2 of %d gave '%s'\n", i, CiErrStr(circ)));
			continue;
		}
		SHOW(("  BitFlow device %d is %s%02d, node %d, type '%s', "
			  "switches %d, on bus '%s' (%s) (0x%04x 0x%04X '%s')\n",
			  i, "/dev/video", ndx, node, sBoardNames[ift & kCICT_VFGmask], sw, name,
			  (0 != (ift & kCICT_isMaster)) ? "master" : "not_master", ih, il, vfg));
		/*
		 **   Set library debug function if '-D' was specified.
		 */
		circ = CiSetDebug(sCIp, -1, (0 == sLibDebug) ? NULL : LDBdisplay);
		if (kCIEnoErr != circ)
		{
			ERR(("CiSetDebug of %d gave '%s'\n", i, CiErrStr(circ)));
		}
		/*
		 **   Try to open this frame grabber (readWrite in case not flashed)
		 */
		circ = CiVFGopen(ndx, kCIBO_writeAccess, &sCIp);
		switch (circ)
		{
		case kCIEnoErr:
			break;
		case kCIEexclusiveFail:
			/*
			 ** It's already open exclusiveWrite so it will not need to be
			 ** flashed.
			 */
			circ = CiVFGopen(ndx, kCIBO_readOnly, &sCIp);
			if (kCIEnoErr == circ)
			{
				break;
			}
		default:
			ERR(("CiVFGopen of %d/%d gave '%s'\n", i, ndx, CiErrStr(circ)));
			continue;
		}
		/*
		 **   Get overall VFG state.
		 */
		circ = CiVFGqueryState(sCIp, &stt);
		if (kCIEnoErr != circ)
		{
			ERR(("CiVFGqueryState of %d gave '%s'\n", i, CiErrStr(circ)));
			goto closeAndNext;
		}
		str = name;
		switch (stt)
		{
		CASE_STATE_STR(shuttingDown);
		CASE_STATE_STR(startingUp);
		CASE_STATE_STR(needsFirmware);
		CASE_STATE_STR(firmwareLoaded);
		CASE_STATE_STR(dmaBufferReady);
		CASE_STATE_STR(dmaIsActive);
		CASE_STATE_STR(dmaIsAborted);
		CASE_STATE_STR(stateUnknown);
		default:
			(void)sprintf(str, "--%d invalid--", stt);
		}
		SHOW(("  frame grabber %d in %d/%s state\n", i, stt, str));
		/*
		 **   If we need firmware then there is no point continuing.
		 */
		if (kCIBQ_needsFirmware == stt)
		{
			goto closeAndNext;
		}
		/*
		 **   Query all possible typed data driver information.
		 */
		circ = CiVFGinquire2(sCIp, &cfgFN, &cfg, &camFN, &cam, &firmFN, &firm, &serial, &cxp, &spare, &fmt);
		if (kCIEnoErr != circ)
		{
			ERR(("CiVFGinquire of %d gave '%s'\n", i, CiErrStr(circ)));
			goto closeAndNext;
		}
		ShowCITDP("configuration file name", cfgFN);
		ShowCITDP("configuration file data", cfg);
		ShowCITDP("camera file name", camFN);
		ShowCITDP("camera file data", cam);
		ShowCITDP("firmware file name", firmFN);
		ShowCITDP("firmware file data", firm);
		ShowCITDP("CameraLink serial port data", serial);
		ShowCITDP("CXP link affinity", cxp);
		ShowCITDP("spare", spare);
		SHOW(("fmt str: '%s'\n", fmt));
		/*
		 **   Display all interrupt count information.
		 */
		circ = CiGetIntCounts(sCIp, ic);
		if (kCIEnoErr != circ)
		{
			ERR(("CiGetIntCounts of %d gave '%s'\n", i, CiErrStr(circ)));
			goto closeAndNext;
		}
		SHOW(("IntCounts:      HW   TRIG    SER   QUAD   CTAB    EOF OVSTEP    CXP   TOTAL\n" "            %6d %6d %6d %6d %6d %6d %6d %6d %6d\n", ic[0], ic[1], ic[2], ic[3], ic[4], ic[5], ic[6], ic[7], ic[8]));
		/*
		 **   Display buffer configuration.
		 */
		circ = CiBufferInterrogate(sCIp, &nf, &bpp, &h0, &hsz, &v0, &vsz, &stride);
		switch (circ)
		{
		case kCIEnoErr:
			SHOW(("  %d frames, %d bitsPerPix, %d-%d horizontal, %d-%d vertical, %d stride\n", nf, bpp, h0, hsz, v0, vsz, stride));
			break;
		case kCIEnotConfigured:
			SHOW(("CiBufferInterrogate of %d gave '%s'\n", i, CiErrStr(circ)));
			/*
			 **   No point checking furrther.
			 */
			goto closeAndNext;
		default:
			ERR(("CiBufferInterrogate of %d gave '%s'\n", i, CiErrStr(circ)));
			goto closeAndNext;
		}
		/*
		 **   Display acquisition status.
		 */
		circ = CiAqGetStatus(sCIp, &stt, &nf, &nerr);
		if (kCIEnoErr != circ)
		{
			ERR(("CiAqGetStatus of %d gave '%s'\n", i, CiErrStr(circ)));
			goto closeAndNext;
		}
		str = name;
		switch (stt & kCIAQSstateMask)
		{
		CASE_AQSTATE_STR(invalidState);
		CASE_AQSTATE_STR(idle);
		CASE_AQSTATE_STR(snapShotInProcess);
		CASE_AQSTATE_STR(snapShotDone);
		CASE_AQSTATE_STR(continuousInProcess);
		CASE_AQSTATE_STR(errorDone);
		CASE_AQSTATE_STR(continuousStopping);
		CASE_AQSTATE_STR(continuousDone);
		default:
			(void)sprintf(str, "(%d not known) ", (stt & kCIAQSstateMask));
		}
		if (0 != (stt & (kCIAQSaqIsSIP | kCIAQSaqUnderrun | kCIAQSaqAbort)))
		{
			(void)sprintf((str + strlen(str)), " (");
		}
		if (0 != (stt & kCIAQSaqIsSIP))
		{
			(void)sprintf((str + strlen(str)), " SIP");
		}
		if (0 != (stt & kCIAQSaqUnderrun))
		{
			(void)sprintf((str + strlen(str)), " under");
		}
		if (0 != (stt & kCIAQSaqAbort))
		{
			(void)sprintf((str + strlen(str)), " abort");
		}
		if (0 != (stt & (kCIAQSaqIsSIP | kCIAQSaqUnderrun | kCIAQSaqAbort)))
		{
			(void)sprintf((str + strlen(str)), " )");
		}
		SHOW(("  AqStatus: %s (%d frames collected, %d errs)\n", name, nf, nerr));
		/*
		 **   Get a count of available CON registerx.
		 */
		circ = CiRegGetCount(sCIp, &nf);
		if (kCIEnoErr != circ)
		{
			ERR(("CiRegGetCount of %d gave '%s'\n", i, CiErrStr(circ)));
			goto closeAndNext;
		}
		SHOW(("  registers CON0 through CON%d supported\n", nf - 1));
		/*
		 **   Display all diagnostic information.
		 */
		circ = CiGetDiag(sCIp, &ds);
		if (kCIEnoErr != circ)
		{
			ERR(("CiGetDiag of %d gave '%s'\n", i, CiErrStr(circ)));
			goto closeAndNext;
		}
		SHOW(("DiagCounts: aqStat fActiv fCount lCount pCount fenCnt vCount hCount\n" "          %08X %6d %6d %6d %6d %6d %6d %6d\n", ds.db.aqStat, ds.db.fActive, ds.db.fCount, ds.db.lCount, ds.db.pCount, ds.db.fenCount, ds.db.vCount, ds.db.hCount));
		/*
		 **   Dump the CTABs
		 */
		circ = CiReadCTABcompressed(sCIp, 0, ctab);
		if (kCIEnoErr == circ)
		{
			SHOW(("H CTAB:\n"));
			for (nf = 0; nf < kCIszCTAB; nf++)
			{
				SHOW(("  %2d: 0x%08X\n", nf, ctab[nf]));
			}
		}
		circ = CiReadCTABcompressed(sCIp, 1, ctab);
		if (kCIEnoErr == circ)
		{
			SHOW(("V CTAB:\n"));
			for (nf = 0; nf < kCIszCTAB; nf++)
			{
				SHOW(("  %2d: 0x%08X\n", nf, ctab[nf]));
			}
		}
		/*
		 **   Finally, show any available CameraLink information.
		 */
		circ = CiCLinfo(sCIp, &db, &par, &sb, &baud);
		switch (circ)
		{
		case kCIEnoErr:
			SHOW(("  CameraLink: %d dataBits, %d parity, %d stopBits, %d baud\n", db, par, sb, baud));
			break;
		case kCIEnotOpen:
			SHOW(("  CameraLink: not open\n"));
			break;
		case kCIEnotSupported:
			SHOW(("  CameraLink not supported on hardware\n"));
			break;
		default:
			ERR(("CiCLinfo of %d gave '%s'\n", i, CiErrStr(circ)));
			goto closeAndNext;
		}

closeAndNext:
		/*
		 **   Try for HW rev -- only good on Karbon master
		 */
		if (kCIEnoErr == CiReadHWrev(sCIp, hwRev))
		{
			SHOW(("  hwRev is '%s'\n", hwRev));
		}
		else
		{
			SHOW(("  hwRev not available\n"));
		}
		/*
		 **   Try for serial number
		 */
		if (kCIEnoErr == CiReadSerialNo(sCIp, hwRev))
		{
			SHOW(("  serNo is '%s'\n", hwRev));
		}
		else
		{
			SHOW(("  serNo not available\n"));
		}
		/*
		 **   Deal with registers and /proc
		 */
		if (0 != doAll)
		{
			GetRegAndProc(ndx);
		}
		/*
		 **   Close access to this frame grabber.
		 */
		if ((NULL != sCIp) && (kCIEnoErr != (circ = CiVFGclose(sCIp))))
		{
			ERR(("CiVFGclose of %d gave '%s'\n", i, CiErrStr(circ)));
		}
		sCIp = NULL;
	}

andOut:
	/*
	 ** Cleanly close access on error.
	 */
	if ((NULL != sCIp) && (kCIEnoErr != (circ = CiVFGclose(sCIp))))
	{
		ERR(("CiVFGclose gave '%s'\n", CiErrStr(circ)));
	}

	return;
}

/*--------------------------------------------------------------------------*/
static int DecodeArgs(int argc, char **argv)
/*
**	Parse the input arguments.
*/
{
	char *str;

	argv += 1;
	argc -= 1;					/* skip program name */

	while (argc-- > 0)
	{
		str = *argv++;
		if (str[0] != '-')
		{
			ERR(("Do not know '%s' arg\n", str));
			ShowHelp();
			sExitAns = 1;
			return (sExitAns);
		}
		switch (str[1])
		{
		case 'h':
			ShowHelp();
			return (1);
		case 'i':
			DoShowInfo(0);
			return (1);
		case 'I':
			DoShowInfo(1);
			return (1);
		case 'D':
			sLibDebug = 1;
			break;
		case 'x':
			(void)sscanf(*argv, "%d", &sNdx);
			argv += 1;
			argc -= 1;
			break;
		case 'm':
			(void)sscanf(*argv, "%d", &sMaxFrames);
			argv += 1;
			argc -= 1;
			break;
		case 'C':
			sNoClear = 1;
			break;
		case 'P':
			sHiFrameRate = 1;
			break;
		case 'b':
			sBGok = 1;
			break;
		case 'c':
			(void)strcpy(sCfgFN, *argv);
			argv += 1;
			argc -= 1;
			break;
		case 'n':
			(void)sscanf(*argv, "%d", &sNframes);
			argv += 1;
			argc -= 1;
			break;
		case 's':
			(void)sscanf(*argv, "%d", &sSkipFrames);
			argv += 1;
			argc -= 1;
			break;
		case 'l':
			(void)sscanf(*argv, "%d", &sLines);
			argv += 1;
			argc -= 1;
			break;
		case 'W':
			(void)sscanf(*argv, "%d", &sPixShow);
			argv += 1;
			argc -= 1;
			break;
		case 'w':
			(void)sscanf(*argv, "%d", &sDelay);
			argv += 1;
			argc -= 1;
			break;
		case 't':
			(void)sscanf(*argv, "%d", &sTimeout);
			argv += 1;
			argc -= 1;
			break;
		case 'T':
			(void)sscanf(*argv, "%d", &sSecondsOA);
			argv += 1;
			argc -= 1;
			break;
		case 'N':
			sNoRule = 1;
			break;
		default:
			ERR(("Do not know arg '%s' arg\n", str));
			ShowHelp();
			sExitAns = 1;
			return (sExitAns);
		}
	}

	return (kCIEnoErr);
}

/*--------------------------------------------------------------------------*/
static int CheckForKeyboardInput(void)
/*
**	Return 0 if no input available from stdin, 1 else
**
**	Note: the console needs a newline in order to post input.
*/
{
	fd_set exceptfds, readfds, writefds;
	struct timeval tv;
	int ans;
	char buff[1024];

	FD_ZERO(&exceptfds);
	FD_ZERO(&readfds);
	FD_ZERO(&writefds);
	FD_SET(fileno(stdin), &readfds);
	(void)memset(&tv, '\0', sizeof(struct timeval));

	ans = select(1, &readfds, &writefds, &exceptfds, &tv);

	if ((ans == 1) && FD_ISSET(fileno(stdin), &readfds))
	{
		/*
		 **   Consume the line.
		 */
		(void)fgets(buff, 1024, stdin);
		return (1);
	}

	return (0);
}

/*--------------------------------------------------------------------------*/
static tCIDOUBLE GetTime(void)
/*
**	Return fractional seconds.
*/
{
	tCIDOUBLE ans = 0.0;

#ifdef _POSIX_TIMERS

	struct timespec tp;

	(void)clock_gettime(CLOCK_MONOTONIC_RAW, &tp);
	ans = (tCIDOUBLE) tp.tv_sec;
	ans += ((tCIDOUBLE) tp.tv_nsec) / 1000000000.0;

#else

	struct timeb tb;

	(void)ftime(&tb);
	ans = tb.millitm;
	ans /= 1000.0;
	ans += tb.time;

#endif

	return (ans);
}

/*--------------------------------------------------------------------------*/
static void InitAndGetDataUntilKeyPress(void)
/*
**	Illustrate a simple exampler board interaction sequence.
*/
{
	tCIRC circ;
	char buff[256], *str;
	tCIU32 n, i, j;
	tCIU32 nPtrs;
	tCIU8 **uPtrs = NULL;
	tCIU8 *p8;
	unsigned short *p16;
	tCIU32 *p32;
	tCIU32 frameID, value, value2, value3, pixToShow;
	tCIU8 *frameP;
	tCIU32 nFrames, bitsPerPix, hROIoffset, hROIsize, vROIoffset, vROIsize, stride;
	tCIU32 totalFrames = 0, bytesPerFrame = 0;
	tCIDOUBLE before = 0.0, after = 0.0, bps, fps;
	tCIextraFrameInfo xi;
	tCIU32 ic[kCINinterruptCounters];

	(void)memset(ic, '\0', sizeof(ic));
	/*
	 ** Set library debug function if '-D' was specified.
	 */
	circ = CiSetDebug(sCIp, -1, (0 == sLibDebug) ? NULL : LDBdisplay);
	if (kCIEnoErr != circ)
	{
		ERR(("CiSetDebug gave '%s'\n", CiErrStr(circ)));
	}
	/*
	 ** Open the ndx'th frame grabber with write permission.
	 */
	circ = CiVFGopen(sNdx, kCIBO_writeAccess, &sCIp);
	if (kCIEnoErr != circ)
	{
		ERR(("CiVFGopen gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		return;
	}
	/*
	 ** Get and display the state of the frame grabber.
	 */
	circ = CiVFGqueryState(sCIp, &n);
	if (kCIEnoErr != circ)
	{
		ERR(("CiVFGqueryState gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	str = buff;
	switch (n)
	{
	CASE_STATE_STR(shuttingDown);
	CASE_STATE_STR(startingUp);
	CASE_STATE_STR(needsFirmware);
	CASE_STATE_STR(firmwareLoaded);
	CASE_STATE_STR(dmaBufferReady);
	CASE_STATE_STR(dmaIsActive);
	CASE_STATE_STR(stateUnknown);
	default:
		(void)sprintf(str, "--%d invalid--", n);
	}
	/*
	 ** Initialize the VFG, possibly with a custom config file.
	 */
	circ = CiVFGinitialize(sCIp, sCfgFN);
	if (kCIEnoErr != circ)
	{
		ERR(("CiVFGinitialize gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	/*
	 ** If nFrames specified on command line then reconfigure the buffers.
	 */
	if ((0 != sNframes) && (kCIEnoErr != (circ = CiDrvrBuffConfigure(sCIp, sNframes, 0, 0, 0, 0))))
	{
		ERR(("CiDrvrBuffConfigure gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	/*
	 ** Determine buffer configuration.
	 */
	circ = CiBufferInterrogate(sCIp, &nFrames, &bitsPerPix, &hROIoffset, &hROIsize, &vROIoffset, &vROIsize, &stride);
	if (kCIEnoErr != circ)
	{
		ERR(("CiBufferInterrogate gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	SHOW(("  %d frames, %d bitsPerPix, %d-%d horizontal, %d-%d vertical, %d stride\n", nFrames, bitsPerPix, hROIoffset, hROIsize, vROIoffset, vROIsize, stride));
	bytesPerFrame = stride * vROIsize;
	/*
	 ** Test for condition we know will cause data errors: continuous
	 ** acquisition into a single buffer.
	 */
	if (2 > nFrames)
	{
		SHOW(("WARNING: %d frames inadequate for continuous acquisition!\n", nFrames));
	}
	/*
	 ** Get the buffer pointers for read/write access to buffers.
	 */
	circ = CiMapFrameBuffers(sCIp, 1, &nPtrs, &uPtrs);
	if (kCIEnoErr != circ)
	{
		ERR(("CiMapFrameBuffers gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	/*
	 ** Reset acquisition.
	 */
	circ = CiAqSWreset(sCIp);
	if (kCIEnoErr != circ)
	{
		ERR(("CiAqSWreset gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	/*
	 ** Clear the first line(s) of all buffers.
	 */
	for (i = 0; i < nPtrs; i++)
	{
		(void)memset(uPtrs[i], '\0', stride * sLines);
	}
	/*
	 ** Decide how many per line we show.
	 */
	switch (bitsPerPix)
	{
	case 64:
	case 8:
		pixToShow = 16;
		break;
	case 10:
	case 12:
		pixToShow = 12;
		break;
	case 14:
	case 16:
	case 24:
		pixToShow = 8;
		break;
	case 30:
	case 32:
	case 36:
	case 48:
		pixToShow = 6;
		break;
	default:
		ERR(("Do not know pixBitDepth of %d\n", bitsPerPix));
		sExitAns = 1;
		goto andOut;
	}
	if (0 < sPixShow)
	{
		pixToShow = sPixShow;
	}
	/*
	 ** Tell the user how to stop this cleanly.
	 */
	if (0 == sBGok)
	{
		if (0 == sMaxFrames)
		{
			SHOW(("%d Will dump first line of frames until newline (skip %d, ndx %d, %d nc, %d hfr)\n", ((int)(getpid())), sSkipFrames, sNdx, sNoClear, sHiFrameRate));
		}
		else
		{
			SHOW(("%d Will dump first line of frames until newline or %d (skip %d, ndx %d, %d nc, %d hfr)\n", ((int)(getpid())), sMaxFrames, sSkipFrames, sNdx, sNoClear, sHiFrameRate));
		}
	}
	else
	{
		SHOW(("%d Will dump first line of frames until %d (skip %d, ndx %d, %d nc, %d hfr)\n", ((int)(getpid())), sMaxFrames, sSkipFrames, sNdx, sNoClear, sHiFrameRate));
	}
	/*
	 ** Delay for, e.g., taskset, trigger setup, etc.
	 */
	if (0 < sDelay)
	{
		usleep(sDelay * 1000);
		SHOW(("  after %.3lf sec\n", (((double)(sDelay * 1000)) / (1000000.0))));
	}
	/*
	 ** Start continuous acquisition.
	 */
	if (sHiFrameRate == 0)
	{
		circ = CiAqStart(sCIp, 0);
		if (kCIEnoErr != circ)
		{
			ERR(("CiAqStart gave '%s'\n", CiErrStr(circ)));
			sExitAns = 1;
			goto andOut;
		}
	}
	else
	{
		circ = CiAqStartHiFrameRate(sCIp, 0);
		if (kCIEnoErr != circ)
		{
			ERR(("CiAqStartHiFrameRate gave '%s'\n", CiErrStr(circ)));
			sExitAns = 1;
			goto andOut;
		}
	}
	if (0 != sNoRule)
	{
		/*
		 **   Turn off INT_OVSTEP: must be done _after_ CiAqStart()
		 */
		SHOW((" using DMA_NO_RULE logic\n"));
		circ = CiFieldPoke(sCIp, CiFieldNdxFromStr(sCIp, (char *)"ENINT_OVSTEP"), 0);
		if (kCIEnoErr != circ)
		{
			ERR(("CiFieldPoke (ENINT_OVSTEP) gave %s\n", CiErrStr(circ)));
			sExitAns = 1;
			goto andOut;
		}
		/*
		 **   Set DMA_NO_RULE: WARNING! register modification is persistent!
		 */
		circ = CiFieldPoke(sCIp, CiFieldNdxFromStr(sCIp, (char *)"DMA_NO_RULE"), 1);
		if (kCIEnoErr != circ)
		{
			ERR(("CiFieldPoke (DMA_NO_RULE) gave %s\n", CiErrStr(circ)));
			sExitAns = 1;
			goto andOut;
		}
	}
	/*
	 ** Display each acquired frame 1st line in a loop.  Stop at newline.
	 */
	before = GetTime();			/* mark acquisition start */
	do
	{
		/*
		 ** Check to see if a frame is already available before waiting.
		 */
checkAgain:
		circ = CiGetOldestNotDeliveredFrame(sCIp, &frameID, &frameP);
		switch (circ)
		{
		case kCIEnoErr:
			/*
			 **   We have the frame.
			 */
			break;
		case kCIEnoNewData:
			/*
			 **   We need to wait for another frame.
			 */
			circ = CiWaitNextUndeliveredFrame(sCIp, sTimeout);
			if (kCIEnoErr != circ)
			{
				switch (circ)
				{
				case kCIEaqAbortedErr:
					SHOW(("CiWaitNextUndeliveredFrame gave '%s' at %d\n", CiErrStr(circ), totalFrames));
					break;
				default:
					ERR(("CiWaitNextUndeliveredFrame gave '%s'\n", CiErrStr(circ)));
					sExitAns = 1;
				}
				goto andOut;
			}
			goto checkAgain;
		case kCIEaqAbortedErr:
			SHOW(("CiGetOldestNotDeliveredFrame: aquisition was aborted\n"));
			goto andOut;
		default:
			ERR(("CiGetOldestNotDeliveredFrame gave '%s'\n", CiErrStr(circ)));
			sExitAns = 1;
			goto andOut;
		}
		totalFrames += 1;		/* another frame */
		/*
		 **   If DMA_NORULE then do not attempt to display each frame; every
		 **   100th should do fine.
		 */
		if ((0 != sNoRule) && (0 != (totalFrames % 100)))
		{
			goto noDisplay;
		}
		/*
		 **   And allow skipping on other conditions.
		 */
		if ((0 != sSkipFrames) && (0 != (totalFrames % (sSkipFrames + 1))))
		{
			goto noDisplay;
		}
		/*
		 **   If not DMA_NORULE then get the timestamp.
		 */
		xi.frameID = frameID;
		if (0 != sNoRule)
		{
			xi.timestamp = 0;
		}
		else if (kCIEnoErr != (circ = CiGetExtraFrameInfo(sCIp, sizeof(xi), &xi)))
		{
			ERR(("CiGetExtraFrameInfo gave '%s'\n", CiErrStr(circ)));
			sExitAns = 1;
			goto andOut;
		}
		/*
		 **   Display the frameID and the first line of frame data.
		 */
		for (j = 0; j < sLines; j++)
		{
			SHOW(("frameID %6d(%1d)(%Ld):", frameID, j, (tCIU64) xi.timestamp));
			switch (bitsPerPix)
			{
			case 64:
			case 8:
				p8 = frameP;
				for (i = 0; i < pixToShow; i++)
				{
					value = *p8++;
					SHOW((" %02X", value));
				}
				break;
			case 10:
			case 12:
				p16 = (tCIU16 *) frameP;
				for (i = 0; i < pixToShow; i++)
				{
					value = *p16++;
					SHOW((" %03X", value));
				}
				break;
			case 14:
			case 16:
				p16 = (tCIU16 *) frameP;
				for (i = 0; i < pixToShow; i++)
				{
					value = *p16++;
					SHOW((" %04X", value));
				}
				break;
			case 24:
				p8 = (tCIU8 *) frameP;
				for (i = 0; i < pixToShow; i++)
				{
					value = *p8++;
					value2 = *p8++;
					value3 = *p8++;
					SHOW((" %02X.%02X.%02X", value, value2, value3));
				}
				break;
			case 30:
				p32 = (tCIU32 *) frameP;
				for (i = 0; i < pixToShow; i++)
				{
					value = *p32++;
					value2 = (value >> 10) & 0x3FF;
					value3 = (value >> 20) & 0x3FF;
					value &= 0x03FF;
					SHOW((" %03X.%03X.%03X", value, value2, value3));
				}
				break;
			case 32:
				p32 = (tCIU32 *) frameP;
				for (i = 0; i < pixToShow; i++)
				{
					value = *p32++;
					SHOW((" %08X", value));
				}
				break;
			case 36:
				p16 = (tCIU16 *) frameP;
				for (i = 0; i < pixToShow; i++)
				{
					value = *p16++;
					value2 = *p16++;
					value3 = *p16++;
					SHOW((" %03X.%03X.%03X", value, value2, value3));
				}
				break;
			case 48:
				p16 = (tCIU16 *) frameP;
				for (i = 0; i < pixToShow; i++)
				{
					value = *p16++;
					value2 = *p16++;
					value3 = *p16++;
					SHOW((" %04X.%04X.%04X", value, value2, value3));
				}
				break;
			default:
				ERR(("Do not know pixBitDepth of %d\n", bitsPerPix));
				sExitAns = 1;
				goto andOut;
			}
			SHOW(("\n"));
			/*
			 **   We need to be able to cooperate w/CIcmdln showUnchanged
			 */
			if (0 == sNoClear)
			{
				/*
				 ** Clear this line so we know when it is rewritten.
				 */
				(void)memset(frameP, '\0', stride);
			}
			/*
			 **   End of line loop: advance frame pointer.
			 */
			frameP += stride;
		}
noDisplay:
		/*
		 **   Break out of loop on newline
		 */
		if (0 == sBGok)
		{
			if (0 != CheckForKeyboardInput())
			{
				break;
			}
		}
		/*
		 **   Break out of loop if countdown hits zero
		 */
		if ((0 != sMaxFrames) && (--sMaxFrames == 0))
		{
			break;
		}
	}
	while ((sSecondsOA == 0) ||
		   /*
		    ** Break out if nothing happens.
		    */
		   (GetTime() < (before + ((tCIDOUBLE) (sSecondsOA)))));

#ifdef	STOP_AQ
#warning "STOP_AQ is set"
	/*
	 ** Abort acquisition.
	 */
	circ = CiAqAbort(sCIp);
	if (kCIEnoErr != circ)
	{
		ERR(("CiAqStop gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
#endif
	/*
	 ** Here is a good place to see how many frames are still waiting...
	 */
	circ = CiGetIntCounts(sCIp, ic);
	if (kCIEnoErr != circ)
	{
		ERR(("CiGetIntCounts of %d gave '%s'\n", i, CiErrStr(circ)));
		goto andOut;
	}

andOut:
	after = GetTime();			/* mark acquisition end */

#ifdef	STOP_AQ
#warning "STOP_AQ is set"
	/*
	 ** Reset acquisition.
	 */
	circ = CiAqSWreset(sCIp);
	if (kCIEnoErr != circ)
	{
		ERR(("CiAqSWreset gave '%s'\n", CiErrStr(circ)));
	}
#endif
	/*
	 ** Unmap the frame buffers.
	 */
	if ((NULL != uPtrs) && (kCIEnoErr != (circ = CiUnmapFrameBuffers(sCIp))))
	{
		ERR(("CiUnmapFrameBuffers gave '%s'\n", CiErrStr(circ)));
	}
	/*
	 ** Avoid hassles.
	 */
	if (0 != sNoRule)
	{
		circ = CiFieldPoke(sCIp, CiFieldNdxFromStr(sCIp, (char *)"DMA_NO_RULE"), 0);
		if (kCIEnoErr != circ)
		{
			ERR(("CiFieldPoke (DMA_NO_RULE) gave %s\n", CiErrStr(circ)));
			sExitAns = 1;
			goto andOut2;
		}
		SHOW(("  DMA_NO_RULE reset to 0\n"));
	}
andOut2:
	/*
	 ** Display data rate, ususally determined by the EOF count, but
	 ** determined by the QUAD count when DMA_NO_RULE is set.
	 */
	bps = bytesPerFrame;
	bps *= totalFrames;

	if (after == before)
	{
		bps = 0.0;
		fps = 0.0;
	}
	else
	{
		bps = bps / (after - before);
		fps = totalFrames;
		fps /= after - before;
	}

	if ((sSecondsOA > 0.0) && (GetTime() >= (before + ((tCIDOUBLE) (sSecondsOA)))))
	{
		str = (char *)"timeout";
	}
	else
	{
		str = (char *)"";
	}

	SHOW(("%d had %d(%d) frames of %d bytes, %.3f bytes/sec (%.1lf FPS(%s))\n", sNdx, totalFrames, ((ic[kCIintCountEOF] == 0) ? (0) : (ic[kCIintCountEOF] - totalFrames)), bytesPerFrame, (tCIDOUBLE) bps, fps, str));
	/*
	 ** Close the access.
	 */
	if ((NULL != sCIp) && (kCIEnoErr != (circ = CiVFGclose(sCIp))))
	{
		ERR(("CiVFGclose gave '%s'\n", CiErrStr(circ)));
	}

	return;
}

/*==========================================================================*/
int main(int argc, char **argv)
/*
**	Decode command line and (usually) acquire/display frames until EOF
*/
{
	sArgv0 = *argv;

	if (kCIEnoErr == DecodeArgs(argc, argv))
	{
		if (0 == sMaxFrames)
		{
			SHOW(("main: will init and get data until newline\n"));
		}
		else
		{
			SHOW(("main: will init and get data for %d frames\n", sMaxFrames));
		}
		InitAndGetDataUntilKeyPress();
	}

	return (sExitAns);
}

/*==========================================================================*/
/*
	$Log: CIexample.c,v $
	Revision 1.42  2020/10/02 23:30:12  steve
	CLOCK_MONOTONIC is not always monotonic, so prefer CLOCK_MONOTONIC_RAW.

	Revision 1.41  2020/10/02 01:17:22  steve
	ftime is deprecated. Use clock_gettime.

	Revision 1.40  2020/09/02 23:53:05  steve
	Deprecate Karbon CXP, add Claxon.

	Revision 1.39  2019/04/29 21:34:43  steve
	Improvements to CiAqStartHiFrameRate integration.

	Revision 1.38  2019/04/27 00:00:19  steve
	Added -H flag to enable High Frame Rate Polling

	Revision 1.37  2017/12/12 11:05:51  steve
	Fixes for axn+vivid

	Revision 1.36  2017/12/04 21:33:36  steve
	Much gn2 isr work; diag work; base options

	Revision 1.35  2017/09/29 09:14:00  steve
	Now dump all camf

	Revision 1.34  2016/07/11 20:02:56  steve
	Gn2 xx-2Y/2YE, GPUD, DGMA

	Revision 1.33  2016/03/06 23:16:08  steve
	Support CXP_usualInit and support Axion

	Revision 1.32  2015/06/23 17:11:03  steve
	Ready for ndif and cxp2

	Revision 1.31  2015-01-26 13:06:18  steve
	Final 9.02

	Revision 1.30  2015-01-24 19:35:52  steve
	Almost done

	Revision 1.29  2015-01-15 17:24:40  steve
	Cyton available

	Revision 1.28  2014-06-09 10:49:26  steve
	Address release test errs

	Revision 1.27  2014-05-29 13:14:14  steve
	User lib c++ supporting Neon/Alta/Kbn/KbnCXP

	Revision 1.26  2014-03-18 02:22:14  steve
	Fix err msg

	Revision 1.25  2014-03-15 20:57:28  steve
	Reorg for c++, SDK 9.00

	Revision 1.24  2013-03-03 18:58:11  steve
	Ready w/CXP

	Revision 1.23  2012-11-20 00:43:13  steve
	Fixed CCx_CON issue w/ISR

	Revision 1.22  2012-09-14 14:25:56  steve
	CiSetDebugCL(), CON51, fix sscanf(), cust kern dir

	Revision 1.21  2012-06-01 00:14:19  steve
	Final on 8.10

	Revision 1.20  2012-04-27 21:15:32  steve
	Fix CONFIG_X86_NOPAT, doc iommu, add CIsimpleSG

	Revision 1.19  2012-02-10 02:41:14  steve
	Fix more details

	Revision 1.18  2012-02-10 01:02:29  steve
	Fix typo

	Revision 1.17  2012-02-10 00:52:53  steve
	Fix some tests

	Revision 1.16  2011-05-17 21:55:57  steve
	Fix details.

	Revision 1.15  2011-05-16 21:44:18  steve
	Demote aq abort from err.

	Revision 1.14  2011-05-16 21:11:15  steve
	Faster serial out.  Abort wakes all sleepers.

	Revision 1.13  2011-03-27 16:01:37  steve
	Minimize SIP restart time

	Revision 1.12  2011-02-18 17:07:02  steve
	Fixed CB/SIP, DPM test, added slow test cam

	Revision 1.11  2011-02-14 04:10:05  steve
	Karbon, SIP, DMA fixes

	Revision 1.10  2011-01-05 19:00:04  steve
	Ignore buffID err on norule.  Also tweak DMAboundary test

	Revision 1.9  2011-01-03 19:30:25  steve
	SIP lines, TSC, bugfix CCn, cb nolog err

	Revision 1.8  2010-06-03 14:34:29  steve
	Added DAM/SG diagnostics to CIcmdln.

	Revision 1.7  2010-02-24 21:21:09  steve
	v706 candidate w/SIP

	Revision 1.6  2009-06-18 13:37:54  steve
	Full ROI, s-g DMA, flush CL, Kbn flash delay

	Revision 1.5  2009-01-24 17:54:00  steve
	CIexample has DMA_NO_RULE benchmark

	Revision 1.4  2009-01-22 11:38:53  steve
	User-alloc DMA; full ROI; register fields

	Revision 1.3  2008-10-16 02:50:32  steve
	Beta2: now have .so library.

	Revision 1.2  2008-10-08 18:13:49  steve
	Release test ready.

	Revision 1.1  2008-09-29 06:41:14  steve
	CIexample now exists.  Version pumped.

*/