/*
**	$Id: IORawSerial.cpp 1.5 1995/12/03 12:16:23 olsen Exp olsen $
**
**	:ts=4
*/

/*
 * Copyright © 1995 by Olaf Barthel, All Rights Reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
 * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software has not been validated by the ``Bundesamt fuer Zulassungen in
 * der Telekommunikation'' of the ``Deutsche Bundepost Telekom'' and thus
 * must not be used for accessing the BTX-Network of the Telekom in Germany.
 *
 * Diese Software hat keine Zulassung durch das Bundesamt fuer Zulassungen in
 * der Telekommunikation der Deutschen Bundespost Telekom und darf daher nicht
 * am Netz der Deutschen Bundespost Telekom in Deutschland betrieben werden.
 */

/****************************************************************************/

#include <exec/memory.h>
#include <exec/errors.h>

#include <devices/serial.h>
#include <devices/timer.h>

#include <hardware/cia.h>

#include <clib/exec_protos.h>

#ifdef __SASC
#include <pragmas/exec_pragmas.h>

extern struct ExecBase *SysBase;
#endif	// _SASC

#include <string.h>
#include <stdio.h>

/****************************************************************************/

#ifndef _IORAWSERIAL_HPP
#include "IORawSerial.hpp"
#endif

/****************************************************************************/

VOID IORawSerial::StopRead(VOID)
{
	if(Valid && Reading)
	{
		if(!CheckIO((struct IORequest *)ReadRequest))
			AbortIO((struct IORequest *)ReadRequest);

		WaitIO((struct IORequest *)ReadRequest);

		Reading = FALSE;
	}

	SetSignal(0,ReadMask);
}

VOID IORawSerial::StartRead(VOID)
{
	if(Valid)
	{
		StopRead();

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

		SendIO((struct IORequest *)ReadRequest);

		Reading = TRUE;
	}
}

BYTE IORawSerial::WaitRead(VOID)
{
	BYTE Error;

	if(Valid)
	{
		if(Reading)
		{
			Error = WaitIO((struct IORequest *)ReadRequest);

			Reading = FALSE;
		}
		else
			Error = 0;
	}
	else
		Error = IOERR_OPENFAIL;

	return(Error);
}

VOID IORawSerial::Close(VOID)
{
	if(Valid)
	{
		StopRead();
		StopTime();

		CloseDevice((struct IORequest *)TimeRequest);

		DeleteIORequest((struct IORequest *)TimeRequest);

		DeleteMsgPort(TimePort);

		CloseDevice((struct IORequest *)ReadRequest);

		DeleteIORequest((struct IORequest *)ReadRequest);

		FreeVec(WriteRequest);

		DeleteMsgPort(WritePort);

		DeleteMsgPort(ReadPort);

		Valid = FALSE;
	}
}

LONG IORawSerial::Open(CONST STRPTR Channel,ULONG Unit,ULONG Baud,BOOL RTS_CTS)
{
	if(ReadPort = CreateMsgPort())
	{
		ReadMask = 1UL << ReadPort->mp_SigBit;

		if(WritePort = CreateMsgPort())
		{
			if(ReadRequest = (struct IOExtSer *)CreateIORequest(ReadPort,sizeof(struct IOExtSer)))
			{
				if(WriteRequest = (struct IOExtSer *)AllocVec(sizeof(struct IOExtSer),MEMF_ANY | MEMF_PUBLIC))
				{
					if(!OpenDevice((UBYTE *)Channel,Unit,(struct IORequest *)ReadRequest,NULL))
					{
						ReadRequest->IOSer.io_Command	= SDCMD_SETPARAMS;
						ReadRequest->io_ExtFlags		= 0;
						ReadRequest->io_ReadLen			= 8;
						ReadRequest->io_WriteLen		= 8;
						ReadRequest->io_StopBits		= 1;

						if(Baud)
							ReadRequest->io_Baud = Baud;

						ReadRequest->io_SerFlags = (UBYTE)((ReadRequest->io_SerFlags & SERF_7WIRE) | SERF_XDISABLED);

						if(RTS_CTS >= FALSE)
						{
							if(RTS_CTS)
								ReadRequest->io_SerFlags |=  SERF_7WIRE;
							else
								ReadRequest->io_SerFlags &= ~SERF_7WIRE;
						}

						if(!DoIO((struct IORequest *)ReadRequest))
						{
							CopyMem(ReadRequest,WriteRequest,sizeof(struct IOExtSer));

							WriteRequest->IOSer.io_Message.mn_ReplyPort = WritePort;

							if(TimePort = CreateMsgPort())
							{
								TimeMask = 1UL << TimePort->mp_SigBit;

								if(TimeRequest = (struct timerequest *)CreateIORequest(TimePort,sizeof(struct timerequest)))
								{
									if(!OpenDevice((UBYTE *)TIMERNAME,UNIT_VBLANK,(struct IORequest *)TimeRequest,NULL))
									{
										Valid = TRUE;

										StartRead();

										return(0);
									}

									DeleteIORequest((struct IORequest *)TimeRequest);
									TimeRequest = NULL;
								}

								DeleteMsgPort(TimePort);
								TimePort = NULL;
							}
						}

						CloseDevice((struct IORequest *)ReadRequest);
					}

					FreeVec(WriteRequest);
					WriteRequest = NULL;
				}

				DeleteIORequest((struct IORequest *)ReadRequest);
				ReadRequest = NULL;
			}

			DeleteMsgPort(WritePort);
			WritePort = NULL;
		}

		DeleteMsgPort(ReadPort);
		ReadPort = NULL;
	}

	return(-1);
}

