////////////////////////////////////////////////////////////////////////////
//
// 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: process.c,v 1.1.2.3 2003/04/01 00:54:51 mohan Exp $
//
//////////////////////////////////////////////////////////////////////////////

/**
 * @file process.c 
 * 
 * This contains process management and thread management modules.
 ***************************************************************************/

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

#ifdef MB_XILKERNEL
#include <sys/mblaze_asm.h>
#endif

#include <sys/sched.h>
#include <sys/mem.h>
#include <sys/sema.h>
#include <sys/msg.h>
#include <sys/shm.h>

#ifdef PPC_XILKERNEL
#include <xexception_l.h>
#include <xtime_l.h>
#endif

#include <sys/init.h>
#include <os_config.h>
#include <config_param.h>

extern signed char run_q ;		/* Run Queue */
extern struct _queue ready_q[] ;	/* Ready Queue */
extern unsigned int _stack ;		/* _stack value from the Sym. table */

process_struct ptable[MAX_PROCS] ;	/**< Process Table */
unsigned int kernel_sp ; 		/**< Kernel Stack pointer */
signed char current_pid = -1 ;		/**< Current Process Id */
signed char flags = 0x00 ;		/**< Kernel Flags - Used bits are:
					   	- Bit 0 - THREAD_CREATE 
					 	- Bit 1 - PROCESS_YIELD
					 	- Bit 2 - PROCESS_BLOCK
					 	- Bit 3 - CS_TIMER	*/

process_context *scontext ;		/**< Temp mem for reg switch. */
void idle_task( void ) ;		/* Idle task prototype */

unsigned int pc ;

/**************************************************************************/
/**
 * Initialise the system - This function is called at the start of system. 
 * It initialises the system. 
 * - Initializes the process vector table.
 * - Creates the Idle process (pid - 0). 
 * - Creates the static set of processes.
 *   
 * @return
 * 	None.
 * 	
 * @note
 * 	None.
 *
 ***************************************************************************/
void sys_init()
{
  unsigned int i = 0 ;

  for( ; i < MAX_PROCS; i++ ) {
    ptable[i].is_allocated = 0 ;
  }
	
#ifdef MB_XILKERNEL
  GET_KERNEL_SP 	/* Assign the _stack value to kernel_sp */
#endif

    /* System wide Initialization of various components */
    readyq_init() ;
#ifdef CONFIG_THREAD_SUPPORT
  bss_mem_init() ;
#endif
#ifdef CONFIG_SEMA
  sema_heap_init() ;
#endif
#ifdef CONFIG_MSGQ
  msgq_init() ;
#endif
#ifdef CONFIG_SHM
  shm_init() ;
#endif
#ifdef CONFIG_MALLOC
  malloc_init() ;
#endif
#ifdef CONFIG_MUTEX
  // mutex_heap_init();
#endif


  /* 
   * Process 0. Idle task - assigned the lowest priority and runs
   * if no other process exists.
   */
  sys_process_create( (unsigned int)idle_task, MAX_PRIORITY ) ;
  ptable[0].pcontext.regs[1] = kernel_sp ;
  start_process_init() ;		/* Create other processes */
}


/**************************************************************************/
/**
 * Idle task.
 * - This task is run when the system is idle. This task is initialized 
 *   during system init and is always run with the least priority.
 *   
 * @return
 * 	None
 * 	
 * @note	
 * 	None
 *
 ***************************************************************************/
void idle_task( void )
{
  unsigned int i ;
  while(1) 
    for(i=0; i < 10000; i++) ;
}


/**************************************************************************/
/**
 * Process creation.
 * - Reserves a pid for the process.
 * - Initilaises the process structure. Also loads the r15 with the start
 *   address of the process.
 * - Places the process in the READY_Q.
 * - Calls the process_scheduler for scheduling.
 *   
 * @param	pstart_addr is the start address of the process
 * @param	priority is the priority of the process
 * 
 * @return
 * - PID of the new process.
 * - -1 on Error. Max. process exceeded.
 * 	
 * @note
 * 	None
 *
 ***************************************************************************/
