/*
    NetEthernetModuleDown.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/Modules/Ethernet/NetEthernetModuleDown.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 "NetModulesNew.h"
#include "NetAttr.h"
#include "LWThreads.h"
#include "NetBitFields.h"
#include "NetByteorder.h"
#include "NetDebug.h"
#include "NetDevice.h"
#include "NetEthernetModule.h"


void netEthernetModuleDownInit (NetModule this)
{
  /* XXX what should I do? */
  return;
}


int netEthernetModuleDownConfig (NetModule this, Message *msg)
{
  /* XXX protocol modules normaly shouldn't need to be configured */
  return 0;
}


void netEthernetModuleDownMain (LWPool lw, ThreadArg arg)
{
  NetModule		modDesc = (NetModule) arg;
  NetEthernetHdr	*hdr;
  unsigned short	val[3], deviceNr;
  NetBuf			data;
  NetAttrs		meta;
  NetDevice		d;
  long			len;


  while(1)
  {




    netmodRecv (lw, &data, &meta);
    PACKSTART(modDesc);

    netdbgPrintf(NETDEBUG_IP6, "netEthernet: netEthernetModuleDownMain receiving -----------------------\n");
    /* allocate head space for header */
    if (netbufAddHeadSpace(&data, sizeof(NetEthernetHdr)) != 1)
    {
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta,
                 "netEthernet: Failed to allocate header!\n");
      continue;
    }
    hdr = (NetEthernetHdr*) NETBUF_DATA(data);

    /* get the device descriptor */
    if (! netattrGet(meta, NETATTR_IF_TO, &deviceNr) )
    {
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "netEthernet: no device specified\n");
      continue;
    }
    if (! netdevGet(deviceNr, &d) )
    {
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "netEthernet: no such device\n");
      continue;
    }

    netdbgPrintf(NETDEBUG_ETHERNET,
                 "netEthernet: driver %s @ %p (q %i dev %i)\n", d->name, d,
                 d->queue, d->device);

    netattrUnset(meta, NETATTR_SENDTO_LOOP); /* just to be shure */
    if (!(	netattrSet(meta, NETATTR_SENDTO_THREADHIGH,
                      ((d->queue >> 16) & 0xFFFF)) &&
           netattrSet(meta, NETATTR_SENDTO_THREADLOW,
                      (d->queue & 0xFFFF)) ))
    {
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "netEthernet: can not set attributes\n");
      continue;
    }

    /* now build the packet */

    /* Set destination address */
    /* first try to get the specified to address */
    if (!(	netattrGet(meta, NETATTR_ETH_REMOTE_0, &val[0]) &&
           netattrGet(meta, NETATTR_ETH_REMOTE_1, &val[1]) &&
           netattrGet(meta, NETATTR_ETH_REMOTE_2, &val[2]) ) )
    {
      /* failed what now: Normaly we would dump the packet
       * becuase it is not possible to guess/know the destination
       * on a broadcast network. If this is a poit to point link 
       * this would be a diffrent story */
      //-CJ			goto fail;
      /* XXX this is a hack to get it working, if the arp packet does
         some bad stuff */
      val[0] = 0xFEFD;
      val[1] = 0xBAD0;
      val[2] = 0xADD0;
    }
    /* all ok so set it */
    hdr->dest[1] = val[0] & 0xFF;
    hdr->dest[0] = val[0] >> 8;
    hdr->dest[3] = val[1] & 0xFF;
    hdr->dest[2] = val[1] >> 8;
    hdr->dest[5] = val[2] & 0xFF;
    hdr->dest[4] = val[2] >> 8;

    /* if you want to use the ethernet address specified in the meta-
       data of the packet enable this code. Normaly this is a bad
       behaviour because you could fake Ethernet frames with this */
