/*
**	SerialIO.c
**
**	Serial read/write routines
**
**	Copyright © 1990-1996 by Olaf `Olsen' Barthel
**		All Rights Reserved
**
**	:ts=4
*/

#ifndef _GLOBAL_H
#include "Global.h"
#endif

STATIC BOOLEAN __aligned	ReadQueued	= FALSE,
							WriteQueued	= FALSE;

	/* ResetSerialRead():
	 *
	 *	Reset the read status.
	 */

VOID
ResetSerialRead()
{
	ReadQueued = FALSE;
}

	/* CheckSerialRead():
	 *
	 *	Check if a read request is finished.
	 */

BOOL
CheckSerialRead()
{
	if(ReadRequest && ReadQueued)
	{
		if(CheckIO(ReadRequest))
			return(TRUE);
	}

	return(FALSE);
}

	/* WaitSerialRead():
	 *
	 *	Wait for the read request to terminate.
	 */

BYTE
WaitSerialRead()
{
	if(ReadRequest && ReadQueued)
	{
		ReadQueued = FALSE;

		return(WaitIO(ReadRequest));
	}
	else
		return(0);
}

	/* FlushSerialRead():
	 *
	 *	Forget about the current contents of the serial
	 *	read buffer.
	 */

BYTE
FlushSerialRead()
{
	if(ReadRequest)
	{
		BOOL WasRunning;

		if(WasRunning = ReadQueued)
			StopSerialRead();

		ReadRequest->IOSer.io_Command = CMD_CLEAR;

		DoIO(ReadRequest);

		if(WasRunning)
			StartSerialRead(ReadBuffer,1);
	}

	return(0);
}

	/* StopSerialRead():
	 *
	 *	Force a read request to terminate.
	 */

VOID
StopSerialRead()
{
	if(ReadRequest && ReadQueued)
	{
		if(!CheckIO(ReadRequest))
			AbortIO(ReadRequest);

		WaitIO(ReadRequest);

		ReadQueued = FALSE;
	}
}

	/* StartSerialRead(register ULONG Length,register APTR Data):
	 *
	 *	Start a serial read request asynchronously.
	 */

VOID
StartSerialRead(register APTR Data,register ULONG Length)
{
	if(ReadRequest)
	{
		if(ReadQueued)
			StopSerialRead();

		ReadRequest->IOSer.io_Command	= CMD_READ;
		ReadRequest->IOSer.io_Length	= Length;
		ReadRequest->IOSer.io_Data		= Data;

		ClrSignal(1L << ReadPort->mp_SigBit);

		SendIO(ReadRequest);

		ReadQueued = TRUE;
	}
}

	/* DoSerialRead(register ULONG Length,register APTR Data):
	 *
	 *	Perform a read request synchronously.
	 */

BYTE
DoSerialRead(register APTR Data,register ULONG Length)
{
	if(ReadRequest)
	{
		if(ReadQueued)
			StopSerialRead();

		ReadRequest->IOSer.io_Command	= CMD_READ;
		ReadRequest->IOSer.io_Length	= Length;
		ReadRequest->IOSer.io_Data		= Data;

		return(DoIO(ReadRequest));
	}
	else
		return(IOERR_SELFTEST);
}

	/* ResetSerialWrite():
	 *
	 *	Reset the write status.
	 */

VOID
ResetSerialWrite()
{
	WriteQueued = FALSE;
}

	/* CheckSerialWrite():
	 *
	 *	Check if a write request is finished.
	 */

BOOL
CheckSerialWrite()
{
	if(WriteRequest && WriteQueued)
	{
		if(!CheckIO(WriteRequest))
			return(TRUE);
	}

	return(FALSE);
}

	/* WaitSerialWrite():
	 *
	 *	Wait for the write request to terminate.
	 */

BYTE
WaitSerialWrite()
{
	if(WriteRequest && WriteQueued)
	{
		WriteQueued = FALSE;

		return(WaitIO(WriteRequest));
	}
	else
		return(0);
}

	/* StopSerialWrite():
	 *
	 *	Force a write request to terminate.
	 */

