/*
    Copyright 2000 (c) by Claudio Jeker,
    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/Net/NetInit.c,v $
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 2936 $
    Last Date of Change:   $Date: 2007-03-21 15:23:42 +0100 (Mit, 21 Mär 2007) $  by: $Author: baderma $
 
*/
#include "NetInQueueMngr.h"
#include "NetConfigMngr.h"
#include "NetModulesNew.h"
#include "NetModulesDef.h"
#include "NetDevice.h"
#include "NetMemoryManager.h"
#include "NetError.h"
#include "Syscall.h"
#include "Support.h"
#include "IO.h"
#include "NetBuf.h"
#include "NetDebug.h"
#include "NetBootP.h"
#include "NetTftp.h"
#include "NetAutoConfiguration.h"

// for testing TFTP XXX
Message			TftpCmdMsg,
TftpReply;
char*				FileName;
address_t		TftpBuffer;

struct netDeviceTable
{
  short major;
  u8 type;
  char *name;
};

struct netDeviceTable devTestList[] =
  {
    {
      ETAP, IFT_ETHER, "etap"
    },
    { AM79C970, IFT_ETHER, "le" },
    { AM79C97X, IFT_ETHER, "lex" },
    { 0, 0, 0 } /* terminator */
  };

extern ThreadId netMemoryThread;
extern NetModuleRegDesc netModuleRegistryTable[];
extern ThreadId arp_main_thread;

void arp_main(ThreadArg arg);
void netMemoryHandler(ThreadArg arg);

#define NUM_OF_POOLTHREAD 4 /* for the beginning */

static void netAddModules(void);
static void netInitDistributors(void);

