/** @file ksample01.c
*
* @author Krauth Julien
*
* Demonstrate how to use the driver in kernel mode on one APCI-3200 module.
* Mode SCAN - connection type: differential
* This sample manages one module of one board.
*/

/** @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 <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
#include <linux/config.h>
#else
	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
		#include <linux/autoconf.h>
	#else
		#include <generated/autoconf.h>
	#endif
#endif

#include <linux/spinlock.h>
#include <linux/ioctl.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/pci.h> // struct pci_dev
#include <asm/io.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0)
	#include <asm/system.h>
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,12,0)
#include <asm/uaccess.h>
#else
    #include <linux/uaccess.h>
#endif
#include <asm/bitops.h>
#include <linux/sched.h>

#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/proc_fs.h>

#include <apci3200.h>
#include <apci3200-kapi.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ADDI-DATA");
MODULE_DESCRIPTION("APCI-3200 sample");

#if LINUX_VERSION_CODE < KERNEL_VERSION(5,12,0)
MODULE_SUPPORTED_DEVICE("APCI-3200");
#endif

EXPORT_NO_SYMBOLS;

/** The name of the modul. */
#define MODULE_NAME "ksample01 apci3200"

//--------------------------------------------------------------------------------

struct apci3200_irq_t {

	DWORD value [7];
	DWORD irq_cpt;
	DWORD minor;
	DWORD mask;
	BYTE flag;
};

static struct apci3200_irq_t irq_info;

//--------------------------------------------------------------------------------

/** The user callback interrupt
*
* Each interrupt return values for 1 module.
*
* @param [out]	minor:	The minor number of the board that generates<br>
* 						the interrupt.
*
* @param [out]	mask:	The interrupt mask. 0x10 for scan interrupt.<br>
* 						The first number is the module number that<br>
* 						generates the interrupt.<br>
* 						To get the module number:<br>
* 						Module 0: (mask&3) = 0<br>
* 						Module 1: (mask&3) = 1<br>
* 						Module 2: (mask&3) = 2<br>
* 						Module 3: (mask&3) = 3
*
* @param [out]	value:	Read value<br>
* 						0) Channel 0<br>
* 						1) Channel 1<br>
* 						2) Channel CJC<br>
* 						3) Channel Calibration offset<br>
* 						4) Channel Calibration gain
* 						5) always 0
* 						6) always 0
*/
static void interrupt_callback (DWORD minor, DWORD mask, DWORD *value)
{
	printk ("minor: %x mask: %x\n", minor, mask);

	memcpy (irq_info.value, value, sizeof (DWORD) * 7);
	irq_info.irq_cpt += 1;
	irq_info.minor = minor;
	irq_info.mask = mask;
	irq_info.flag = 1;

	return;
}

