
/*
 *  UNIT_STR.C			STREAM PROTOCOL (not impl yet)
 *
 *				currently just datagram proto.
 */

#include "defs.h"

void StreamBeginIO(Iob *);
void StreamAbortIO(Iob *);
void StreamClose(Iob *);
void StreamData(int, Packet *, long);

void UnitStreamOpen(Iob *, long, long);

/*
 *  Called under Forbid
 */

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

    if (unit = FindUnitForPort(iob->io_Port)) {
	if (unit->BeginIO != StreamBeginIO) {
	    iob->io_Error = PPERR_PORT_IN_USE;
	    return;
	}
    } else {
	unit = AllocUnit(iob, StreamBeginIO, StreamAbortIO, StreamData, StreamClose);
    }
    iob->io_Unit = unit;
    ++unit->RefCnt;
}


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

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

/*
 *  UnitStreamData() 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 unit locked.
 */

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

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

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

	    n = actual;
	    iob->io_Actual = actual;
	    if (n < 0)
		n = 0;
	    if (n > iob->io_Length) {
		n = iob->io_Length;
		iob->io_Error = PPERR_WARN_OVFLOW;
	    }
	    movmem((char *)packet->Data1, (char *)iob->io_Data, 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, " TMSTR %d",actual);

	Remove(packet->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, " WRSTR %d",actual);

	Remove(packet->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
StreamBeginIO(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, NULL, 0);
	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
StreamAbortIO(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);
	}
    }
}

