/*
** $VER: server.c 1.9 (20 Aug 1995)
**
** magplip.device - Parallel Line Internet Protocol
**
** Original code written by Oliver Wagner and Michael Balzer.
**
** This version has been completely reworked by Marius Gröger, introducing
** slight protocol changes. The new source is a lot better organized and
** maintainable.
**
** Additional changes and code cleanup by Jan Kratochvil and Martin Mares.
** The new source is significantly faster and yet better maintainable.
**
** (C) Copyright 1993-1994 Oliver Wagner & Michael Balzer
** (C) Copyright 1995 Marius Gröger
** (C) Copyright 1995 Jan Kratochvil & Martin Mares
**     All Rights Reserved
**
** $HISTORY:
**
** 20 Aug 1995 : 001.009 :  support for ASM xfer routines
**                          removed obsolete CIA macros (mag/jk/mm)
** 29 Jul 1995 : 001.008 :  support for arbitration delay
**                          symmetrical handling
** 26 Apr 1995 : 001.007 :  _very_ nasty bug would miss packets and get
**                          the driver totally irritated
** 25 Apr 1995 : 001.006 :  now compiles with ANSI and STRICT
**                          fixed bug with resource allocation
** 08 Mar 1995 : 001.005 :  write req. are now handled by device.c
** 06 Mar 1995 : 001.004 :  collision delay added
** 06 Mar 1995 : 001.003 :  hardware transmission errors are no longer retried
**                          because this is any upper layers job
** 04 Mar 1995 : 001.002 :  event tracking *much* more conform to SANA-2
** 18 Feb 1995 : 001.001 :  startup now a bit nicer
**                          using BASEPTR
** 12 Feb 1995 : 001.000 :  reworked original
*/

#define DEBUG 0

/*F*/ /* includes */
#ifndef CLIB_EXEC_PROTOS_H
#include <clib/exec_protos.h>
#include <pragmas/exec_sysbase_pragmas.h>
#endif
#ifndef CLIB_DOS_PROTOS_H
#include <clib/dos_protos.h>
#include <pragmas/dos_pragmas.h>
#endif
#ifndef CLIB_CIA_PROTOS_H
#include <clib/cia_protos.h>
#include <pragmas/cia_pragmas.h>
#endif
#ifndef CLIB_MISC_PROTOS_H
#include <clib/misc_protos.h>
#include <pragmas/misc_pragmas.h>
#endif
#ifndef CLIB_TIME_PROTOS_H
#include <clib/timer_protos.h>
#include <pragmas/timer_pragmas.h>
#endif
#ifndef CLIB_UTILITY_PROTOS_H
#include <clib/utility_protos.h>
#include <pragmas/utility_pragmas.h>
#endif

#ifndef EXEC_MEMORY_H
#include <exec/memory.h>
#endif
#ifndef EXEC_INTERRUPTS_H
#include <exec/interrupts.h>
#endif
#ifndef EXEC_DEVICES_H
#include <exec/devices.h>
#endif
#ifndef EXEC_IO_H
#include <exec/io.h>
#endif

#ifndef DEVICES_SANA2_H
#include <devices/sana2.h>
#endif

#ifndef HARDWARE_CIA_H
#include <hardware/cia.h>
#endif

#ifndef RESOURCES_MISC_H
#include <resources/misc.h>
#endif

#ifndef _STRING_H
#include <string.h>
#endif

#ifndef __MAGPLIP_H
#include "magplip.h"
#endif
#ifndef __DEBUG_H
#include "debug.h"
#endif
/*E*/

/*F*/ /* defines, types and enums */

   /*
   ** return codes for arbitratedwrite()
   */
typedef enum { AW_OK, AW_ABORTED, AW_BUFFER_ERROR, AW_ERROR } AW_RESULT;

/*E*/
/*F*/ /* imports */
extern VOID dotracktype(BASEPTR, ULONG type, ULONG ps, ULONG pr, ULONG bs, ULONG br, ULONG pd);
extern VOID DevTermIO(BASEPTR, struct IOSana2Req *ios2);
extern USHORT ASM CRC16(REG(a0) UBYTE *, REG(d0) SHORT);
extern BOOL ASM hwsend(REG(a0) BASEPTR);
extern BOOL ASM hwrecv(REG(a0) BASEPTR);
extern VOID ASM interrupt(REG(a1) BASEPTR);

