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

/**************************************************************************/
/**
 * @file mem.c  
 *
 * This contains the Memory allocation routines. 
 * 
 * - All the memory initialization routines for various system modules is 
 *   defined here. The memory for all the modules are allocated statically,
 *   the size of which can be configured by the user. This can be specified
 *   in structures defined in sys/init.c. Based on this the total memory
 *   requirements for each module is calculated.
 *
 * - This also contains routines that allocate thread context and 
 *   buffer management.
 * 
 ***************************************************************************/
#include <stdio.h>
#include <sys/mem.h>
#include <sys/init.h>
#include <sys/shm.h>
#include <sys/msg.h>

/**
 * Memory allocated for storing Process IDs. This memory is used for Ready
 * Queue and Semaphore Queue. The memory is split as follows:
 * -# Memory for Ready Queue's.
 * -# Memory for Semaphore Queue's.
 */
char pid_queue_mem[PID_QUEUE_MSIZE] ;
extern struct _process_init start_p[] ;

#ifdef CONFIG_MSGQ
/**
 * Memory allocated for storing Message Queues. The total memory is user
 * customizable.
 */
char msg_queue_mem[MSG_QUEUE_MSIZE] ;
extern struct _msgq_init msgq_config[] ;
extern struct msgid_ds msgq_heap[] ;
#endif

#ifdef CONFIG_SHM
/**
 * Memory allocated for Shared Memory modules. The total memory is user 
 * customizable.
 */
char shm_mem[SHM_MSIZE] ;
extern struct _shm_init shm_config[] ;
extern struct shmid_ds shm_heap[] ;
#endif

#ifdef CONFIG_THREAD_SUPPORT
/**
 * Total Memory allocated for thread context. This depends on the number of
 * threads in the system and size of the thread context. This is user
 * customizable.
 */
char thread_bss_mem[THREAD_BSS_MSIZE] ;
/**
 * Array of table used to keep track of Memory ID for the allocated thread
 * context.
 * 0 - unallocated
 * 1 - allocated
 */
char thread_bss_memid[MAX_THREADS] ;	
#endif

#ifdef CONFIG_MALLOC
/**
 * Total memory allocated for buffer management. The memory is divided among
 * different memory sized blocks based on the user configuration.
 */
char malloc_mem[MALLOC_MSIZE] ;
/**
 * Each memory block is associated with this structure. This contains 
 * various information that is used to allocate memory to the user
 */
struct _malloc_info minfo[N_MALLOC_BLOCKS] ;
/**
 * Array of table used to keep track of allocated memory blocks. Each of the 
 * memory elements within a memory block is associated with this table. The
 * mem info block keeps track of the starting memory block for its elements.
 */
/*
 * 	-----------------------------------------------------
 * 	| Mem block0 | Mem Block1| ----------- | Mem Blockn |  minfo
 * 	-----------------------------------------------------
 * 	|            \           \             \
 * 	\             |           \             \
 * 	 -----------------------------------------------------------
 * 	 |0|0|1|....|0|1|1|0|0| ...|0|1]........ |1|1|1|.........|1| malloc
 * 	 ----------------------------------------------------------- blocks
 */
char malloc_blks[TOT_MALLOC_BLOCKS] ;
extern struct _malloc_init malloc_config[] ;
#endif


/**************************************************************************/
/**
 * Allocate memory to the process ID queue's. 
 * - This includes the Ready_Q and Semaphore_Q. Initial memory is allocated 
 *   for Ready queue, followed by the semaphore queue.
 *
 * @param 	queue is the queue of PIDs
 * @param 	qtype is the queue type. Can be READY_Q or SEMA_Q
 * @param	qno is the Queue number. (eg) There are N_PRIO number of
 * 		Ready Queue. The qno denotes this number. The memory for
 * 		queue is allocated sequentially.
 * 		
 * @return
 * 	None
 * 	
 * @note
 * 	SEMA_Q is valid only if CONFIG_SEMA is defined.
 * 
 ***************************************************************************/
