/* 
 * sim.c
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1996,1993,1991,1990  Arizona Board of Regents
 */

/*#include <sys/types.h>*/
#include "x_stdio.h"
#include "xkernel.h"
#include "eth.h"
#include "eth_i.h"
#include "ip.h"
#include "ip_i.h"
#include "sim_i.h"
#include "sc.h"
/* #include "arp.h" */
/* #include "machine.h"	For routines to establish interrupt handler */

int	tracesimp;
XTime	sim_begTime;

/* static Msg		emptymsg; */
static ETHhost		ethBcastHost = ETH_BCAST_HOST;
static char 		*arpCodeNames[] = {
  "impossible",
  "req", 
  "rply",
  "rreq",
  "rrply"
};

/* static ArpHdr 		arpHdr; */
static int	TestCount=0;
/* static Sc	routerTypeFunSc; */

SIM_PSTATE	*SimPstate;

static int	simControl( Protl, int, char *, int );
static XkReturn	simOpenEnable( Protl, Protl, Protl, Part * );
static XkHandle	simPush( Sessn, Msg * );
static void	arp_print(char *s, ArpHdr *m);


int xsimBegTest(void)
{
  TestCount++;
  return 0;
}

u_long GlobalPacketsDropped=0;
u_long GlobalBytesDropped=0;

int xsimEndTest(void)
{
  int printEventMax();

  if (--TestCount == 0) {
    printf("****\n");
    printf("* Global Packets Dropped: %ld\n", GlobalPacketsDropped);
    printf("* Global Bytes Dropped: %ld\n", GlobalBytesDropped);
    printf("****\n");
#ifdef X_PROFILE
    monitor(0);
#endif
    traceSaveAll();
    printEventMax();
    printf("\n  *****  End of Simulation  *****\n");
#ifdef X_PROFILE
    _exit(0);
#else
    exit(0);
#endif
  }
  return 0;
}

/*
void xsimInsertRouterFun(char *type, void (*fun)())
{
  ScInsert(routerTypeFunSc, type, (void *)fun);
}

void *xsimGetRouterFun(char *type)
{
  return ScLookup(routerTypeFunSc, type);
}
*/

static void
arpHdrStore(VOID *hdr, char *netHdr, long int len, VOID *arg) 
{   
    /*
     * Need a temporary header structure to avoid alignment problems
     */
    ArpHdr tmpHdr;

    xAssert( len == sizeof(ArpHdr) );
    bcopy( hdr, (char *)&tmpHdr, sizeof(ArpHdr) );
    tmpHdr.arp_hrd = htons(tmpHdr.arp_hrd);
    tmpHdr.arp_prot = htons(tmpHdr.arp_prot);
    tmpHdr.arp_op = htons(tmpHdr.arp_op);
    bcopy( (char *)&tmpHdr, netHdr, sizeof(ArpHdr) );
}           

static long
arpHdrLoad(VOID *hdr, char *netHdr, long len, VOID *arg)
{    
    xAssert( len == sizeof(ArpHdr) );
    
    bcopy( netHdr, hdr, sizeof(ArpHdr) );
    ((ArpHdr *)hdr)->arp_hrd = ntohs(((ArpHdr *)hdr)->arp_hrd);
    ((ArpHdr *)hdr)->arp_prot = ntohs(((ArpHdr *)hdr)->arp_prot);
    ((ArpHdr *)hdr)->arp_op = ntohs(((ArpHdr *)hdr)->arp_op);
    return len;
}   
    
static void
arp_print(s, m)
    char *s;    
    ArpHdr *m;
{
  printf("%s arp %s (%d) message:\n", s,
	 (m->arp_op > ARP_MAXOP) ? "unknown" : arpCodeNames[m->arp_op],
	 m->arp_op);
  printf("  source %s @ %s\n",
	 ethHostStr((ETHhost *)&m->arp_sha), ipHostStr(&m->arp_spa));
  printf("  target %s @ %s\n",
	 ethHostStr((ETHhost *)&m->arp_tha), ipHostStr(&m->arp_tpa));
}

