/*
**	termSerial.c
**
**	Serial driver support routines
**
**	Copyright © 1990-1994 by Olaf `Olsen' Barthel
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* The quantum of bytes to be read from the serial port,
	 * to be fed to the terminal emulation monster. This is where
	 * two worlds collide; first there is the idea to process as
	 * much data as there is currently available for reasons
	 * of efficiency. Second there is a problem caused by the
	 * fact that terminal emulation may not be able to
	 * output text as fast as new data comes in. Terminal and
	 * serial input will be blocked until all data is processed,
	 * so the quantum should be pretty small to avoid unnecessary
	 * processing delays.
	 */

	/* Local copy of serial driver name and unit number. */

STATIC UBYTE __far	SerialDevice[40];
STATIC LONG		UnitNumber = -1;

STATIC BOOLEAN		SerialLocked = FALSE;

	/* SendBreak():
	 *
	 *	Transmit a break signal.
	 */

VOID
SendBreak()
{
	if(WriteRequest)
	{
		BYTE OldStatus = Status;

		Status = STATUS_BREAKING;

		WriteRequest -> IOSer . io_Command = SDCMD_BREAK;

		DoIO(WriteRequest);

		Status = OldStatus;
	}
}

	/* HangUp():
	 *
	 *	Hang up the line.
	 */

VOID
HangUp()
{
	if(WriteRequest)
	{
		BYTE OldStatus = Status;

		Status = STATUS_HANGUP;

			/* Are we to drop the DTR line
			 * before sending the hangup
			 * string?
			 */

		if(Config -> ModemConfig -> DropDTR)
		{
			/* Let's be nice and try to transmit the
			 * `drop the line' command before
			 * trying to close and reopen the driver.
			 */

			WriteRequest -> IOSer . io_Command	= SIOCMD_SETCTRLLINES;
			WriteRequest -> IOSer . io_Offset	= SIOB_DTRF;
			WriteRequest -> IOSer . io_Length	= 0;

				/* Transmit the command. */

			if(!DoIO(WriteRequest))
			{
					/* Wait a bit... */

				WaitTime(1,0);

					/* Raise the line again. */

				WriteRequest -> IOSer . io_Command	= SIOCMD_SETCTRLLINES;
				WriteRequest -> IOSer . io_Offset	= SIOB_DTRF;
				WriteRequest -> IOSer . io_Length	= SIOB_DTRF;

				DoIO(WriteRequest);
			}
			else
			{
					/* Do it the standard way: close and reopen
					 * the serial driver (the serial.device is
					 * supposed to drop the DTR line when closed).
					 */

				if(!DropDTR())
				{
					if(!MyEasyRequest(Window,LocaleString(MSG_TERMMAIN_FAILED_TO_REOPEN_UNIT_TXT),LocaleString(MSG_TERMMAIN_IGNORE_QUIT_TXT),Config -> SerialConfig -> SerialDevice,Config -> SerialConfig -> UnitNumber))
						MainTerminated = TRUE;
				}
			}
		}

			/* Transmit the hangup command. */

		if(Config -> ModemConfig -> ModemHangup[0])
			SerialCommand(Config -> ModemConfig -> ModemHangup);

			/* Reset to old status. */

		Status = OldStatus;
	}
}

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

VOID __regargs
SetFlags(struct IOExtSer *SomeRequest)
{
		/* Remember new baud rate. */

	DTERate = Config -> SerialConfig -> BaudRate;

	SomeRequest -> io_Baud		= Config -> SerialConfig -> BaudRate;
	SomeRequest -> io_BrkTime	= Config -> SerialConfig -> BreakLength;
	SomeRequest -> io_ReadLen	= Config -> SerialConfig -> BitsPerChar;
	SomeRequest -> io_WriteLen	= Config -> SerialConfig -> BitsPerChar;
	SomeRequest -> io_StopBits	= Config -> SerialConfig -> StopBits;
	SomeRequest -> io_RBufLen	= Config -> SerialConfig -> SerialBufferSize;

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

	switch(Config -> SerialConfig -> 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;
	}

	if(Config -> SerialConfig -> HandshakingProtocol != HANDSHAKING_NONE)
		SomeRequest -> io_SerFlags |= SERF_7WIRE;

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

	if(Config -> SerialConfig -> Shared)
		SomeRequest -> io_SerFlags |= SERF_SHARED;

	SomeRequest -> io_SerFlags |= SERF_XDISABLED;
}

	/* GetFlags(struct Configuration *Config,struct IOExtSer *SomeRequest):
	 *
	 *	Update configuration with serial settings.
	 */

VOID __regargs
GetFlags(struct Configuration *Config,struct IOExtSer *SomeRequest)
{
	Config -> SerialConfig -> BaudRate	= SomeRequest -> io_Baud;
	Config -> SerialConfig -> BreakLength	= SomeRequest -> io_BrkTime;
	Config -> SerialConfig -> BitsPerChar	= SomeRequest -> io_ReadLen;
	Config -> SerialConfig -> BitsPerChar	= SomeRequest -> io_WriteLen;
	Config -> SerialConfig -> StopBits	= SomeRequest -> io_StopBits;

	if(SomeRequest -> io_SerFlags & SERF_PARTY_ON)
	{
		if(SomeRequest -> io_SerFlags & SERF_PARTY_ODD)
			Config -> SerialConfig -> Parity = PARITY_ODD;
		else
		{
			if(SomeRequest -> io_ExtFlags & SEXTF_MSPON)
			{
				if(SomeRequest -> io_ExtFlags & SEXTF_MARK)
					Config -> SerialConfig -> Parity = PARITY_MARK;
				else
					Config -> SerialConfig -> Parity = PARITY_SPACE;
			}
		}
	}
	else
		Config -> SerialConfig -> Parity = PARITY_NONE;

	if(SomeRequest -> io_SerFlags & SERF_7WIRE)
		Config -> SerialConfig -> HandshakingProtocol = HANDSHAKING_RTSCTS;
	else
		Config -> SerialConfig -> HandshakingProtocol = HANDSHAKING_NONE;

	if(SomeRequest -> io_SerFlags & SERF_RAD_BOOGIE)
		Config -> SerialConfig -> HighSpeed = TRUE;
	else
		Config -> SerialConfig -> HighSpeed = FALSE;

	if(SomeRequest -> io_SerFlags & SERF_SHARED)
		Config -> SerialConfig -> Shared = TRUE;
	else
		Config -> SerialConfig -> Shared = FALSE;

	SomeRequest -> io_SerFlags |= SERF_XDISABLED;
}

	/* CallMenu(STRPTR Name,ULONG Code):
	 *
	 *	Call a menu function either through the name of the corresponding
	 *	menu item or a menu number.
	 */

