/*
   TMMain.c, Copyright  (c) by George Fankhauser et al.,
   Swiss Federal Institute of Technology,
   Computer Engineering and Networks Laboratory.
 
   TOPSY -- A Teachable Operating System.
            Implementation of a tiny and simple
            micro kernel for teaching purposes.
 
   For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
   This software is provided under the terms of the GNU General Public Licence.
 
   A full copy of the GNU GPL is provided in the file COPYING found in the 
   development root of Topsy.
*/
/*
	File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/Threads/TMMain.c,v $
	Author(s):             George Fankhauser et al.
	Affiliation:           ETH Zuerich, TIK
	Version:               $Revision: 3044 $
	Creation Date:         
	Last Date of Change:   $Date: 2007-07-04 22:16:17 +0200 (Mit, 04 Jul 2007) $      by: $Author: sryffel $
*/


//#define ETHIXBTEST
//#define UENGTEST

#include "Syscall.h"
#include "TMMain.h"
#include "IOMain.h"
#include "Memory.h"
#include "TMThread.h"
#include "TMScheduler.h"
#include "TMTime.h"
#include "TMIPC.h"
#include "HashList.h"
#include "Support.h"
#include "vm_loader.h"

#ifdef ETHIXBTEST
#include "IXPEthInit.h"
#include "IXPBusInit.h"
#endif

#ifdef UENGTEST
#include "IXPuEInit.h"
#include "uCode/rcv.x"
#include "uCode/xmt.x"
#include "IXPInt.h"
#endif

#include "tm-include.h"
#include "TMProtectionDomain.h"
/* CJ 20010123 */
#ifdef CRAZYCOW
#include "NetInit.h"
#endif

/* LR 20010131 */
#ifdef WITH_TIMER_QUEUE
#include "TMTimerNG.h"
#endif

/* Global variables */
HashList blockedThreadsOnRecvHashList;
pdom_t		mainKernelPD;						/* the PD containing the kernel init stuff */
pdom_t		mainUserPD;							/* the PD containing the main user parts (shell) */
/* XXX this can be extended freely */

/* Module global variables */
ThreadId nextThreadId=1;     /* first threadId (must be > 0) */


