/*
 *	Serial functions.
 *
 */

#include "hc.h"

ULONG ReadFlag;

static struct MsgPort *ReadPort;
static struct MsgPort *WritePort;
static struct IOExtSer *ReadIO;
static struct IOExtSer *WriteIO;
static UBYTE *ReadBuffer;
static UWORD SerOpened;
static UWORD Synchronize;
static ULONG BaudRate;

void InitRead (void)
{
	ReadIO->IOSer.io_Command = CMD_READ;
	ReadIO->IOSer.io_Length = 2;
	ReadIO->IOSer.io_Data = (APTR)ReadBuffer;
	SendIO (ReadIO);
}


void SyncSer (void)
{
	Synchronize = 1;
}


void ReadSer (void)
{
	UWORD count;

	/*
	 *	Wait for data and classify it.  Should always be 2 bytes long,
	 *	unless we asked for one byte for synchronisation.
	 */

DB	Say ("Waiting for IO.\n");

	WaitIO (ReadIO);

DB	Say ("Storing data.\n");

	if (Synchronize != 2)
		ClassData (ReadBuffer, ReadIO->IOSer.io_Actual);
	else
		Synchronize = 0;

	if (BaudRate != ReadIO->io_Baud)
	{
DB		Say ("Changing read baud rate.\n");

		ReadIO->IOSer.io_Command = SDCMD_SETPARAMS;
		ReadIO->io_Baud = BaudRate;
		DoIO (ReadIO);
	}

	while (1)
	{
DB		Say ("Querying amount of data.\n");

		ReadIO->IOSer.io_Command = SDCMD_QUERY;
		DoIO (ReadIO);
		if (ReadIO->IOSer.io_Actual < 2)
		{
			if (Synchronize == 1)
			{
				/*
				 *	If we must get in sync and there's a byte ready, do it
				 *	right away, else we request one byte.
				 */
				if (ReadIO->IOSer.io_Actual == 1)
				{
					ReadIO->IOSer.io_Command = CMD_READ;
					ReadIO->IOSer.io_Length = 1;
					ReadIO->IOSer.io_Data = (APTR)ReadBuffer;
					DoIO (ReadIO);
					Synchronize = 0;
				}
				else
				{
					ReadIO->IOSer.io_Command = CMD_READ;
					ReadIO->IOSer.io_Length = 1;
					ReadIO->IOSer.io_Data = (APTR)ReadBuffer;
					SendIO (ReadIO);
					Synchronize = 2;
					return;
				}
			}
			ReadIO->IOSer.io_Command = CMD_READ;
			ReadIO->IOSer.io_Length = 2;
			ReadIO->IOSer.io_Data = (APTR)ReadBuffer;
			SendIO (ReadIO);
			return;
		}
		else
		{
			count = Synchronize + min (ReadIO->IOSer.io_Actual & 0xfffe, 4096);
			ReadIO->IOSer.io_Command = CMD_READ;
			ReadIO->IOSer.io_Length = count;
			ReadIO->IOSer.io_Data = (APTR)ReadBuffer;
			DoIO (ReadIO);
			ClassData (ReadBuffer + Synchronize, count - Synchronize);
			Synchronize = 0;
		}
	}
}


void WriteSer (UBYTE *Buffer, UWORD Size)
{
	if (!CheckIO (WriteIO))
		WaitIO (WriteIO);

	if (BaudRate != WriteIO->io_Baud)
	{
		WriteIO->IOSer.io_Command = SDCMD_SETPARAMS;
		WriteIO->io_Baud = BaudRate;
		DoIO (WriteIO);
	}

	WriteIO->IOSer.io_Command = CMD_WRITE;
	WriteIO->IOSer.io_Length = Size;
	WriteIO->IOSer.io_Data = (APTR)Buffer;
	SendIO (WriteIO);
}


void FlushWriteSer (void)
{
	WaitIO (WriteIO);
}


BOOL OpenSer (void)
{
	if (ReadPort)
		return 1;

	ReadBuffer = AllocMem (4096, 0L);
	if (ReadBuffer != NULL)
	{
		ReadPort = CreatePort (0, 0);
		if (ReadPort != NULL)
		{
			WritePort = CreatePort (0, 0);
			if (WritePort != NULL)
			{
				ReadIO = (struct IOExtSer *)CreateExtIO (ReadPort, sizeof (struct IOExtSer));
				if (ReadIO != NULL)
				{
					WriteIO = (struct IOExtSer *)CreateExtIO (WritePort, sizeof (struct IOExtSer));
					if (WriteIO != NULL)
					{
						if (!OpenDevice ("serial.device", 0, ReadIO, 0))
						{
							SerOpened = 1;
							CopyMem (ReadIO, WriteIO, sizeof (struct IOExtSer));
							WriteIO->IOSer.io_Message.mn_ReplyPort = WritePort;
							WriteIO->IOSer.io_Command = SDCMD_SETPARAMS;
							WriteIO->io_Baud = 9600;
							WriteIO->io_ReadLen = 8;
							WriteIO->io_WriteLen = 8;
							WriteIO->io_StopBits = 1;
							WriteIO->io_SerFlags &= ~SERF_PARTY_ODD;
							DoIO (WriteIO);
							ReadIO->IOSer.io_Command = SDCMD_SETPARAMS;
							ReadIO->io_Baud = 9600;
							ReadIO->io_ReadLen = 8;
							ReadIO->io_WriteLen = 8;
							ReadIO->io_StopBits = 1;
							ReadIO->io_SerFlags &= ~SERF_PARTY_ODD;
							DoIO (ReadIO);
							ReadFlag = 1L << ReadPort->mp_SigBit;
							BaudRate = 9600;
							return 1;
						}
					}
				}
			}
		}
	}

	CloseSer ();
	return 0;
}



void SetBaud (UWORD Baud)
{
	BaudRate = Baud;
}


UWORD GetBaud (void)
{
	return (UWORD)BaudRate;
}


void CloseSer (void)
{
	if (SerOpened)
	{
		if (ReadIO) StopIO (ReadIO);
		if (WriteIO) StopIO (WriteIO);
		CloseDevice (ReadIO);
	}
	if (ReadIO) DeleteExtIO (ReadIO);
	if (WriteIO) DeleteExtIO (WriteIO);
	if (ReadPort) DeletePort (ReadPort);
	if (WritePort) DeletePort (WritePort);
	if (ReadBuffer) FreeMem (ReadBuffer, 4096);

	ReadIO = NULL;
	WriteIO = NULL;
	ReadPort = NULL;
	WritePort = NULL;
	SerOpened = 0;
	ReadFlag = 0;
	ReadBuffer = NULL;
}


void StopIO (struct IOExtSer *IO)
{
	Forbid ();
	if (!CheckIO (IO))
		AbortIO (IO);
	Permit ();
	WaitIO (IO);
}
