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

#include "termGlobal.h"

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

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

STATIC BOOLEAN		SerialLocked = FALSE;

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

STATIC BYTE __regargs
SetFlags(BOOLEAN Read)
{
	if(Read)
	{
		return(SetSerialWriteAttributes(
			SERA_Baud,		Config -> SerialConfig -> BaudRate,
			SERA_BreakTime,		Config -> SerialConfig -> BreakLength,
			SERA_BitsPerChar,	Config -> SerialConfig -> BitsPerChar,
			SERA_StopBits,		Config -> SerialConfig -> StopBits,
			SERA_BufferSize,	Config -> SerialConfig -> SerialBufferSize,
			SERA_Parity,		Config -> SerialConfig -> Parity,
			SERA_Handshaking,	Config -> SerialConfig -> HandshakingProtocol,
			SERA_HighSpeed,		Config -> SerialConfig -> HighSpeed,
			SERA_Shared,		Config -> SerialConfig -> Shared,
		TAG_DONE));
	}
	else
	{
		return(SetSerialWriteAttributes(
			SERA_Baud,		Config -> SerialConfig -> BaudRate,
			SERA_BreakTime,		Config -> SerialConfig -> BreakLength,
			SERA_BitsPerChar,	Config -> SerialConfig -> BitsPerChar,
			SERA_StopBits,		Config -> SerialConfig -> StopBits,
			SERA_BufferSize,	Config -> SerialConfig -> SerialBufferSize,
			SERA_Parity,		Config -> SerialConfig -> Parity,
			SERA_Handshaking,	Config -> SerialConfig -> HandshakingProtocol,
			SERA_HighSpeed,		Config -> SerialConfig -> HighSpeed,
			SERA_Shared,		Config -> SerialConfig -> Shared,
		TAG_DONE));
	}
}

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

VOID
SendBreak()
{
	BYTE OldStatus = Status;

	Status = STATUS_BREAKING;

	DoSerialCmd(SDCMD_BREAK);

	Status = OldStatus;
}

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

VOID
HangUp()
{
	BYTE OldStatus = Status;

	StopSerialWrite();

	Status = STATUS_HANGUP;

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

	if(Config -> ModemConfig -> DropDTR && WriteRequest)
	{
		/* 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;
}

	/* 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);

			/* Reopen the driver. */

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

			ResetSerialRead();
			ResetSerialWrite();

				/* Fill in the rest. */

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

			WriteRequest -> IOSer . io_Message . mn_ReplyPort = WritePort;

			SetFlags(FALSE);

			DoSerialCmd(SDCMD_SETPARAMS);

				/* Restart read activity. */

			RestartSerial(FALSE);

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

		return(TRUE);
	}

	return(FALSE);
}

	/* CopyWriteFlags():
	 *
	 *	Update configuration with serial settings.
	 */

