
/*
 *  PARNET.C
 *
 */

#include "defs.h"
#include <resources/misc.h>
#include <hardware/cia.h>
#include <exec/memory.h>

extern func_ptr DeviceVectors[];
extern func_ptr UnitVectors[];
extern char DeviceName[];
extern char IdString[];

extern void VInt(void);				/*  wakeup.asm	*/
extern Task *VTask;				/*  wakeup.asm	*/
extern long VMask;				/*  wakeup.asm	*/
extern int AutoAllocMiscResource(int, int);	/*  wakeup.asm	*/
extern int AutoFreeMiscResource(int);		/*  wakeup.asm	*/
extern int VAddICRVector(int, int, struct Interrupt *);	/*  wakeup.asm	*/
extern int VRemICRVector(int, int, struct Interrupt *);	/*  wakeup.asm	*/

long	SysBase    = NULL;	/*  sysbase			*/
Device	*DevBase   = NULL;	/*  device base 		*/
BPTR	DevSegment = NULL;	/*  seglist for device		*/
long	PLock[2]   = { 0, 0 };	/*  arbitrate network access	*/
PubPort *StickyPort= NULL;

char	ActiveMRPP = 0;
char	ActiveMRPB = 0;
char	ActiveTask = 0;
char	ActiveInt  = 0;

struct Interrupt VInterrupt = {
    {
	NULL, NULL,
	NT_INTERRUPT,
	-126,
	"parnet.device"
    },
    NULL,
    VInt
};

/* Prototyping */

APTR CDevInit(BPTR);
Device *CDevOpen(long,long,Iob *);
long CDevExpunge(void);
BPTR CDevClose(Iob *);
void ReadConfig(void);
void WriteConfig(void);
int DetermineTimeouts(void);

/*
 *  Init is called when the device is loaded to create it.
 */

APTR
CDevInit(seg)
BPTR seg;
{
    Device *db;

    SysBase = *(long *)4;

    DevBase = db = (Device *)MakeLibrary((long **)DeviceVectors,NULL,NULL,sizeof(Device),NULL);
    db->Lib.lib_Node.ln_Type = NT_DEVICE;
    db->Lib.lib_Node.ln_Name = DeviceName;
    db->Lib.lib_Flags = LIBF_CHANGED|LIBF_SUMUSED;
    db->Lib.lib_Version = 1;
    db->Lib.lib_IdString= (APTR)IdString;
    db->ParAddress = 254;		    /*	default address     */
    db->Port.mp_Node.ln_Type = NT_MSGPORT;
    db->Port.mp_SigBit = SIGBREAKB_CTRL_E;  /*	for VTask	    */
    db->Port.mp_Flags  = PA_SIGNAL;
    NewList(&db->UnitList);
    NewList(&db->Port.mp_MsgList);

    ReadConfig();
    WriteConfig();

    DevSegment = seg;
    AddDevice((struct Device *)db);
    return((APTR)db);
}

