#include "apci3600-private.h"

EXPORT_SYMBOL(i_PCI3600_InitChronometerModule);
EXPORT_SYMBOL(i_PCI3600_ReleaseChronometerModule);
EXPORT_SYMBOL(i_PCI3600_StartChronometerModuleAcquisition);
EXPORT_SYMBOL(i_PCI3600_GetChronometerModuleFIFOStatus);
EXPORT_SYMBOL(i_PCI3600_ReadChronometerModuleValue);
EXPORT_SYMBOL(i_PCI3600_StopChronometerModuleAcquisition);
EXPORT_SYMBOL(i_PCI3600_InitChronometerModuleCyclicAcquisition);
EXPORT_SYMBOL(i_PCI3600_StartChronometerModuleCyclicAcquisition);
EXPORT_SYMBOL(i_PCI3600_GetChronometerModuleCyclicAcquisitionStatus);
EXPORT_SYMBOL(i_PCI3600_IncrChronoCyclicAcquisitionBufferReadPointer);
EXPORT_SYMBOL(i_PCI3600_StopChronometerModuleCyclicAcquisition );
EXPORT_SYMBOL(i_PCI3600_ReleaseChronometerModuleCyclicAcquisition);
EXPORT_SYMBOL(i_PCI3600_Chronometer_EnableDisableDigitalInputExternalTrigger);
EXPORT_SYMBOL(i_PCI3600_Chronometer_EnableDisableMasterTrigger);

int __needlock i_PCI3600_InitChronometerModule (struct pci_dev *pdev, uint8_t module, uint8_t clockDivisor)
{
	str_ChronometerInfos *str;
	uint32_t registerContent = 0;

	if (!pdev)
		return -1;

	if (module >= APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.b_ChronometerNumber)
		return -2;

	if (clockDivisor > 15)
		return -3;

	str = &APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.s_ChronometerInfos[module];
	if ((str->b_ChronometerInitStatus != 0) ||
			((str->b_ChronometerInitStatus == 1) && (str->b_ChronometerInitProcessIndex != 0)))
		return -4;

	/* Clear the Chronometer FIFO */
	INPDW_MEM(GET_MEM_BAR3(pdev) + PCI3600_CHRONOMETER_OFFSET, 0, &registerContent);
	OUTPDW_MEM(GET_MEM_BAR3(pdev) + PCI3600_CHRONOMETER_OFFSET, 0, registerContent | (16 << (module * 8)));

	INPDW_MEM(GET_MEM_BAR3(pdev) + PCI3600_CHRONOMETER_OFFSET, 0, &registerContent);

	/* Clear the configuration of the module */
	registerContent &= (0xFFFFFFFF - (0xFF << (module * 8)));
	registerContent |= (uint32_t)(clockDivisor << (module * 8));

	OUTPDW_MEM(GET_MEM_BAR3(pdev) + PCI3600_CHRONOMETER_OFFSET, 0, registerContent);

	str->b_ChronometerInitStatus = 1;
	str->b_ChronometerInitProcessIndex = 0;

	return 0;
}

int __needlock i_PCI3600_ReleaseChronometerModule(struct pci_dev *pdev, uint8_t module)
{
	str_ChronometerInfos *str;
	if (!pdev)
		return -1;

	str = &APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.s_ChronometerInfos[module];
	if (module >= APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.b_ChronometerNumber)
		return -2;

	if ((str->b_ChronometerInitStatus != 1) || (str->b_ChronometerInitProcessIndex != 0))
		return -3;

	str->b_ChronometerInitStatus = 0;

	return 0;
}

