V8/usr/sys/chaosld/chaos_devi.BAK

/*
 *             C H A O S _ D E V I C E
 *
 * Chaosnet pseudo-device.  This is the multiplexer/demultiplexer
 * used in conjunction with the Chaosnet line discipline.
 *
 *
 * (c) Copyright 1985  Nirvonics, Inc.
 *
 * Written by Kurt Gollhardt
 * Last update Thu Mar 14 16:03:34 1985
 *
 * This software is the property of Nirvonics, Inc.
 * All rights reserved.
 *
 */

#include "ch.h"
#if NCH > 0
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/stream.h"
#include "../h/ioctl.h"
#include "../h/ttyld.h"
#include "../h/buf.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../chaosld/types.h"
#include "../chaosld/constants.h"
#include "../chaosld/globals.h"

#define CHIOPRIO    (PZERO+1)      /* Just interruptible */

int NCh = NCH; /* For peeking */
int NChopen;

int  nodev(), chdopen(), chdclose(), chdput(), chdosrv();
static struct qinit chdrinit = { nodev, NULL, chdopen, chdclose, 0, 0 },
              chdwinit = { chdput, chdosrv, chdopen, chdclose, 1024, 129 };
struct streamtab chdinfo = { &chdrinit, &chdwinit };


chdopen(q, dev)
     register struct queue    *q;
     dev_t                    dev;
{
     dev = minor(dev);

     if (ChaosQ == (struct queue *)0)   /* Make sure line-discipline is on */
          return 0;
     if (dev > NCH || Chconn[dev] != NOCONN)
          return 0;
     if ((Chconn[dev] = new_conn(dev)) == NOCONN)
          return 0;

     NChopen++;
     Chconn[dev]->cn_rdq = q;
     Chconn[dev]->cn_state |= CSSTARTING;
     q->ptr = WR(q)->ptr = (caddr_t)Chconn[dev];
     q->flag |= QDELIM;
     WR(q)->flag |= QNOENB|QBIGB;

     if (!Chtimer)
	  ch_timer();

     return 1;
}

chdclose(q)
     struct queue   *q;
{
     register struct connection    *conn;
     register struct packet   *pkt;
     int       ps;

     if ((conn = (struct connection *)q->ptr) == NOCONN)
          return;
     conn->cn_time = Chclock;

     switch (conn->cn_mode) {
          case CHSTREAM:
	       ps = spl6();
               if (setjmp(u.u_qsav)) {
                    if ((pkt = new_packet()) != NOPKT) {
                         pkt->pk_op = CLSOP;
	                 append_packet(pkt, "User interrupted", 16);
	            }
                    chld_close(conn, pkt);
                    goto shut;
               }
               if (conn->cn_flags & CHWRITER) {
                    /*
                     * We set this flag telling the interrupt time
                     * receiver to abort the connection if any new packets
                     * arrive.
                     */
                    conn->cn_sflags |= CHCLOSING;
                    /*
                     * Closing a stream transmitter involves flushing
                     * the last packet, sending an EOF and waiting for
                     * it to be acknowledged.  If the connection was
                     * bidirectional, the reader should have already
                     * read until EOF if everything is to be closed
                     * cleanly.
                     */
                    if (conn->cn_state == CSOPEN ||
                              conn->cn_state == CSRFCRCVD) {
                         if (conn->cn_toutput) {
                              chld_write(conn, conn->cn_toutput);
			      conn->cn_toutput = NULL;
                         }
                         if (conn->cn_state == CSOPEN)
                              chld_eof(conn);
                    }
                    while (!chtempty(conn)) {
                         conn->cn_sflags |= CHOWAIT;
                         sleep((caddr_t)&conn->cn_thead, CHIOPRIO);
                    }
		    if (conn->cn_flags & CHREADER) {
		         if (conn->cn_flags & CHSERVER)
			      chld_eof(conn);
                         else
			      goto close_wait;
                    }
               } else if (conn->cn_state == CSOPEN) {
                    /*
                     * If we are only reading then we should read the EOF
                     * before closing and wait for the other end to close.
                     */
close_wait:
                    if (conn->cn_flags & CHEOFSEEN)
                         while (conn->cn_state == CSOPEN)
                              sleep((caddr_t)conn, CHIOPRIO);
               }
               splx(ps);
               /* Fall into... */
          case CHRECORD: /* Record oriented close is just sending a CLOSE */
               if (conn->cn_state == CSOPEN) {
                    if ((pkt = new_packet()) != NOPKT)
                         pkt->pk_op = CLSOP;
                    chld_close(conn, pkt);
               }
     }
shut:
     chld_close(conn, NOPKT);

