/*
 * Copyright (c) 2003 Linuxant inc.
 * Copyright (c) 2001-2003 Conexant Systems, Inc.
 *
 * NOTE: The use and distribution of this software is governed by the terms in
 * the file LICENSE, which is included in the package. You must read this and
 * agree to these terms before using or distributing this software.
 *
 */

/*osTime.c

This file includes os specific code for timing related functionalities.

*/
#include "oscompat.h"
#include <linux/timer.h>
#include <linux/time.h>
#include <linux/ptrace.h>
#include <linux/interrupt.h>
#include <asm/semaphore.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <asm/system.h>
#include <linux/smp_lock.h>
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,7) )
#include <linux/completion.h>
#endif

#include "oscompat.h"
#include "ossysenv.h"
#include "ostime_ex.h"
#include "osmemory_ex.h"
#include "osstring_ex.h"
#include "linuxres.h"


#define TICKS_TO_MSECS(ticks) (ticks*(1000/HZ))

static inline long MSECS_TO_TICKS(UINT32 msecs)
{
	long ticks = (msecs)/(1000/HZ);

	// round up to next tick
	if ((!ticks && msecs) || (msecs % (1000/HZ)) >= ((1000/HZ) / 2))
		ticks++;

	return ticks;
}

typedef struct TIME_OUT_INSTANCE_TYPE
{
	struct tq_struct	TaskQueue;
	struct wait_queue	*WaitQueue;
	struct timer_list	Timer;
	BOOL                active;
	int					SchedCount;

	UINT32			mSec;
	BOOL			bLocked;

	PFREE_FUNC		pFuncFree;
	PVOID			pRefData;

	PCBFUNC			pTimeOutCallBack;

}TIME_OUT_INSTANCE_T, *PTIME_OUT_INSTANCE_T;
 
typedef struct TIMER_TAG
{
	UINT32 msec;
	struct timer_list timer;
} TIMER_T, *PTIMER_T;

/********************************************************************/

STATIC	VOID TimerThreadFunction(PVOID pData)
{
	PTIME_OUT_INSTANCE_T	pTimeOutInstance = (PTIME_OUT_INSTANCE_T)pData;

	if (pTimeOutInstance->active == TRUE )
	{
	    	/* beware: some callback functions use FPU instructions */
		pTimeOutInstance->pTimeOutCallBack(pTimeOutInstance->pRefData);

		// callback might have set active to FALSE
		if (pTimeOutInstance->active == TRUE ) {
			mod_timer(&pTimeOutInstance -> Timer, jiffies + MSECS_TO_TICKS(pTimeOutInstance -> mSec));
		}
	}

	pTimeOutInstance->SchedCount--;
}

STATIC VOID TimeOutHandler(	unsigned long Data )
{
	PTIME_OUT_INSTANCE_T	pTimeOutInstance = (PTIME_OUT_INSTANCE_T)Data;

	if (pTimeOutInstance->active == FALSE )
	{
		return;
	}

	pTimeOutInstance->SchedCount++;
	if (OsModemThreadSchedule(&(pTimeOutInstance -> TaskQueue)) == 0)
	{
		//printk(KERN_ERR "%s: TimeOutHandler(%d) - schedule_task FAILED (jiffies=%ld)\n", __FUNCTION__, pTimeOutInstance->SchedCount, jiffies);
		pTimeOutInstance->SchedCount--;
	}
}

static DECLARE_TASK_QUEUE(tq_mdmthrd);
static DECLARE_WAIT_QUEUE_HEAD(mdmthrd_wait);
#ifdef DECLARE_COMPLETION
static DECLARE_COMPLETION(mdmthrd_exited);
#endif
static int mdmthrd_pid;

int OsModemThreadSchedule(PVOID task)
{
	int ret;

	if(mdmthrd_pid <= 0) {
		printk(KERN_ERR"%s: WARNING: modem thread not present\n", __FUNCTION__);
	}
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,19) )
	ret = queue_task((struct tq_struct *)task, &tq_mdmthrd);
