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

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

ADDI-DATA GmbH
Airpark Business Center
Airport Boulevard B210
77836 Rheinmuenster
Germany
Tel: +49(0)7229/1847-0
Fax: +49(0)7229/1847-200
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 __APCI3120_PRIVDATA_H_
#define __APCI3120_PRIVDATA_H_

#define APCI3120_NB_ANALOG_INPUT 16

#include <linux/version.h>
#include <linux/wait.h>
#include <linux/types.h>      /* dma_addr_t */
#include <linux/dma-mapping.h>

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

#define APCI3120_SIZE_EVENT_FIFO 4096

DECLARE_NEW_FIFO_TYPE ( apci3120_event_fifo_t, APCI3120_SIZE_EVENT_FIFO, apci3120_event_t);

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

union csTimerCtr0_t
{
	struct
	{
			unsigned int timer_select : 2;
			unsigned int dummy        : 2;
			unsigned int digOutReg    : 4;
	};
	uint8_t raw;
};

/* internal driver data */
struct apci3120_str_BoardInformations
{
	/* field used to implement linked list */
	struct pci_dev * previous; /**< previous in known-devices linked list */
	struct pci_dev * next; /**< next in known-devices linked list */

	spinlock_t lock; /**< protect the board data */

	uint8_t	nrAnaIn;
	uint8_t	nrAnaOut;
	union csTimerCtr0_t crt0;
	uint8_t	crt1;				/* saved HW register */
	uint8_t	modeSelect;			/* saved HW register: APCI3120_WRITE_MODE_SELECT */
	uint8_t	digOutMem;
	uint8_t	lastChannel;
	uint8_t	intMode;
	uint8_t	interruptInitialized;
	uint16_t nWrAddr;			/* saved HW register: APCI3120_WR_ADDRESS */
	uint8_t	outputPolarity[8];		/* Max 8 analog outputs channels */
	uint32_t t2Delay;
	uint8_t	t2Init;
	uint8_t	t2Started;
	uint8_t	t2Interrupt;
	uint8_t	timerMode;
	unsigned long base_io; /* base address to perform I/O accesses (BAR1 for APCI3120, BAR2+APCIE3120_REG_OFFSET for APCIE3120 */

	/*
	 * State of the device
	 */
	enum state device_state;

	/*
	 * DMA
	 */
	uint8_t dma_state; // ENABLE, DISABLE

	unsigned long* dma_buffer; // pointer to the dma memory area
	unsigned long dma_buffer_size;
	unsigned long dma_buffer_pages;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
	dma_addr_t dma_buffer_hw; // bus address returned by dma_alloc_coherent()
#else
	unsigned long* dma_buffer_hw; // pointer to the Hardware address (legacy virt_to_bus)
#endif

	// We use two buffers for the DMA. If a buffer is read, we are able to save data in the other buffer
	unsigned long* dma_old_buffer; // pointer to the dma memory area
	unsigned long dma_old_buffer_size;
	unsigned long dma_old_buffer_pages;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
	dma_addr_t dma_old_buffer_hw;
#else
	unsigned long* dma_old_buffer_hw;
#endif
	unsigned char dma_buf_index;

	/*
	 * Parameters used for the sequence acquisition
	 * We must save them in order to do just one init !
	 * Indeed, if we do not save these parameters, we have to call the function init each time we want to start a sequence acquisition
	 * By saving the parameters, we can do : init acquisition sequence, start sequence, stop sequence, start sequence , etc ...
	 */
	uint8_t sequence_array_size;
	uint8_t sequence[APCI3120_NB_ANALOG_INPUT];
	uint8_t gain[APCI3120_NB_ANALOG_INPUT];
	uint8_t polarity[APCI3120_NB_ANALOG_INPUT];
	uint32_t ram_array[APCI3120_NB_ANALOG_INPUT];
	unsigned short conversion_time; // The computed conversion time for the hardware
	struct apci3120_sequence_acquisition_data sequence_data; // The structure is described in the file "apci3120.h"

	apci3120_event_fifo_t evtfifo;
	wait_queue_head_t evt_wq;
};
//------------------------------------------------------------------------------
/** initialise board's private data - fill it when adding new members and ioctl handlers */
static __inline__ void apci3120_init_priv_data(struct apci3120_str_BoardInformations * data)
{
	/* Be carefull, don't put a memset data after spin_lock_init!! */
	// FILLME

	memset (data, 0, sizeof (struct apci3120_str_BoardInformations));

	/* Set a default value for the number of input channels */
	data->nrAnaIn = APCI3120_NB_ANALOG_INPUT;

	/* Set a default value for the number of output channels */
	data->nrAnaOut = 8;

	/* Set the state as uninitialized */
	data->device_state = STATE_UNINIT;
	data->interruptInitialized = APCI3120_DISABLE; // no interruption registered for the moment
	data->dma_state = APCI3120_DISABLE;

	apci3120_event_fifo_t_initialise( &data->evtfifo );
	init_waitqueue_head ( &data->evt_wq );

	spin_lock_init(& (data->lock) );
}
//------------------------------------------------------------------------------
/** return the private data field of a pci_dev structure.
* @note The implementation of this function differs in 2.4 and 2.6 kernel
*/
static __inline__ struct apci3120_str_BoardInformations * APCI3120_PRIVDATA(struct pci_dev * pdev)
{
	return (struct apci3120_str_BoardInformations *) pci_get_drvdata(pdev);
}
//------------------------------------------------------------------------------
/** lock the board */
static __inline__ void APCI3120_LOCK(struct pci_dev * pdev, unsigned long * flags)
{
	spin_lock_irqsave(& (APCI3120_PRIVDATA(pdev)->lock), *flags );
}
//------------------------------------------------------------------------------
/** unlock the board */
static __inline__ void APCI3120_UNLOCK(struct pci_dev * pdev, unsigned long flags)
{
	spin_unlock_irqrestore(& (APCI3120_PRIVDATA(pdev)->lock) , flags );
}
//------------------------------------------------------------------------------
// append to the list of known devices
extern void apci3120_known_dev_append(struct pci_dev * pdev);
// remove from the list of known devices
extern void apci3120_known_dev_remove(struct pci_dev * pdev);
//------------------------------------------------------------------------------

/* EVENTS */
static inline int apci3120_evt_add(struct pci_dev * pdev, apci3120_event_t * evt, int count)
{
	return apci3120_event_fifo_t_add( &(APCI3120_PRIVDATA(pdev)->evtfifo), evt, count );
}

static inline int apci3120_evt_retrieve(struct pci_dev * pdev, apci3120_event_t * evt, int count)
{
	return apci3120_event_fifo_t_retrieve( &(APCI3120_PRIVDATA(pdev)->evtfifo), evt, count );
}

static inline int apci3120_evt_count(struct pci_dev * pdev)
{
	return apci3120_event_fifo_t_count( &(APCI3120_PRIVDATA(pdev)->evtfifo));
}

/* wakeup all (interruptible sleeping) processes waiting for event on this board */
static inline int apci3120_evt_wakeup(struct pci_dev * pdev)
{
	wake_up_interruptible( &(APCI3120_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 apci3120_evt_wait(struct pci_dev * pdev)
{
	register struct apci3120_str_BoardInformations * drvdata = APCI3120_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 // __APCI3120_PRIVDATA_H_
