
/* Serial.c By Kamran Karimi 
   Part of EasyAmiga source.
*/
#include <dos.h>

#include <exec/types.h>
#include <exec/io.h>

#include <devices/serial.h>
#include <devices/timer.h>

#include <proto/exec.h>

#include <hardware/custom.h>
#include <hardware/intbits.h>

extern struct Custom far custom;
#define RBF 14

#include "ErrCodes.h"

#define min(a,b) (((a) <= (b)) ? (a) : (b))

extern struct PacketStruct Packet; 

#define SERTESTLEN 255
#define PACKET_SIZE 255
#define RECBUFFLEN 260 /* PACKET_SIZE + 5 */
struct PacketStruct
{
 UBYTE Len;
 UBYTE Data[PACKET_SIZE + 2];
};

extern VOID Message(LONG MessageNum);
extern LONG CheckEsc(VOID);
extern VOID ShowTransfer(int Direction, UBYTE Len);

extern ULONG WorkstationWait[MAXHARDSPEED];

extern SHORT CheckSumTry; 
extern char SerialName[];
extern SHORT UnitNum;
extern ULONG Speed[MAXHARDSPEED];
extern SHORT CurrSpeed,DelayTime;

struct MsgPort *SerialMP = NULL;
struct IOExtSer *SerialIO = NULL;

struct MsgPort *TimerMP = NULL;
struct timerequest *TimerIO = NULL;

ULONG Serial_OK = FALSE;
ULONG Timer_OK = FALSE;
ULONG SystemRequest = FALSE;

LONG OpenTimer(VOID)
{
 LONG Unit = UNIT_VBLANK;

 if(TimerMP = CreatePort(NULL,0))
 {
  if(TimerIO = (struct timerequest *) 
                           CreateExtIO(TimerMP,sizeof(struct timerequest)))
  {
   if(!OpenDevice("timer.device",Unit,(struct IORequest *)TimerIO,0))
    {
     Timer_OK = TRUE; 
     return NO_ERROR;
    }
   else return ERROR_OpenDevice;
  }
  else return ERROR_CreateExtIO;
 }
 else return ERROR_CreatePort;
}


VOID CloseTimer(VOID)
{
 if(Timer_OK) 
 { 
  CloseDevice((struct IORequest *)TimerIO);
  Timer_OK = FALSE;
 }
 if(TimerIO) { DeleteExtIO((struct IORequest *)TimerIO); TimerIO = NULL; }
 if(TimerMP) { DeletePort(TimerMP); TimerMP = NULL; }
}


LONG OpenSerial(VOID)
{
 if(SerialMP = CreatePort(NULL,0))
 {
  if(SerialIO = (struct IOExtSer *)
                                CreateExtIO(SerialMP,sizeof(struct IOExtSer)))
  {
   SerialIO->io_SerFlags = SERF_XDISABLED;
   if(!OpenDevice(SerialName,UnitNum,(struct IORequest *)SerialIO,0))
    {
     Serial_OK = TRUE; 
     return NO_ERROR;
    }
   else return ERROR_OpenDevice;
  }
  else return ERROR_CreateExtIO;
 }
 else return ERROR_CreatePort;
}


LONG ActualSendPacket(USHORT Len)
{
 ULONG Sig;
 LONG Error;

 if(Serial_OK)
 {
  if((Error = CheckEsc())) return Error;
  if(OpenTimer()) { CloseTimer(); return ERROR_Timer; }
  SerialIO->IOSer.io_Command = CMD_WRITE;
  SerialIO->IOSer.io_Length = Len;
  SerialIO->IOSer.io_Data = (APTR)Packet.Data;
  SendIO((struct IORequest *)SerialIO);
  TimerIO->tr_node.io_Command = TR_ADDREQUEST;
  TimerIO->tr_time.tv_secs = 
          (ULONG)( DelayTime + 2 + Len / WorkstationWait[CurrSpeed]);
  TimerIO->tr_time.tv_micro = 5;
  SendIO((struct IORequest *)TimerIO);
  Sig = Wait(( 1 << SerialMP->mp_SigBit) | (1 << TimerMP->mp_SigBit));
  if(Sig & (1 << TimerMP->mp_SigBit))
  {
   AbortIO((struct IORequest *)SerialIO);
   WaitIO((struct IORequest *)SerialIO);

   WaitIO((struct IORequest *)TimerIO);
   CloseTimer(); 
   return ERROR_Timeout;
  }
  else
  {
   WaitIO((struct IORequest *)SerialIO);

   AbortIO((struct IORequest *)TimerIO);
   WaitIO((struct IORequest *)TimerIO);
   CloseTimer();
   return NO_ERROR;
  }
 }
 else return ERROR_NoDevice;
} 