#else
	{
		struct tq_struct *bh_pointer = (struct tq_struct *)task;
		task_queue *bh_list = &tq_mdmthrd;

		ret = 0;
		if (!test_and_set_bit(0,&bh_pointer->sync)) {
			unsigned long flags;
			spin_lock_irqsave(&tqueue_lock, flags);
			bh_pointer->next = *bh_list;
			*bh_list = bh_pointer;
			spin_unlock_irqrestore(&tqueue_lock, flags);
			ret = 1;
		}
	}
#endif
	wake_up(&mdmthrd_wait);
	return ret;
}

/* lock_kernel() must be called before this function */
static void thrd_daemonize(void)
{
#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) )
	exit_files(current);  /* daemonize doesn't do exit_files */
	current->files = init_task.files;
	atomic_inc(&current->files->count);
#endif
#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) ) && !defined(daemonize)
	{
		struct fs_struct *fs;

		exit_mm(current);

		current->session = 1;
		current->pgrp = 1;

		exit_fs(current);	/* current->fs->count--; */
		fs = init_task.fs;
		current->fs = fs;
		atomic_inc(&fs->count);
	}
#else
	daemonize();
#endif

#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10) ) && \
		( LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) )
	reparent_to_init();
#endif
}

#ifndef DECLARE_COMPLETION
static int mdmthrd_running;
#endif

#ifdef for_each_process
#define sigmask_lock sighand->siglock
#define recalc_sigpending_curtask(t) recalc_sigpending()
#else
#define sighand sig
#define recalc_sigpending_curtask(t) recalc_sigpending(t)
#endif

static int mdmthrd(void *startup)
{
  	register struct task_struct *curtask = current;

	lock_kernel();

	/*  this thread doesn't need any user-level access,
	 *  so get rid of all our resources.
	 */
	thrd_daemonize();

	strncpy(curtask->comm, __FUNCTION__, sizeof(curtask->comm));

	unlock_kernel();

	spin_lock_irq(&curtask->sigmask_lock);
	sigemptyset(&curtask->blocked);
	flush_signals(curtask);
	{ // flush_signal_handlers
		int i;
		struct k_sigaction *ka = &curtask->sighand->action[0];
		for (i = _NSIG ; i != 0 ; i--) {
			if (ka->sa.sa_handler != SIG_IGN)
				ka->sa.sa_handler = SIG_DFL;
			ka->sa.sa_flags = 0;
			sigemptyset(&ka->sa.sa_mask);
			ka++;
		}
	}
	recalc_sigpending_curtask(curtask);
	spin_unlock_irq(&curtask->sigmask_lock);

#ifdef DECLARE_COMPLETION
	complete((struct completion *)startup);
#else
	mdmthrd_running = 1;
#endif

	/* beware: this thread executes floating-point instructions! */

	wait_event_interruptible(mdmthrd_wait, (run_task_queue(&tq_mdmthrd), FALSE));

	spin_lock_irq(&curtask->sigmask_lock);
	flush_signals(curtask);
	recalc_sigpending_curtask(curtask);
	spin_unlock_irq(&curtask->sigmask_lock);

#ifdef DECLARE_COMPLETION
#if defined(complete_and_exit) || ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9) )
	complete_and_exit(&mdmthrd_exited, 0);
#else
	complete(&mdmthrd_exited);
	up_and_exit(NULL, 0);
#endif
#else
	mdmthrd_running = 0;
	return 0;
#endif
}

static INT32 ntimers;