STATIC VOID __inline
CallMenu(STRPTR Name,ULONG Code)
{
	WORD MenuNum = -1,Item = 0,Sub = 0,i;

		/* Are we to look for a name? */

	if(Name)
	{
		WORD Len = strlen(Name);

			/* Scan the menu list... */

		for(i = 0 ; TermMenu[i] . nm_Type != NM_END ; i++)
		{
			switch(TermMenu[i] . nm_Type)
			{
				case NM_TITLE:

					MenuNum++;
					Item = Sub = 0;
					break;

				case NM_ITEM:

					Sub = 0;
					break;
			}

				/* Did we get a valid name string? */

			if(TermMenu[i] . nm_Label != NM_BARLABEL)
			{
					/* Does the name match our template? */

				if(!Strnicmp(TermMenu[i] . nm_Label,Name,Len))
				{
					struct MenuItem *MenuItem = ItemAddress(Menu,FULLMENUNUM(MenuNum,Item,Sub));

					if(MenuItem)
						HandleMenuCode((ULONG)TermMenu[i] . nm_UserData,NULL);

					break;
				}
			}

			switch(TermMenu[i] . nm_Type)
			{
				case NM_ITEM:

					Item++;
					break;

				case NM_SUB:

					Sub++;
					break;
			}
		}
	}
	else
	{
		WORD	TheMenu	=  Code % 100,
			TheItem	= (Code / 100) % 100,
			TheSub	=  Code / 10000;

			/* Scan the menu list... */

		for(i = 0 ; TermMenu[i] . nm_Type != NM_END ; i++)
		{
			switch(TermMenu[i] . nm_Type)
			{
				case NM_TITLE:

					MenuNum++;
					Item = Sub = 0;
					break;

				case NM_ITEM:

					Sub = 0;
					break;
			}

				/* Is it the menu number we want? */

			if(TheMenu == MenuNum && TheItem == Item && TheSub == Sub)
			{
				if(TermMenu[i] . nm_Label != NM_BARLABEL)
				{
					struct MenuItem *MenuItem = ItemAddress(Menu,FULLMENUNUM(MenuNum,Item,Sub));

					if(MenuItem)
						HandleMenuCode((ULONG)TermMenu[i] . nm_UserData,NULL);
				}

				break;
			}

			switch(TermMenu[i] . nm_Type)
			{
				case NM_ITEM:

					Item++;
					break;

				case NM_SUB:

					Sub++;
					break;
			}
		}
	}
}

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