void alloc_pidq_mem( queuep queue, unsigned int qtype, unsigned int qno )
{
	if( qtype == READY_Q ){
		queue->items = pid_queue_mem + (qno*MAX_READYQ) ;
	} 
	#ifdef CONFIG_SEMA
	if( qtype == SEMA_Q ){
		queue->items = (pid_queue_mem+SEMAQ_START)+(qno*MAX_SEMA_WAITQ);
	}
	#endif
}


/**************************************************************************/
/**
 * Create the initial set of processes statically.
 *
 * @return
 * 	None
 * 	
 * @note
 * 	None
 * 
 ***************************************************************************/
void start_process_init()
{
	struct _process_init *pinit ;
	unsigned int i = 0 ;
	/*
	 * Load the system processes to run during init. 
	 * Load the shell for interactive mode or load set of processes if 
	 * shell not needed.
	 * Atleast a single process should be loaded during initialisation.
	 */
	pinit = start_p ;
	for( i = 0; i < N_INIT_PROCESS; i++) {
		sys_process_create(pinit->p_start_addr, pinit->priority) ;
		pinit++ ;
	}
}


#ifdef CONFIG_MSGQ
/**************************************************************************/
/**
 * Allocate memory to the Message Queues.
 *
 * @param 	queue is the queue of PIDs
 * @param	qno is the Queue number. 
 * 
 * @return
 * 	None
 * 	
 * @note
 * 	None
 * 
 ***************************************************************************/
void alloc_msgq_mem( queuep queue, unsigned int qno )
{
	/* It is always called in Sequence during init !! */
	static unsigned int msgq_start_ptr = 0 ;

	queue->items = (msg_queue_mem + msgq_start_ptr) ;
	msgq_start_ptr = msgq_config[qno].msg_size * msgq_config[qno].msgq_len;
}

/**************************************************************************/
/**
 * The message Queue structures are initialized. 
 * - Based on the system configuration the message queues are created and 
 *   defaults assigned to them.
 *
 * @return
 * 	None
 * 	
 * @note
 * 	None
 * 
 ***************************************************************************/
void msgq_init()
{
	unsigned int i = 0 ;
	for( ; i < N_MSGQ; i++ ){
		msgq_heap[i].msgid = -1 ;
		msgq_heap[i].msgsize = msgq_config[i].msg_size ;
		msgq_heap[i].maxmsg = msgq_config[i].msgq_len ;
		alloc_q( &msgq_heap[i].msg_q, msgq_heap[i].maxmsg,
			  MSG_Q, msgq_heap[i].msgsize, i ) ;
	}
}
#endif		/* CONFIG_MSGQ */


#ifdef CONFIG_SHM
/**************************************************************************/
/**
 * The shared memory structures are initialised based on the system config.
 *
 * @return
 * 	None
 * 	
 * @note
 * 	None
 * 
 ***************************************************************************/
void shm_init()
{
	unsigned int i = 0 ;
	unsigned int size = 0 ;
	
	for( ; i < N_SHM; i++ ) {
		shm_heap[i].shmid = -1 ;
		shm_heap[i].size = shm_config[i].shm_size ;
		shm_heap[i].shmem = (char *)(shm_mem+size) ;
		size += shm_config[i].shm_size ;
	}
}
#endif		/* CONFIG_SHM */


#ifdef CONFIG_THREAD_SUPPORT
/**************************************************************************/
/**
 * Initialise the bss memory allocation array.
 *
 * @return
 * 	None
 * 	
 * @note
 * 	None
 * 
 ***************************************************************************/
void bss_mem_init()
{
	unsigned int i ;
	for(i = 0; i < MAX_THREADS; i++)
		thread_bss_memid[i] = 0 ;
}

/**************************************************************************/
/**
 * Allocate memory for the bss memory, from the memory pool.
 *
 * @param 	start is the start address of the bss memory
 * @param	end is the end address of the bss memory
 * 
 * @return
 * -	On Success, start and end are assigned the start and end address of
 * 	bss memory respectively. Mem ID is returned.
 * - 	-1 on Error
 *   	
 * @note
 * 	None
 * 
 ***************************************************************************/