/*
LONG ActualReceivePacketHard(USHORT Len)
{
 LONG Error;
 USHORT Enable,Offset;
 struct Message *TimerMsg;

 if(Serial_OK)
 {
  if((Error = CheckEsc())) return Error;
  if(OpenTimer()) { CloseTimer(); return ERROR_Timer; }
  Enable = custom.intenar;
  Enable &= INTF_RBF;
  custom.intena = INTF_RBF; 
  Offset = 0;
   TimerIO->tr_node.io_Command = TR_ADDREQUEST;
  if(SystemRequest)
   TimerIO->tr_time.tv_secs = 0;
  else
   TimerIO->tr_time.tv_secs = 
            (ULONG)(DelayTime + 2 + Len / WorkstationWait[CurrSpeed]);
  if(SystemRequest)
   TimerIO->tr_time.tv_micro = 500;
  else
   TimerIO->tr_time.tv_micro = 5;
  SendIO((struct IORequest *)TimerIO);
  while(!(TimerMsg = GetMsg(TimerMP)))
  {
   if(custom.serdatr & (1 << RBF))
   {
    Packet.Data[Offset] = (UBYTE)(custom.serdatr & 0xFF);
    Offset++;
   }
   if(Offset >= Len) break;
  }
  if(Enable)
   custom.intena = INTF_RBF | INTF_SETCLR;
  if(TimerMsg) 
  {
   ReplyMsg(TimerMsg);
   CloseTimer();
   return ERROR_Timeout;
  }
  else
  {
   AbortIO((struct IORequest *)TimerIO);
   WaitIO((struct IORequest *)TimerIO);
   CloseTimer();   
   return NO_ERROR;
  }
 }
 else return ERROR_NoDevice;
}
*/

LONG ActualReceivePacket(USHORT Len)
{
 ULONG Sig;
 LONG Error;

 if(Serial_OK)
 {
  if((Error = CheckEsc())) return Error;
  if(OpenTimer()) { CloseTimer(); return ERROR_Timer; }
  SerialIO->IOSer.io_Command = CMD_READ;
  SerialIO->IOSer.io_Length = Len;
  SerialIO->IOSer.io_Data = (APTR)Packet.Data;
  SendIO((struct IORequest *)SerialIO);
  TimerIO->tr_node.io_Command = TR_ADDREQUEST;
  if(SystemRequest)
   TimerIO->tr_time.tv_secs = 0;
  else
   TimerIO->tr_time.tv_secs = 
            (ULONG)(DelayTime + 2 + Len / WorkstationWait[CurrSpeed]);
  if(SystemRequest)
   TimerIO->tr_time.tv_micro = 500;
  else
   TimerIO->tr_time.tv_micro = 5;
  SendIO((struct IORequest *)TimerIO);
  Sig = Wait(( 1 << SerialMP->mp_SigBit) | (1 << TimerMP->mp_SigBit));
  if(Sig & (1 << TimerMP->mp_SigBit))
  {
   AbortIO((struct IORequest *)SerialIO);
   WaitIO((struct IORequest *)SerialIO);

   WaitIO((struct IORequest *)TimerIO); 
   CloseTimer();
   return ERROR_Timeout;
  }
  else
  {
   WaitIO((struct IORequest *)SerialIO);

   AbortIO((struct IORequest *)TimerIO);
   WaitIO((struct IORequest *)TimerIO);
   CloseTimer();   
   return NO_ERROR;
  }
 }
 else return ERROR_NoDevice;
} 


VOID ClrSer(VOID)
{
 LONG UnRead,Read;
 char ReadBuff[RECBUFFLEN];

 if(Serial_OK)
 {
  SerialIO->IOSer.io_Command = SDCMD_QUERY;
  DoIO((struct IORequest *)SerialIO);
  UnRead =  SerialIO->IOSer.io_Actual;
  while(UnRead > 0)
  {
   Read = min(UnRead,RECBUFFLEN);
   SerialIO->IOSer.io_Command = CMD_READ;
   SerialIO->IOSer.io_Length = Read;
   SerialIO->IOSer.io_Data = (APTR)ReadBuff;
   DoIO((struct IORequest *)SerialIO);
   UnRead -= Read;
  }
 }
}


LONG SendPacket(VOID)
{
 USHORT count;
 LONG Error;
 SHORT NumTry = 1;
 UBYTE temp;
 union Check
 {
  USHORT CheckWord;
  UBYTE CheckByte[2];
 } CheckSum;

 if(Packet.Len > 1)
 {
  if(NumTry <= CheckSumTry) NumTry = CheckSumTry + 1;
  CheckSum.CheckWord = 0;
  for(count = 0; count < Packet.Len; count++)
   CheckSum.CheckWord += (USHORT)Packet.Data[count];
  Packet.Data[Packet.Len] = CheckSum.CheckByte[0];
  Packet.Data[Packet.Len + 1] = CheckSum.CheckByte[1];   
  while(NumTry > 0)
  {
   if((Error = ActualSendPacket(Packet.Len + 2))) return Error;
   temp = Packet.Data[0];
   if((Error = ActualReceivePacket(1))) return Error;
   if(Packet.Data[0] == 'Y')
   {
    if((Error = ActualSendPacket(1))) return Error;
    Packet.Data[0] = temp;
    return NO_ERROR;
   }
   else
   {
    NumTry--;
   }
  }
  return ERROR_CheckSum;
 }
 else return ActualSendPacket(Packet.Len); 
}