/********************************************************************/
/* Construct a periodic time out instance.RunTime manager does not	*/
/* simply use global timers , since these are called with the		*/
/* same thread handle as the ring3 application. This prevent		*/
/* synchronization between passive and async calls with semaphores	*/
/* or mutexes.                                                      */
/* Parameters :                                                     */
/* Irql - run time IQRL in which the timeout call back              */  
/*        should be called. In Wi95 this parameter in ignored,      */
/*        and all timeout are called at dispatch level.             */
/* InitialTimeout - Initial timeout interval in ms pRefData         */
/*                  should be passed as a parameter to this function*/
/* PTimeOutCallBack - CallBack to be call every timeout duration.   */
/* pFuncAlloc - alloc function,pRefData should be passed as         */ 
/*              parameter for thids function. function should       */
/*              allocate memory in non-paged pool                   */
/* pFuncFree - free function,pRefData should be passed as           */
/*             parameter for thids function                         */
/* pRefData -  reference data to be passed to pTimeOutCallBack      */
/*             pFuncAllc and pFuncFree                              */
/********************************************************************/
// @@@@ Irql unreferenced

GLOBAL	HANDLE	OSCreatePeriodicTimeOut	(	IN	TIMER_IRQL_TYPE	Irql,
											IN	UINT32			InitialTimeOut,
											IN	PCBFUNC			pTimeOutCallBack,
											IN	PALLOC_FUNC		pFuncAlloc,
											IN	PFREE_FUNC		pFuncFree,
											IN	PVOID			pRefData)
{
	PTIME_OUT_INSTANCE_T	pTimeOutInstance;
	if(pFuncAlloc) {
		pTimeOutInstance= pFuncAlloc(sizeof(TIME_OUT_INSTANCE_T), pRefData);
	} else {
		ASSERT(OSContextAllowsSleeping());
		pTimeOutInstance= kmalloc(sizeof(TIME_OUT_INSTANCE_T), GFP_KERNEL);
	}
	if (NULL == pTimeOutInstance )
		return(NULL);
	pTimeOutInstance->pFuncFree			= pFuncFree;
	pTimeOutInstance->pRefData			= pRefData;
	pTimeOutInstance->pTimeOutCallBack	= pTimeOutCallBack;
	pTimeOutInstance->bLocked			= FALSE;
	pTimeOutInstance->mSec				= InitialTimeOut;
 
	INIT_TQUEUE(&pTimeOutInstance -> TaskQueue, TimerThreadFunction, pTimeOutInstance);

	init_timer(&pTimeOutInstance -> Timer);
	pTimeOutInstance -> Timer.function = TimeOutHandler;
	pTimeOutInstance -> Timer.data = (unsigned long)pTimeOutInstance;
	pTimeOutInstance -> Timer.expires = jiffies + MSECS_TO_TICKS(100);
	pTimeOutInstance -> SchedCount = 0;

	if(OsAtomicIncrement(&ntimers) == 1) {
#ifdef COMPLETION_INITIALIZER
		static struct completion startup = COMPLETION_INITIALIZER(startup);
#endif

		MOD_INC_USE_COUNT;
#ifdef COMPLETION_INITIALIZER
		mdmthrd_pid = kernel_thread(mdmthrd, &startup, 0);
		wait_for_completion(&startup);
#else
		mdmthrd_running = 0;
		mdmthrd_pid = kernel_thread(mdmthrd, NULL, 0);
		{
			int i;

			/* wait 5 seconds maximum for modem thread to start */
			for (i=5000; !mdmthrd_running && (i > 0); i -= 50) {
				OsSleep(50);
			}
			if(i <= 0) {
				printk(KERN_ERR"%s: giving up on modem thread\n", __FUNCTION__);
			}
		}
#endif

		if(mdmthrd_pid <= 0) {
			MOD_DEC_USE_COUNT;
			OsAtomicDecrement(&ntimers);
			if(pTimeOutInstance->pFuncFree) {
				pTimeOutInstance->pFuncFree(pTimeOutInstance,pTimeOutInstance->pRefData);
			} else {
				kfree(pTimeOutInstance);
			}
			return NULL;
		}
	}

	if(InitialTimeOut != 0) {
		pTimeOutInstance -> active = TRUE;
		add_timer(&(pTimeOutInstance -> Timer));
	} else
		pTimeOutInstance -> active = FALSE;

	return( (HANDLE)pTimeOutInstance);
}

