
/*
 *  DNETLIB.C
 *
 *  DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved.
 *
 *  Library Interface for DNET.
 */

#define NOEXT
#include "/dnet/dnet.h"

#define CHANN	struct _CHANN

#define NAMELEN sizeof("DNET.PORT.XXXXX")
#define NAMEPAT "DNET.PORT.%ld"

CHANN {
    PORT    port;	    /*	receive data, replies	    */
    PORT    *dnetport;	    /*	dnet's master port          */
    LIST    rdylist;	    /*	ready to be read	    */
    uword   chan;	    /*	channel # for open channels */
    ubyte   eof;	    /*	channel remotely closed/eof */
    ubyte   filler;
    int     qlen;	    /*	allowed write queue size    */
    int     queued;	    /*	current # packets queued    */
};

static CHANN *MakeChannel();

PORT *
DListen(portnum)
uword portnum;
{
    PORT *port = NULL;
    char *ptr;

    ptr = AllocMem(NAMELEN, MEMF_PUBLIC);   /*  memory the the name     */
    sprintf(ptr, NAMEPAT, portnum);
    Forbid();                               /*  task-atomic operation   */
    if (FindPort(ptr) || !(port = CreatePort(ptr,0)))
	FreeMem(ptr, NAMELEN);
    Permit();
    return(port);
}

void
DUnListen(lisport)
PORT *lisport;
{
    if (lisport) {
	register char *ptr = lisport->mp_Node.ln_Name;
	Forbid();                       /*  task-atomic operation       */
	while (DNAAccept(lisport));     /*  remove all pending requests */
	DeletePort(lisport);            /*  gone!                       */
	Permit();
	FreeMem(ptr, NAMELEN);
    }
}

/*
 *  DAccept()
 *
 *  Note:   This call will work even if called by a task which does not
 *	    own the listen port.
 */

PORT *
DAccept(lisport)
PORT *lisport;
{
    register IOR *ior;
    register CHANN *chan = NULL;

    while (!chan && (ior = GetMsg(lisport))) {
	switch(ior->io_Command) {
	case DNCMD_SOPEN:
	    chan = MakeChannel(ior, NULL);
	    break;
	default:
	    ior->io_Error = 1;
	    break;
	}
	ReplyMsg(ior);
    }
    if (lisport->mp_MsgList.lh_Head != (NODE *)&lisport->mp_MsgList.lh_Tail)
	SetSignal(1 << lisport->mp_SigBit, 1 << lisport->mp_SigBit);
    return((PORT *)chan);
}

/*
 *  Refuse a connection
 */

DNAAccept(lisport)
PORT *lisport;
{
    IOR *ior;

    if (ior = GetMsg(lisport)) {
	ior->io_Error = 1;
	ReplyMsg(ior);
    }
    if (lisport->mp_MsgList.lh_Head != (NODE *)&lisport->mp_MsgList.lh_Tail)
	SetSignal(1 << lisport->mp_SigBit, 1 << lisport->mp_SigBit);
    return(ior != NULL);
}

DPri(chan, pri)
CHANN *chan;
{
}


PORT *
DOpen(host, portnum, txpri, rxpri)
char *host;
char txpri, rxpri;
uword portnum;
{
    IOR ior;
    CHANN *chan;

    if (!host)
	host = "0";
    chan = MakeChannel(&ior, host);
    if (rxpri > 126)
	rxpri = 126;
    if (rxpri < -127)
	rxpri = -127;
    if (txpri > 126)
	txpri = 126;
    if (txpri < -127)
	txpri = -127;
    if (chan->dnetport) {
	ior.io_Command = DNCMD_OPEN;
	ior.io_Unit = (void *)portnum;
	ior.io_Offset = (long)chan;
	ior.io_Message.mn_ReplyPort = (PORT *)chan;
	ior.io_Message.mn_Node.ln_Pri = txpri;
	ior.io_Message.mn_Node.ln_Name= (char *)rxpri;

	PutMsg(chan->dnetport, &ior);
	WaitMsg(&ior);
	if (ior.io_Error == 0) {
	    chan->chan = (long)ior.io_Unit;
	    FixSignal(chan);
	    return((PORT *)chan);
	}
    }
    DeleteChannel(chan);
    return(NULL);
}


