/*
    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/IO/Drivers/Ethertap.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 "IO.h"
#ifdef IO_ETHERTAP /* used on Linux MipsSim and Linux user space port */

#include "Drivers/Ethertap.h"
#include "Messages.h"
#include "IODevice.h"
#include "TMIPC.h"
#include "Support.h"
#include "Syscall.h" /* for tmMsgSend */

#define CONTROL_SEND  1
#define CONTROL_READ  2

IODeviceDesc ethertapDesc = {
                              NULL, NULL,              /* pointers for doubly linked list */
                              "ethertap",              /* device name */
                              (Address)ETHERTAP_BASE,  /* device base address */
                              NULL,                    /* buffer must be allocated by driver */
                              ETHERTAPINT,             /* interrupt */
                              (InterruptHandler)ethertap_interruptHandler,
                              /* interrupt handling function */
                              0,                       /* thread id filled in after start */
                              ethertap_read,           /* device read function */
                              ethertap_write,          /* device write function */
                              ethertap_close,          /* device close function */
                              ethertap_init,           /* device init function */
                              ethertap_handleMsg,      /* private msg handler */
                              FALSE,                   /* initialisation state, normally FALSE */
                              NULL                      /* data extension */
                            };


void
ethertap_interruptHandler (IODevice this)
{
  Ethertap dev;
  unsigned short *rxlen;
  unsigned char *control;

  dev = (Ethertap) this->extension;
  if (dev->subscribed != 0)
  {
    /* Send notify message to subscribed thread. */
    Message msg;

    msg.from = this->threadId;
    msg.id = IO_NOTIFY;
    msg.msg.ioNotify.deviceNumber = IO_ETHERTAP;

    rxlen = (unsigned short *) ((unsigned long) (this->base)
                                + ETHERTAP_RX_SIZE);

    msg.msg.ioNotify.length = *rxlen;
    if (kSend (dev->subscribed, &msg) == TM_OK)
      return;

    // kSend failed...
    ioConsolePutString ("Ethertap interrupt: kSend failed.\n");
  }
  // packet is lost. set control_read...
  control =
    (unsigned char *) ((unsigned long) (this->base) +
                       ETHERTAP_CONTROL_REGISTER);
  *control |= CONTROL_READ;
}



Error
ethertap_init (IODevice this)
{
  Ethertap dev;
  unsigned char *control;

  if (!this->isInitialised)
  {
    Ethertap tmp;
    tmp = this->extension;
    hmAlloc (&(this->extension), sizeof (EthertapDesc));
    dev = (Ethertap) this->extension;
    this->isInitialised = TRUE;
  }
  else
  {
    dev = (Ethertap) this->extension;
  }
  control =
    (unsigned char *) ((unsigned long) (this->base) +
                       ETHERTAP_CONTROL_REGISTER);
  *control |= CONTROL_READ;
  dev->subscribed = 0;
  dev->fragment = 0;

  return IO_INITOK;
}


/*   - Control register (8 bit):
 *  
 *  Offset (bit)  Description
 *  ------------  --------------------------------
 *       0 (LSM)  Send data
 *	 1        Data read
 *
 *
 *  - Status register (8 bit):
 *
 *  Offset (bit)  Description
 *  ------------  --------------------------------
 *       0 (LSM)  Received data
 */

