#include <sys/types.h>
#include <sys/param.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/strsun.h>
#include <sys/termio.h>
#include <sys/pcb.h>
#include <sys/signal.h>
#include <sys/cred.h>
#include <sys/strtty.h>
#include <sys/errno.h>
#include <sys/cmn_err.h>
#include <sys/jioctl.h>
#include <sys/ptem.h>
#include <sys/ptms.h>
#include <sys/debug.h>
#include <sys/kmem.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/conf.h>
#include <sys/modctl.h>
extern struct streamtab pteminfo;
static struct fmodsw fsw = {
"ptem",
&pteminfo,
D_MTQPAIR | D_MP | _D_SINGLE_INSTANCE
};
static struct modlstrmod modlstrmod = {
&mod_strmodops, "pty hardware emulator", &fsw
};
static struct modlinkage modlinkage = {
MODREV_1, &modlstrmod, NULL
};
int
_init()
{
return (mod_install(&modlinkage));
}
int
_fini()
{
return (mod_remove(&modlinkage));
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int ptemopen(queue_t *, dev_t *, int, int, cred_t *);
static int ptemclose(queue_t *, int, cred_t *);
static int ptemrput(queue_t *, mblk_t *);
static int ptemwput(queue_t *, mblk_t *);
static int ptemwsrv(queue_t *);
static struct module_info ptem_info = {
0xabcd,
"ptem",
0,
_TTY_BUFSIZ,
_TTY_BUFSIZ,
128
};
static struct qinit ptemrinit = {
ptemrput,
NULL,
ptemopen,
ptemclose,
NULL,
&ptem_info,
NULL
};
static struct qinit ptemwinit = {
ptemwput,
ptemwsrv,
ptemopen,
ptemclose,
nulldev,
&ptem_info,
NULL
};
struct streamtab pteminfo = {
&ptemrinit,
&ptemwinit,
NULL,
NULL
};
static void ptioc(queue_t *, mblk_t *, int);
static int ptemwmsg(queue_t *, mblk_t *);
static int
ptemopen(
queue_t *q,
dev_t *devp,
int oflag,
int sflag,
cred_t *credp)
{
struct ptem *ntp;
mblk_t *mop;
struct stroptions *sop;
struct termios *termiosp;
int len;
if (sflag != MODOPEN)
return (EINVAL);
if (q->q_ptr != NULL) {
return (0);
}
ntp = kmem_alloc(sizeof (*ntp), KM_SLEEP);
if ((ntp->dack_ptr = allocb(4, BPRI_MED)) == NULL) {
kmem_free(ntp, sizeof (*ntp));
return (EAGAIN);
}
mop = allocb(sizeof (struct stroptions), BPRI_MED);
if (mop == NULL) {
freemsg(ntp->dack_ptr);
kmem_free(ntp, sizeof (*ntp));
return (EAGAIN);
}
mop->b_datap->db_type = M_SETOPTS;
mop->b_wptr += sizeof (struct stroptions);
sop = (struct stroptions *)mop->b_rptr;
sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY;
sop->so_hiwat = _TTY_BUFSIZ;
sop->so_lowat = 256;
ntp->q_ptr = q;
q->q_ptr = ntp;
WR(q)->q_ptr = ntp;
if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(), 0, "ttymodes",
(caddr_t)&termiosp, &len) == DDI_PROP_SUCCESS &&
len == sizeof (struct termios)) {
ntp->cflags = termiosp->c_cflag;
kmem_free(termiosp, len);
} else {
cmn_err(CE_WARN, "ptem: Couldn't get ttymodes property!");
}
ntp->wsz.ws_row = 0;
ntp->wsz.ws_col = 0;
ntp->wsz.ws_xpixel = 0;
ntp->wsz.ws_ypixel = 0;
ntp->state = 0;
qprocson(q);
putnext(q, mop);
return (0);
}
static int
ptemclose(queue_t *q, int flag, cred_t *credp)
{
struct ptem *ntp;
qprocsoff(q);
ntp = (struct ptem *)q->q_ptr;
freemsg(ntp->dack_ptr);
kmem_free(ntp, sizeof (*ntp));
q->q_ptr = WR(q)->q_ptr = NULL;
return (0);
}
static int
ptemrput(queue_t *q, mblk_t *mp)
{
struct iocblk *iocp;
struct copyresp *resp;
int error;
switch (mp->b_datap->db_type) {
case M_DELAY:
case M_READ:
freemsg(mp);
break;
case M_IOCTL:
iocp = (struct iocblk *)mp->b_rptr;
switch (iocp->ioc_cmd) {
case TCSBRK:
error = miocpullup(mp, sizeof (int));
if (error != 0) {
miocnak(q, mp, 0, error);
break;
}
if (!(*(int *)mp->b_cont->b_rptr)) {
if (!putnextctl(q, M_BREAK)) {
miocnak(q, mp, 0, EAGAIN);
break;
}
}
mioc2ack(mp, NULL, 0, 0);
qreply(q, mp);
break;
case JWINSIZE:
case TIOCGWINSZ:
case TIOCSWINSZ:
ptioc(q, mp, RDSIDE);
break;
case TIOCSIGNAL:
if (iocp->ioc_count == TRANSPARENT) {
intptr_t sig = *(intptr_t *)mp->b_cont->b_rptr;
if (sig < 1 || sig >= NSIG) {
mcopyin(mp, NULL, sizeof (int), NULL);
qreply(q, mp);
break;
}
}
ptioc(q, mp, RDSIDE);
break;
case TIOCREMOTE:
if (iocp->ioc_count != TRANSPARENT)
ptioc(q, mp, RDSIDE);
else {
mcopyin(mp, NULL, sizeof (int), NULL);
qreply(q, mp);
}
break;
default:
putnext(q, mp);
break;
}
break;
case M_IOCDATA:
resp = (struct copyresp *)mp->b_rptr;
if (resp->cp_rval) {
freemsg(mp);
break;
}
switch (resp->cp_cmd) {
case TIOCSWINSZ:
case TIOCSIGNAL:
case TIOCREMOTE:
ptioc(q, mp, RDSIDE);
break;
case JWINSIZE:
case TIOCGWINSZ:
mp->b_datap->db_type = M_IOCACK;
mioc2ack(mp, NULL, 0, 0);
qreply(q, mp);
break;
default:
freemsg(mp);
break;
}
break;
case M_IOCACK:
case M_IOCNAK:
freemsg(mp);
break;
case M_HANGUP:
{
struct ptem *ntp = (struct ptem *)q->q_ptr;
if (ntp->state & OFLOW_CTL) {
ntp->state &= ~OFLOW_CTL;
qenable(WR(q));
}
}
default:
putnext(q, mp);
break;
}
return (0);
}
static int
ptemwput(queue_t *q, mblk_t *mp)
{
struct ptem *ntp = (struct ptem *)q->q_ptr;
struct iocblk *iocp;
struct copyresp *resp;
unsigned char type = mp->b_datap->db_type;
if (type >= QPCTL) {
switch (type) {
case M_IOCDATA:
resp = (struct copyresp *)mp->b_rptr;
if (resp->cp_rval) {
freemsg(mp);
break;
}
switch (resp->cp_cmd) {
case TIOCSWINSZ:
ptioc(q, mp, WRSIDE);
break;
case JWINSIZE:
case TIOCGWINSZ:
mioc2ack(mp, NULL, 0, 0);
qreply(q, mp);
break;
default:
freemsg(mp);
}
break;
case M_FLUSH:
if (*mp->b_rptr & FLUSHW) {
if ((ntp->state & IS_PTSTTY) &&
(*mp->b_rptr & FLUSHBAND))
flushband(q, *(mp->b_rptr + 1),
FLUSHDATA);
else
flushq(q, FLUSHDATA);
}
putnext(q, mp);
break;
case M_READ:
freemsg(mp);
break;
case M_STOP:
ntp->state |= OFLOW_CTL;
putnext(q, mp);
break;
case M_START:
ntp->state &= ~OFLOW_CTL;
putnext(q, mp);
qenable(q);
break;
default:
putnext(q, mp);
break;
}
return (0);
}
if (q->q_first != NULL || !bcanputnext(q, mp->b_band)) {
switch (type) {
case M_IOCTL:
iocp = (struct iocblk *)mp->b_rptr;
switch (iocp->ioc_cmd) {
case TCSETSW:
case TCSETSF:
case TCSETAW:
case TCSETAF:
case TCSBRK:
break;
default:
(void) ptemwmsg(q, mp);
return (0);
}
break;
case M_DELAY:
freemsg(mp);
return (0);
case M_DATA:
if ((mp->b_wptr - mp->b_rptr) < 0) {
freemsg(mp);
return (0);
} else if ((mp->b_wptr - mp->b_rptr) == 0) {
if (!(ntp->state & IS_PTSTTY)) {
freemsg(mp);
return (0);
}
}
}
(void) putq(q, mp);
return (0);
}
if (!ptemwmsg(q, mp))
(void) putq(q, mp);
return (0);
}
static int
ptemwsrv(queue_t *q)
{
mblk_t *mp;
while ((mp = getq(q)) != NULL) {
if (!bcanputnext(q, mp->b_band) || !ptemwmsg(q, mp)) {
(void) putbq(q, mp);
break;
}
}
return (0);
}
static int
ptemwmsg(queue_t *q, mblk_t *mp)
{
struct ptem *ntp = (struct ptem *)q->q_ptr;
struct iocblk *iocp;
struct termio *termiop;
struct termios *termiosp;
mblk_t *dack_ptr;
mblk_t *pckt_msgp;
mblk_t *dp;
tcflag_t cflags;
int error;
switch (mp->b_datap->db_type) {
case M_IOCTL:
iocp = (struct iocblk *)mp->b_rptr;
switch (iocp->ioc_cmd) {
case TCSETAF:
case TCSETSF:
if (putnextctl1(q, M_FLUSH, FLUSHR) == 0) {
miocnak(q, mp, 0, EAGAIN);
break;
}
case TCSETA:
case TCSETAW:
case TCSETS:
case TCSETSW:
switch (iocp->ioc_cmd) {
case TCSETAF:
case TCSETA:
case TCSETAW:
error = miocpullup(mp, sizeof (struct termio));
if (error != 0) {
miocnak(q, mp, 0, error);
goto out;
}
cflags = ((struct termio *)
mp->b_cont->b_rptr)->c_cflag;
ntp->cflags =
(ntp->cflags & 0xffff0000 | cflags);
break;
case TCSETSF:
case TCSETS:
case TCSETSW:
error = miocpullup(mp, sizeof (struct termios));
if (error != 0) {
miocnak(q, mp, 0, error);
goto out;
}
cflags = ((struct termios *)
mp->b_cont->b_rptr)->c_cflag;
ntp->cflags = cflags;
break;
}
if ((cflags & CBAUD) == B0) {
dack_ptr = ntp->dack_ptr;
if (dack_ptr) {
ntp->dack_ptr = NULL;
putnext(q, dack_ptr);
}
} else {
if ((pckt_msgp = copymsg(mp)) == NULL) {
miocnak(q, mp, 0, EAGAIN);
break;
}
putnext(q, pckt_msgp);
}
mioc2ack(mp, NULL, 0, 0);
qreply(q, mp);
out:
break;
case TCGETA:
dp = allocb(sizeof (struct termio), BPRI_MED);
if (dp == NULL) {
miocnak(q, mp, 0, EAGAIN);
break;
}
termiop = (struct termio *)dp->b_rptr;
termiop->c_cflag = (ushort_t)ntp->cflags;
mioc2ack(mp, dp, sizeof (struct termio), 0);
qreply(q, mp);
break;
case TCGETS:
dp = allocb(sizeof (struct termios), BPRI_MED);
if (dp == NULL) {
miocnak(q, mp, 0, EAGAIN);
break;
}
termiosp = (struct termios *)dp->b_rptr;
termiosp->c_cflag = ntp->cflags;
mioc2ack(mp, dp, sizeof (struct termios), 0);
qreply(q, mp);
break;
case TCSBRK:
error = miocpullup(mp, sizeof (int));
if (error != 0) {
miocnak(q, mp, 0, error);
break;
}
if ((pckt_msgp = copymsg(mp)) == NULL) {
miocnak(q, mp, 0, EAGAIN);
break;
}
putnext(q, pckt_msgp);
if (!(*(int *)mp->b_cont->b_rptr))
(void) putnextctl(q, M_BREAK);
mioc2ack(mp, NULL, 0, 0);
qreply(q, mp);
break;
case JWINSIZE:
case TIOCGWINSZ:
case TIOCSWINSZ:
ptioc(q, mp, WRSIDE);
break;
case TIOCSTI:
if ((pckt_msgp = copymsg(mp)) == NULL) {
miocnak(q, mp, 0, EAGAIN);
break;
}
if ((ntp->state & REMOTEMODE) == 0) {
mblk_t *bp;
error = miocpullup(mp, sizeof (char));
if (error != 0) {
freemsg(pckt_msgp);
miocnak(q, mp, 0, error);
break;
}
if ((bp = allocb(1, BPRI_MED)) == NULL) {
freemsg(pckt_msgp);
miocnak(q, mp, 0, EAGAIN);
break;
}
if (!bcanputnext(RD(q), mp->b_band)) {
freemsg(bp);
freemsg(pckt_msgp);
miocnak(q, mp, 0, EAGAIN);
break;
}
*bp->b_wptr++ = *mp->b_cont->b_rptr;
qreply(q, bp);
}
putnext(q, pckt_msgp);
mioc2ack(mp, NULL, 0, 0);
qreply(q, mp);
break;
case PTSSTTY:
if (ntp->state & IS_PTSTTY) {
miocnak(q, mp, 0, EEXIST);
} else {
ntp->state |= IS_PTSTTY;
mioc2ack(mp, NULL, 0, 0);
qreply(q, mp);
}
break;
default:
miocnak(q, mp, 0, EINVAL);
break;
}
break;
case M_DELAY:
freemsg(mp);
break;
case M_DATA:
if ((mp->b_wptr - mp->b_rptr) < 0) {
freemsg(mp);
break;
} else if ((mp->b_wptr - mp->b_rptr) == 0) {
if (!(ntp->state & IS_PTSTTY)) {
freemsg(mp);
break;
}
}
if (ntp->state & OFLOW_CTL)
return (0);
default:
putnext(q, mp);
break;
}
return (1);
}
static void
ptioc(queue_t *q, mblk_t *mp, int qside)
{
struct ptem *tp;
struct iocblk *iocp;
struct winsize *wb;
struct jwinsize *jwb;
mblk_t *tmp;
mblk_t *pckt_msgp;
int error;
iocp = (struct iocblk *)mp->b_rptr;
tp = (struct ptem *)q->q_ptr;
switch (iocp->ioc_cmd) {
case JWINSIZE:
if ((tp->wsz.ws_row == 0) && (tp->wsz.ws_col == 0) &&
(tp->wsz.ws_xpixel == 0) && (tp->wsz.ws_ypixel == 0)) {
miocnak(q, mp, 0, EINVAL);
return;
}
tmp = allocb(sizeof (struct jwinsize), BPRI_MED);
if (tmp == NULL) {
miocnak(q, mp, 0, EAGAIN);
return;
}
if (iocp->ioc_count == TRANSPARENT)
mcopyout(mp, NULL, sizeof (struct jwinsize), NULL, tmp);
else
mioc2ack(mp, tmp, sizeof (struct jwinsize), 0);
jwb = (struct jwinsize *)mp->b_cont->b_rptr;
jwb->bytesx = tp->wsz.ws_col;
jwb->bytesy = tp->wsz.ws_row;
jwb->bitsx = tp->wsz.ws_xpixel;
jwb->bitsy = tp->wsz.ws_ypixel;
qreply(q, mp);
return;
case TIOCGWINSZ:
tmp = allocb(sizeof (struct winsize), BPRI_MED);
if (tmp == NULL) {
miocnak(q, mp, 0, EAGAIN);
return;
}
mioc2ack(mp, tmp, sizeof (struct winsize), 0);
wb = (struct winsize *)mp->b_cont->b_rptr;
wb->ws_row = tp->wsz.ws_row;
wb->ws_col = tp->wsz.ws_col;
wb->ws_xpixel = tp->wsz.ws_xpixel;
wb->ws_ypixel = tp->wsz.ws_ypixel;
qreply(q, mp);
return;
case TIOCSWINSZ:
error = miocpullup(mp, sizeof (struct winsize));
if (error != 0) {
miocnak(q, mp, 0, error);
return;
}
wb = (struct winsize *)mp->b_cont->b_rptr;
if ((tp->wsz.ws_row != wb->ws_row) ||
(tp->wsz.ws_col != wb->ws_col) ||
(tp->wsz.ws_xpixel != wb->ws_xpixel) ||
(tp->wsz.ws_ypixel != wb->ws_xpixel)) {
if (qside == WRSIDE)
(void) putnextctl1(RD(q), M_SIG, SIGWINCH);
else if (qside == RDSIDE)
(void) putnextctl1(q, M_SIG, SIGWINCH);
mp->b_datap->db_type = M_IOCTL;
if (qside == WRSIDE) {
if ((pckt_msgp = copymsg(mp)) == NULL) {
miocnak(q, mp, 0, EAGAIN);
return;
}
putnext(q, pckt_msgp);
}
tp->wsz.ws_row = wb->ws_row;
tp->wsz.ws_col = wb->ws_col;
tp->wsz.ws_xpixel = wb->ws_xpixel;
tp->wsz.ws_ypixel = wb->ws_ypixel;
}
mioc2ack(mp, NULL, 0, 0);
qreply(q, mp);
return;
case TIOCSIGNAL: {
int sig;
if (DB_TYPE(mp) == M_IOCTL && iocp->ioc_count != TRANSPARENT) {
error = miocpullup(mp, sizeof (int));
if (error != 0) {
miocnak(q, mp, 0, error);
return;
}
}
if (DB_TYPE(mp) == M_IOCDATA || iocp->ioc_count != TRANSPARENT)
sig = *(int *)mp->b_cont->b_rptr;
else
sig = (int)*(intptr_t *)mp->b_cont->b_rptr;
if (sig < 1 || sig >= NSIG) {
miocnak(q, mp, 0, EINVAL);
return;
}
if (putnextctl1(q, M_PCSIG, sig) == 0) {
miocnak(q, mp, 0, EAGAIN);
return;
}
mioc2ack(mp, NULL, 0, 0);
qreply(q, mp);
return;
}
case TIOCREMOTE: {
int onoff;
mblk_t *mctlp;
if (DB_TYPE(mp) == M_IOCTL) {
error = miocpullup(mp, sizeof (int));
if (error != 0) {
miocnak(q, mp, 0, error);
return;
}
}
onoff = *(int *)mp->b_cont->b_rptr;
mctlp = mkiocb(onoff ? MC_NO_CANON : MC_DO_CANON);
if (mctlp == NULL) {
miocnak(q, mp, 0, EAGAIN);
return;
}
mctlp->b_datap->db_type = M_CTL;
putnext(q, mctlp);
mioc2ack(mp, NULL, 0, 0);
qreply(q, mp);
if (onoff)
tp->state |= REMOTEMODE;
else
tp->state &= ~REMOTEMODE;
return;
}
default:
putnext(q, mp);
return;
}
}