     free_conn(conn);
     q->ptr = (caddr_t)0;
     --NChopen;
}

chdstart(q, conn)
     register struct queue         *q;
     register struct connection    *conn;
{
     register struct packet   *pkt;
     register struct block    *bp;
     register struct chopen   *c;
     int       len, rwsize, err, ps;

     err = 0;
     conn->cn_sflags |= CHOPNWAIT;
     if (chb_pullup(q, sizeof(struct chopen)) == 0) {
          err = EINVAL;
	  goto flush;
     }
     c = (struct chopen *)q->first->rptr;

     len = c->co_clength + c->co_length + (c->co_length? 1 : 0);
     if (len > CHMAXPKT || c->co_clength <= 0) {
	  err = E2BIG;
flush:
          flush_packet(q);
	  goto out;
     }
     if (chb_pullup(q, len + sizeof(struct chopen)) == 0) {
          err = EINVAL;
	  goto flush;
     }
     bp = getq(q);
     flush_packet(q);
     c = (struct chopen *)bp->rptr;

     if ((pkt = new_packet()) == NOPKT) {
          err = ENOMEM;
	  goto out;
     }
     append_packet(pkt, bp->rptr += sizeof(struct chopen), c->co_clength);
     if (c->co_length) {
          append_packet(pkt, " ", 1);
          append_packet(pkt, bp->rptr += c->co_clength, c->co_length);
     }

     conn->cn_mode = c->co_mode;
     if (c->co_mode == CHSTREAM)
	  RD(q)->flag &= ~QDELIM;
     rwsize = (c->co_rwsize ? c->co_rwsize : CHDRWSIZE);
     if (c->co_host)
          chld_open(conn, c->co_host, rwsize, pkt);
     else
          chld_listen(conn, rwsize, pkt);

out:
     ps = spl6();
     if (conn->cn_sflags & CHOPNWAIT) {
	  if (c->co_async || err)
	       chdoreply(conn, err, NOPKT, c->co_async);
     }
     splx(ps);
}

chdoreply(conn, err, pkt, async)
     struct connection   *conn;
     struct packet       *pkt;
{
     struct choreply     c;
     struct queue        *q;

     conn->cn_sflags &= ~CHOPNWAIT;
     q = conn->cn_rdq;

     if (err == 0 && !async) {
	  if (conn->cn_flags & CHSERVER) {
	       if (conn->cn_state != CSRFCRCVD)
		    err = EIO;
	  } else {
	       if (conn->cn_state != CSOPEN &&
			 (conn->cn_state != CSCLOSED ||
			      pkt == NOPKT || pkt->pk_op != ANSOP))
		    err = EIO;
	  }
     }
     c.errno = err;
     putcpy(q->next, &c, sizeof(c));
     putctl(q->next, M_DELIM);
}

chdput(q, bp)
     register struct queue    *q;
     register struct block    *bp;
{
     switch(bp->type){
          case M_IOCTL:
               chdioctl(q, bp);
               break;
          case M_DATA:
               putq(q, bp);
               break;
          case M_DELIM:
               putq(q, bp);
	       if (!(((struct connection *)q->ptr)->cn_sflags & CHOQWAIT))
		    qenable(q);
               break;
          default:
               freeb(bp);
               break;
     }
}