VOID __regargs
SerialCommand(STRPTR String)
{
	BYTE	(*  SendLineLocal)(register STRPTR,register LONG);

	LONG	Count = 0,i,Len = strlen(String);

	BYTE	GotControl	= FALSE,
		GotEscape	= FALSE;

	SendLineLocal = SendLine;

		/* Scan the string. */

	for(i = 0 ; i < Len ; 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)
				{
					(*SendLineLocal)(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]) <= '_')
					SharedBuffer[Count++] = ToUpper(String[i]) - '@';
				else
					SharedBuffer[Count++] = String[i];

				GotEscape = FALSE;
			}

				/* The next character represents a command. */

			if(GotControl)
			{
				switch(ToUpper(String[i]))
				{
						/* Fall back to default send mode. */

					case '0':

						if(Count)
						{
							(*SendLineLocal)(SharedBuffer,Count);

							Count = 0;
						}

						SendLineLocal = SendLine;
						break;

						/* Select `direct' send mode. */

					case '1':

						if(Count)
						{
							(*SendLineLocal)(SharedBuffer,Count);

							Count = 0;
						}

						SendLineLocal = SendLineSimple;
						break;

						/* Select `echo' send mode. */

					case '2':

						if(Count)
						{
							(*SendLineLocal)(SharedBuffer,Count);

							Count = 0;
						}

						if(Config -> ClipConfig -> SendTimeout)
							SendLineLocal = SendLineEcho;
						else
							SendLineLocal = SendLineSimple;

						break;

						/* Select `any echo' send mode. */

					case '3':

						if(Count)
						{
							(*SendLineLocal)(SharedBuffer,Count);

							Count = 0;
						}

						if(Config -> ClipConfig -> SendTimeout)
							SendLineLocal = SendLineAnyEcho;
						else
							SendLineLocal = SendLineSimple;

						break;

						/* Select `prompt' send mode. */

					case '4':

						if(Count)
						{
							(*SendLineLocal)(SharedBuffer,Count);

							Count = 0;
						}

						if(Config -> ClipConfig -> SendTimeout)
							SendLineLocal = SendLinePrompt;
						else
							SendLineLocal = SendLineSimple;

						break;

						/* Select `delay' send mode. */

					case '5':

						if(Count)
						{
							(*SendLineLocal)(SharedBuffer,Count);

							Count = 0;
						}

						if(Config -> ClipConfig -> LineDelay || Config -> ClipConfig -> CharDelay)
							SendLineLocal = SendLineDelay;
						else
							SendLineLocal = SendLineSimple;

						break;

						/* Select `keyboard' send mode. */

					case '6':

						if(Count)
						{
							(*SendLineLocal)(SharedBuffer,Count);

							Count = 0;
						}

						SendLineLocal = SendLineKeyDelay;
						break;

						/* Translate code. */

					case '*':

						i++;

						while(i < Len && String[i] == ' ')
							i++;

						if(i < Len)
						{
							UBYTE DummyBuffer[5],j = 0,Char;

							if(String[i] >= '0' && String[i] <= '9')
							{
								while(j < 3 && i < Len)
								{
									Char = String[i++];

									if(Char >= '0' && Char <= '9')
										DummyBuffer[j++] = Char;
									else
									{
										i--;

										break;
									}
								}
							}
							else
							{
								while(j < 4 && i < Len)
								{
									Char = ToLower(String[i++]);

									if((Char >= '0' && Char <= '9') || (Char >= 'a' && Char <= 'z'))
										DummyBuffer[j++] = Char;
									else
									{
										i--;

										break;
									}
								}
							}

							DummyBuffer[j] = 0;

							SharedBuffer[Count++] = NameToCode(DummyBuffer);
						}

						i--;

						break;

						/* Execute an AmigaDOS command. */

					case 'D':

						if(!InRexx)
						{
							if(!WeAreBlocking)
							{
								BlockWindows();

								SendAmigaDOSCommand(&String[i + 1]);

								ReleaseWindows();
							}
							else
								SendAmigaDOSCommand(&String[i + 1]);
						}

						return;

						/* Execute an ARexx command. */

					case 'A':

						if(!InRexx)
						{
							if(!WeAreBlocking)
							{
								BlockWindows();

								SendARexxCommand(&String[i + 1]);

								ReleaseWindows();
							}
							else
								SendARexxCommand(&String[i + 1]);
						}

						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)
							{
								(*SendLineLocal)(SharedBuffer,Count);

								Count = 0;
							}

							(*SendLineLocal)(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 the current user name. */

					case 'U':

						if(UserName[0])
						{
							if(Count)
							{
								(*SendLineLocal)(SharedBuffer,Count);

								Count = 0;
							}

							(*SendLineLocal)(UserName,strlen(UserName));
						}

						break;

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

					case 'X':

						if(Count)
						{
							(*SendLineLocal)(SharedBuffer,Count);

							Count = 0;
						}

						SendBreak();

						break;

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

					case 'I':

						if(Count)
							(*SendLineLocal)(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;

						/* Send a string to the clipboard. */

					case 'H':

						if(String[i + 1])
							AddClip(&String[i + 1],strlen(&String[i + 1]));

						return;

						/* Produce the escape character. */

					case 'E':

						SharedBuffer[Count++] = ESC;
						break;

						/* Call a menu item. */

					case 'C':

						i++;

							/* Scan for a menu number or
							 * a single quote...
							 */

						while(i < Len)
						{
							if(String[i] >= '0' && String[i] <= '9')
								break;

							if(String[i] == '\'')
								break;

							if(String[i] != ' ')
								break;

							i++;
						}

						if(i < Len)
						{
							UBYTE DummyBuffer[256];

								/* Did we get a quote? */

							if(String[i] == '\'')
							{
								LONG Start = ++i;

								if(String[Start])
								{
									WORD Length;

									while(i < Len)
									{
										if(String[i] != '\'')
											i++;
										else
											break;
									}

									if(String[i] == '\'')
										Length = i - Start;
									else
										Length = i - Start + 1;

									memcpy(DummyBuffer,&String[Start],Length);

									DummyBuffer[Length] = 0;

									CallMenu(DummyBuffer,0);
								}
							}
							else
							{
								if(String[i] >= '0' && String[i] <= '9')
								{
									LONG Start = i,Length;

									while(i < Len)
									{
										if(String[i] >= '0' && String[i] <= '9')
											i++;
										else
											break;
									}

									if(i == Start)
										Length = 1;
									else
										Length = i - Start;

									memcpy(DummyBuffer,&String[Start],Length);

									DummyBuffer[Length] = 0;

									CallMenu(NULL,Atol(DummyBuffer));
								}
							}
						}

						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)
		{
			(*SendLineLocal)(SharedBuffer,Count);

			Count = 0;
		}
	}

	if(Count)
		(*SendLineLocal)(SharedBuffer,Count);
}

	/* SerWriteVerbatim(APTR Buffer,LONG Size,BOOLEAN Echo):
	 *
	 *	The `no fancy features' version of SerWrite().
	 */

VOID __regargs
SerWriteVerbatim(APTR Buffer,LONG Size,BOOLEAN Echo)
{
	if(XProtocolBase && (TransferBits & XPRS_USERMON))
		Size = XProtocolUserMon(XprIO,Buffer,Size,Size);

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

		if(Echo)
		{
			if(Marking)
				DropMarker();

			SendIO(WriteRequest);

			ConProcess(Buffer,Size);

			WaitIO(WriteRequest);
		}
		else
			DoIO(WriteRequest);

		BytesOut += Size;
	}
}

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

