/*
**	termSerial.c
**
**	Serial driver support routines
**
**	Copyright © 1990-1992 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;

		default:		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(UBYTE *Name,ULONG Code):
	 *
	 *	Call a menu function either through the name of the corresponding
	 *	menu item or a menu number.
	 */

STATIC VOID __regargs
CallMenu(UBYTE *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)
						HandleCode((ULONG)TermMenu[i] . nm_UserData,NULL,MenuItem);

					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)
						HandleCode((ULONG)TermMenu[i] . nm_UserData,NULL,MenuItem);
				}

				break;
			}

			switch(TermMenu[i] . nm_Type)
			{
				case NM_ITEM:	Item++;
						break;

				case NM_SUB:	Sub++;
						break;
			}
		}
	}
}

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

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

	BYTE	GotControl	= FALSE,
		GotEscape	= FALSE,

		OldStatus,
		BlockSet;

		/* 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(!WeAreBlocking)
							{
								BlockSet = TRUE;

								BlockWindows();
							}
							else
								BlockSet = FALSE;

							SendAmigaDOSCommand(&String[i + 1]);

							if(BlockSet)
								ReleaseWindows();

							return;

						/* Execute an ARexx command. */

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

								BlockWindows();
							}
							else
								BlockSet = FALSE;

							SendARexxCommand(&String[i + 1]);

							if(BlockSet)
								ReleaseWindows();

							return;

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

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

						/* This is a backspace. */

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

						/* This is a form feed. */

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

						/* This is a line feed. */

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

						/* Send the current password. */

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

									Count = 0;
								}

								SerWrite(Password,strlen(Password));
							}

							break;

						/* This is a carriage return. */

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

						/* This is a tab. */

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

						/* Send the current user name. */

					case 'U':	if(UserName[0])
							{
								if(Count)
								{
									SerWrite(SharedBuffer,Count);

									Count = 0;
								}

								SerWrite(UserName,strlen(UserName));
							}

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

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

					ConProcess(Buffer,Size);

					WaitIO(WriteRequest);
				}

				BytesOut += Size;
			}
		}
	}
}

	/* EmulationSerWrite(STRPTR Buffer):
	 *
	 *	Send text to the serial port, called by the emulation handler
	 *	process.
	 */

VOID __regargs
EmulationSerWrite(STRPTR Buffer)
{
	LONG Size = strlen(Buffer);

	if(WriteRequest && EmulationRequest)
	{
			/* xpr wants to see the data before it is
			 * transferred.
			 */

		if(XProtocolBase && (TransferBits & XPRS_USERMON))
		{
			if(Size = XProtocolUserMon(XprIO,Buffer,Size,Size))
			{
				EmulationRequest -> IOSer . io_Command	= CMD_WRITE;
				EmulationRequest -> IOSer . io_Data	= Buffer;
				EmulationRequest -> IOSer . io_Length	= Size;

				DoIO(EmulationRequest);

				BytesOut += Size;
			}
		}
		else
		{
			EmulationRequest -> IOSer . io_Command	= CMD_WRITE;
			EmulationRequest -> IOSer . io_Data	= Buffer;
			EmulationRequest -> IOSer . io_Length	= Size;

			DoIO(EmulationRequest);

			BytesOut += Size;
		}
	}
}

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

			SetSignal(0,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;

	EmulationCleanup();

	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 -> 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)
{
	switch(Error)
	{
		case IOERR_BADLENGTH:
		case IOERR_BADADDRESS:
		case IOERR_SELFTEST:
		case IOERR_OPENFAIL:		if(Reset)
							*Reset = FALSE;

						return(LocaleString(MSG_SERIAL_OPENFAILURE_TXT));

		case SerErr_DevBusy:		if(Reset)
							*Reset = FALSE;

						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 = FALSE;

						return(LocaleString(MSG_SERIAL_ERROR_BUFERR_TXT));

		case SerErr_InvParam:		if(Reset)
							*Reset = TRUE;

						return(LocaleString(MSG_SERIAL_ERROR_INVPARAM_TXT));

		case SerErr_LineErr:		if(Reset)
							*Reset = FALSE;

						return(LocaleString(MSG_SERIAL_ERROR_LINEERR_TXT));

		case SerErr_ParityErr:		if(Reset)
							*Reset = TRUE;

						return(LocaleString(MSG_SERIAL_ERROR_PARITYERR_TXT));

		case SerErr_TimerErr:		if(Reset)
							*Reset = FALSE;

						return(LocaleString(MSG_SERIAL_ERROR_TIMERERR_TXT));

		case SerErr_BufOverflow:	if(Reset)
							*Reset = FALSE;

						return(LocaleString(MSG_SERIAL_ERROR_BUFOVERFLOW_TXT));

		case SerErr_NoDSR:		if(Reset)
							*Reset = FALSE;

						return(LocaleString(MSG_SERIAL_ERROR_NODSR_TXT));

	/*	case SerErr_UnitBusy:	*/
		case IOERR_UNITBUSY:
		case 16:			if(Reset)
							*Reset = FALSE;

						return(LocaleString(MSG_SERIAL_ERROR_UNIT_BUSY_TXT));

		default:			return(NULL);
	}
}

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

UBYTE *
CreateSerial()
{
	struct MsgPort *WritePort;

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

Start:	if(OwnDevUnitBase)
	{
		UBYTE *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 & (1 << 3))
									{
										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;

								SetSignal(0,SIG_SERIAL);

								SendIO(ReadRequest);

								EmulationSetup();

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