home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 239.lha / amiga / src / dnet / control.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-05-02  |  15.1 KB  |  659 lines

  1.  
  2. /*
  3.  *  CONTROL.C
  4.  *
  5.  *  DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved.
  6.  *
  7.  */
  8.  
  9. #include "dnet.h"
  10. #include <stdio.h>
  11.  
  12. static ubyte Dctl;
  13.  
  14. /*
  15.  *  RTO:    read timeout.   Timeout occured while waiting for the rest of
  16.  *        a packet.  Reset state and restart read.
  17.  *
  18.  *  called from iosink loop, ok if signal mask cleared
  19.  */
  20.  
  21. do_rto(ior)
  22. IOT *ior;
  23. {
  24.     NetAbortRead();
  25.     NetStartRead(&Raux->sync, 3);
  26.     if (RState == RS_DATA && (Dctl & PKF_MASK) == PKCMD_WRITE)
  27.     replywindow(Dctl >> 5);     /* NAK the packet */
  28.     RState = 0;
  29. }
  30.  
  31. /*
  32.  *  WTO:    Write timeout (unresolved output windows exist).  Resend a CHECK
  33.  *        command for all unresolved (READY) output windows
  34.  */
  35.  
  36. void
  37. do_wto(ior)
  38. {
  39.     register short i;
  40.     register PKT *pkt;
  41.  
  42.     if (Restart) {
  43.     NetWrite(RestartPkt, 3, 1);
  44.     return;
  45.     }
  46.     for (i = 0; i < WPUsed; ++i) {
  47.     pkt = WPak[i];
  48.     if (pkt->state == READY)    /*  send check  */
  49.         NetWrite(CheckPkt[(WPStart+i)&7], 3, 1);
  50.     }
  51. }
  52.  
  53. /*
  54.  *  RNET:   Receved data ready from network.  The RNet request will
  55.  *        automagically be reissued on return.
  56.  *
  57.  *  NOTE:   The buffer the data is contained in can be anything, and
  58.  *        is not restricted to the Auxillary request this routine uses.
  59.  *
  60.  *        called from iosink loop, signal mask can be cleared.
  61.  */
  62.  
  63. do_rnet(ior)
  64. IOR *ior;
  65. {
  66.     register ubyte *ptr = (ubyte *)ior->io_Data;
  67.     register long len  = ior->io_Actual;
  68.     long n;
  69.     static uword dlen;
  70.     static uword dblen;
  71.  
  72.     if (len <= 0) {
  73.     len = 0;
  74.     RState = RS_SYNC;
  75.     }
  76.     while (len) {
  77.     switch(RState) {
  78.     case RS_SYNC:            /*  SYNC BYTE        */
  79.         --len;
  80.         if (*ptr++ == SYNC) {
  81.         RState = RS_CTL;
  82.         } else {
  83.         ++GarbageIn;
  84.         if (DDebug)
  85.             printf("Garbage: '%c'\n", *(ptr-1));
  86.         }
  87.         break;
  88.     case RS_CTL:            /*  CONTROL BYTE    */
  89.         --len;
  90.         Dctl = *ptr++;
  91.         RState = RS_CCHK;
  92.         break;
  93.     case RS_CCHK:            /*  CHECK BYTE        */
  94.         if ((ubyte)(((SYNC<<1)^Dctl)) == *ptr) {
  95.         ++ptr;
  96.         --len;
  97.         if (Dctl & PKF_DATA) {  /*  long packet     */
  98.             RState = RS_LEN1;
  99.             break;
  100.         }
  101.         RState = RS_SYNC;    /*  short packet    */
  102.         do_cmd(Dctl, NULL, 0);
  103.         } else {
  104.         if (len) {              /*  error           */
  105.             ++len;
  106.             --ptr;
  107.         }
  108.         }
  109.         RState = RS_SYNC;
  110.         break;
  111.     case RS_LEN1:            /*  first byte of pkt len   */
  112.         dlen = *ptr;
  113.         ++ptr;
  114.         --len;
  115.         RState = RS_LEN2;
  116.         break;
  117.     case RS_LEN2:            /*  second byte of pkt len  */
  118.         dlen = (dlen << 8) + *ptr + 2;
  119.         if (dlen < 2)
  120.         fprintf(stderr, "WARNING, DATALEN <2: %02lx %ld\n", Dctl, dlen);
  121.         if (dlen > MAXPKT+2) {
  122.         fprintf(stderr, "DATALEN FAILURE: %02lx %ld\n", Dctl, dlen);
  123.         RState = RS_SYNC;
  124.         break;
  125.         }
  126.         ++ptr;
  127.         --len;
  128.         dblen = 0;
  129.         RState = RS_DATA;
  130.         break;
  131.     case RS_DATA:
  132.         len += dblen;    /*  move to beginning of data buffer    */
  133.         ptr -= dblen;
  134.         if (dlen <= len) {
  135.         register uword chk = chkbuf(ptr, dlen - 2);
  136.         if ((chk >> 8) == ptr[dlen-2] && (ubyte)chk == ptr[dlen-1]) {
  137.             do_cmd(Dctl, ptr, dlen-2);
  138.             len -= dlen;
  139.             ptr += dlen;
  140.         } else {
  141.             if ((Dctl & PKF_MASK) == PKCMD_WRITE)
  142.             replywindow(Dctl >> 5); /*  NAK the packet */
  143.         }
  144.         RState = RS_SYNC;
  145.         } else {
  146.         goto break2;    /*  incomplete read  */
  147.         }
  148.         break;
  149.     }
  150.     }
  151. break2:
  152.     if (Rto_act) {
  153.     AbortIO(&Rto);
  154.     WaitIO(&Rto);
  155.     Rto_act = 0;
  156.     }
  157.     n = NetReady();
  158.     if (n > MAXPKT)
  159.     n = MAXPKT;
  160.     switch(RState) {
  161.     default:
  162.     printf("SoftError: Illegal state %ld\n", RState);
  163.     RState = RS_SYNC;
  164.     /* fall through */
  165.     case RS_SYNC:    /*  Wait for sync & cmd.  No timeout needed    */
  166.     ptr = &Raux->sync;
  167.     len = MAX(3,n);
  168.     break;
  169.     case RS_CTL:    /*  Have sync, need CTL and CCHK.        */
  170.     ptr = &Raux->ctl;
  171.     len = MAX(2,n);
  172.     break;
  173.     case RS_CCHK:
  174.     ptr = &Raux->cchk;
  175.     len = MAX(1,n);
  176.     break;
  177.     case RS_LEN1:
  178.     ptr = &Raux->lenh;
  179.     len = MAX(2,n);
  180.     break;
  181.     case RS_LEN2:
  182.     ptr = &Raux->lenl;
  183.     len = MAX(1,n);
  184.     break;
  185.     case RS_DATA:    /*  need dlen, have len.  Start read request and TO   */
  186.     if (ptr != Raux->data && len)
  187.         CopyMem(ptr, Raux->data, len);
  188.     dblen = len;
  189.     ptr = (ubyte *)Raux->data + dblen;
  190.     len = dlen - len;
  191.     Rto.tr_time.tv_secs = RTimeoutVal / 1000000;    /*  packet to  */
  192.     Rto.tr_time.tv_micro= RTimeoutVal % 1000000;
  193.     SendIO(&Rto);
  194.     Rto_act = 1;
  195.     break;
  196.     }
  197.     do_cmd(-1, NULL, 0);
  198.     NetStartRead(ptr, len);
  199. }
  200.  
  201. /*
  202.  *  WNET:   The last data packet has been sent to the network...  Start a
  203.  *        timeout sequence (1 second).  If this times out we will send
  204.  *        a CHECK packet for all unresolved transmission windows.
  205.  */
  206.  
  207. do_wnet()
  208. {
  209.     if (Wto_act) {
  210.     AbortIO(&Wto);
  211.     WaitIO(&Wto);
  212.     }
  213.     Wto.tr_time.tv_secs = WTimeoutVal / 1000000;
  214.     Wto.tr_time.tv_micro= WTimeoutVal % 1000000;
  215.     SendIO(&Wto);
  216.     Wto_act = 1;
  217. }
  218.  
  219. /*
  220.  *  DO_WUPDATE()
  221.  *
  222.  *  (1) Remove EMPTY windows at head of transmit queue.
  223.  *  (2) Fill up transmit queue with pending requests, if any.
  224.  *
  225.  *  First two bits determine CMD as follows:
  226.  *        0bbbbbbb        DATA        0-127 bytes of DATA
  227.  *        10bbbccc        DATA        0-7 bytes of DATA, ccc=channel
  228.  *                                  command.
  229.  *        11bbbbbb bbbbbbbb    DATA        0-1023 bytes of DATA
  230.  */
  231.  
  232. do_wupdate()
  233. {
  234.     static short XPri;
  235.     int maxpktsize;
  236.     register IOR *ior;
  237.     register PKT *pkt;
  238.     register long len;
  239.  
  240.     while (WPUsed && WPak[0]->state == EMPTY) {
  241.     pkt = WPak[0];
  242.     WPak[0] = WPak[1];
  243.     WPak[1] = WPak[2];
  244.     WPak[2] = WPak[3];
  245.     WPak[3] = pkt;
  246.     --WPUsed;
  247.     ++WPStart;
  248.     }
  249.     if (Restart)
  250.     return(0);
  251.     while (WPUsed != 4 && (ior = (IOR *)RemHead(&TxList))) {
  252.     register long offset = 0;
  253.     if (DDebug)
  254.         printf("Extract Request: %08lx %ld bytes\n", ior, ior->io_Length);
  255.     {
  256.         short npri;
  257.  
  258.         if (ior->io_Command == SCMD_DATA) {
  259.         ubyte chan = (ulong)ior->io_Unit;
  260.         if (Chan[chan].state == CHAN_CLOSE) {   /* channel closed */
  261.             ior->io_Error = 1;
  262.             ReplyMsg(ior);
  263.             continue;
  264.         }
  265.         npri = ior->io_Message.mn_Node.ln_Pri << 2;
  266.         } else {
  267.         npri = XPri;
  268.         }
  269.         if (npri >= XPri)
  270.         XPri = npri;
  271.         else {
  272.         if (XPri - npri > 100)
  273.             XPri -= 10;
  274.         else if (XPri - npri > 50)
  275.             XPri -= 5;
  276.         else
  277.             --XPri;
  278.         }
  279.         maxpktsize = MAXPKT - (XPri - npri);
  280.         if (maxpktsize < MINPKT)
  281.         maxpktsize = MINPKT;
  282.     }
  283.     pkt = WPak[WPUsed];
  284.     pkt->state = READY;
  285.     pkt->sync  = SYNC;
  286.     pkt->ctl   = PKCMD_WRITE | PKF_DATA | ((WPStart + WPUsed)<<5);
  287.     pkt->cchk = (pkt->sync << 1) ^ pkt->ctl;
  288.     for (;;) {
  289.         if (offset > (maxpktsize-8))        /*  not enough room */
  290.         break;
  291.         if (ior->io_Command == SCMD_DATA && (ulong)ior->io_Unit   != WChan) {
  292.         /*  CSWITCH */
  293.         WChan = (ulong)ior->io_Unit;
  294.         pkt->data[offset+0] = 0x80|SCMD_SWITCH|(2<<3);
  295.         pkt->data[offset+1] = WChan >> 8;
  296.         pkt->data[offset+2] = WChan;
  297.         offset += 3;
  298.         }
  299.         len = ior->io_Length - ior->io_Actual;
  300.         if (ior->io_Command == SCMD_DATA) {     /*  DATA OUT   */
  301.         if (offset + len > (maxpktsize-4))
  302.             len = (maxpktsize-4) - offset;
  303.         if (len < 128) {
  304.             pkt->data[offset] = len;
  305.             ++offset;
  306.         } else {
  307.             pkt->data[offset+0] = (len>>8)|0xC0;
  308.             pkt->data[offset+1] = len;
  309.             offset += 2;
  310.         }
  311.         BytesOut += len;
  312.         } else {                    /*    COMMAND OUT */
  313.         pkt->data[offset] = 0x80|ior->io_Command|(len<<3);
  314.         ++offset;
  315.         }
  316.         CopyMem((char *)ior->io_Data + ior->io_Actual, pkt->data + offset, len);
  317.         offset += len;
  318.         ior->io_Actual += len;
  319.         if (ior->io_Actual == ior->io_Length) {
  320.         ReplyMsg(ior);
  321.         ior = (IOR *)RemHead(&TxList);        /*  Next packet     */
  322.         if (ior == NULL)
  323.             break;
  324.         }
  325.     }
  326.     pkt->iolength = offset + OVERHEAD;
  327.     pkt->lenh = offset >> 8;
  328.     pkt->lenl = offset;
  329.     {
  330.         register uword chksum = chkbuf(pkt->data, offset);
  331.         pkt->data[offset+0] = chksum >> 8;
  332.         pkt->data[offset+1] = chksum;
  333.     }
  334.     NetWrite(&pkt->sync, pkt->iolength, 1);
  335.     if (ior) {
  336.         ++ior->io_Message.mn_Node.ln_Pri;
  337.         Enqueue(&TxList, ior);
  338.         --ior->io_Message.mn_Node.ln_Pri;
  339.     }
  340.     ++WPUsed;
  341.     ++PacketsOut;
  342.     ResetIdle();
  343.     break;        /*    One at a time, else would take too long */
  344.     }
  345. }
  346.  
  347. void
  348. dumpcheck(ptr)
  349. register ubyte *ptr;
  350. {
  351.     register short i;
  352.     for (i = 0; i < 8; ++i) {
  353.     if (ptr[i])
  354.         replywindow(i);
  355.     ptr[i] = 0;
  356.     }
  357. }
  358.  
  359. do_cmd(ctl, buf, bytes)
  360. uword ctl;    /*  usually just 8 bits though    */
  361. ubyte *buf;
  362. {
  363.     ubyte window = ctl >> 5;
  364.     ubyte rwindow;
  365.     static ubyte Chk, Chkwin[8];
  366.  
  367.     if (ctl == 0xFFFF) {
  368.     if (Chk) {
  369.         dumpcheck(Chkwin);
  370.         Chk = 0;
  371.     }
  372.     return;
  373.     }
  374.  
  375.     if (PDebug)
  376.     printf("RECV %02x %4ld bytes\n", ctl, bytes);
  377.  
  378.     if ((ctl & PKF_MASK) == PKCMD_CHECK) {
  379.     Chkwin[window] = 1;
  380.     Chk = 1;
  381.     return;
  382.     }
  383.     if (Chk) {
  384.     dumpcheck(Chkwin);
  385.     Chk = 0;
  386.     }
  387.     switch(ctl & PKF_MASK) {
  388.     case PKCMD_WRITE:
  389.     rwindow = (window - RPStart) & 7;
  390.     if (rwindow < 4) {
  391.         CopyMem(buf, RPak[rwindow]->data, bytes);
  392.         RPak[rwindow]->iolength = bytes;
  393.         RPak[rwindow]->state = READY;
  394.         do_rupdate();
  395.     }
  396.     replywindow(window);
  397.     break;
  398.     case PKCMD_ACK:
  399.     rwindow = (window - WPStart) & 7;
  400.     if (rwindow < WPUsed)       /*  mark as sent    */
  401.         WPak[rwindow]->state = EMPTY;
  402.     break;
  403.     case PKCMD_NAK:            /*    resend        */
  404.     rwindow = (window - WPStart) & 7;
  405.     if (rwindow < WPUsed) {     /*  resend          */
  406.         ++PacketsResent;
  407.         NetWrite(&WPak[rwindow]->sync, WPak[rwindow]->iolength, 0);
  408.     } else {
  409.         printf("Soft Error: Illegal NAK: %ld %ld %ld %ld\n",
  410.         window, WPStart, rwindow, WPUsed
  411.         );
  412.     }
  413.     break;
  414.     case PKCMD_RESTART:
  415.     case PKCMD_ACKRSTART:
  416.     if ((ctl & PKF_MASK) == PKCMD_ACKRSTART)
  417.         Restart = 0;
  418.     do_netreset();
  419.     /* RxPtr? */
  420.  
  421.     if ((ctl & PKF_MASK) == PKCMD_RESTART) {
  422.         short len;
  423.         uword chksum;
  424.         static ubyte buf[32];
  425.  
  426.         NetWaitWrite();
  427.         strcpy(buf+5, HostName);
  428.         len = strlen(buf+5)+1;
  429.         buf[0] = SYNC;
  430.         buf[1] = PKCMD_ACKRSTART | PKF_DATA;
  431.         buf[2] = (SYNC << 1) ^ buf[1];
  432.         buf[3] = 0;
  433.         buf[4] = len;
  434.         chksum = chkbuf(buf+5, len);
  435.         buf[5+len] = chksum >> 8;
  436.         buf[6+len] = chksum;
  437.         NetWrite(buf, 7+len, 1);
  438.     }
  439.     /*
  440.     if (bytes)
  441.         setlistenport(buf);
  442.     else
  443.         setlistenport("");
  444.     */
  445.     break;
  446.     }
  447.     do_rupdate();
  448. }
  449.  
  450. do_rupdate()
  451. {
  452.     while (RPak[0]->state == READY) {
  453.     register PKT *pkt = RPak[0];
  454.     register ubyte *ptr = pkt->data;
  455.     register uword len;
  456.     uword iolen = pkt->iolength;
  457.     ubyte cmd;
  458.  
  459.     while (iolen) {
  460.         cmd = SCMD_DATA;
  461.         len = ptr[0];
  462.         ++ptr;
  463.         --iolen;
  464.         if (len >= 128) {
  465.         if (len < 0xC0) {
  466.             cmd = len & 7;
  467.             len = (len >> 3) & 7;
  468.         } else {
  469.             len = ((len << 8) | *ptr) & 0x3FFF;
  470.             ++ptr;
  471.             --iolen;
  472.         }
  473.         }
  474.         iolen -= len;
  475.         if (DDebug)
  476.         printf("RECEIVE CMD %2ld ", cmd);
  477.         do_reccmd(cmd, ptr, len);
  478.         ptr += len;
  479.     }
  480.     RPak[0] = RPak[1];
  481.     RPak[1] = RPak[2];
  482.     RPak[2] = RPak[3];
  483.     RPak[3] = pkt;
  484.     pkt->state = EMPTY;
  485.     ++RPStart;
  486.     }
  487. }
  488.  
  489. do_reccmd(cmd, ptr, len)
  490. ubyte *ptr;
  491. {
  492.     switch(cmd) {
  493.     case SCMD_DATA:    /*  data for channel    */
  494.     if (RChan < MAXCHAN && (Chan[RChan].flags & CHANF_ROK)) {
  495.         register IOR *ior = (IOR *)AllocMem(sizeof(IOR), MEMF_PUBLIC);
  496.         ior->io_Unit = (struct Unit *)RChan;
  497.         ior->io_Data = (APTR)AllocMem(len, MEMF_PUBLIC);
  498.         ior->io_Length = len;
  499.         ior->io_Actual = 0;
  500.         CopyMem(ptr, ior->io_Data, len);
  501.         ior->io_Message.mn_Node.ln_Name = (char *)PKT_REQ;
  502.         ior->io_Command = DNCMD_WRITE;
  503.         ior->io_Message.mn_ReplyPort = IOSink;
  504.         PutMsg(Chan[RChan].port, ior);
  505.         BytesIn += len;
  506.         ResetIdle();    /*  not idle, have received data    */
  507.     }
  508.     break;
  509.     case SCMD_SWITCH:
  510.     RChan = (ptr[0]<<8)|ptr[1];
  511.     break;
  512.     case SCMD_OPEN:
  513.     {
  514.         register COPEN *cop = (COPEN *)ptr;
  515.         PORT *port;
  516.         CACKCMD ack;
  517.         char buf[32];
  518.         uword chan = (cop->chanh << 8) | cop->chanl;
  519.         uword portnum = (cop->porth << 8) | cop->portl;
  520.  
  521.         ack.chanh = cop->chanh;
  522.         ack.chanl = cop->chanl;
  523.         ack.error = 0;
  524.  
  525.         if (chan >= MAXCHAN || Chan[chan].state) {
  526.         ack.error = 33;     /* magic */
  527.         WriteStream(SCMD_ACKCMD, &ack, sizeof(CACKCMD), -1);
  528.         break;
  529.         }
  530.         sprintf(buf, "DNET.PORT.%ld", portnum);
  531.         if ((port = (PORT *)FindPort(buf)) == NULL) {
  532.         RunServer(portnum);
  533.         if ((port = (PORT *)FindPort(buf)) == NULL) {
  534.             ack.error = 2;
  535.             WriteStream(SCMD_ACKCMD, &ack, sizeof(CACKCMD), -1);
  536.             break;
  537.         }
  538.         }
  539.         /*    ln_Name type of 0 causes reply to go to DNetPort    */
  540.         WritePort(port, DNCMD_SOPEN, NULL, 0, 0, chan);
  541.         Chan[chan].state = CHAN_ROPEN;
  542.         Chan[chan].pri = cop->pri;
  543.     }
  544.     break;
  545.     case SCMD_CLOSE:    /*  receive close   */
  546.     {
  547.         register CCLOSE *clo = (CCLOSE *)ptr;
  548.         uword chan = (clo->chanh<<8)|clo->chanl;
  549.  
  550.         if (DDebug)
  551.         printf("Remote close, chan %d state %d\n", chan, Chan[chan].state);
  552.  
  553.         if (chan >= MAXCHAN || Chan[chan].state == CHAN_FREE) {
  554.         break;
  555.         }
  556.         Chan[chan].state = CHAN_CLOSE;
  557.         Chan[chan].flags |= CHANF_RCLOSE;
  558.         Chan[chan].flags &= ~(CHANF_ROK|CHANF_WOK);
  559.         if (Chan[chan].flags & CHANF_LCLOSE) {
  560.         if (DDebug)
  561.             printf("Local already closed, reply %08lx\n", Chan[chan].ior);
  562.         Chan[chan].state = CHAN_FREE;
  563.         ReplyMsg(Chan[chan].ior);
  564.         Chan[chan].ior = NULL;
  565.         } else {    /*  send EOF  */
  566.         if (DDebug)
  567.             printf("Local not already closed\n");
  568.         WritePort(Chan[chan].port, DNCMD_CLOSE, NULL, 0, PKT_REQ, chan);
  569.         }
  570.     }
  571.     break;
  572.     case SCMD_ACKCMD:    /*  acknowledge of my open    */
  573.     {
  574.         register CACKCMD *cack = (CACKCMD *)ptr;
  575.         uword chan = (cack->chanh<<8)|cack->chanl;
  576.         if (chan >= MAXCHAN || Chan[chan].state != CHAN_LOPEN) {
  577.         break;
  578.         }
  579.         /*
  580.          *    Channel in use (collision), try again
  581.          */
  582.         if (cack->error == 33) {
  583.         uword newchan = alloc_channel();
  584.         COPEN co;
  585.         if (newchan < MAXCHAN) {
  586.             Chan[newchan] = Chan[chan];
  587.             Chan[chan].state = CHAN_FREE;
  588.             Chan[chan].ior = NULL;
  589.             co.chanh = newchan >> 8;
  590.             co.chanl = newchan;
  591.             co.porth = (ulong)Chan[newchan].ior->io_Unit >> 8;
  592.             co.portl = (ulong)Chan[newchan].ior->io_Unit;
  593.             co.error = 0;
  594.             co.pri = Chan[chan].pri;
  595.             WriteStream(SCMD_OPEN, &co, sizeof(COPEN), chan);
  596.             break;
  597.         }
  598.         }
  599.         if (cack->error) {
  600.         Chan[chan].state = CHAN_FREE;
  601.         Chan[chan].ior->io_Error = cack->error;
  602.         ReplyMsg(Chan[chan].ior);
  603.         Chan[chan].ior = NULL;
  604.         } else {
  605.         Chan[chan].state = CHAN_OPEN;
  606.         Chan[chan].ior->io_Error = 0;
  607.         Chan[chan].ior->io_Unit = (struct Unit *)chan;
  608.         Chan[chan].flags = CHANF_ROK|CHANF_WOK;
  609.         ReplyMsg(Chan[chan].ior);
  610.         Chan[chan].ior = NULL;
  611.         }
  612.     }
  613.     break;
  614.     case SCMD_EOFCMD:    /*  EOF on channel        */
  615.     {
  616.         register CEOFCMD *eof = (CEOFCMD *)ptr;
  617.         uword chan = (eof->chanh<<8)|eof->chanl;
  618.  
  619.         if (chan < MAXCHAN && Chan[chan].state == CHAN_OPEN) {
  620.         Chan[chan].flags &= ~eof->flags;
  621.         if (eof->flags & CHANF_ROK)
  622.             WritePort(Chan[chan].port, DNCMD_EOF, NULL, 0, PKT_REQ, chan);
  623.         } else {
  624.         printf("SCMD_EOFCMD: Error chan %ld  state %ld\n",
  625.             chan, Chan[chan].state
  626.         );
  627.         }
  628.     }
  629.     break;
  630.     case SCMD_IOCTL:
  631.     {
  632.         register CIOCTL *cio = (CIOCTL *)ptr;
  633.         uword chan = (cio->chanh<<8)|cio->chanl;
  634.  
  635.         if (Chan[chan].state == CHAN_OPEN)
  636.         WritePort(Chan[chan].port, DNCMD_IOCTL, cio, sizeof(*cio), PKT_REQ, chan);
  637.     }
  638.     break;
  639.     default:
  640.     if (DDebug)
  641.         printf("BAD SCMD, %ld\n", cmd);
  642.     break;
  643.     }
  644. }
  645.  
  646. replywindow(window)
  647. {
  648.     ubyte rwindow = (window - RPStart) & 7;
  649.  
  650.     if (rwindow >= 4 || RPak[rwindow]->state == READY) {  /* data ready */
  651.     NetWrite(AckPkt[window], 3, 0);
  652.     ++PacketsIn;
  653.     } else {
  654.     NetWrite(NakPkt[window], 3, 0);
  655.     ++PacketsNakd;
  656.     }
  657. }
  658.  
  659.