VOID __regargs
SerWrite(APTR Buffer,LONG Size)
{
	if(Size < 0)
		Size = strlen(Buffer);

	if(Size)
	{
		if(RememberInput)
		{
			RememberInputText(Buffer,Size);

			if(((UBYTE *)Buffer)[Size - 1] == '\r' && RecordingLine)
			{
				RememberSpill();

				RecordingLine = FALSE;

				RememberOutput = TRUE;
				RememberInput = FALSE;

				CheckItem(MEN_RECORD_LINE,FALSE);
			}
		}
		else
		{
			if(RememberOutput)
			{
				RememberInputText(Buffer,Size);

				RememberSpill();
			}
		}

		if(WriteRequest)
		{
			STATIC UBYTE __far TranslateData[512];

				/* xpr wants to see the data before it is
				 * transferred.
				 */

			if(XProtocolBase && (TransferBits & XPRS_USERMON))
			{
				if(Size = XProtocolUserMon(XprIO,Buffer,Size,Size))
				{
					if(SendTable)
					{
						struct TranslationHandle Handle;

							/* Set up for buffer translation. */

						TranslateSetup(&Handle,Buffer,Size,TranslateData,512,SendTable);

							/* Full or half duplex? */

						if(Config -> SerialConfig -> Duplex == DUPLEX_FULL)
						{
							if(SerWriteBypass)
							{
									/* Process the data... */

								while(Size = TranslateBuffer(&Handle))
								{
									if((*SerWriteBypass)(TranslateData,Size))
									{
										WriteRequest -> IOSer . io_Command	= CMD_WRITE;
										WriteRequest -> IOSer . io_Data		= TranslateData;
										WriteRequest -> IOSer . io_Length	= Size;

											/* ...and send it. */

										DoIO(WriteRequest);
									}

									BytesOut += Size;
								}
							}
							else
							{
									/* Process the data... */

								while(Size = TranslateBuffer(&Handle))
								{
									WriteRequest -> IOSer . io_Command	= CMD_WRITE;
									WriteRequest -> IOSer . io_Data		= TranslateData;
									WriteRequest -> IOSer . io_Length	= Size;

										/* ...and send it. */

									DoIO(WriteRequest);

									BytesOut += Size;
								}
							}
						}
						else
						{
							if(Marking)
								DropMarker();

							if(SerWriteBypass)
							{
								while(Size = TranslateBuffer(&Handle))
								{
									if((*SerWriteBypass)(TranslateData,Size))
									{
										WriteRequest -> IOSer . io_Command	= CMD_WRITE;
										WriteRequest -> IOSer . io_Data		= TranslateData;
										WriteRequest -> IOSer . io_Length	= Size;

											/* Process the data while it is transferred. */

										SendIO(WriteRequest);

										ConProcess(Buffer,Size);

										WaitIO(WriteRequest);
									}

									BytesOut += Size;
								}
							}
							else
							{
								while(Size = TranslateBuffer(&Handle))
								{
									WriteRequest -> IOSer . io_Command	= CMD_WRITE;
									WriteRequest -> IOSer . io_Data		= TranslateData;
									WriteRequest -> IOSer . io_Length	= Size;

										/* Process the data while it is transferred. */

									SendIO(WriteRequest);

									ConProcess(Buffer,Size);

									WaitIO(WriteRequest);

									BytesOut += Size;
								}
							}
						}
					}
					else
					{
						if(SerWriteBypass)
						{
							if((*SerWriteBypass)(Buffer,Size))
							{
								WriteRequest -> IOSer . io_Command	= CMD_WRITE;
								WriteRequest -> IOSer . io_Data		= Buffer;
								WriteRequest -> IOSer . io_Length	= Size;

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

								if(Config -> SerialConfig -> Duplex == DUPLEX_FULL)
									DoIO(WriteRequest);
								else
								{
										/* Process the data while it is transferred. */

									SendIO(WriteRequest);

									if(Marking)
										DropMarker();

									ConProcess(Buffer,Size);

									WaitIO(WriteRequest);
								}
							}
							else
							{
								if(Config -> SerialConfig -> Duplex != DUPLEX_FULL)
								{
									if(Marking)
										DropMarker();

									ConProcess(Buffer,Size);
								}
							}
						}
						else
						{
							WriteRequest -> IOSer . io_Command	= CMD_WRITE;
							WriteRequest -> IOSer . io_Data		= Buffer;
							WriteRequest -> IOSer . io_Length	= Size;

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

							if(Config -> SerialConfig -> Duplex == DUPLEX_FULL)
								DoIO(WriteRequest);
							else
							{
									/* Process the data while it is transferred. */

								SendIO(WriteRequest);

								if(Marking)
									DropMarker();

								ConProcess(Buffer,Size);

								WaitIO(WriteRequest);
							}
						}

						BytesOut += Size;
					}
				}
			}
			else
			{
				if(SendTable)
				{
					struct TranslationHandle Handle;

						/* Set up for buffer translation. */

					TranslateSetup(&Handle,Buffer,Size,TranslateData,512,SendTable);

						/* Full or half duplex? */

					if(Config -> SerialConfig -> Duplex == DUPLEX_FULL)
					{
						if(SerWriteBypass)
						{
								/* Process the data... */

							while(Size = TranslateBuffer(&Handle))
							{
								if((*SerWriteBypass)(TranslateData,Size))
								{
									WriteRequest -> IOSer . io_Command	= CMD_WRITE;
									WriteRequest -> IOSer . io_Data		= TranslateData;
									WriteRequest -> IOSer . io_Length	= Size;

										/* ...and send it. */

									DoIO(WriteRequest);
								}

								BytesOut += Size;
							}
						}
						else
						{
								/* Process the data... */

							while(Size = TranslateBuffer(&Handle))
							{
								WriteRequest -> IOSer . io_Command	= CMD_WRITE;
								WriteRequest -> IOSer . io_Data		= TranslateData;
								WriteRequest -> IOSer . io_Length	= Size;

									/* ...and send it. */

								DoIO(WriteRequest);

								BytesOut += Size;
							}
						}
					}
					else
					{
						if(Marking)
							DropMarker();

						if(SerWriteBypass)
						{
							while(Size = TranslateBuffer(&Handle))
							{
								if((*SerWriteBypass)(TranslateData,Size))
								{
									WriteRequest -> IOSer . io_Command	= CMD_WRITE;
									WriteRequest -> IOSer . io_Data		= TranslateData;
									WriteRequest -> IOSer . io_Length	= Size;

										/* Process the data while it is transferred. */

									SendIO(WriteRequest);

									ConProcess(Buffer,Size);

									WaitIO(WriteRequest);
								}

								BytesOut += Size;
							}
						}
						else
						{
							while(Size = TranslateBuffer(&Handle))
							{
								WriteRequest -> IOSer . io_Command	= CMD_WRITE;
								WriteRequest -> IOSer . io_Data		= TranslateData;
								WriteRequest -> IOSer . io_Length	= Size;

									/* Process the data while it is transferred. */

								SendIO(WriteRequest);

								ConProcess(Buffer,Size);

								WaitIO(WriteRequest);

								BytesOut += Size;
							}
						}
					}
				}
				else
				{
					if(SerWriteBypass)
					{
						if((*SerWriteBypass)(Buffer,Size))
						{
							WriteRequest -> IOSer . io_Command	= CMD_WRITE;
							WriteRequest -> IOSer . io_Data		= Buffer;
							WriteRequest -> IOSer . io_Length	= Size;

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

							if(Config -> SerialConfig -> Duplex == DUPLEX_FULL)
								DoIO(WriteRequest);
							else
							{
									/* Process the data while it is transferred. */

								SendIO(WriteRequest);

								if(Marking)
									DropMarker();

								ConProcess(Buffer,Size);

								WaitIO(WriteRequest);
							}
						}
					}
					else
					{
						WriteRequest -> IOSer . io_Command	= CMD_WRITE;
						WriteRequest -> IOSer . io_Data		= Buffer;
						WriteRequest -> IOSer . io_Length	= Size;

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

						if(Config -> SerialConfig -> Duplex == DUPLEX_FULL)
							DoIO(WriteRequest);
						else
						{
								/* Process the data while it is transferred. */

							SendIO(WriteRequest);

							if(Marking)
								DropMarker();

							ConProcess(Buffer,Size);

							WaitIO(WriteRequest);
						}
					}

					BytesOut += Size;
				}
			}
		}
	}
}

	/* RestartSerial():
	 *
	 *	Restart read/write activity on the serial line.
	 */