int __needlock i_PCI3600_StartChronometerModuleAcquisition(struct pci_dev *pdev, uint8_t module)
{
	str_ChronometerInfos *str;
	uint32_t             registerContent = 0;

	if (!pdev)
		return -1;

	if (module >= APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.b_ChronometerNumber)
		return -2;

	str = &APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.s_ChronometerInfos[module];
	if ((str->b_ChronometerInitStatus != 1) || (str->b_ChronometerInitProcessIndex != 0))
		return -3;

	INPDW_MEM(GET_MEM_BAR3(pdev) + PCI3600_CLOCK_BUS_GENERAL_OFFSET, 4, &registerContent);
	if ((uint8_t)((registerContent >> (module + 4)) & 1) != 0)
		return -4;

	/* Clear the chronometer FIFO */
	INPDW_MEM(GET_MEM_BAR3(pdev) + PCI3600_CHRONOMETER_OFFSET, 0, &registerContent);
	OUTPDW_MEM(GET_MEM_BAR3(pdev) + PCI3600_CHRONOMETER_OFFSET, 0, registerContent | (16 << (module * 8)));

	/* Set the start bit */
	INPDW_MEM(GET_MEM_BAR3(pdev) + PCI3600_CLOCK_BUS_GENERAL_OFFSET, 4, &registerContent);
	OUTPDW_MEM(GET_MEM_BAR3(pdev) + PCI3600_CLOCK_BUS_GENERAL_OFFSET, 4, registerContent | (1 << (module + 4)));
	return 0;
}

int __needlock i_PCI3600_GetChronometerModuleFIFOStatus(struct pci_dev *pdev, uint8_t module, uint8_t* fifoStatus)
{
	str_ChronometerInfos *str;
	uint32_t             registerContent = 0;
	if (!pdev)
		return -1;

	if (module >= APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.b_ChronometerNumber)
		return -2;

	str = &APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.s_ChronometerInfos[module];
	if (((str->b_ChronometerInitStatus != 1) || (str->b_ChronometerInitProcessIndex != 0)))
		return -3;

	/* Get the Chronometer FIFO status */
	INPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_CHRONOMETER_OFFSET, 20, &registerContent);
	*fifoStatus = (uint8_t)((registerContent >> (module * 8)) & 3);

	return 0;
}

int __needlock i_PCI3600_ReadChronometerModuleValue(struct pci_dev *pdev, uint8_t module, uint32_t* values)
{
	str_ChronometerInfos *str;
	uint32_t registerContent = 0;

	if (!pdev)
		return -1;

	if (module >= APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.b_ChronometerNumber)
		return -1;

	str = &APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.s_ChronometerInfos[module];
	if ((str->b_ChronometerInitStatus != 1) || (str->b_ChronometerInitProcessIndex != 0))
		return -3;

	INPDW_MEM(GET_MEM_BAR3(pdev) + PCI3600_CHRONOMETER_OFFSET, 20, &registerContent);

	if ((uint8_t)((registerContent >> (module * 8)) & 3) == 0)
		return -4;

	/* Read the values from the FIFO */
	INPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_CHRONOMETER_OFFSET, 4 + (module * 4), &registerContent);
	values[0] = registerContent;

	return 0;
}

int __needlock i_PCI3600_StopChronometerModuleAcquisition(struct pci_dev *pdev, uint8_t module)
{
	str_ChronometerInfos *str;
	uint32_t registerContent = 0;

	if (!pdev)
		return -1;

	if (module >= APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.b_ChronometerNumber)
		return -2;

	str = &APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.s_ChronometerInfos[module];
	if ((str->b_ChronometerInitStatus != 1) || (str->b_ChronometerInitProcessIndex != 0))
		return -3;

	/* Reset the start bit */
	INPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_CLOCK_BUS_GENERAL_OFFSET, 4, &registerContent);
	OUTPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_CLOCK_BUS_GENERAL_OFFSET, 4, registerContent & (0xFFFFFFFFUL - (1 << (module + 4))));

	return 0;
}