int sys_process_create( unsigned int pstart_addr, unsigned int priority )
{
  unsigned char i ;
  unsigned int j ;
  process_struct *pcb ;

  pcb = ptable ;
  for( i=0; i < MAX_PROCS; i++ ){
    if( !pcb->is_allocated ){	/* Get the first unallocated*/
      pcb->is_allocated = 1 ;	/* PCB for the process      */
      pcb->pid = i ;
      pcb->state = PROC_NEW ;
      pcb->priority = priority ;
      pcb->mem_id = -1 ;
#ifdef MB_XILKERNEL
      for(j=0;j < 33;j++)pcb->pcontext.regs[j] = 0x0;
      pcb->pcontext.regs[15] = pstart_addr ;
#endif
#ifdef PPC_XILKERNEL
      pcb->pcontext.regs[0] = (int)&pcb->pcontext ;
			
      if( i == 0 ) { // on startup only
        scontext = &pcb->pcontext ;
        asm volatile ("lwz 0, 0(%0)" : : "b" (scontext)) ;
        asm volatile ("mtspr 0x100, 0") ;	//USPRG0
      }
      pcb->pcontext.regs[1] = 0x0 ;
      pcb->pcontext.regs[2] = pstart_addr ;
      pcb->pcontext.regs[3] = 0x8000 ;

#endif
#if SCHED_TYPE == SCHED_RR
      penq( &ready_q[0], i, 0 ) ;
#else  /* SCHED_TYPE == SCHED_PRIO */
      penq( &ready_q[priority], i, 0 ) ;
#endif
      break ;
    }
    pcb++ ;
  }
  if( i == MAX_PROCS ) {
    return -1 ;
  }
#ifdef MB_XILKERNEL
  /*
   * If called from sys_thread_create, then do not call scheduler,
   * Gets called by the thread_create.
   */
  if( (current_pid != -1) && ((flags&THREAD_CREATE)==0x00) )
    process_scheduler() ;
#endif
  return i ;
}


/**************************************************************************/
/**
 * Remove the process entry from the process table. 
 * - Set the is_allocated flag to '0'. Set the current_pid to -1. so that the
 *   next process gets scheduled.
 * - Can also do other cleaning stuff like closing files, locks, etc..
 *   
 * @return
 * 	0 on Success
 * 	
 * @note
 * 	Included only if CONFIG_PROCESS_EXIT is defined. (os_config.h)
 *
 ***************************************************************************/
#ifdef CONFIG_PROCESS_EXIT
int sys_exit()
{
#ifdef MB_XILKERNEL
  ptable[current_pid].is_allocated = 0 ;
  current_pid = run_q = -1 ;
  process_scheduler() ;
#endif
#ifdef PPC_XILKERNEL
  ptable[current_pid].state = PROC_DEAD;
  // print("killed "); putnum(current_pid);
#endif
  return 0 ;
}
#endif	/* CONFIG_PROCESS_EXIT */	


/**************************************************************************/
/**
 * Kill the Process with pid. Here the assumption that only shell can invoke 
 * this command. Can check this, but not done here.
 * - Same as sys_exit but the current system remains the current process.
 *
 * @param 	pid is the process ID of process to kill
 * 
 * @return
 * - 0 on Success
 * - -1 on Error
 *   
 * @note
 * 	Included only if CONFIG_PROCESS_KILL is defined. (os_config.h)
 *
 ***************************************************************************/
#ifdef CONFIG_PROCESS_KILL
int sys_process_kill( unsigned int pid ) 
{
  unsigned char cpid = pid;
  if( (pid == current_pid) || (ptable[pid].is_allocated == 0) )
    return -1 ;
  sys_process_remove(pid);
#if SCHED_TYPE == SCHED_RR
  delq( &ready_q[0], cpid ) ;
#else /* SCHED_TYPE == SCHED_PRIO */
  delq( &ready_q[ptable[pid].priority], cpid ) ;
#endif
  return 0 ;
}
#endif /* CONFIG_PROCESS_KILL */
/**
 * Remove the Process with pid. 
 * This is an internal proc that is called by 
 * sys_process_kill() and also by the schedulers to remove "DEAD" processes
 * 
 * @param 	pid is the process ID of process to kill
 * 
 * @return
 * - 0 on Success
 * - -1 on Error
 *
 ***************************************************************************/