VOID __regargs
RestartSerial(BYTE Override)
{
	if(ReadRequest)
	{
		ReadRequest -> IOSer . io_Command	= CMD_READ;
		ReadRequest -> IOSer . io_Data		= ReadBuffer;
		ReadRequest -> IOSer . io_Length 	= 1;

		ClrSignal(SIG_SERIAL);

		SendIO(ReadRequest);
	}
}

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

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

		WaitIO(ReadRequest);
	}

	if(WriteRequest)
	{
		WriteRequest -> IOSer . io_Command = CMD_FLUSH;

		DoIO(WriteRequest);

		WriteRequest -> IOSer . io_Command = CMD_CLEAR;

		DoIO(WriteRequest);
	}
}

	/* DropDTR():
	 *
	 *	Drop the data terminal ready signal (i.e. close, wait a bit
	 *	and then reopen the serial drive).
	 */

BYTE
DropDTR()
{
		/* Finish all serial read activity. */

	ClearSerial();

		/* Do we have any channels to work with? */

	if(ReadRequest && WriteRequest)
	{
			/* Close the device. */

		CloseDevice(ReadRequest);

			/* Wait a bit. */

		WaitTime(1,0);

			/* Set up the original configuration data. */

		SetFlags(ReadRequest);

		ReadRequest -> io_RBufLen = Config -> SerialConfig -> SerialBufferSize;

			/* Reopen the driver. */

		if(!OpenDevice(Config -> SerialConfig -> SerialDevice,Config -> SerialConfig -> UnitNumber,ReadRequest,0))
		{
			struct MsgPort *WritePort = WriteRequest -> IOSer . io_Message . mn_ReplyPort;

				/* Fill in the rest. */

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

			WriteRequest -> IOSer . io_Message . mn_ReplyPort = WritePort;

			WriteRequest -> IOSer . io_Command = SDCMD_SETPARAMS;

			SetFlags(WriteRequest);

			DoIO(WriteRequest);

				/* Restart read activity. */

			RestartSerial(FALSE);

			return(TRUE);
		}
		else
			DeleteSerial();
	}
	else
	{
		DeleteSerial();

		return(TRUE);
	}

	return(FALSE);
}

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

VOID
DeleteSerial()
{
	BYTE Closed = FALSE;

	if(ReadRequest)
	{
		if(ReadRequest -> IOSer . io_Device)
		{
			struct Device	*Device;
			UBYTE		 Name[MAX_FILENAME_LENGTH];

			if(!Config -> SerialConfig -> Shared)
			{
				ReadRequest -> IOSer . io_Command = CMD_RESET;

				DoIO(ReadRequest);
			}

			strcpy(Name,ReadRequest -> IOSer . io_Device -> dd_Library . lib_Node . ln_Name);

			CloseDevice(ReadRequest);

			Forbid();

			if(Device = (struct Device *)FindName(&SysBase -> DeviceList,Name))
				RemDevice(Device);

			Permit();

			Closed = TRUE;
		}

		DeleteIORequest(ReadRequest);

		ReadRequest = NULL;
	}

	if(WriteRequest)
	{
		if(WriteRequest -> IOSer . io_Device && !Closed)
		{
			struct Device	*Device;
			UBYTE		 Name[MAX_FILENAME_LENGTH];

			if(!Config -> SerialConfig -> Shared && ReadRequest)
			{
				ReadRequest -> IOSer . io_Command = CMD_RESET;

				DoIO(ReadRequest);
			}

			strcpy(Name,WriteRequest -> IOSer . io_Device -> dd_Library . lib_Node . ln_Name);

			CloseDevice(WriteRequest);

			Forbid();

			if(Device = (struct Device *)FindName(&SysBase -> DeviceList,Name))
				RemDevice(Device);

			Permit();
		}

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

		DeleteIORequest(WriteRequest);

		WriteRequest = NULL;
	}

	if(ReadPort)
	{
		DeleteMsgPort(ReadPort);

		ReadPort = NULL;
	}

	if(ReadBuffer)
	{
		FreeVecPooled(ReadBuffer);

		ReadBuffer = NULL;
	}

	if(HostReadBuffer)
	{
		FreeVecPooled(HostReadBuffer);

		HostReadBuffer = NULL;
	}

	if(StripBuffer)
	{
		FreeVecPooled(StripBuffer);

		StripBuffer = NULL;
	}

	if(OwnDevUnitBase && SerialDevice[0] && UnitNumber != -1 && SerialLocked)
	{
		FreeDevUnit(SerialDevice,UnitNumber);

		SerialDevice[0] = 0;

		UnitNumber = -1;
	}

	if(OwnDevBit != -1)
	{
		FreeSignal(OwnDevBit);

		OwnDevBit = -1;
	}

	SerialLocked = FALSE;
}

	/* GetSerialError(LONG Error,BYTE *Reset):
	 *
	 *	Return an error message for each possible serial device error.
	 */

