#include <exec/types.h>
#include <exec/io.h>
#include <exec/errors.h>
#include <exec/devices.h>
#include <proto/exec.h>

#include "fakesr.h"
#include "fakesrprivate.h"

#define NewList(list) {(list)->lh_Head=(struct Node *)&(list)->lh_Tail;(list)->lh_TailPred=(struct Node *)&(list)->lh_Head;(list)->lh_Tail=NULL;}

ULONG __asm _FakeSer_BeginIO(register __a1 void *IORequest,register __a6 struct FakeSerDevice *FakeSerBase)
{
	struct FSRUnit *Unit;
	Unit=(struct FSRUnit *)((struct IORequest *)IORequest)->io_Unit;
	if (!Unit || !Unit->fsru_BeginIO) return 0;
	return Unit->fsru_BeginIO(IORequest);
}

ULONG __asm _FakeSer_AbortIO(register __a1 void *IORequest,register __a6 struct FakeSerDevice *FakeSerBase)
{
	struct FSRUnit *Unit;
	Unit=(struct FSRUnit *)((struct IORequest *)IORequest)->io_Unit;
	if (!Unit || !Unit->fsru_AbortIO) return 0;
	return Unit->fsru_AbortIO(IORequest);
}

int AddUnit(struct FakeSerDevice *FakeSerBase,struct FSRUnit *Unit)
/* returns zero for no error */
/* returns 1 for bad address/other mem error */
/* returns to for non-uniqe unit */
{
	struct FSRUnit *CurUnit;
	void *SysBase;

  SysBase=*((void **)0x00000004);

	if (!FakeSerBase || !Unit) return 1;
	for (CurUnit=(struct FSRUnit *)FakeSerBase->fsd_UnitList.lh_Head;
		CurUnit->fsru_Node.ln_Succ && CurUnit->fsru_Num != Unit->fsru_Num;
		CurUnit=(struct FSRUnit *)CurUnit->fsru_Node.ln_Succ);
	
	if (CurUnit->fsru_Node.ln_Succ) {
		/* we have a duplicated unit # */
		return 2;
		
	}
	else AddTail(&FakeSerBase->fsd_UnitList,Unit);
	return 0;
}

ULONG CtlUnit_BeginIO(void *IORequest)
{
	struct IOStdReq *IOReq;
	struct FakeSerDevice *FakeSerBase;
	struct FSRUnit *Unit;
	int Err;
	
	void *SysBase;

  SysBase=*((void **)0x00000004);
	
	if (!IORequest) return 0;
	
	IOReq=IORequest;
	FakeSerBase=(struct FakeSerDevice *)IOReq->io_Device;
	if (!FakeSerBase) return 0;
	
	if (IOReq->io_Flags & IOF_QUICK) {
		IOReq->io_Flags &= ~(IOF_QUICK);
		/* we don't explicitly support quick io */
	}
	IOReq->io_Error=0;
	switch (IOReq->io_Command) {
		case FSRCMD_ADDUNIT:
			Unit=(struct FSRUnit *)IOReq->io_Data;
			if (Unit->fsru_Num == FSR_UNITNONE) {
				Forbid();
				for (Unit->fsru_Num=MAXRESERVEDUNIT+1;(Err=AddUnit(FakeSerBase,Unit))==2;Unit->fsru_Num++)
					;
				Permit();
			} else {
				Forbid();
				Err=AddUnit(FakeSerBase,Unit);
				Permit();
			}
			if (Err) {
				if (Err==1) IOReq->io_Error=IOERR_BADADDRESS;
				else IOReq->io_Error=FSRCTLERR_NONUNIQUE;
			}
			ReplyMsg(&IOReq->io_Message);
		break;
		
		case FSRCMD_REMUNIT:
			Unit=(struct FSRUnit *)IOReq->io_Data;
			Forbid();
			Remove(Unit);
			Permit();
			ReplyMsg(&IOReq->io_Message);
		break;
		
		default:
		  IOReq->io_Error=IOERR_NOCMD;
			ReplyMsg(&IOReq->io_Message);
			
	}
}

ULONG CtlUnit_AbortIO(void *IORequest)
{
	return 0; /* We do stuff quickly anyway */	
}



void __saveds __asm _UserLibInit(register __a6 struct FakeSerDevice *FakeSerBase )
{
	void *SysBase;

  SysBase=*((void **)0x00000004);

	if (!FakeSerBase) return;
	NewList(&FakeSerBase->fsd_UnitList);
	FakeSerBase->fsd_CtlUnit.fsru_Num=FSR_CTLUNIT;
	FakeSerBase->fsd_CtlUnit.fsru_BeginIO=CtlUnit_BeginIO;
	FakeSerBase->fsd_CtlUnit.fsru_AbortIO=CtlUnit_AbortIO;
	FakeSerBase->fsd_CtlUnit.fsru_Open=NULL;
	FakeSerBase->fsd_CtlUnit.fsru_Close=NULL;
	Forbid(); /* Lock the list */
	AddUnit(FakeSerBase,&FakeSerBase->fsd_CtlUnit);
	Permit();
}

void __saveds __asm _UserLibCleanup(register __a6 struct FakeSerDevice *FakeSerBase)
{
	return; /* No cleanup work required */
}

void __saveds _UserLibOpen(struct FakeSerDevice *FakeSerBase, void *IORequest, LONG UnitNum,LONG Flags)
{
	struct FSRUnit *Unit;
	struct IORequest *IOReq;
	void *SysBase;

  SysBase=*((void **)0x00000004);

	IOReq=IORequest;
	if (!IOReq) return;
	IOReq->io_Message.mn_Node.ln_Type=NT_REPLYMSG;
	
	Forbid(); /* We Forbid()-protect the unit list */
	for (Unit=(struct FSRUnit *)FakeSerBase->fsd_UnitList.lh_Head;Unit->fsru_Node.ln_Succ;Unit=(struct FSRUnit *)Unit->fsru_Node.ln_Succ)
		if (Unit->fsru_Num==UnitNum) {
			IOReq->io_Unit=(struct Unit *)Unit;
			if (Unit->fsru_Open) Unit->fsru_Open(IORequest);
			Permit();
			return;
		}
	Permit();
	
	IOReq->io_Error=IOERR_OPENFAIL;
	IOReq->io_Device=NULL;
	
}

void __saveds _UserLibClose(struct FakeSerDevice *FakeSerBase,void *IORequest)
{
	struct FSRUnit *Unit;
	struct IORequest *IOReq;
	void *SysBase;

  SysBase=*((void **)0x00000004);
	
	if (!FakeSerBase || !IORequest) return;
	IOReq=IORequest;
	
	Unit=(struct FSRUnit *)IOReq->io_Unit;
	if (!Unit) return;
	
	if (Unit->fsru_Close) Unit->fsru_Close(IORequest);
	return;
}




