/*
    NetConfigMngr.c, Copyright  (c) by Boris Lutz,
    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/NetConfigMngr.c,v $
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 2922 $
    Last Date of Change:   $Date: 2007-03-19 17:48:52 +0100 (Mon, 19 Mär 2007) $  by: $Author: baderma $
 
*/

#include "Topsy.h"
#include "Messages.h"
#include "Syscall.h"
#include "Support.h"
#include "NetPCB.h"
#include "NetConfigMngr.h"
#include "NetError.h"
#include "NetModulesNew.h"
#include "NetModulesConf.h"
#include "NetDevice.h"
#include "NetDebug.h"
#include "NetSyscall.h"
#include "NetMemoryManager.h"
#include "HashList.h"


static void netConfigChainRequest(Message *msg);
static void netConfigSocketManager(Message *msg);


HashList chainTable;


void netConfigManager(ThreadArg arg)
{
  Message msg, reply;
  ThreadId requestor;

  if (! netInitPCB() )
  {
    printf("netConfigManager: AIIIEEEE can not init the PCB structures\n");
    tmExit();
  }
  if (! netmodInit() )
  {
    printf("netConfigManager: AIIIEEEE can not init the module structures\n");
    tmExit();
  }
  if ( (chainTable = hashListNew() ) == NULL )
  {
    printf("netConfigManager: AIIIEEEE can not alloc the chainTable\n");
    tmExit();
  }

  while(1)
  {
    requestor = ANY;
    if ( tmMsgRecv(&requestor, ANYMSGTYPE, &msg,
                   INFINITY) != TM_MSGRECVOK )
    {
      printf ("netConfigManager: PANIC tmMsgRecv failed\n");
      /* XXX */
      continue;
    }
    netdbgPrintf(NETDEBUG_CONFMNGR,
                 "netConfigManager: message from %i id %i\n",
                 requestor, msg.id);
    switch ( msg.id )
    {
    case NET_CONF_POOLREPLY: /* XXX */
      netdbgPrintf(NETDEBUG_CONFMNGR,
                   "netConfigManager: NET_CONF_POOLREPLY message\n");
      break;
    case NET_CONF_PCBREQ:
      netdbgPrintf(NETDEBUG_CONFMNGR,
                   "netConfigManager: NET_CONF_PCBREQ is obsolete\n");
      reply.id = UNKNOWN_SYSCALL;
      tmMsgSend(msg.from, &reply);
      break;
    case NET_CONF_PCBDEL:
    case BIND:
    case CONNECT:
      netdbgPrintf(NETDEBUG_CONFMNGR,
                   "netConfigManager: NET_CONF_PCB.* message\n");
      netConfigSocketManager(&msg);
      break;
    case NET_CONF_CHAINREQ:
      netdbgPrintf(NETDEBUG_CONFMNGR,
                   "netConfigManager: NET_CONF_CHAINREQ message\n");
      netConfigChainRequest(&msg);
      break;
    case NET_CONF_MODULEREQ:
      netdbgPrintf(NETDEBUG_CONFMNGR,
                   "netConfigManager: NET_CONF_MODULEREQ message\n");
      reply.id = NET_CONF_MODULEREPLY;
      netmodGetModule(&reply, msg.msg.netConf.ModuleReq.request);
      tmMsgSend(msg.from, &reply);
      break;
    case NET_CONF_MODULEADD:
      netdbgPrintf(NETDEBUG_CONFMNGR,
                   "netConfigManager: NET_CONF_MODULEADD message\n");
      reply.id = NET_CONF_REPLY;
      reply.msg.netConfReply.errno =
        netmodRegAdd(msg.msg.netConf.ModuleAdd.moduleRegistry,
                     msg.msg.netConf.ModuleAdd.copyFlag)?
        NET_CONF_OK:NET_CONF_FAILED;
      tmMsgSend(msg.from, &reply);
      break;
    case NET_CONF_MODULESET:
    case NET_CONF_MODULEGET:
    case NET_CONF_TYPEGET:
    case NET_CONF_TYPESET:
      netdbgPrintf(NETDEBUG_CONFMNGR,
                   "netConfigManager: NET_CONF_(MODULE|TYPE).* message\n");
      netmodConfigure(&msg);
      break;
    case NET_CONF_ADDRSET:
      if (netdevSetAddr (msg.msg.netConf.AddrSet.deviceNr,
                         (NetAttr *)msg.msg.netConf.AddrSet.attrVec,
                         msg.msg.netConf.AddrSet.len,
                         msg.msg.netConf.AddrSet.index) )
      {
        netdbgPrintf(NETDEBUG_CONFMNGR,	"netConfigManager: NET_CONF_ADDRSET received!\n");
        reply.id = NET_CONF_REPLY;
        reply.msg.netConfReply.errno = NET_CONF_OK;
        tmMsgSend(msg.from, &reply);
      }
      else
      {
        netdbgPrintf(NETDEBUG_CONFMNGR,
                     "netConfigManager: Failed to set address!\n");
        reply.id = NET_CONF_REPLY;
        reply.msg.netConfReply.errno = NET_CONF_FAILED;
        tmMsgSend(msg.from, &reply);
      }
      break;
    case NET_CONF_GETHWADDR:
      if (netdevGetHWAddr(msg.msg.netConf.HWAddrGet.deviceNr,
                          (NetAttr *)msg.msg.netConf.HWAddrGet.attrVec,
                          msg.msg.netConf.HWAddrGet.len) )
      {
        reply.id = NET_CONF_REPLY;
        reply.msg.netConfReply.errno = NET_CONF_OK;
        tmMsgSend(msg.from, &reply);
      }
      else
      {
        netdbgPrintf(NETDEBUG_CONFMNGR,
                     "netConfigManager: Failed to get HW address!\n");
        reply.id = NET_CONF_REPLY;
        reply.msg.netConfReply.errno = NET_CONF_FAILED;
        tmMsgSend(msg.from, &reply);
      }
      break;
    case NET_CONF_CHAINGETFIRST:
      netdbgPrintf(NETDEBUG_CONFMNGR,
                   "netConfigManager: NET_CONF_CHAINGETFIRST message\n");
      reply.id = NET_CONF_CHAINGETREPLY;
      if ( hashListGet(chainTable,
                       (void **) &(reply.msg.netChainGet.id),
                       msg.msg.netConf.ChainReq.chainKey)
           != HASHOK )
      {
        reply.msg.netChainGet.errno = NET_CONF_FAILED;
        reply.msg.netChainGet.id = 0;
      }
      else
      {
        reply.msg.netChainGet.errno = NET_CONF_OK;
      }
      tmMsgSend(msg.from, &reply);
    case NET_CONF_CHAINGETNEXT:
      netdbgPrintf(NETDEBUG_CONFMNGR,
                   "netConfigManager: NET_CONF_CHAINGETNEXT message\n");
      reply.id = NET_CONF_CHAINGETREPLY;
      reply.msg.netChainGet.id =
        netmodChainNext(msg.msg.netConf.ChainReq.id);
      reply.msg.netChainGet.errno = NET_CONF_OK;
      tmMsgSend(msg.from, &reply);
      break;
    case UNKNOWN_SYSCALL:
      /* if we get a UNKNOWN_SYSCALL don't resend a UNKNOWN_SYSCALL
         message or we will get nice loops */
      break;
    default:
      netdbgPrintf(NETDEBUG_CONFMNGR,
                   "netConfigManager: unknown message\n");
      /* return error */
      reply.id = UNKNOWN_SYSCALL;
      tmMsgSend(msg.from, &reply);
      break;
    }
  }
}