extern FAR volatile struct CIA ciaa,ciab;
/*E*/
/*F*/ /* exports */
extern VOID __saveds ServerTask(VOID);
/*E*/
/*F*/ /* statics */
static struct PLIPBase *startup(void);
static REGARGS VOID DoEvent(BASEPTR, long event);
static VOID readargs(BASEPTR);
static BOOL init(BASEPTR);
static BOOL hwattach(BASEPTR);
static VOID hwdetach(BASEPTR);
static REGARGS BOOL goonline(BASEPTR);
static REGARGS VOID gooffline(BASEPTR);
static REGARGS AW_RESULT arbitratedwrite(BASEPTR, struct IOSana2Req *ios2);
static REGARGS VOID dowritereqs(BASEPTR);
static REGARGS VOID doreadreqs(BASEPTR);
static REGARGS VOID dos2reqs(BASEPTR);
/*E*/

/*F*/ /* CIA access macros & functions */

#define CLEARINT        SetICR(CIAABase, CIAICRF_FLG)
#define DISABLEINT      AbleICR(CIAABase, CIAICRF_FLG)
#define ENABLEINT       AbleICR(CIAABase, CIAICRF_FLG | CIAICRF_SETCLR)

#define SETCIAOUTPUT    ciaa.ciaddrb = 0xFF
#define SETCIAINPUT     ciaa.ciaddrb = 0x00
#define PARINIT(b)      SETCIAINPUT;                                       \
                        ciab.ciaddra &= ~((b)->pb_HandshakeMask[HS_LINE]); \
                        ciab.ciaddra |= (b)->pb_HandshakeMask[HS_REQUEST]

#define TESTLINE(b)     (ciab.ciapra & (b)->pb_HandshakeMask[HS_LINE])
#define SETREQUEST(b)   ciab.ciapra |= (b)->pb_HandshakeMask[HS_REQUEST]
#define CLEARREQUEST(b) ciab.ciapra &= ~((b)->pb_HandshakeMask[HS_REQUEST])

/*E*/

   /*
   ** functions to gain hardware, initialise communication
   ** and to release hardware
   */
/*F*/ static BOOL hwattach(BASEPTR)
{
   BOOL rc = FALSE;

   d(("entered\n"));

   if (MiscBase = OpenResource("misc.resource"))
   {
      if (CIAABase = OpenResource("ciaa.resource"))
      {
         CiaBase = CIAABase;

         d(("ciabase is %lx\n",CiaBase));

         /* obtain exclusive access to the parallel hardware */
         if (!AllocMiscResource(MR_PARALLELPORT, pb->pb_DevNode.lib_Node.ln_Name))
         {
            pb->pb_AllocFlags |= 1;
            if (!AllocMiscResource(MR_PARALLELBITS, pb->pb_DevNode.lib_Node.ln_Name))
            {
               pb->pb_AllocFlags |= 2;

               /* Add our interrupt to handle CIAICRB_FLG.
               ** This is also cia.resource means of granting exclusive
               ** access to the related registers in the CIAs.
               */
               pb->pb_Interrupt.is_Node.ln_Type = NT_INTERRUPT;
               pb->pb_Interrupt.is_Node.ln_Pri  = 127;
               pb->pb_Interrupt.is_Node.ln_Name = SERVERTASKNAME;
               pb->pb_Interrupt.is_Data         = (APTR)pb;
               pb->pb_Interrupt.is_Code         = (VOID (*)())&interrupt;

               /* We must Disable() bcos there could be an interrupt already
               ** waiting for us. We may, however, not Able/SetICR() before
               ** we have access!
               */
               Disable();
               if (!AddICRVector(CIAABase, CIAICRB_FLG, &pb->pb_Interrupt))
               {
                  DISABLEINT;                       /* this is what I meant */
                  rc = TRUE;
               }
               Enable();

               if (rc)
               {
                  pb->pb_AllocFlags |= 4;
                  PARINIT(pb);    /* cia to input, handshake in/out setting */
                  CLEARREQUEST(pb);                /* setup handshake lines */
                  CLEARINT;                         /* clear this interrupt */
                  ENABLEINT;                            /* allow interrupts */
               }

            }
            else
               d(("no parallelbits\n"));
         }
         else
            d(("no parallelport\n"));
      }
      else
         d(("no misc resource\n"));
   }
   else
      d(("no misc resource\n"));

   return rc;
}
/*E*/
/*F*/ static VOID hwdetach(BASEPTR)
{
   if (pb->pb_AllocFlags & 4)
   {
      DISABLEINT;
      CLEARINT;
      RemICRVector(CIAABase, CIAICRB_FLG, &pb->pb_Interrupt);
   }

   if (pb->pb_AllocFlags & 2) FreeMiscResource(MR_PARALLELBITS);

   if (pb->pb_AllocFlags & 1) FreeMiscResource(MR_PARALLELPORT);

   pb->pb_AllocFlags = 0;
}
/*E*/

   /*
   ** functions to go online/offline
   */
