+#include "../h/param.h"
+#include "../h/systm.h"
+#include "../h/dir.h"
+#include "../h/user.h"
+#include "../h/proc.h"
+#include "../h/tty.h"
+#include "../h/inode.h"
+#define KERNEL 1
+#include "../h/mx.h"
+#include "../h/file.h"
+#include "../h/conf.h"
+#include "../h/buf.h"
+
+/*
+ * multiplexor driver
+ */
+struct chan chans[NCHANS];
+struct group *groups[NGROUPS];
+int mpxline;
+struct chan *xcp();
+struct chan *addch();
+struct chan *nextcp();
+
+#define MIN(a,b) ((a<b)?a:b)
+short cmask[16] ={
+ 01, 02, 04,
+ 010, 020, 040,
+ 0100, 0200, 0400,
+ 01000, 02000, 04000,
+ 010000, 020000, 040000, 0100000
+};
+
+#define HIQ 100
+#define LOQ 20
+#define FP ((struct file *)cp)
+
+
+
+char mcdebugs[NDEBUGS];
+
+/*
+ * Timing cell
+ */
+int mxdummy;
+int *MP = &mxdummy;
+
+
+
+struct group *
+getmpx(dev)
+dev_t dev;
+{
+register d;
+
+ d = minor(dev);
+ if (d >= NGROUPS)
+ return(NULL);
+ return(groups[d]);
+}
+
+
+
+
+mxopen(dev, flag)
+{
+register struct group *gp;
+register struct file *fp;
+register struct chan *cp;
+int msg;
+ gp = getmpx(dev);
+ if (gp == NULL) {
+ bad:
+ u.u_error = ENXIO;
+ return;
+ }
+
+ if (gp->g_state == COPEN) {
+ gp->g_state = INUSE+ISGRP;
+ return;
+ }
+ if (!(gp->g_state&INUSE))
+ goto bad;
+ fp = u.u_ofile[u.u_r.r_val1];
+ if (fp->f_inode != gp->g_inode)
+ goto bad;
+ if ((cp=addch(gp->g_inode, 0))==NULL)
+ goto bad;
+ cp->c_flags = XGRP;
+ cp->c_ottyp = cp->c_ttyp = (struct tty *)cp;
+ cp->c_line = cp->c_oline = mpxline;
+ fp->f_flag |= FMPY;
+ fp->f_flag |= FREAD+FWRITE;
+ fp->f_un.f_chan = cp;
+ if (gp->g_inode == mpxip) {
+ plock(mpxip);
+ mpxname(cp);
+ msg = M_OPEN;
+ } else
+ msg = M_WATCH;
+ scontrol(cp, msg, u.u_uid);
+ sleep((caddr_t)cp,TTIPRI);
+ if (cp->c_flags&NMBUF)
+ prele(mpxip);
+ if (cp->c_flags & WCLOSE) {
+ chdrain(cp);
+ chfree(cp);
+ goto bad;
+ }
+ cp->c_fy = fp;
+ cp->c_pgrp = u.u_procp->p_pgrp;
+}
+
+
+char mxnmbuf[NMSIZE];
+int nmsize;
+struct chan *mxnmcp;
+mpxname(cp)
+register struct chan *cp;
+{
+register char *np;
+register c;
+ np = mxnmbuf;
+ u.u_dirp = (caddr_t)u.u_arg[0];
+
+ while (np < &mxnmbuf[NMSIZE]) {
+ c = uchar();
+ if (c <= 0)
+ break;
+ *np++ = c;
+ }
+ *np++ = '\0';
+ nmsize = np - mxnmbuf;
+
+ cp->c_flags |= NMBUF;
+}
+
+
+mxclose(dev, flag, cp)
+dev_t dev;
+register struct chan *cp;
+{
+register struct group *gp;
+register struct inode *ip;
+register struct file *fp;
+int i, fmp;
+
+ fmp = flag&FMP;
+
+ /*
+ * close a channel
+ */
+ if (cp!=NULL && fmp && fmp!=FMP) {
+ for(fp=file; fp < &file[NFILE]; fp++)
+ if(fp->f_count && fp->f_flag&FMP && fp->f_un.f_chan==cp){
+ return;
+ }
+ chdrain(cp);
+ if ((cp->c_flags&WCLOSE)==0) {
+ scontrol(cp, M_CLOSE, 0);
+ cp->c_flags |= WCLOSE;
+ } else {
+ chfree(cp);
+ }
+ return;
+ }
+
+ if ((gp = getmpx(dev)) == NULL)
+ return;
+ ip = gp->g_inode;
+ if (ip==NULL || (ip->i_mode&IFMT)!=IFMPC) {
+ return;
+ }
+
+ for(fp=file; fp < &file[NFILE]; fp++) {
+ if (fp->f_count && (fp->f_flag&FMP)==FMP && fp->f_inode==ip) {
+ return;
+ }
+ }
+
+ if (ip == mpxip) {
+ mpxip = NULL;
+ prele(ip);
+ }
+
+ for(i=0;i<NINDEX;i++)
+ if ((cp=gp->g_chans[i])!=NULL)
+ detach(cp);
+
+ groups[minor(dev)] = NULL;
+ plock(ip);
+ i = ip->i_mode;
+ i &= ~IFMT;
+ i |= IFCHR;
+ ip->i_mode = i;
+ zero((caddr_t)gp, sizeof (struct group));
+ ip->i_flag |= IUPD|ICHG;
+ iput(ip);
+}
+
+zero(s, cc)
+register char *s;
+register cc;
+{
+ while (cc--)
+ *s++ = 0;
+}
+
+char m_eot[] ={ M_EOT, 0, 0, 0};
+
+/*
+ * Mxread + mxwrite are entered from cdevsw
+ * for all read/write calls. Operations on
+ * an mpx file are handled here.
+ * Calls are made through linesw to handle actual
+ * data movement.
+ */
+mxread(dev)
+{
+register struct group *gp;
+register struct chan *cp;
+register esc;
+struct rh h;
+caddr_t base;
+unsigned count;
+int s, xfr, more, fmp;
+
+ if ((gp=getmpx(dev))==NULL) {
+bad:
+ u.u_error = ENXIO;
+ return;
+ }
+ FP = getf(u.u_arg[0]);
+ fmp = FP->f_flag & FMP;
+ if (fmp != FMP) {
+ msread(fmp, FP->f_un.f_chan);
+ return;
+ }
+
+ if ((int)u.u_base & 1)
+ goto bad;
+ s = spl6();
+ while (gp->g_datq == 0) {
+
+ sleep((caddr_t)&gp->g_datq, TTIPRI);
+ }
+
+ while (gp->g_datq && u.u_count >= CNTLSIZ + 2) {
+ splx(s);
+ esc = 0;
+ cp = nextcp(gp);
+ if (cp==NULL) {
+ continue;
+ }
+ h.index = cpx(cp);
+ if (count = cp->c_ctlx.c_cc) {
+ count += CNTLSIZ;
+ if (cp->c_flags&NMBUF)
+ count += nmsize;
+ if (count > u.u_count) {
+ sdata(cp);
+ return;
+ }
+ esc++;
+ }
+ base = u.u_base;
+ count = u.u_count;
+ u.u_base += sizeof h;
+ u.u_count -= sizeof h;
+ xfr = u.u_count;
+ if (esc && cp->c_flags&PORT) {
+ more = mcread(cp);
+ } else {
+ more = (*linesw[cp->c_line].l_read)(cp->c_ttyp);
+ }
+ if (more > 0)
+ sdata(cp);
+ if (more < 0)
+ scontrol(cp, M_CLOSE, 0);
+ if (xfr == u.u_count) {
+ esc++;
+ iomove((caddr_t)m_eot, sizeof m_eot, B_READ);
+ }
+ xfr -= u.u_count;
+ if (esc) {
+ h.count = 0;
+ h.ccount = xfr;
+ } else {
+ h.count = xfr;
+ h.ccount = 0;
+ mxrstrt(cp, &cp->cx.datq, BLOCK|ALT);
+ }
+ if (u.u_count && (xfr&1)) {
+ u.u_base++;
+ u.u_count--;
+ }
+ copyout((caddr_t)&h, base, sizeof h);
+
+ s = spl6();
+ }
+}
+
+
+
+
+
+mxwrite(dev)
+{
+register struct chan *cp;
+struct wh h;
+struct group *gp;
+int ucount, esc, fmp, burpcount;
+caddr_t ubase, hbase;
+
+ if ((gp=getmpx(dev))==NULL) {
+ u.u_error = ENXIO;
+ return;
+ }
+ FP = getf(u.u_arg[0]);
+ fmp = FP->f_flag & FMP;
+ if (fmp != FMP) {
+ mswrite(fmp, FP->f_un.f_chan);
+ return;
+ }
+ burpcount = 0;
+ while (u.u_count >= sizeof h) {
+ hbase = u.u_base;
+ iomove((caddr_t)&h, sizeof h, B_WRITE);
+ if (u.u_error)
+ return;
+ esc = 0;
+ if (h.count==0) {
+ esc++;
+ h.count = h.ccount;
+ }
+ cp = xcp(gp, h.index);
+ if (cp==NULL) {
+ continue;
+ }
+ ucount = u.u_count;
+ ubase = u.u_base;
+ u.u_count = h.count;
+ u.u_base = h.data;
+
+ if (esc==0) {
+ struct tty *tp;
+ caddr_t waddr;
+ int line;
+
+ if (cp->c_flags&PORT) {
+ line = cp->c_line;
+ tp = cp->c_ttyp;
+ } else {
+ line = cp->c_oline;
+ tp = cp->c_ottyp;
+ }
+ loop:
+ waddr = (caddr_t)(*linesw[line].l_write)(tp);
+ if (u.u_count) {
+ if (gp->g_state&ENAMSG) {
+ burpcount++;
+ cp->c_flags |= BLKMSG;
+/*
+ scontrol(cp, M_BLK, u.u_count);
+*/
+ h.ccount = -1;
+ h.count = u.u_count;
+ h.data = u.u_base;
+ copyout((caddr_t)&h, hbase, sizeof h);
+ } else {
+ if (waddr==0) {
+ u.u_error = ENXIO;
+ return;
+ }
+ sleep(waddr, TTOPRI);
+ goto loop;
+ }
+ }
+ } else
+ mxwcontrol(cp);
+ u.u_count = ucount;
+ u.u_base = ubase;
+ }
+ u.u_count = burpcount;
+}
+
+
+
+/*
+ * Mcread and mcwrite move data on an mpx file.
+ * Transfer addr and length is controlled by mxread/mxwrite.
+ * Kernel-to-Kernel and other special transfers are not
+ * yet in.
+ */
+mcread(cp)
+register struct chan *cp;
+{
+register struct clist *q;
+register char *np;
+
+int cc;
+
+ q = (cp->c_ctlx.c_cc) ? &cp->c_ctlx : &cp->cx.datq;
+ cc = mxmove(q, B_READ);
+
+ if (cp->c_flags&NMBUF && q == &cp->c_ctlx) {
+ np = mxnmbuf;
+ while (nmsize--)
+ passc(*np++);
+ cp->c_flags &= ~NMBUF;
+ prele(mpxip);
+ }
+ if (cp->c_flags&PORT)
+ return(cp->c_ctlx.c_cc + cp->c_ttyp->t_rawq.c_cc); else
+ return(cp->c_ctlx.c_cc + cp->cx.datq.c_cc);
+
+}
+
+
+char *
+mcwrite(cp)
+register struct chan *cp;
+{
+register struct clist *q;
+register cc;
+int s;
+
+ q = &cp->cy.datq;
+ while (u.u_count) {
+ s = spl6();
+ if (q->c_cc > HIQ || (cp->c_flags&EOTMARK)) {
+ cp->c_flags |= SIGBLK;
+ splx(s);
+ break;
+ }
+ splx(s);
+ cc = mxmove(q, B_WRITE);
+ }
+ wakeup((caddr_t)q);
+ return((caddr_t)q);
+}
+
+
+/*
+ * Msread and mswrite move bytes
+ * between user and non-multiplexed channel.
+ */
+msread(fmp, cp)
+register struct chan *cp;
+{
+register struct clist *q;
+int s;
+
+ q = (fmp&FMPX) ? &cp->cx.datq : &cp->cy.datq;
+ s = spl6();
+ while (q->c_cc == 0) {
+ if (cp->c_flags & EOTMARK) {
+ cp->c_flags &= ~EOTMARK;
+ if (cp->c_flags&ENAMSG)
+ scontrol(cp, M_UBLK, 0);
+ else {
+ wakeup((caddr_t)cp);
+ wakeup((caddr_t)q);
+ }
+ goto out;
+ }
+ if (cp->c_flags&WCLOSE) {
+ u.u_error = ENXIO;
+ goto out;
+ }
+ sleep((caddr_t)q,TTIPRI);
+ }
+ splx(s);
+ while (mxmove(q, B_READ) > 0)
+ ;
+ mxrstrt(cp, q, SIGBLK);
+ return;
+out:
+ splx(s);
+}
+
+
+mswrite(fmp, cp)
+register struct chan *cp;
+{
+register struct clist *q;
+register int cc;
+
+ q = (fmp&FMPX) ? &cp->cy.datq : &cp->cx.datq;
+ while (u.u_count) {
+
+ spl6();
+ if (cp->c_flags&WCLOSE) {
+ bad:
+ signal(SIGPIPE, cp->c_pgrp);
+ return;
+ }
+
+ while (q->c_cc>100) {
+ if (cp->c_flags&WCLOSE)
+ goto bad;
+ sdata(cp);
+ cp->c_flags |= BLOCK;
+ sleep((caddr_t)q+1,TTOPRI);
+ }
+ spl0();
+ cc = mxmove(q, B_WRITE);
+ if (cc < 0)
+ break;
+ }
+ if (fmp&FMPX) {
+ if (cp->c_flags&YGRP)
+ sdata(cp); else
+ wakeup((caddr_t)q);
+ } else {
+ if (cp->c_flags&XGRP)
+ sdata(cp); else
+ wakeup((caddr_t)q);
+ }
+}
+
+
+
+/*
+ * move chars between clist and user space.
+ */
+mxmove(q, dir)
+register struct clist *q;
+register dir;
+{
+register cc;
+char buf[HIQ];
+
+ cc = MIN(u.u_count, sizeof buf);
+ if (dir == B_READ)
+ cc = q_to_b(q, buf, cc);
+ if (cc <= 0)
+ return(cc);
+ iomove((caddr_t)buf, cc, dir);
+ if (dir == B_WRITE)
+ cc = b_to_q(buf, cc, q);
+ return(cc);
+}
+
+
+
+mxrstrt(cp, q, b)
+register struct chan *cp;
+register struct clist *q;
+register b;
+{
+int s;
+
+ s = spl6();
+ if (cp->c_flags&b && q->c_cc<LOQ) {
+ cp->c_flags &= ~b;
+ if (b&ALT)
+ wakeup((caddr_t)q+1); else
+ mcstart(cp, (caddr_t)q);
+ }
+ if (cp->c_flags&WFLUSH)
+ wakeup((caddr_t)q+2);
+ splx(s);
+}
+
+
+
+/*
+ * called from driver start or xint routines
+ * to wakeup output sleeper.
+ */
+mcstart(cp, q)
+register struct chan *cp;
+register caddr_t q;
+{
+
+ if (cp->c_flags&(BLKMSG)) {
+ cp->c_flags &= ~BLKMSG;
+ scontrol(cp, M_UBLK, 0);
+ } else
+ wakeup((caddr_t)q);
+}
+
+
+mxwcontrol(cp)
+register struct chan *cp;
+{
+short cmd[2];
+int s;
+
+ iomove((caddr_t)cmd, sizeof cmd, B_WRITE);
+ switch(cmd[0]) {
+ /*
+ * not ready to queue this up yet.
+ */
+ case M_EOT:
+ s = spl6();
+ while (cp->c_flags & EOTMARK)
+ if (cp->c_flags&ENAMSG) {
+ scontrol(cp, M_BLK, 0);
+ goto out;
+ } else
+ sleep((caddr_t)cp, TTOPRI);
+ cp->c_flags |= EOTMARK;
+ out:
+ splx(s);
+ break;
+ case M_IOCTL:
+printf("M_IOCTL");
+ break;
+ default:
+ u.u_error = ENXIO;
+ }
+}
+
+
+
+
+
+
+
+
+mxioctl(dev, cmd, addr, flag)
+caddr_t addr;
+{
+struct group *gp;
+int fmp;
+struct file *fp;
+
+ if ((gp = getmpx(dev)) == NULL) {
+bad:
+ u.u_error = ENXIO;
+ return;
+ }
+
+ fp = getf(u.u_arg[0]);
+ if (fp==NULL)
+ goto bad;
+
+ fmp = fp->f_flag & FMP;
+
+ if (fmp == FMP) {
+ switch(cmd) {
+ case MXLSTN:
+ if (mpxip == NULL) {
+ mpxip = gp->g_inode;
+ } else
+ goto bad;
+ break;
+ case MXNBLK:
+ gp->g_state |= ENAMSG;
+ break;
+ default:
+ goto bad;
+ }
+ }
+}
+
+
+
+
+chdrain(cp)
+register struct chan *cp;
+{
+register struct tty *tp;
+int wflag;
+
+ chwake(cp);
+
+ wflag = (cp->c_flags&WCLOSE)==0;
+ tp = cp->c_ttyp;
+ if (tp == NULL) /* prob not required */
+ return;
+ if (cp->c_flags&PORT && tp->t_chan == cp) {
+ cp->c_ttyp = NULL;
+ tp->t_chan = NULL;
+ return;
+ }
+ if (wflag)
+ wflush(cp,&cp->cx.datq); else
+ flush(&cp->cx.datq);
+ if (!(cp->c_flags&YGRP)) {
+ flush(&cp->cy.datq);
+ }
+}
+
+chwake(cp)
+register struct chan *cp;
+{
+register char *p;
+
+ wakeup((caddr_t)cp);
+ flush(&cp->c_ctlx);
+ p = (char *)&cp->cx.datq;
+ wakeup((caddr_t)p); wakeup((caddr_t)++p); wakeup((caddr_t)++p);
+ p = (char *)&cp->cy.datq;
+ wakeup((caddr_t)p); wakeup((caddr_t)++p); wakeup((caddr_t)++p);
+}
+
+
+chfree(cp)
+register struct chan *cp;
+{
+register struct group *gp;
+register i;
+
+ gp = cp->c_group;
+ if (gp==NULL)
+ return;
+ i = cp->c_index;
+ if (cp == gp->g_chans[i]) {
+ gp->g_chans[i] = NULL;
+ }
+ cp->c_group = NULL;
+}
+
+
+flush(q)
+register struct clist *q;
+{
+
+ while(q->c_cc)
+ getc(q);
+}
+
+
+wflush(cp,q)
+register struct chan *cp;
+register struct clist *q;
+{
+register s;
+
+ s = spl6();
+ while(q->c_cc) {
+ if (cp->c_flags & WCLOSE) {
+ flush(q);
+ goto out;
+ }
+ cp->c_flags |= WFLUSH;
+ sdata(cp);
+ sleep((caddr_t)q+2,TTOPRI);
+ }
+out:
+ cp->c_flags &= ~WFLUSH;
+ splx(s);
+}
+
+
+scontrol(cp,event,value)
+register struct chan *cp;
+short event,value;
+{
+register struct clist *q;
+int s;
+
+ q = &cp->c_ctlx;
+ s = spl6();
+ if (sdata(cp) == NULL)
+ return;
+ putw(event,q);
+ putw(value,q);
+ splx(s);
+}
+
+sdata(cp)
+register struct chan *cp;
+{
+register struct group *gp;
+register short x;
+register struct group *lgp;
+int s;
+
+ gp = cp->c_group;
+ if (gp==NULL) {
+ return(0);
+ }
+ x = cp->c_index;
+
+ s = spl6();
+ while (gp) {
+ if ((gp->g_state&ISGRP)==0) {
+ return(0);
+ }
+ gp->g_datq |= cmask[x];
+ x = gp->g_index;
+ lgp = gp;
+ gp = gp->g_group;
+ }
+ gp = lgp;
+ splx(s);
+ wakeup((caddr_t)&gp->g_datq);
+ return((int)gp);
+}
+
+
+
+struct chan *
+xcp(gp, x)
+register struct group *gp;
+register short x;
+{
+register i;
+
+ i = 0;
+ while (i<NLEVELS && gp->g_state&ISGRP) {
+ gp = (struct group *)gp->g_chans[x&017];
+ x >>= 4;
+ if ((x&017) >= NINDEX)
+ break;
+ i++;
+ }
+ return((struct chan *)gp);
+}
+
+cpx(cp)
+register struct chan *cp;
+{
+register x;
+register struct group *gp;
+
+ if (cp==NULL)
+ return(-1);
+ x = (-1<<4) + cp->c_index;
+ gp = cp->c_group;
+ if (gp==NULL || (gp->g_state&ISGRP)==0)
+ return(-1);
+ gp = gp->g_group;
+ while (gp && gp->g_state&ISGRP) {
+ x <<= 4;
+ x |= gp->g_index;
+ gp = gp->g_group;
+ }
+ return(x);
+}
+
+
+
+struct chan *
+nextcp(gp)
+register struct group *gp;
+{
+
+ if (gp->g_datq == 0) {
+ gp = NULL;
+ goto out;
+ }
+
+ while (gp != NULL && gp->g_state&ISGRP) {
+ while ( (gp->g_datq & gp->g_rotmask) == 0) {
+ gp->g_rot++;
+ gp->g_rot &= 017;
+ if (gp->g_rot)
+ gp->g_rotmask <<= 1; else
+ gp->g_rotmask = 1;
+ }
+ gp = (struct group *)gp->g_chans[gp->g_rot];
+ }
+ if (gp)
+ rmdata(gp);
+out:
+ return((struct chan *)gp);
+}
+
+rmdata(cp)
+register struct chan *cp;
+{
+register struct group *gp;
+register short x;
+
+ gp = cp->c_group;
+ x = cp->c_index;
+
+ while (gp) {
+ gp->g_datq &= ~cmask[x];
+ if (gp->g_datq)
+ return;
+ x = gp->g_index;
+ gp = gp->g_group;
+ }
+}
+
+
+
+
+
+mcrint(c, tp)
+struct tty *tp;
+{
+}
+
+mcxint(tp)
+struct tty *tp;
+{
+}
+/*
+prstuff(s,cc)
+register char *s;
+register cc;
+{
+ while (cc--)
+ printf("%o ",*s++&0377);
+}
+
+prascii(s, cc)
+register char *s;
+register cc;
+{
+register c;
+ while (cc--) {
+ c = *s++;
+ if (c>=040 && c<=0176)
+ putchar(c); else
+ printf(" %o ", c&0377);
+ }
+}
+*/