int __needlock i_PCI3600_InitChronometerModuleCyclicAcquisition(struct pci_dev *pdev,
                                                                uint8_t        module,
                                                                uint8_t        mode,
                                                                uint32_t       onBoardBufferSize,
                                                                uint32_t       compareValue)
{
	str_ChronometerInfos *str;
	uint32_t onBoardBufferRealSize = 0;
	uint32_t registerContent    = 0;
	uint32_t realRunMode = (mode == 0) ? 1 : 0;

	if (!pdev)
		return -1;

	onBoardBufferRealSize = (1024 * ((uint32_t)1 << onBoardBufferSize));

	if (module >= APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.b_ChronometerNumber)
		return -2;

	str = &APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.s_ChronometerInfos[module];

	if (mode > 1)
		return -3;

	if (str->b_ChronometerInitStatus != 1)
		return -4;

	/* Test if a computer buffer is available */
	if (APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.s_AnaInpChronomPCDMABufferInfos.dw_BufferSize == 0)
		return -5;

	/* Test the compare value */
	if ((((compareValue > (onBoardBufferRealSize / 4)) && (mode == PCI3600_AUTO_STOP_MODE)) ||
		 ((compareValue >= (onBoardBufferRealSize / 4)) && (mode == PCI3600_RING_BUFFER_MODE))) ||
		(compareValue == 0))
		return -6;

	/* Test if the cyclic acquisition is not already initialised by another process */
	if ((str->b_ChronometerCyclicAcquisitionInitStatus != 0) ||
		((str->b_ChronometerCyclicAcquisitionInitStatus == 1) && (str->b_ChronometerCyclicAcquisitionInitProcessIndex != 0)))
		return -7;

	/* Test if the cyclic acquisition is not running */
	if (str->b_ChronometerCyclicAcquisitionStartStatus != 0)
		return -8;

	/* Test if there is enough place in the on board RAM */
	if (onBoardBufferRealSize > APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.s_HardwareInformations.dw_OnBoardRAMSize)
		return -9;

	/* Reserve the on board RAM buffer */
	if (i_PCI3600_Intern_ReserveOnBoardRAMBuffer (&(APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation), (uint8_t)(module + 6), onBoardBufferRealSize) != 0)
		return -9;

	/* Read the value of the register */
	INPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_CHRONOMETER_OFFSET, 0, &registerContent);

	/* Clear the Chronometer FIFO */
	registerContent = (registerContent | (0x10 << (module * 8)));

	/* Write the on board buffer address */
	OUTPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_ON_BOARD_LOCAL_DMA_CONTROLLER_OFFSET,28 + (module * 4), APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.ps_OnBoardRAMReservedBuffer[module + 6].dw_Address - PCI3600_ON_BOARD_RAM_BASE_ADDRESS);

	/* Write the on board buffer size */
	OUTPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_ON_BOARD_LOCAL_DMA_CONTROLLER_OFFSET,68 + (module * 4), onBoardBufferRealSize);

	/* Clear the write pointer */
	OUTPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_ON_BOARD_LOCAL_DMA_CONTROLLER_OFFSET,108 + (module * 4), 0);

	/* Clear the read pointer */
	OUTPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_ON_BOARD_LOCAL_DMA_CONTROLLER_OFFSET,148 + (module * 4), 0);

	/* Write the Compare value */
	OUTPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_ON_BOARD_LOCAL_DMA_CONTROLLER_OFFSET,188 + (module * 4), compareValue * 4);

	/* Initialise the cyclic mode */
	INPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_ON_BOARD_LOCAL_DMA_CONTROLLER_OFFSET, 0, &registerContent);

	/* Clear the configuration of the module */
	registerContent &= (0xFFFFFFFFUL - (1 << (24 + module)) - (1 << (14 + module)) - (1 << (4 + module)));

	/* Set the mode and enable the interrupt */
	registerContent |= (uint32_t)(1 << (14 + module)) | (uint32_t)(realRunMode << (24 + module));

	OUTPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_ON_BOARD_LOCAL_DMA_CONTROLLER_OFFSET, 0, registerContent);

	str->b_ChronometerCyclicAcquisitionInitStatus       = 1;
	str->b_ChronometerCyclicAcquisitionInitProcessIndex = 0;
	str->b_ChronometerCyclicAcquisitionRunMode          = mode;

	return 0;
}

int __needlock i_PCI3600_StartChronometerModuleCyclicAcquisition(struct pci_dev *pdev, uint8_t module)
{
	str_ChronometerInfos *str;
	uint32_t registerContent = 0;

	if (!pdev)
		return -1;

	if (module >= APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.b_ChronometerNumber)
		return -2;

	str = &APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.s_ChronometerInfos[module];
	if ((str->b_ChronometerCyclicAcquisitionInitStatus != 1) || (str->b_ChronometerCyclicAcquisitionInitProcessIndex != 0))
		return -3;

	if (str->b_ChronometerCyclicAcquisitionStartStatus == 1)
		return -4;

	/* Enable the Local DMA */
	INPDW_MEM(GET_MEM_BAR3(pdev) + PCI3600_ON_BOARD_LOCAL_DMA_CONTROLLER_OFFSET, 0, &registerContent);
	OUTPDW_MEM(GET_MEM_BAR3(pdev) + PCI3600_ON_BOARD_LOCAL_DMA_CONTROLLER_OFFSET, 0, registerContent | (1 << (module + 4)));

	/* Set the general start of the ADC */
	INPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_CLOCK_BUS_GENERAL_OFFSET, 4, &registerContent);
	OUTPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_CLOCK_BUS_GENERAL_OFFSET, 4, registerContent | (1 << (module + 4)));

	str->b_ChronometerCyclicAcquisitionStartStatus = 1;
	str->b_ChronometerCyclicAcquisitionStartProcessIndex = 0;

	return 0;
}

