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

#ifndef __APCI3701_PRIVATE__
#define __APCI3701_PRIVATE__

#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
        #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)
                #include <linux/smp_lock.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 <apci.h>
#include <apci3701.h>
#include <apci3701-kapi.h>
#include "privdata.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

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

/** ressources of a board */

static __inline__ unsigned long GET_BAR0(struct pci_dev * dev)
{
	return dev->resource[0].start;
}

static __inline__ unsigned long GET_BAR1(struct pci_dev * dev)
{
	return dev->resource[1].start;
}

static __inline__ unsigned long GET_BAR2(struct pci_dev * dev)
{
	return dev->resource[2].start;
}

static __inline__ unsigned long GET_BAR3(struct pci_dev * dev)
{
	return dev->resource[3].start;
}

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

static __inline__ void __iomem * GET_MEM_BAR0(struct pci_dev * dev)
{
   return(APCI3701_PRIVDATA(dev)->bar0);
}

static __inline__ void __iomem * GET_MEM_BAR3(struct pci_dev * dev)
{
   return(APCI3701_PRIVDATA(dev)->bar3);
}

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

static __inline__ VOID INPDW_MEM (void __iomem * address, unsigned long offset, uint32_t * val)
{
	*val = readl( (void __iomem *) ( ((unsigned long)address) + offset) );
}

static __inline__ VOID OUTPDW_MEM (void __iomem * address,unsigned long offset, uint32_t val)
{
	writel(val, ( (void __iomem *) ( ((unsigned long)address) + offset) ) );
}
        
//---------------------------------------------------------------

/** Utility fonction that returns 0 if the given index corresponds to a card already configured.
 * 
 * @param count Number of board 
 * @param index Minor number to check
 * 
 * @return 0 if minor number valid, not 0 otherwise
 * 
 * count ranges from 1 to ...
 * index ranges from 0 to ...
 * 
 * */

static __inline__ int apci3701_INDEX_NOT_VALID(atomic_t * count , unsigned int index )
{
	volatile unsigned int __count =  atomic_read(count);
	if (index >= __count) return 1;
	return 0;
	
}

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

/* number of board detected by the kernel */
extern atomic_t apci3701_count;

#define __DRIVER_NAME "apci3701"

/* major number (attributed by the OS) */
extern unsigned int apci3701_majornumber;

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

/* /dev function */
/* Asynchronous IO signal implementation - send a SIGIO to all registered processes*/
/* If using signal to inform user mode, uncomment following line: */
// int apci3701_fasync_lookup(int fd, struct file *filp, int mode);
int apci3701_open_lookup (struct inode *inode, struct file *filp);
int apci3701_release_lookup (struct inode *inode,struct file *filp);

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
int  apci3701_ioctl_lookup (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
#else
long apci3701_ioctl_lookup (struct file *filp, unsigned int cmd, unsigned long arg);
#endif

ssize_t apci3701_read(struct file *filep, char __user * buff, size_t count, loff_t * offp);

/* kernel-side accessible API */
//int apci3701_do_ioctl(struct pci_dev * pdev, unsigned int cmd, unsigned long arg);

/*/proc functions  */
void apci3701_proc_init(void);
void apci3701_proc_release(void);
void apci3701_proc_create_device(struct pci_dev * dev, unsigned int minor_number);
void apci3701_proc_release_device(struct pci_dev * dev);

/* interrupt related function */
int apci3701_register_interrupt(struct pci_dev * pdev);
int apci3701_deregister_interrupt(struct pci_dev * pdev);

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

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

#include "api.h"
#include "privdata.h"
#include "iomap.h"
#include "eeprom/include/pcidetection.h"

/** return 1 if an acquisition is started, 0 otherwise */ 
static inline int APCI3701_ACQUISITION_STARTED(struct pci_dev * pdev) 
{
	return ( ( ( readl( GET_MEM_BAR3(pdev) + APCI3701_REG_SEQUENCE_CONTROL ) ) & ANALOG_SEQUENCE_START_STOP_BITMASK ) == ANALOG_SEQUENCE_START_STOP_BITMASK);
}

/** busy loops until the condition is true or a timeout occurs 
 * 
 * This function is intended to be used in context when interrupts are or could be deactivated.
 * 
 * Its use must be restrained to these cases, as busylooping is the most inefficient way of doing that.
 * 
 * @param[in] pdev The device concerned.
 * @param[in] checkf A function that performs the check. it must return 0 if the condition is not fullfilled, 1 otherwise
 * @param[in] timeout The timeout in ticks of 5 microseconds
 * 
 */
static inline int apci3701_eeprom_busywait_with_timeout(int (*checkf) (pstr_AddressInformation ps_Address), pstr_AddressInformation ps_Address, unsigned int timeout)
{
    unsigned long count = 0;
    while(1)
    {
		if ( checkf(ps_Address) )
        	return 0; /* condition becomes true */

        /* wait 100 microseconds */
        udelay(100);
        count++;
        if (count>=timeout)
        	return 1; /* timeout */
    }
}

#endif //__apci3701_PRIVATE__