/********************************************************************/
GLOBAL	VOID	OSDestroyPeriodicTimeOut	(IN	HANDLE	hTimeOut)
{
	PTIME_OUT_INSTANCE_T	pTimeOutInstance = (PTIME_OUT_INSTANCE_T)hTimeOut;

	ASSERT(OSContextAllowsSleeping());

	pTimeOutInstance -> bLocked	= TRUE;
	pTimeOutInstance -> active = FALSE;

#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) )
	del_timer_sync(&pTimeOutInstance -> Timer);
#else
	del_timer(&pTimeOutInstance -> Timer);
#endif

	while(pTimeOutInstance->SchedCount > 0) {
	    schedule();
	}

	if(OsAtomicDecrement(&ntimers) == 0) {
		if(mdmthrd_pid > 0) {
			int r = kill_proc(mdmthrd_pid, SIGKILL, 1);
			if(r) {
				printk(KERN_ERR"%s: kill_proc mdmthrd_pid=%d r=%d\n", __FUNCTION__, mdmthrd_pid, r);
			}
#ifdef DECLARE_COMPLETION
			wait_for_completion(&mdmthrd_exited);
#else
			{
				int i;

				/* wait 5 seconds maximum for modem thread to terminate */
				for (i=5000; mdmthrd_running && (i > 0); i -= 50) {
					OsSleep(50);
				}
				if(i <= 0) {
					printk(KERN_ERR"%s: giving up on modem thread\n", __FUNCTION__);
				}
			}
#endif
			MOD_DEC_USE_COUNT;
		}
	}

	if(pTimeOutInstance->pFuncFree) {
		pTimeOutInstance->pFuncFree(pTimeOutInstance,pTimeOutInstance->pRefData);
	} else {
		kfree(pTimeOutInstance);
	}

}
/********************************************************************/
GLOBAL	BOOL	OSSetPeriodicTimeOut		(	IN	HANDLE			hTimeOut,
												IN	UINT32			NewTimeOut)
{
	PTIME_OUT_INSTANCE_T	pTimeOutInstance = (PTIME_OUT_INSTANCE_T)hTimeOut;

	pTimeOutInstance->mSec = NewTimeOut;
	if(NewTimeOut != 0) {
		pTimeOutInstance -> active = TRUE;
		mod_timer(&pTimeOutInstance -> Timer, jiffies + MSECS_TO_TICKS(pTimeOutInstance -> mSec));
	} else {
		pTimeOutInstance -> active = FALSE;
		del_timer(&pTimeOutInstance -> Timer);
	}
	return TRUE;
}

static time_t epoch = 0;

/********************************************************************/
GLOBAL	UINT32	OSGetSystemTime(			VOID	)
{
	struct timeval timestamp;
	do_gettimeofday(&timestamp);
	// result returned in milliseconds
	return ((UINT32)(((timestamp.tv_sec-epoch)*1000) + (timestamp.tv_usec/1000)));
}

/********************************************************************/

HANDLE OSCreateTimer(UINT32 msec, PVOID pCBFunc, PVOID pRefData)
{
	PTIMER_T pTimer;

	if ( pCBFunc == NULL )
	{
		ASSERT(pCBFunc);
		return (NULL);
	}

	ASSERT(OSContextAllowsSleeping());
	pTimer = kmalloc(sizeof(TIMER_T),GFP_KERNEL);
	if ( NULL == pTimer )
	{
		ASSERT(pTimer);
		return (NULL);
	}

	memset(pTimer,0,sizeof(TIMER_T));

	init_timer(&(pTimer->timer));
	pTimer->timer.function = pCBFunc ;
	pTimer->timer.data = (unsigned long)pRefData;
	pTimer->timer.expires = jiffies + MSECS_TO_TICKS(msec);
	pTimer->msec = msec;
	return ((HANDLE)pTimer);
}