Device *
CDevOpen(unitnum, flags, iob)
long unitnum;
long flags;
Iob *iob;
{
    Device *nd = DevBase;

    iob->io_Unit = NULL;

    LockAddr(PLock);        /*  because we make calls that kill Forbid()    */

    nd->Lib.lib_Flags &= ~LIBF_DELEXP;

    if (++nd->Lib.lib_OpenCnt == 1) {
	/*
	 *  Initial Open, allocate resources
	 */

	if (AutoAllocMiscResource(MR_PARALLELPORT, -1)) {
	    iob->io_Actual = PPERR_NOPORTRES;
	    goto badop;
	}
	ActiveMRPP = 1;
	if (AutoAllocMiscResource(MR_PARALLELBITS, -1)) {
	    iob->io_Actual = PPERR_NOBITSRES;
	    goto badop;
	}
	ActiveMRPB = 1;
	VTask = CreateTask("parnet.device", 0, (APTR)ParNetTask, 4096);
	if (VTask == NULL) {
	    iob->io_Actual = PPERR_NOTASK;
	    goto badop;
	}
	ActiveTask = 1;
	VMask = SIGBREAKF_CTRL_F;
	nd->Port.mp_SigTask = VTask;	/*  for asynchronous I/O    */

	if (VAddICRVector(0, CIAICRB_FLG, &VInterrupt)) {
	    iob->io_Actual = PPERR_NOINT;
	    goto badop;
	}
	ActiveInt = 1;

	if (DetermineTimeouts() < 0)    /*  couldn't use timer.device   */
	    goto badop;

    }
    iob->io_Actual= 0;
    iob->io_Error = 0;

    /*
     *	Attempt to open the specified unit by protocol
     */

    {
	short proto = iob->io_Flags & PRO_MASK;

	if (proto <= PRO_LAST) {
	    (*UnitVectors[proto])(iob, unitnum, flags);
	    if (iob->io_Error == 0) {
		UnlockAddr(PLock);
		return(nd);
	    }
	}
    }

    /*
     *	OPEN FAILED
     */

badop:
    CDevClose(iob);
    iob->io_Actual = iob->io_Error;
    iob->io_Error  = IOERR_OPENFAIL;
    UnlockAddr(PLock);
    return(NULL);
}

long
CDevExpunge(void)
{
    Device *nd = DevBase;
    BPTR ds = DevSegment;

    if (ds == NULL)
	Alert(24);

    if (nd->Lib.lib_OpenCnt) {              /*  delayed expunge */
	nd->Lib.lib_Flags |= LIBF_DELEXP;
	return(NULL);
    }
    Remove((NODE *)nd);
    FreeMem((char *)nd - nd->Lib.lib_NegSize, nd->Lib.lib_NegSize + nd->Lib.lib_PosSize);
    DevSegment = NULL;
    return(ds);
}

BPTR
CDevClose(iob)
Iob *iob;
{
    Device *nd = DevBase;

    if (iob->io_Unit)
	(*iob->io_Unit->CloseFunc)(iob);

    iob->io_Unit   = NULL;			/*  clear vectors	*/
    iob->io_Device = NULL;

    if (nd->Lib.lib_OpenCnt == 1)   {           /*  last close          */
	if (ActiveInt)
	    VRemICRVector(0, CIAICRB_FLG, &VInterrupt);
	if (ActiveTask)
	    RemTask(VTask);
	if (ActiveMRPB)
	    AutoFreeMiscResource(MR_PARALLELBITS);
	if (ActiveMRPP)
	    AutoFreeMiscResource(MR_PARALLELPORT);
	ActiveInt = 0;
	ActiveTask= 0;
	ActiveMRPB= 0;
	ActiveMRPP= 0;
    }
    if (nd->Lib.lib_OpenCnt && --nd->Lib.lib_OpenCnt)
	return(NULL);                           /*  still has refs      */

    /*
     *	Note: LIBF_DELEXP never set if DevClose() called from DevOpen()
     */

    if (nd->Lib.lib_Flags & LIBF_DELEXP)
	return(CDevExpunge());                  /*  expunge             */
    return(NULL);                               /*  no expunge          */
}

/*
 *  UNITS
 */

Unit *UnitCache = NULL;

Unit *
FindUnitForPort(port)
uword port;
{
    Unit *unit;
    Device *db;

    if (UnitCache && port == UnitCache->Port)
	return(UnitCache);

    db = DevBase;
    for (unit = (Unit *)db->UnitList.lh_Head; unit != (Unit *)&db->UnitList.lh_Tail; unit = (Unit *)unit->Node.ln_Succ) {
	if (port == unit->Port) {
	    UnitCache = unit;
	    return(unit);
	}
    }
    return(NULL);
}

