////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2002 Xilinx, Inc.  All rights reserved.
//
// Xilinx, Inc.
// XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A 
// COURTESY TO YOU.  BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS
// ONE POSSIBLE   IMPLEMENTATION OF THIS FEATURE, APPLICATION OR 
// STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION
// IS FREE FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE 
// FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION.  
// XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO 
// THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO 
// ANY WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE 
// FROM CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY 
// AND FITNESS FOR A PARTICULAR PURPOSE.
// 
//
// $Id: sema.c,v 1.1.2.2 2003/04/01 00:42:30 mohan Exp $
//
//////////////////////////////////////////////////////////////////////////////

/***************************************************************************/
/**
 * @file sema.c 
 * 
 * Semaphore handling routines.
 *   - Restriction of this implementation :: The wait queue is a FIFO, 
 *     should be priority based, in case the processes have priorities.
 ***************************************************************************/
#include <sys/sema.h>
#include <sys/process.h>

#ifdef CONFIG_SEMA	/* Include the functions only if configured by user */

/**
 * Array of Semaphore structures. They are used to keep track of allocated
 * semaphore's. Also used for other bookkeeping operations on Semaphore.
 */
semaphore sema_heap[MAX_SEMA] ;	/* Memory for allocating semaphores */

/**************************************************************************/
/**
 * Initilize the semaphores.
 * - Intialize the semaphore data structure
 * - Allocate the memory for the semaphore Queue's
 *
 * @return
 * 	None
 * 	
 * @note
 * 	None
 * 
 ***************************************************************************/
void sema_heap_init()
{
  unsigned int i = 0 ;
  for( ; i < MAX_SEMA; i++ ){
    sema_heap[i].sema_id = -1 ;
    alloc_q( &sema_heap[i].wait_q, MAX_SEMA_WAITQ,
             SEMA_Q, sizeof(char), i ) ;
  }
}


/**************************************************************************/
/**
 * Initialise and create the semaphore.
 * - Allocate storage for semaphore from the memory location.
 * - Assign a semaphore ID, initilise the count.
 *
 * @param	sema is the semaphore structure which is returned when a new
 * 		semaphore is created.
 * @param	count is the resource count for the semaphore
 * 
 * @return
 * - sema is assigned the new semaphore structure on success. 0 is returned
 * - -1 if Error.
 *   
 * @note
 * 	None
 * 
 ***************************************************************************/
int sys_sema_init( semaphore **sema, int count ) 
{
  unsigned int i = 0 ;
  //enter_kernel() ;
  for( ; i < MAX_SEMA; i++ ){
    if( sema_heap[i].sema_id == -1 ){
      if(count < 0 ) 
        return -1 ;
      sema_heap[i].sema_id = i ;
      sema_heap[i].count = count ;
      qinit( &sema_heap[i].wait_q ) ;
      *sema = &sema_heap[i] ;
      return 0 ;
    }
  }
  //exit_kernel() ;
  return -1 ;
}


/**************************************************************************/
/**
 * Semaphore Wait - Wait on the semaphpore resource.
 * - Decrement the resource count.
 * - If resource available, then allocate the resource for the process.
 * - Else Block the process
 *
 * @param 	sema is the semaphore structure returned by calling sema_init
 * 
 * @return
 * - 0 on Success
 * - -1 on Error
 *   
 * @note
 * 	None
 * 
 ***************************************************************************/
int sys_sema_wait( semaphore *sema ) 
{
  if( sema->sema_id == -1 ) return -1 ;
  // if the process is already in a wait state, do nothing
  if (process_is_waiting())
    return -1;
  sema->count -- ;		/* Decrement the resource count */
  if( sema->count < 0 ) {		/* Is the resource available */
    /* If not available, block process */
    //enter_kernel() ;
    process_block( &sema_heap[sema->sema_id].wait_q, PROC_WAITING ) ;
#ifdef PPC_XILKERNEL
    // process_block returns immediately after changing the process state
    // return -1 immediately to caller
    return -1;
#endif
  }
  return 0 ;
}
int sys_sema_wait_blocked( semaphore *sema ) 
{
  if( sema->sema_id == -1 ) return -1 ;
  // if the process is already in a wait state, do nothing
  if (process_is_waiting())
    return -1;
  // sema count was decremented already so dont do it again
  // sema->count -- ;		/* Decrement the resource count */
  // this check should not succeed
  if( sema->count < 0 ) {		/* Is the resource available */
    /* If not available, block process */
    //enter_kernel() ;
    process_block( &sema_heap[sema->sema_id].wait_q, PROC_WAITING ) ;
#ifdef PPC_XILKERNEL
    // process_block returns immediately after changing the process state
    // return -1 immediately to caller
    return -1;
#endif
  }
  return 0 ;
}


/**************************************************************************/
/**
 * Semaphore trywait - Try to get the semaphore resource. (non-blocking)
 * - If the resource is available, then get the resource.
 * - Else return -1 
 *
 * @param 	sema is the semaphore structure returned by calling sema_init
 * 
 * @return
 * - 0 on Success
 * - -1 on Error
 * 	
 * @note
 * 	None
 * 
 ***************************************************************************/
int sys_sema_trywait( semaphore *sema ) 
{
  if( sema->sema_id == -1 ) return -1 ;
  if( sema->count > 0 ) {
    sema->count-- ;
    return 0 ;
  }
  return -1 ;
}


/**************************************************************************/
/**
 * Free the semaphore resource or signal the availability of the resource.
 * - Increment the resource count.
 * - If process waiting on this resource, then unblock the process.
 *
 * @param 	sema is the semaphore structure returned by calling sema_init
 * 
 * @return
 * - 0 on Success
 * - -1 on Error
 * 	
 * @note
 * 	None
 * 
 ***************************************************************************/
int sys_sema_post( semaphore *sema ) 
{
  if( sema->sema_id == -1 ) return -1 ;
  sema->count ++ ;
  if( sema->count <= 0 ){
    process_unblock( &sema_heap[sema->sema_id].wait_q ) ;
  }
  return 0 ;
}


/**************************************************************************/
/**
 * Release the semaphore
 *
 * @param 	sema is the semaphore structure returned by calling sema_init
 * 
 * @return
 * - 0 on Success
 * - -1 on Error
 * 	
 * @note
 * 	None
 * 
 ***************************************************************************/
int sys_sema_destroy( semaphore *sema ) 
{
  if( sema->sema_id == -1 ) return -1 ;
  if( sema->count <= 0 ) return -1 ;	/* ?? */
  sema->sema_id = -1 ;
  return 0 ;
}

#endif	/* CONFIG_SEMA */