LONG ReceivePacket(VOID)
{
 USHORT count;
 LONG Error;
 SHORT NumTry = 1;
 UBYTE temp;
 union Check
 {
  USHORT CheckWord;
  UBYTE CheckByte[2];
 } CheckSum;

 if(Packet.Len > 1)
 {
  if(NumTry <= CheckSumTry) NumTry = CheckSumTry + 1;
  while(NumTry > 0)
  {
   if((Error = ActualReceivePacket(Packet.Len + 2))) return Error;
   CheckSum.CheckWord = 0;
   for(count = 0; count < Packet.Len; count++)
    CheckSum.CheckWord += (USHORT)Packet.Data[count];
   if( (Packet.Data[Packet.Len] != CheckSum.CheckByte[0]) ||
       (Packet.Data[Packet.Len + 1] != CheckSum.CheckByte[1]) )
   {
    NumTry--;
    Packet.Data[0] = 'N';
    if((Error = ActualSendPacket(1))) return Error;
   }
   else 
   {
    temp = Packet.Data[0];
    Packet.Data[0] = 'Y';
    if((Error = ActualSendPacket(1))) return Error;
    if((Error = ActualReceivePacket(1))) return Error;
    Packet.Data[0] = temp;
    return NO_ERROR;
   }
  }
  return ERROR_CheckSum;
 }
 else return ActualReceivePacket(Packet.Len);
}
 

VOID CloseSerial(VOID)
{
 if(Serial_OK) 
 { 
  CloseDevice((struct IORequest *)SerialIO);
  Serial_OK = FALSE;
 }
 if(SerialIO) { DeleteExtIO((struct IORequest *)SerialIO); SerialIO = NULL; }
 if(SerialMP) { DeletePort(SerialMP); SerialMP = NULL; }
}

LONG ConfigSerial(ULONG BAUD)
{
 ULONG Sig;
 UBYTE SerFlags = SERF_XDISABLED; 
 
 if(Serial_OK)
 {
  SerialIO->IOSer.io_Command = SDCMD_SETPARAMS;
  SerialIO->io_Baud = BAUD;
  SerialIO->io_ReadLen = 8;
  SerialIO->io_WriteLen = 8;
  SerialIO->io_StopBits = 1;
  SerialIO->io_SerFlags = SerFlags;
  SendIO((struct IORequest *)SerialIO);
  Sig = Wait(1 << SerialMP->mp_SigBit);
  WaitIO((struct IORequest *)SerialIO);  
  return NO_ERROR;
 }
 else return ERROR_NoDevice; 
}

LONG GetSynch(VOID)
{
 LONG Error;

 Packet.Len = 1;
 if((Error = ReceivePacket())) return Error;
 if(Packet.Data[0] - 150 == ERROR_CHDIR) return ERROR_CHDIR;
 if(Packet.Data[0] - 150 == ERROR_MKDIR) return ERROR_MKDIR;
 if(Packet.Data[0] - 150 == ERROR_RMDIR) return ERROR_RMDIR;
 if(Packet.Data[0] - 150 == ERROR_SendRFile) return ERROR_SendRFile;
 if(Packet.Data[0] - 150 == ERROR_RecFile) return ERROR_RecFile;
 if(Packet.Data[0] - 150 == ERROR_FDelete) return ERROR_FDelete;
 if(Packet.Data[0] - 150 == ERROR_SRFExists) return ERROR_SRFExists;
 if(Packet.Data[0] != '!') return ERROR_NoSynch;
 else return NO_ERROR;
}

LONG PutSynch(VOID)
{
 LONG Error;

 Packet.Len = 1;
 Packet.Data[0] = '?';
 if((Error = SendPacket())) return Error;
 else return NO_ERROR;
} 

LONG PutError(long ErrNo)
{
 LONG Error;

 Packet.Len = 1;
 Packet.Data[0] = 150 + ErrNo;
 if((Error = SendPacket())) return Error;
 else return NO_ERROR;
} 


LONG CheckLink(VOID)
{
 LONG Error;

 Packet.Len = 1;
 Packet.Data[0] = 'L';
 if((Error = SendPacket())) return Error;
 Packet.Len = 1;
 SystemRequest = TRUE;
 Error = ActualReceivePacket(1);
 SystemRequest = FALSE;
 if(Error) return ERROR_Link;
 else return NO_ERROR;
}
