
 /* storage for various magic items */

struct Library *OwnDevUnitBase;

static struct IOExtSer SerialRead;
static struct IOExtSer SerialWrite;
static struct MsgPort *SReadPort;
static struct MsgPort *SWritePort;
static ULONG SReadSigMask;
static ULONG SWriteSigMask;

static UBYTE SReadBuf[256];
static ULONG SReadPos;
static ULONG SReadSize;

static UBYTE SWriteBuf[256];
static ULONG SWritePos;

static STRPTR SerDevice;
static ULONG SerUnit;

 /* open up the serial device */

BOOL OpenSerial(STRPTR Device, ULONG Unit, BOOL AttemptMode)
{
	register struct IOExtSer *ior = &SerialRead;
	register struct IOExtSer *iow = &SerialWrite;

	STRPTR ODUresult;

	/* save these away for when we need to close the device */

	SerDevice = Device;
	SerUnit = Unit;

	/* allocate our message ports */
	if (!(SReadPort = CreatePort(NULL, 0)))
		return FALSE;

	if (!(SWritePort = CreatePort(NULL, 0))) {
		DeletePort(SReadPort);
		return FALSE;
	}

	SReadSigMask = 1L << SReadPort->mp_SigBit;
	SWriteSigMask = 1L << SWritePort->mp_SigBit;

	/*
	 * if OwnDevUnit.library is around then use it to try and lock the
	 * port
	 */

	if (OwnDevUnitBase = OpenLibrary(ODU_NAME, 0)) {
		if (AttemptMode)
			ODUresult = AttemptDevUnit(Device, Unit, PROGNAME, 0L);
		else
			ODUresult = LockDevUnit(Device, Unit, PROGNAME, 0L);

		if (ODUresult) {
			CloseLibrary(OwnDevUnitBase);
			DeletePort(SWritePort);
			DeletePort(SReadPort);
			return FALSE;
		}
	}

	/* try and open the device */
	ior->io_SerFlags = SERF_XDISABLED;
	ior->IOSer.io_Message.mn_ReplyPort = SReadPort;

	if (OpenDevice(Device, Unit, (struct IORequest *) ior, 0)) {
		if (OwnDevUnitBase) {
			FreeDevUnit(Device, Unit);
			CloseLibrary(OwnDevUnitBase);
		}

		DeletePort(SWritePort);
		DeletePort(SReadPort);
		return FALSE;
	}

	ior->IOSer.io_Command = SDCMD_QUERY;
	DoIO((struct IORequest *) ior);

	ior->io_Baud = 600;
	ior->io_ReadLen = ior->io_WriteLen = 8;
	ior->io_StopBits = 1;
	ior->IOSer.io_Command = SDCMD_SETPARAMS;
	if (DoIO((struct IORequest *) ior)) {
		CloseSerial();
		return FALSE;
	}

	CopyMem((APTR) ior, (APTR) iow, sizeof(struct IOExtSer));
	iow->IOSer.io_Message.mn_ReplyPort = SWritePort;

	SReadPos = SReadSize = 0;

	return TRUE;
}

 /* close down the serial handling stuff */

void CloseSerial(void)
{
	CloseDevice((struct IORequest *) & SerialRead);

	if (OwnDevUnitBase) {
		FreeDevUnit(SerDevice, SerUnit);
		CloseLibrary(OwnDevUnitBase);
	}

	DeletePort(SWritePort);
	DeletePort(SReadPort);
}

 /* flush out the serial read buffer */

void ClearSerial(void)
{
	SReadPos = SReadSize = 0;

	SerialRead.IOSer.io_Command = CMD_CLEAR;
	DoIO((struct IORequest *) & SerialRead);
}

 /* send a string to the serial device */

void SerWrite(STRPTR Buf, ULONG Len)
{
	SerialWrite.IOSer.io_Command = CMD_WRITE;
	SerialWrite.IOSer.io_Length = Len;
	SerialWrite.IOSer.io_Data = (APTR) Buf;
	DoIO((struct IORequest *) & SerialWrite);
}

 /* get a character from the serial device with a timeout */

UWORD SerGetRawChar(ULONG timeout)
{
	UBYTE Buffer[2];
	ULONG Signals;

	if (SReadPos < SReadSize)
		return (UWORD) SReadBuf[SReadPos++];

	SetSignal(0L, SReadSigMask | TimerSigMask);

	SerialRead.IOSer.io_Command = CMD_READ;
	SerialRead.IOSer.io_Length = 1;
	SerialRead.IOSer.io_Data = (APTR) Buffer;
	SendIO((struct IORequest *) & SerialRead);

	TimerIO.tr_node.io_Command = TR_ADDREQUEST;
	TimerIO.tr_time.tv_secs = timeout;
	TimerIO.tr_time.tv_micro = 0;
	SendIO((struct IORequest *) & TimerIO);

	Signals = Wait(SReadSigMask | TimerSigMask);

	if (Signals & SReadSigMask) {
		if (!CheckIO((struct IORequest *) & TimerIO)) {
			AbortIO((struct IORequest *) & TimerIO);
		}
		WaitIO((struct IORequest *) & TimerIO);

		WaitIO((struct IORequest *) & SerialRead);

		if (!SerialRead.IOSer.io_Error && SerialRead.IOSer.io_Actual) {
			SerialRead.IOSer.io_Command = SDCMD_QUERY;
			DoIO((struct IORequest *) & SerialRead);

			if (SerialRead.IOSer.io_Actual) {
				SerialRead.IOSer.io_Command = CMD_READ;
				SerialRead.IOSer.io_Length = min(sizeof(SReadBuf), SerialRead.IOSer.io_Actual);
				SerialRead.IOSer.io_Data = (APTR) SReadBuf;
				DoIO((struct IORequest *) & SerialRead);

				if (!SerialRead.IOSer.io_Error) {
					SReadPos = 0;
					SReadSize = SerialRead.IOSer.io_Actual;
				}
			}

			return (UWORD) Buffer[0];
		}
		else
			return 0;
	}

	if (Signals & TimerSigMask)
		WaitIO((struct IORequest *) & TimerIO);

	/* if we get down here we have timed out waiting for a character */

	if (!CheckIO((struct IORequest *) & SerialRead)) {
		AbortIO((struct IORequest *) & SerialRead);
	}
	WaitIO((struct IORequest *) & SerialRead);

	return TIMEOUT;
}