void
sim_init(Protl self)
{
  int		k;
  long		time();
  SIM_PSTATE	*ps;
  
  xsimDbg(SL2_FLAG, printf("sim_init\n"));
  if (sim_begTime.sec == 0)
    xGetTime(&sim_begTime);
  xTrace0(simp, TR_MAJOR_EVENTS, "sim_init");
  SimPstate = ps = X_NEW(SIM_PSTATE);
  bzero((char *)ps, sizeof(SIM_PSTATE));
  self->state = (VOID *)ps;
/*   sim_createNetwork(ps); */
  ps->arpFlag = 1; 
  ps->rarpFlag = 1;
  time((time_t *)&ps->seed);
  self->push = simPush;
  self->controlprotl = simControl;
  self->openenable  = simOpenEnable;
  self->up = 0;

/*   arpHdr.arp_hrd = 1; */
/*   arpHdr.arp_prot=ARP_PROT; */
/*   arpHdr.arp_hlen=6;  */
/*   arpHdr.arp_plen=4; */

  for (k=1; k<globalArgc; k++) {
    if (!strcmp(globalArgv[k], "-arp"))
      ps->arpFlag = 0;
    else if (!strcmp(globalArgv[k], "-rarp"))
      ps->rarpFlag = 0;
    else if (!strcmp(globalArgv[k], "-seed")) 
      sscanf(globalArgv[++k], "%d", &ps->seed);
  }
  srand48((long)ps->seed);
  printf("Using seed: %d\n", ps->seed);
/*  routerTypeFunSc = ScCreate(0, 16); */
/*  ScInsert(routerTypeFunSc, "FCFS", (void *)sim_FCFS_input); */
/*  ScInsert(routerTypeFunSc, "RED", (void *)sim_RED_input); */

}

static void
cbHandler (Event ev, void *arg)
{
  void		(*cbFun)();
  CallBack	*cbp=(CallBack *)arg;

  /* --- if curHost is 0 => callback is like interrupt! but may be probs */
  cbFun = (void (*)()) cbp->arg1;
  cbFun(cbp->op, cbp->rv, cbp->state);
  if (cbp->op == OP_SENT_CB  &&  cbp->rv == RV_FAILED) {
    xsimDbg(SL2_FLAG,
	    printf("sim.c: cbHandler received RV_FAILED\n"));
    freeSimPacket((SimPacket *)cbp->arg2);
  }
  freeCallBack(cbp);
}