VOID
StopSerialWrite()
{
	if(WriteRequest && WriteQueued)
	{
		if(!CheckIO(WriteRequest))
			AbortIO(WriteRequest);

		WaitIO(WriteRequest);

		WriteQueued = FALSE;
	}
}

	/* StartSerialWrite(register ULONG Length,register APTR Data):
	 *
	 *	Start a serial write request asynchronously.
	 */

VOID
StartSerialWrite(register APTR Data,register ULONG Length)
{
	if(WriteRequest)
	{
		if(WriteQueued)
			StopSerialWrite();

		WriteRequest->IOSer.io_Command	= CMD_WRITE;
		WriteRequest->IOSer.io_Length	= Length;
		WriteRequest->IOSer.io_Data		= Data;

		ClrSignal(1L << WriteRequest->IOSer.io_Message.mn_ReplyPort->mp_SigBit);

		SendIO(WriteRequest);

		WriteQueued = TRUE;
	}
}

	/* DoSerialWrite(register ULONG Length,register APTR Data):
	 *
	 *	Perform a write request synchronously.
	 */

BYTE
DoSerialWrite(register APTR Data,register ULONG Length)
{
	if(WriteRequest)
	{
		if(WriteQueued)
			StopSerialWrite();

		WriteRequest->IOSer.io_Command	= CMD_WRITE;
		WriteRequest->IOSer.io_Length	= Length;
		WriteRequest->IOSer.io_Data		= Data;

		return(DoIO(WriteRequest));
	}
	else
		return(IOERR_SELFTEST);
}

	/* DoSerialCmd(register UWORD Command):
	 *
	 *	Perform single command.
	 */

BYTE
DoSerialCmd(register LONG Command)
{
	if(WriteRequest)
	{
		if(WriteQueued)
			StopSerialWrite();

		WriteRequest->IOSer.io_Command = Command;

		return(DoIO(WriteRequest));
	}
	else
		return(IOERR_SELFTEST);
}

	/* GetSerialWaiting():
	 *
	 *	Query the number of bytes still waiting to be read
	 *	from the serial port.
	 */

ULONG
GetSerialWaiting()
{
	if(WriteRequest)
	{
		if(WriteQueued)
			StopSerialWrite();

		WriteRequest->IOSer.io_Command = SDCMD_QUERY;

		DoIO(WriteRequest);

		return(WriteRequest->IOSer.io_Actual);
	}
	else
		return(0);
}

	/* GetSerialStatus():
	 *
	 *	Query the current serial port status.
	 */

LONG
GetSerialStatus()
{
	if(WriteRequest)
	{
		if(WriteQueued)
			StopSerialWrite();

		WriteRequest->IOSer.io_Command = SDCMD_QUERY;

		DoIO(WriteRequest);

		return(WriteRequest->io_Status);
	}
	else
		return(CIAF_COMCD | CIAF_COMDSR);
}

	/* GetSerialInfo(register ULONG *Waiting,register UWORD *Status):
	 *
	 *	Query both the number of bytes waiting to be read and
	 *	the current serial status.
	 */

VOID
GetSerialInfo(register ULONG *Waiting,register UWORD *Status)
{
	if(WriteRequest)
	{
		if(WriteQueued)
			StopSerialWrite();

		WriteRequest->IOSer.io_Command = SDCMD_QUERY;

		DoIO(WriteRequest);

		*Waiting	= WriteRequest->IOSer.io_Actual;
		*Status		= WriteRequest->io_Status;
	}
	else
	{
		*Waiting	= 0;
		*Status		= CIAF_COMCD | CIAF_COMDSR;
	}
}

	/* SetSerialAttributes():
	 *
	 *	Set the serial driver attributes.
	 */