void tmMain( ThreadArg userInitAddress)
{
  ThreadId 							ioThreadId,
  									initThreadId,
  									newThreadId,
  									idleThreadId,
  									netThreadId,
  									fromId,
  									loaderThreadId;
  Thread*							threadPtr;
  ThreadInfo						infoDesc;
  Message							message,
  									reply;
  long int							values[2];
  char								*name;
  ThreadArg							*parameter;
  ThreadMainFunction				*fctnAddress;
  size_t							len;
  long								err;
  int 								i = 0;
  ThreadPriority 					userPrio;
  ThreadPermission 					userPerm;
  char 								userName[256];

  printf("starting tmMain...\n");
  /* Starting of a new thread for IO processing */
  if ((ioThreadId = threadStart(ioMain, NULL, KERNEL, "ioThread",
                                TMTHREADID, mainKernelPD->pdId /* destPD */, KERNEL_PRIORITY_HIGH, IOTHREADID))
      == TM_THREADSTARTFAILED)
  {
    PANIC("ioThread could not be started");
  }

  /* Starting a idle thread */
  /* do this early to be sure that there is a idle handler */
  if ((idleThreadId = threadStart( (ThreadMainFunction)tmIdleMain,
                                   NULL, KERNEL, "idleThread", TMTHREADID, mainKernelPD->pdId /* destPD */,
                                   IDLE_PRIORITY, IDLETHREADID)) == TM_THREADSTARTFAILED)
  {
    PANIC("idleThread could not be started");
  }

#if 1
  /* Starting loader */
  printf("starting loader\n");
  if ((loaderThreadId = threadStart( (ThreadMainFunction)vm_loader,
                                     NULL, KERNEL, "vm_loader", TMTHREADID, mainKernelPD->pdId /* destPD */,
                                     KERNEL_PRIORITY_HIGH, 0)) == TM_THREADSTARTFAILED)
  {
    PANIC("idleThread could not be started");
  }
  printf("loader started (TID: %li)\n", loaderThreadId);
#endif

#ifdef CRAZYCOW

  if ((netThreadId = threadStart(netInit, NULL, KERNEL, "netInit",
                                 TMTHREADID, mainKernelPD->pdId /* destPD */, USER_PPRIORITY_HIGH, 0L))
      == TM_THREADSTARTFAILED)
  {
    PANIC("netInitThread could not be started");
  }
#endif

#if 0
  //	mainUserPD = pdCreate(0, "User-PD", (address_t)0x00000000, (address_t)0xffffffff, 0, USER_PRIORITY_HIGH);
  if ((initThreadId = threadStart( (ThreadMainFunction)0x0d000000,
                                   //					NULL, USER, "shell", TMTHREADID, mainUserPD->pdId /* destPD */,
                                   NULL, KERNEL, "shell", TMTHREADID, mainKernelPD->pdId,
                                   KERNEL_PRIORITY_HIGH, 0L))
      == TM_THREADSTARTFAILED)
  {
    PANIC("userThread could not be started");
  }
#endif

#ifdef ETHIXBTEST
  led(0xFF);
  MemAccess_Init();
  led(0xEE);
  printf(" ### printing 8 Port Mac CSRs...\n");
  Eth_PrintCSRs(0);
  printf(" ### printing 8 Port Mac Counters...\n");
  Eth_PrintCounters(0);
  printf(" ### printing IX-Bus CSRs...\n");
  IXBus_PrintCSRs();

  printf("\n\n ### init 8 Port MAC...\n");
  led(0x01);
  for (i = 0; i < 8; i++)
  {
    Eth_InitPort(i);
  }
  printf(" ### init IX-Bus...\n");
  led(0x02);
  IXBus_Init();
  led(0x03);
  printf(" ### start 8 Port Mac...\n");
  for (i = 0; i < 8; i++)
  {
    Eth_StartPort(i);
  }
  led(0x04);
  printf(" ### printing 8 Port Mac CSRs...\n");
  Eth_PrintCSRs(0);
  led(0x05);
  printf(" ### printing 8 Port Mac Counters...\n");
  Eth_PrintCounters(0);
  led(0x06);
  printf(" ### printing IX-Bus CSRs...\n");
  IXBus_PrintCSRs();
  printf(" ### done!\n");
#endif

#ifdef UENGTEST

  printf("### booting uEngines...\n");
  Control_BootUEs(&rcv_uCode, &xmt_uCode);
  printf(" ### done!\n");
  IXBus_SignalThread(0);
  IXBus_SignalThread(2);
  printf("### enable uEngines-Interrupts...\n");
  Int_Init(ueInterruptHandler);
  printf(" ### done!\n");
#endif

  /* Entering main loop to process incoming messages */
  while (TRUE)
  {
    fromId = ANY;
    CLEAR_MSGENV(reply);

#ifdef ETHIXBTEST

    printf(" ### printing 8 Port Mac CSRs...\n");
    Eth_PrintCSRs(0);
    printf(" ### printing 8 Port Mac Counters...\n");
    Eth_PrintCounters(0);
    printf(" ### printing IX-Bus CSRs...\n");
    IXBus_PrintCSRs();
#endif

#if 1

    if (i == 2)
    {
      printf("tmMain: starting user.bin loader id % li\n", loaderThreadId);
      str_cpy(userName, "user.bin");
      userPerm = 0;
      userPrio = USER_PRIORITY_HIGH;
      message.id = LD_LOAD;
      if (attachEnvMsg(&message, VM_PP_INIT) != 0)
      {
        printf("        failed to attach enveloppe.\n");
        CLEAR_MSGENV(message);
        continue;
      }
      pushEnvMsg(&message, userName, str_len(userName) + 1);
      pushEnvMsg(&message, &userPerm, sizeof(ThreadPermission));
      pushEnvMsg(&message, &userPrio, sizeof(ThreadPriority));
      tmMsgSend(loaderThreadId, &message);
    }
#endif

    tmMsgRecv( &fromId, ANYMSGTYPE, &message, INFINITY);
    i++;
    // printf("tmThread: recv from %2li  &msg   %p  msg.id   %i\n", fromId, &message, message.id);

    switch (message.id)
    {
    case TM_START:
      reply.id = TM_STARTREPLY;
      if ( (threadPtr = getTHbyId(message.from)) == NULL)
      {
        reply.msg.tmStartReply.errorCode = TM_STARTFAILED;
        detachEnvMsg(&message);
        tmMsgSend(message.from, &reply);
        break;
      }
      // Threads will be started with priority of parent
      len = popEnvMsg(&message, (address_t*)&fctnAddress);
      if (len != sizeof(ThreadMainFunction) )
      {
        reply.msg.tmStartReply.errorCode = TM_STARTFAILED;
        detachEnvMsg(&message);
        tmMsgSend(message.from, &reply);
        break;
      }
      // printf("TM got ARG0:	fctnAddress = %p\n", *fctnAddress);
      len = popEnvMsg(&message, (address_t*)&parameter);
      if (len != sizeof(ThreadArg) )
      {
        reply.msg.tmStartReply.errorCode = TM_STARTFAILED;
        detachEnvMsg(&message);
        tmMsgSend(message.from, &reply);
        break;
      }
      // printf("TM got ARG1:	parameter = %p\n", *parameter);
      len = popEnvMsg(&message, (address_t*)&name);
      if (len >= (sizeof(char) * MAXNAMESIZE) )
      {
        reply.msg.tmStartReply.errorCode = TM_STARTFAILED;
        detachEnvMsg(&message);
        tmMsgSend(message.from, &reply);
        break;
      }
      // printf("TM got ARG2:	name = %s\n", name);
      if ( (newThreadId = threadStart( *fctnAddress,
                                       *parameter,  THREAD_PERMISSION(threadPtr),
                                       name, message.from, threadPtr->pdPtr->pdId,
                                       THREAD_PRIO(threadPtr),0L ) ) == TM_THREADSTARTFAILED)
      {
        reply.msg.tmStartReply.errorCode = TM_STARTFAILED;
      }
      else
      {
        reply.msg.tmStartReply.errorCode = TM_STARTOK;
        reply.msg.tmStartReply.newId = newThreadId;
      }
      detachEnvMsg(&message);
      break;

    case TM_EXIT:
      threadExit(message.from);
      continue;

    case TM_KILL:
      if ((threadKill( message.msg.tmKill.id,
                       message.from)) == TM_THREADKILLFAILED)
      {
        reply.msg.tmKillReply.errorCode = TM_KILLFAILED;
      }
      else
      {
        reply.msg.tmKillReply.errorCode = TM_KILLOK;
      }
      reply.id = TM_KILLREPLY;
      break;

    case TM_KILLREPLY:
      /* simply digest Message (coming from oneself) */
      continue;

    case TM_INFO:
      reply.id = TM_INFOREPLY;
      // attach a page to msg (don't keep, init PP)
      if (attachEnvMsg(&reply, VM_PP_INIT ) != 0)
      {
        CLEAR_MSGENV(reply);		// make pop fail
        tmMsgSend(message.from, &reply);
        break;
      }
      if ((threadInfo(message.from, message.msg.tmInfo.about,
                      message.msg.tmInfo.kind, &infoDesc,
                      values)) != TM_OK)
      {
        err = TM_INFOFAILED;
        pushEnvMsg(&reply, &err, sizeof(err));
      }
      else
      {
        err = TM_INFOOK;
        pushEnvMsg(&reply, &err, sizeof(err));
        pushEnvMsg(&reply, values, sizeof(long) * 2);
        pushEnvMsg(&reply, &infoDesc, sizeof(ThreadInfo));
      }
      break;

    case TM_GETTIME:
      if (tmTimeGet(& reply.msg.tmTime.seconds,
                    & reply.msg.tmTime.microSeconds) != TM_OK)
      {
        reply.msg.tmTime.errorCode = TM_TIMEFAILED;
      }
      else
      {
        reply.msg.tmTime.errorCode = TM_TIMEOK;
      }
      reply.id = TM_GETTIMEREPLY;
      break;

    case TM_SETTIME:
      if (tmTimeSet(message.msg.tmTime.seconds,
                    message.msg.tmTime.microSeconds) != TM_OK)
      {
        reply.msg.tmTime.errorCode = TM_TIMEFAILED;
      }
      else
      {
        reply.msg.tmTime.errorCode = TM_TIMEOK;
      }
      reply.id = TM_SETTIMEREPLY;
      break;

    case LD_REPLY:
      if (message.msg.ld_reply.err == LD_OK)
      {
        printf("TMMain: successfuly loaded user.bin\n");
      }
      else
      {
        printf("TMMain: failed to load user.bin\n");
      }
      continue;

#ifdef WITH_TIMER_QUEUE
      /* LR 010131 */
    case TM_TIMERSET:
      reply.msg.tmTimerSetReply.errorCode = TM_TIMERSETFAILED;
      if (tmTimerInsert(message.msg.tmTimerSet.deltaTicks,
                        message.from,
                        &reply.msg.tmTimerSetReply.timerId)
          == TIMER_OK)
        reply.msg.tmTimerSetReply.errorCode = TM_TIMERSETOK;
      reply.id = TM_TIMERSETREPLY;
      break;

    case TM_TIMERCLEAR:
      reply.msg.tmTimerClearReply.errorCode = TM_TIMERSETFAILED;
      reply.id = TM_TIMERCLEARREPLY;
      if (tmTimerRemove(message.from,
                        message.msg.tmTimerClear.timerId)
          == TIMER_OK)
        reply.msg.tmTimerClearReply.errorCode = TM_TIMERCLEAROK;
      break;
#endif

    case UNKNOWN_SYSCALL:
      WARNING("thread manager: message type UNKOWN_SYSCALL received");
      printf("tmMain: message from % li id %i\n", message.from, message.id);
      continue; /* do not reply on unknown syscalls to prevent loops */
    default:
      /* Unknown system call received */
      WARNING("thread manager: unknown message type received");
      reply.id = UNKNOWN_SYSCALL;
      if ( ( tmMsgSend( message.from, &reply) ) != TM_MSGSENDOK )
      {
        WARNING("syscallReply could not be sent\n");
      }
      continue;

    } // end switch(message.id)
    
    // printf("tmThread: send to   %2li  &reply %p  reply.id %i\n", message.from, &reply, reply.id);
    tmMsgSend(message.from, &reply);

  }
}
