/* $Revision Header * Header built automatically - do not edit! *************
 *
 *	(C) Copyright 1991 by Olaf 'Olsen' Barthel & MXM
 *
 *	Name .....: Serial.c
 *	Created ..: Monday 21-Jan-91 20:12
 *	Revision .: 0
 *
 *	Date            Author          Comment
 *	=========       ========        ====================
 *	21-Jan-91       Olsen           Created this file!
 *
 * $Revision Header ********************************************************/

#include "TermGlobal.h"

	/* XOn():
	 *
	 *	Perform XON (stop data flow).
	 */

VOID
XOn()
{
	if(Config . Handshaking == HANDSHAKING_XONXOFF)
		Status = STATUS_HOLDING;
}

	/* SerialCommand(UBYTE *String):
	 *
	 *	Send a command string to the serial line and
	 *	interprete the control sequences.
	 */

VOID
SerialCommand(UBYTE *String)
{
	LONG	Count = 0,i;

	BYTE	GotControl	= FALSE,
		GotEscape	= FALSE,

		OldStatus,
		BlockSet;

		/* Scan the string. */

	for(i = 0 ; i < strlen(String) ; i++)
	{
			/* We are looking for plain characters
			 * and the control ('\') and escape
			 * ('^') characters.
			 */

		if(!GotControl && !GotEscape)
		{
				/* Got a control character,
				 * the next byte will probably be
				 * a command sequence.
				 */

			if(String[i] == '\\')
			{
				GotControl = TRUE;
				continue;
			}

				/* Got an escape character,
				 * the next byte will be some
				 * kind of control character
				 * (such as XON, XOF, bell, etc.).
				 */

			if(String[i] == '^')
			{
				GotEscape = TRUE;
				continue;
			}

				/* This tells us to wait another
				 * second before continuing with
				 * the scanning.
				 */

			if(String[i] == '~')
			{
				if(Count)
				{
					SerWrite(SharedBuffer,Count);

					Count = 0;
				}

				WaitTime(0,MILLION / 2);
				HandleSerial();

				continue;
			}

				/* Stuff the character into the
				 * buffer.
				 */

			SharedBuffer[Count++] = String[i];
		}
		else
		{
				/* Convert the character to a control
				 * style character (^C, etc.).
				 */

			if(GotEscape)
			{
				if(ToUpper(String[i]) >= 'A' && ToUpper(String[i]) <= 91)
					SharedBuffer[Count++] = ToUpper(String[i]) - '@';
				else
					SharedBuffer[Count++] = String[i];

				GotEscape = FALSE;
			}

				/* The next character represents a command. */

			if(GotControl)
			{
				switch(ToUpper(String[i]))
				{
						/* Execute an AmigaDOS command. */

					case 'D':	if(!WeAreBlocking)
							{
								BlockSet = TRUE;

								BlockWindows();
							}
							else
								BlockSet = FALSE;

							SendAmigaDOSCommand(&String[i + 1]);

							if(BlockSet)
								ReleaseWindows();

							return;

						/* Execute an ARexx command. */

					case 'A':	if(!WeAreBlocking)
							{
								BlockSet = TRUE;

								BlockWindows();
							}
							else
								BlockSet = FALSE;

							SendARexxCommand(&String[i + 1]);

							if(BlockSet)
								ReleaseWindows();

							return;

						/* Add the control character ('\'). */

					case '\\':	SharedBuffer[Count++] = '\\';
							break;

						/* This is a backspace. */

					case 'B':	SharedBuffer[Count++] = '\b';
							break;

						/* This is a form feed. */

					case 'F':	SharedBuffer[Count++] = '\f';
							break;

						/* This is a line feed. */

					case 'N':	SharedBuffer[Count++] = '\n';
							break;

						/* Send the current password. */

					case 'P':	if(Password[0])
							{
								if(Count)
								{
									SerWrite(SharedBuffer,Count);

									Count = 0;
								}

								SerWrite(Password,strlen(Password));
							}

							break;

						/* This is a carriage return. */

					case 'R':	SharedBuffer[Count++] = '\r';
							break;

						/* This is a tab. */

					case 'T':	SharedBuffer[Count++] = '\t';
							break;

						/* Send a break across the serial line. */

					case 'X':	if(Count)
							{
								SerWrite(SharedBuffer,Count);

								Count = 0;
							}

							if(WriteRequest)
							{
								OldStatus = Status;

								Status = STATUS_BREAKING;

								WriteRequest -> IOSer . io_Command = SDCMD_BREAK;
								DoIO(WriteRequest);

								Status = OldStatus;
							}

							break;

						/* Feed the contents of the
						 * clipboard into the input
						 * stream.
						 */

					case 'I':	if(Count)
								SerWrite(SharedBuffer,Count);

							Count = LoadClip(SharedBuffer,256);

							break;

						/* Send a string to the clipboard. */

					case 'G':	if(String[i + 1])
								SaveClip(&String[i + 1],strlen(&String[i + 1]));

							return;

						/* Produce the escape character. */

					case 'E':	SharedBuffer[Count++] = ESC;
							break;

						/* Stuff the character into the buffer. */

					default:	SharedBuffer[Count++] = String[i];
							break;
				}

				GotControl = FALSE;
			}
		}

			/* If the buffer is full, release it. */

		if(Count == 256)
		{
			SerWrite(SharedBuffer,Count);

			Count = 0;
		}
	}

	if(Count)
		SerWrite(SharedBuffer,Count);
}

	/* SerWrite(APTR Buffer,LONG Size):
	 *
	 *	Send a number of bytes across the serial line.
	 */

