/*
** $Header: SRC:CVSROOT/Vector/ParNet/parnet.c,v 1.1.1.1 1994/06/23 02:39:39 Barnard Exp $
*/

/*
** This code was originally written by Matthew Dillon and put into Public Domain
**
** All changes concerning the adaption of Matt's original code to the
** Vector Connection I/O board are © 1991-1994 by Henning Schmiedehausen
** All rights for this changes are reserved. The original code is Public Domain
**
** This code is distributed with the expressed written permission of Matthew
** Dillon (Thank you very much, Matt)
**
*/

/*
 *  PARNET.C
 *
 */

#include "defs.h"
#include "parnet.device_rev.h"
#include "parnet_asm.h"

#include "parnet_protos.h"

#include <proto/exec.h>

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

#include <devices/parallel.h>
#include <vector/vectorpar.h>

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


#define PRODUCTION

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

extern void PioInt();

extern Task *VTask;		/*  wakeup.asm	*/
extern long VMask;		/*  wakeup.asm	*/

extern void ParNetTask();       /*  task.c      */

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  PIOInt =
{
	{
	NULL, NULL,
	NT_INTERRUPT,
	20,						/* *WICHTIG* NICHT ÄNDERN! SO SITZT DER INTERRUPT *VOR* DEM DEVICE */
	"parnet.device"
	},
NULL,
PioInt
};

struct MsgPort		*ParPort;
struct IOExtPar		*ParRequest;
ULONG				 UnitBase;
ULONG				 PIOBase;

char				*version = VERSTAG;



/*
 *  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 = VERSION;
    db->Lib.lib_Revision = REVISION;
    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;
{
#if PIO_CHANNEL == 0
	int piounit = 0;
#endif
#if PIO_CHANNEL == 1
	int piounit = 1;
#endif

    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(!(ParPort = (struct MsgPort *)CreatePort("PIONet-Port",NULL)))
	{
		goto badop;
	}
	if(!(ParRequest = (struct IOExtPar *)CreateExtIO(ParPort, sizeof(struct IOExtPar))))
	{
		goto badop;
	}

	if(OpenDevice(PARALLELDEVICE, piounit, ParRequest, NULL))
	{
		goto badop;
	}

#ifndef DEBUG
	ParRequest->IOPar.io_Command = PIOCMD_INACTIVE;			/* Unit abklemmen */
	DoIO(ParRequest);
	UnitBase = (ULONG)ParRequest->IOPar.io_Actual;
	PIOBase  = (ULONG)ParRequest->IOPar.io_Length;

#else

	UnitBase = 0xe94001;
	PIOBase  = 0xe94001;

#endif


	VTask = (struct Task *)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    */

	ClearPIOInt();			/* ev. pending PIO Interrupts löschen */

	AddIntServer(PIO_INTERRUPT, &PIOInt);

	SetUpPIO();

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

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

	{
	ClearPIOInt();

	RemIntServer(PIO_INTERRUPT, &PIOInt);

	}
	if (ActiveTask)
	    RemTask(VTask);

#ifndef DEBUG
	ParRequest->IOPar.io_Command = PIOCMD_ACTIVE;		/* Port wieder fürs Device */
	DoIO(ParRequest);
#endif

	CloseDevice(ParRequest);
	DeleteExtIO(ParRequest);
	DeletePort(ParPort);

	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 = (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 = (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()
{
    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()
{
    char *portName = PORTNAME;
    PubPort *port;

    if ((port = StickyPort) == NULL && (port = (PubPort *)FindPort(portName)) == NULL) {
	port = (PubPort *)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.
 */

DetermineTimeouts()
{
    IOT iot;
    long cnt = 0;
    PORT *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);
}


