/* * CONTROL.C * * DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved. * * Main packet control. Medium level. Interprets formatted received * packets and sends formatted tx packets. Uses low level packet * construction and reception routines in PACKET.C */ #include "dnet.h" #include void do_rupdate(); void do_reccmd(); void replywindow(); void StartWriteTimeout(); /* * RTO: read timeout. Timeout occured while waiting for the rest of * a packet. Reset state and restart read. * * called from iosink loop, ok if signal mask cleared */ void do_rto(ior) IOT *ior; { NetStartRead(RecvPacket(NULL, 0)); } /* * WTO: Write timeout (unresolved output windows exist). Resend a CHECK * command for all unresolved (READY) output windows */ void do_wto(ior) IOT *ior; { short i; PKT *pkt; short to = 0; if (Restart) { WriteRestart(); return; } for (i = 0; i < WPUsed; ++i) { pkt = WPak[i]; if (pkt->state == READY) { WriteChk((WPStart + i) & 7); ++to; } } if (to) StartWriteTimeout(8); } void do_rnet(ptr, len) ubyte *ptr; long len; { long expect; long n; if (len < 0) ptr = NULL; expect = RecvPacket(ptr, len); n = NetReady(); if (n > expect) expect = n; NetStartRead(expect); } /* * StartWriteTimeout() * * Begins a timeout for written packets. This timeout normally never * occurs even with data errors (the receiver detects missing windows * and sends the appropriate NAK). */ void StartWriteTimeout(secs) { if (Wto_act) { AbortIO((IOR *)&Wto); WaitIO((IOR *)&Wto); Wto_act = 0; } if (secs) { Wto.tr_time.tv_secs = secs; Wto.tr_time.tv_micro= 0; SendIO((IOR *)&Wto); Wto_act = 1; } } /* * DO_WUPDATE() * * (1) Remove EMPTY windows at head of transmit queue. * (2) Fill up transmit queue with pending requests, if any. * (3) Start timeout for write if did send packet. * * CMD 0x20-0x3F 0-31 bytes data * 0x40-0x7F 0-N bytes data (w/ extend byte 0x20-0x7F) * 0x80-0xFF Command. * * Note that normal data encoding overhead uses 0x20-0x7F, * thereby allowing 7 bit data to be transmitted with normal * packets instead of expanded (8->6 bits) packets. */ void do_wupdate() { static short XPri; static ubyte DBuf[MAXPACKET]; int maxpktsize; IOSTD *ior; PKT *pkt; long len; char wrotePkt = 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; /* * Fill new packets and send. */ while (WPUsed != 4 && (ior = (IOSTD *)RemHead(&TxList))) { long offset = 0; if (DDebug) printf("Extract Request: %08lx %ld bytes\n", ior, ior->io_Length); { short npri; if (ior->io_Command == SCMD_DATA) { ubyte chan = (ulong)ior->io_Unit; if (Chan[chan].state == CHAN_CLOSE) { /* channel closed */ ior->io_Error = 1; ReplyMsg((MSG *)ior); continue; } npri = ior->io_Message.mn_Node.ln_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 (maxpktsize < MINPKT) maxpktsize = MINPKT; } pkt = WPak[WPUsed]; pkt->state = READY; for (;;) { if (offset > (maxpktsize-8)) /* not enough room */ break; if (ior->io_Command == SCMD_DATA && (ulong)ior->io_Unit != WChan) { /* CSWITCH */ WChan = (ulong)ior->io_Unit; DBuf[offset+0] = 0x80|SCMD_SWITCH|(2<<3); DBuf[offset+1] = WChan >> 8; DBuf[offset+2] = WChan; offset += 3; } len = ior->io_Length - ior->io_Actual; if (ior->io_Command == SCMD_DATA) { /* DATA OUT */ if (offset + len > (maxpktsize-4)) len = (maxpktsize-4) - offset; if (len < 0x20) { DBuf[offset] = len + 0x20; ++offset; } else { DBuf[offset+0] = 0x40 + len / 96; DBuf[offset+1] = 0x20 + len % 96; offset += 2; } BytesOut += len; } else { /* COMMAND OUT */ DBuf[offset] = 0x80|ior->io_Command|(len<<3); ++offset; } BMov((char *)ior->io_Data + ior->io_Actual, DBuf + offset, len); offset += len; ior->io_Actual += len; if (ior->io_Actual == ior->io_Length) { ReplyMsg((MSG *)ior); ior = (IOSTD *)RemHead(&TxList); /* Next packet */ if (ior == NULL) break; } } BuildDataPacket(pkt, (WPStart + WPUsed) & 7, DBuf, offset); WritePacket(pkt); wrotePkt = 1; if (ior) { ++ior->io_Message.mn_Node.ln_Pri; Enqueue(&TxList, (NODE *)ior); --ior->io_Message.mn_Node.ln_Pri; } ++WPUsed; ++PacketsOut; ResetIdle(); break; /* One at a time, else would take too long */ } if (wrotePkt) StartWriteTimeout(8); } void dumpcheck(ptr) ubyte *ptr; { short i; for (i = 0; i < 8; ++i) { if (ptr[i]) replywindow(i); ptr[i] = 0; } } void do_cmd(ctl, buf, bytes) uword ctl; /* usually just 8 bits though */ ubyte *buf; { ubyte window = ctl & PKF_SEQUENCE; ubyte rwindow; static ubyte Chk, Chkwin[8]; if (ctl == 0xFFFF) { if (Chk) { dumpcheck(Chkwin); Chk = 0; } return; } if (DDebug) printf("RECV-PACKET %02x %4ld bytes\n", ctl, bytes); if ((ctl & PKF_MASK) == PKCMD_CHECK) { Chkwin[window] = 1; Chk = 1; return; } if (Chk) { dumpcheck(Chkwin); Chk = 0; } switch(ctl & PKF_MASK) { case PKCMD_WRITE: case PKCMD_WRITE6: case PKCMD_WRITE7: rwindow = (window - RPStart) & 7; if (rwindow < 4) { BMov(buf, RPak[rwindow]->data, bytes); RPak[rwindow]->buflen = bytes; /* dummy */ RPak[rwindow]->state = READY; do_rupdate(); /* * Check for missing receive packet. rwindow 1..3 and * (rwindow - 1) != READY */ if (rwindow && RPak[rwindow-1]->state != READY) WriteNak((window - 1) & 7); } 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 */ ++PacketsResent; WritePacket(WPak[rwindow]); StartWriteTimeout(8); } else { printf("Soft Error: Illegal NAK: %ld %ld %ld %ld\n", window, WPStart, rwindow, WPUsed ); } break; case PKCMD_RESTART: case PKCMD_ACKRSTART: if ((ctl & PKF_MASK) == PKCMD_ACKRSTART) Restart = 0; do_netreset(); /* RxPtr? */ if ((ctl & PKF_MASK) == PKCMD_RESTART) { static ubyte buf[32]; strcpy(buf, " "); /* * note, restart packet may contain only ascii 0x20-0x7F * 00 is definitely out. */ WritePacket(BuildRestartAckPacket(buf, strlen(buf))); StartWriteTimeout(5); } break; } do_rupdate(); } /* * Multiplex data onto the low level stream * * 0x20-0x3F 0-31 bytes of data for current channel * 0x80-0xBF control-command w/0-7 bytes of data * 0x40-0x7F this + extend byte (0x20-0x7F) for long data pkts */ void do_rupdate() { while (RPak[0]->state == READY) { PKT *pkt = RPak[0]; ubyte *ptr = pkt->data; uword len; uword iolen = pkt->buflen; ubyte cmd; while (iolen) { cmd = SCMD_DATA; len = ptr[0]; ++ptr; --iolen; if (len >= 128) { cmd = len & 7; len = (len >> 3) & 7; } else { if (len < 0x40) { len -= 0x20; if (len < 0) { printf("HLP len error1 %d\n", len); len = 0; } } else { if (*ptr < 0x20) printf("HLP len error2 %02x %02x\n", len, *ptr); len = (len - 0x40) * 96 + (*ptr - 0x20); ++ptr; --iolen; } } iolen -= len; if (DDebug) printf("RECEIVE CMD %2ld ", cmd); 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; } } void do_reccmd(cmd, ptr, len) int cmd; ubyte *ptr; int len; { switch(cmd) { case SCMD_DATA: /* data for channel */ if (RChan < MAXCHAN && (Chan[RChan].flags & CHANF_ROK)) { IOSTD *ior = AllocMem(sizeof(IOSTD), MEMF_PUBLIC); ior->io_Unit = (struct Unit *)RChan; ior->io_Data = AllocMem(len, MEMF_PUBLIC); ior->io_Length = len; ior->io_Actual = 0; BMov(ptr, ior->io_Data, len); ior->io_Message.mn_Node.ln_Name = (char *)PKT_REQ; ior->io_Command = DNCMD_WRITE; ior->io_Message.mn_ReplyPort = IOSink; PutMsg(Chan[RChan].port, (MSG *)ior); BytesIn += len; ResetIdle(); /* not idle, have received data */ } break; case SCMD_SWITCH: RChan = (ptr[0]<<8)|ptr[1]; break; case SCMD_OPEN: { COPEN *cop = (COPEN *)ptr; PORT *port; CACKCMD ack; char buf[32]; uword chan = (cop->chanh << 8) | cop->chanl; uword portnum = (cop->porth << 8) | cop->portl; 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), (uword)-1); break; } sprintf(buf, "DNET.PORT.%ld", portnum); if ((port = (PORT *)FindPort(buf)) == NULL) { RunServer(portnum); if ((port = (PORT *)FindPort(buf)) == NULL) { ack.error = 2; WriteStream(SCMD_ACKCMD, &ack, sizeof(CACKCMD), (uword)-1); break; } } /* ln_Name type of 0 causes reply to go to DNetPort */ WritePort(port, DNCMD_SOPEN, NULL, 0, 0, chan); Chan[chan].state = CHAN_ROPEN; Chan[chan].pri = cop->pri; } break; case SCMD_CLOSE: /* receive close */ { CCLOSE *clo = (CCLOSE *)ptr; uword chan = (clo->chanh<<8)|clo->chanl; if (DDebug) printf("Remote close, chan %d state %d\n", chan, Chan[chan].state); 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); if (Chan[chan].flags & CHANF_LCLOSE) { if (DDebug) printf("Local already closed, reply %08lx\n", Chan[chan].ior); Chan[chan].state = CHAN_FREE; ReplyMsg((MSG *)Chan[chan].ior); Chan[chan].ior = NULL; } else { /* send EOF */ if (DDebug) printf("Local not already closed\n"); WritePort(Chan[chan].port, DNCMD_CLOSE, NULL, 0, PKT_REQ, chan); } } break; case SCMD_ACKCMD: /* acknowledge of my open */ { CACKCMD *cack = (CACKCMD *)ptr; uword chan = (cack->chanh<<8)|cack->chanl; if (chan >= MAXCHAN || Chan[chan].state != CHAN_LOPEN) { break; } /* * Channel in use (collision), try again */ if (cack->error == 33) { uword newchan = alloc_channel(); COPEN co; if (newchan < MAXCHAN) { Chan[newchan] = Chan[chan]; Chan[chan].state = CHAN_FREE; Chan[chan].ior = NULL; co.chanh = newchan >> 8; co.chanl = newchan; co.porth = (ulong)Chan[newchan].ior->io_Unit >> 8; co.portl = (ulong)Chan[newchan].ior->io_Unit; co.error = 0; co.pri = Chan[chan].pri; WriteStream(SCMD_OPEN, &co, sizeof(COPEN), chan); break; } } if (cack->error) { Chan[chan].state = CHAN_FREE; Chan[chan].ior->io_Error = cack->error; ReplyMsg((MSG *)Chan[chan].ior); Chan[chan].ior = NULL; } else { Chan[chan].state = CHAN_OPEN; Chan[chan].ior->io_Error = 0; Chan[chan].ior->io_Unit = (struct Unit *)chan; Chan[chan].flags = CHANF_ROK|CHANF_WOK; ReplyMsg((MSG *)Chan[chan].ior); Chan[chan].ior = NULL; } } break; case SCMD_EOFCMD: /* EOF on channel */ { 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) WritePort(Chan[chan].port, DNCMD_EOF, NULL, 0, PKT_REQ, chan); } else { printf("SCMD_EOFCMD: Error chan %ld state %ld\n", chan, Chan[chan].state ); } } break; case SCMD_IOCTL: { CIOCTL *cio = (CIOCTL *)ptr; uword chan = (cio->chanh<<8)|cio->chanl; if (Chan[chan].state == CHAN_OPEN) WritePort(Chan[chan].port, DNCMD_IOCTL, cio, sizeof(*cio), PKT_REQ, chan); } break; default: if (DDebug) printf("BAD SCMD, %ld\n", cmd); break; } } void replywindow(window) int window; { ubyte rwindow = (window - RPStart) & 7; if (rwindow >= 4 || RPak[rwindow]->state == READY) { /* data ready */ WriteAck(window); ++PacketsIn; } else { WriteNak(window); ++PacketsNakd; } }