STATIC VOID
SetSerialAttributes(struct IOExtSer *Request,struct TagItem *Tags)
{
	struct TagItem *Tag;

	while(Tag = NextTagItem(&Tags))
	{
		switch(Tag->ti_Tag)
		{
			case SERA_Baud:

				Request->io_Baud = Tag->ti_Data;
				break;

			case SERA_BreakTime:

				Request->io_BrkTime = Tag->ti_Data;
				break;

			case SERA_BitsPerChar:

				Request->io_ReadLen	= Tag->ti_Data;
				Request->io_WriteLen	= Tag->ti_Data;
				break;

			case SERA_StopBits:

				Request->io_StopBits = Tag->ti_Data;
				break;

			case SERA_BufferSize:

				Request->io_RBufLen = Tag->ti_Data;
				break;

			case SERA_Parity:

				Request->io_ExtFlags &= ~(SEXTF_MSPON | SEXTF_MARK);
				Request->io_SerFlags &= ~(SERF_PARTY_ON | SERF_PARTY_ODD);

				switch(Tag->ti_Data)
				{
					case PARITY_EVEN:

						Request->io_SerFlags |= SERF_PARTY_ON;
						break;

					case PARITY_ODD:

						Request->io_SerFlags |= SERF_PARTY_ON | SERF_PARTY_ODD;
						break;

					case PARITY_MARK:

						Request->io_SerFlags |= SERF_PARTY_ON;
						Request->io_ExtFlags |= SEXTF_MSPON | SEXTF_MARK;
						break;

					case PARITY_SPACE:

						Request->io_SerFlags |= SERF_PARTY_ON;
						Request->io_ExtFlags |= SEXTF_MSPON;
						break;
				}

				break;

			case SERA_Handshaking:

				if(Tag->ti_Data == HANDSHAKING_NONE)
					Request->io_SerFlags &= ~SERF_7WIRE;
				else
					Request->io_SerFlags |=  SERF_7WIRE;

				break;

			case SERA_HighSpeed:

				if(Tag->ti_Data)
					Request->io_SerFlags |=  SERF_RAD_BOOGIE;
				else
					Request->io_SerFlags &= ~SERF_RAD_BOOGIE;

				break;

			case SERA_Shared:

				if(Tag->ti_Data)
					Request->io_SerFlags |=  SERF_SHARED;
				else
					Request->io_SerFlags &= ~SERF_SHARED;

				break;
		}
	}

	Request->io_SerFlags |= SERF_XDISABLED;
}

	/* GetSerialAttributes():
	 *
	 *	Get the serial driver attributes.
	 */

STATIC ULONG
GetSerialAttributes(struct IOExtSer *Request,struct TagItem *Tags)
{
	struct TagItem	*Tag;
	ULONG			*Data;
	ULONG			 Result;

	Result = NULL;

	while(Tag = NextTagItem(&Tags))
	{
		if(!(Data = (ULONG *)Tag->ti_Data))
			Data = &Result;

		switch(Tag->ti_Tag)
		{
			case SERA_Baud:

				*Data = Request->io_Baud;
				break;

			case SERA_BreakTime:

				*Data = Request->io_BrkTime;
				break;

			case SERA_BitsPerChar:

				*Data = Request->io_ReadLen;
				break;

			case SERA_StopBits:

				*Data = Request->io_StopBits;
				break;

			case SERA_BufferSize:

				*Data = Request->io_RBufLen;
				break;

			case SERA_Parity:

				Request->io_ExtFlags &= ~(SEXTF_MSPON | SEXTF_MARK);
				Request->io_SerFlags &= ~(SERF_PARTY_ON | SERF_PARTY_ODD);

				switch(Tag->ti_Data)
				{
					case PARITY_EVEN:

						Request->io_SerFlags |= SERF_PARTY_ON;
						break;

					case PARITY_ODD:

						Request->io_SerFlags |= SERF_PARTY_ON | SERF_PARTY_ODD;
						break;

					case PARITY_MARK:

						Request->io_SerFlags |= SERF_PARTY_ON;
						Request->io_ExtFlags |= SEXTF_MSPON | SEXTF_MARK;
						break;

					case PARITY_SPACE:

						Request->io_SerFlags |= SERF_PARTY_ON;
						Request->io_ExtFlags |= SEXTF_MSPON;
						break;
				}

				switch(Request->io_ExtFlags & (SEXTF_MSPON | SEXTF_MARK))
				{
					case SEXTF_MSPON | SEXTF_MARK:

						*Data = PARITY_MARK;
						break;

					case SEXTF_MSPON:

						*Data = PARITY_SPACE;
						break;

					default:

						switch(Request->io_SerFlags & (SERF_PARTY_ON | SERF_PARTY_ODD))
						{
							case SERF_PARTY_ON | SERF_PARTY_ODD:

								*Data = PARITY_ODD;
								break;

							case SERF_PARTY_ON:

								*Data = PARITY_EVEN;
								break;

							default:

								*Data = PARITY_NONE;
								break;
						}

						break;
				}

				break;

			case SERA_Handshaking:

				if(Request->io_SerFlags & SERF_7WIRE)
					*Data = TRUE;
				else
					*Data = FALSE;

				break;

			case SERA_HighSpeed:

				if(Request->io_SerFlags & SERF_RAD_BOOGIE)
					*Data = TRUE;
				else
					*Data = FALSE;

				break;

			case SERA_Shared:

				if(Request->io_SerFlags & SERF_SHARED)
					*Data = TRUE;
				else
					*Data = FALSE;

				break;
		}
	}

	return(Result);
}

	/* SetBothSerialAttributes():
	 *
	 *	Set the serial read driver parameters.
	 */

