#include <sys/types.h>
#include <sys/param.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/strsun.h>
#include <sys/kmem.h>
#include <sys/errno.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/tihdr.h>
#include <sys/ptem.h>
#include <sys/conf.h>
#include <sys/debug.h>
#include <sys/modctl.h>
#include <sys/vtrace.h>
#include <sys/rlioctl.h>
#include <sys/termios.h>
#include <sys/termio.h>
#include <sys/byteorder.h>
#include <sys/cmn_err.h>
#include <sys/cryptmod.h>
extern struct streamtab rloginmodinfo;
static struct fmodsw fsw = {
"rlmod",
&rloginmodinfo,
D_MTQPAIR | D_MP
};
static struct modlstrmod modlstrmod = {
&mod_strmodops,
"rloginmod module",
&fsw
};
static struct modlinkage modlinkage = {
MODREV_1, &modlstrmod, NULL
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
struct rlmod_info;
static int rlmodopen(queue_t *, dev_t *, int, int, cred_t *);
static int rlmodclose(queue_t *, int, cred_t *);
static int rlmodrput(queue_t *, mblk_t *);
static int rlmodrsrv(queue_t *);
static int rlmodwput(queue_t *, mblk_t *);
static int rlmodwsrv(queue_t *);
static int rlmodrmsg(queue_t *, mblk_t *);
static mblk_t *make_expmblk(char);
static int rlwinctl(queue_t *, mblk_t *);
static mblk_t *rlwinsetup(queue_t *, mblk_t *, unsigned char *);
static void rlmod_timer(void *);
static void rlmod_buffer(void *);
static boolean_t tty_flow(queue_t *, struct rlmod_info *, mblk_t *);
static boolean_t rlmodwioctl(queue_t *, mblk_t *);
static void recover(queue_t *, mblk_t *, size_t);
static void recover1(queue_t *, size_t);
#define RLMOD_ID 106
#define SIMWAIT (1*hz)
static struct module_info rloginmodiinfo = {
RLMOD_ID,
"rlmod",
0,
INFPSZ,
512,
256
};
static struct qinit rloginmodrinit = {
rlmodrput,
rlmodrsrv,
rlmodopen,
rlmodclose,
nulldev,
&rloginmodiinfo,
NULL
};
static struct qinit rloginmodwinit = {
rlmodwput,
rlmodwsrv,
NULL,
NULL,
nulldev,
&rloginmodiinfo,
NULL
};
struct streamtab rloginmodinfo = {
&rloginmodrinit,
&rloginmodwinit,
NULL,
NULL
};
struct rlmod_info
{
int flags;
bufcall_id_t wbufcid;
bufcall_id_t rbufcid;
timeout_id_t wtimoutid;
timeout_id_t rtimoutid;
int rl_expdat;
int stopmode;
mblk_t *unbind_mp;
char startc;
char stopc;
char oobdata[1];
mblk_t *wndw_sz_hd_mp;
};
#define RL_DISABLED 0x1
#define RL_IOCPASSTHRU 0x2
static void
dummy_callback(void *arg)
{}
static int
rlmodopen(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *cred)
{
struct rlmod_info *rmip;
union T_primitives *tp;
mblk_t *bp;
int error;
if (sflag != MODOPEN)
return (EINVAL);
if (q->q_ptr != NULL) {
return (0);
}
rmip = kmem_zalloc(sizeof (*rmip), KM_SLEEP);
q->q_ptr = rmip;
WR(q)->q_ptr = rmip;
rmip->rl_expdat = 0;
rmip->stopmode = TIOCPKT_DOSTOP;
rmip->startc = CTRL('q');
rmip->stopc = CTRL('s');
rmip->oobdata[0] = (char)TIOCPKT_WINDOW;
rmip->wndw_sz_hd_mp = NULL;
rmip->flags |= RL_DISABLED;
qprocson(q);
while ((rmip->unbind_mp = allocb(sizeof (union T_primitives),
BPRI_HI)) == NULL) {
bufcall_id_t id = qbufcall(q, sizeof (union T_primitives),
BPRI_HI, dummy_callback, NULL);
if (!qwait_sig(q)) {
qunbufcall(q, id);
error = EINTR;
goto fail;
}
qunbufcall(q, id);
}
rmip->unbind_mp->b_wptr = rmip->unbind_mp->b_rptr +
sizeof (struct T_unbind_req);
rmip->unbind_mp->b_datap->db_type = M_PROTO;
tp = (union T_primitives *)rmip->unbind_mp->b_rptr;
tp->type = T_UNBIND_REQ;
while ((bp = allocb(sizeof (union T_primitives), BPRI_HI)) == NULL) {
bufcall_id_t id = qbufcall(q, sizeof (union T_primitives),
BPRI_HI, dummy_callback, NULL);
if (!qwait_sig(q)) {
qunbufcall(q, id);
error = EINTR;
goto fail;
}
qunbufcall(q, id);
}
bp->b_datap->db_type = M_PROTO;
bp->b_wptr = bp->b_rptr + sizeof (union T_primitives);
tp = (union T_primitives *)bp->b_rptr;
tp->type = T_DATA_REQ;
tp->data_req.MORE_flag = 0;
putnext(q, bp);
return (0);
fail:
qprocsoff(q);
if (rmip->unbind_mp != NULL) {
freemsg(rmip->unbind_mp);
}
kmem_free(rmip, sizeof (struct rlmod_info));
q->q_ptr = NULL;
WR(q)->q_ptr = NULL;
return (error);
}
static int
rlmodclose(queue_t *q, int flag, cred_t *credp)
{
struct rlmod_info *rmip = (struct rlmod_info *)q->q_ptr;
mblk_t *mp;
while (mp = getq(WR(q)))
putnext(WR(q), mp);
(void) putnextctl(q, M_HANGUP);
qprocsoff(q);
if (rmip->wbufcid) {
qunbufcall(q, rmip->wbufcid);
rmip->wbufcid = 0;
}
if (rmip->rbufcid) {
qunbufcall(q, rmip->rbufcid);
rmip->rbufcid = 0;
}
if (rmip->wtimoutid) {
(void) quntimeout(q, rmip->wtimoutid);
rmip->wtimoutid = 0;
}
if (rmip->rtimoutid) {
(void) quntimeout(q, rmip->rtimoutid);
rmip->rtimoutid = 0;
}
if (rmip->unbind_mp != NULL) {
freemsg(rmip->unbind_mp);
}
if (rmip->wndw_sz_hd_mp != NULL) {
freemsg(rmip->wndw_sz_hd_mp);
}
kmem_free(q->q_ptr, sizeof (struct rlmod_info));
q->q_ptr = WR(q)->q_ptr = NULL;
return (0);
}
static int
rlmodrput(queue_t *q, mblk_t *mp)
{
struct rlmod_info *rmip = (struct rlmod_info *)q->q_ptr;
union T_primitives *tip;
TRACE_2(TR_FAC_RLOGINP, TR_RLOGINP_RPUT_IN, "rlmodrput start: "
"q %p, mp %p", q, mp);
if ((mp->b_datap->db_type < QPCTL) &&
((q->q_first) ||
((rmip->flags & RL_DISABLED) &&
(mp->b_datap->db_type == M_DATA)))) {
(void) putq(q, mp);
TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_RPUT_OUT,
"rlmodrput end: q %p, mp %p, %s", q, mp, "flow");
return (0);
}
switch (mp->b_datap->db_type) {
case M_PROTO:
case M_PCPROTO:
tip = (union T_primitives *)mp->b_rptr;
switch (tip->type) {
case T_ORDREL_IND:
case T_DISCON_IND:
mp->b_datap->db_type = M_HANGUP;
mp->b_wptr = mp->b_rptr;
if (mp->b_cont) {
freemsg(mp->b_cont);
mp->b_cont = NULL;
}
if (tip->type == T_DISCON_IND && rmip->unbind_mp !=
NULL) {
putnext(q, mp);
qreply(q, rmip->unbind_mp);
rmip->unbind_mp = NULL;
} else {
putnext(q, mp);
}
break;
case T_OK_ACK:
ASSERT(rmip->unbind_mp == NULL);
freemsg(mp);
break;
default:
cmn_err(CE_NOTE,
"rlmodrput: got 0x%x type M_PROTO/M_PCPROTO msg",
tip->type);
freemsg(mp);
}
break;
case M_DATA:
if (canputnext(q) && q->q_first == NULL) {
(void) rlmodrmsg(q, mp);
} else {
(void) putq(q, mp);
}
break;
case M_FLUSH:
mp->b_flag |= MSGMARK;
if (*mp->b_rptr & FLUSHR)
flushq(q, FLUSHALL);
putnext(q, mp);
break;
case M_PCSIG:
case M_ERROR:
case M_IOCACK:
case M_IOCNAK:
case M_SETOPTS:
if (mp->b_datap->db_type <= QPCTL && !canputnext(q))
(void) putq(q, mp);
else
putnext(q, mp);
break;
default:
#ifdef DEBUG
cmn_err(CE_NOTE, "rlmodrput: unexpected msg type 0x%x",
mp->b_datap->db_type);
#endif
freemsg(mp);
}
TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_RPUT_OUT, "rlmodrput end: q %p, "
"mp %p, %s", q, mp, "done");
return (0);
}
static int
rlmodrsrv(queue_t *q)
{
mblk_t *mp;
struct rlmod_info *rmip = (struct rlmod_info *)q->q_ptr;
union T_primitives *tip;
TRACE_1(TR_FAC_RLOGINP, TR_RLOGINP_RSRV_IN, "rlmodrsrv start: "
"q %p", q);
while ((mp = getq(q)) != NULL) {
switch (mp->b_datap->db_type) {
case M_DATA:
if (rmip->flags & RL_DISABLED) {
(void) putbq(q, mp);
TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_RSRV_OUT,
"rlmodrsrv end: q %p, mp %p, %s", q, mp,
"disabled");
return (0);
}
if (!canputnext(q)) {
(void) putbq(q, mp);
TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_RSRV_OUT,
"rlmodrsrv end: q %p, mp %p, %s",
q, mp, "!canputnext");
return (0);
}
if (!rlmodrmsg(q, mp)) {
TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_RSRV_OUT,
"rlmodrsrv end: q %p, mp %p, %s",
q, mp, "!rlmodrmsg");
return (0);
}
break;
case M_PROTO:
tip = (union T_primitives *)mp->b_rptr;
switch (tip->type) {
case T_ORDREL_IND:
case T_DISCON_IND:
mp->b_datap->db_type = M_HANGUP;
mp->b_wptr = mp->b_rptr;
if (mp->b_cont) {
freemsg(mp->b_cont);
mp->b_cont = NULL;
}
if (tip->type == T_DISCON_IND &&
rmip->unbind_mp != NULL) {
putnext(q, mp);
qreply(q, rmip->unbind_mp);
rmip->unbind_mp = NULL;
} else {
putnext(q, mp);
}
break;
case T_OK_ACK:
ASSERT(rmip->unbind_mp == NULL);
freemsg(mp);
break;
default:
cmn_err(CE_NOTE,
"rlmodrsrv: got 0x%x type PROTO msg",
tip->type);
freemsg(mp);
}
break;
case M_SETOPTS:
if (!canputnext(q)) {
(void) putbq(q, mp);
TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_RSRV_OUT,
"rlmodrsrv end: q %p, mp %p, %s",
q, mp, "!canputnext M_SETOPTS");
return (0);
}
putnext(q, mp);
break;
default:
#ifdef DEBUG
cmn_err(CE_NOTE,
"rlmodrsrv: unexpected msg type 0x%x",
mp->b_datap->db_type);
#endif
freemsg(mp);
}
}
TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_RSRV_OUT, "rlmodrsrv end: q %p, "
"mp %p, %s", q, mp, "empty");
return (0);
}
static int
rlmodwput(queue_t *q, mblk_t *mp)
{
char cntl;
struct rlmod_info *rmip = (struct rlmod_info *)q->q_ptr;
mblk_t *tmpmp;
int rw;
TRACE_2(TR_FAC_RLOGINP, TR_RLOGINP_WPUT_IN, "rlmodwput start: "
"q %p, mp %p", q, mp);
if (rmip->rl_expdat) {
cntl = rmip->oobdata[0] | TIOCPKT_FLUSHWRITE;
if (!canputnext(q)) {
(void) putq(q, mp);
TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WPUT_OUT,
"rlmodwput end: q %p, mp %p, %s",
q, mp, "expdata && !canputnext");
return (0);
}
if ((tmpmp = make_expmblk(cntl))) {
putnext(q, tmpmp);
rmip->rl_expdat = 0;
} else {
recover1(q, sizeof (mblk_t));
}
}
if ((q->q_first || rmip->rl_expdat) && mp->b_datap->db_type < QPCTL) {
(void) putq(q, mp);
TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WPUT_OUT, "rlmodwput end: "
"q %p, mp %p, %s", q, mp, "queued data");
return (0);
}
switch (mp->b_datap->db_type) {
case M_DATA:
if (!canputnext(q))
(void) putq(q, mp);
else
putnext(q, mp);
break;
case M_FLUSH:
rw = *mp->b_rptr;
*mp->b_rptr &= ~FLUSHW;
qreply(q, mp);
if (rw & FLUSHW) {
flushq(q, FLUSHDATA);
(void) putnextctl1(q, M_FLUSH, FLUSHW);
cntl = rmip->oobdata[0] | TIOCPKT_FLUSHWRITE;
if (!canputnext(q)) {
TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WPUT_OUT,
"rlmodwput end: q %p, mp %p, %s",
q, mp, "flushw && !canputnext");
return (0);
}
if ((mp = make_expmblk(cntl)) == NULL) {
rmip->rl_expdat = 1;
recover1(q, sizeof (mblk_t));
TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WPUT_OUT,
"rlmodwput end: q %p, mp %p, %s",
q, mp, "!make_expmblk");
return (0);
}
putnext(q, mp);
}
break;
case M_IOCTL:
if (!rlmodwioctl(q, mp))
(void) putq(q, mp);
break;
case M_PROTO:
switch (((union T_primitives *)mp->b_rptr)->type) {
case T_EXDATA_REQ:
case T_ORDREL_REQ:
case T_DISCON_REQ:
putnext(q, mp);
break;
default:
#ifdef DEBUG
cmn_err(CE_NOTE,
"rlmodwput: unexpected TPI primitive 0x%x",
((union T_primitives *)mp->b_rptr)->type);
#endif
freemsg(mp);
}
break;
case M_PCPROTO:
if (((struct T_exdata_req *)mp->b_rptr)->PRIM_type ==
T_DISCON_REQ) {
putnext(q, mp);
} else {
freemsg(mp);
}
break;
default:
#ifdef DEBUG
cmn_err(CE_NOTE,
"rlmodwput: unexpected msg type 0x%x",
mp->b_datap->db_type);
#endif
freemsg(mp);
break;
}
TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WPUT_OUT, "rlmodwput end: "
"q %p, mp %p, %s", q, mp, "done");
return (0);
}
static int
rlmodwsrv(queue_t *q)
{
mblk_t *mp, *tmpmp;
char cntl;
struct rlmod_info *rmip = (struct rlmod_info *)q->q_ptr;
TRACE_1(TR_FAC_RLOGINP, TR_RLOGINP_WSRV_IN, "rlmodwsrv "
"start: q %p", q);
if (rmip->rl_expdat) {
cntl = rmip->oobdata[0] | TIOCPKT_FLUSHWRITE;
if (!canputnext(q)) {
TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WSRV_OUT,
"rlmodwsrv end: q %p, mp %p, %s",
q, NULL, "!canputnext && expdat");
return (0);
}
if ((tmpmp = make_expmblk(cntl))) {
putnext(q, tmpmp);
rmip->rl_expdat = 0;
} else {
recover1(q, sizeof (mblk_t));
TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WSRV_OUT,
"rlmodwsrv end: q %p, mp %p, %s",
q, NULL, "!make_expmblk");
return (0);
}
}
while ((mp = getq(q)) != NULL) {
if (!canputnext(q) || rmip->rl_expdat) {
(void) putbq(q, mp);
TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WSRV_OUT,
"rlmodwsrv end: q %p, mp %p, %s",
q, mp, "!canputnext || expdat");
return (0);
}
if (mp->b_datap->db_type == M_IOCTL) {
if (!rlmodwioctl(q, mp)) {
TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WSRV_OUT,
"rlmodwsrv end: q %p, mp %p, %s",
q, mp, "!rlmodwioctl");
(void) putbq(q, mp);
return (0);
}
continue;
}
putnext(q, mp);
}
TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WSRV_OUT, "rlmodwsrv end: q %p, "
"mp %p, %s", q, mp, "done");
return (0);
}
static mblk_t *
make_expmblk(char cntl)
{
mblk_t *mp;
mblk_t *bp;
struct T_exdata_req *data_req;
bp = allocb(sizeof (struct T_exdata_req), BPRI_MED);
if (bp == NULL)
return (NULL);
if ((mp = allocb(sizeof (char), BPRI_MED)) == NULL) {
freeb(bp);
return (NULL);
}
bp->b_datap->db_type = M_PROTO;
data_req = (struct T_exdata_req *)bp->b_rptr;
data_req->PRIM_type = T_EXDATA_REQ;
data_req->MORE_flag = 0;
bp->b_wptr += sizeof (struct T_exdata_req);
mp->b_datap->db_type = M_DATA;
mp->b_wptr = mp->b_rptr + 1;
(*(char *)(mp->b_rptr)) = cntl;
bp->b_cont = mp;
return (bp);
}
static int
rlmodrmsg(queue_t *q, mblk_t *mp)
{
unsigned char *tmp, *tmp1;
mblk_t *newmp;
size_t sz;
ssize_t count, newcount = 0;
struct rlmod_info *rmip = (struct rlmod_info *)q->q_ptr;
if (msgdsize(mp) == 0) {
ASSERT(rmip->wndw_sz_hd_mp == NULL);
goto out;
}
if (rmip->wndw_sz_hd_mp != NULL) {
linkb(rmip->wndw_sz_hd_mp, mp);
mp = rmip->wndw_sz_hd_mp;
rmip->wndw_sz_hd_mp = NULL;
}
newmp = mp;
while (mp) {
tmp = mp->b_rptr;
while (tmp < mp->b_wptr) {
if (tmp[0] == RLOGIN_MAGIC) {
count = newcount + tmp - mp->b_rptr;
if ((pullupmsg(newmp, -1)) == 0) {
sz = msgdsize(newmp);
recover(q, newmp, sz);
return (0);
}
mp = newmp;
tmp = mp->b_rptr + count;
newcount = 0;
tmp1 = tmp + 4 + sizeof (struct winsize);
if (tmp1 > mp->b_wptr) {
rmip->wndw_sz_hd_mp = mp;
return (TRUE);
}
if ((tmp[1] == RLOGIN_MAGIC) &&
(tmp[2] == 's') && (tmp[3] == 's')) {
if (rlwinsetup(q, mp, tmp) == NULL) {
sz = msgdsize(mp);
recover(q, mp, sz);
return (0);
}
continue;
}
}
tmp++;
}
newcount += (mp->b_wptr - mp->b_rptr);
mp = mp->b_cont;
}
if (msgdsize(newmp) == 0) {
freemsg(newmp);
newmp = NULL;
}
out:
if (newmp) {
if (!canputnext(q)) {
(void) putbq(q, newmp);
return (0);
} else {
putnext(q, newmp);
}
}
return (TRUE);
}
static int
rlwinctl(queue_t *q, mblk_t *mp)
{
mblk_t *rl_msgp;
struct iocblk *iocbp;
struct rlmod_info *rmip = (struct rlmod_info *)q->q_ptr;
TRACE_2(TR_FAC_RLOGINP, TR_RLOGINP_WINCTL_IN, "rlwinctl start: q %p, "
"mp %p", q, mp);
rmip->oobdata[0] &= ~TIOCPKT_WINDOW;
if ((rl_msgp = mkiocb(TIOCSWINSZ)) == NULL) {
TRACE_2(TR_FAC_RLOGINP, TR_RLOGINP_WINCTL_OUT, "rlwinctl end: "
"q %p, mp %p, allocb failed", q, mp);
return (0);
}
rl_msgp->b_cont = mp;
iocbp = (struct iocblk *)rl_msgp->b_rptr;
iocbp->ioc_count = msgdsize(mp);
putnext(q, rl_msgp);
TRACE_2(TR_FAC_RLOGINP, TR_RLOGINP_WINCTL_OUT, "rlwinctl end: "
"q %p, mp %p, done", q, mp);
return (1);
}
static mblk_t *
rlwinsetup(queue_t *q, mblk_t *mp, unsigned char *blk)
{
mblk_t *mp1;
unsigned char *jmpmp;
ssize_t left = 0;
struct winsize win;
jmpmp = (blk + 4 + sizeof (struct winsize));
left = mp->b_wptr - jmpmp;
if ((mp1 = allocb(sizeof (struct winsize), BPRI_MED)) == NULL)
return (0);
mp1->b_datap->db_type = M_DATA;
mp1->b_wptr = mp1->b_rptr + sizeof (struct winsize);
bcopy(blk + 4, &win, sizeof (struct winsize));
win.ws_row = ntohs(win.ws_row);
win.ws_col = ntohs(win.ws_col);
win.ws_xpixel = ntohs(win.ws_xpixel);
win.ws_ypixel = ntohs(win.ws_ypixel);
bcopy(&win, mp1->b_rptr, sizeof (struct winsize));
if ((rlwinctl(q, mp1)) == 0) {
freeb(mp1);
return (0);
}
if (left > 0) {
bcopy(jmpmp, blk, left);
mp->b_wptr = blk + left;
} else
mp->b_wptr = blk;
return (mp);
}
static boolean_t
tty_flow(queue_t *q, struct rlmod_info *rmip, mblk_t *mp)
{
struct iocblk *ioc;
struct termios *tp;
struct termio *ti;
int stop, ixon;
mblk_t *tmpmp;
char cntl;
int error;
ioc = (struct iocblk *)mp->b_rptr;
switch (ioc->ioc_cmd) {
case TCSETS:
case TCSETSW:
case TCSETSF:
error = miocpullup(mp, sizeof (struct termios));
if (error != 0) {
miocnak(q, mp, 0, error);
return (B_TRUE);
}
tp = (struct termios *)(mp->b_cont->b_rptr);
rmip->stopc = tp->c_cc[VSTOP];
rmip->startc = tp->c_cc[VSTART];
ixon = tp->c_iflag & IXON;
break;
case TCSETA:
case TCSETAW:
case TCSETAF:
error = miocpullup(mp, sizeof (struct termio));
if (error != 0) {
miocnak(q, mp, 0, error);
return (B_TRUE);
}
ti = (struct termio *)(mp->b_cont->b_rptr);
ixon = ti->c_iflag & IXON;
break;
default:
#ifdef DEBUG
cmn_err(CE_PANIC,
"rloginmod: tty_flow: bad ioctl 0x%x", ioc->ioc_cmd);
#else
miocnak(q, mp, 0, EINVAL);
return (B_TRUE);
#endif
}
stop = (ixon && (rmip->stopc == CTRL('s')) &&
(rmip->startc == CTRL('q')));
if (rmip->stopmode == TIOCPKT_NOSTOP) {
if (stop) {
cntl = rmip->oobdata[0] | TIOCPKT_DOSTOP;
if ((tmpmp = make_expmblk(cntl)) == NULL) {
recover(q, mp, sizeof (mblk_t));
return (B_FALSE);
}
if (!canputnext(q)) {
freemsg(tmpmp);
return (B_FALSE);
}
putnext(q, tmpmp);
rmip->stopmode = TIOCPKT_DOSTOP;
}
} else {
if (!stop) {
cntl = rmip->oobdata[0] | TIOCPKT_NOSTOP;
if ((tmpmp = make_expmblk(cntl)) == NULL) {
recover(q, mp, sizeof (mblk_t));
return (B_FALSE);
}
if (!canputnext(q)) {
freemsg(tmpmp);
return (B_FALSE);
}
putnext(q, tmpmp);
rmip->stopmode = TIOCPKT_NOSTOP;
}
}
miocack(q, mp, 0, 0);
return (B_TRUE);
}
static boolean_t
rlmodwioctl(queue_t *q, mblk_t *mp)
{
struct iocblk *ioc;
struct rlmod_info *rmip = (struct rlmod_info *)q->q_ptr;
int error;
ioc = (struct iocblk *)mp->b_rptr;
switch (ioc->ioc_cmd) {
case RL_IOC_ENABLE:
if (!(rmip->flags & RL_DISABLED)) {
miocnak(q, mp, 0, EINVAL);
break;
}
if (mp->b_cont) {
(void) putbq(RD(q), mp->b_cont);
mp->b_cont = NULL;
}
if (rmip->flags & RL_DISABLED)
rmip->flags &= ~RL_DISABLED;
qenable(RD(q));
miocack(q, mp, 0, 0);
TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WPUT_OUT,
"rlmodwput end: q %p, mp %p, %s",
q, mp, "IOCACK enable");
return (B_TRUE);
case TCSETS:
case TCSETSW:
case TCSETSF:
case TCSETA:
case TCSETAW:
case TCSETAF:
return (tty_flow(q, rmip, mp));
#ifdef DEBUG
case TIOCSWINSZ:
case TIOCSTI:
case TCSBRK:
miocnak(q, mp, 0, EINVAL);
break;
#endif
case CRYPTPASSTHRU:
error = miocpullup(mp, sizeof (uchar_t));
if (error != 0) {
miocnak(q, mp, 0, error);
break;
}
if (*(mp->b_cont->b_rptr) == 0x01)
rmip->flags |= RL_IOCPASSTHRU;
else
rmip->flags &= ~RL_IOCPASSTHRU;
miocack(q, mp, 0, 0);
break;
default:
if (rmip->flags & RL_IOCPASSTHRU) {
putnext(q, mp);
} else {
#ifdef DEBUG
cmn_err(CE_NOTE,
"rlmodwioctl: unexpected ioctl type 0x%x",
ioc->ioc_cmd);
#endif
miocnak(q, mp, 0, EINVAL);
}
}
return (B_TRUE);
}
static void
rlmod_timer(void *arg)
{
queue_t *q = arg;
struct rlmod_info *rmip = (struct rlmod_info *)q->q_ptr;
ASSERT(rmip);
if (q->q_flag & QREADR) {
ASSERT(rmip->rtimoutid);
rmip->rtimoutid = 0;
} else {
ASSERT(rmip->wtimoutid);
rmip->wtimoutid = 0;
}
enableok(q);
qenable(q);
}
static void
rlmod_buffer(void *arg)
{
queue_t *q = arg;
struct rlmod_info *rmip = (struct rlmod_info *)q->q_ptr;
ASSERT(rmip);
if (q->q_flag & QREADR) {
ASSERT(rmip->rbufcid);
rmip->rbufcid = 0;
} else {
ASSERT(rmip->wbufcid);
rmip->wbufcid = 0;
}
enableok(q);
qenable(q);
}
static void
recover(queue_t *q, mblk_t *mp, size_t size)
{
ASSERT(mp->b_datap->db_type < QPCTL);
noenable(q);
(void) putbq(q, mp);
recover1(q, size);
}
static void
recover1(queue_t *q, size_t size)
{
struct rlmod_info *rmip = (struct rlmod_info *)q->q_ptr;
timeout_id_t tid;
bufcall_id_t bid;
if (q->q_flag & QREADR) {
if (rmip->rtimoutid || rmip->rbufcid)
return;
} else {
if (rmip->wtimoutid || rmip->wbufcid)
return;
}
if (!(bid = qbufcall(RD(q), size, BPRI_MED, rlmod_buffer, q))) {
tid = qtimeout(RD(q), rlmod_timer, q, SIMWAIT);
if (q->q_flag & QREADR)
rmip->rtimoutid = tid;
else
rmip->wtimoutid = tid;
} else {
if (q->q_flag & QREADR)
rmip->rbufcid = bid;
else
rmip->wbufcid = bid;
}
}