static XkHandle
simPush( self, msg )
  Sessn	self;
  Msg 	*msg;
{
  ETHhdr	*hdr = (ETHhdr *)msgGetAttr(msg, 0);
  int		host;
  SIM_PSTATE	*ps = (SIM_PSTATE *)self->state;
  Key		*key, *kp;
  SimPacket	*pkt;
  Net		*net;
  ArpHdr	ahdr;
  ETHhdr	ehdr;
  void          *buf;

  xTrace0(simp, TR_EVENTS, "simPush");
  xAssert(hdr);
  
  host = CurHost;
  CurHost = 0;
  xsimDbg(SL2_FLAG,
	  printf("%s simPush, msg: %x\n", sim_getTime(), msg));
  if ((key = sim_addr2key(ps, (char *)&(hdr->src), TYPE_ETH)) == NULL)
    return XMSG_ERR_HANDLE;
  net = ps->Nets[key->net];
  xsimDbg(SL2_FLAG,
	  printf("             ulp: %s\n", key->name));

  /* --- ARP packet ! */
  if (ntohs(hdr->type) == ARP_TYPE  &&  ps->arpFlag) {
    xsimDbg(SL2_FLAG, printf("             ARP packet!\n"));

    buf = msgPop(msg, sizeof(ETHhdr));
    xAssert(buf);
    ethdMsgLoad(&ehdr, buf, sizeof(ETHhdr), 0);

    buf = msgPop(msg, sizeof(ArpHdr));
    xAssert(buf);
    arpHdrLoad(&ahdr, buf, sizeof(ArpHdr), 0);

    xsimDbg(SL2_FLAG,
	    arp_print("ARP:  ", &ahdr));
    ehdr.dst = ehdr.src;
    ahdr.arp_op = ARP_RPLY;
    kp = sim_addr2key(ps, (char *)&ahdr.arp_tpa, TYPE_IP);
    ahdr.arp_sha = ahdr.arp_tha = *(MAC48bithost *)&kp->ethAddr;
    ahdr.arp_spa = kp->ipAddr;
/*     msgConstructCopy(&rmsg, msg);  */
    
    buf = msgPush(msg, sizeof(ArpHdr));
    xAssert(buf);
    arpHdrStore(&ahdr, buf, sizeof(ArpHdr), 0);

    buf = msgPush(msg, sizeof(ETHhdr));
    xAssert(buf);
    ethdMsgStore(&ehdr, buf, sizeof(ETHhdr), 0);

    pkt = sim_create_eth_packet(ps, net, msg, &ehdr);

    net->control(net, OP_SEND, cbHandler, (void *)key->obj, 
		 (void *)key->cbFun, (void *)pkt);

/*     h = simGetHost(pkt->ps, &pkt->hop); */
/*     xsimSetCurHost(h); */
/*     evDetach(evSchedule(net->handler, (void *)pkt, 0)); */
/*     msgDestroy(&rmsg);  */
    CurHost = host;
    return XMSG_NULL_HANDLE;
  }
  /* --- RARP packet ! */
  else if (ntohs(hdr->type) == RARP_TYPE  &&  ps->rarpFlag) {
    xsimDbg(SL2_FLAG, printf("             RARP packet!\n"));

    buf = msgPop(msg, sizeof(ETHhdr));
    xAssert(buf);
    ethdMsgLoad(&ehdr, buf, sizeof(ETHhdr), 0);

    buf = msgPop(msg, sizeof(ArpHdr));
    xAssert(buf);
    arpHdrLoad(&ahdr, buf, sizeof(ArpHdr), 0);

    xsimDbg(SL2_FLAG,
	    arp_print("RARP:  ", &ahdr));
    ehdr.dst = ehdr.src;
    ahdr.arp_op = ARP_RRPLY;
    ahdr.arp_spa = ahdr.arp_tpa = key->ipAddr;
/*     msgConstructCopy(&rmsg, msg); */

    buf = msgPush(msg, sizeof(ArpHdr));
    xAssert(buf);
    arpHdrStore(&ahdr, buf, sizeof(ArpHdr), 0);

    buf = msgPush(msg, sizeof(ETHhdr));
    xAssert(buf);
    ethdMsgStore(&ehdr, buf, sizeof(ETHhdr), 0);
    
    pkt = sim_create_eth_packet(ps, net, msg, &ehdr);
    net->control(net, OP_SEND, cbHandler, (void *)key->obj, 
		 (void *)key->cbFun, (void *)pkt);

/*     h = simGetHost(pkt->ps, &pkt->hop); */
/*     xsimSetCurHost(h); */
/*     evDetach(evSchedule(net->handler, (void *)pkt, 0)); */
    CurHost = host;
    return XMSG_NULL_HANDLE;
  }
  
  if ( ETH_ADS_EQUAL(hdr->dst, ethBcastHost) ) {
    /* Broadcast packet */
    pkt = sim_create_eth_packet(ps, net, msg, hdr);
    pkt->flags |= PKT_FLAG_BCAST;
    net->control(net, OP_SEND, cbHandler, (void *)key->obj, 
		 (void *)key->cbFun, (void *)pkt);
  }
  /* Create a Normal Packet */
  else {
    pkt = sim_create_eth_packet(ps, net, msg, hdr);
    /* call defaultNetControl */
    xsimDbg(SL2_FLAG,
	    printf("simPush, pkt:%ld\n",pkt->id));
    net->control(net, OP_SEND, cbHandler, (void *)key->obj, 
		 (void *)key->cbFun, (void *)pkt);
  }
  CurHost = host;
  return XMSG_NULL_HANDLE;
}    