int __needlock i_PCI3600_GetChronometerModuleCyclicAcquisitionStatus(struct pci_dev *pdev,
                                                                     uint8_t        module,
                                                                     uint8_t        *status,
                                                                     uint32_t       *numberOfValues)
{
	str_ChronometerInfos *str;
	uint8_t generalStart     = 0;
	uint32_t registerContent = 0;

	if (!pdev)
		return -1;

	if (module >= APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.b_ChronometerNumber)
		return -2;

	str = &APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.s_ChronometerInfos[module];
	if ((str->b_ChronometerCyclicAcquisitionStartStatus == 0) ||
		((str->b_ChronometerCyclicAcquisitionStartStatus == 1) && (str->b_ChronometerCyclicAcquisitionStartProcessIndex != 0)))
		return -3;

	/* Read the start bit */
	INPDW_MEM(GET_MEM_BAR3(pdev) + PCI3600_CLOCK_BUS_GENERAL_OFFSET, 4, &registerContent);
	generalStart = (uint8_t)((registerContent >> (module + 4)) & 1);

	/* Get the status of the Cyclic Acquisition */
	*status = 0;
	if (generalStart == 1)
	{
		if (str->b_ChronometerDigExtTriggerEnable == 1)
		{
			INPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_CHRONOMETER_OFFSET, 20, &registerContent);
			*status = (uint8_t)((registerContent >> (4 + (module * 8))) & 1);
		}
		else
		{
			*status = 1;
		}
	}

	/* Read the number of value in the on board RAM buffer */
	INPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_ON_BOARD_LOCAL_DMA_CONTROLLER_OFFSET, 188 + (module * 4), numberOfValues);

	return 0;
}

int __needlock i_PCI3600_IncrChronoCyclicAcquisitionBufferReadPointer(struct pci_dev *pdev, uint32_t dw_NbrOfValue)
{
	return i_PCI3600_IncrAnalogInCyclicAcqBufferReadPointer(pdev, dw_NbrOfValue);
}

int __needlock i_PCI3600_StopChronometerModuleCyclicAcquisition(struct pci_dev *pdev, uint8_t b_Module)
{
	uint32_t             readValue;
	str_ChronometerInfos *str;

	if (!pdev)
		return -1;

	if (b_Module >= APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.b_ChronometerNumber)
		return -2;

	str = &APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.s_ChronometerInfos[b_Module];
	if ((str->b_ChronometerCyclicAcquisitionStartStatus == 0) ||
			((str->b_ChronometerCyclicAcquisitionStartStatus == 1) && (str->b_ChronometerCyclicAcquisitionStartProcessIndex != 0)))
		return -3;

	/* Reset the general start of the ADC */
	INPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_CLOCK_BUS_GENERAL_OFFSET, 4, &readValue);
	OUTPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_CLOCK_BUS_GENERAL_OFFSET, 4, readValue & (0xFFFFFFFFUL - (1 << (b_Module + 4))));

	/* Disable the Local DMA*/
	INPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_ON_BOARD_LOCAL_DMA_CONTROLLER_OFFSET, 0, &readValue);
	OUTPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_ON_BOARD_LOCAL_DMA_CONTROLLER_OFFSET, 0, readValue & (0xFFFFFFFFUL - (1 << (b_Module + 4))));

	str->b_ChronometerCyclicAcquisitionStartStatus       = 0;
	str->b_ChronometerCyclicAcquisitionStartProcessIndex = 0;

	return 0;
}