VOID
SerWrite(APTR Buffer,LONG Size)
{
	if(WriteRequest && Size)
	{
		if(XProtocolBase)
		{
				/* xpr wants to see the data before it is
				 * transferred.
				 */

			if(TransferBits & XPRS_USERMON)
				if(!(Size = XProtocolUserMon(XprIO,Buffer,Size,Size)))
					return;
		}

			/* If full duplex is enabled, send the entire
			 * buffer.
			 */

		if(Config . Duplex == DUPLEX_FULL)
		{
			WriteRequest -> IOSer . io_Command	= CMD_WRITE;
			WriteRequest -> IOSer . io_Data		= Buffer;
			WriteRequest -> IOSer . io_Length	= Size;

			DoIO(WriteRequest);
		}
		else
		{
			UBYTE *ByteBuffer = Buffer;

				/* Half duplex is enabled, send only
				 * a single character at a time.
				 */

			WriteRequest -> IOSer . io_Command	= CMD_WRITE;
			WriteRequest -> IOSer . io_Length	= 1;

			while(Size--)
			{
				WriteRequest -> IOSer . io_Data = ByteBuffer;

				DoIO(WriteRequest);

				ConProcess(ByteBuffer,1);

				ByteBuffer++;
			}
		}
	}
}

	/* SetFlags(struct IOExtSer *SomeRequest):
	 *
	 *	Set the contents of a serial device request according
	 *	to the current configuration settings.
	 */

VOID
SetFlags(struct IOExtSer *SomeRequest)
{
	SomeRequest -> io_Baud		= Config . BaudRate;
	SomeRequest -> io_BrkTime	= Config . BreakLength;
	SomeRequest -> io_ReadLen	= Config . BitsPerChar;
	SomeRequest -> io_WriteLen	= Config . BitsPerChar;
	SomeRequest -> io_StopBits	= Config . StopBits;

	SomeRequest -> io_ExtFlags	&= ~(SEXTF_MSPON|SEXTF_MARK);
	SomeRequest -> io_SerFlags	&= ~(SERF_PARTY_ON|SERF_PARTY_ODD|SERF_7WIRE|SERF_RAD_BOOGIE);

	switch(Config . Parity)
	{
		case PARITY_EVEN:	SomeRequest -> io_SerFlags |= SERF_PARTY_ON;
					break;

		case PARITY_ODD:	SomeRequest -> io_SerFlags |= SERF_PARTY_ON|SERF_PARTY_ODD;
					break;

		case PARITY_MARK:	SomeRequest -> io_SerFlags |= SERF_PARTY_ON;
					SomeRequest -> io_ExtFlags |= SEXTF_MSPON|SEXTF_MARK;
					break;

		case PARITY_SPACE:	SomeRequest -> io_SerFlags |= SERF_PARTY_ON;
					SomeRequest -> io_ExtFlags |= SEXTF_MSPON;
					break;

		default:		break;
	}

	if(Config . Handshaking == HANDSHAKING_RTSCTS)
		SomeRequest -> io_SerFlags |= SERF_7WIRE;

	if(Config . HighSpeed)
		SomeRequest -> io_SerFlags |= SERF_RAD_BOOGIE;

	SomeRequest -> io_SerFlags |= SERF_XDISABLED;
}

	/* SetParameters():
	 *
	 *	Set the parameters for both serial requests and the
	 *	serial line.
	 */

