 /* @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 __APCI3701_PRIVDATA_H_
#define __APCI3701_PRIVDATA_H_

#include <linux/version.h>

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,27)
	#define __user
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	#define __iomem
#endif


#define APCI3701_ITER_TIMEOUT 100 // use when testing the ready bit

#define APCI3701_MAX_LVDT_TRANSDUCERS 40

#define APCI3701_NUMBER_OF_ANALOG_INPUT 16


/*
 * FIFO of events
 */
#define APCI3701_SIZE_EVENT_FIFO 20

#include <apci3701-events.h>
#include <apci3701-kapi.h>
#include "fifo.h"

DECLARE_NEW_FIFO_TYPE(apci3701_event_fifo_t,APCI3701_SIZE_EVENT_FIFO,apci3701_event_t);

/* internal driver data */
struct apci3701_str_BoardInformations
{
	spinlock_t lock; /**< protect the board data */

  	/* 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 */

	/* Asynchronous IO signal implementation - send a SIGIO to all registered processes*/
	/* If using signal to inform user mode, uncomment following line: */
	// struct fasync_struct * async_queue; /* asynchronous readers */

	/* Memory mapped address */
	void __iomem * bar0;
	void __iomem * bar3;

	uint8_t b_NumberOfAnalogInput;	/**< Number of analog input */
	uint16_t w_SequenceSize;		/**< Size of the analog input sequence */
	uint16_t w_ChannelSequence[APCI3701_NUMBER_OF_ANALOG_INPUT];	/**< An APCI-3701 has a max. of 16 channels */

	/* Eeprom and header informations */
	uint16_t * pw_BoardEeprom;
	uint16_t * pw_BoardHeader;

	wait_queue_head_t data_wq; /* wait queue of process sleeping waiting for data */

	/* Diagnostic*/
	uint8_t b_diagnostic_short_circuit_interrupt; // 1 if we use interruption to detect short circuit
	uint8_t b_open_circuit_primary_enable; // 1 if enabled

	uint8_t b_short_circuit_secondary_enable; // 1 if enabled
	uint8_t b_open_circuit_secondary_enable; // 1 if enabled

	/*
	 * Transducer plugged information
	 * When doing initialization, we will store the data here
	 * For example, to test the secondary line short and open circuit, we have to know some information specific to each transducer
	 * So we have to know which transducer are plugged
	 * We can identify each transducer by its type
	 */
	uint16_t transducers_type [ APCI3701_NUMBER_OF_ANALOG_INPUT ];

	/*
	 * Event fifo handling
	 */
	apci3701_event_fifo_t evtfifo; // FIFO of events
	wait_queue_head_t evt_wq; // wait queue of process sleeping waiting for event
};

/** initialise board's private data - fill it when adding new members and ioctl handlers */
static __inline__ void apci3701_init_priv_data(struct apci3701_str_BoardInformations * data)
{
	int i = 0;

	/* Be carefull, don't put a memset data after spin_lock_init!! */
	memset(data,0,sizeof(struct apci3701_str_BoardInformations));

	/* Memory mapped address */
	data->bar0 = NULL;
	data->bar3 = NULL;

	data->b_NumberOfAnalogInput = APCI3701_NUMBER_OF_ANALOG_INPUT;
	data->w_SequenceSize = 0;
	memset (data->w_ChannelSequence, 0, sizeof(uint16_t)*APCI3701_NUMBER_OF_ANALOG_INPUT);

	/* Eeprom and header informations */
	data->pw_BoardEeprom = NULL;
	data->pw_BoardHeader = NULL;

	/* Asynchronous IO signal implementation - send a SIGIO to all registered processes*/
	/* If using signal to inform user mode, uncomment following line: */
	// data->async_queue = NULL;

	spin_lock_init(& (data->lock) );
	init_waitqueue_head( &(data->data_wq) );

	data->b_diagnostic_short_circuit_interrupt = 0;
	data->b_open_circuit_primary_enable = 0;
	data->b_short_circuit_secondary_enable = 0;
	data->b_open_circuit_secondary_enable = 0;

	// Initialize the array of the transducer types that are plugged
	for ( i = 0 ; i < APCI3701_NUMBER_OF_ANALOG_INPUT; i++ )
		data->transducers_type[i] = 0;

	/*
	 * Events
	 */
	apci3701_event_fifo_t_initialise( &(data->evtfifo) );
	init_waitqueue_head( &(data->evt_wq) );
}


/** 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 apci3701_str_BoardInformations * APCI3701_PRIVDATA(struct pci_dev * pdev)
{
	return (struct apci3701_str_BoardInformations *) pci_get_drvdata(pdev);
}


/** lock the board */
static __inline__ void APCI3701_LOCK(struct pci_dev * pdev, unsigned long * flags)
{
	spin_lock_irqsave(& (APCI3701_PRIVDATA(pdev)->lock) , * flags);
}

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


/* wakeup all (interruptible sleeping) processes waiting for data on this board */
static inline int apci3701_data_wakeup(struct pci_dev * pdev)
{
	wake_up_interruptible( &(APCI3701_PRIVDATA(pdev)->data_wq) );
	return 0;
}

/* EVENTS */

static inline int apci3701_evt_add(struct pci_dev * pdev, apci3701_event_t * evt, int count)
{
	return apci3701_event_fifo_t_add( &(APCI3701_PRIVDATA(pdev)->evtfifo), evt, count );
}

static inline int apci3701_evt_retrieve(struct pci_dev * pdev, apci3701_event_t * evt, int count)
{
	return apci3701_event_fifo_t_retrieve( &(APCI3701_PRIVDATA(pdev)->evtfifo), evt, count );
}

static inline int apci3701_evt_count(struct pci_dev * pdev)
{
	return apci3701_event_fifo_t_count( &(APCI3701_PRIVDATA(pdev)->evtfifo));
}

/* wakeup all (interruptible sleeping) processes waiting for event on this board */
static inline int apci3701_evt_wakeup(struct pci_dev * pdev)
{
	wake_up_interruptible( &(APCI3701_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 apci3701_evt_wait(struct pci_dev * pdev)
{
	register struct apci3701_str_BoardInformations * drvdata = APCI3701_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;
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,27)
	#undef __user
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
  #undef __iomem
#endif

#endif // __APCI3701_PRIVDATA_H_
