/*--------------------------------------------------------------*/
/*	SerialIO.c: Amiga Serial I/O Device Routines		*/
/*      Created: June 1986 by J.A. Lydiatt			*/
/*								*/
/*	Note:							*/
/*	  A Task may only open the serial port once.		*/
/*--------------------------------------------------------------*/

#include <exec/types.h>
#include <exec/memory.h>
#include <stdio.h>
#include <devices/serial.h>
#include <functions.h>

/* Allowable Mode Values */
#define MODEHALF    0
#define MODEFULL    1
#define MODEECHO    2

/* Default starting values */
#define STARTBAUD   1200  /* default baud rate */
#define STARTMODE   MODEFULL
#define SERFLAGS    (SERF_SHARED | SERF_XDISABLED)
#define CTLCHAR     0x11130501    

static int Mode = STARTMODE;
static int amClosing = FALSE;

/* declarations for the serial stuff */
static struct IOExtSer *Read_Request = NULL;
static UBYTE rs_in[2];
static struct IOExtSer *Write_Request = NULL;
static UBYTE rs_out[2];

/* stack to save Serial Port flags */
#define MAXSTACK 10
static int stackSize = 0;
static int   modeStack[ MAXSTACK ];
static UBYTE flagStack[ MAXSTACK ];


/*------------------------------------------------------------*/
/*  GetSerialSigBit: return Read_Request's Signal Bit	      */
/*------------------------------------------------------------*/

int GetSerialSigBit()
{
   return Read_Request->IOSer.io_Message.mn_ReplyPort->mp_SigBit;
}

/*------------------------------------------------------------*/
/*  CheckSerIO : return TRUE if serial port has a character   */
/*------------------------------------------------------------*/

BOOL CheckSerIO()
{
   return (BOOL)( CheckIO( Read_Request ) != NULL);
}

/*------------------------------------------------------------*/
/* 	 FlushSerIO: Flush the receive buffer		      */
/*------------------------------------------------------------*/

void FlushSerIO()
{
   register struct IOExtSer *r = Read_Request;

   AbortIO( r );                 /* abort it */
   AbortIO( Write_Request );

   r->IOSer.io_Command = CMD_FLUSH;
   DoIO( r );                       /* flush all IO requests */
   r->IOSer.io_Command = CMD_CLEAR;
   DoIO( r);                       /* flush receive buffer  */

   if ( !amClosing )
     {
        Read_Request->IOSer.io_Command = CMD_READ;
        BeginIO( r );		   /* start receive request */
     }
}


/*------------------------------------------------------------*/
/*  PushSerState: save current io flags                       */
/*------------------------------------------------------------*/
void PushSerState()
{
   register struct IOExtSer *r = Read_Request; 

   if ( stackSize < MAXSTACK )
     {
        /* Save the current Mode */
        modeStack[ stackSize ] = Mode;
        
	/* Get the current flags */
	AbortIO( r );
	flagStack[ stackSize++ ] = r->io_SerFlags;
	BeginIO( r );
     }
}

/*------------------------------------------------------------*/
/*  PullSerState: restore last saved flag state		      */
/*------------------------------------------------------------*/
void PullSerState()
{
   register struct IOExtSer *r = Read_Request; 

   if ( stackSize > 0 )
     {
	/* Reset the Mode */
	Mode = modeStack[ --stackSize ];

	/* Set the old flags */
	AbortIO( r );
	r->io_SerFlags = flagStack[ stackSize ];
	r->IOSer.io_Command = SDCMD_SETPARAMS;
	DoIO( r );
	r->IOSer.io_Command = CMD_READ;
	BeginIO( r );
     }
}
/*-------------------------------------------------------------*/
/*           CloseSerialIO: Close the serial port              */
/*-------------------------------------------------------------*/
void CloseSerialIO()
{
   register struct IOExtSer *r = Read_Request;
   register struct IOExtSer *w = Write_Request;

   if ( r != NULL )
     {
	amClosing = TRUE;
	CloseDevice( r );
	DeletePort( r->IOSer.io_Message.mn_ReplyPort );
	FreeMem( r, (long)sizeof( struct IOExtSer ) );
	Read_Request = NULL;
      }

    if ( w != NULL )
      {
	CloseDevice( w );
	DeletePort( w->IOSer.io_Message.mn_ReplyPort );
	FreeMem( w, (long)sizeof( struct IOExtSer ) );
	Write_Request = NULL;
      }
}
/*-------------------------------------------------------------*/
/* InitSerialIO: Open serial IO - return read Port	       */
/*-------------------------------------------------------------*/	
struct IOExtSer *InitSerialIO()
{
   register struct IOExtSer *r, *w;