int __needlock i_PCI3600_ReleaseChronometerModuleCyclicAcquisition(struct pci_dev *pdev, uint8_t b_Module)
{
	uint32_t             readValue;
	str_ChronometerInfos *str;

	if (!pdev)
		return -1;

	if (b_Module >= APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.b_ChronometerNumber)
		return -2;

	str = &APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.s_ChronometerInfos[b_Module];
	if ((str->b_ChronometerCyclicAcquisitionInitStatus == 0) ||
			((str->b_ChronometerCyclicAcquisitionInitStatus == 1) && (str->b_ChronometerCyclicAcquisitionInitProcessIndex != 0)))
		return -3;

	/* Reset the general start of the ADC */
	INPDW_MEM(GET_MEM_BAR3(pdev) + PCI3600_CLOCK_BUS_GENERAL_OFFSET, 4, &readValue);
	OUTPDW_MEM(GET_MEM_BAR3(pdev) + PCI3600_CLOCK_BUS_GENERAL_OFFSET, 4, readValue & (0xFFFFFFFFUL - (1 << (b_Module + 4))));

	/* Disable the Local DMA*/
	INPDW_MEM(GET_MEM_BAR3(pdev) + PCI3600_ON_BOARD_LOCAL_DMA_CONTROLLER_OFFSET, 0, &readValue);
	OUTPDW_MEM(GET_MEM_BAR3(pdev) + PCI3600_ON_BOARD_LOCAL_DMA_CONTROLLER_OFFSET, 0, readValue & (0xFFFFFFFFUL - (1 << (b_Module + 4))));

	i_PCI3600_Intern_FreeOnBoardRAMBuffer(&(APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation), (uint8_t)(b_Module + 6));

	str->b_ChronometerCyclicAcquisitionStartStatus       = 0;
	str->b_ChronometerCyclicAcquisitionStartProcessIndex = 0;
	str->b_ChronometerCyclicAcquisitionInitStatus        = 0;
	str->b_ChronometerCyclicAcquisitionInitProcessIndex  = 0;

	return 0;
}

int __needlock i_PCI3600_Chronometer_EnableDisableDigitalInputExternalTrigger(struct pci_dev *pdev, uint8_t module, uint8_t externTriggerFlag)
{
	uint32_t             readValue;
	uint32_t             valueToWrite = 0;
	str_ChronometerInfos *str;

	if (!pdev)
		return -1;

	if (module >= APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.b_ChronometerNumber)
		return -2;

	if (externTriggerFlag > 1)
		return -3;

	str = &APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.s_ChronometerInfos[module];
	if (str->b_ChronometerInitStatus != 1)
		return -4;

	if (APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.s_DigExtTriggerInfos.b_InitStatus != 1)
		return -5;

	INPDW_MEM(GET_MEM_BAR3(pdev) + PCI3600_CHRONOMETER_OFFSET, 0, &readValue);

	if (externTriggerFlag == PCI3600_ENABLE)
		valueToWrite = readValue | (uint32_t)(32 << (module * 8));
	else
		valueToWrite = readValue | (uint32_t)(0xFFFFFFFFUL - (32 << (module * 8)));

	OUTPDW_MEM(GET_MEM_BAR3(pdev) + PCI3600_CHRONOMETER_OFFSET, 0, valueToWrite);

	str->b_ChronometerDigExtTriggerEnable = externTriggerFlag;

	return 0;
}

int __needlock i_PCI3600_Chronometer_EnableDisableMasterTrigger(struct pci_dev *pdev, uint8_t module, uint8_t masterTriggerFlag)
{
	uint32_t             readValue;
	uint32_t             valueToWrite = 0;
	str_ChronometerInfos *str;

	if (!pdev)
		return -1;

	if (module >= APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.b_ChronometerNumber)
		return -2;

	if (masterTriggerFlag > 1)
		return -3;

	str = &APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.s_ChronometerInfos[module];
	if (str->b_ChronometerInitStatus != 1)
		return -4;

	INPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_CHRONOMETER_OFFSET, 0, &readValue);

	if (masterTriggerFlag == 1)
		valueToWrite = readValue | (uint32_t)(0x20 << (module * 8));
	else
		valueToWrite = readValue | (uint32_t)(0xFFFFFFFFUL - (0x20 << (module * 8)));

	OUTPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_CHRONOMETER_OFFSET, 0, valueToWrite);

	str->b_ChronometerMasterTriggerEnable = masterTriggerFlag;

	return 0;
}
