/*
    Copyright 2000 (c) by Reto Gaehler, Toni Kaufmann,
    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/TCP/Ports.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: Ports.c,v $
    Revision 1.3  2000/11/15 10:12:14  ruf
    Repository Reorganisation
 
    Revision 1.2  2000/04/03 17:45:30  gfa
    *** empty log message ***
 
    Revision 1.1  2000/03/31 17:50:37  gfa
    Merged with /Net from several term projects
 
*/
#include "Ports.h"

//**************************************************************************************************
//	ptInit
//	initializes a port
//**************************************************************************************************
int ptInit(PortPtr pPort,unsigned int uMaxNrOfMsg)
{
  PortMsgPtr	pNextMsg, pPrevMsg;
  int			nCount;

  if (uMaxNrOfMsg>PORT_MAX_NUM_OF_MSG)
    return PORT_FAILED;
  pPort->pFree=&pPort->aryMsgBlock[0];
  nCount=uMaxNrOfMsg;
  for ( pPrevMsg=pNextMsg=pPort->pFree ;  --nCount > 0  ;  pPrevMsg=pNextMsg )
  {
    pPrevMsg->pNext = ++pNextMsg;
  }
  pPrevMsg->pNext = (PortMsgPtr)NULL;

  semInit(&pPort->recPortSem,1);
  semInit(&pPort->recTxSem,uMaxNrOfMsg);
  semInit(&pPort->recRxSem,0);
  pPort->pMsgHead = pPort->pMsgTail = (PortMsgPtr)NULL;
  pPort->nsSeqChg++;
  pPort->nsMaxMsg = uMaxNrOfMsg;

  return PORT_OK;
}
//**************************************************************************************************
//	ptClear
//**************************************************************************************************
void ptClear(PortPtr pPort, PortStateSet setNewState, void (*dispose)());
void ptClear(PortPtr pPort, PortStateSet setNewState, void (*dispose)())
{
  PortMsgPtr	pMsg;

  if (pPort!=NULL)
  {
    /* put port in limbo until done freeing processes */
    pPort->nsSeqChg++;
    pMsg=pPort->pMsgHead;
    if (pMsg != (PortMsgPtr)NULL)
    {
      if (dispose != PTNODISP)
        for(; pMsg != (PortMsgPtr) NULL ; pMsg=pMsg->pNext)
          (*dispose)( pMsg->pMsg);
      (*dispose)( pMsg->u32Msg);
      pPort->pMsgTail->pNext = pPort->pFree;
      pPort->pFree =pPort->pMsgHead;
    }
    if (setNewState == PORT_ALLOC)
    {
      pPort->pMsgTail = pPort->pMsgHead = (PortMsgPtr) NULL;
      semReset(&pPort->recTxSem, pPort->nsMaxMsg);
      semReset(&pPort->recRxSem, 0);
    }
  }
}
//**************************************************************************************************
//	ptReset
//	resets a port
//**************************************************************************************************
int ptReset(PortPtr pPort, void (*dispose)())
{
  if (pPort!=NULL)
  {
    semWait(&pPort->recPortSem);
    ptClear(pPort, PORT_ALLOC, dispose);
    semSignal(&pPort->recPortSem);
    return PORT_OK;
  }
  return PORT_FAILED;
}
//**************************************************************************************************
//	ptCount
//	returns the number of messages of this port
//**************************************************************************************************
int	ptCount(PortPtr pPort)
{
  int				nsCount;
  int				nsTxCount;

  if (pPort!=NULL)
  {
    semWait(&pPort->recPortSem);
    nsCount = semCount(&pPort->recRxSem);
    nsTxCount = semCount(&pPort->recTxSem);
    if (nsTxCount < 0 )
    {
      nsCount -= nsTxCount;
    }
    semSignal(&pPort->recPortSem);
    return nsCount;
  }
  return PORT_FAILED;
}
//**************************************************************************************************
//	ptReceive
//	receives a Message from this port
//**************************************************************************************************
int		ptReceive(PortPtr pPort, void **msg, unsigned long *u32Msg)
{
  PortMsgPtr		pMsg;
  int				nsSeq;

  if (pPort!=NULL)
  {
    semWait(&pPort->recPortSem);
    /* wait for message and verify that the port is still allocated */

    nsSeq = pPort->nsSeqChg;
    semSignal(&pPort->recPortSem);
    if (semWait(&pPort->recRxSem) == SEM_FAILED || pPort->nsSeqChg != nsSeq)
    {
      return PORT_FAILED;
    }

    semWait(&pPort->recPortSem);
    /* dequeue first message that is waiting in the port */
    pMsg = pPort->pMsgHead;
    *msg = pMsg->pMsg;
    *u32Msg = pMsg->u32Msg;
    if (pPort->pMsgHead == pPort->pMsgTail)	/* delete last item	*/
      pPort->pMsgHead = pPort->pMsgTail = (PortMsgPtr)NULL;
    else
      pPort->pMsgHead = pMsg->pNext;
    pMsg->pNext = pPort->pFree;		/* return to free list	*/
    pPort->pFree = pMsg;
    semSignal(&pPort->recTxSem);
    semSignal(&pPort->recPortSem);
    return PORT_OK;
  }
  return PORT_FAILED;
}
//**************************************************************************************************
//	ptSend
//	sends a Message to this port
//**************************************************************************************************
int		ptSend(PortPtr pPort,void  *msg,unsigned long u32Msg)
{
  int				nsSeq;
  PortMsgPtr		pFreeNode;

  if (pPort!=NULL)
  {
    semWait(&pPort->recPortSem);
    /* wait for space and verify port is still allocated */

    nsSeq = pPort->nsSeqChg;
    semSignal(&pPort->recPortSem);
    if (semWait(&pPort->recTxSem) == SEM_FAILED || pPort->nsSeqChg != nsSeq)
    {
      return PORT_FAILED;
    }
    if (pPort->pFree == NULL)
    {
      return PORT_FAILED;
    }
    semWait(&pPort->recPortSem);
    pFreeNode = pPort->pFree;
    pPort->pFree  = pFreeNode->pNext;
    pFreeNode->pNext = (PortMsgPtr) NULL;
    pFreeNode->pMsg  = msg;
    pFreeNode->u32Msg  = u32Msg;
    if (pPort->pMsgTail == (PortMsgPtr) NULL)	/* empty queue */
      pPort->pMsgTail = pPort->pMsgHead = pFreeNode;
    else
    {
      (pPort->pMsgTail)->pNext = pFreeNode;
      pPort->pMsgTail = pFreeNode;
    }
    semSignal(&pPort->recRxSem);
    semSignal(&pPort->recPortSem);
    return PORT_OK;
  }
  return PORT_FAILED;
}