STRPTR __regargs
GetSerialError(LONG Error,BYTE *Reset)
{
	if(Reset)
		*Reset = FALSE;

	switch(Error)
	{
		case IOERR_BADLENGTH:
		case IOERR_BADADDRESS:
		case IOERR_SELFTEST:
		case IOERR_OPENFAIL:

			return(LocaleString(MSG_SERIAL_OPENFAILURE_TXT));

		case SerErr_DevBusy:

			return(LocaleString(MSG_SERIAL_ERROR_DEVBUSY_TXT));

		case SerErr_BaudMismatch:

			if(Reset)
				*Reset = TRUE;

			return(LocaleString(MSG_SERIAL_ERROR_BAUDMISMATCH_TXT));

		case SerErr_BufErr:

			if(Reset)
				*Reset = TRUE;

			return(LocaleString(MSG_SERIAL_ERROR_BUFERR_TXT));

		case SerErr_InvParam:

			if(Reset)
				*Reset = TRUE;

			return(LocaleString(MSG_SERIAL_ERROR_INVPARAM_TXT));

		case SerErr_LineErr:

			return(LocaleString(MSG_SERIAL_ERROR_LINEERR_TXT));

		case SerErr_ParityErr:

			if(Reset)
				*Reset = TRUE;

			return(LocaleString(MSG_SERIAL_ERROR_PARITYERR_TXT));

		case SerErr_TimerErr:

			return(LocaleString(MSG_SERIAL_ERROR_TIMERERR_TXT));

		case SerErr_BufOverflow:

			if(Reset)
				*Reset = TRUE;

			return(LocaleString(MSG_SERIAL_ERROR_BUFOVERFLOW_TXT));

		case SerErr_NoDSR:

			return(LocaleString(MSG_SERIAL_ERROR_NODSR_TXT));

	/*	case SerErr_UnitBusy:	*/
		case IOERR_UNITBUSY:
		case 16:

			return(LocaleString(MSG_SERIAL_ERROR_UNIT_BUSY_TXT));

		default:

			return(NULL);
	}
}

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

STRPTR
CreateSerial()
{
	struct MsgPort *WritePort;

		/* If OwnDevUnit.library is available, try to lock
		 * the serial driver.
		 */

Start:	if(OwnDevUnitBase && Config -> SerialConfig -> UseOwnDevUnit)
	{
		STRPTR Error;

			/* Allocate a signal for OwnDevUnit, in case we might need it. */

		OwnDevBit = AllocSignal(-1);

			/* Attempt to lock the device unit... */

		if(Error = AttemptDevUnit(Config -> SerialConfig -> SerialDevice,Config -> SerialConfig -> UnitNumber,TermIDString,OwnDevBit == -1 ? NULL : OwnDevBit))
		{
				/* Check for error type if any. */

			if(!Strnicmp(Error,ODUERR_LEADCHAR,1))
				SPrintf(SharedBuffer,LocaleString(MSG_SERIAL_ERROR_ACCESSING_TXT),Config -> SerialConfig -> SerialDevice,Config -> SerialConfig -> UnitNumber,&Error[1]);
			else
				SPrintf(SharedBuffer,LocaleString(MSG_SERIAL_DEVICE_IN_USE_TXT),Config -> SerialConfig -> SerialDevice,Config -> SerialConfig -> UnitNumber,Error);

			SerialDevice[0] = 0;

			UnitNumber = -1;

			SerialLocked = FALSE;

			return(SharedBuffer);
		}
		else
		{
			strcpy(SerialDevice,Config -> SerialConfig -> SerialDevice);

			UnitNumber = Config -> SerialConfig -> UnitNumber;

			SerialLocked = TRUE;
		}
	}
	else
		SerialLocked = FALSE;

	if(ReadBuffer = AllocVecPooled(Config -> SerialConfig -> SerialBufferSize,MEMF_ANY))
	{
		if(StripBuffer = AllocVecPooled(Config -> SerialConfig -> SerialBufferSize,MEMF_ANY))
		{
			if(XProtocolBase && (TransferBits & XPRS_HOSTNOWAIT))
				HostReadBuffer = AllocVecPooled(Config -> SerialConfig -> SerialBufferSize,MEMF_ANY);
			else
				HostReadBuffer = NULL;

			if(ReadPort = (struct MsgPort *)CreateMsgPort())
			{
				if(ReadRequest = (struct IOExtSer *)CreateIORequest(ReadPort,sizeof(struct IOExtSer)))
				{
					LONG Error;

					SetFlags(ReadRequest);

					if(!(Error = OpenDevice(Config -> SerialConfig -> SerialDevice,Config -> SerialConfig -> 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;

								WriteRequest -> IOSer . io_Command = SDCMD_SETPARAMS;
								SetFlags(WriteRequest);
								DoIO(WriteRequest);

								ReadRequest -> IOSer . io_Command = SDCMD_SETPARAMS;
								SetFlags(ReadRequest);
								DoIO(ReadRequest);

								/* If RTS/CTS (7 wire handshaking) is
								 * selected, have a look at the DSR
								 * line to see whether the modem
								 * connected is willing to support
								 * this handshaking mode.
								 */

								if(Config -> SerialConfig -> HandshakingProtocol == HANDSHAKING_RTSCTS_DSR)
								{
Retry:									WriteRequest -> IOSer . io_Command = SDCMD_QUERY;

									DoIO(WriteRequest);

										/* If the line happens to
										 * be high, there is no
										 * DSR signal present.
										 */

									if(WriteRequest -> io_Status & CIAF_COMDSR)
									{
										if(MyEasyRequest(Window,LocaleString(MSG_SERIAL_NO_DSR_SIGNAL_TXT),LocaleString(MSG_SERIAL_RETRY_CANCEL_TXT)))
											goto Retry;
										else
										{
											DeleteSerial();

											Config -> SerialConfig -> HandshakingProtocol = HANDSHAKING_NONE;

											SerialMessage = LocaleString(MSG_SERIAL_NO_DSR_SIGNAL_HANDSHAKING_DISABLED_TXT);

											goto Start;
										}
									}
								}

								RestartSerial(FALSE);

								return(NULL);
							}
						}
						else
						{
							SerialMessage = NULL;

							DeleteSerial();

							return(LocaleString(MSG_SERIAL_FAILED_TO_CREATE_WRITE_PORT_TXT));
						}
					}
					else
					{
						STRPTR String;

						ReadRequest -> IOSer . io_Device = NULL;

						DeleteSerial();

						SerialMessage = NULL;

						if(!(String = GetSerialError(Error,NULL)))
							String = LocaleString(MSG_SERIAL_ERROR_DEVBUSY_TXT);

						SPrintf(SharedBuffer,String,Config -> SerialConfig -> SerialDevice,Config -> SerialConfig -> UnitNumber);

						return(SharedBuffer);
					}
				}
			}
			else
			{
				SerialMessage = NULL;

				DeleteSerial();

				return(LocaleString(MSG_SERIAL_FAILED_TO_CREATE_READ_PORT_TXT));
			}
		}
	}

	SerialMessage = NULL;

	DeleteSerial();

	return(LocaleString(MSG_SERIAL_NOT_ENOUGH_MEMORY_TXT));
}

	/* ReconfigureSerial(struct Window *Window,struct SerialSettings *SerialConfig):
	 *
	 *	Reconfigure the serial driver according to the new
	 *	serial settings.
	 */