static void netConfigChainGet( NetModule head, NetChain** a, long *len )
{
  NetModule mod;
  NetChain *c;
  long i;

  for(i = 0, mod = head; mod; i++)
    mod = mod->chain;

  if ( i == 0 ||
       netMMAlloc((Address*) a, (i+1)*sizeof(NetChain) ) != NMM_OK )
  {
    *a = NULL;
    *len = 0;
    return;
  }
  *len = i;

  c = *a;
  for(i = 0, mod = head; mod; i++ )
  {
    c[i].function = mod->head->mainFunc;
    c[i].id = mod->id;
    c[i].arg = mod;
    mod = mod->chain;
  }
  c[i].function = NULL;
  c[i].id = 0;
  c[i].arg = NULL;
}


static void netConfigChainRequest(Message *msg)
{
  Message reply;
  unsigned short chainKey;
  NetModule module;

  reply.id = NET_CONF_CHAINREPLY;
  chainKey = msg->msg.netConf.ChainReq.chainKey;

  netdbgPrintf(NETDEBUG_CONFMNGR,
               "netConfigChainRequest: request for chain %#x\n", chainKey);
  if ( hashListGet(chainTable, (void **) &module, chainKey) != HASHOK )
  {
    netdbgPrintf(NETDEBUG_CONFMNGR,
                 "netConfigChainRequest: chain %#x not yet allocated\n", chainKey);
    module = netChainAlloc(chainKey);
    if ( hashListAdd(chainTable, (void *) module, chainKey) != HASHOK )
    {
      printf("netChainAlloc: faild to add chain %#x to chainTable. Ignored\n",
             chainKey);
    }
  }
  if ( module == NULL )
  {
    /* failed */
    netdbgPrintf(NETDEBUG_CONFMNGR,
                 "netConfigChainRequest: failed no such chain %#x\n", chainKey);
    reply.msg.netConf.ChainReply.chain = NULL;
    reply.msg.netConf.ChainReply.len = 0;
  }
  else
  {
    netdbgPrintf(NETDEBUG_CONFMNGR,
                 "netConfigChainRequest: got chain start module %s (id %i type %#x)\n", module->head->name, module->id, module->head->type);
    netConfigChainGet(module,
                      (NetChain **)	&reply.msg.netConf.ChainReply.chain,
                      &reply.msg.netConf.ChainReply.len);
  }
  netdbgPrintf(NETDEBUG_CONFMNGR,
               "netConfigChainRequest: sending response to %i\n", msg->from);
  tmMsgSend(msg->from, &reply);
}