VOID
SetParameters()
{
	if(WriteRequest)
	{
		WriteRequest -> IOSer . io_Command = SDCMD_SETPARAMS;

		SetFlags(WriteRequest);
		SetFlags(ReadRequest);

		DoIO(WriteRequest);
	}
}

	/* FlushSerial():
	 *
	 *	Marks the current serial read buffer as invalid.
	 */

VOID
FlushSerial()
{
	if(WriteRequest)
	{
		WriteRequest -> IOSer . io_Command = CMD_FLUSH;
		DoIO(WriteRequest);
	}
}

	/* ClearSerial():
	 *
	 *	Terminate all read/write activity on the serial
	 *	line.
	 */

VOID
ClearSerial()
{
	if(ReadPort)
	{
		if(!CheckIO(ReadRequest))
			AbortIO(ReadRequest);

		WaitIO(ReadRequest);

		WriteRequest -> IOSer . io_Command = CMD_CLEAR;

		DoIO(WriteRequest);
	}
}

	/* DeleteSerial():
	 *
	 *	Close the serial device and release all associated
	 *	resources.
	 */

VOID
DeleteSerial()
{
	BYTE Closed = FALSE;

	if(ReadBuffer)
	{
		FreeVec(ReadBuffer);

		ReadBuffer = NULL;
	}

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

			DoIO(ReadRequest);

			CloseDevice(ReadRequest);

			Closed = TRUE;
		}

		if(ReadRequest -> IOSer . io_Message . mn_ReplyPort)
			DeleteMsgPort(ReadRequest -> IOSer . io_Message . mn_ReplyPort);

		DeleteIORequest(ReadRequest);

		ReadRequest = NULL;
	}

	if(WriteRequest)
	{
		if(WriteRequest -> IOSer . io_Device && !Closed)
		{
			WriteRequest -> IOSer . io_Command = CMD_RESET;
			DoIO(WriteRequest);

			CloseDevice(WriteRequest);
		}

		if(WriteRequest -> IOSer . io_Message . mn_ReplyPort)
			DeleteMsgPort(WriteRequest -> IOSer . io_Message . mn_ReplyPort);

		DeleteIORequest(WriteRequest);

		WriteRequest = NULL;
	}

	ReadPort = NULL;
}

	/* CreateSerial():
	 *
	 *	Create handles for the serial device and open it.
	 */

BYTE
CreateSerial()
{
	struct MsgPort	*WritePort;

	if(ReadBuffer = AllocVec(SERBUFF_SIZE,MEMF_PUBLIC))
	{
		if(ReadPort = (struct MsgPort *)CreateMsgPort())
		{
			if(ReadRequest = (struct IOExtSer *)CreateIORequest(ReadPort,sizeof(struct IOExtSer)))
			{
				SetFlags(ReadRequest);

				ReadRequest -> io_RBufLen = SERBUFF_SIZE;

				if(!OpenDevice(Config . SerialDevice,Config . UnitNumber,ReadRequest,0))
				{
					if(WritePort = (struct MsgPort *)CreateMsgPort())
					{
						if(WriteRequest = (struct IOExtSer *)CreateIORequest(WritePort,sizeof(struct IOExtSer)))
						{
							CopyMem(ReadRequest,WriteRequest,sizeof(struct IOExtSer));

							WriteRequest -> IOSer . io_Message . mn_ReplyPort = WritePort;

							SetParameters();

							ReadRequest -> IOSer . io_Command	= CMD_READ;
							ReadRequest -> IOSer . io_Data		= ReadBuffer;
							ReadRequest -> IOSer . io_Length	= 1;

							SendIO(ReadRequest);

							return(TRUE);
						}
					}
				}
			}
		}
	}

	return(FALSE);
}