int sys_process_remove(unsigned int pid)
{
  ptable[pid].is_allocated = 0 ;
#ifdef CONFIG_THREAD_SUPPORT
  if( ptable[pid].mem_id != -1 )
    free_bss_mem( ptable[pid].mem_id ) ;
#endif
  return 0;
}

/**************************************************************************/
/**
 * Return the status of the process.
 *
 * @param 	pid is the Process ID of the process.
 * @param 	ps is the structure where the status is returned.
 * 
 * @return
 * - The status of the process is returned on ps
 * - If pid not an active process, ps is assigned to NULL
 *   
 * @note
 * 	None
 *
 ***************************************************************************/
int sys_process_status( unsigned int pid, p_stat *ps ) 
{
  if(ptable[pid].is_allocated){
    ps->pid = pid ;
    ps->state = ptable[pid].state ;
  } else {
    ps = NULL ;
    return -1 ;
  }
  return 0 ; 
}



#ifdef CONFIG_PROCESS_SLEEP
int sys_process_sleep( unsigned int time ) 
{
}
#endif	/* CONFIG_PROCESS_SLEEP */


/**************************************************************************/
/**
 * Yield the processor to the next process.
 * - The current process changes to READY state and is enqueued in the
 *   ready queue.
 *   
 * @return
 * 	None
 * 	
 * @note
 * 	Included only if CONFIG_PROCESS_YIELD is defined. (os_config.h)
 *
 ***************************************************************************/
#ifdef CONFIG_PROCESS_YIELD
int sys_process_yield() 
{
	flags |= PROCESS_YIELD ;
#ifdef MB_XILKERNEL
	process_scheduler() ;
#endif
	return 0 ;
}
#endif	/* CONFIG_PROCESS_YIELD */


int sys_get_currentPID()
{
	return current_pid ;
}

/**************************************************************************/
/**
 * Get the priority of current process or thread.
 *   
 * @return
 * 	The priority of the current process or thread.
 * @note
 *	None.
 *	
 ***************************************************************************/
int sys_process_getpriority()
{
	return ptable[current_pid].priority ;
}


/**************************************************************************/
/**
 * Set priority of current process or thread to new value.
 * 
 * @param 	priority is the new priority of the process or thread.
 * 
 * @return
 * 	0 on Success.
 * 	-1 on failure.
 * 	
 * @note
 *	This function is relevant only if SCHED_TYPE = SCHED_PRIO.
 *	
 ***************************************************************************/
int sys_process_setpriority( int priority )
{
#if SCHED_TYPE == SCHED_PRIO
  if( (priority < MIN_PRIORITY) || (priority > MAX_PRIORITY) )
    return -1 ;
  ptable[current_pid].priority = priority ;
#ifdef MB_XILKERNEL
  process_scheduler() ;
#endif
#endif
  return 0 ;
}

#ifdef CONFIG_THREAD_SUPPORT
/**************************************************************************/
/**
 * Create a thread.
 * - Allocate a context (bss memory) for the new thread.
 * - Call sys_process_create to allocate a pid.
 * - The start address of the thread is the start of the function and arg
 *   is the argument to the function.
 * - Call the process_scheduler
 *
 * @param	func is the start address of the function from which the 
 * 		thread starts to execute.
 * @param 	arg is the argument to the function.
 * @param	priority is the priority assigned to the thread.
 * 
 * @return
 * - Thread ID (PID) of the created thread.
 * - -1 on Error.
 *   
 * @note
 * - Currently the argument is only an unsigned int, should be anything !!
 * - Included only if CONFIG_THREAD_SUPPORT is defined. (os_config.h)
 *
 ***************************************************************************/