static int allocPCB(NetPCBTable head, NetPCB *pcb, ThreadId from,
                    unsigned short domain, unsigned short protocol)
{

  if (! (*pcb = netAllocPCB() ) )
  {
    return 0;
  }
  if ( head == NULL )
  {
    /* paranoia */
    return 0;
  }
  if (!netConfPCB(*pcb, from, NULL /* XXX */, domain, protocol) )
  {
    printf("allocPCB: netConfPCB failed\n");
    return 0;
  }
  if (! netAddPCB(head, *pcb) )
  {
    printf("allocPCB: netAddPCB failed\n");
    return 0;
  }
  /* success */
  return 1;
}

static void netConfigSocketManager(Message *msg)
{
  NetPCB pcb;
  NetPCBTable head;
  Message reply;
  unsigned short domain, protocol;

  reply.id = NET_CONF_REPLY;
  switch (msg->id)
  {
  case NET_CONF_PCBDEL:
    pcb = netSocket2PCB(msg->from);
    if ( pcb == NULL )
      return;
    /* XXX */
    netUnlinkPCB(pcb);
    netFreePCB(pcb);
    return;
  case BIND:
  case CONNECT:
    domain = msg->msg.netSockBindConnect.domain;
    protocol = msg->msg.netSockBindConnect.protocol;

    reply.id = NET_SOCKET_CONNECTED;
    reply.msg.netConfReply.errno = NET_CONF_OK;

    head = netGetPCBTable(domain, protocol);
    if ( head == NULL )
    {
      netdbgPrintf(NETDEBUG_CONFMNGR,
                   "netBindPCB: no PCB table found for d %i p %i\n",
                   domain, protocol);
      reply.msg.netConfReply.errno = NET_CONF_FAILED;
      tmMsgSend(msg->from, &reply);
      return;
    }
    pcb = netSocket2PCB (msg->from);
    if ( pcb == NULL )
    {
      if (! allocPCB(head, &pcb, msg->from, domain, protocol) )
      {
        netdbgPrintf(NETDEBUG_CONFMNGR,
                     "netBindPCB: can not alloc new PCB\n");
        reply.msg.netConfReply.errno = NET_CONF_FAILED;
        tmMsgSend(msg->from, &reply);
        return;
      }
    }
    if (! netBindPCB(head, pcb, msg->id,
                     msg->msg.netSockBindConnect.attrVec,
                     msg->msg.netSockBindConnect.len) )
    {
      netdbgPrintf(NETDEBUG_CONFMNGR,
                   "netConfigSocketRequest: bind failed\n");
      reply.msg.netConfReply.errno = NET_CONF_FAILED;
    }
    tmMsgSend(msg->from, &reply);
    break;
  default: /* to make gcc happy */
    break;
  }
}

