/* @file privadata.h
 *
 * @brief Holds definition for driver's private data and its manipulation functions.
 *
 *
 *
 */

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

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 library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.

This library 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA


You also shoud find the complete LGPL in the LGPL.txt file accompanying
this source code.

@endverbatim
**/

#ifndef __APCI3200_PRIVDATA_H_
#define __APCI3200_PRIVDATA_H_

#include <apci3200-events.h>
#include "fifo.h"

#define APCI3200_MAX_ANALOG_INPUTS_PER_MODULE (4)
#define APCI3200_MAX_MODULES_PER_BOARD (4)

#define APCI3200_SIZE_EVENT_FIFO 2000

DECLARE_NEW_FIFO_TYPE(apci3200_event_fifo_t,APCI3200_SIZE_EVENT_FIFO,apci3200_event_t);

//------------------------------------------------------------------------------
struct apci3200_scan_data
{
	/** counter incremented by each interrupt.
	 *  when it reaches ui_NbDataToRead the user application is advertised */
	uint32_t counter;
	/**< data acquired by a scan.
	ADDIDATA_THERMOCOUPLE =>
	dw_DigitalAnalogInputValue[0]: channel 0 value
	dw_DigitalAnalogInputValue[1]: channel 1 value
	dw_DigitalAnalogInputValue[2]: channel 2 value
	dw_DigitalAnalogInputValue[3]: channel 3 value
	dw_DigitalAnalogInputValue[4]: CJC value
	dw_DigitalAnalogInputValue[5]: Calibration Offset value
	dw_DigitalAnalogInputValue[6]: Calibration Gain value
	If ADDIDATA_RTD =>
	dw_DigitalAnalogInputValue[0]: channel 0 value
	dw_DigitalAnalogInputValue[1]: channel 1 value
	dw_DigitalAnalogInputValue[2]: CJC value (but not used)
	dw_DigitalAnalogInputValue[3]: Calibration Offset value
	dw_DigitalAnalogInputValue[4]: Calibration Gain value
	*/
	DWORD    dw_DigitalAnalogInputValue[7];

};

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

/* internal driver data */
struct apci3200_module_data
{
	unsigned long ul_CurrentSourceCJC;
	unsigned long ul_CurrentSource [APCI3200_MAX_ANALOG_INPUTS_PER_MODULE]; /**< one current source per module */
	unsigned long  ul_GainFactor [8]; // Gain Factor
	unsigned int w_GainValue [10];

	unsigned char b_SCANStarted;
	unsigned char b_SensorType;
	unsigned char b_SensorValue[APCI3200_MAX_ANALOG_INPUTS_PER_MODULE];
	unsigned int  w_NbChannel; /**< number of channel - depends on the type of connection (single-ended or differencial) */
	unsigned char b_ModuleInitialised;
	unsigned char b_SCANInitialised; /**< module initialised in mode SCAN */
	unsigned long dw_SCANConversionReloadValueSelected;
	unsigned char b_SCANDelayModeSelected;
	unsigned long dw_SCANDelayReloadValueSelected;
	unsigned char b_SCANDelayTimeBaseSelected;
	unsigned char b_SCANExternTriggerSelected;
	unsigned char b_SCANExternTriggerFrontSelected;
	unsigned int  ui_NbDataToRead; /**< number of data to be acquired in a scan before generating an interrupt - it depends of the configuration of the board*/
	unsigned char b_SCANModeSelected;
	unsigned char b_DiffSingSelected;
	unsigned int  dw_ReferenceVoltage[APCI3200_MAX_ANALOG_INPUTS_PER_MODULE]; /**< integer representing the reference voltage in mV - one per module */
	struct apci3200_scan_data scan_data; /** contains the data acquired during a SCAN */
};

struct apci3200_str_BoardInformations
{
	spinlock_t lock; /**< protect the board data */
	int32_t OutputMemoryStatus; /**< Digital Output Memory Status (0: not active, 1: active) */
	uint32_t DigitalOutputState; /**< Memory field for digital output state */

	uint8_t b_InterruptInitialised;
	uint16_t w_NbHardwareComponent;	/**< number of modules */
	struct apci3200_module_data s_Module[APCI3200_MAX_MODULES_PER_BOARD];
	void (*UserInterruptCallback) (DWORD minor, DWORD mask, DWORD *value);
	unsigned char minor;
	unsigned char DiagnosticMode[16]; /**< define the diagnostic mode to use:
	                                   APCI3200_DIAGNOSTIC_NONE: No diagnostic, is the default mode.
	                                   APCI3200_DIAGNOSTIC_SHORT_CIRCUIT: Test short circuit.
	                                   APCI3200_DIAGNOSTIC_OPEN_CONNECTION: Test if a sensor is connected.
	                                   */