BYTE __regargs
ReconfigureSerial(struct Window *Window,struct SerialSettings *SerialConfig)
{
	BYTE	Success	= RECONFIGURE_NOCHANGE,
		IsNew;

		/* Are new settings to be installed or have they already
		 * been copied to the correct locations?
		 */

	if(SerialConfig)
	{
		if(memcmp(Config -> SerialConfig,SerialConfig,sizeof(struct SerialSettings)))
			IsNew = TRUE;
		else
			IsNew = FALSE;
	}
	else
	{
		if(memcmp(Config -> SerialConfig,PrivateConfig -> SerialConfig,sizeof(struct SerialSettings)))
			IsNew = TRUE;
		else
			IsNew = FALSE;
	}

		/* Any changes in the serial configuration area? */

	if(IsNew)
	{
		BYTE SameDevice = TRUE;

			/* Any new data to swap in? */

		if(SerialConfig)
		{
				/* Store the previous settings. */

			SaveConfig(Config,PrivateConfig);

				/* Install the new settings. */

			memcpy(Config -> SerialConfig,SerialConfig,sizeof(struct SerialSettings));
		}

			/* Any device name or unit number change? */

		if(strcmp(PrivateConfig -> SerialConfig -> SerialDevice,Config -> SerialConfig -> SerialDevice) || PrivateConfig -> SerialConfig -> UnitNumber != Config -> SerialConfig -> UnitNumber || PrivateConfig -> SerialConfig -> UseOwnDevUnit != Config -> SerialConfig -> UseOwnDevUnit)
			SameDevice = FALSE;
		else
		{
				/* Handshaking mode changed to RTS/CTS protocol? */

			if((PrivateConfig -> SerialConfig -> HandshakingProtocol == HANDSHAKING_NONE && Config -> SerialConfig -> HandshakingProtocol != HANDSHAKING_NONE) || (PrivateConfig -> SerialConfig -> HandshakingProtocol != HANDSHAKING_NONE && Config -> SerialConfig -> HandshakingProtocol == HANDSHAKING_NONE))
				SameDevice = FALSE;
		}

			/* Stop any IO activity. */

		if(ReadRequest)
			ClearSerial();
		else
			SameDevice = FALSE;

			/* No dramatic changes? Simply change the parameters. */

		if(SameDevice)
		{
			LONG Error;

			if(PrivateConfig -> SerialConfig -> SerialBufferSize != Config -> SerialConfig -> SerialBufferSize)
			{
				STRPTR NewReadBuffer;

				if(NewReadBuffer = AllocVecPooled(Config -> SerialConfig -> SerialBufferSize,MEMF_ANY))
				{
					STRPTR NewStripBuffer;

					if(NewStripBuffer = AllocVecPooled(Config -> SerialConfig -> SerialBufferSize,MEMF_ANY))
					{
						FreeVecPooled(ReadBuffer);
						FreeVecPooled(StripBuffer);

						ReadBuffer	= NewReadBuffer;
						StripBuffer	= NewStripBuffer;
					}
					else
					{
						FreeVecPooled(NewReadBuffer);

						Config -> SerialConfig -> SerialBufferSize = PrivateConfig -> SerialConfig -> SerialBufferSize;
					}
				}
				else
					Config -> SerialConfig -> SerialBufferSize = PrivateConfig -> SerialConfig -> SerialBufferSize;
			}

				/* Use new parameters... */

			SetFlags(WriteRequest);
			SetFlags(ReadRequest);

				/* ...and set them. */

			WriteRequest -> IOSer . io_Command	= SDCMD_SETPARAMS;
			ReadRequest -> IOSer . io_Command	= SDCMD_SETPARAMS;

			if(!(Error = DoIO(WriteRequest)))
				Error = DoIO(ReadRequest);

				/* Trouble? */

			if(Error)
			{
				STRPTR	String;
				BYTE	Reset;

					/* Query the error message. */

				if(!(String = GetSerialError(Error,&Reset)))
					String = LocaleString(MSG_SERIAL_ERROR_DEVBUSY_TXT);

					/* Build the device name/unit number string. */

				SPrintf(SharedBuffer,String,Config -> SerialConfig -> SerialDevice,Config -> SerialConfig -> UnitNumber);

					/* Display the error requester. */

				LT_LockWindow(Window);

				MyEasyRequest(Window,LocaleString(MSG_GLOBAL_TERM_HAS_A_PROBLEM_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),SharedBuffer);

				LT_UnlockWindow(Window);

					/* Is a serial driver reset required? */

				if(Reset)
				{
						/* Execute the reset command. */

					WriteRequest -> IOSer . io_Command = CMD_RESET;

					DoIO(WriteRequest);

						/* Copy the serial driver settings
						 * to the global configuration.
						 */

					GetFlags(Config,WriteRequest);

						/* Also set the read request driver
						 * flags.
						 */

					SetFlags(ReadRequest);

					ReadRequest -> IOSer . io_Command = SDCMD_SETPARAMS;

					DoIO(ReadRequest);
				}
			}

				/* Restart read request. */

			RestartSerial(FALSE);
		}
		else
		{
			STRPTR Error;

				/* Shut down the serial driver. */

			DeleteSerial();

				/* Reinitialize the serial driver. */

			if(!(Error = CreateSerial()))
			{
					/* Free the work buffer. */

				if(StripBuffer)
					FreeVecPooled(StripBuffer);

					/* Try to allocate a new serial data work buffer. */

				if(!(StripBuffer = (STRPTR)AllocVecPooled(Config -> SerialConfig -> SerialBufferSize,MEMF_ANY)))
					Error = LocaleString(MSG_GLOBAL_NO_AUX_BUFFERS_TXT);
			}

				/* Trouble? */

			if(Error)
			{
					/* Display the error requester. */

				LT_LockWindow(Window);

				MyEasyRequest(Window,LocaleString(MSG_GLOBAL_TERM_HAS_A_PROBLEM_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),Error);

					/* Clean up the mess :-( */

				DeleteSerial();

				LT_UnlockWindow(Window);

				Success = RECONFIGURE_FAILURE;
			}
			else
			{
					/* Are we to display an error message? */

				if(SerialMessage)
				{
					MyEasyRequest(Window,LocaleString(MSG_GLOBAL_TERM_HAS_A_PROBLEM_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),SerialMessage);

					SerialMessage = NULL;
				}
			}
		}
	}

	return(Success);
}

	/* ReopenSerial():
	 *
	 *	Reopen the serial driver.
	 */