BYTE __stdargs
SetBothSerialAttributes(Tag FirstTag,...)
{
	if(ReadRequest && WriteRequest)
	{
		BYTE Result;

		if(ReadQueued)
			StopSerialRead();

		if(WriteQueued)
			StopSerialWrite();

		SetSerialAttributes(ReadRequest,(struct TagItem *)&FirstTag);

		if(ReadRequest->IOSer.io_Device)
		{
			ReadRequest->IOSer.io_Command = SDCMD_SETPARAMS;

			Result = DoIO(ReadRequest);
		}
		else
			Result = 0;

		if(!Result)
		{
			struct MsgPort *WritePort = WriteRequest->IOSer.io_Message.mn_ReplyPort;

			CopyMem(ReadRequest,WriteRequest,sizeof(struct IOExtSer));

			WriteRequest->IOSer.io_Message.mn_ReplyPort = WritePort;
		}

		return(Result);
	}
	else
		return(IOERR_SELFTEST);
}

	/* SetSerialReadAttributes():
	 *
	 *	Set the serial read driver parameters.
	 */

BYTE __stdargs
SetSerialReadAttributes(Tag FirstTag,...)
{
	if(ReadRequest)
	{
		if(ReadQueued)
			StopSerialRead();

		SetSerialAttributes(ReadRequest,(struct TagItem *)&FirstTag);

		if(ReadRequest->IOSer.io_Device)
		{
			ReadRequest->IOSer.io_Command = SDCMD_SETPARAMS;

			return(DoIO(ReadRequest));
		}
		else
			return(0);
	}
	else
		return(IOERR_SELFTEST);
}

	/* SetSerialWriteAttributes():
	 *
	 *	Set the serial write driver parameters.
	 */

BYTE __stdargs
SetSerialWriteAttributes(Tag FirstTag,...)
{
	if(WriteRequest)
	{
		if(WriteQueued)
			StopSerialWrite();

		SetSerialAttributes(WriteRequest,(struct TagItem *)&FirstTag);

		WriteRequest->IOSer.io_Command = SDCMD_SETPARAMS;

		return(DoIO(WriteRequest));
	}
	else
		return(IOERR_SELFTEST);
}

	/* GetSerialReadAttributes():
	 *
	 *	Get the serial read driver parameters.
	 */

ULONG __stdargs
GetSerialReadAttributes(Tag FirstTag,...)
{
	if(ReadRequest)
		return(GetSerialAttributes(ReadRequest,(struct TagItem *)&FirstTag));
	else
		return(NULL);
}

	/* GetSerialWriteAttributes():
	 *
	 *	Get the serial write driver parameters.
	 */

ULONG __stdargs
GetSerialWriteAttributes(Tag FirstTag,...)
{
	if(WriteRequest)
		return(GetSerialAttributes(WriteRequest,(struct TagItem *)&FirstTag));
	else
		return(NULL);
}