void OSSetTimer(PVOID pTimer)
{
	struct timer_list*	pTimerH;
	PTIMER_T pTimerS = (PTIMER_T)pTimer;
	if ( pTimer == NULL )
	{
		ASSERT(pTimer);
		return;
	}
	pTimerH = &(pTimerS->timer);
	mod_timer(pTimerH, jiffies + MSECS_TO_TICKS(pTimerS->msec));
}

void OSCancelTimer(PVOID pTimer)
{
	struct timer_list*	pTimerH;
	if ( pTimer == NULL )
	{
		ASSERT(pTimer);
		return;
	}
	pTimerH = &((PTIMER_T)pTimer)->timer;
	
	del_timer(pTimerH);
}

void OSChangeTimerTimeOut(PVOID pTimer, UINT32 msec)
{
	PTIMER_T pTimerS = (PTIMER_T)pTimer;
	if ( pTimer == NULL )
	{
		ASSERT(pTimer);
		return;
	}
	pTimerS->msec = msec;
}

void OSDestroyTimer(PVOID pTimer)
{
	if ( pTimer == NULL )
	{
		ASSERT(pTimer);
		return;
	}

#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) )
	del_timer_sync(&((PTIMER_T)pTimer)->timer);
#else
	del_timer(&((PTIMER_T)pTimer)->timer);
#endif
	kfree(pTimer);
}

/********************************************************************/
/* Utilities                                                        */
/********************************************************************/

static spinlock_t atomic_lock __attribute__((unused)) = SPIN_LOCK_UNLOCKED;

/****************************************************************************************
    The OsAtomicCompareAndSwap function compares the value at the specified address with 
	oldVal. The value of newValue is written to the address only if oldValue and the 
	value at the address are equal. OSCompareAndSwap returns true if newValue is written 
	to the address; otherwise, it returns false. 
    
    Params:
        oldValue The value to compare at address.
        newValue The value to write to address if oldValue compares true.
        address The 4-byte aligned address of the data to update atomically.
    Result: true if newValue was written to the address. 
****************************************************************************************/
BOOL OsAtomicCompareAndSwap (PVOID oldValue, PVOID newValue, PVOID* address)
{
#ifdef __HAVE_ARCH_CMPXCHG
	return (cmpxchg(address, oldValue, newValue) == oldValue);
#else
	unsigned long flags;

	spin_lock_irqsave(&atomic_lock, flags);
  	if (*(PUINT32)address ==  (UINT32)oldValue)
  	{
		*(PUINT32)address = (UINT32)newValue;
		spin_unlock_irqrestore(&atomic_lock, flags);
  		return TRUE;
  	}
	spin_unlock_irqrestore(&atomic_lock, flags);

	return FALSE;
#endif
}
/****************************************************************************************
    OsAtomicAdd
   
    OsAtomicAdd function adds the specified amount to the value at the specified 
    address and returns the result.
    Params:
        amount  The amount to add.
        address The 4-byte aligned address of the value to update atomically.
    Result: The result of the addition.
****************************************************************************************/
INT32	OsAtomicAdd   (INT32 amount, PINT32 address)
{
	unsigned long flags;
	atomic_t *v = (atomic_t *)address;

	spin_lock_irqsave(&atomic_lock, flags);
	atomic_add(amount, v);
	amount = atomic_read(v);
	spin_unlock_irqrestore(&atomic_lock, flags);

	return amount;
}

INT32	OsAtomicIncrement (PINT32 address)
{
	unsigned long flags;
	atomic_t *v = (atomic_t *)address;
	INT32 amount;

	spin_lock_irqsave(&atomic_lock, flags);
	atomic_inc(v);
	amount = atomic_read(v);
	spin_unlock_irqrestore(&atomic_lock, flags);

	return amount;
}

