/** @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 Rheinm�nster
        Germany
        Tel: +49(0)7229/1847-0
        Fax: +49(0)7229/1847-200
        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 should have received a copy of the GNU General Public License along with 
this program; if not, write to the Free Software Foundation, 
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

You shoud also 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 "apcie1532-private.h"


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

EXPORT_NO_SYMBOLS;

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

/* Empty the event FIFO - takes no arguments */
int do_CMD_APCIE1532_EventEmpty(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	unsigned long irqstate;
	APCIE1532_LOCK(pdev,&irqstate);
	{
		apcie1532_event_t evt;
		while( apcie1532_evt_count(pdev) )
		{
			apcie1532_evt_retrieve(pdev,&evt,1); 
		}
	}
	APCIE1532_UNLOCK(pdev,irqstate);
	return 0;
}

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

/* try to atomically retrieve _one_ event */
static inline unsigned int apcie1532_evt_retrieve_atomic(struct pci_dev * pdev, apcie1532_event_t * evt)
{
	unsigned long irqstate;
	unsigned int retrieved = 0;
	APCIE1532_LOCK(pdev,&irqstate);
	{		 
		retrieved = apcie1532_evt_retrieve(pdev,evt,1);
	}
	APCIE1532_UNLOCK(pdev,irqstate);
	return retrieved;
}

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

/* Makes the caller sleep until there is an event */
int do_CMD_APCIE1532_EventWait(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	apcie1532_event_t evt;	
	memset(&evt,0,sizeof(apcie1532_event_t));
	
	/* is there is already events in the FIFO, don't ever register for waiting, 
	 * fetch the requested amount of events and return it to the caller */
	
	{
		/* atomically try to retrieve an event */
		unsigned int retrieved = apcie1532_evt_retrieve_atomic(pdev, &evt);
		
		if(retrieved) /* an event was already available */
		{
			if ( copy_to_user( (apcie1532_event_t __user * ) arg, &evt, sizeof(apcie1532_event_t) ) )
			{
				/* event has been lost ! */
				return -EFAULT;
			}
			return 0;
		}
	}
	
	/* no event were available, go to bed */
	while(1)
	{
		int ret = apcie1532_evt_wait(pdev);
		if (ret)
			return ret;

		/* process has been wake'd up */
		{
			/* atomically try to retrieve an event */
			unsigned int retrieved = apcie1532_evt_retrieve_atomic(pdev, &evt);
			
			if(retrieved) /* an event was already available */
			{
				if ( copy_to_user( (apcie1532_event_t __user * ) arg, &evt, sizeof(apcie1532_event_t) ) )
				{
					/* event has been lost ! */
					return -EFAULT;
				}
				/* OK, leave the loop */
				return 0;
			}
			/* no event was available - probably a race condition ( another process ate it up ) - return to sleep */
		}
	}
	/* should never be reached */
	BUG();
	return 0;
}

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