static int
simControl( s, op, buf, len )
  Protl	s;
  int 	op, len;
  char 	*buf;
{
  int		host;
  SIM_PSTATE	*ps = (SIM_PSTATE *)s->state;
  Key		*key;
  Net		*net;

  switch (op) {

  case GETMAXPACKET:
  case GETOPTPACKET:
    checkLen(len, sizeof(int));
    if ((key = sim_obj2key(ps, *((char **)buf))) == NULL) {
      xsimDbg(SL2_FLAG, printf("** UNKNOWN **\n"));
      return -1;
    }
    else if (ps->Nets[key->net]->maxPacket > 0) {
    xsimDbg(SL2_FLAG,
            printf("%s simControl(GETMAXPACKET): %d\n", sim_getTime(),
                   ps->Nets[key->net]->maxPacket));
      bcopy((char *)&ps->Nets[key->net]->maxPacket, buf, sizeof(int));
      return sizeof(int);
    }
    return -1;

  case GETMYHOST:
    xsimDbg(SL2_FLAG,
	    printf("%s simControl(GETMYHOST) buf:%x\n", sim_getTime(), 
		   *((char **)buf)));
    if ((key = sim_obj2key(ps, *((char **)buf))) == NULL) {
      xsimDbg(SL2_FLAG, printf("** UNKNOWN **\n"));
      return -1;
    }
    else if (ps->Nets[key->net]->type == TYPE_ETH) {
      checkLen(len, sizeof(ETHhost));
      bcopy((char *)&(key->ethAddr), buf, sizeof(ETHhost));
      xsimDbg(SL2_FLAG,
	      printf("             %s\n", 
		     sim_addr2str(&key->ethAddr, TYPE_ETH)));
      return (sizeof(ETHhost));
    }
    else if (ps->Nets[key->net]->type == TYPE_IP) {
      checkLen(len, sizeof(IPhost));
      if (len == sizeof(ETHhost)) {
        bcopy((char *)&(key->ethAddr), buf, sizeof(ETHhost));
        xsimDbg(SL2_FLAG, printf("             %s\n",
                                 sim_addr2str(&key->ethAddr, TYPE_ETH)));
        return (sizeof(ETHhost));
      }
      else {
        bcopy((char *)&(key->ipAddr), buf, sizeof(IPhost));
        xsimDbg(SL2_FLAG, printf("             %s\n", 
		                 sim_addr2str(&key->ipAddr, TYPE_IP)));
        return (sizeof(IPhost));
      }
    }

  case SIM_ISBUSY:
    xsimDbg(SL2_FLAG,
	    printf("%s simControl(SIM_ISBUSY) buf:%x\n", sim_getTime(),
		   *((char **)buf)));
    if ((key = sim_obj2key(ps, *((char **)buf))) == NULL) {
      xsimDbg(SL2_FLAG, printf("** UNKNOWN **\n"));
      return -1;
    }
    else {
      net = ps->Nets[key->net];
      *buf = net->control(net, OP_ISBUSY, NULL, NULL, NULL, NULL);
      xsimDbg(SL2_FLAG,
	      printf("              ISBUSY:%d\n", (int) *buf));
      return 1;
    }

  case SIM_REGISTER_CB:
    xsimDbg(SL2_FLAG,
	    printf("%s simControl(SIM_REGISTER_CB) buf:%x\n", sim_getTime(),
		   *((char **)buf)));
    if ((key = sim_obj2key(ps, *((char **)buf))) == NULL) {
      xsimDbg(SL2_FLAG, printf("** UNKNOWN **\n"));
      return -1;
    }
    else {
      key->cbFun = (void (*)()) *((char **)(buf+sizeof(char *)));
      xsimDbg(SL2_FLAG,
	      printf("              REGISTER_CB:%x\n", *((char **)(buf+sizeof(char *)))));
      return 0;
    }
    
  case SIM_NOTBUSY_CB:
    xsimDbg(SL2_FLAG,
	    printf("%s simControl(SIM_NOTBUSY_CB) buf:%x\n", sim_getTime(),
		   *((char **)buf)));
    if ((key = sim_obj2key(ps, *((char **)buf))) == NULL) {
      xsimDbg(SL2_FLAG, printf("** UNKNOWN **\n"));
      return -1;
    }
    else {
      net = ps->Nets[key->net];
      host = CurHost;
      CurHost = 0;
      /* Call defaultNetControl */
      net->control(net, OP_NOTBUSY_CB, cbHandler, (void *)key->obj, 
		   (void *)key->cbFun, NULL);
      CurHost = host;
      return 0;
    }

  case SIM_GETSEED:
    checkLen(len, sizeof(int));
    bcopy((char *)&(ps->seed), buf, sizeof(int));
    return sizeof(int);

  default:
    return -1;
  }
}
    
static XkReturn
simOpenEnable( self, hlp, hlpType, p )
    Protl        self, hlp, hlpType;
    Part        *p;
{       
  SIM_PSTATE	*ps = (SIM_PSTATE *)self->state;
  Key		*key;

  xsimDbg(SL2_FLAG, printf("%s simOpenEnable, ulp:%s\n", sim_getTime(), 
			   hlp->fullName));
  if ((key = sim_name2key(ps, hlp->fullName)) == NULL)
    return XK_FAILURE;
  else {
    key->obj = (char *) hlp;
    return XK_SUCCESS;
  }
}                   
                    
