/************************************************************************/
/* Copyright 2002 MBARI													*/
/************************************************************************/
/************************************************************************/
/* Summary	: Definitions for Semaphore library for OASIS5				*/
/* Filename : sem.c														*/
/* Author	: Robert Herlien (rah)										*/
/* Project	: OASIS5 Mooring Controller									*/
/* Revision: 1.0														*/
/* Created	: 06/15/2016, from OASIS3 sem.c								*/
/*																			*/
/* MBARI provides this documentation and code "as is", with no warranty,	*/
/* express or implied, of its quality or consistency. It is provided without*/
/* support and without obligation on the part of the Monterey Bay Aquarium	*/
/* Research Institute to assist in its use, correction, modification, or	*/
/* enhancement. This information should not be published or distributed to	*/
/* third parties without specific written permission from MBARI.			*/
/*																			*/
/************************************************************************/
/* Modification History:												*/
/* 10oct2002 rah - OASIS3 version created from OASIS sem.c				*/
/* 15jun2016 rah - OASIS5 version created from OASIS3 sem.c				*/
/* $Log$
 */
/************************************************************************/
/************************************************************************/
/* This module wraps the FreeRTOS semphr library functions in order to	*/
/* save a list of mutex semaphores.  This is used for 2 purposes:		*/
/* 1. When a task exits, we look at all mutexes and release any it owns */
/* 2. For display from the "SYSTEM" command								*/
/************************************************************************/

#include <mbariTypes.h>					/* MBARI type definitions		*/
#include <mbariConst.h>					/* MBARI constants				*/
#include <oasis.h>						/* OASIS controller definitions	*/
#include <debug.h>
#include <olist.h>						/* OASIS linked list definitions*/
#include <sem.h>						/* OASIS semaphore definitions	*/ 
#include <otask.h>						/* OASIS task definitions		*/
#include <FreeRTOS.h>					/* FreeRTOS definitions			*/
#include <semphr.h>						/* FreeRTOS semaphore functions	*/


/********************************/
/*		Module Local Data		*/
/********************************/

MLocal LstHead	semList;				/* List of all mutex  semaphores in system*/


/************************************************************************/
/* Function    : semLibInit												*/
/* Purpose     : Initialize this module									*/
/* Inputs      : None													*/
/* Outputs     : None													*/
/************************************************************************/
void semLibInit(void)
{
  list_init( &semList );

} /* semLibInit() */


/************************************************************************/
/* Function	   : semCreateMutex											*/
/* Purpose	   : Create a semaphore from a Semaphore buffer				*/
/* Inputs	   : Semaphore struct ptr, defined by sem.h					*/
/* Outputs	   : Returns same Sem ptr if successful, NULL if unsuccessful*/
/* Comment     : If successful, fills in sem->semHandle					*/
/************************************************************************/
SemID	semCreateMutex(Semaphore *sem)
{
	if ((sem->semHandle = xSemaphoreCreateMutexStatic(&(sem->semBuf))) == NULL)
		return(NULL);

	sem->semRecursive = SEM_NORM;
	sem->semOwner = NULL;
	sem->sem_name[0] = '\0';
	list_add(&semList, (Node *)sem);

	return(sem);
}

/************************************************************************/
/* Function	   : semCreateRecursiveMutex								*/
/* Purpose	   : Create a semaphore from a Semaphore buffer				*/
/* Inputs	   : Semaphore struct ptr, defined by sem.h					*/
/* Outputs	   : Returns same Sem ptr if successful, NULL if unsuccessful*/
/* Comment     : If successful, fills in sem->semHandle					*/
/************************************************************************/
SemID	semCreateRecursiveMutex(Semaphore *sem)
{
	if ((sem->semHandle = xSemaphoreCreateRecursiveMutexStatic(&(sem->semBuf))) == NULL)
		return(NULL);

	sem->semRecursive = SEM_RECURSIVE;
	sem->sem_name[0] = '\0';
	list_add(&semList, (Node *)sem);

	return(sem);
}


/************************************************************************/
/* Function	   : semTakeTmout											*/
/* Purpose	   : Take a semaphore, with a timeout						*/
/* Inputs	   : SemID, timeout in ticks								*/
/* Outputs	   : OK if got semaphore, ERROR if timed out				*/
/************************************************************************/
Errno	semTakeTmout(SemID sem, TickType_t ticksToWait)
{
	BaseType_t		rtn;

	if (sem->semHandle == NULL)
		return(ERROR);
	
	/* Show task as pending on a semaphore		*/
	vTaskSetThreadLocalStoragePointer(NULL, State, (void *)PENDING);
	vTaskSetThreadLocalStoragePointer(NULL, StateParm, sem);

	if (sem->semRecursive == SEM_NORM)
		rtn = xSemaphoreTake(sem->semHandle, ticksToWait);
	else
		rtn = xSemaphoreTakeRecursive(sem->semHandle, ticksToWait);

	sem->semOwner = xTaskGetCurrentTaskHandle();
	vTaskSetThreadLocalStoragePointer(NULL, State, (void *)READY);
	return(rtn ? OK : ERROR);
}


/************************************************************************/
/* Function	   : semGive												*/
/* Purpose	   : Give a semaphore										*/
/* Inputs	   : SemID													*/
/* Outputs	   : OK if semaphore was released; ERROR if not				*/
/************************************************************************/
MBool	semGive(SemID sem)
{
	if ((sem->semHandle == NULL) || (sem->semOwner != xTaskGetCurrentTaskHandle()))
		return(ERROR);
	
	if (sem->semRecursive == SEM_NORM)
		return(xSemaphoreGive(sem->semHandle) ? OK : ERROR);
	else
		return(xSemaphoreGiveRecursive(sem->semHandle) ? OK : ERROR);
}


/************************************************************************/
/* Function	   : semTaskExit											*/
/* Purpose	   : Unlock semaphores owned by exiting task				*/
/* Inputs	   : Task Handle of exiting task							*/
/* Outputs	   : None													*/
/************************************************************************/
void semTaskExit(TaskHandle_t tid)
{
	Reg Node		*np;
	SemID			sem;

	if (tid == NULL)
		tid = xTaskGetCurrentTaskHandle();

	if (lockListT(&semList, TICKS_PER_SEC/4))
	{
		for (np = list_first(&semList); np != NULLNODE; np = list_next(np))
		{
			sem = NODEtoSEM(np);
			if ((xSemaphoreGetMutexHolder(sem->semHandle) == tid) &&
				(tid == xTaskGetCurrentTaskHandle()))
				// Can only give mutex semaphore if we're owner
				while (uxSemaphoreGetCount(sem->semHandle) < 1)
					semGive(sem->semHandle);
		}

		unlockList(&semList);
	}

} /* semTaskExit() */


#ifdef DEBUG_SYSTEM_CMD

/************************************************************************/
/* Function	   : semGetSemList											*/
/* Purpose	   : Get list of all system semaphores						*/
/* Inputs	   : None													*/
/* Outputs	   : Ptr to sem list										*/
/************************************************************************/
LstHead *semGetSemList(void)
{
  return(&semList);

} /* semGetSemList */

#endif
