home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 200-299 / ff220.lzh / DNet / dnet / control.c < prev    next >
C/C++ Source or Header  |  1989-06-04  |  16KB  |  716 lines

  1.  
  2. /*
  3.  *  CONTROL.C
  4.  *
  5.  *    DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
  6.  *
  7.  *  -handle various actions:
  8.  *    RTO    - read timeout
  9.  *    WTO    - write timeout (a packet not acked)
  10.  *    RNET    - state machine for raw receive packet
  11.  *    WNET    - starts timeout sequence if Write occured
  12.  *    WUPDATE - update write windows, send packets
  13.  *    RUPDATE - execute received packets in correct sequence
  14.  *    RECCMD    - execute decoded SCMD commands obtained from received
  15.  *            packets.
  16.  */
  17.  
  18. #include "dnet.h"
  19. #include <stdio.h>
  20.  
  21. extern void do_cmd();
  22.  
  23. /*
  24.  *  RTO:    read timeout.   Timeout occured while waiting for the rest of
  25.  *        a packet.  Reset state and restart read.
  26.  */
  27.  
  28. do_rto()
  29. {
  30.     RState = 0;
  31.     RcvData = 0;
  32.     if (DDebug)
  33.     fprintf(stderr, "RTO\n");
  34. }
  35.  
  36. /*
  37.  *  WTO:    Write timeout (unresolved output windows exist).  Resend a CHECK
  38.  *        command for all unresolved (READY) output windows
  39.  */
  40.  
  41. void
  42. do_wto()
  43. {
  44.     register short i;
  45.     register PKT *pkt;
  46.     register uword len = 0;
  47.  
  48.     if (DDebug)
  49.     fprintf(stderr, "WTO\n");
  50.     if (Restart) {
  51.     WCBuf[0] = SYNC;
  52.     WCBuf[1] = PKCMD_RESTART;
  53.     WCBuf[2] = (WCBuf[0]<<1)^WCBuf[1];
  54.     NetWrite(WCBuf, 3, 1);
  55.     WTimeout(WTIME);
  56.     return;
  57.     }
  58.     for (i = 0; i < WPUsed; ++i) {
  59.     pkt = WPak[i];
  60.     if (pkt->state == READY) {  /*  send check  */
  61.         WCBuf[len+0] = SYNC;
  62.         WCBuf[len+1] = PKCMD_CHECK | ((WPStart + i) << 5);
  63.         WCBuf[len+2] = (WCBuf[len+0] << 1) ^ WCBuf[len+1];
  64.         len += 3;
  65.     }
  66.     }
  67.     if (len) {
  68.     NetWrite(WCBuf, len, 1);
  69.     WTimeout(WTIME);
  70.     }
  71. }
  72.  
  73. /*
  74.  *  RNET:   Receved data ready from network.  The RNet request will
  75.  *        automagically be reissued on return.
  76.  */
  77.  
  78. do_rnet()
  79. {
  80.     register ubyte *ptr = RcvBuf;
  81.     register long len  = RcvData;
  82.     long lrd = len;
  83.     long mark;
  84.     static uword dlen;
  85.     static uword dblen;
  86.     static ubyte dctl;
  87.  
  88.     if (DDebug)
  89.     fprintf(stderr, "NETREAD %08lx %ld\n", RcvBuf, RcvData);
  90.     while (len) {
  91.     switch(RState) {
  92.     case RS_SYNC:
  93.         --len;
  94.         if (*ptr++ == SYNC)
  95.         RState = RS_CTL;
  96.         break;
  97.     case RS_CTL:
  98.         --len;
  99.         dctl = *ptr++;
  100.         RState = RS_CCHK;
  101.         break;
  102.     case RS_CCHK:
  103.         /* warning: (ubyte) cast not implemented properly for some
  104.          * compilers
  105.          */
  106.         if ((((SYNC<<1)^dctl) & 0xFF) == *ptr) {
  107.         ++ptr;
  108.         --len;
  109.         if (dctl & PKF_DATA) {
  110.             RState = RS_LEN1;
  111.             break;
  112.         }
  113.         RState = RS_SYNC;
  114.         if (DDebug)
  115.             fprintf(stderr, "DO COMMAND (0DATA) 0x%02lx\n", dctl);
  116.         do_cmd(dctl, NULL, 0);
  117.         } else {
  118.         if (DDebug)
  119.             fprintf(stderr, "RS_CCHK FAILED\n");
  120.         if (len) {      /*  Resync at earliest point    */
  121.             ++len;
  122.             --ptr;
  123.         }
  124.         }
  125.         RState = RS_SYNC;
  126.         break;
  127.     case RS_LEN1:
  128.         dlen = *ptr;
  129.         ++ptr;
  130.         --len;
  131.         RState = RS_LEN2;
  132.         break;
  133.     case RS_LEN2:
  134.         dlen = (dlen << 8) + *ptr + 2;
  135.         ++ptr;
  136.         --len;
  137.         if (dlen > MAXPKT+2) {
  138.         if (DDebug)
  139.             fprintf(stderr,"RAW DATA: LENGTH FAILURE (cmd %02x) %ld\n",
  140.             dctl, dlen
  141.             );
  142.         RState = RS_SYNC;
  143.         break;
  144.         }
  145.         RState = RS_DATA;
  146.         break;
  147.     case RS_DATA:
  148.         if (DDebug)
  149.         fprintf(stderr, "RS_DATA->");
  150.         if (dlen <= len) {
  151.         register uword chk = chkbuf(ptr, dlen - 2);
  152.         if ((chk >> 8) == ptr[dlen-2] && (chk & 0xFF) == ptr[dlen-1]) {
  153.             if (DDebug)
  154.             fprintf(stderr, "DO MPX (%ld DATA) 0x%02lx\n",
  155.                 dlen-2, dctl
  156.             );
  157.             do_cmd(dctl, ptr, dlen-2);
  158.             len -= dlen;
  159.             ptr += dlen;
  160.         } else {
  161.             if (DDebug)
  162.             fprintf(stderr, "RS_DATA: FAILED\n");
  163.         }
  164.         RState = RS_SYNC;
  165.         } else {
  166.         if (DDebug)
  167.             fprintf(stderr, "RS_DATA: INCOMPLETE READ\n");
  168.         goto break2;    /*  incomplete read  */
  169.         }
  170.         break;
  171.     }
  172.     }
  173. break2:
  174.     switch(RState) {
  175.     case RS_DATA:
  176.     RExpect = dlen - len;
  177.     if (RExpect < 0) {
  178.         fprintf(stderr, "panic, RExpect = %ld\n", RExpect);
  179.         exit(1);
  180.     }
  181.     break;
  182.     default:
  183.     RExpect = 0;
  184.     break;
  185.     }
  186.     do_cmd(-1, NULL, 0);
  187.     mark = len;
  188.     {
  189.     if (len && ptr != RcvBuf)
  190.         bcopy(ptr, RcvBuf, len);
  191.     RcvData = len;
  192.     }
  193.     if (RState == RS_DATA) {
  194.     ;
  195.     /*
  196.      *RDisable();
  197.      *RTimeout(RTIME/1000);
  198.      */
  199.     }
  200.     /*    REMOVED 21 Sept 1988
  201.      * do_wupdate();
  202.      */
  203.     return(mark);
  204. }
  205.  
  206. /*
  207.  *  WNET:   The last data packet has been sent to the network...  Start a
  208.  *        timeout sequence (1 second).  If this times out we will send
  209.  *        a CHECK packet for all unresolved transmission windows.
  210.  */
  211.  
  212. do_wnet()
  213. {
  214.     if (DidWrite) {
  215.     DidWrite = 0;
  216.     if (DDebug)
  217.         fprintf(stderr, "WNET-STARTTO\n");
  218.     WTimeout(WTIME);
  219.     } else {
  220.     if (DDebug)
  221.         fprintf(stderr, "WNET-NOP\n");
  222.     }
  223. }
  224.  
  225. /*
  226.  *  DO_WUPDATE()
  227.  *
  228.  *  (1) Remove EMPTY windows at head of transmit queue.
  229.  *  (2) Fill up transmit queue with pending requests, if any.
  230.  *
  231.  *  First two bits determine CMD as follows:
  232.  *        0bbbbbbb        DATA        0-127 bytes of DATA
  233.  *        10bbbccc        DATA        0-7 bytes of DATA, ccc=channel
  234.  *                                  command.
  235.  *        11bbbbbb bbbbbbbb    DATA        0-1023 bytes of DATA
  236.  */
  237.  
  238. do_wupdate()
  239. {
  240.     static short XPri;
  241.     int maxpktsize;
  242.     register XIOR *ior;
  243.     register PKT *pkt;
  244.     register long len;
  245.  
  246.     while (WPUsed && WPak[0]->state == EMPTY) {
  247.     pkt = WPak[0];
  248.     WPak[0] = WPak[1];
  249.     WPak[1] = WPak[2];
  250.     WPak[2] = WPak[3];
  251.     WPak[3] = pkt;
  252.     --WPUsed;
  253.     ++WPStart;
  254.     }
  255.     if (Restart)
  256.     return(0);
  257.  
  258.     while (WPUsed != 4 && (ior = (XIOR *)RemHead(&TxList))) {
  259.     register long offset = 0;
  260.  
  261.     {
  262.         short npri;
  263.  
  264.         if (ior->io_Command == SCMD_DATA)
  265.         npri = ior->io_Pri << 2;
  266.         else
  267.         npri = XPri;
  268.         if (npri >= XPri)  {
  269.         XPri = npri;
  270.         } else {
  271.         if (XPri - npri > 100)
  272.             XPri -= 10;
  273.         else if (XPri - npri > 50)
  274.             XPri -= 5;
  275.         else
  276.             --XPri;
  277.         }
  278.         maxpktsize = MAXPKT - (XPri - npri);
  279.         if (DDebug)
  280.         fprintf(stderr, "**MAXPKTSIZE = %ld  XPri %ld npri %ld\n",
  281.             maxpktsize, XPri, npri
  282.         );
  283.         if (maxpktsize < MINPKT)
  284.         maxpktsize = MINPKT;
  285.     }
  286.  
  287.     pkt = WPak[WPUsed];
  288.     pkt->state = READY;
  289.     pkt->sync  = SYNC;
  290.     pkt->ctl   = PKCMD_WRITE | PKF_DATA | ((WPStart + WPUsed)<<5);
  291.     pkt->cchk = (pkt->sync << 1) ^ pkt->ctl;
  292.     for (;;) {
  293.         if (offset > (maxpktsize-8))                /*  not enough room */
  294.         break;
  295.         if (ior->io_Command == SCMD_DATA && ior->io_Channel != WChan) {
  296.         /*  CSWITCH */
  297.         WChan = ior->io_Channel;
  298.         pkt->data[offset+0] = 0x80|SCMD_SWITCH|(2<<3);
  299.         pkt->data[offset+1] = WChan >> 8;
  300.         pkt->data[offset+2] = WChan;
  301.         offset += 3;
  302.         if (DDebug)
  303.             fprintf(stderr, "SEND SWITCH %ld\n", WChan);
  304.         }
  305.         len = ior->io_Length - ior->io_Actual;
  306.         if (ior->io_Command == SCMD_DATA) {     /*  DATA    */
  307.         if (DDebug)
  308.             fprintf(stderr, "SEND DATA %ld bytes\n", len);
  309.         if (offset + len > (maxpktsize-4))
  310.             len = (maxpktsize-4) - offset;
  311.         if (len < 128) {
  312.             pkt->data[offset] = len;
  313.             ++offset;
  314.         } else {
  315.             pkt->data[offset+0] = (len>>8)|0xC0;
  316.             pkt->data[offset+1] = len;
  317.             offset += 2;
  318.         }
  319.         } else {                    /*    COMMAND */
  320.         pkt->data[offset] = 0x80|ior->io_Command|(len<<3);
  321.         ++offset;
  322.         }
  323.         bcopy((char *)ior->io_Data+ior->io_Actual,pkt->data+offset,len);
  324.         offset += len;
  325.         ior->io_Actual += len;
  326.         if (ior->io_Actual == ior->io_Length) {
  327.         free(ior->io_Data);
  328.         free(ior);
  329.         ior = (XIOR *)RemHead(&TxList);          /* Next packet      */
  330.         if (ior == NULL)
  331.             break;
  332.         }
  333.     }
  334.     pkt->iolength = offset + OVERHEAD;
  335.     pkt->lenh = offset >> 8;
  336.     pkt->lenl = offset;
  337.     {
  338.         register uword chksum = chkbuf(pkt->data, offset);
  339.         pkt->data[offset+0] = chksum >> 8;
  340.         pkt->data[offset+1] = chksum;
  341.     }
  342.     NetWrite(&pkt->sync, pkt->iolength, 1);
  343.     if (ior) {
  344.         ++ior->io_Pri;
  345.         Enqueue(&TxList, ior);
  346.         --ior->io_Pri;
  347.     }
  348.     ++WPUsed;
  349.     }
  350. }
  351.  
  352. void
  353. dumpcheck(ptr)
  354. register ubyte *ptr;
  355. {
  356.     register short i;
  357.     for (i = 0;i < 8; ++i) {
  358.     if (ptr[i])
  359.         replywindow(i);
  360.     ptr[i] = 0;
  361.     }
  362. }
  363.  
  364. void
  365. do_cmd(ctl, buf, bytes)
  366. short ctl;    /* actually a ubyte */
  367. ubyte *buf;
  368. {
  369.     ubyte window = ctl >> 5;
  370.     ubyte rwindow;
  371.     static ubyte Chk, Chkwin[8];    /* remember window checks */
  372.  
  373.     if (ctl == -1)  {                           /* end of sequence */
  374.     dumpcheck(Chkwin);
  375.     Chk = 0;
  376.     return;
  377.     }
  378.     if ((ctl & PKF_MASK) == PKCMD_CHECK) {      /* CHECK packet    */
  379.     Chkwin[window] = 1;
  380.     Chk = 1;
  381.     return;
  382.     }
  383.     if (Chk) {                                  /* NON-CHECK packet*/
  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.         bcopy(buf, RPak[rwindow]->data, bytes);
  392.         RPak[rwindow]->iolength = bytes;
  393.         RPak[rwindow]->state = READY;
  394.         if (rwindow == 0)
  395.         do_rupdate();
  396.     }
  397.     replywindow(window);
  398.     break;
  399.     case PKCMD_ACK:
  400.     rwindow = (window - WPStart) & 7;
  401.     if (rwindow < WPUsed)       /*  mark as sent    */
  402.         WPak[rwindow]->state = EMPTY;
  403.     break;
  404.     case PKCMD_NAK:            /*    resend        */
  405.     rwindow = (window - WPStart) & 7;
  406.     if (rwindow < WPUsed) {     /*  resend          */
  407.         NetWrite(&WPak[rwindow]->sync, WPak[rwindow]->iolength, 0);
  408.     } else {
  409.         fprintf(stderr, "Soft Error: Illegal NAK\n");
  410.     }
  411.     break;
  412.     case PKCMD_ACKRSTART:
  413.     case PKCMD_RESTART:
  414.     {
  415.         uword chan;
  416.         uword chksum;
  417.         int len;
  418.         int fd;
  419.  
  420.         if ((ctl & PKF_MASK) == PKCMD_ACKRSTART)
  421.         Restart = 0;
  422.         do_netreset();
  423.         if ((ctl & PKF_MASK) == PKCMD_RESTART) {
  424.         gethostname(WCBuf+5, sizeof(WCBuf)-5-3);
  425.         len = strlen(WCBuf+5)+1;
  426.         WCBuf[0] = SYNC;
  427.         WCBuf[1] = PKCMD_ACKRSTART | PKF_DATA;
  428.         WCBuf[2] = (SYNC << 1) ^ WCBuf[1];
  429.         WCBuf[3] = 0;
  430.         WCBuf[4] = len;
  431.         chksum = chkbuf(WCBuf+5, len);
  432.         WCBuf[5+len] = chksum >> 8;
  433.         WCBuf[6+len] = chksum;
  434.         NetWrite(WCBuf, 7+len, 1);
  435.         }
  436.         if (bytes)
  437.         setlistenport(buf);
  438.         else
  439.         setlistenport("");
  440.         do_wupdate();
  441.         WTimeout(WTIME);
  442.     }
  443.     break;
  444.     }
  445.     do_rupdate();
  446. }
  447.  
  448. do_rupdate()
  449. {
  450.     while (RPak[0]->state == READY) {
  451.     register PKT *pkt = RPak[0];
  452.     register ubyte *ptr = pkt->data;
  453.     register uword len;
  454.     uword iolen = pkt->iolength;
  455.     ubyte cmd;
  456.  
  457.     if (DDebug)
  458.         fprintf(stderr, "Interpret Received Commands\n");
  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.         fprintf(stderr, " MPXCMD %ld (%ld bytes)\n", cmd, len);
  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:
  494.     if (RChan < MAXCHAN && (Chan[RChan].flags & CHANF_ROK))
  495.         gwrite(Chan[RChan].fd, ptr, len);
  496.     break;
  497.     case SCMD_SWITCH:
  498.     RChan = (ptr[0]<<8)|ptr[1];
  499.     break;
  500.     case SCMD_OPEN:
  501.     {
  502.         register COPEN *cop = (COPEN *)ptr;
  503.         CACKCMD ack;
  504.         uword chan = (cop->chanh << 8) | cop->chanl;
  505.  
  506.         ack.chanh = cop->chanh;
  507.         ack.chanl = cop->chanl;
  508.         ack.error = 0;
  509.  
  510.         if (chan >= MAXCHAN || Chan[chan].state) {
  511.         ack.error = 33;     /* magic */
  512.         WriteStream(SCMD_ACKCMD, &ack, sizeof(CACKCMD), chan);
  513.         break;
  514.         }
  515.         {
  516.         int error;
  517.         int s;
  518.         uword port = (cop->porth<<8)|cop->portl;
  519.  
  520.         if (isinternalport(port)) {
  521.             error = iconnect(&s, port);
  522.         } else {
  523.             struct sockaddr sa;
  524.             s = socket(PF_UNIX, SOCK_STREAM, 0);
  525.             if (DDebug)
  526.             fprintf(stderr, " REC OPEN, CONNECTING ch%d po%d\n",
  527.                 chan, port
  528.             );
  529.             sa.sa_family = AF_INET;
  530.             sprintf(sa.sa_data,".PORT.%ld", port);
  531.             error = connect(s, &sa, sizeof(sa));
  532.             if (error < 0) {
  533.             startserver(port);
  534.             error = connect(s, &sa, sizeof(sa));
  535.             }
  536.             if (DDebug)
  537.             fprintf(stderr, " CONNECTED err=%ld\n", error);
  538.         }
  539.         if (error < 0) {
  540.             ack.error = 2;
  541.         } else {
  542.             extern void do_open();
  543.             fcntl(s, F_SETFL, FNDELAY);
  544.             Chan[chan].state = CHAN_OPEN;
  545.             Chan[chan].flags = CHANF_ROK|CHANF_WOK;
  546.             Chan[chan].fd = s;
  547.             Chan[chan].pri= cop->pri;
  548.             FD_SET(s, &Fdread);
  549.             FD_SET(s, &Fdexcept);
  550.             FdChan[s] = chan;
  551.             Fdstate[s] = do_open;
  552.         }
  553.         WriteStream(SCMD_ACKCMD, &ack, sizeof(CACKCMD), -1);
  554.         }
  555.     }
  556.     break;
  557.     case SCMD_CLOSE:    /*  receive close   */
  558.     {
  559.         extern void nop();
  560.         register CCLOSE *clo = (CCLOSE *)ptr;
  561.         uword chan = (clo->chanh<<8)|clo->chanl;
  562.         int fd = Chan[chan].fd;
  563.  
  564.         if (DDebug)
  565.         fprintf(stderr, " SCMD_CLOSE\n");
  566.         if (chan >= MAXCHAN || Chan[chan].state == CHAN_FREE)
  567.         break;
  568.         /*
  569.         Chan[chan].state = CHAN_CLOSE;
  570.         Chan[chan].flags |= CHANF_RCLOSE;
  571.         */
  572.         Chan[chan].flags &= ~(CHANF_ROK|CHANF_WOK);
  573.         FD_CLR(fd, &Fdread);
  574.         FD_CLR(fd, &Fdexcept);
  575.         Chan[chan].state = CHAN_FREE;
  576.         Chan[chan].fd = -1;
  577.         Fdstate[fd] = nop;
  578.         close(fd);
  579.         ClearChan(&TxList, chan, 0);
  580.  
  581.         if (Chan[chan].flags & CHANF_LCLOSE) {
  582.         if (DDebug)
  583.             fprintf(stderr," REMOTE CLOSE %ld, LOCAL ALREADY CLOSED\n",
  584.             fd
  585.             );
  586.         } else {
  587.         CCLOSE cc;
  588.         char dummy;
  589.         cc.chanh = chan >> 8;
  590.         cc.chanl = chan;
  591.         WriteStream(SCMD_CLOSE, &cc, sizeof(CCLOSE), chan);
  592.         /*
  593.         shutdown(Chan[chan].fd, 2);
  594.         write(Chan[chan].fd, &dummy, 0);
  595.         */
  596.         if (DDebug)
  597.             fprintf(stderr," REMOTE CLOSE %ld, LOCAL NOT YET CLOSED\n",
  598.             fd
  599.             );
  600.         }
  601.     }
  602.     break;
  603.     case SCMD_ACKCMD:    /*  acknowledge of my open    */
  604.     {
  605.         register CACKCMD *cack = (CACKCMD *)ptr;
  606.         uword chan = (cack->chanh<<8)|cack->chanl;
  607.         if (chan >= MAXCHAN || Chan[chan].state != CHAN_LOPEN)
  608.         break;
  609.         if (DDebug)
  610.         fprintf(stderr, "ackerr = %ld\n", cack->error);
  611.         if (cack->error == 33) {
  612.         uword newchan = alloc_channel();
  613.         COPEN co;
  614.         if (newchan < MAXCHAN) {
  615.             Chan[newchan] = Chan[chan];
  616.             Chan[chan].state = CHAN_FREE;
  617.             Chan[chan].fd = -1;
  618.             co.chanh = newchan >> 8;
  619.             co.chanl = newchan;
  620.             co.porth = Chan[chan].port >> 8;
  621.             co.portl = Chan[chan].port;
  622.             co.error = 0;
  623.             co.pri   = Chan[chan].pri;
  624.             WriteStream(SCMD_OPEN, &co, sizeof(COPEN), newchan);
  625.             break;
  626.         }
  627.         }
  628.         if (cack->error) {
  629.         extern void nop();
  630.         ubyte error = cack->error;
  631.         int fd = Chan[chan].fd;
  632.  
  633.         gwrite(fd, &error, 1);
  634.         Fdstate[fd] = nop;
  635.         Chan[chan].fd = -1;
  636.         Chan[chan].state = CHAN_FREE;
  637.         FD_CLR(fd, &Fdread);
  638.         FD_CLR(fd, &Fdexcept);
  639.         close(fd);
  640.         } else {
  641.         ubyte error = 0;
  642.         extern void do_open();
  643.         gwrite(Chan[chan].fd, &error, 1);
  644.         Chan[chan].state = CHAN_OPEN;
  645.         Chan[chan].flags = CHANF_ROK|CHANF_WOK;
  646.         Fdstate[Chan[chan].fd] = do_open;
  647.         }
  648.     }
  649.     break;
  650.     case SCMD_EOFCMD:    /*  EOF on channel        */
  651.     {
  652.         register CEOFCMD *eof = (CEOFCMD *)ptr;
  653.         uword chan = (eof->chanh<<8)|eof->chanl;
  654.  
  655.         if (chan < MAXCHAN && Chan[chan].state == CHAN_OPEN) {
  656.         Chan[chan].flags &= ~eof->flags;
  657.         if (eof->flags & CHANF_ROK) {
  658.             char dummy;
  659.             shutdown(Chan[chan].fd, 1);
  660.             write(Chan[chan].fd, &dummy, 0);
  661.         }
  662.         }
  663.     }
  664.     break;
  665.     case SCMD_QUIT:
  666.     dneterror("QUIT");
  667.     break;
  668.     case SCMD_IOCTL:
  669.     {
  670.         register CIOCTL *cio = (CIOCTL *)ptr;
  671.         uword chan = (cio->chanh<<8)|cio->chanl;
  672.  
  673.         if (chan < MAXCHAN && Chan[chan].state == CHAN_OPEN) {
  674.         switch(cio->cmd) {
  675.         case CIO_SETROWS:
  676.             isetrows(Chan[chan].fd, (cio->valh<<8)|cio->vall);
  677.             break;
  678.         case CIO_SETCOLS:
  679.             isetcols(Chan[chan].fd, (cio->valh<<8)|cio->vall);
  680.             break;
  681.         case CIO_STOP:
  682.             break;
  683.         case CIO_START:
  684.             break;
  685.         case CIO_FLUSH:
  686.             ClearChan(&TxList, chan, 0);
  687.             break;
  688.         }
  689.         }
  690.     }
  691.     break;
  692.     default:
  693.     break;
  694.     }
  695. }
  696.  
  697. replywindow(window)
  698. {
  699.     ubyte rwindow = (window - RPStart) & 7;
  700.     WCBuf[0] = SYNC;
  701.  
  702.     if (DDebug) {
  703.     if (rwindow >= 4 || RPak[rwindow]->state == READY)
  704.         fprintf(stderr, " ACK WINDOW %ld\n", window);
  705.     else
  706.         fprintf(stderr, " NAK WINDOW %ld\n", window);
  707.     }
  708.     if (rwindow >= 4 || RPak[rwindow]->state == READY)  /* data ready */
  709.     WCBuf[1] = PKCMD_ACK | (window << 5);           /* ack it     */
  710.     else
  711.     WCBuf[1] = PKCMD_NAK | (window << 5);           /* nack it    */
  712.     WCBuf[2] = (SYNC << 1) ^ WCBuf[1];
  713.     NetWrite(WCBuf, 3, 0);
  714. }
  715.  
  716.