VOID
CopyWriteFlags()
{
	ULONG	Baud,
		BreakTime,
		BitsPerChar,
		StopBits,
		BufferSize,
		Parity,
		Handshaking,
		HighSpeed,
		Shared;

	GetSerialWriteAttributes(
		SERA_Baud,		&Baud,
		SERA_BreakTime,		&BreakTime,
		SERA_BitsPerChar,	&BitsPerChar,
		SERA_StopBits,		&StopBits,
		SERA_BufferSize,	&BufferSize,
		SERA_Parity,		&Parity,
		SERA_Handshaking,	&Handshaking,
		SERA_HighSpeed,		&HighSpeed,
		SERA_Shared,		&Shared,
	TAG_DONE);

	Config -> SerialConfig -> BaudRate		= Baud;
	Config -> SerialConfig -> BreakLength		= BreakTime;
	Config -> SerialConfig -> BitsPerChar		= BitsPerChar;
	Config -> SerialConfig -> StopBits		= StopBits;
	Config -> SerialConfig -> SerialBufferSize	= BufferSize;
	Config -> SerialConfig -> Parity		= Parity;
	Config -> SerialConfig -> HandshakingProtocol	= Handshaking;
	Config -> SerialConfig -> HighSpeed		= HighSpeed;
	Config -> SerialConfig -> Shared		= Shared;
}

	/* 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)
	{
		if(Echo)
		{
			if(Marking)
				DropMarker();

			StartSerialWrite(Buffer,Size);

			ConProcess(Buffer,Size);

			WaitSerialWrite();
		}
		else
			DoSerialWrite(Buffer,Size);

		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))
									{
											/* ...and send it. */

										DoSerialWrite(TranslateData,Size);
									}

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

								while(Size = TranslateBuffer(&Handle))
								{
										/* ...and send it. */

									DoSerialWrite(TranslateData,Size);

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

							if(SerWriteBypass)
							{
								while(Size = TranslateBuffer(&Handle))
								{
									if((*SerWriteBypass)(TranslateData,Size))
									{
											/* Process the data while it is transferred. */

										StartSerialWrite(TranslateData,Size);

										ConProcess(Buffer,Size);

										WaitSerialWrite();
									}

									BytesOut += Size;
								}
							}
							else
							{
								while(Size = TranslateBuffer(&Handle))
								{
										/* Process the data while it is transferred. */

									StartSerialWrite(TranslateData,Size);

									ConProcess(TranslateData,Size);

									WaitSerialWrite();

									BytesOut += Size;
								}
							}
						}
					}
					else
					{
						if(SerWriteBypass)
						{
							if((*SerWriteBypass)(Buffer,Size))
							{
									/* If full duplex is enabled, send the entire
									 * buffer without processing.
									 */

								if(Config -> SerialConfig -> Duplex == DUPLEX_FULL)
									DoSerialWrite(Buffer,Size);
								else
								{
										/* Process the data while it is transferred. */

									StartSerialWrite(Buffer,Size);

									if(Marking)
										DropMarker();

									ConProcess(Buffer,Size);

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

									ConProcess(Buffer,Size);
								}
							}
						}
						else
						{
								/* If full duplex is enabled, send the entire
								 * buffer without processing.
								 */

							if(Config -> SerialConfig -> Duplex == DUPLEX_FULL)
								DoSerialWrite(Buffer,Size);
							else
							{
									/* Process the data while it is transferred. */

								StartSerialWrite(Buffer,Size);

								if(Marking)
									DropMarker();

								ConProcess(Buffer,Size);

								WaitSerialWrite();
							}
						}

						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))
								{
										/* ...and send it. */

									DoSerialWrite(TranslateData,Size);
								}

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

							while(Size = TranslateBuffer(&Handle))
							{
									/* ...and send it. */

								DoSerialWrite(TranslateData,Size);

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

						if(SerWriteBypass)
						{
							while(Size = TranslateBuffer(&Handle))
							{
								if((*SerWriteBypass)(TranslateData,Size))
								{
										/* Process the data while it is transferred. */

									StartSerialWrite(TranslateData,Size);

									ConProcess(TranslateData,Size);

									WaitSerialWrite();
								}

								BytesOut += Size;
							}
						}
						else
						{
							while(Size = TranslateBuffer(&Handle))
							{
									/* Process the data while it is transferred. */

								StartSerialWrite(TranslateData,Size);

								ConProcess(TranslateData,Size);

								WaitSerialWrite();

								BytesOut += Size;
							}
						}
					}
				}
				else
				{
					if(SerWriteBypass)
					{
						if((*SerWriteBypass)(Buffer,Size))
						{
								/* If full duplex is enabled, send the entire
								 * buffer without processing.
								 */

							if(Config -> SerialConfig -> Duplex == DUPLEX_FULL)
								DoSerialWrite(Buffer,Size);
							else
							{
									/* Process the data while it is transferred. */

								StartSerialWrite(Buffer,Size);

								if(Marking)
									DropMarker();

								ConProcess(Buffer,Size);

								WaitSerialWrite();
							}
						}
					}
					else
					{
							/* If full duplex is enabled, send the entire
							 * buffer without processing.
							 */

						if(Config -> SerialConfig -> Duplex == DUPLEX_FULL)
							DoSerialWrite(Buffer,Size);
						else
						{
								/* Process the data while it is transferred. */

							StartSerialWrite(Buffer,Size);

							if(Marking)
								DropMarker();

							ConProcess(Buffer,Size);

							WaitSerialWrite();
						}
					}

					BytesOut += Size;
				}
			}
		}
	}
}

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

VOID __regargs
RestartSerial(BYTE Override)
{
	StartSerialRead(ReadBuffer,1);
}

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

VOID
ClearSerial()
{
	StopSerialRead();

	DoSerialCmd(CMD_CLEAR);
}

	/* 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];

			StopSerialRead();

			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];

			StopSerialWrite();

			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;

	ResetSerialRead();
	ResetSerialWrite();
}

	/* 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;

	Update_CR_LF_Translation();

	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(TRUE);

					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;

								ResetSerialRead();
								ResetSerialWrite();

								SetFlags(TRUE);
								SetFlags(FALSE);

								/* 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)
								{
										/* If the line happens to
										 * be high, there is no
										 * DSR signal present.
										 */

Retry:									if(GetSerialStatus() & 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;
			}

			Update_CR_LF_Translation();

				/* Use new parameters... */

			if(!(Error = SetFlags(FALSE)))
				Error = SetFlags(TRUE);

				/* 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. */

					DoSerialCmd(CMD_RESET);

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

					CopyWriteFlags();

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

					SetFlags(TRUE);
				}
			}

				/* 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,SerialBufferSize);

			if(Translate_CR_LF)
				Length = (* Translate_CR_LF)(HostReadBuffer,Length);

			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(CheckSerialRead())
		{
			MoreData = TRUE;

			if(!WaitSerialRead())
			{
				if(Marking)
					DropMarker();

				BytesIn++;

				if(Translate_CR_LF)
				{
					register LONG Size = (* Translate_CR_LF)(ReadBuffer,1);

					if(Size)
						(* ConTransfer)(ReadBuffer,Size);
				}
				else
					(* ConTransfer)(ReadBuffer,1);

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

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

					if(Length = GetSerialWaiting())
					{
						ULONG Max = SerialBufferSize;

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

						if(Length > Max)
							Length = Max;

						if(!DoSerialRead(ReadBuffer,Length))
						{
							BytesIn += Length;

							if(Translate_CR_LF)
							{
								if(Length = (* Translate_CR_LF)(ReadBuffer,Length))
									(* ConTransfer)(ReadBuffer,Length);
							}
							else
								(* ConTransfer)(ReadBuffer,Length);
						}
					}
				}
			}

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

	return(MoreData);
}