LONG IORawSerial::GetChar(LONG Timeout)
{
	LONG Result;

	if(Valid)
	{
		if(Reading)
		{
			if(CheckIO((struct IORequest *)ReadRequest))
			{
				if(WaitRead())
					Result = CHANNELERR_Nothing;
				else
					Result = (LONG)SingleChar;

				StartRead();
			}
			else
			{
				if(Timeout)
				{
					ULONG Signals;

					StartTime(Timeout);

					for(;;)
					{
						Signals = Wait(ReadMask | TimeMask);

						if(Signals & ReadMask)
						{
							if(WaitRead())
								Result = CHANNELERR_Nothing;
							else
								Result = (LONG)SingleChar;

							StopTime();

							StartRead();

							break;
						}

						if(Signals & TimeMask)
						{
							WaitTime();

							Result = CHANNELERR_Timeout;

							break;
						}
					}
				}
				else
					Result = CHANNELERR_Nothing;
			}
		}
		else
			Result = CHANNELERR_Nothing;
	}
	else
		Result = CHANNELERR_EOF;

	return(Result);
}

VOID IORawSerial::PutString(CONST STRPTR String,LONG Len)
{
	if(Valid)
	{
		if(Len < 0)
			Len = strlen(String);

		WriteRequest->IOSer.io_Command	= CMD_WRITE;
		WriteRequest->IOSer.io_Data 	= (APTR)String;
		WriteRequest->IOSer.io_Length	= Len;

		DoIO((struct IORequest *)WriteRequest);
	}
}

LONG IORawSerial::Waiting(VOID)
{
	if(Valid)
	{
		if(Reading)
		{
			if(CheckIO((struct IORequest *)ReadRequest))
				return(1);
		}

		WriteRequest->IOSer.io_Command = SDCMD_QUERY;

		DoIO((struct IORequest *)WriteRequest);

		return((LONG)WriteRequest->IOSer.io_Actual);
	}
	else
		return(0);
}

ULONG IORawSerial::WaitMask(VOID)
{
	return(ReadMask);
}

IORawSerial::IORawSerial()
{
	Reading	= FALSE;
	Ticking	= FALSE;
	Valid	= FALSE;
}

IORawSerial::~IORawSerial()
{
	Close();
}

VOID IORawSerial::StartTime(ULONG Seconds,ULONG Micros)
{
	StopTime();

	TimeRequest->tr_node.io_Command = TR_ADDREQUEST;
	TimeRequest->tr_time.tv_secs	= Seconds;
	TimeRequest->tr_time.tv_micro	= Micros;

	SendIO((struct IORequest *)TimeRequest);

	Ticking = TRUE;
}

VOID IORawSerial::StopTime(VOID)
{
	if(Ticking)
	{
		if(!CheckIO((struct IORequest *)TimeRequest))
			AbortIO((struct IORequest *)TimeRequest);

		WaitTime();
	}

	SetSignal(0,TimeMask);
}

VOID IORawSerial::WaitTime(VOID)
{
	if(Ticking)
	{
		WaitIO((struct IORequest *)TimeRequest);

		Ticking = FALSE;
	}
}

VOID IORawSerial::Flush(VOID)
{
	if(Valid)
	{
		BOOL IsReading = Reading;

		StopRead();

		WriteRequest->IOSer.io_Command = CMD_CLEAR;
		DoIO((struct IORequest *)WriteRequest);

		if(IsReading)
			StartRead();
	}
}
