/*
    Copyright 2000 (c) by David Schweikert,
    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/old/Ethertap/NetEthertap.c,v $
    Author(s):             
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 2922 $
    Creation Date:         
    Last Date of Change:   $Date: 2007-03-19 17:48:52 +0100 (Mon, 19 Mär 2007) $      by: $Author: baderma $
 
 
    $Log: NetEthertap.c,v $
    Revision 1.2  2000/11/15 10:12:14  ruf
    Repository Reorganisation
 
    Revision 1.1  2000/03/31 17:50:36  gfa
    Merged with /Net from several term projects
 
 
*/
#include <Topsy.h>
#include <Messages.h>
#include <Syscall.h>
#include <NetAttr.h>
#include <NetBuf.h>
#include <NetConfig.h>
#include <NetDebug.h>
#include <NetIface.h>
#include <NetIO.h>
#include <NetModules.h>
#include <Ethertap/NetEthertap.h>

static void nettapRead(ThreadId dev, unsigned long int length, NetIfDesc *iface);
static void nettapDown(ThreadId dev, NetBuf buf, NetAttrs attrs);

static void nettap_setaddr(NetAttrs config, char *addr)
{
  unsigned short tmp;

  netdbgPrintf(NETDEBUG_ETHERTAP, "nettap: hw-address: %02x:%02x:%02x:%02x:%02x:%02x\n",
               addr[0],addr[1],addr[2],addr[3],addr[4],addr[5]);

  tmp = addr[1];
  tmp<<=8;
  tmp+=addr[0];
  netattrSet(config, NETATTR_ETH_FROM_0, tmp);

  tmp = addr[3];
  tmp<<=8;
  tmp+=addr[2];
  netattrSet(config, NETATTR_ETH_FROM_1, tmp);

  tmp = addr[5];
  tmp<<=8;
  tmp+=addr[4];
  netattrSet(config, NETATTR_ETH_FROM_2, tmp);
}

void nettapMain(NetIfDesc *iface)
{
  ThreadId ethertapMainId, ethertapDeviceId, parentid;
  NetAttrs config = iface->config;
  NetModMsg msg;
  Message *mmsg;
  char addr[6];

  tmGetInfo(SELF, &ethertapMainId, &parentid);

  /* Open/initialize device */
  ioOpen(IO_ETHERTAP, &ethertapDeviceId);
  ioInit(ethertapDeviceId);

  /* Subscribe to Ethertap kernel device driver */
  ioSubscribe(ethertapDeviceId, ethertapMainId);

  /* Get HW address / set attr */
  ioGetAddr(ethertapDeviceId, addr, 6);
  nettap_setaddr(config, addr);

  /* Add to list of modules */
  netmodAdd(NETMODULE_ETHERTAP, ethertapMainId);

  netdbgDisplay(NETDEBUG_ETHERTAP, "Ethertap module started.\n");

  while (1)
  {
    if(!netmodMsgRecv(&msg))
      return;

    switch (msg.id)
    {
    case IO_NOTIFY:
      netdbgPrintf(NETDEBUG_ETHERTAP, "nettap: NOTIFY!\n");
      mmsg = (Message *) &msg;
      nettapRead(msg.from, mmsg->msg.ioNotify.length, iface);
      break;
    case NETMOD_SENDDOWN:
      nettapDown(ethertapDeviceId, msg.buf, msg.attrs);
      break;
    default:
      netdbgPrintf(NETDEBUG_ETHERTAP, "nettap: Unknown syscall!\n");
      break;
    }
  }
}

static void nettapRead(ThreadId id, unsigned long int length, NetIfDesc *iface)
{
  NetBuf buf;
  NetAttrs attrs;

  if (!netbufAlloc(&buf, length+2))
  {
    netdbgDisplay(NETDEBUG_GENERIC, "*** ERROR: Ethertap: netbufAlloc failed.\n");
    return;
  }
  buf->start+=2; // hack for TCP aligned structure access

  /* ioOpen isn't needed because threadId is already known... */
  ioRead(id, NETBUF_DATA(buf), &length);
  buf->end = buf->start + length;

  netdbgPrintf(NETDEBUG_ETHERTAP, "nettap: got %d bytes.\n", length);

  /* set attributes */
  if(!netattrNew(&attrs))
  {
    netdbgDisplay(NETDEBUG_GENERIC, "*** ERROR: Ethertap: netattrNew failed.\n");
    netbufFree(buf);
    return;
  }
  netattrSet(attrs, NETATTR_IF_FROM, iface->nr);

  netcfgSendNextUp(NETMODULE_ETHERTAP, buf, attrs);
}

static void nettapDown(ThreadId dev, NetBuf buf, NetAttrs attrs)
{
  netdbgPrintf(NETDEBUG_ETHERTAP, "nettap: Sending down packet (len=%d)\n",
               netbufLen(buf));
  netioWrite(dev, buf);

  netbufFree(buf);
  netbufFree((NetBuf)attrs);
}