Unit *
AllocUnit(iob, beginFunc, abortFunc, dataFunc, closeFunc)
Iob *iob;
func_void beginFunc;
func_void abortFunc;
func_void dataFunc;
func_void closeFunc;
{
    Unit *unit = AllocMem(sizeof(Unit), MEMF_CLEAR | MEMF_PUBLIC);
    Device *db = DevBase;

    unit->BeginIO = beginFunc;
    unit->AbortIO = abortFunc;
    unit->CloseFunc = closeFunc;
    unit->DataFunc = dataFunc;

    unit->Port = iob->io_Port;
    unit->Protocol = iob->io_Flags & PRO_MASK;
    unit->DestAddr = iob->io_Addr;

    NewList(&unit->PendIOR);
    NewList(&unit->PendIOW);
    NewList(&unit->PendCon);
    NewList(&unit->PendLsn);

    AddHead(&db->UnitList, (struct Node *)unit);

    return(unit);
}

void
FreeUnit(unit)
Unit *unit;
{
    Remove(&unit->Node);
    FreeMem(unit, sizeof(Unit));
    UnitCache = NULL;
}

/*
 *  Packets
 */

Packet *
AllocParPacket(iob, unit, data1, bytes1, data2, bytes2)
Iob *iob;
Unit *unit;
ubyte *data1;
ubyte *data2;
long bytes1;
long bytes2;
{
    Packet *packet = AllocMem(sizeof(Packet), MEMF_CLEAR|MEMF_PUBLIC);

    packet->iob = iob;
    packet->io_Unit = unit;
    if (iob)
	packet->DestAddr = iob->io_Addr;
    packet->DestPort = unit->Port;
    packet->Data1 = data1;
    packet->Data2 = data2;
    packet->DLen1 = bytes1;
    packet->DLen2 = bytes2;

    return(packet);
}

void
FreeParPacket(packet)
Packet *packet;
{
    FreeMem(packet, sizeof(Packet));
}

/*
 *			    CONFIG
 */

void
ReadConfig(void)
{
    PubPort *port;

    if ((port = StickyPort) == NULL)
	port = (PubPort *)FindPort(PORTNAME);

    if (port) {
	DevBase->ParAddress = port->Address;
	ParAddress(DevBase->ParAddress);
	/*ParLLTimeout = port->Timeout;*/
    }
    StickyPort = port;
}

void
WriteConfig(void)
{
    char *portName = PORTNAME;
    PubPort *port;

    if ((port = StickyPort) == NULL && (port = (PubPort *)FindPort(portName)) == NULL) {
	port = AllocMem(sizeof(PubPort) + strlen(portName) + 1, MEMF_PUBLIC | MEMF_CLEAR);
	port->Port.mp_Node.ln_Name = (char *)(port + 1);

	strcpy(port->Port.mp_Node.ln_Name, portName);
	port->Port.mp_Node.ln_Type = NT_MSGPORT;
	port->Port.mp_Node.ln_Pri  = 0;
	NewList(&port->Port.mp_MsgList);
	AddPort((PORT *)port);
    }
    port->Address = DevBase->ParAddress;
    port->Timeout = ParLLTimeout;
    StickyPort = port;
}

/*
 *  Determines the processor speed for soft wired timeouts in PAR.ASM
 *  by timing 1 second.
 */
int
DetermineTimeouts(void)
{
    IOT iot;
    long cnt = 0;
    PORT *port = CreatePort(NULL, 0);

    if (OpenDevice(TIMERNAME, UNIT_VBLANK, &iot, 0))
	return(-1);
    iot.tr_node.io_Command = TR_ADDREQUEST;
    iot.tr_node.io_Message.mn_Node.ln_Type = NT_MESSAGE;
    iot.tr_node.io_Message.mn_ReplyPort = port;
    iot.tr_time.tv_secs = 1;
    iot.tr_time.tv_micro= 0;
    SendIO(&iot);
    while (CheckIO(&iot) == NULL) {
	Time10000();
	cnt += 10000;
    }
    WaitIO(&iot);
    ParLLTimeout = cnt;
    CloseDevice(&iot);
    DeletePort(port);
    return(0);
}


