home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fred Fish Collection 1.5
/
ffcollection-1-5-1992-11.iso
/
ff_disks
/
200-299
/
ff294.lzh
/
DNet
/
unix
/
dnet
/
control.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-12-11
|
13KB
|
576 lines
/*
* CONTROL.C
*
* DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
*
* -handle various actions:
* RTO - read timeout
* WTO - write timeout (a packet not acked)
* RNET - state machine for raw receive packet
* WNET - starts timeout sequence if Write occured
* WUPDATE - update write windows, send packets
* RUPDATE - execute received packets in correct sequence
* RECCMD - execute decoded SCMD commands obtained from received
* packets.
*/
#include "dnet.h"
#include <stdio.h>
extern void do_cmd();
ubyte TmpBuf[MAXPACKET];
/*
* RTO: read timeout. Timeout occured while waiting for the rest of
* a packet. Reset state and restart read.
*/
do_rto()
{
RState = 0;
RcvData = 0;
if (DDebug)
fprintf(stderr, "RTO\n");
}
/*
* WTO: Write timeout (unresolved output windows exist). Resend a CHECK
* command for all unresolved (READY) output windows
*/
void
do_wto()
{
register short i;
register PKT *pkt;
short to = 0;
if (DDebug)
fprintf(stderr, "WTO\n");
if (Restart) {
WriteRestart();
return;
}
for (i = 0; i < WPUsed; ++i) {
pkt = WPak[i];
if (pkt->state == READY) { /* send check */
WriteChk((WPStart + i) & 7);
++to;
}
}
if (to)
WTimeout(WTIME);
}
/*
* RNET: Receved data ready from network. The RNet request will
* automagically be reissued on return.
*/
do_rnet()
{
ubyte *ptr = RcvBuf;
long len = RcvData;
RcvData = 0;
RExpect = RecvPacket(ptr, len);
}
/*
* DO_WUPDATE()
*
* (1) Remove EMPTY windows at head of transmit queue.
* (2) Fill up transmit queue with pending requests, if any.
*
* First two bits determine CMD as follows:
* 0bbbbbbb+0x20 DATA 0-95 bytes of DATA
* 10bbbccc DATA 0-7 bytes of DATA, ccc=channel
* command.
* 11bbbbbb bbbbbbbb DATA 0-1023 bytes of DATA
*
* Note! By encoding the quick-data packet with the length + 0x20, you
* don't take the 6 bit encoding hit when sending small amounts of ascii
* data such as keystrokes.
*/
do_wupdate()
{
static short XPri;
int maxpktsize;
register XIOR *ior;
register PKT *pkt;
register long len;
short loop = 0;
WReady = 0;
while (WPUsed && WPak[0]->state == EMPTY) {
pkt = WPak[0];
WPak[0] = WPak[1];
WPak[1] = WPak[2];
WPak[2] = WPak[3];
WPak[3] = pkt;
--WPUsed;
++WPStart;
}
if (Restart)
return(0);
while (WPUsed != 4 && (ior = (XIOR *)RemHead(&TxList))) {
register long offset = 0;
if (++loop == 2) {
WReady = 1;
AddHead(&TxList, ior);
return(0);
}
{
short npri;
if (ior->io_Command == SCMD_DATA)
npri = ior->io_Pri << 2;
else
npri = XPri;
if (npri >= XPri) {
XPri = npri;
} else {
if (XPri - npri > 100)
XPri -= 10;
else if (XPri - npri > 50)
XPri -= 5;
else
--XPri;
}
maxpktsize = MAXPKT - (XPri - npri);
if (DDebug)
fprintf(stderr, "**MAXPKTSIZE = %ld XPri %ld npri %ld\n",
maxpktsize, XPri, npri
);
if (maxpktsize < MINPKT)
maxpktsize = MINPKT;
}
pkt = WPak[WPUsed];
for (;;) {
if (offset > (maxpktsize-8)) /* not enough room */
break;
if (ior->io_Command == SCMD_DATA && ior->io_Channel != WChan) {
/* CSWITCH */
WChan = ior->io_Channel;
TmpBuf[offset+0] = 0x80|SCMD_SWITCH|(2<<3);
TmpBuf[offset+1] = WChan >> 8;
TmpBuf[offset+2] = WChan;
offset += 3;
if (DDebug)
fprintf(stderr, "SEND SWITCH %ld\n", WChan);
}
len = ior->io_Length - ior->io_Actual;
if (ior->io_Command == SCMD_DATA) { /* DATA */
if (DDebug)
fprintf(stderr, "SEND DATA %ld bytes\n", len);
if (offset + len > (maxpktsize-4))
len = (maxpktsize-4) - offset;
if (len < 0x20) {
TmpBuf[offset] = len + 0x20;
++offset;
} else {
TmpBuf[offset+0] = 0x40 + len / 96;
TmpBuf[offset+1] = 0x20 + len % 96;
offset += 2;
}
} else { /* COMMAND */
TmpBuf[offset] = 0x80|ior->io_Command|(len<<3);
++offset;
if (DDebug)
printf("constr sdcmd %02x len %d\n", TmpBuf[0], len);
}
bcopy((char *)ior->io_Data+ior->io_Actual,TmpBuf+offset,len);
offset += len;
ior->io_Actual += len;
if (ior->io_Actual == ior->io_Length) {
free(ior->io_Data);
free(ior);
ior = (XIOR *)RemHead(&TxList); /* Next packet */
if (ior == NULL)
break;
}
}
pkt->state = READY;
BuildDataPacket(pkt, (WPStart + WPUsed) & 7, TmpBuf, offset);
WritePacket(pkt);
if (ior) {
++ior->io_Pri;
Enqueue(&TxList, ior);
--ior->io_Pri;
}
++WPUsed;
}
}
void
dumpcheck(ptr)
register ubyte *ptr;
{
register short i;
for (i = 0;i < 8; ++i) {
if (ptr[i])
replywindow(i);
ptr[i] = 0;
}
}
void
do_cmd(ctl, buf, bytes)
short ctl; /* actually a ubyte */
ubyte *buf;
{
ubyte window = ctl & 7;
ubyte rwindow;
static ubyte Chk, Chkwin[8]; /* remember window checks */
if (ctl == -1) { /* end of sequence */
dumpcheck(Chkwin);
Chk = 0;
return;
}
if ((ctl & PKF_MASK) == PKCMD_CHECK) { /* CHECK packet */
Chkwin[window] = 1;
Chk = 1;
return;
}
if (Chk) { /* NON-CHECK packet*/
dumpcheck(Chkwin);
Chk = 0;
}
switch(ctl & PKF_MASK) {
case PKCMD_WRITE:
case PKCMD_WRITE6:
case PKCMD_WRITE7:
rwindow = (window - RPStart) & 7;
if (rwindow < 4) {
bcopy(buf, RPak[rwindow]->data, bytes);
RPak[rwindow]->buflen = bytes;
RPak[rwindow]->state = READY;
if (rwindow == 0)
do_rupdate();
}
replywindow(window);
break;
case PKCMD_ACK:
rwindow = (window - WPStart) & 7;
if (rwindow < WPUsed) /* mark as sent */
WPak[rwindow]->state = EMPTY;
break;
case PKCMD_NAK: /* resend */
rwindow = (window - WPStart) & 7;
if (rwindow < WPUsed) { /* resend */
WritePacket(WPak[rwindow]);
} else {
fprintf(stderr, "Soft Error: Illegal NAK\n");
}
break;
case PKCMD_ACKRSTART:
case PKCMD_RESTART:
{
uword chan;
uword chksum;
int len;
int fd;
if ((ctl & PKF_MASK) == PKCMD_ACKRSTART)
Restart = 0;
do_netreset();
if ((ctl & PKF_MASK) == PKCMD_RESTART) {
WritePacket(BuildRestartAckPacket("3", 1));
}
if (bytes)
setlistenport(buf);
else
setlistenport("3");
do_wupdate();
}
break;
default:
if (DDebug)
printf("do_cmd: bad packet ctl %02x\n", ctl);
break;
}
do_rupdate();
}
do_rupdate()
{
while (RPak[0]->state == READY) {
register PKT *pkt = RPak[0];
register ubyte *ptr = pkt->data;
register uword len;
uword iolen = pkt->buflen;
ubyte cmd;
while (iolen) {
if (DDebug)
printf("rupdate %02x\n", ptr[0]);
cmd = SCMD_DATA;
len = ptr[0];
++ptr;
--iolen;
if (len >= 128) {
cmd = len & 7;
len = (len >> 3) & 7;
} else {
if (len < 0x40) {
len -= 0x20;
} else {
len = (len - 0x40) * 96 + (*ptr - 0x20);
++ptr;
--iolen;
}
}
iolen -= len;
if (DDebug)
fprintf(stderr, " MPXCMD %ld (%ld bytes)\n", cmd, len);
do_reccmd(cmd, ptr, len);
ptr += len;
}
RPak[0] = RPak[1];
RPak[1] = RPak[2];
RPak[2] = RPak[3];
RPak[3] = pkt;
pkt->state = EMPTY;
++RPStart;
}
}
do_reccmd(cmd, ptr, len)
ubyte *ptr;
{
if (DDebug)
printf("-reccmd %02x (%ld bytes)\n", cmd, len);
switch(cmd) {
case SCMD_DATA:
if (RChan < MAXCHAN && (Chan[RChan].flags & CHANF_ROK))
gwrite(Chan[RChan].fd, ptr, len);
break;
case SCMD_SWITCH:
RChan = (ptr[0]<<8)|ptr[1];
break;
case SCMD_OPEN:
{
register COPEN *cop = (COPEN *)ptr;
CACKCMD ack;
uword chan = (cop->chanh << 8) | cop->chanl;
ack.chanh = cop->chanh;
ack.chanl = cop->chanl;
ack.error = 0;
if (chan >= MAXCHAN || Chan[chan].state) {
ack.error = 33; /* magic */
WriteStream(SCMD_ACKCMD, &ack, sizeof(CACKCMD), chan);
break;
}
{
int error;
int s;
uword port = (cop->porth<<8)|cop->portl;
if (isinternalport(port)) {
error = iconnect(&s, port);
} else {
struct sockaddr sa;
s = socket(PF_UNIX, SOCK_STREAM, 0);
if (DDebug)
fprintf(stderr, " REC OPEN, CONNECTING ch%d po%d\n",
chan, port
);
sa.sa_family = AF_INET;
sprintf(sa.sa_data,".PORT.%ld", port);
error = connect(s, &sa, sizeof(sa));
if (error < 0) {
startserver(port);
error = connect(s, &sa, sizeof(sa));
}
if (DDebug)
fprintf(stderr, " CONNECTED err=%ld\n", error);
}
if (error < 0) {
ack.error = 2;
} else {
extern void do_open();
fcntl(s, F_SETFL, FNDELAY);
Chan[chan].state = CHAN_OPEN;
Chan[chan].flags = CHANF_ROK|CHANF_WOK;
Chan[chan].fd = s;
Chan[chan].pri= cop->pri;
FD_SET(s, &Fdread);
FD_SET(s, &Fdexcept);
FdChan[s] = chan;
Fdstate[s] = do_open;
}
WriteStream(SCMD_ACKCMD, &ack, sizeof(CACKCMD), -1);
}
}
break;
case SCMD_CLOSE: /* receive close */
{
extern void nop();
register CCLOSE *clo = (CCLOSE *)ptr;
uword chan = (clo->chanh<<8)|clo->chanl;
int fd = Chan[chan].fd;
if (DDebug)
fprintf(stderr, " SCMD_CLOSE\n");
if (chan >= MAXCHAN || Chan[chan].state == CHAN_FREE)
break;
/*
Chan[chan].state = CHAN_CLOSE;
Chan[chan].flags |= CHANF_RCLOSE;
*/
Chan[chan].flags &= ~(CHANF_ROK|CHANF_WOK);
FD_CLR(fd, &Fdread);
FD_CLR(fd, &Fdexcept);
Chan[chan].state = CHAN_FREE;
Chan[chan].fd = -1;
Fdstate[fd] = nop;
close(fd);
ClearChan(&TxList, chan, 0);
if (Chan[chan].flags & CHANF_LCLOSE) {
if (DDebug)
fprintf(stderr," REMOTE CLOSE %ld, LOCAL ALREADY CLOSED\n",
fd
);
} else {
CCLOSE cc;
char dummy;
cc.chanh = chan >> 8;
cc.chanl = chan;
WriteStream(SCMD_CLOSE, &cc, sizeof(CCLOSE), chan);
/*
shutdown(Chan[chan].fd, 2);
write(Chan[chan].fd, &dummy, 0);
*/
if (DDebug)
fprintf(stderr," REMOTE CLOSE %ld, LOCAL NOT YET CLOSED\n",
fd
);
}
}
break;
case SCMD_ACKCMD: /* acknowledge of my open */
{
register CACKCMD *cack = (CACKCMD *)ptr;
uword chan = (cack->chanh<<8)|cack->chanl;
if (chan >= MAXCHAN || Chan[chan].state != CHAN_LOPEN)
break;
if (DDebug)
fprintf(stderr, "ackerr = %ld\n", cack->error);
if (cack->error == 33) {
uword newchan = alloc_channel();
COPEN co;
if (newchan < MAXCHAN) {
Chan[newchan] = Chan[chan];
Chan[chan].state = CHAN_FREE;
Chan[chan].fd = -1;
co.chanh = newchan >> 8;
co.chanl = newchan;
co.porth = Chan[chan].port >> 8;
co.portl = Chan[chan].port;
co.error = 0;
co.pri = Chan[chan].pri;
WriteStream(SCMD_OPEN, &co, sizeof(COPEN), newchan);
break;
}
}
if (cack->error) {
extern void nop();
ubyte error = cack->error;
int fd = Chan[chan].fd;
gwrite(fd, &error, 1);
Fdstate[fd] = nop;
Chan[chan].fd = -1;
Chan[chan].state = CHAN_FREE;
FD_CLR(fd, &Fdread);
FD_CLR(fd, &Fdexcept);
close(fd);
} else {
ubyte error = 0;
extern void do_open();
gwrite(Chan[chan].fd, &error, 1);
Chan[chan].state = CHAN_OPEN;
Chan[chan].flags = CHANF_ROK|CHANF_WOK;
Fdstate[Chan[chan].fd] = do_open;
}
}
break;
case SCMD_EOFCMD: /* EOF on channel */
{
register CEOFCMD *eof = (CEOFCMD *)ptr;
uword chan = (eof->chanh<<8)|eof->chanl;
if (chan < MAXCHAN && Chan[chan].state == CHAN_OPEN) {
Chan[chan].flags &= ~eof->flags;
if (eof->flags & CHANF_ROK) {
char dummy;
shutdown(Chan[chan].fd, 1);
write(Chan[chan].fd, &dummy, 0);
}
}
}
break;
case SCMD_QUIT:
dneterror("QUIT");
break;
case SCMD_IOCTL:
{
register CIOCTL *cio = (CIOCTL *)ptr;
uword chan = (cio->chanh<<8)|cio->chanl;
if (chan < MAXCHAN && Chan[chan].state == CHAN_OPEN) {
switch(cio->cmd) {
case CIO_SETROWS:
isetrows(Chan[chan].fd, (cio->valh<<8)|cio->vall);
break;
case CIO_SETCOLS:
isetcols(Chan[chan].fd, (cio->valh<<8)|cio->vall);
break;
case CIO_STOP:
break;
case CIO_START:
break;
case CIO_FLUSH:
ClearChan(&TxList, chan, 0);
break;
}
}
}
break;
default:
break;
}
}
replywindow(window)
{
ubyte rwindow = (window - RPStart) & 7;
if (DDebug) {
if (rwindow >= 4 || RPak[rwindow]->state == READY)
fprintf(stderr, " ACK WINDOW %ld\n", window);
else
fprintf(stderr, " NAK WINDOW %ld\n", window);
}
if (rwindow >= 4 || RPak[rwindow]->state == READY) /* data ready */
WriteAck(window);
else
WriteNak(window);
}