int alloc_bss_mem( unsigned int *start, unsigned int *end )
{
	unsigned int i ;
	for(i = 0; i < MAX_THREADS; i++ ){
		if( thread_bss_memid[i] == 0 ){ 
			/* Need to align along the boundry */
			*start = (unsigned int)thread_bss_mem + 
						(THREAD_BSS_BSIZE*i) ;
			*end = (*start + THREAD_BSS_BSIZE) ;
			thread_bss_memid[i] = 1 ;
			return i ;
		}
	}
	return -1 ;
}

/**************************************************************************/
/**
 * Free the bss memory allocated for the thread.
 *
 * @return
 * 	None
 * 	
 * @note
 * 	None
 * 
 ***************************************************************************/
void free_bss_mem( unsigned int memid )
{
	thread_bss_memid[memid] = 0 ;
}
#endif		/* CONFIG_THREAD_SUPPORT */


#ifdef CONFIG_MALLOC
/**************************************************************************/
/**
 * Initialize the memory blocks, that would be used by malloc and free
 *
 * @return
 * 	None
 * 	
 * @note
 * 	None
 * 
 ***************************************************************************/
void malloc_init()
{
	unsigned int j, i = 0 ;
	unsigned int tsize, size = 0 ;
	int tblk, blk = 0 ;
	
	for(; i < N_MALLOC_BLOCKS; i++ ) {
		tsize = minfo[i].mem_bsize = malloc_config[i].mem_bsize ;
		tblk = minfo[i].max_blks = malloc_config[i].n_blocks ;
		minfo[i].n_blks = 0 ;
		minfo[i].start_blk = blk ;
		minfo[i].start_addr = (malloc_mem + size ) ;
		size += (tsize*tblk) ;
		j = blk ;
		blk += tblk ;
		for(; j < blk; j++ )
			malloc_blks[j] = 0 ;		
	}
}

/**************************************************************************/
/**
 * Allocate memory of size 'size' and return the memory.
 * - The memory blocks of size 'size' is searched for free memory. If memory
 *   is available, then it is returned.
 * - If not available then return NULL.
 *
 * @param	size is the size of memory to allocate.
 * 
 * @return
 * - On success return the start address of the Memory block.
 * - Null if Error.
 *   
 * @note
 * 	None
 * 
 ***************************************************************************/
void *bufmalloc( unsigned int size )
{
	unsigned int i = 0 ;
	unsigned int sblk, eblk, j = 0 ;
		
	while( (i < N_MALLOC_BLOCKS) && (minfo[i].mem_bsize < size) )
		i++ ;
	if( (i < N_MALLOC_BLOCKS) && (minfo[i].mem_bsize == size) ) {
		if( minfo[i].n_blks == minfo[i].max_blks )
			return NULL ;
		sblk = minfo[i].start_blk ;
		eblk = sblk + minfo[i].max_blks ;
		while( malloc_blks[sblk] ) { /* Free blks are available */
			sblk++ ; j++ ;
		}
		malloc_blks[sblk] = 1 ;
		minfo[i].n_blks++ ;
		return (void *)(minfo[i].start_addr + (j*size)) ;
	} else 
		return NULL ;
}

/**************************************************************************/
/**
 * Free the memory allocated by bufmalloc
 *
 * @param	mem is the address of the memory block, that was got from 
 * 		bufmalloc
 * 		
 * @return
 * 	None
 * 	
 * @note
 * 	None
 * 
 ***************************************************************************/
void buffree( void *mem )
{
	int i = 0 ;
	struct _malloc_info *m ;
	unsigned int blk ;
	
	if( ((unsigned int *)mem < (unsigned int *)malloc_mem) || 
		((unsigned int *)mem > ((unsigned int *)malloc_mem+MALLOC_MSIZE)))
		return ;	/* Memory not allocated by malloc */
	m = minfo ; i++ ;
	while( (i < N_MALLOC_BLOCKS) && 
		((unsigned int *)mem >= ((unsigned int *)(m+1)->start_addr))) {
		i++ ; m++ ;
	}
	blk = ((signed char *)mem - m->start_addr)/m->mem_bsize ;
	blk += (m->start_blk) ;
	m->n_blks-- ;
	malloc_blks[blk] = 0 ;
	return ;
}
#endif		/* CONFIG_MALLOC */
