////////////////////////////////////////////////////////////////////////////
//
// 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: msg.c,v 1.1.2.2 2003/04/01 00:42:31 mohan Exp $
//
//////////////////////////////////////////////////////////////////////////////

/**************************************************************************/
/**
 * @file msg.c 
 *
 * This contains functions handling the Message Queue functionality
 * 
 ***************************************************************************/
#include <sys/msg.h>
//#include <xparameters.h>

#ifdef CONFIG_MSGQ	/* Include only if CONFIG_MSGQ defined */

/**
 * Array of Message Queue structures. They are used to keep track of allocated
 * message queue's. Also used for other bookkeeping operations on message q.
 */
struct msgid_ds msgq_heap[N_MSGQ] ;

/**************************************************************************/
/**
 * Create a new Message Queue associated with a key.
 * - A new message queue is created if none with the given key exists.
 * - If flag = IPC_CREAT, then existing mesg queue ID for the given key is 
 *   returned.
 * - If flag = IPC_EXCL, if message queue exists for the given key return -1
 *
 * @param	key is used to uniquely identify the Message queue.
 * @param	msgsize is the size of the Message.
 * @param	maxmsg is the maximum number of messages in the queue.
 * @param	flag used to identify IPC options.
 * 
 * @return
 * - unique message queue ID is returned on Success.
 * - -1 if Error
 *   
 * @note
 * 	None
 * 
 ***************************************************************************/
int sys_msgget( unsigned int key, unsigned int msgsize, unsigned int maxmsg, unsigned int flag ) 
{
  unsigned int i = 0 ;
  int msgid = -1 ;
  struct msgid_ds *msgds ;
	
  if( (msgsize > MAX_MSGSIZE) || (maxmsg > MAX_MSG) )
    return -1 ;
  //enter_kernel() ;
  msgds = msgq_heap ;
  for( ; i < N_MSGQ; i++ ){
    if(msgds->msgid != -1){		/* Check for key match */
      if(msgds->key == key)
        break ;
    } else if((msgid == -1)&&(msgds->msgsize == msgsize)
              &&(msgds->maxmsg == maxmsg))
      msgid = i ;	/* Store the first free msgq block */
    msgds++ ;
  }
  if( i != N_MSGQ ){		/* MsgQ with the key exists */
    if( flag == IPC_CREAT) {
      return i ;
    }else /* IPC_EXCL */
      return -1 ;
  } else if( msgid == -1 ) 
    return -1 ;
  msgds = &msgq_heap[msgid] ;
  /*
   * Initialize the new MesgQ structure.
   */
  msgds->msgid = msgid ;
  msgds->key = key ;
  sys_sema_init(&msgds->full, maxmsg ) ;
  sys_sema_init(&msgds->empty, 0 ) ;
  qinit( &msgds->msg_q ) ;
  //exit_kernel() ;
  return msgid ;
}


/**************************************************************************/
/**
 * Control the Message Queue.
 * - If cmd = IPC_STAT, then return the MesgQ status in buf.
 * - If cmd = IPC_RMID, then remove the MesgQ. the msgid is set to -1.
 *
 * @param	msgid is the Message Queue ID got from msgget.
 * @param	cmd is the command to the control function.
 * @param	buf is the buffer where the status is returned
 * 
 * @return
 * - 0 is returned on Success. Status is returned in buf for IPC_STAT.
 * - -1 on Error.
 *   
 * @note
 * 	None
 * 
 ***************************************************************************/
int sys_msgctl( unsigned int msgid, unsigned int cmd, struct msgid_ds *buf ) 
{
  struct msgid_ds *msgds ;
	
  if( msgq_heap[msgid].msgid == -1 ) 
    return -1 ;
  msgds = &msgq_heap[msgid] ;
  if( cmd == IPC_STAT ){
    buf->msgid = msgds->msgid ;
    buf->key = msgds->key ;
    buf->msgsize = msgds->msgsize ;
    buf->maxmsg = msgds->maxmsg ;
  } else if( cmd == IPC_RMID ){
    /* Signal all the waiting process..... */
    sys_sema_destroy( msgds->full ) ;
    sys_sema_destroy( msgds->empty ) ;
    msgds->msgid = -1 ;
  } else return -1 ;
  return 0 ;
}

/**************************************************************************/
/**
 * Send the Message Queue.
 * - If Message queue is not full then enqueue the message in the queue. 
 * - Signal the availability of message in the queue.
 * - If Message queue is full, then wait for queue space.
 * - If flag - IPC_NOWAIT, then return with -1, if the queue is full
 *
 * @param	msgid is the Message Queue ID got from msgget.
 * @param	msg is the messsage to send.
 * @param	nbytes is the size of the message.
 * @param 	flag is used to give options.
 * 
 * @return
 * - 0 on Success
 * - -1 on Error
 *   
 * @note
 * - nbytes is not used. The message size number of bytes specified during
 *   msgget is used for a message.
 * - flag is not used.
 * 
 ***************************************************************************/
int sys_msgsend( unsigned int msgid, const void *msg, unsigned int nbytes, unsigned int flag) 
{
  struct msgid_ds *msgds ;
	
  if( msgq_heap[msgid].msgid == -1 ) 
    return -1 ;
  msgds = &msgq_heap[msgid] ;
  /* Check if Queue is full */
  if( flag == IPC_NOWAIT ){
    if(sys_sema_trywait( msgds->full )) 
      return -1 ;
  } else if (flag == IPC_BLOCKED) {
    if (sys_sema_wait_blocked( msgds->full )) // ppc never waits, always returns -1
      return -1;
  } else {
    if (sys_sema_wait( msgds->full )) // ppc never waits, always returns -1
      return -1;
  }
  enq( &msgds->msg_q, msg, 0 ) ;	/* Enqueue message in queue */
  sys_sema_post( msgds->empty ) ;	/* Signal the consumer of mesg in Q */
  return 0 ;
}

/**************************************************************************/
/**
 * Receive the Message and returned in msg.
 * - If mesg Q is empty, then wait for the message.
 * - If flag = IPC_NOWAIT, then return -1 if queue is empty.
 * - If message available, it is copied to the msg.
 * - Decrement the message count in the Queue.
 *
 * @param	msgid is the Message Queue ID got from msgget.
 * @param	msg is the buffer where messsage is received.
 * @param	nbytes is the size of the message.
 * @param 	type is used to specify receive options or patterns.
 * @param 	flag is used to give options.
 * 
 * @return
 * - 0 on Success
 * - -1 on Error
 *   
 * @note
 * - nbytes is not used. The message size number of bytes specified during
 *   msgget is used for a message.
 * - flag is not used.
 * - type is not used.
 * 
 ***************************************************************************/
int sys_msgrecv( unsigned int msgid, void *msg, unsigned int nbytes, unsigned int type, unsigned int flag ) 
{
  struct msgid_ds *msgds ;
	
  if( msgq_heap[msgid].msgid == -1 ) 
    return -1 ;
  msgds = &msgq_heap[msgid] ;
  /* Check if the Queue is Empty */
  if( flag == IPC_NOWAIT ){
    if(sys_sema_trywait( msgds->empty )) 
      return -1 ;
  } else if (flag == IPC_BLOCKED) {
    if(sys_sema_wait_blocked( msgds->empty )) // ppc never waits, always returns -1
      return -1;
  } else {
    if(sys_sema_wait( msgds->empty )) // ppc never waits, always returns -1
      return -1;
  }
  deq( &msgds->msg_q, msg, 0 ) ;	/* Get the message in the Queue */
  sys_sema_post( msgds->full ) ;	/* Decrement the message count */
  return 0 ;
}

#endif
