BSD 4 development
[unix-history] / usr / src / sys / dev / mx2.c
/* mx2.c 4.2 11/9/80 */
#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"
#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;
short cmask[16] ={
01, 02, 04,
010, 020, 040,
0100, 0200, 0400,
01000, 02000, 04000,
010000, 020000, 040000, 0100000
};
#define IOMOVE iomove
#define FPEND &file[NFILE]
struct chan *xcp(),*addch(),*nextcp();
#define HIQ 100
#define LOQ 20
#define FP ((struct file *)cp)
char mcdebugs[NDEBUGS];
struct group *
getmpx(dev)
dev_t dev;
{
register d;
d = minor(dev);
if (d >= NGROUPS) {
u.u_error = ENXIO;
return(NULL);
}
return(groups[d]);
}
/*ARGSUSED*/
mxopen(dev, flag)
{
register struct group *gp;
register struct file *fp;
register struct chan *cp;
int msg;
if ((gp=getmpx(dev)) == NULL) {
return;
}
if (!(gp->g_state&INUSE)) {
u.u_error = ENXIO;
return;
}
fp = u.u_ofile[u.u_r.r_val1];
if (fp->f_inode != gp->g_inode) {
u.u_error = ENXIO;
return;
}
if ((cp=addch(gp->g_inode,0)) == NULL) {
u.u_error = ENXIO;
return;
}
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+(cp->c_index<<8), u.u_uid);
sleep((caddr_t)cp,TTIPRI);
if (cp->c_flags&NMBUF)
prele(mpxip);
if (cp->c_flags & WCLOSE) {
chdrain(cp);
chfree(cp);
u.u_error = ENXIO;
return;
}
cp->c_fy = fp;
cp->c_pgrp = u.u_procp->p_pgrp;
}
char mxnmbuf[NMSIZE];
int nmsize;
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;
if ((gp=getmpx(dev)) == NULL)
return;
ip = gp->g_inode;
if (ip==NULL || (ip->i_mode&IFMT)!=IFMPC) {
return;
}
/*
* close a channel
*/
if (cp!=NULL && fmp && fmp!=FMP) {
for(fp=file; fp< FPEND; 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);
}
goto out;
}
for(fp=file; fp < FPEND; 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++)
(void) detach(gp->g_chans[i]);
out:
if (ip->i_count == 1) {
groups[minor(dev)] = NULL;
plock(ip);
zero((caddr_t)gp, sizeof (struct group));
ip->i_mode = IFREG + 0666;
ip->i_un.i_rdev = 0;
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 || (FP=getf(u.u_arg[0]))==NULL) {
u.u_error = ENXIO;
return;
}
fmp = FP->f_flag & FMP;
if (fmp != FMP) {
if (u.u_count == 0)
return;
msread(fmp, FP->f_un.f_chan);
return;
}
if ((int)u.u_base & 1) {
u.u_error = ENXIO;
return;
}
s = spl6();
if (u.u_count == 0)
{
if (gp->g_datq == 0)
u.u_error = ENXIO;
splx(s);
return;
}
while (gp->g_datq == 0) {
sleep((caddr_t)&gp->g_datq, TTIPRI);
}
splx(s);
while (gp->g_datq && u.u_count >= CNTLSIZ + 2) {
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) {
(void) 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) {
more = mcread(cp);
} else {
more = (*linesw[cp->c_line].l_read)(cp->c_ttyp);
}
if (more > 0)
(void) sdata(cp);
if (more < 0)
scontrol(cp, M_CLOSE, 0);
(void) spl0();
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--;
}
(void) copyout((caddr_t)&h, base, sizeof h);
}
}
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 || (FP=getf(u.u_arg[0]))==NULL) {
return;
}
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 || cp->c_flags&ISGRP) {
u.u_error = ENXIO;
return;
}
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;
(void) 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;
q = (cp->c_ctlx.c_cc) ? &cp->c_ctlx : &cp->cx.datq;
(void) mxmove(q, B_READ);
if (cp->c_flags&NMBUF && q == &cp->c_ctlx) {
np = mxnmbuf;
while (nmsize--)
(void) 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);
}
caddr_t
mcwrite(cp)
register struct chan *cp;
{
register struct clist *q;
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);
(void) 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&WCLOSE) {
u.u_error = ENXIO;
goto out;
}
if (cp->c_flags & EOTMARK) {
cp->c_flags &= ~EOTMARK;
if(msgenab(cp))
scontrol(cp, M_UBLK, 0);
else {
wakeup((caddr_t)cp);
wakeup((caddr_t)q);
}
goto out;
}
sleep((caddr_t)q,TTIPRI);
}
if (cp->c_flags&WCLOSE) {
u.u_error = ENXIO;
goto out;
}
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) {
(void) spl6();
if (cp->c_flags&WCLOSE) {
gsignal(cp->c_pgrp, SIGPIPE);
(void) spl0();
return;
}
if (q->c_cc>= HIQ || cp->c_flags&FBLOCK) {
if (cp->c_flags&WCLOSE) {
gsignal(cp->c_pgrp, SIGPIPE);
(void) spl0();
return;
}
(void) sdata(cp);
cp->c_flags |= BLOCK;
sleep((caddr_t)q+1,TTOPRI);
(void) spl0();
continue;
}
(void) spl0();
cc = mxmove(q, B_WRITE);
if (cc < 0)
break;
}
if (fmp&FMPX) {
if (cp->c_flags&YGRP) (void) sdata(cp);
else wakeup((caddr_t)q);
} else {
if (cp->c_flags&XGRP) (void) 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 cbuf[HIQ];
cc = MIN(u.u_count, sizeof cbuf);
if (dir == B_READ)
cc = q_to_b(q, cbuf, cc);
if (cc <= 0)
return(cc);
IOMOVE((caddr_t)cbuf, (unsigned)cc, dir);
if (dir == B_WRITE)
cc = b_to_q(cbuf, 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;
struct sgttyb vec;
int s;
IOMOVE((caddr_t)&cmd, sizeof cmd, B_WRITE);
if (u.u_error)
return;
switch(cmd) {
/*
* not ready to queue this up yet.
*/
case M_EOT:
s = spl6();
while (cp->c_flags & EOTMARK)
if(msgenab(cp)){
scontrol(cp, M_BLK, 0);
goto out;
} else
sleep((caddr_t)cp, TTOPRI);
cp->c_flags |= EOTMARK;
out:
wakeup((caddr_t)&cp->cy.datq);
splx(s);
break;
case M_IOCTL:
break;
case M_IOANS:
if (cp->c_flags&SIOCTL) {
IOMOVE((caddr_t)&vec, sizeof vec, B_WRITE);
(void) b_to_q((caddr_t)&vec, sizeof vec, &cp->c_ctly);
cp->c_flags &= ~SIOCTL;
wakeup((caddr_t)cp);
}
break;
case M_BLK:
cp->c_flags |= FBLOCK;
break;
case M_UBLK:
cp->c_flags &= ~FBLOCK;
chwake(cp);
break;
default:
u.u_error = ENXIO;
}
}
/*ARGSUSED*/
mxioctl(dev, cmd, addr, flag)
caddr_t addr;
{
struct group *gp;
int fmp;
struct file *fp;
struct {
short c_ctl;
short c_cmd;
struct sgttyb c_vec;
} ctlbuf;
if ((gp=getmpx(dev))==NULL || (fp=getf(u.u_arg[0]))==NULL) {
return;
}
fmp = fp->f_flag & FMP;
if (fmp == FMP) {
switch(cmd) {
case MXLSTN:
if (mpxip == NULL) {
mpxip = gp->g_inode;
} else {
u.u_error = ENXIO;
return;
}
break;
case MXNBLK:
gp->g_state |= ENAMSG;
break;
default:
u.u_error = ENXIO;
return;
}
} else {
ctlbuf.c_ctl = M_IOCTL;
ctlbuf.c_cmd = cmd;
(void) copyin(addr, (caddr_t)&ctlbuf.c_vec, sizeof (struct sgttyb));
sioctl(fp->f_un.f_chan, (char *)&ctlbuf, sizeof ctlbuf);
(void) copyout((caddr_t)&ctlbuf, addr, sizeof (struct sgttyb));
}
}
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;
wakeup((caddr_t)gp);
}
flush(q)
register struct clist *q;
{
while(q->c_cc)
(void) getc(q);
}
wflush(cp,q)
register struct chan *cp;
register struct clist *q;
{
register s;
s = spl6();
if(q->c_cc && (cp->c_flags&WCLOSE) == 0) {
cp->c_flags |= WFLUSH;
(void) sdata(cp);
(void) tsleep((caddr_t)q+2, TTOPRI, 30);
}
flush(q);
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;
(void) putw(event,q);
(void) putw(value,q);
splx(s);
}
sioctl(cp, vec, cc)
register struct chan *cp;
char *vec;
{
register s;
register struct clist *q;
s = spl6();
q = &cp->cx.datq;
while (q->c_cc) {
cp->c_flags |= BLOCK;
if (sdata(cp)==NULL) {
u.u_error = ENXIO;
return;
}
sleep((caddr_t)q+1, TTOPRI);
}
(void) b_to_q(vec, cc, &cp->c_ctlx);
cp->c_flags |= SIOCTL;
while (cp->c_flags&SIOCTL) {
if (cp->c_ctlx.c_cc)
if (sdata(cp)==NULL) {
u.u_error = ENXIO;
return;
}
sleep((caddr_t)cp, TTOPRI);
}
(void) q_to_b(&cp->c_ctly, vec, cp->c_ctly.c_cc);
splx(s);
}
sdata(cp)
struct chan *cp;
{
register struct group *gp = (struct group *)cp;
register struct group *ngp;
register int s;
ngp = gp->g_group;
if (ngp==NULL || (ngp->g_state&ISGRP)==0)
return(NULL);
s = spl6();
do {
ngp->g_datq |= cmask[gp->g_index];
wakeup((caddr_t)&ngp->g_datq);
gp = ngp;
} while(ngp=ngp->g_group);
splx(s);
return((int)gp);
}
struct chan *
xcp(gp, x)
register struct group *gp;
register short x;
{
register int i;
while (gp->g_group) gp=gp->g_group;
for (i=0;i<NLEVELS;i++) {
if ((x&017) >= NINDEX)
break;
if (gp==NULL || (gp->g_state&ISGRP)==0)
return((struct chan *)NULL);
gp = (struct group *)gp->g_chans[x&017];
x >>= 4;
}
return((struct chan *)gp);
}
cpx(cp)
register struct chan *cp;
{
register x;
register struct group *gp;
x = (-1<<4) + cp->c_index;
gp = cp->c_group;
while (gp->g_group) {
x <<= 4;
x |= gp->g_index;
gp = gp->g_group;
}
return(x);
}
struct chan *
nextcp(gp)
register struct group *gp;
{
register struct group *lgp, *ngp;
do {
while ((gp->g_datq & cmask[gp->g_rot]) == 0) {
gp->g_rot = (gp->g_rot+1)%NINDEX;
}
lgp = gp;
gp = (struct group *)gp->g_chans[gp->g_rot];
} while (gp!=NULL && gp->g_state&ISGRP);
lgp->g_datq &= ~cmask[lgp->g_rot];
lgp->g_rot = (lgp->g_rot+1)%NINDEX;
while (ngp=lgp->g_group) {
ngp->g_datq &= ~cmask[lgp->g_index];
if (ngp->g_datq)
break;
lgp = ngp;
}
return((struct chan *)gp);
}
msgenab(cp)
register struct chan *cp;
{
register struct group *gp;
for(gp=cp->c_group;gp;gp=gp->g_group)
if(gp->g_state & ENAMSG)return(1);
return(0);
}