	apci3200_event_fifo_t evtfifo; /* FIFO of events */
	wait_queue_head_t evt_wq; /* wait queue */
};

//------------------------------------------------------------------------------
/** initialise board's private data - fill it when adding new members and ioctl handlers */
static __inline__ void apci3200_init_priv_data(struct apci3200_str_BoardInformations * data)
{
	memset (data, 0, sizeof (struct apci3200_str_BoardInformations));
	spin_lock_init(& (data->lock) );
	data->UserInterruptCallback = NULL;
	apci3200_event_fifo_t_initialise( &(data->evtfifo) );
	init_waitqueue_head( &(data->evt_wq) );
	memset (data->DiagnosticMode, APCI3200_DIAGNOSTIC_NONE, sizeof (data->DiagnosticMode));
}
//------------------------------------------------------------------------------

/* typed cast (safer to use) */
static __inline__ struct apci3200_str_BoardInformations * APCI3200_PRIVDATA(struct pci_dev * pdev)
{
	return (struct apci3200_str_BoardInformations *) pci_get_drvdata(pdev);
}
//------------------------------------------------------------------------------
/* accessor to module structure of index "module" in device "pdev" */
static inline struct apci3200_module_data * GET_MODULE_DATA(struct pci_dev * pdev, uint16_t module)
{
	return &(APCI3200_PRIVDATA(pdev)->s_Module[module]);
}
//------------------------------------------------------------------------------
/* accessor to scan structure of index "module" in device "pdev" */
static inline struct apci3200_scan_data * GET_SCAN_DATA(struct pci_dev *pdev, int module)
{
	return &(GET_MODULE_DATA(pdev, module)->scan_data);
}
//------------------------------------------------------------------------------

/* return 1 if memory on, else 0 */
static __inline__ int APCI3200_OUTP_MEM_ON(struct pci_dev * pdev)
{
	return  (APCI3200_PRIVDATA(pdev)->OutputMemoryStatus);
}

/* lock the board */
static __inline__ void APCI3200_LOCK(struct pci_dev * pdev, unsigned long * irqstate)
{
	spin_lock_irqsave(& (APCI3200_PRIVDATA(pdev)->lock), *irqstate );
}

/* unlock the board */
static __inline__ void APCI3200_UNLOCK(struct pci_dev * pdev, unsigned long irqstate)
{
	spin_unlock_irqrestore(& (APCI3200_PRIVDATA(pdev)->lock) , irqstate);
}

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

/* Events */

static inline int apci3200_evt_full(struct pci_dev * pdev)
{
	return apci3200_event_fifo_t_is_full( &(APCI3200_PRIVDATA(pdev)->evtfifo));
}

static inline int apci3200_evt_add(struct pci_dev * pdev, apci3200_event_t * evt, int count)
{
	return apci3200_event_fifo_t_add( &(APCI3200_PRIVDATA(pdev)->evtfifo), evt, count );
}

static inline int apci3200_evt_retrieve(struct pci_dev * pdev, apci3200_event_t * evt, int count)
{
	return apci3200_event_fifo_t_retrieve( &(APCI3200_PRIVDATA(pdev)->evtfifo), evt, count );
}

static inline int apci3200_evt_count(struct pci_dev * pdev)
{
	return apci3200_event_fifo_t_count( &(APCI3200_PRIVDATA(pdev)->evtfifo));
}

/* wakeup all (interruptible sleeping) processes waiting for event on this board */
static inline int apci3200_evt_wakeup(struct pci_dev * pdev)
{
	wake_up_interruptible( &(APCI3200_PRIVDATA(pdev)->evt_wq) );
	return 0;
}

/* this function is for processes that want to sleep waiting an for event in the FIFO */
static inline int apci3200_evt_wait(struct pci_dev * pdev)
{
	register struct apci3200_str_BoardInformations * drvdata = APCI3200_PRIVDATA(pdev);
	/* sleep until process interrupted or evtfifo.count is superior to zero */
	if ( wait_event_interruptible( drvdata->evt_wq, (drvdata->evtfifo.count > 0) ) )
	{
		/* process has been interrupted */
    	return -ERESTARTSYS;
    }
    return 0;
}

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

#endif // __APCI3200_PRIVDATA_H_