/*F*/ static REGARGS VOID rejectpackets(BASEPTR)
{
   struct IOSana2Req *ios2;

   ObtainSemaphore(&pb->pb_WriteListSem);
   while(ios2 = (struct IOSana2Req *)RemHead((struct List*)&pb->pb_WriteList))
   {
      ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
      ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
      DevTermIO(pb,ios2);
   }
   ReleaseSemaphore(&pb->pb_WriteListSem);

   ObtainSemaphore(&pb->pb_ReadListSem);
   while(ios2 = (struct IOSana2Req *)RemHead((struct List*)&pb->pb_ReadList))
   {
      ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
      ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
      DevTermIO(pb,ios2);
   }
   ReleaseSemaphore(&pb->pb_ReadListSem);

   ObtainSemaphore(&pb->pb_ReadOrphanListSem);
   while(ios2 = (struct IOSana2Req *)RemHead((struct List*)&pb->pb_ReadOrphanList))
   {
      ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
      ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
      DevTermIO(pb,ios2);
   }
   ReleaseSemaphore(&pb->pb_ReadOrphanListSem);
}
/*E*/
/*F*/ static REGARGS BOOL goonline(BASEPTR)
{
   BOOL rc = FALSE;

   d(("trying to go online\n"));

   if (pb->pb_Flags & (PLIPF_OFFLINE | PLIPF_NOTCONFIGURED))
   {
      if (!hwattach(pb))
      {
         d(("error going online\n"));
      }
      else
      {
         GetSysTime(&pb->pb_DevStats.LastStart);
         pb->pb_Flags &= ~(PLIPF_OFFLINE | PLIPF_NOTCONFIGURED);
         DoEvent(pb, S2EVENT_ONLINE);
         rc = TRUE;
         d(("i'm now online!\n"));
      }
   }

   return rc;
}
/*E*/
/*F*/ static REGARGS VOID gooffline(BASEPTR)
{
   if (!(pb->pb_Flags & (PLIPF_OFFLINE | PLIPF_NOTCONFIGURED)))
   {
      hwdetach(pb);

      pb->pb_Flags |= PLIPF_OFFLINE;

      DoEvent(pb, S2EVENT_OFFLINE);
   }
   d(("ok!\n"));
}
/*E*/

   /*
   ** SANA-2 Event management
   */
/*F*/ static REGARGS VOID DoEvent(BASEPTR, long event)
{
   struct IOSana2Req *ior, *ior2;

   d(("event is %lx\n",event));

   ObtainSemaphore(&pb->pb_EventListSem );
   
   for(ior = (struct IOSana2Req *) pb->pb_EventList.lh_Head;
       ior2 = (struct IOSana2Req *) ior->ios2_Req.io_Message.mn_Node.ln_Succ;
       ior = ior2 )
   {
      if (ior->ios2_WireError & event)
      {
         Remove((struct Node*)ior);
         DevTermIO(pb, ior);
      }
   }
   
   ReleaseSemaphore(&pb->pb_EventListSem );
}
/*E*/

   /*
   ** writing packets
   */