DNRead(chan, buf, bytes)
CHANN *chan;
char *buf;
{
    register IOR *ior;
    int len = 0;
    long n;

    if (chan->eof)
	return(-1);
    while (bytes && ((ior = RemHead(&chan->rdylist)) || (ior = GetMsg(chan)))) {
	if (ior->io_Message.mn_Node.ln_Type == NT_REPLYMSG) {
	    if (!chan->queued)
		puts("DNRead: Software Error");
	    else
		--chan->queued;
	    if (ior->io_Length)
		FreeMem(ior->io_Data, ior->io_Length);
	    FreeMem(ior, sizeof(IOR));
	    continue;
	}
	switch(ior->io_Command) {
	case DNCMD_CLOSE:
	case DNCMD_EOF:
	    chan->eof = 1;
	    ReplyMsg(ior);
	    break;
	case DNCMD_WRITE:
	    n = ior->io_Length - ior->io_Actual;
	    if (n <= bytes) {
		CopyMem((char *)ior->io_Data + ior->io_Actual, buf, n);
		bytes -= n;
		len += n;
		buf += n;
		ReplyMsg(ior);
	    } else {
		CopyMem((char *)ior->io_Data + ior->io_Actual, buf, bytes);
		len += bytes;
		ior->io_Actual += bytes;
		bytes = 0;
		Forbid();   /*  DNET device is a task, no need to Disable() */
		ior->io_Message.mn_Node.ln_Type = NT_MESSAGE;
		AddHead(&chan->port.mp_MsgList, ior);
		Permit();
	    }
	    break;
	default:
	    ior->io_Error = 1;
	    ReplyMsg(ior);
	}
    }
    FixSignal(chan);
    if (chan->eof)
	SetSignal(1 << chan->port.mp_SigBit, 1 << chan->port.mp_SigBit);
    return(len);
}

DRead(chan, buf, bytes)
char *buf;
CHANN *chan;
{
    long len = 0;
    long n;

    if (chan->eof)
	return(-1);
    while (bytes) {
	WaitPort(chan);
	n = DNRead(chan, buf, bytes);
	if (n < 0)
	    break;
	len += n;
	buf += n;
	bytes -= n;
	if (chan->eof)
	    break;
    }
    return(len);
}

DQueue(chan, n)
CHANN *chan;
{
    chan->qlen = n;
}

DWrite(chan, buf, bytes)
CHANN *chan;
{
    int error = bytes;

    if (chan->qlen) {
	if (WaitQueue(chan, NULL) >= 0) {
	    register IOR *ior = AllocMem(sizeof(IOR), MEMF_CLEAR|MEMF_PUBLIC);
	    ior->io_Command = DNCMD_WRITE;
	    ior->io_Unit = (void *)chan->chan;
	    ior->io_Offset = (long)chan;
	    ior->io_Message.mn_ReplyPort = (PORT *)chan;
	    ior->io_Data = AllocMem(bytes, MEMF_PUBLIC);
	    ior->io_Length = bytes;
	    CopyMem(buf, ior->io_Data, bytes);
	    PutMsg(chan->dnetport, ior);
	    ++chan->queued;
	} else {
	    error = -1;
	}
    } else {
	IOR ior;
	ior.io_Command = DNCMD_WRITE;
	ior.io_Unit = (void *)chan->chan;
	ior.io_Offset = (long)chan;
	ior.io_Message.mn_ReplyPort = (PORT *)chan;
	ior.io_Data = (APTR)buf;
	ior.io_Length = bytes;
	PutMsg(chan->dnetport, &ior);
	WaitMsg(&ior);
	if (ior.io_Error)
	    error = -1;
    }
    FixSignal(chan);
    return(error);
}

DEof(chan)
CHANN *chan;
{
    IOR ior;

    ior.io_Command = DNCMD_EOF;
    ior.io_Unit = (void *)chan->chan;
    ior.io_Offset = (long)chan;
    ior.io_Message.mn_ReplyPort = (PORT *)chan;
    PutMsg(chan->dnetport, &ior);
    WaitMsg(&ior);
    FixSignal(chan);
}