INT32	OsAtomicDecrement (PINT32 address)
{
	unsigned long flags;
	atomic_t *v = (atomic_t *)address;
	INT32 amount;

	spin_lock_irqsave(&atomic_lock, flags);
	atomic_dec(v);
	amount = atomic_read(v);
	spin_unlock_irqrestore(&atomic_lock, flags);

	return amount;
}

void OsSleep(UINT32 ms)
{
	if(!OSContextAllowsSleeping()) {
		printk(KERN_ERR"%s(%lu): shouldn't sleep in this context!\n", __FUNCTION__, ms);
		ASSERT(OSContextAllowsSleeping());
		mdelay(ms);
		return;
	}

	if(ms > TICKS_TO_MSECS(1)) {
		UINT32 start, end;
		long timeout = MSECS_TO_TICKS(ms);
		//long ort = timeout;

		start = OSGetSystemTime();
		do {
			set_current_state(TASK_UNINTERRUPTIBLE);
		} while ((timeout = schedule_timeout(timeout)));
		end = OSGetSystemTime() - start;
		if(end < ms) {
			//printk(KERN_ERR"OsSleep: short timeout; wanted %ld got %ldms ort=%ld\n", ms, end, ort);
			OsSleep(end);
		}
		//else if(end > ms) {
		//	printk(KERN_ERR"OsSleep: timeout %ldms (%ldms too much) ort=%ld\n", ms, end - ms, ort);
		//}
	} else {
		// short delay
		mdelay(ms);
	}
}

DWORD OsGlobalProcessorFreq;

tMuSecTimeStamp OsGetMuSecStartStamp(void)
{
  tMuSecTimeStamp  StartCnt;
  rdtscl(StartCnt);
  if(0==StartCnt)
        StartCnt=1;

  return StartCnt;
}

tMuSecTimeStamp OsDeltaTimeMuSec(tMuSecTimeStamp PrevStamp, tMuSecTimeStamp NextStamp)
{
  tMuSecTimeStamp Delta=0;
  if(PrevStamp)
  {
    if(0==NextStamp)
    {
      NextStamp=OsGetMuSecStartStamp();
    }

    Delta=NextStamp - PrevStamp;
  
    Delta/=OsGlobalProcessorFreq;
  }
  return Delta;

}

void OsBusyLoopMuSec(DWORD DelayInMicroSecs)
{
  tMuSecTimeStamp StartCnt=OsGetMuSecStartStamp();
  while( OsDeltaTimeMuSec(StartCnt, 0) < (tMuSecTimeStamp)DelayInMicroSecs );
}


void OsBusyLoopMilliSec(DWORD DelayInMilliSecs)
{
    int Cnt;

    for(Cnt=0;Cnt<1000;Cnt++)
	OsBusyLoopMuSec(DelayInMilliSecs);
}



static atomic_t locksHeld;

void OsObtainedLock(void)
{
	atomic_inc(&locksHeld);
}

void OsReleasedLock(void)
{
	atomic_dec(&locksHeld);
}

int OsLocksHeld(void)
{
	return atomic_read(&locksHeld);
}

/* Semaphores */
/********************************************************************/
GLOBAL HANDLE	OSSemaphoreCreate(	IN	int InitCount)
{
	struct semaphore *pSemaphore;
	
	ASSERT(OSContextAllowsSleeping());
	pSemaphore = kmalloc(sizeof(struct semaphore), GFP_KERNEL);
	if (NULL == pSemaphore)
	{
		ASSERT(pSemaphore);
		return (NULL);
	}
	sema_init(pSemaphore, InitCount);
	return ((HANDLE)pSemaphore);
}

/********************************************************************/
GLOBAL VOID	OSSemaphoreDestroy(		IN	HANDLE hSemaphore)
{
	if (NULL != hSemaphore)
		kfree((void *)hSemaphore);
}

/********************************************************************/
GLOBAL VOID	OSSemaphoreWait(		IN	HANDLE hSemaphore)
{
	struct semaphore *pSemaphore = (struct semaphore *)hSemaphore;
	if (NULL == hSemaphore) {
		ASSERT(hSemaphore);
		return;
	}
	ASSERT(OSContextAllowsSleeping());
	down(pSemaphore);
}