chdioctl(q, bp)
     register struct queue    *q;
     register struct block    *bp;
{
     register struct connection    *conn;
     register struct packet   *pkt;
     union stmsg    *sp;
     int       ps, len;

     sp = (union stmsg *)(bp->rptr);
     if ((conn = (struct connection *)q->ptr) == NOCONN)
          goto bad;
     bp->type = M_IOCACK;

     switch(sp->ioc0.com){
          case CHIOCRPKT:
	       /* Read a packet that would normally be discarded.
	        * This is typically used to read the error message
	        * given with a CLS packet, or to look at the contact
	        * string for a listener.
		* The ioctl returns 2 bytes for the opcode, 2 bytes for
		* the (data) length, and the bytes of data.
	        */
               ps = spl6();
	       if ((pkt = conn->cn_expkt) == NOPKT || flatten(pkt))
	            break;
	       conn->cn_expkt = NULL;
               splx(ps);
               len = pkt->pk_len + 4 + sizeof(sp->ioc0.com);
	       if (bp->lim - bp->base < len) {
	            freeb(bp);
		    if ((bp = allocb(len)) == NOBLOCK
		              || bp->lim - bp->base < len)
                         break;
                    bp->type = M_IOCACK;
               }
	       sp = (union stmsg *)(bp->rptr = bp->base);
	       bp->wptr = bp->rptr + len;
	       sp->ioc0.com = CHIOCRPKT;
	       *(short *)(&sp->iocx.xxx[0]) = pkt->pk_op;
	       *(short *)(&sp->iocx.xxx[2]) = pkt->pk_len;
	       bcopy(pkt->data->rptr, &sp->iocx.xxx[4], pkt->pk_len);
	       free_packet(pkt);
	       goto reply;
          case CHIOCFLUSH:
	       /* Flush the current output packet if there is one.
	        * Applicable in stream mode only.
		*/
	       if (conn->cn_toutput != NOPKT) {
	            chld_write(conn, conn->cn_toutput);
		    conn->cn_toutput = NULL;
               }
	       goto noreply;
          case CHIOCOWAIT:
               /* Wait for all output to be acknowledged.
	        * If in stream mode, any pending output is flushed first.
	        * If *addr is non-zero, an EOF is also sent before waiting.
		*/
	       if (conn->cn_toutput != NOPKT) {
	            chld_write(conn, conn->cn_toutput);
		    conn->cn_toutput = NULL;
               }
               if (sp->iocx.xxx[0])
	            chld_eof(conn);
               ps = spl6();
	       if (chtempty(conn)) {
		    /* already empty */
		    splx(ps);
		    goto noreply;
	       }
	       conn->cn_sflags |= CHEMPWAIT;
	       conn->cn_wait = bp;
	       splx(ps);
	       return;
          case CHIOCGSTAT:
	       /* Return the status of the connection */
	       ps = spl6();
	       {
	            register struct chstatus *chst;

                    chst = (struct chstatus *)sp->iocx.xxx;
		    chst->st_fhost = conn->cn_faddr.ch_addr;
		    chst->st_cnum = conn->cn_lidx.ci_tidx;
		    chst->st_rwsize = conn->cn_rwsize;
		    chst->st_twsize = conn->cn_twsize;
		    chst->st_state = conn->cn_state;
		    chst->st_cmode = conn->cn_mode;
		    chst->st_oroom = conn->cn_twsize -
		                        (conn->cn_tlast - conn->cn_tacked);
                    chst->st_ptype = chst->st_plength = 0;
		    bp->wptr = (u_char *)(chst + 1);
               }
	       splx(ps);
	       goto reply;
          case CHIOCSWAIT:
	       /* Wait for the state of the connection to be different
	        * from the given state.
		*/
	       ps = spl6();
	       if (conn->cn_state != *(int *)sp->iocx.xxx) {
		    /* already out of the given state */
		    splx(ps);
		    goto noreply;
	       }
	       conn->cn_sflags |= CHSWAIT;
	       conn->cn_wait = bp;
	       splx(ps);
	       return;
          case CHIOCANSWER:
	       /* Answer an RFC.  This sets a bit saying the next write
	        * call should send an ANS packet and close the connection.
		*/
               if (conn->cn_state == CSRFCRCVD) {
	            conn->cn_flags |= CHANSWER;
		    goto noreply;
               }
	       break;
          case CHIOCREJECT:
	       /* Reject an RFC.  This closes the connection with a CLS
	        * packet containing an error message string.  The user
		* passes in a structure containing a pointer to the error
		* message (reason) and its length.
		*/
	       {
	            register struct chreject *cr;
                    int       flag, c;

                    cr = (struct chreject *)sp->iocx.xxx;
		    pkt = NOPKT;
		    flag = 0;
		    if (cr->cr_length > 0 && cr->cr_length <= CHMAXPKT) {
		         if ((pkt = new_packet()) == NOPKT)
			      ++flag;
			 else {
			      pkt->pk_op = CLSOP;
                              while (cr->cr_length-- > 0) {
			           if ((c = fubyte(cr->cr_reason++)) < 0)
					break;
			           append_packet(pkt, &c, 1);
                              }
                         }
                    }
		    chld_close(conn, pkt);
		    if (flag == 0)
		         goto noreply;
               }
	       break;
          case CHIOCACCEPT:
	       /* Accept an RFC causing the OPN packet to be sent */
               ps = spl6();
	       if (conn->cn_state == CSRFCRCVD) {
	            chld_accept(conn);
		    splx(ps);
		    goto noreply;
               }
	       splx(ps);
	       break;
     }

bad:
     bp->type = M_IOCNAK;
noreply:
     bp->wptr = bp->rptr;
reply:
     qreply(q, bp);
}

