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

#include "termGlobal.h"

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

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

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

	/* TypeChars(STRPTR String,LONG Len):
	 *
	 *	Send single characters, just as if they had
	 *	been typed on the keyboard.
	 */

STATIC VOID __regargs
TypeChars(register STRPTR String,register LONG Len,struct timeval *KeyRepeatDelay)
{
	if(KeyRepeatDelay)
	{
		while(Len--)
		{
			SerWrite(String++,1);

			if(Len)
			{
				TimeRequest -> tr_node . io_Command	= TR_ADDREQUEST;
				TimeRequest -> tr_time			= *KeyRepeatDelay;

				DoIO(TimeRequest);
			}
		}
	}
	else
		SerWrite(String,Len);
}

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

VOID __regargs
SerialCommand(STRPTR String)
{
	LONG	Count = 0,i,Len = strlen(String);

	BYTE	GotControl	= FALSE,
		GotEscape	= FALSE,
		OldStatus;

	struct Preferences	 Prefs;
	struct timeval		*KeyRepeatDelay;

		/* Get current key repeat delay. */

	GetPrefs(&Prefs,offsetof(struct Preferences,KeyRptDelay));

		/* Any delay specified at all? */

	if(Prefs . KeyRptSpeed . tv_secs || Prefs . KeyRptSpeed . tv_micro)
		KeyRepeatDelay = &Prefs . KeyRptSpeed;
	else
		KeyRepeatDelay = NULL;

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

				GotEscape = FALSE;
			}

				/* The next character represents a command. */

			if(GotControl)
			{
				switch(ToUpper(String[i]))
				{
						/* 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':
#ifdef USE_AREXX
						if(!InRexx)
						{
							if(!WeAreBlocking)
							{
								BlockWindows();

								SendARexxCommand(&String[i + 1]);

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

#endif	/* USE_AREXX */

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

							TypeChars(Password,strlen(Password),KeyRepeatDelay);
						}

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

								Count = 0;
							}

							TypeChars(UserName,strlen(UserName),KeyRepeatDelay);
						}

						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;

						/* 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)
		{
			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 __regargs
SerWrite(APTR Buffer,LONG Size)
{
	if(Size < 0)
		Size = strlen(Buffer);

	if(WriteRequest && Size)
	{
		STATIC UBYTE 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)
					{
							/* 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();

						ClearCursor();

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

						DrawCursor();
					}
				}
				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();

						ClearCursor();

						ConProcess(Buffer,Size);

						DrawCursor();

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

					ClearCursor();

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

					DrawCursor();
				}
			}
			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();

					ClearCursor();

					ConProcess(Buffer,Size);

					DrawCursor();

					WaitIO(WriteRequest);
				}

				BytesOut += Size;
			}
		}
	}
}

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

VOID
RestartSerial()
{
	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. */

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

			ClrSignal(SIG_SERIAL);

			SendIO(ReadRequest);

			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)
		{
			if(!Config -> SerialConfig -> Shared)
			{
				ReadRequest -> IOSer . io_Command = CMD_RESET;

				DoIO(ReadRequest);
			}

			CloseDevice(ReadRequest);

			Closed = TRUE;
		}

		DeleteIORequest(ReadRequest);

		ReadRequest = NULL;
	}

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

				DoIO(ReadRequest);
			}

			CloseDevice(WriteRequest);
		}

		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)
	{
		FreeVec(ReadBuffer);

		ReadBuffer = NULL;
	}

	if(HostReadBuffer)
	{
		FreeVec(HostReadBuffer);

		HostReadBuffer = NULL;
	}

	if(StripBuffer)
	{
		FreeVec(StripBuffer);

		StripBuffer = NULL;
	}

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

		SerialDevice[0] = 0;

		UnitNumber = -1;
	}
}

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

STRPTR
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)
	{
		STRPTR Error;

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

		if(Error = AttemptDevUnit(Config -> SerialConfig -> SerialDevice,Config -> SerialConfig -> UnitNumber,TermIDString,NULL))
		{
				/* 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;

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

			UnitNumber = Config -> SerialConfig -> UnitNumber;
		}
	}

	if(ReadBuffer = AllocVec(Config -> SerialConfig -> SerialBufferSize,MEMF_ANY))
	{
		if(StripBuffer = AllocVec(Config -> SerialConfig -> SerialBufferSize,MEMF_ANY))
		{
			if(XProtocolBase && (TransferBits & XPRS_HOSTNOWAIT))
				HostReadBuffer = AllocVec(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);

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

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

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

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

								ClrSignal(SIG_SERIAL);

								SendIO(ReadRequest);

								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
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)
			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 = AllocVec(Config -> SerialConfig -> SerialBufferSize,MEMF_ANY))
				{
					STRPTR NewStripBuffer;

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

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

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

				BlockWindow(Window);

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

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

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

			ClrSignal(SIG_SERIAL);

			SendIO(ReadRequest);
		}
		else
		{
			STRPTR Error;

				/* Shut down the serial driver. */

			DeleteSerial();

				/* Reinitialize the serial driver. */

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

				if(StripBuffer)
					FreeVec(StripBuffer);

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

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

				/* Trouble? */

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

				BlockWindow(Window);

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

					/* Clean up the mess :-( */

				DeleteSerial();

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

				ClearCursor();

				ConProcess(HostReadBuffer,Length);

				if(Status == STATUS_HOLDING)
				{
					DrawCursor();

					return(FALSE);
				}
			}

			MoreData = TRUE;
		}

			/* Any news? */

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

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

				ClearCursor();

				BytesIn++;

				(* ConTransfer)(ReadBuffer,1);

				if(Status != STATUS_HOLDING)
				{
					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(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);
						}
					}
				}

				DrawCursor();
			}

				/* Ask for another byte. */

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

			ClrSignal(SIG_SERIAL);

			SendIO(ReadRequest);
		}
	}

	return(MoreData);
}