/*F*/ static REGARGS AW_RESULT arbitratedwrite(BASEPTR, struct IOSana2Req *ios2)
{
   BOOL having_line;
   AW_RESULT rc;

   /*
   ** Arbitration
   ** ===========================================================
   **
   ** Pseudo code of the arbitration:
   **
   **  if LINE is high (other side is ready to receive) then
   **     set REQUEST high (tell the other side we're ready to send)
   **     if LINE is high (other side is still ready to receive) then
   **        we have the line, do transfer
   **     reset REQUEST to low (tell other side we're ready to receive)
   **
   **    AW_OK             if we could transmit all the data correctly
   **    AW_BUFF_ERROR     if the BufferManagement callback failed
   **    AW_ERROR          if we got the line, but the actual transfer
   **                      failed, perhaps due to a timeout
   **    AW_ABORT          if we couldn't get the line
   */

   having_line = FALSE;

   if (!TESTLINE(pb))                               /* is the line free ? */
   {
      SETREQUEST(pb);                     /* indicate our request to send */
      
      if (pb->pb_ArbitrationDelay > 0)
      {
         pb->pb_TimeReq.tr_time.tv_secs    = 0;
         pb->pb_TimeReq.tr_time.tv_micro   = pb->pb_ArbitrationDelay;
         pb->pb_TimeReq.tr_node.io_Command = TR_ADDREQUEST;
         DoIO((struct IORequest*)&pb->pb_TimeReq);
      }

      if (!TESTLINE(pb))                      /* is the line still free ? */
         having_line = TRUE;
      else
      {
         if (!(pb->pb_Flags & PLIPF_RECEIVING))
            CLEARREQUEST(pb);                         /* reset line state */
         d2(("couldn't get the line-1\n"));
      }
   }
   else d2(("couldn't get the line-2\n"));

   if (having_line)
   {
      struct BufferManagement *bm;

      if (!(pb->pb_Flags & PLIPF_RECEIVING))
      {
         d(("having line for: typ %08lx, size %ld\n",ios2->ios2_PacketType,
                                                      ios2->ios2_DataLength));

         pb->pb_SendFrame.pf_Type = ios2->ios2_PacketType;
         pb->pb_SendFrame.pf_Size = ios2->ios2_DataLength + PKTFRAMESIZE_2;

         bm = (struct BufferManagement *)ios2->ios2_BufferManagement;

         if (!(*bm->bm_CopyFromBuffer)((UBYTE*)pb->pb_SendFrame.pf_Data,
                                     ios2->ios2_Data, ios2->ios2_DataLength))
         {
            rc = AW_BUFFER_ERROR;
            CLEARREQUEST(pb);                         /* reset line state */
         }
         else
            rc = hwsend(pb) ? AW_OK : AW_ERROR;
      }
      else
      {
         d4(("arbitration error!\n"));
         rc = AW_ABORTED;
      }
   }
   else
      rc = AW_ABORTED;

   return rc;
}
/*E*/
/*F*/ static REGARGS VOID dowritereqs(BASEPTR)
{
   struct IOSana2Req *currentwrite, *nextwrite;
   AW_RESULT code;

   ObtainSemaphore(&pb->pb_WriteListSem);

   for(currentwrite = (struct IOSana2Req *)pb->pb_WriteList.lh_Head;
       nextwrite = (struct IOSana2Req *) currentwrite->ios2_Req.io_Message.mn_Node.ln_Succ;
       currentwrite = nextwrite )
   {
      if (pb->pb_Flags & PLIPF_RECEIVING)
      {
         d(("incoming data!"));
         break;
      }

      code = arbitratedwrite(pb, currentwrite);

      if (code == AW_ABORTED)                         /* arbitration failed */
      {
         pb->pb_Flags |= PLIPF_COLLISION;
         d(("couldn't get the line, trying again later\n"));
         if ((currentwrite->ios2_Req.io_Error++) > pb->pb_Retries)
         {
            currentwrite->ios2_Req.io_Error = S2ERR_TX_FAILURE;
            currentwrite->ios2_WireError = S2WERR_TOO_MANY_RETIRES;
            Remove((struct Node*)currentwrite);
            DevTermIO(pb, currentwrite);
         }
         break;
      }
      else if (code == AW_BUFFER_ERROR)  /* BufferManagement callback error */
      {
         d(("buffer error\n"));
         DoEvent(pb, S2EVENT_ERROR | S2EVENT_BUFF | S2EVENT_SOFTWARE);
         currentwrite->ios2_Req.io_Error = S2ERR_SOFTWARE;
         currentwrite->ios2_WireError = S2WERR_BUFF_ERROR;
         Remove((struct Node*)currentwrite);
         DevTermIO(pb, currentwrite);
      }
      else if (code == AW_ERROR)
      {
         /*
         ** this is a real line error, upper levels (e.g. Internet TCP) have
         ** to care for reliability!
         */
         d(("error transmitting packet\n"));
         DoEvent(pb, S2EVENT_ERROR | S2EVENT_TX | S2EVENT_HARDWARE);
         currentwrite->ios2_Req.io_Error = S2ERR_TX_FAILURE;
         currentwrite->ios2_WireError = S2WERR_GENERIC_ERROR;
         Remove((struct Node*)currentwrite);
         DevTermIO(pb, currentwrite);
      }
      else /*if (code == AW_OK)*/                             /* well done! */
      {
         d(("packet transmitted successfully\n"));
         pb->pb_DevStats.PacketsSent++;
         dotracktype(pb, pb->pb_SendFrame.pf_Type, 1, 0, currentwrite->ios2_DataLength, 0, 0);
         currentwrite->ios2_Req.io_Error = S2ERR_NO_ERROR;
         currentwrite->ios2_WireError = S2WERR_GENERIC_ERROR;
         Remove((struct Node*)currentwrite);
         DevTermIO(pb, currentwrite);
      }
   }

   ReleaseSemaphore(&pb->pb_WriteListSem);
}
/*E*/

   /*
   ** reading packets
   */