VOID
ReopenSerial()
{
	BYTE	SerialClosed = TRUE;
	STRPTR	Error;

	do
	{
		if(Error = CreateSerial())
		{
			DeleteSerial();

			switch(MyEasyRequest(Window,LocaleString(MSG_GLOBAL_TERM_HAS_A_PROBLEM_TXT),LocaleString(MSG_TERMMAIN_RETRY_IGNORE_QUIT_TXT),Error))
			{
				case 2:	SerialClosed = FALSE;
					break;

				case 0:	MainTerminated = TRUE;
					break;
			}
		}
		else
		{
			SerialClosed = FALSE;

			if(SerialMessage)
			{
				MyEasyRequest(Window,LocaleString(MSG_GLOBAL_TERM_HAS_A_PROBLEM_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),SerialMessage);

				SerialMessage = NULL;
			}
		}
	}
	while(SerialClosed);
}

	/* HandleSerial():
	 *
	 *	Handle the data coming in from the serial line.
	 */

BYTE
HandleSerial()
{
	BYTE MoreData = FALSE;

	if(ReadPort && !ReleaseSerial && Status != STATUS_HOLDING)
	{
		if(HostReadBuffer)
		{
			LONG Length = XProtocolHostMon(XprIO,HostReadBuffer,0,Config -> SerialConfig -> SerialBufferSize);

			if(Length)
			{
				if(Marking)
					DropMarker();

				ConProcess(HostReadBuffer,Length);

				if(Status == STATUS_HOLDING)
					return(FALSE);
			}

			MoreData = TRUE;
		}

		if(DataHold)
		{
			register STRPTR	Data	= DataHold;
			register LONG	Size	= DataSize;

			DataHold = NULL;

			if(Marking)
				DropMarker();

			(* ConTransfer)(Data,Size);

			MoreData = TRUE;

			RestartSerial(FALSE);

			return(MoreData);
		}

			/* Any news? */

		if(CheckIO(ReadRequest))
		{
			MoreData = TRUE;

			if(!WaitIO(ReadRequest))
			{
				if(Marking)
					DropMarker();

				BytesIn++;

				(* ConTransfer)(ReadBuffer,1);

				if(Status != STATUS_HOLDING && !DataHold)
				{
					ULONG Length;

						/* Check how many bytes are still in
						 * the serial buffer.
						 */

					WriteRequest -> IOSer . io_Command = SDCMD_QUERY;

					DoIO(WriteRequest);

					if(Length = WriteRequest -> IOSer . io_Actual)
					{
						ULONG Max = Config -> SerialConfig -> SerialBufferSize;

						if(Max > Config -> SerialConfig -> Quantum)
							Max = Config -> SerialConfig -> Quantum;

						if(Length > Max)
							Length = Max;

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

						if(!DoIO(ReadRequest))
						{
							BytesIn += Length;

							(* ConTransfer)(ReadBuffer,Length);
						}
					}
				}
			}

			if(DataHold)
				ClrSignal(SIG_SERIAL);
			else
				RestartSerial(FALSE);
		}
	}

	return(MoreData);
}