//--------------------------------------------------------------------------------
static void apci3200_run_test(struct pci_dev * pdev)
{
	/* All define are available in apci3200.h */
	INT error = 0;
	WORD w_Module = 0;
	BYTE b_SensorType = ADDIDATA_THERMOCOUPLE;
	BYTE b_SensorValue[4] = {ADDIDATA_THERMOCOUPLE_TYPE_K,
			ADDIDATA_THERMOCOUPLE_TYPE_K,
			ADDIDATA_THERMOCOUPLE_TYPE_K,
			ADDIDATA_THERMOCOUPLE_TYPE_K};
	DWORD dw_ConversionTime = 160;
	BYTE b_SCANTimeMode = APCI3200_DELAY_MODE1_USED;
	DWORD dw_SCANTime = 1000;
	BYTE b_SCANTimeUnit = APCI3200_UNIT_MS;
	BYTE b_ExternTriggerMode = APCI3200_DISABLE;
	BYTE b_SCANMode = APCI3200_CONTINUOUS_SCAN;
	BYTE b_NbrChannels = 0;

	irq_info.mask = 0;
	irq_info.minor = 0;
	irq_info.irq_cpt = 0;
	irq_info.value[0] = 0;
	irq_info.value[1] = 0;
	irq_info.value[2] = 0;
	irq_info.value[3] = 0;
	irq_info.value[4] = 0;
	irq_info.value[5] = 0;
	irq_info.value[6] = 0;
	irq_info.flag = 0;

	printk ("Scan mode sample\n");

	error = i_APCI3200_GetNumberOfAnalogInputChannels (pdev, APCI3200_CONNECTION_TYPE_DIFFERENTIAL, &b_NbrChannels);

	printk ("i_APCI3200_GetNumberOfAnalogInputChannels :");
	switch (error) {
	case 0: printk ("OK with %i channels.\n", b_NbrChannels);
	break;
	case 1: printk ("The handle parameter of the board is wrong\n");
	break;
	case 2: printk ("Can't get the number of input channels.\n");
	break;
	default: printk ("Unknown return value %d\n", error);
	}

	/* Set the user interrupt callback */
	error = i_APCI3200_SetBoardIntRoutine (pdev, interrupt_callback);

	printk ("i_APCI3200_SetBoardIntRoutine :");
	switch (error) {
	case 0: printk ("OK\n");
	break;
	case 1: printk ("The handle parameter of the board is wrong\n");
	break;
	case 2: printk ("The interrupt callback is NULL.\n");
	break;
	default: printk ("Unknown return value %d\n", error);
	}

	error = i_APCI3200_InitScanModuleAcquisitionSensor (pdev, w_Module, b_SensorType, b_SensorValue, APCI3200_CONNECTION_TYPE_DIFFERENTIAL);

	printk ("i_APCI3200_InitScanModuleAcquisitionSensor :");
	switch (error) {
	case 0: printk ("OK\n");
	break;
	case 1: printk ("The handle parameter of the board is wrong\n");
	break;
	case 2: printk ("The module parameter is wrong.\n");
	break;
	case 3: printk ("The sensor parameter is wrong.\n");
	break;
	default: printk ("Unknown return value %d\n", error);
	}

	error = i_APCI3200_InitScanModuleAcquisition (pdev, w_Module,
			dw_ConversionTime,
			b_SCANTimeMode,
			dw_SCANTime,
			b_SCANTimeUnit,
			b_SCANMode,
			b_ExternTriggerMode);

	printk ("i_APCI3200_InitScanModuleAcquisition :");
	switch (error) {
	case 0: printk ("OK\n");
	break;
	case 1: printk ("The handle parameter of the board is wrong\n");
	break;
	case 2: printk ("The module parameter is wrong.\n");
	break;
	case 3: printk ("Scan not initialized.\n");
	break;
	case 4: printk ("Scan already started.\n");
	break;
	case 5: printk ("Scan can't be started.\n");
	break;
	default: printk ("Unknown return value %d\n", error);
	}

	error = i_APCI3200_StartScanModuleAcquisition(pdev, w_Module);

	printk ("i_APCI3200_StartScanModuleAcquisition :");
	switch (error) {
	case 0: printk ("OK\n");
	break;
	case 1: printk ("The handle parameter of the board is wrong\n");
	break;
	case 2: printk ("The module parameter is wrong.\n");
	break;
	case 3: printk ("Scan not initialized.\n");
	break;
	case 4: printk ("Scan already started.\n");
	break;
	case 5: printk ("Scan can't be started.\n");
	break;
	default: printk ("Unknown return value %d\n", error);
	}

	/* Stop after 10 acquisitions */
	while (irq_info.irq_cpt <= 10) {

		if (irq_info.flag) {
			printk ("minor: %x mask: %x val: %x %x %x %x %x %x %x %i\n",
					irq_info.minor,
					irq_info.mask,
					irq_info.value[0],
					irq_info.value[1],
					irq_info.value[2],
					irq_info.value[3],
					irq_info.value[4],
					irq_info.value[5],
					irq_info.value[6],
					irq_info.irq_cpt);
			irq_info.flag = 0;
		}
		mdelay (5);
	}

	error = i_APCI3200_StopScanModuleAcquisition (pdev, w_Module);

	printk ("i_APCI3200_StopScanModuleAcquisition :");
	switch (error) {
	case 0: printk ("OK\n");
	break;
	case 1: printk ("The handle parameter of the board is wrong\n");
	break;
	case 2: printk ("The module parameter is wrong.\n");
	break;
	case 3: printk ("Scan not initialized.\n");
	break;
	case 4: printk ("Scan not started.\n");
	break;
	case 5: printk ("Scan can't be stopped.\n");
	break;
	default: printk ("Unknown return value %d\n", error);
	}

	/* Reset the user interrupt callback */
	error = i_APCI3200_ResetBoardIntRoutine (pdev);

	printk ("i_APCI3200_ResetBoardIntRoutine :");
	switch (error) {
	case 0: printk ("OK\n");
	break;
	case 1: printk ("The handle parameter of the board is wrong\n");
	break;
	default: printk ("Unknown return value %d\n", error);
	}

}
//--------------------------------------------------------------------------------

/** Called when the driver is loaded.*/
static int __init _init_module(void)
{
	struct pci_dev * pdev = NULL; /* Pointer on the board structure */
	int index = 0; /* Index of the board to use (here the first board) */

	/* Get a pointer on the board to use */
	if ((pdev = apci3200_lookup_board_by_index(index)) == NULL) {
		printk ( KERN_DEBUG "Board index %i not found\n", index);
		return 1;
	}
	printk ( KERN_DEBUG "Board index %i found\n", index);

	/* Test the first board found */
	apci3200_run_test (pdev);

	printk ("Sample concluded, you can now rmmod it!\n");

	return 0;
}

//--------------------------------------------------------------------------------

/** Called when the driver is unloaded.*/
static void __exit _cleanup_module(void)
{

}

//--------------------------------------------------------------------------------

module_init(_init_module);
module_exit(_cleanup_module);