/*F*/ static REGARGS VOID doreadreqs(BASEPTR)
{
   LONG datasize;
   struct IOSana2Req *got;
   ULONG pkttyp;
   struct BufferManagement *bm;

   if (hwrecv(pb))
   {
      pb->pb_DevStats.PacketsReceived++;

      datasize = pb->pb_ReceiveFrame.pf_Size - PKTFRAMESIZE_2;

      dotracktype(pb, pkttyp = pb->pb_ReceiveFrame.pf_Type, 0, 1, 0, datasize, 0);

      d(("packet %08lx, size %ld received\n",pkttyp,datasize));

      ObtainSemaphore(&pb->pb_ReadListSem);

      for(got = (struct IOSana2Req *)pb->pb_ReadList.lh_Head;
          got->ios2_Req.io_Message.mn_Node.ln_Succ;
          got = (struct IOSana2Req *)got->ios2_Req.io_Message.mn_Node.ln_Succ )
      {
         if (got->ios2_PacketType == pkttyp )
         {
            Remove((struct Node*)got);

            bm = (struct BufferManagement *)got->ios2_BufferManagement;

            if (!(*bm->bm_CopyToBuffer)(got->ios2_Data, (UBYTE*)pb->pb_ReceiveFrame.pf_Data, datasize))
            {
               d(("CopyToBuffer: error\n"));
               got->ios2_Req.io_Error = S2ERR_SOFTWARE;
               got->ios2_WireError = S2WERR_BUFF_ERROR;
               DoEvent(pb, S2EVENT_ERROR | S2EVENT_BUFF | S2EVENT_SOFTWARE);
            }
            else
            {
               got->ios2_Req.io_Error = got->ios2_WireError = 0;
            }

            got->ios2_Req.io_Flags = 0;
            got->ios2_SrcAddr[0] = (pb->pb_Flags & PLIPF_SIDEA) ?  0     : (1<<7);
            got->ios2_DstAddr[0] = (pb->pb_Flags & PLIPF_SIDEA) ? (1<<7) :  0    ;
            got->ios2_DataLength = datasize;

            d(("packet received, satisfying S2Request\n"));
            DevTermIO(pb, got);
            got = NULL;
            break;
         }
      }

      ReleaseSemaphore(&pb->pb_ReadListSem);
   }
   else
   {
      DoEvent(pb, S2EVENT_HARDWARE | S2EVENT_ERROR | S2EVENT_RX);
      
      got = NULL;
      pb->pb_DevStats.BadData++;
   }
   
   if (got)
   {
      d(("unknown packet\n"));

      pb->pb_DevStats.UnknownTypesReceived++;
      
      ObtainSemaphore(&pb->pb_ReadOrphanListSem);
      got = (struct IOSana2Req *)RemHead((struct List*)&pb->pb_ReadOrphanList);
      ReleaseSemaphore(&pb->pb_ReadOrphanListSem);

      if (got)
      {
         bm = (struct BufferManagement *)got->ios2_BufferManagement;
         if (!(*bm->bm_CopyToBuffer)(got->ios2_Data, (UBYTE*)pb->pb_ReceiveFrame.pf_Data, datasize))
         {
            got->ios2_Req.io_Error = S2ERR_SOFTWARE;
            got->ios2_WireError = S2WERR_BUFF_ERROR;
         }
         else
         {
            got->ios2_Req.io_Error = got->ios2_WireError = 0;
         }
         
         got->ios2_Req.io_Flags = 0;
         got->ios2_SrcAddr[0] = (pb->pb_Flags & PLIPF_SIDEA) ?  0     : (1<<7);
         got->ios2_DstAddr[0] = (pb->pb_Flags & PLIPF_SIDEA) ? (1<<7) :  0    ;
         got->ios2_DataLength = datasize;

         d(("orphan read\n"));

         DevTermIO(pb, got);
      }
      else
      {
         dotracktype(pb, pkttyp, 0, 0, 0, 0, 1);
         d(("packet thrown away...\n"));
      }
   }
}
/*E*/

   /*
   ** 2nd level device command dispatcher (~SANA2IOF_QUICK)
   */