chdosrv(q)
     struct queue   *q;
{
     register struct block    *bp;

     if (chdwrite(q))
	  return;

     /* See if there's another packet on the queue */
     for (bp = q->first; bp != NOBLOCK; bp = bp->next)
	  if (bp->type == M_DELIM) {
	       qenable(q);    /* re-enable queue for the next packet */
	       break;
          }
}

chdwrite(q)
     register struct queue    *q;
{
     register struct packet   *pkt;
     register struct block    *bp, *tail;
     register struct connection        *conn;
     int       n, ps;

     if ((conn = (struct connection *)q->ptr) == NOCONN) {
          flush_packet(q);
	  return 1;
     }
     if (conn->cn_state == CSSTARTING) {
          chdstart(q, conn);
          return 0;
     }
     conn->cn_flags |= CHWRITER;

     ps = spl6();
     if (chtfull(conn)) {
	  conn->cn_sflags |= CHOQWAIT;
	  splx(ps);
	  return 1;
     }
     splx(ps);

     switch (conn->cn_mode) {
          case CHSTREAM:
               while ((bp = getq(q)) != NOBLOCK) {
                    if (bp->rptr >= bp->wptr) {
                         freeb(bp);
                         continue;
                    }
                    if (conn->cn_state != CSOPEN &&
                              (conn->cn_state != CSRFCRCVD ||
                               (conn->cn_flags & CHANSWER) == 0)) {
                         freeb(bp);
                         flush_packet(q);
                         return 1;
                    }
                    if ((pkt = conn->cn_toutput) == NOPKT) {
                         if ((pkt = new_packet()) == NOPKT) {
                              printf("chodsrv: can't allocate packet\n");
                              return 0;
                         }
                         setconn(conn, pkt);
                         pkt->pk_op = (conn->cn_flags & CHANSWER ?
                                             ANSOP : DATOP);
			 conn->cn_toutput = pkt;
                    }
                    n = CHMAXDATA - pkt->pk_len;
                    if (bp->wptr - bp->rptr >= n) {
                         append_packet(pkt, bp->rptr, n);
                         bp->rptr += n;
                         conn->cn_toutput = NOPKT;
                         if (bp->rptr < bp->wptr)
                              putbq(q, bp);
			 chld_write(conn, pkt);
                    } else {
                         bp->next = NOBLOCK;
                         if (pkt->data == NOBLOCK)
                              pkt->data = bp;
                         else {
                              for (tail = pkt->data; tail->next != NOBLOCK;)
                                   tail = tail->next;
                              tail->next = bp;
                         }
                         pkt->pk_lenword += bp->wptr - bp->rptr;
                    }
               }
               break;
          case CHRECORD:
               if ((pkt = get_packet(q, 1)) == NOPKT) {
                    printf("chdosrv: can't allocate packet\n");
                    return 0;
               }
               setconn(conn, pkt);
	       chld_write(conn, pkt);
               break;
     }

     return 0;
}