/********************************************************************/
GLOBAL VOID	OSSemaphoreSignal(		IN	HANDLE hSemaphore)
{
	struct semaphore *pSemaphore = (struct semaphore *)hSemaphore;
	if (NULL == hSemaphore) {
		ASSERT(hSemaphore);
		return;
	}
	up(pSemaphore);
}

/* Critical Section (Mutex) */
/********************************************************************/
typedef struct {
	spinlock_t spinlock;
	unsigned long flags;
#ifdef DEBUG
	void *pmutex, *pfrom;
#endif
	void *owner;
	unsigned long nestcnt;
} CRIT_T;

GLOBAL HANDLE __OSCriticalSectionCreate( char *file, int line )
{
    CRIT_T *mutex;

	ASSERT(OSContextAllowsSleeping());
    mutex  = kmalloc(sizeof(CRIT_T), GFP_KERNEL);
	if(!mutex) {
		ASSERT(mutex);
		return NULL;
	}

	spin_lock_init(&mutex->spinlock);
#ifdef DEBUG
	mutex->pmutex = NULL;
	mutex->pfrom = NULL;
#endif
	mutex->owner = NULL;
	mutex->nestcnt = 0;

    return ((HANDLE)mutex);
}

/********************************************************************/
GLOBAL VOID	OSCriticalSectionDestroy(	IN	HANDLE hMutex)
{
	if ( NULL == hMutex )
	{
		ASSERT(hMutex);
		return;
	}
	kfree((CRIT_T *)hMutex);
}

#ifdef DEBUG
static CRIT_T *pmutex;
static void *pfrom;
#endif

/********************************************************************/
GLOBAL VOID	OSCriticalSectionAcquire(	IN	HANDLE hMutex)
{
	CRIT_T *mutex = (CRIT_T *) hMutex;
	unsigned long flags;

#ifdef local_irq_save
	local_irq_save(flags);
#else
	save_flags(flags);
	cli();
#endif
	if(!spin_trylock(&mutex->spinlock)) {
		if(!in_interrupt() && mutex->owner == current) {
			mutex->nestcnt++;
#ifdef local_irq_restore
			local_irq_restore(flags);
#else
			restore_flags(flags);
#endif
			return;
		}
		spin_lock(&mutex->spinlock);
	}
	mutex->flags = flags;
#ifdef DEBUG
	mutex->pmutex = pmutex;
	mutex->pfrom = pfrom;
	pmutex = mutex;
	pfrom = __builtin_return_address(0);
#endif
	if(!in_interrupt())
		mutex->owner = current;
	else
		mutex->owner = NULL;
	OsObtainedLock();
}

/********************************************************************/
GLOBAL VOID	OSCriticalSectionRelease(	IN	HANDLE hMutex)
{
	CRIT_T *mutex = (CRIT_T *) hMutex;

#ifdef DEBUG
	ASSERT(pmutex == mutex);
	pmutex = mutex->pmutex;
	pfrom  = mutex->pfrom;
#endif
	if(mutex->nestcnt) {
		mutex->nestcnt--;
		return;
	}

	mutex->owner = NULL;
	spin_unlock_irqrestore(&mutex->spinlock, mutex->flags);
	OsReleasedLock();
}

/********************************************************************/
GLOBAL	VOID	OSSetTimeSensitivity(	IN	UINT32				Interval)			
{
}
/********************************************************************/
GLOBAL	VOID	OSRestoreTimeSensitivity(		IN	UINT32				Interval)
{
}

/********************************************************************/

GLOBAL	VOID	OSInitTime(			VOID	)
{
	struct timeval timestamp;
	do_gettimeofday(&timestamp);

	epoch = timestamp.tv_sec;

	OsGlobalProcessorFreq = LinuxCalcCpuRate()/1000000;
}