/*F*/ static REGARGS VOID dos2reqs(BASEPTR)
{
   struct IOSana2Req *ios2;

   /*
   ** Every pending IO message will be GetMsg()'ed and processed. At the
   ** end of the loop it will be DevTermIO()'ed back to the sender,
   ** _but_only_if_ it is non-NULL. In such cases the message has been
   ** put in a separate queue to be DevTermIO()'ed later (i.e. CMD_WRITEs
   ** and similar stuff).
   ** You find the same mimique in the 1st level dispatcher (device.c)
   */
   while(ios2 = (struct IOSana2Req *)GetMsg(pb->pb_ServerPort))
   {
      if (pb->pb_Flags & PLIPF_RECEIVING)
      {
         d(("incoming data!"));
         break;
      }

      d(("sana2req %ld from serverport\n", ios2->ios2_Req.io_Command));

      switch (ios2->ios2_Req.io_Command)
      {
         case S2_ONLINE:
            if (!goonline(pb))
            {
               ios2->ios2_Req.io_Error = S2ERR_NO_RESOURCES;
               ios2->ios2_WireError = S2WERR_GENERIC_ERROR;
            }
         break;

         case S2_OFFLINE:
            gooffline(pb);
            rejectpackets(pb); /* reject all pending requests */
         break;

         case S2_CONFIGINTERFACE:
            if (pb->pb_Flags & PLIPF_NOTCONFIGURED)
            {
               ios2->ios2_SrcAddr[0] = (pb->pb_Flags & PLIPF_SIDEA) ? (1<<7) : 0;
               
               if (!goonline(pb))
               {
                  ios2->ios2_Req.io_Error = S2ERR_NO_RESOURCES;
                  ios2->ios2_WireError = S2WERR_GENERIC_ERROR;
               }
            }
            else
            {
               ios2->ios2_Req.io_Error = S2ERR_BAD_STATE;
               ios2->ios2_WireError = S2WERR_IS_CONFIGURED;
            }
         break;
      }

      if (ios2) DevTermIO(pb,ios2);
   }
}
/*E*/

   /*
   ** startup,initialisation and termination functions
   */
