
/** @file sample01.c
*
* This (check / test) sample is using fix parameters and initialize a cyclic acquisition on 4 channels with 12,5 kHz and a compare value of 32000.
* Contrary to the sample 13, here you will see how to get the number of available values to read and check if the data package is split.
* (see between lines 253 - 295)
*/

/** @par LICENCE
* @verbatim
 Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code and the documentation.

        ADDI-DATA GmbH
        Dieselstrasse 3
        D-77833 Ottersweier
        Tel: +19(0)7223/9493-0
        Fax: +49(0)7223/9493-92
        http://www.addi-data-com
        info@addi-data.com

    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 shoud find the complete GPL in the COPYING file accompanying
    this source code.
* @endverbatim
*/


#include <stdlib.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


#include <apci3600.h>
#include <apci3600-events.h>

#define NBR_CHANNELS	4

volatile int stop = 0;

void signal_handler(int signum, siginfo_t *info, void *context)
{
	if (signum == SIGINT)
		stop = 1;
}


int main(int argc, char *argv[])
{
	apci3600_event_t evt;
	char buf[256] = {0};
	int i = 0;
	int ret = 0;
	int boardFds[5] = {0};
	unsigned long count = 0;
	unsigned long countChannel[2][4] = {{0},{0}};
	long nbrBytes = 0;
	struct sigaction act;
	uint32_t *data = NULL;
	uint8_t *data8 = NULL;

	str_InitAnalogInputModuleCyclicAcquisitionArg_t acquisitionInitArg;
	str_InitBoardArg_t config;
	uint8_t calibrationStatus;
	str_InitAnalogInputModuleArg_t configInit;
	InitDigitalInputExternalTrigger_t InputExternalTrigger;

	InputExternalTrigger.b_TriggerLevel = 1;
	InputExternalTrigger.w_TriggerCountValue = 1;

	configInit.pb_SingleDiffMode[0] = 0;
	configInit.pb_SingleDiffMode[1] = 0;
	configInit.pb_Coupling[0] = 1;
	configInit.pb_Coupling[1] = 1;
	configInit.pb_EnableCurrentSource[0] = 1;
	configInit.pb_EnableCurrentSource[1] = 1;
	configInit.pb_GainSelection[0] = 0;
	configInit.pb_GainSelection[1] = 0;
	configInit.b_SamplingClockSelection = 0;

	configInit.b_SpeedMode = 0;
	config.b_ClockGen1_Divisor = 16;
	config.b_ClockGen2_Divisor = 4;

	config.b_MasterSlaveMode = 0;
	config.b_SamplingClock1_Selection = 0;
	config.b_SamplingClock2_Selection = 0;
	config.b_SlaveSamplingClock1_Selection = 0;
	config.b_SlaveSamplingClock2_Selection = 0;
	config.b_Chronometer_MasterClock_Divisor = 2;

	/* Initialize the acquisition */
	acquisitionInitArg.dw_OnBoardBufferSize = 15; /* 32 MB */
	acquisitionInitArg.dw_CompareValue = 32000;
	acquisitionInitArg.dw_Mode = 1;              /* Ring buffer mode for continuous acquisition */

	/* Allocate memory to get the data + the data header */
	data = (uint32_t *) malloc (((acquisitionInitArg.dw_CompareValue + 1) * sizeof (uint32_t)) * 2);
	if (data == NULL)
	{
		perror ("malloc: ");
		return 0;
	}

	/* Will be used to get data */
	data8 = (uint8_t *)data;

	/* Setup a signal handler to catch SIGINT */
	memset(&act, 0, sizeof(act));
	act.sa_sigaction = &signal_handler;
	if (sigaction(SIGINT, &act, NULL) < 0)
		perror("sigaction");

	/* Open the main node */
	boardFds[0] = open("/dev/apci3600/0", O_RDWR);
	if (boardFds[0] < 0)
	{
		printf("open board %d failed", 0);
		goto end0;
	}

	/* Open channel nodes */
	for (i = 0; i < NBR_CHANNELS; i++)
	{
		/* Set the device node name to open */
		sprintf (buf, "/dev/apci3600/%i", (APCI3600_DEV_MINOR_ANALOG_INPUT_0 + i));
		boardFds[(1+i)] = open(buf, O_RDWR);
		if (boardFds[(1+i)] < 0)
		{
			printf("open analog %i failed\n", i);
			goto end0;
		}
	}

	/* Initialize the board */
	ret = ioctl(boardFds[0], CMD_APCI3600_InitBoard, &config);
	if (ret != 0)
	{
		printf("CMD_APCI3600_InitBoard failed with error %d!\n", ret);
		goto end0;
	}

	/* Initialize the analog inputs */
	for (i = 0; i < NBR_CHANNELS; i++)
	{
		ret = ioctl(boardFds[(1+i)], CMD_APCI3600_InitAnalogInputModule, &configInit);
		if (ret != 0)
		{
			printf("CMD_APCI3600_InitAnalogInputModule %d failed with error %d! \n", i, ret);
			goto end1;
		}

		/* Wait until the analog input calibration ends */
		do
		{
			ret = ioctl(boardFds[(1+i)], CMD_APCI3600_GetAnalogInputModuleCalibrationStatus, &calibrationStatus);
			if (ret != 0)
			{
				printf("CMD_APCI3600_GetAnalogInputModuleCalibrationStatus %d failed with error %d!\n", i, ret);
				goto end1;
			}
		} while (calibrationStatus == 1);

		ret = ioctl(boardFds[(1+i)], CMD_APCI3600_InitAnalogInputModuleCyclicAcquisition, &acquisitionInitArg);
		if (ret != 0)
		{
			printf("CMD_APCI3600_InitAnalogInputModuleCyclicAcquisition %d failed with error %d!\n", i, ret);
			goto end1;
		}
	}

	/* Empty the acquisition buffer */
	ret = ioctl(boardFds[0], CMD_APCI3600_ResetAcquisitionBuffer, NULL);
	if (ret != 0)
	{
		printf("CMD_APCI3600_ResetAcquisitionBuffer failed with error %d!\n", ret);
		goto end1;
	}

	/* Empty the event buffer */
	ret = ioctl(boardFds[0], CMD_APCI3600_EventEmpty, NULL);
	if (ret != 0)
	{
		printf("CMD_APCI3600_EventEmpty failed with error %d!\n", ret);
		goto end1;
	}

	/* Start the all channels one by one */
	for (i = 0; i < NBR_CHANNELS; i++)
	{
		ret = ioctl(boardFds[(1+i)], CMD_APCI3600_StartAnalogInputModuleCyclicAcquisition, NULL);
		if (ret != 0)
		{
			printf("CMD_APCI3600_StartAnalogInputModuleCyclicAcquisition %d failed with error %d!\n", i, ret);
			goto end1;
		}
	}

	do
	{
		/* Wait for events */
		ret = ioctl(boardFds[0], CMD_APCI3600_EventWait, &evt);
		if (ret != 0)
		{
			printf("CMD_APCI3600_EventWait failed with error %d!\n", ret);
			goto end1;
		}

		if (evt.mask)
			printf ("Event -> Source: 0x%08X \t Mask: 0x%08X \t Buffer number: 0x%08X \t Bytes to read %u \t count %lu\n", evt.source, evt.mask, evt.buffnumber, evt.numberbytes, ++count);

		/* If error */
		if (evt.mask > 1)
		{
			stop = 1;
			break;
		}

		/* If one of the analog input generates a DMA interrupt */
		if ((evt.source & 0x3C) && (evt.mask & 0x1))
		{
			if (evt.source & 0x4)
				++countChannel[0][0];

			if (evt.source & 0x8)
				++countChannel[0][1];

			if (evt.source & 0x10)
				++countChannel[0][2];

			if (evt.source & 0x20)
				++countChannel[0][3];

			printf ("Channels (event) count \t %lu \t %lu \t %lu \t %lu\n", countChannel[0][0], countChannel[0][1], countChannel[0][2], countChannel[0][3]);

			/* Read data */
			nbrBytes = 0;

			/* Loop on read to be sure to get all the asked values. */
			do
			{
				if ((ret = read(boardFds[0], &(data8[nbrBytes]), (evt.numberbytes - nbrBytes))) < 0)
				{
					perror ("read: ");
					stop = 1;
				}

				/* Count the number of read bytes */
				nbrBytes += ret;

			} while ((nbrBytes < evt.numberbytes) && (!stop));

			++countChannel[1][(data[0] >> 2) & 0x3];

			printf ("Channels (dma) count \t %lu \t %lu \t %lu \t %lu\n", countChannel[1][0], countChannel[1][1], countChannel[1][2], countChannel[1][3]);

			/*
			 * In the data index > 0 are the channels values.
			 * In the channel value following information are
			 * encoded:
			 *
			 * bits 0 - 23: The value
			 * bit 24: Overflow
			 * bit 25 - 31: Channel index
			 * 				For source 0: channel 0 or 1
			 * 				For source 1: channel 2 or 3
			 * 				For source 2: channel 4 or 5
			 * 				For source 3: channel 6 or 7
			 */

			/* data are provided in form of packages preceded by a 4-bytes header describing the content
			 * bits 31-4 Number of values in 4 bytes units
			 * bit 3-2: Index of the source ( 0 .. 3 )
			 * bit 0: Source (0: ADC ,  1: Chronometer)
			 */
			{
				int h = 0;
				int index = 0;
				int nbrValues = 0;

				/* evt.buffnumber define the number of packages that have been read.
				 * if evt.buffnumber = 0 then 1 package has been read
				 * if evt.buffnumber = 1 then 2 packages have been read
				 * ...
				 */
				while ((h < evt.buffnumber) && (!stop))
				{
					/* Get the number of values (in DWORD) in the package */
					nbrValues = (data[index] >> 4);

					printf ("Read %li bytes, packages %lu bytes\n", nbrBytes, (nbrValues * 4));
					printf ("Header %i\n", h++);
					printf ("\t Source: %i\n", data[index] & 0x1);
					printf ("\t Index of the source: %i\n", (data[index] >> 2) & 0x3);
					printf ("\t Number of values: %i\n", nbrValues);

					/* Compute the index of the next header (in DWORD) */
					index = nbrValues + 1;
				}
			}
		}

	} while(!stop);

end1:

	/* Stop and release all analog inputs. */
	for (i = 0; i < NBR_CHANNELS; i++)
	{
		ret = ioctl(boardFds[(1+i)], CMD_APCI3600_StopAnalogInputModuleCyclicAcquisition, NULL);
		if (ret != 0)
			printf("CMD_APCI3600_StopAnalogInputModuleCyclicAcquisition %d failed with error %d! \n", i, ret);

		ret = ioctl(boardFds[(1+i)], CMD_APCI3600_ReleaseAnalogInputModuleCyclicAcquisition, NULL);
		if (ret != 0)
			printf("CMD_APCI3600_ReleaseAnalogInputModuleCyclicAcquisition %d failed with error %d!\n", i, ret);

		ret = ioctl(boardFds[(1+i)], CMD_APCI3600_ReleaseAnalogInputModule, NULL);
		if (ret != 0)
			printf("CMD_APCI3600_ReleaseAnalogInputModule %d failed with error %d!\n", i, ret);
	}

end0:
	if (data)
		free (data);

	return 0;
}