void netInit()
{
  int i = 0, j = 0;
  ThreadId t;

  /* init netStuff */
  netdbgInit();
  printf("starting Network\n");

  /* first of all start the netMemoryMsgHandler Thread */
  if( tmStart(&netMemoryThread, netMemoryHandler, NULL,
              "netMemoryHandler") != TM_STARTOK)
  {
    netdbgPrintf(0,
                 "netInit: AIIIEEE can not start netMemoryHandler\n");
    return;
  }
  netbufInit();
  netMMInit();

  /* first call all needed init functions */
  if (! netdevInit() )
  {
    netdbgDisplay(0, "netInit: AIIIEEE can not init, giving ...\n");
    tmExit();
  }

  /* first find and init the network devices */
  for ( i = 0; devTestList[i].major; i++ )
  {
    netdbgPrintf(NETDEBUG_CONFMNGR,
                 "netInit: trying major device %i\n", devTestList[i].major);
    for (j = 1; ioOpen(devTestList[i].major, j, &t) == IO_OPENOK; j++)
    {
      netdbgPrintf(NETDEBUG_CONFMNGR,
                   "\t\tfound device major %i minor %i (%i)\n",
                   devTestList[i].major, j, t);
      netdevAdd(t, devTestList[i].major, j, devTestList[i].type,
                devTestList[i].name);
    }
  }

  /* then start the configuration module */
  if ( tmStart(&netConfigThread, netConfigManager, NULL,
               "NetConfigManager") != TM_STARTOK )
  {
    netdbgPrintf(0,
                 "netInit: AIIIEEE can not start Config Manager, giving up\n");
    tmExit();
  }

  /* it's now inqueue managers turn to run */
  if ( tmStart(&netInqueueThread, netInQueueManager,
               (ThreadArg) NUM_OF_POOLTHREAD,
               "NetInqueueManager") != TM_STARTOK )
  {
    netdbgPrintf(0,
                 "netInit: AIIIEEE can not start Inqueue Manager, giving up\n");
    tmExit();
  }

  /* ARP Main thread, needed for ARP lookups */
  if ( tmStart(&arp_main_thread, arp_main,
               (ThreadArg) NUM_OF_POOLTHREAD-1,
               /* leave one slot (pool thread) for arp requests */
               "ARP daemon") != TM_STARTOK )
  {
    netdbgPrintf(0,
                 "netInit: AIIIEEE can not start Inqueue Manager, giving up\n");
    tmExit();
  }

  netAddModules();

  netInitDistributors();
  /* last but not least start the devqueue threads */
  netdevStartAll();
  /* BootP to get IP */
  netdbgPrintf (NETDEBUG_CONFIG, "netInit: starting BootP\n");
  if ( tmStart(&t, netBootP, (ThreadArg) 0, "BootP") != TM_STARTOK )
  {
    netdbgPrintf(0, "netInit: Failed to start BootP!\n");
  }
#if 1
  /* XXX netTftp test */
  netdbgPrintf (NETDEBUG_TEST, "netInit: starting TFTP\n");
  if ( tmStart(&t, netTftp, (ThreadArg) 0, "TFTP") != TM_STARTOK )
  {
    netdbgPrintf(0, "netInit: Failed to start TFTP!\n");
  }
  vmAlloc((address_t)&FileName, 10 );
  str_cpy(FileName, "test");
  vmAlloc((address_t)&TftpBuffer, 4000L );
  TftpCmdMsg.msg.netTftpGet.file = FileName;
  TftpCmdMsg.msg.netTftpGet.buff = TftpBuffer;
  TftpCmdMsg.id = NET_TFTP_GETCMD;
  printf("NetInit: sending command to TFTP\n");
  tmMsgSend(t, &TftpCmdMsg);
  tmMsgRecv(&t, NET_TFTP_SIZE_REPLY, &TftpReply, INFINITY);
  printf("NetInit: received reply from tftp: size = %i\n", TftpReply.msg.netTftpSize.size);
  printf("NetInit: sending command to TFTP\n");
  tmMsgSend(t, &TftpCmdMsg);
  tmMsgRecv(&t, NET_TFTP_SIZE_REPLY, &TftpReply, INFINITY);
  printf("NetInit: received reply from tftp: size = %i\n", TftpReply.msg.netTftpSize.size);
#endif

#if 0

  netdbgPrintf(NETDEBUG_AUTOCONFIG, "netInit: starting AutoConfiguration\n");
  if ( tmStart(&t, netAutoConfiguration, (ThreadArg) 1,"AutoConfiguration") != TM_STARTOK )
  {
    printf(0, "netInit: Failed to start AutoConfiguration!\n");
  }
#endif

#if 0
  while (1)
  {
    Message msg;
    t = ANY;
    netdbgPrintf(0, "netInit: waiting for message\n");
    if ( tmMsgRecv(&t, ANYMSGTYPE, &msg,
                   INFINITY) != TM_MSGRECVOK )
    {
      printf("configDistributor: tmMsgRecv failed\n");
      return;
    }
    netdbgPrintf(0, "netInit: got message from %i, type %i (UNKNOWN_SYSCALL %i)\n",
                 msg.from, msg.id, UNKNOWN_SYSCALL);
    tmExit();
  }
#endif
}

static void netAddModules(void)
{
  Message msg, reply;
  ThreadId t;
  int i = 0;

  /* register all known modules */
  msg.id = NET_CONF_MODULEADD;
  msg.msg.netConf.ModuleAdd.copyFlag = 0; /* do not copy the descriptor
  											   so we can save some space */
  for ( i = 0; netModuleRegistryTable[i].type != 0; i++ )
  {
    msg.msg.netConf.ModuleAdd.moduleRegistry = &netModuleRegistryTable[i];
    t = netConfigThread;
    if ( tmMsgSend(netConfigThread, &msg) != TM_MSGSENDOK )
    {
      netdbgPrintf(0,
                   "netInit: Failed to send registry to config manager\n");
    }
    else if ( tmMsgRecv(&t, NET_CONF_REPLY, &reply,
                        INFINITY) != TM_MSGRECVOK )
    {
      netdbgPrintf(0,
                   "netInit: Failed to get registry reply\n");
    }
    else if ( reply.msg.netConfReply.errno != NET_CONF_OK )
    {
      netdbgPrintf(0,
                   "netInit: Failed to set registry\n");
    }
    else
    {
      netdbgPrintf(NETDEBUG_CONFMNGR,
                   "netInit: netModule %s loaded\n",
                   netModuleRegistryTable[i].name);
    }
  }
}