/*F*/ static struct PLIPBase *startup(void)
{
   struct ServerStartup *ss;
   struct Process *we;
   struct PLIPBase *base;
   LOCALSYSBASE;

   we = (struct Process*)FindTask(NULL);

   d(("waiting for startup msg...\n"));
   WaitPort(&we->pr_MsgPort);
   ss = (struct ServerStartup *)GetMsg(&we->pr_MsgPort);
   base = ss->ss_PLIPBase;
   d(("go startup msg at %lx, PLIPBase is %lx\n", ss, ss->ss_PLIPBase));
   ReplyMsg((struct Message*)ss);

   return base;
}
/*E*/
/*F*/ static VOID readargs(BASEPTR)
{
   struct RDArgs *rda;
   struct PLIPConfig args = { 0 };
   BPTR plipvar, oldinput;

   d(("entered\n"));

   if (plipvar = Open(CONFIGFILE, MODE_OLDFILE))
   {
      oldinput = SelectInput(plipvar);
      
      rda = ReadArgs(TEMPLATE , (LONG *)&args, NULL);
      
      if(rda)
      {
         if (args.timeout)
            pb->pb_Timeout = *args.timeout;

         if (args.priority)
            SetTaskPri((struct Task*)pb->pb_Server, *args.priority);

         if (args.mtu && (*args.mtu <= PLIP_MAXMTU))
            pb->pb_MTU = *args.mtu;

         if (args.bps)
            pb->pb_ReportBPS = *args.bps;

         if (args.retries &&  (*args.retries <= PLIP_MAXRETRIES))
            pb->pb_Retries = *args.retries;

         if (args.sendcrc)
            pb->pb_Flags |= PLIPF_SENDCRC;
          else
            pb->pb_Flags &= ~PLIPF_SENDCRC;

         if (args.collisiondelay)
            pb->pb_CollisionDelay = *args.collisiondelay;
         else
            pb->pb_CollisionDelay = PLIP_DEFDELAY +
                        (pb->pb_Flags & PLIPF_SIDEA) ? PLIP_DELAYDIFF : 0;

         if (args.arbitrationdelay)
            pb->pb_ArbitrationDelay = *args.arbitrationdelay;
         else
            pb->pb_ArbitrationDelay = PLIP_DEFARBITRATIONDELAY;

         FreeArgs(rda);
      }

      Close(SelectInput(oldinput));
   }

   d(("timeout %ld, pri %ld, mtu %ld, bps %ld, retries %ld, flags %08lx, delay %ld\n",
      pb->pb_Timeout, (LONG)pb->pb_Server->pr_Task.tc_Node.ln_Pri, pb->pb_MTU, pb->pb_ReportBPS, pb->pb_Retries,
      pb->pb_Flags, pb->pb_CollisionDelay));

   d(("left\n"));

}
/*E*/
/*F*/ static BOOL init(BASEPTR)
{
   BOOL rc = FALSE;

   if ((pb->pb_IntSig = AllocSignal(-1)) != -1)
   {
      pb->pb_IntSigMask = 1L << pb->pb_IntSig;
   
      if (pb->pb_ServerPort = CreateMsgPort())
      {
         if (pb->pb_TimerPort = CreateMsgPort())
         {
            memset((VOID*)&pb->pb_TimeReq, 0, sizeof(pb->pb_TimeReq));
            pb->pb_TimeReq.tr_node.io_Message.mn_ReplyPort = pb->pb_TimerPort;
            if (!OpenDevice( "timer.device", UNIT_MICROHZ, (struct IORequest*)&pb->pb_TimeReq, 0 ))
            {
               TimerBase = (struct Library *)pb->pb_TimeReq.tr_node.io_Device;
               readargs(pb);
               rc = TRUE;
            }
            else
            {
               d(("couldn't open timer.device"));
            }
         }
         else
         {
            d(("no timer port\n"));
         }
      }
      else
      {
         d(("no server port\n"));
      }
   }
   else
   {
      d(("no signal\n",rc));
   }

   d(("left %ld\n",rc));

   return rc;
}
/*E*/
/*F*/ static VOID cleanup(BASEPTR)
{
   struct BufferManagement *bm;

   gooffline(pb);

   while(bm = (struct BufferManagement *)RemHead((struct List *)&pb->pb_BufferManagement))
      FreeVec(bm);

   if (TimerBase) CloseDevice((struct IORequest*)&pb->pb_TimeReq);
   if (pb->pb_TimerPort) DeleteMsgPort(pb->pb_TimerPort);

   if (pb->pb_ServerPort) DeleteMsgPort(pb->pb_ServerPort);
   if (pb->pb_IntSig != -1) FreeSignal(pb->pb_IntSig);
}
/*E*/

   /*
   ** entry point, mainloop
   */