int sys_thread_create( void *func, int arg, unsigned int priority ) 
{
  int mem_id, pid ;
  volatile unsigned int start_addr;
  unsigned int  bss_start_addr, bss_end_addr ;

  mem_id = alloc_bss_mem( &bss_start_addr, &bss_end_addr ) ;
  if( mem_id == -1 )
    return -1 ;
  start_addr = (unsigned int)func ;
  flags |= THREAD_CREATE ;
  pid = sys_process_create( start_addr, priority ) ;
  if( pid != -1 ) {
    ptable[pid].mem_id = mem_id ;
    ptable[pid].pcontext.regs[1] = bss_end_addr ;
#ifdef MB_XILKERNEL
    ptable[pid].pcontext.regs[5] = arg ;
#endif
  } else {
    free_bss_mem( mem_id ) ; 
    flags &= ~THREAD_CREATE ;
    return -1;
  }
  flags &= ~THREAD_CREATE ;
#ifdef MB_XILKERNEL
  process_scheduler() ;
#endif
  return pid ;
}


/**************************************************************************/
/**
 * Exit the thread. Release all the resources.
 * 
 * @return
 * 	None
 * 	
 * @note
 * - Included only if CONFIG_THREAD_SUPPORT is defined. (os_config.h)
 *	
 ***************************************************************************/
int sys_thread_exit()
{
  if( ptable[current_pid].mem_id != -1 )
    free_bss_mem( ptable[current_pid].mem_id ) ;
  // #ifdef CONFIG_PROCESS_EXIT
  return sys_exit() ;
  // #endif
}
#endif	/* Thread Support */


void enter_kernel()
{
	
}


void exit_kernel( void ) 
{
}


/**************************************************************************/
/**
 * Block the process. 
 * - Place the process into the Queue, change the state of the process and
 *   set the kernel flag to PROCESS_BLOCK.
 * - Call the process_scheduler
 *
 * @param 	queue is the queue where the process is enqueued.
 * @param	state is the state of the process in queue.
 * 
 * @return
 * 	None
 * 	
 * @note
 * 	None
 *
 ***************************************************************************/
extern struct t_syscall_data* system_call_data_ptr;
void process_block( queuep queue, unsigned int state ) 
{
#ifdef MB_XILKERNEL
  ptable[current_pid].state= state ;
  penq( queue, current_pid,0 ) ;
  flags |= PROCESS_BLOCK ;
  process_scheduler() ;
#endif
#ifdef PPC_XILKERNEL
ptable[current_pid].state= state ;
  penq( queue, current_pid,0 ) ;
  // do nothing - this function should never be called for PPC
#endif
}

/**************************************************************************/
/**
 * @return 1 if the process is in the wait state
 * @return 0 otherwise
 */
int process_is_waiting()
{
  if (ptable[current_pid].state == PROC_WAITING)
    return 1;
  return 0;
}


void process_blockp( queuep queue, unsigned int pid, unsigned int state ) 
{
}


/**************************************************************************/
/**
 * Unblock the process.
 * - Remove the process from the queue and place it in the Ready Queue.
 * - Call process_scheduler
 *
 * @param 	queue is the queue from which the process is dequeued.
 * 
 * @return
 * 	None
 * 	
 * @note
 * 	None
 *
 ***************************************************************************/
void process_unblock( queuep queue ) 
{
  unsigned char pid ; 
	
  pdeq( queue, &pid, 0 ) ;
  if( pid == 255 ) return ;
#if SCHED_TYPE == SCHED_RR
  penq( &ready_q[0], pid, 0 ) ;
#else /* SCHED_TYPE == SCHED_PRIO */
  penq( &ready_q[ptable[pid].priority], pid, 0 ) ;
#endif
  ptable[pid].state = PROC_READY ;
#ifdef MB_XILKERNEL
  process_scheduler() ;
#endif
}