DIoctl(chan, cmd, val, aux)
CHANN *chan;
ubyte cmd;
uword val;
ubyte aux;
{
    IOR ior;

    ior.io_Command = DNCMD_IOCTL;
    ior.io_Unit = (void *)chan->chan;
    ior.io_Offset = (long)chan;
    ior.io_Message.mn_ReplyPort = (PORT *)chan;
    ior.io_Data = (APTR)(long)((val<<16)|(aux<<8)|cmd);
    PutMsg(chan->dnetport, &ior);
    WaitMsg(&ior);
    FixSignal(chan);
}

DQuit(host)
char *host;
{
    IOR ior;
    char buf[sizeof(DNETPORTNAME)+32];
    PORT *replyport = CreatePort(NULL, 0);
    PORT *dnetport;

    if (!host)
	host = "0";
    sprintf(buf, "%s%s", DNETPORTNAME, host);
    if (dnetport = FindPort(buf)) {
	ior.io_Command = DNCMD_QUIT;
	ior.io_Unit = 0;
	ior.io_Offset = 0;
	ior.io_Message.mn_ReplyPort = replyport;
	PutMsg(dnetport, &ior);
	WaitMsg(&ior);
	DeletePort(replyport);
    }
    return(dnetport != NULL);
}


DClose(chan)
CHANN *chan;
{
    IOR ior;
    IOR *io;

    ior.io_Command = DNCMD_CLOSE;
    ior.io_Unit = (void *)chan->chan;
    ior.io_Offset = (long)chan;
    ior.io_Message.mn_ReplyPort = (PORT *)chan;
    PutMsg(chan->dnetport, &ior);
    ++chan->queued;
    chan->qlen = 0;
    WaitQueue(chan, &ior);
    while ((io = RemHead(&chan->rdylist)) || (io = GetMsg(chan))) {
	io->io_Error = 1;
	ReplyMsg(io);
    }
    DeleteChannel(chan);
}

WaitMsg(ior)
IOR *ior;
{
    while (ior->io_Message.mn_Node.ln_Type != NT_REPLYMSG)
	Wait(1 << ior->io_Message.mn_ReplyPort->mp_SigBit);
    Forbid();
    Remove(ior);
    Permit();
}

WaitQueue(chan, skipior)
CHANN *chan;
IOR *skipior;
{
    register IOR *io;
    short error = 0;
    while (chan->queued > chan->qlen) {     /*  until done  */
	WaitPort(chan);                     /*  something   */
	io = (IOR *)GetMsg(chan);
	if (io->io_Message.mn_Node.ln_Type == NT_REPLYMSG) {
	    if (error == 0)
		error = io->io_Error;
	    if (io != skipior) {
		if (io->io_Length)
		    FreeMem(io->io_Data, io->io_Length);
		FreeMem(io, sizeof(IOR));
	    }
	    --chan->queued;
	} else {
	    AddTail(&chan->rdylist, io);
	}
    }
    return(error);
}

static
CHANN *
MakeChannel(ior, host)
register IOR *ior;
char *host;
{
    CHANN *chan = AllocMem(sizeof(CHANN), MEMF_PUBLIC|MEMF_CLEAR);

    /*	Name, Pri */
    chan->port.mp_Node.ln_Type = NT_MSGPORT;
    chan->port.mp_SigBit = AllocSignal(-1);
    chan->port.mp_SigTask = FindTask(NULL);
    NewList(&chan->port.mp_MsgList);
    NewList(&chan->rdylist);
    chan->chan = (long)ior->io_Unit;
    ior->io_Offset = (long)chan;
    if (host) {
	char buf[sizeof(DNETPORTNAME)+32];
	sprintf(buf, "%s%s", DNETPORTNAME, host);
	ior->io_Message.mn_ReplyPort = FindPort(buf);
    }
    chan->dnetport = ior->io_Message.mn_ReplyPort;
    return(chan);
}

static
DeleteChannel(chan)
CHANN *chan;
{
    FreeSignal(chan->port.mp_SigBit);
    FreeMem(chan, sizeof(CHANN));
}

static
FixSignal(chan)
register CHANN *chan;
{
    if (chan->port.mp_MsgList.lh_Head != (NODE *)&chan->port.mp_MsgList.lh_Tail ||
    chan->rdylist.lh_Head != (NODE *)&chan->rdylist.lh_Tail)
	SetSignal(1 << chan->port.mp_SigBit, 1 << chan->port.mp_SigBit);
}