Error
ethertap_read (IODevice this, ThreadId threadId, char *buffer, long int *size)
{
  unsigned short *rxlen;
  unsigned char *data;
  unsigned char *control;
  unsigned char *status;

  rxlen =
    (unsigned short *) ((unsigned long) (this->base) + ETHERTAP_RX_SIZE);
  data = (unsigned char *) ((unsigned long) (this->base) + ETHERTAP_RX_DATA);
  control =
    (unsigned char *) ((unsigned long) (this->base) +
                       ETHERTAP_CONTROL_REGISTER);
  status =
    (unsigned char *) ((unsigned long) (this->base) +
                       ETHERTAP_STATUS_REGISTER);

  if (*size < *rxlen || (*status & 1) == 0)
    return IO_READFAILED;

  /* Copy from device to user memory */
  //-CJ NEEDED TO FIND A NASTY BYTECOPY BUG
  /*	{
  		unsigned int i = *rxlen, j = 0;
  		unsigned char* c = data;
  		
  		printf("ETHERTAP: ioRead data size %i\n", i);
  		while ( i ) {
  			printf("%02x ", (int) *c);
  			if (! (++j % 8) ) printf("\n");
  			i--;
  			c++;
  		}
  		printf("\n");
  	}
  */
  byteCopy (buffer, data, *rxlen);
  //-CJ NEEDED TO FIND A NASTY BYTECOPY BUG
  /*	{
  		unsigned int i = *rxlen, j = 0;
  		unsigned char* c = buffer;
  		
  		printf("ETHERTAP: ioRead buffer size %i\n", i);
  		while ( i ) {
  			printf("%02x ", (int) *c);
  			if (! (++j % 8) ) printf("\n");
  			i--;
  			c++;
  		}
  		printf("\n");
  	}
  */
  *size = *rxlen;
  *control |= CONTROL_READ;

  return IO_READOK;
}


Error
ethertap_write (IODevice this, ThreadId threadId, char *buffer,
                long int *size)
{
  Ethertap dev;
  unsigned short *txlen;
  unsigned char *data;
  unsigned char *control;
  unsigned char *status;

  dev = (Ethertap) this->extension;
  txlen =
    (unsigned short *) ((unsigned long) (this->base) + ETHERTAP_TX_SIZE);
  data = (unsigned char *) ((unsigned long) (this->base) + ETHERTAP_TX_DATA);
  control =
    (unsigned char *) ((unsigned long) (this->base) +
                       ETHERTAP_CONTROL_REGISTER);
  status =
    (unsigned char *) ((unsigned long) (this->base) +
                       ETHERTAP_STATUS_REGISTER);

  if (*control & CONTROL_SEND)
    return IO_WRITEFAILED;

  if (!dev->fragment)
  {
    *txlen = 0;
  }
  /* Copy from user memory to device */
  byteCopy (data + *txlen, buffer, *size);

  *txlen += *size;
  if (!(dev->flags & ETHERTAP_FLAG_FRAGMENT))
  {
    *control |= CONTROL_SEND;
    dev->fragment = 0;
  }
  else
  {
    dev->fragment = 1;
  }
  return IO_WRITEOK;
}


Error
ethertap_close (IODevice this)
{
  return IO_CLOSEOK;
}

void
ethertap_handleMsg (IODevice this, Message * msg)
{
  Message reply;
  Ethertap dev;
  unsigned char *addr;

  reply.from = this->threadId;
  CLEAR_MSGENV(reply);
  dev = (Ethertap) this->extension;
  addr =
    (unsigned char *) ((unsigned long) (this->base) + ETHERTAP_HW_ADDRESS);

  //-CJ	printf("ETHERTAP: got message from %lu, type %i\n", msg->from, msg->id);
  switch (msg->id)
  {
  case IO_SUBSCRIBE:
    reply.id = IO_SUBSCRIBEREPLY;
    if (dev->subscribed == 0)
    {
      reply.msg.ioSubscribeReply.errorCode = IO_SUBSCRIBEOK;
      dev->subscribed = msg->from;
    }
    else
    {
      reply.msg.ioSubscribeReply.errorCode = IO_SUBSCRIBEFAILED;
    }
    tmMsgSend (msg->from, &reply); /* XXX this has to be a tmMsgSend,
    											  if not it will crash at a very 
    											  diffrent place, don't ask me why.
    											  THIS IS TEMPORARY */
    break;
  case IO_GETADDR:
    reply.id = IO_GETADDRREPLY;
    reply.msg.ioGetAddrReply.addr[0] = addr[0];
    reply.msg.ioGetAddrReply.addr[1] = addr[1];
    reply.msg.ioGetAddrReply.addr[2] = addr[2];
    reply.msg.ioGetAddrReply.addr[3] = addr[3];
    reply.msg.ioGetAddrReply.addr[4] = addr[4];
    reply.msg.ioGetAddrReply.addr[5] = addr[5];
    tmMsgSend (msg->from, &reply); /* XXX */
    break;
  default:
    reply.id = UNKNOWN_SYSCALL;
    tmMsgSend (msg->from, &reply); /* XXX */
    break;
  }
}

#endif