static void configDistributor(NetAttr* attr, NetModuleType type)
{
  Message msg;
  int i = 0;
  ThreadId id = netConfigThread;

  for ( i = 0; attr[i].key != 0; i++)
    ;
  msg.id = NET_CONF_TYPESET;
  msg.msg.netConf.TypeConf.attrVec = attr;
  msg.msg.netConf.TypeConf.len = i;
  msg.msg.netConf.TypeConf.type = type;
  tmMsgSend(netConfigThread, &msg);
  while (1)
  {
    if ( tmMsgRecv(&id, NET_CONF_MODULEREPLY, &msg,
                   INFINITY) != TM_MSGRECVOK )
    {
      printf("configDistributor: tmMsgRecv failed\n");
      return;
    }
    if (msg.msg.netConf.ModuleConf.id == 0 &&
        msg.msg.netConf.ModuleConf.len == 0 )
      return;
  }
}

static void netInitDistributors(void)
{
  NetAttr dist_in[] = {
                        { NETATTR_DISTRIBUTOR_KEY, NETATTR_IN_TYPE },
                        { NETATTR_TYPE_ETHERNET, ETHERNET_UP_CHAIN },
                        { NETATTR_TYPE_UDP, UDP_DOWN_CHAIN },
                        { NETATTR_TYPE_ICMP, ICMP_DOWN_CHAIN },
                        { NETATTR_TYPE_ICMP6, ICMP6_DOWN_CHAIN },
                        { NETATTR_TYPE_TCP, TCP_DOWN_CHAIN },
                        { NETATTR_TYPE_ARP, ARP_SEND_CHAIN },
                        { 0, 0 }
                      };
  NetAttr dist_23[] = {
                        { NETATTR_DISTRIBUTOR_KEY, NETATTR_OSI_3_TYPE },
                        { NETATTR_TYPE_IPv4, IPv4_UP_CHAIN },
                        { NETATTR_TYPE_IPv6, IPv6_UP_CHAIN },
                        { NETATTR_TYPE_ARP, ARP_REPLY_CHAIN },
                        { 0, 0 },
                      };
  NetAttr dist_34[] = {
                        { NETATTR_DISTRIBUTOR_KEY, NETATTR_OSI_4_TYPE },
                        { NETATTR_TYPE_UDP, UDP_UP_CHAIN },
                        { NETATTR_TYPE_ICMP, ICMP_UP_CHAIN },
                        { NETATTR_TYPE_ICMP6, ICMP6_UP_CHAIN },
                        { NETATTR_TYPE_TCP, TCP_UP_CHAIN },
                        { NETATTR_TYPE_IPv4, IP_FORWARDING_CHAIN },
                        { 0, 0 }
                      };
  NetAttr dist_32[] = {
                        { NETATTR_DISTRIBUTOR_KEY, NETATTR_OSI_2_TYPE },
                        { NETATTR_TYPE_ETHERNET, ETHERNET_DOWN_CHAIN },
                        { 0, 0 }
                      };
  NetAttr dist_43[] = {
                        { NETATTR_DISTRIBUTOR_KEY, NETATTR_OSI_3_TYPE },
                        { NETATTR_TYPE_IPv4, IPv4_DOWN_CHAIN },
                        { NETATTR_TYPE_IPv6, IPv6_DOWN_CHAIN },
                        { 0, 0 }
                      };
  NetAttr dist_out[] = {
                         { NETATTR_DISTRIBUTOR_KEY, NETATTR_SENDTO_THREAD },
                         { NETATTR_TYPE_ICMP, ICMP_DOWN_CHAIN },
                         { NETATTR_TYPE_ICMP6, ICMP6_DOWN_CHAIN },
                         { 0, 0 }
                       };

  configDistributor(dist_in, IN_DISTRIBUTOR_TYPE);
  configDistributor(dist_out,OUT_DISTRIBUTOR_TYPE);
  configDistributor(dist_23, OSI_23_DISTRIBUTOR_TYPE);
  configDistributor(dist_34, OSI_34_DISTRIBUTOR_TYPE);
  configDistributor(dist_43, OSI_43_DISTRIBUTOR_TYPE);
  configDistributor(dist_32, OSI_32_DISTRIBUTOR_TYPE);
}
