
/*
 *  UNIT_DGR.C			DATAGRAM PROTOCOL
 */

#include "defs.h"

void DGramBeginIO(Iob *);
void DGramAbortIO(Iob *);
void DGramClose(Iob *);
void DGramData(int, Packet *, long);

void UnitDGramOpen(Iob *, long, long);

/*
 *  Called under Forbid
 */

void
UnitDGramOpen(iob, unitnum, flags)
Iob *iob;
long unitnum;
long flags;
{
    Unit *unit;

    if (unit = FindUnitForPort(iob->io_Port)) {
	if (unit->BeginIO != DGramBeginIO) {
	    iob->io_Error = PPERR_PORT_IN_USE;
	    return;
	}
    } else {
	unit = AllocUnit(iob, DGramBeginIO, DGramAbortIO, DGramData, DGramClose);
    }
    iob->io_Unit = unit;
    ++unit->RefCnt;
}


void
DGramClose(iob)
Iob *iob;
{
    Unit *unit = iob->io_Unit;

    if (--unit->RefCnt == 0) {
	FreeUnit(unit);
    }
    iob->io_Unit = NULL;
}

/*
 *  UnitDGramData() is called whenever a low level network operation
 *		    completes for the given unit.  cmd is:
 *
 *		    'r'     received data packet
 *		    'w'     wrote data packet
 *		    'W'     timeout writing data packet
 *
 *		    The routine is called with the unix locked.
 *
 *  WARNING:	This call may not requeue synchronous packets, a lockout
 *		will result.
 */

void
DGramData(cmd, packet, actual)
int cmd;
Packet *packet;
long actual;	/*  'w', 'W'    */
{
    Unit *unit = packet->io_Unit;
    Iob *iob = packet->iob;	    /*	NULL for 'r'    */
    long n;

    switch(cmd) {
    case 'r':
	sprintf(StickyPort->DebugBuf, " RDDRG %d",actual);

	if (iob = (Iob *)RemHead(&unit->PendIOR)) {
	    iob->io_Flags &= ~IOF_QUEUED;

	    n = actual;
	    if (n < 0)
		n = 0;
	    if (n > iob->io_Length) {
		n = iob->io_Length;
		iob->io_Error = PPERR_WARN_OVFLOW;
	    }
	    actual -= n;
	    movmem((char *)packet->Data1, (char *)iob->io_Data, n);

	    if (iob->io_Length2 && actual > 0) {    /*  a second data buffer    */
		if (actual > iob->io_Length2)
		    actual = iob->io_Length2;
		movmem((char *)packet->Data1 + n, (char *)iob->io_Data2, actual);
		n += actual;
	    }
	    iob->io_Actual = n;

	    if ((iob->io_Flags & IOF_QUICK) == 0)
		ReplyMsg(&iob->io_Message);
	}

	/*
	 *  Note that if no read requests are pending the datagram is
	 *  thrown away.
	 */

	FreeParPacket(packet);
	break;
    case 'W':
	sprintf(StickyPort->DebugBuf, " TMDRG %d",actual);
 
	Remove(iob);
	iob->io_Flags &= ~IOF_QUEUED;
	iob->io_Error = 1;
	iob->io_Actual = actual;
	if ((iob->io_Flags & IOF_QUICK) == 0)
	    ReplyMsg(&iob->io_Message);
	FreeParPacket(packet);
	break;
    case 'w':
	sprintf(StickyPort->DebugBuf, " WRDRG %d",actual);

	Remove(iob);
	iob->io_Flags &= ~IOF_QUEUED;
	iob->io_Actual = actual;
	if ((iob->io_Flags & IOF_QUICK) == 0)
	    ReplyMsg(&iob->io_Message);
	FreeParPacket(packet);
	break;
    }
}

void
DGramBeginIO(iob)
Iob *iob;
{
    Unit *unit = iob->io_Unit;
    Packet *packet;

    iob->io_Error = 0;
    iob->io_Actual = 0;
    iob->io_Message.mn_Node.ln_Type = NT_MESSAGE;

    switch(iob->io_Command) {
    case CMD_READ:
	LockAddr(unit->UnitLock);
	iob->io_Flags &= ~IOF_QUICK;
	iob->io_Flags |= IOF_QUEUED;
	AddTail(&unit->PendIOR, iob);
	UnlockAddr(unit->UnitLock);
	return;
    case CMD_WRITE:
	packet = AllocParPacket(iob, unit, iob->io_Data, iob->io_Length, iob->io_Data2, iob->io_Length2);
	LockAddr(unit->UnitLock);
	AddTail(&unit->PendIOW, iob);
	UnlockAddr(unit->UnitLock);

	QueuePacketForWrite(packet);
	return;
    default:
	CtlBeginIO(iob);
	return;
    }
}

/*
 *  Abort a read or write request.  Currently only read requests may be
 *  aborted.
 */

void
DGramAbortIO(iob)
Iob *iob;
{
    if (iob->io_Command == CMD_READ) {
	LockAddr(iob->io_Unit->UnitLock);
	if ((iob->io_Flags & (IOF_QUEUED|IOF_RUN)) == IOF_QUEUED) {
	    Remove(iob);
	    UnlockAddr(iob->io_Unit->UnitLock);
	    iob->io_Flags &= ~IOF_QUEUED;
	    iob->io_Error = -1;     /*	??? */
	    ReplyMsg(&iob->io_Message);
	} else {
	    UnlockAddr(iob->io_Unit->UnitLock);
	}
    }
}

