V10/sys/io/archosld.c
/*
* Raw line discipline and listener stuff for talking to controller
*/
#include "sys/param.h"
#include "sys/stream.h"
#include "sys/dkio.h"
#include "sys/conf.h"
#include "sys/dkstat.h"
#include "sys/dkmod.h"
/*
* CMC/Listener message structure
*/
struct dialin {
char type;
char srv;
short param0;
short param1;
short param2;
short param3;
short param4;
short param5;
};
struct dialout {
short chan;
struct dialin d;
};
/*
* Message codes
*/
#define T_CALL '0'
#define T_LSTNR 4
#define T_CHG 3
#define T_REPLY 10
#define D_CLOSE 1
#define D_ISCLOSED 2
#define D_ENQ 3
#define D_ENQANS 4
/*
* Raw-mode line discipline for DK (only)
*/
long adkopen();
int adkclose(), adkiput(), adkoput(), adkosrv(), adkisrv();
static struct qinit adkrinit = { adkiput, adkisrv, adkopen, adkclose, 72, 36};
static struct qinit adkwinit = { adkoput, adkosrv, adkopen, nulldev, 72, 36};
struct streamtab adkstream = { &adkrinit, &adkwinit };
extern struct dkstat dkstat;
extern struct dkmodule dkmod[];
extern int dkmodcnt;
#define NDKRAW 32
struct adk {
struct queue *q;
struct dkmodule *module;
short chan;
} adk[NDKRAW];
long
adkopen(q, dev)
register struct queue *q;
{
static timer_on = 0;
register struct adk *dkp, *edkp;
register struct dkmodule *dkm;
if ((dkm = getdkmod(dev)) == NULL)
return (0);
edkp = NULL;
for (dkp=adk; dkp < &adk[NDKRAW]; dkp++) {
if (dkp->q == q) {
if (dkp->chan != minor(dev) - dkm->lo || WR(q)->ptr==NULL) {
printf("q %x dkp %x\n", q, dkp);
panic("adkopen");
}
return(1);
}
if (dkp->q == NULL && edkp==NULL)
edkp = dkp;
}
if (edkp==NULL)
return(0);
edkp->q = q;
edkp->module = dkm;
edkp->chan = minor(dev) - dkm->lo;
q->flag |= QDELIM;
q->ptr = 0;
WR(q)->ptr = (caddr_t)edkp;
if (timer_on==0) {
adktimer();
timer_on = 1;
}
return(1);
}
adkclose(q)
register struct queue *q;
{
register struct block *bp;
register struct adk *dkp = (struct adk *)WR(q)->ptr;
if (WR(q)==dkp->module->listnrq) {
dkp->module->listnrq = NULL;
dkp->module->type = 0;
}
while (bp = getq(q))
(*q->next->qinfo->putp)(q->next, bp);
dkp->q = NULL;
dkp->module = NULL;
}
/*
* output put procedure.
*/
adkoput(q, bp)
register struct queue *q;
register struct block *bp;
{
register struct adk *dkp = (struct adk *)q->ptr;
register struct dkmodule *modp = dkp->module;
register i;
switch (bp->type) {
case M_IOCTL:
switch (stiocom(bp)) {
/* hang everybody up */
case DIOCHUP:
/* must be on listener chan */
if (q == modp->listnrq) {
adkmesg(modp, T_LSTNR, 0, 0, 0, 1);
bp->type = M_IOCACK;
bp->rptr = bp->wptr;
qreply(q, bp);
return;
}
break;
/* announce listener channel */
case DIOCLHN:
if (modp->listnrq == NULL) {
modp->listnrq = q;
RD(q)->flag |= QNOENB;
modp->type = CMCLD;
adkmesg(modp, T_LSTNR, 0, 0, 0, 0);
bp->type = M_IOCACK;
bp->rptr = bp->wptr;
qreply(q, bp);
return;
}
break;
/* delay input */
case DIOCSTOP:
RD(q)->ptr = (caddr_t)1;
bp->type = M_IOCACK;
bp->rptr = bp->wptr;
qreply(q, bp);
return;
/* suggest a free outgoing channel */
case DIOCCHAN:
for (i=modp->lo + 3; i<modp->hi; i+=2) {
if (modp->dkstate[i]==DKCLOSED) {
stiodata(bp)[0] = i;
for (i = 1; i < sizeof(int); i++)
stiodata(bp)[i] = 0; /* silly */
bp->wptr = bp->rptr+STIOCHDR+sizeof(int);
bp->type = M_IOCACK;
break;
}
}
qreply(q, bp);
return;
default:
(*q->next->qinfo->putp)(q->next, bp);
return;
}
bp->type = M_IOCNAK;
bp->wptr = bp->rptr;
qreply(q, bp);
return;
}
putq(q, bp);
}
adkosrv(q)
register struct queue *q;
{
register struct block *bp;
while ((q->next->flag&QFULL)==0 && (bp = getq(q)))
(*q->next->qinfo->putp)(q->next, bp);
}
/*
* input put procedure
* -- ignores all control bytes
*/
adkiput(q, bp)
register struct queue *q;
register struct block *bp;
{
register struct adk *dkp = (struct adk *)WR(q)->ptr;
register int delim;
switch (bp->type) {
case M_DATA:
if (WR(q) == dkp->module->listnrq) {
delim = bp->class & S_DELIM;
putq(q, bp);
if (delim)
qenable(q);
return;
}
if (q->next->flag&QFULL) { /* you lose */
freeb(bp);
return;
}
/* bp->class |= S_DELIM; always push through; this seems wrong */
if (q->ptr) { /* input delayed */
putq(q, bp);
return;
}
(*q->next->qinfo->putp)(q->next, bp);
return;
case M_IOCACK:
case M_IOCNAK:
case M_HANGUP:
(*q->next->qinfo->putp)(q->next, bp);
return;
case M_PRICTL:
switch (*bp->rptr) {
case DKMCLOSE:
if (dkp->module->dkstate[bp->rptr[1]] == DKRCLOSE)
adkmesg(dkp->module, T_CHG, D_ISCLOSED, 0, bp->rptr[1], 0);
else
adkmesg(dkp->module, T_CHG, D_CLOSE, 0, bp->rptr[1], 0);
freeb(bp);
return;
default:
freeb(bp);
return;
}
default:
freeb(bp);
}
}
/*
* listener sends a message to CMC
*/
adkmesg(modp, type, srv, p0, p1, p2)
register struct dkmodule *modp;
{
register struct dialout *dp;
register struct block *bp;
if (modp->listnrq==NULL || modp->listnrq->next->flag&QFULL)
return;
if ((bp = allocb(sizeof(struct dialout))) == NULL)
return; /* hope it will be retried later */
dp = (struct dialout *)bp->wptr;
dp->chan = ((struct adk *)modp->listnrq->ptr)->chan;
dp->d.type = type;
dp->d.srv = srv;
dp->d.param0 = p0;
dp->d.param1 = p1;
dp->d.param2 = p2;
bp->wptr += sizeof(struct dialout);
bp->class |= S_DELIM;
(*modp->listnrq->next->qinfo->putp)(modp->listnrq->next, bp);
}
/* try to construct a struct dialin from the message */
struct block *
adkdialin(q, bp)
register struct queue *q;
register struct block *bp;
{
register struct block *nbp;
register int len;
register int delim;
register int n;
register int over;
if ((len = bp->wptr - bp->rptr) == sizeof(struct dialin))
return bp;
n = sizeof(struct dialin) - len;
delim = bp->class & S_DELIM;
if (bp->lim - bp->rptr >= sizeof(struct dialin))
nbp = bp;
else if ((nbp = allocb(sizeof(struct dialin))) == NULL) {
freeb(bp);
return NULL;
} else {
bcopy(bp->rptr, nbp->wptr, len);
nbp->wptr += len;
freeb(bp);
}
over = 0;
while (!delim && (bp = getq(q))) {
len = bp->wptr - bp->rptr <= n ? bp->wptr - bp->rptr : (over = 1, n);
bcopy(bp->rptr, nbp->wptr, len);
nbp->wptr += len;
n -= len;
delim = bp->class & S_DELIM;
freeb(bp);
}
if (n == 0 && !over && delim)
return nbp;
freeb(nbp);
return NULL;
}
/*
* Receive message for listener
*/
adkisrv(q)
register struct queue *q;
{
register struct adk *dkp = (struct adk *)WR(q)->ptr;
struct dkmodule *modp = dkp->module;
register struct queue *listnrq = modp->listnrq;
register struct block *bp;
register struct dialin *dialp;
register int i;
if (WR(q) != modp->listnrq)
return;
while (bp = getq(q)) {
switch (*bp->rptr) {
case T_CALL:
bp->rptr++;
if (bp->wptr - bp->rptr <= 0 &&
((bp->class & S_DELIM) == 0 || (bp = getq(q)) == NULL))
return;
do {
i = bp->class & S_DELIM;
if (q->next->flag & QFULL)
free(bp);
else
(*q->next->qinfo->putp)(q->next, bp);
} while (i == 0 && (bp = getq(q)));
break;
case T_CHG:
if ((bp = adkdialin(q, bp)) == NULL)
break;
dialp = (struct dialin *)bp->rptr;
i = dialp->param1;
if (i <= 0 || i >= modp->hi - modp->lo) {
dkstat.chgstrange++;
if (dialp->srv)
adkmesg(modp, T_CHG, D_ISCLOSED, 0, i, 0);
freeb(bp);
break;
}
switch (dialp->srv) {
case D_CLOSE: /* remote shutdown */
switch (modp->dkstate[i]) {
case DKRCLOSE:
dkstat.notclosed++;
case DKOPEN:
putctl2(listnrq->next, M_PRICTL, DKMCLOSE, i);
break;
case DKLCLOSE:
case DKCLOSED:
adkmesg(modp, T_CHG, D_ISCLOSED, 0, i, 0);
putctl2(listnrq->next, M_PRICTL, DKMCLOSE, i);
break;
}
break;
case D_ISCLOSED:
switch (modp->dkstate[i]) {
case DKLCLOSE:
case DKCLOSED:
putctl2(listnrq->next, M_PRICTL, DKMCLOSE, i);
break;
case DKOPEN:
case DKRCLOSE:
dkstat.isclosed++;
break;
}
break;
case D_ENQ:
adkmesg(modp, T_CHG, D_ENQANS, 0, i, modp->dkstate[i]);
break;
}
freeb(bp);
break;
case T_REPLY: /* CMC sends reply; find, and hand to process */
if ((bp = adkdialin(q, bp)) == NULL)
break;
dialp = (struct dialin *)bp->rptr;
i = dialp->param0;
if (i < 0 || i >= modp->hi - modp->lo) {
free(bp);
break;
}
for (dkp=adk; dkp<&adk[NDKRAW]; dkp++) {
if (dkp->module==modp && dkp->chan==i) {
bp->class |= S_DELIM;
(*dkp->q->next->qinfo->putp)(dkp->q->next, bp);
break;
}
}
if (dkp == &adk[NDKRAW])
free(bp);
break;
default:
free(bp);
break;
}
}
}
/*
* 15-second timer
* -- should look at dkmod->type, so different
* listeners can coexist
*/
adktimer()
{
register i;
register struct dkmodule *dkp;
for (dkp = dkmod; dkp < &dkmod[dkmodcnt]; dkp++) {
if (dkp->listnrq && dkp->type == CMCLD) {
adkmesg(dkp, T_LSTNR, 0, 0, 0, 0);
for (i=dkp->hi - dkp->lo - 1; i >= 0; --i)
if (dkp->dkstate[i] == DKLCLOSE)
adkmesg(dkp, T_CHG, D_CLOSE, 0, i, 0);
}
}
timeout(adktimer, (caddr_t)NULL, 15*HZ);
}