#if 0 /* here and ... */

    if (!(	netattrGet(meta, NETATTR_ETH_LOCAL_0, &val[0]) &&
           netattrGet(meta, NETATTR_ETH_LOCAL_1, &val[1]) &&
           netattrGet(meta, NETATTR_ETH_LOCAL_2, &val[2]) ))
    {
#endif
      /* set source address */
      /* here we try first the packet attributes and then we use
       * the address from the device descriptor */
      if (!(	netattrGet(d->addrList->addr, NETATTR_ETH_LOCAL_0, &val[0]) &&
             netattrGet(d->addrList->addr, NETATTR_ETH_LOCAL_1, &val[1]) &&
             netattrGet(d->addrList->addr, NETATTR_ETH_LOCAL_2, &val[2]) ))
      {
        /* failed what now: Normaly we would dump the packet
         * How can it be that we do not know our IF_ADDR this will be
         * set when the device is allocated */
        goto fail;
#if 0 /* ... don't forget me */

      }
#endif

    }
    hdr->src[1] = val[0] & 0xFF;
    hdr->src[0] = val[0] >> 8;
    hdr->src[3] = val[1] & 0xFF;
    hdr->src[2] = val[1] >> 8;
    hdr->src[5] = val[2] & 0xFF;
    hdr->src[4] = val[2] >> 8;

    /* Set type */
    /* XXX THIS IS UGLY, the IP module has to know that it talks to
       a ethernet chain as next and has to set the NETATTR_ETH_TYPE
       We should use more generic names like NETATTR_OSI_3_TYPE */
#if 0

    if (netattrGet(meta, NETATTR_ETH_TYPE, &val) )
    {
      hdr->type = htons(val);
    }
    else if (netattrGet (modDesc->attributes, NETATTR_ETH_TYPE, &val) )
    {
      hdr->type = htons(val);
    }
    else
    {
#endif
      if (! netattrGet(meta, NETATTR_OSI_3_TYPE, &val[0] ) )
      {
        netdbgPrintf(0, "NetEthernet: ARRG no protocol type set\n");
        goto fail;
      }
      switch ( val[0] )
      {
      case NETATTR_TYPE_IPv4:
        hdr->type = htons(NETETH_TYPE_IP);
        break;
      case NETATTR_TYPE_IPv6:
        hdr->type = htons(NETETH_TYPE_IPv6);
        break;
      case NETATTR_TYPE_ARP:
        hdr->type = htons(NETETH_TYPE_ARP);
        break;
      default:
fail:
        PACKERROR(modDesc);
        netmodRIP (lw, data, meta,
                   "netEthernet: Failed to get/set attributes\n");
        continue;
      }

      if ( (len = netbufLen (data) ) < 60)
      {	// Min Ethernet length
        netdbgPrintf(NETDEBUG_ETHERNET,
                     "netEthernet: padding Ethernet packet (%i + %i):\n", len, 60-len);
        if (!(netbufAddTailSpace(&data, 60 - len) &&
              netbufZero(&data, 60 - len, len) ))
        {
          PACKERROR(modDesc);
          netmodRIP (lw, data, meta,
                     "netEthernet: Failed to pad Ethernet frame\n");
          continue;
        }

      }

      netdbgPrintf(NETDEBUG_ETHERNET,
                   "netEthernet: sending Ethernet packet to %i:\n", d->queue);
      /*
         netdbgDumpBuf(NETDEBUG_ETHERNET, data);
         netdbgDisplay(NETDEBUG_ETHERNET, "*************************\n");
         netdbgDumpAttr(NETDEBUG_ETHERNET, meta);
         netdbgDisplay(NETDEBUG_ETHERNET, "*************************\n");
       */

      /* Finished: send to the next (driver) */


      netdbgPrintf(NETDEBUG_MODULES,"netEthernet: sending Ethernet packet to %i:\n", d->queue);


      PACKDONE(modDesc);
      netmodSend (lw, CHAINNEXT(modDesc), data, meta);

    }
  }
