////////////////////////////////////////////////////////////////////////////
//
// 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: shm.c,v 1.1.2.1 2003/03/12 22:30:29 mohan Exp $
//
//////////////////////////////////////////////////////////////////////////////

/**************************************************************************/
/**
 * @file shm.c 
 *
 * This contains functions handling the shared memory functionality
 *
 ***************************************************************************/

#include <sys/shm.h>
#include <stdio.h>

#ifdef CONFIG_SHM	/* Include the functions only if CONFIG_SHM defined */

/**
 * Array of Shared Memory structures. They are used to keep track of allocated
 * Shared mem's. Also used for other bookkeeping operations on shared mem.
 */
struct shmid_ds shm_heap[N_SHM] ;

/**************************************************************************/
/**
 * Create a new Shared Memory associated with a key.
 * - A new shared memory is created if none with the given key exists.
 * - If flag = IPC_CREAT, then existing shared mem ID for the given key is 
 *   returned.
 * - If flag = IPC_EXCL, if shared mem exists for the given key return -1
 *
 * @param	key is used to uniquely identify the Shared Memory
 * @param	size is the size of the Shared Memory Segment.
 * @param	flag used to identify IPC options.
 * 
 * @return
 * - unique Shared Memory ID is returned on Success.
 * - -1 if Error
 *   
 * @note
 * 	None
 * 
 ***************************************************************************/
int sys_shmget( unsigned int key, unsigned int size, unsigned int flag ) 
{
  unsigned int i = 0; int shmid = -1 ;
  struct shmid_ds *shmds ;

  shmds = shm_heap ;
  for( ; i < N_SHM; i++ ) {
    if( shmds->shmid != -1 ){	/* Check for key match */
      if(shmds->key == key) 
        break ;
    } else if((shmid == -1)&&(shmds->size == size)) {
      shmid = i ; /* Something else cld be done here */
    }
    shmds++ ;
  }
  if( i != N_SHM ){		/* Shared mem with Key Exists */
    if( flag == IPC_CREAT ){
      return i ;
    } else 	/* IPC_EXCL */
      return -1 ;
  } else if( shmid == -1 )
    return -1 ;
  /*
   * Initialize the new shared mem structure
   */
  shmds = &shm_heap[shmid] ;
  shmds->shmid = shmid ;
  shmds->key = key ;
  shmds->nattach = 0 ;
  return shmid ;
}

/**************************************************************************/
/**
 * Control the Shared memory.
 * - If cmd = IPC_STAT, then return the shared mem status in buf.
 * - If cmd = IPC_RMID, then remove the shared mem. the shmid is set to -1.
 *
 * @param	shmid is the Shared Mem ID got from shmget.
 * @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_shmctl( unsigned int shmid, unsigned int cmd, struct shmid_ds *buf ) 
{
  struct shmid_ds *shmds ;

  if( shm_heap[shmid].shmid == -1 )
    return -1 ;
  shmds = &shm_heap[shmid] ;
  if( cmd == IPC_STAT ) {
    buf->shmid = shmds->shmid ;
    buf->key = shmds->key ;
    buf->size = shmds->size ;
    buf->shmem = shmds->shmem ;
  } else if( cmd == IPC_RMID ){
    if( !shmds->nattach )
      shmds->shmid = -1 ;
    /* Else, nothing happens... But the user can still access */
    /* the shared mem if he wants to :-(			  */
  } else return -1 ;
  return 0 ;
}

/**************************************************************************/
/**
 * Attach the shared memory.
 * - Return the start address of the shared memory location.
 *
 * @param	shmid is the Shared Mem ID got from shmget.
 * @param	flag used to identify IPC options.
 * 
 * @return
 * 	None
 * 	
 * @note
 * 	None
 * 
 ***************************************************************************/
void *sys_shmat( unsigned int shmid, void *addr, unsigned int flag ) 
{
  if( shm_heap[shmid].shmid == -1 )
    return NULL;
  shm_heap[shmid].nattach++ ; 	
  return (void*)(shm_heap[shmid].shmem) ;
}

/**************************************************************************/
/**
 * Detach the shared memory segment from the user process. The segment is
 * not removed from the system and can be attached later.
 * - Decrement the attachment count for the shared memory.
 *
 * @param 	addr is the shared memory address got from shmat
 * 
 * @return
 * - 0 on Success.
 * - If invalid addr, then return -1.
 *   
 * @note
 * 	None
 * 
 ***************************************************************************/
int sys_shmdt( void *addr ) 
{
  struct shmid_ds *shmds ;
  unsigned int i = 0 ;

  shmds = shm_heap ;
  for( ; i < N_SHM; i++ ) {
    if( (char *)shmds->shmem == (char *)addr )
      break ;
    shmds++ ;
  }
  if( i == N_SHM )
    return -1 ;
  else 
    shmds->nattach-- ;
  return 0 ;
}

#endif		/* CONFIG_SHM */