   if ( Read_Request != NULL )
      {
	fprintf( stderr, "Error: Serial Port already open for read.\n");
	return NULL;
      }
   r = (struct IOExtSer *) AllocMem( (long)sizeof(struct IOExtSer),
     (long)(MEMF_PUBLIC|MEMF_CLEAR));
   if (r == NULL)
	return NULL;

   Read_Request = r;
   r->io_SerFlags = SERFLAGS;
   r->io_CtlChar  = CTLCHAR;
   r->IOSer.io_Message.mn_ReplyPort = CreatePort("Read_RS",NULL);
   if(OpenDevice(SERIALNAME,NULL,r,NULL))
      {
	 fprintf( stderr, "Can't open Read device\n");
	goto q4;
      }

   r->io_Baud = STARTBAUD;
   r->io_ReadLen = 8;
   r->io_WriteLen = 8;
   r->IOSer.io_Command = SDCMD_SETPARAMS;
   DoIO(r);
   r->IOSer.io_Command = CMD_READ;
   r->IOSer.io_Length = 1;
   r->IOSer.io_Data = (APTR)&rs_in[0];


   if ( Write_Request != NULL )
      {
	fprintf( stderr, "Error: Serial Port already open for writing.\n");
	goto q3;
      }
   w = (struct IOExtSer *)AllocMem( (long)sizeof( struct IOExtSer ),
      (long)MEMF_PUBLIC|MEMF_CLEAR);
   if (w == NULL) goto q3;

   Write_Request = w;
   w->io_SerFlags = SERFLAGS;
   w->io_CtlChar  = CTLCHAR;
   w->IOSer.io_Message.mn_ReplyPort = CreatePort("Write_RS",NULL);
   if(OpenDevice(SERIALNAME,NULL,w,NULL))
      {
	fprintf( stderr, "Can't open Write device\n");
	goto q1;
      }
   w->io_Baud = STARTBAUD;
   w->io_ReadLen = 8;
   w->io_WriteLen = 8;
   w->IOSer.io_Command = SDCMD_SETPARAMS;
   DoIO(w);

   w->IOSer.io_Command = CMD_WRITE;
   w->IOSer.io_Length = 1;
   w->IOSer.io_Data = (APTR)&rs_out[0];

   BeginIO( r );
   stackSize = 0;

   return r;

q1:   DeletePort( w->IOSer.io_Message.mn_ReplyPort );
q2:   FreeMem( w, (long)sizeof( *w) );
q3:   CloseDevice( r );
q4:   DeletePort( r->IOSer.io_Message.mn_ReplyPort );
q5:   FreeMem( r, (long)sizeof( *r) );

   return NULL;
}

/*----------------------------------------------------------*/
/*         SetXonMode: set Xon On or Off                    */
/*----------------------------------------------------------*/

void SetXonMode( status )
BOOL status;
{

   register UBYTE flags;
   register struct IOExtSer *r = Read_Request; 

   /* Get the current flags */
   AbortIO( r );
   flags = r->io_SerFlags;

   if ( status )
      flags &= ~(SERF_XDISABLED);
   else
      flags |= SERF_XDISABLED; 
   r->io_SerFlags = flags;
   r->IOSer.io_Command = SDCMD_SETPARAMS;
   DoIO( r );
   r->IOSer.io_Command = CMD_READ;
   BeginIO( r );
}

/*----------------------------------------------------------*/
/*         SetSerBaud: set Serial Baud Rate                 */
/*----------------------------------------------------------*/

void SetSerBaud( baud )
int baud;
{
   register struct IOExtSer *r = Read_Request; 

   /* Get the current flags */
   AbortIO( r );
   r->io_Baud = baud;
   r->IOSer.io_Command = SDCMD_SETPARAMS;
   DoIO( r );
   r->IOSer.io_Command = CMD_READ;
   BeginIO( r );
}

/*----------------------------------------------------------*/
/*         SetSerMode: set the Serial Mode		    */
/*----------------------------------------------------------*/

void SetSerMode( mode )
int mode;
{
   Mode = mode;
}

/*----------------------------------------------------------*/
/*   SerIOWrite: Write a byte out the serial port (no echo) */
/*----------------------------------------------------------*/

void SerIOWrite( c )
register UBYTE c;
{
  *rs_out = c;
  DoIO( Write_Request );
}

/*----------------------------------------------------------*/
/* SerIORead: read a byte from the serial port (no echo)    */
/*----------------------------------------------------------*/

UBYTE SerIORead()
{
   register struct IOExtSer *r = Read_Request;
   register UBYTE c;

   WaitIO( r );
   c = *rs_in;
   BeginIO( r );
   return c;
}