chdrint(conn, pkt)
     register struct connection    *conn;
     register struct packet   *pkt;
{
     register struct queue    *q;

     if (conn->cn_sflags & CHOPNWAIT)
          chdoreply(conn, 0, pkt, 0);
     if (ISDATA(pkt) || pkt->pk_op == EOFOP)
          conn->cn_flags |= CHREADER;

     q = conn->cn_rdq;
     if (q) {
          if (conn->cn_mode == CHSTREAM) {
	       if (pkt->pk_op == EOFOP)
	            conn->cn_flags |= CHEOFSEEN;
	       else
	            conn->cn_flags &= ~CHEOFSEEN;
               if (!READABLE(pkt)) {
	            conn->cn_expkt = pkt;
		    return;
               }
          }
          if (q->next->flag & QFULL) {
               free_packet(pkt);
               debug(DABNOR,printf("chdrint: QFULL\n"));
               return;
          }
	  ch_busy++;
          if (conn->cn_mode == CHRECORD)     /* Check for RECORD mode */
               putcpy(q->next, &pkt->pk_op, 1);
          put_pkdata(q, pkt);
	  --ch_busy;
     } else
          free_packet(pkt);
}


chd_newstate(conn)
     register struct connection    *conn;
{
     register struct queue    *qnext = conn->cn_rdq->next;
     int       ps = spl6();

     if (conn->cn_sflags & CHOPNWAIT)
	  chdoreply(conn, 0, NOPKT);

     if (conn->cn_sflags & CHSWAIT) {
          register union stmsg *sp = (union stmsg *)conn->cn_wait->rptr;

          if (conn->cn_state != *(int *)sp->iocx.xxx) {
	       conn->cn_wait->wptr = (u_char *)sp;
	       (*qnext->qinfo->putp) (qnext, conn->cn_wait);
	       conn->cn_sflags &= ~CHSWAIT;
	       conn->cn_wait = NOBLOCK;
          }
     }

     if (chdead(conn)) {      /* send an end-of-file */
          putctl(qnext, M_DELIM);
          putctl(qnext, M_DELIM);
     }

     splx(ps);
}

chd_newout(conn)
     register struct connection    *conn;
{
     int       ps = spl6();

     if (conn->cn_sflags & CHOWAIT)
          wakeup((caddr_t)&conn->cn_thead);

     if (conn->cn_sflags & CHEMPWAIT) {
          register union stmsg *sp = (union stmsg *)conn->cn_wait->rptr;

          if (chtempty(conn)) {
	       conn->cn_wait->wptr = (u_char *)sp;
	       qreply(conn->cn_rdq, conn->cn_wait);
	       conn->cn_sflags &= ~CHEMPWAIT;
	       conn->cn_wait = NOBLOCK;
          }
     }

     if (conn->cn_sflags & CHOQWAIT) {
	  conn->cn_sflags &= ~CHOQWAIT;
	  qenable(WR(conn->cn_rdq));
     }

     splx(ps);
}

#endif