text: #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);
  	}
  }
  */
  