/*F*/ extern VOID SAVEDS ServerTask(void)
{
   BASEPTR;

   d(("server running\n"));

   if (pb = startup())
   {
      if (init(pb))
      {
         ULONG recv, portsigmask, timersigmask, wmask;
         BOOL running, timerqueued = FALSE;

         portsigmask  = 1 << pb->pb_ServerPort->mp_SigBit;
         timersigmask = 1 << pb->pb_TimerPort->mp_SigBit;
      
         wmask = SIGBREAKF_CTRL_F | SIGBREAKF_CTRL_C | pb->pb_IntSigMask | portsigmask | timersigmask;

         for(running=TRUE;running;)
         {
            if (!(pb->pb_Flags & PLIPF_RECEIVING))
               recv = Wait(wmask);
            else
               SetSignal(0, pb->pb_IntSigMask);

            /*if (recv & pb->pb_IntSigMask)*/
            if (pb->pb_Flags & PLIPF_RECEIVING)
            {
               d(("received an interrupt\n"));
               doreadreqs(pb);
            }

            if (recv & portsigmask)
            {
               d(("SANA-II request(s)\n"));
               dos2reqs(pb);
            }

            if (recv & timersigmask)
            {
               /* pop message */
               AbortIO((struct IORequest*)&pb->pb_TimeReq);
               WaitIO((struct IORequest*)&pb->pb_TimeReq);
               timerqueued = FALSE;
               d(("timer wakeup\n"));
            }

               /* try now to do write requests (if any pending) */
            if (!timerqueued)
            {
               dowritereqs(pb);

                  /* don't let the other side wait too long! */
               if (pb->pb_Flags & PLIPF_RECEIVING)
               {
                  d(("received an interrupt\n"));
                  SetSignal(0, pb->pb_IntSigMask);
                  doreadreqs(pb);
               }

               /*
               ** Possible a collision has occurred, which is indicated by a
               ** special flag in PLIPBase.
               **
               ** Using timer.device we periodically will be waked up. This
               ** allows us to delay write packets in cases when we cannot get
               ** the line immediately.
               **
               ** If client and server are very close together, regarding the point
               ** of performance, the same delay time could even force multiple
               ** collisions (at least theoretical, I made no practical tests).
               ** Probably a CSMA/CD-like random-timed delay would be ideal.
               */
               if (pb->pb_Flags & PLIPF_COLLISION)
               {
                  extern void KPrintF(char *,...);

                  pb->pb_Flags &= ~PLIPF_COLLISION;
                  pb->pb_TimeReq.tr_time.tv_secs    = 0;
                  pb->pb_TimeReq.tr_time.tv_micro   = pb->pb_CollisionDelay;
                  pb->pb_TimeReq.tr_node.io_Command = TR_ADDREQUEST;
                  /*KPrintF("waiting %ld/%ld\n", pb->pb_TimeReq.tr_time.tv_secs, pb->pb_TimeReq.tr_time.tv_micro);*/
                  SendIO((struct IORequest*)&pb->pb_TimeReq);
                  timerqueued = TRUE;
               }
            }

            if (recv & SIGBREAKF_CTRL_C)
            {
               d(("received break signal\n"));
               running = FALSE;
            }
         }

         if (timerqueued)
         {
               /* finnish pending timer requests */
            AbortIO((struct IORequest*)&pb->pb_TimeReq);
            WaitIO((struct IORequest*)&pb->pb_TimeReq);
         }
      }
      else
         d(("init() failed\n"));

      d(("cleaning up\n"));
      cleanup(pb);

            /* Exec will enable it's scheduler after we're dead. */
      Forbid();
            /* signal mother we're done */
      if (pb->pb_ServerStoppedSigMask)
         Signal(pb->pb_Task, pb->pb_ServerStoppedSigMask);
      pb->pb_Flags |= PLIPF_SERVERSTOPPED;
   }
   else
      d(("no startup packet\n"));
}
/*E*/

