#include <sys/types.h>
#include <sys/param.h>
#include <sys/signal.h>
#include <sys/cred.h>
#include <sys/vnode.h>
#include <sys/termios.h>
#include <sys/termio.h>
#include <sys/ttold.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/strsun.h>
#include <sys/tty.h>
#include <sys/buf.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/errno.h>
#include <sys/proc.h>
#include <sys/procset.h>
#include <sys/fault.h>
#include <sys/siginfo.h>
#include <sys/debug.h>
#include <sys/session.h>
#include <sys/kmem.h>
#include <sys/cpuvar.h>
#include <sys/kbio.h>
#include <sys/strredir.h>
#include <sys/fs/snode.h>
#include <sys/consdev.h>
#include <sys/conf.h>
#include <sys/cmn_err.h>
#include <sys/console.h>
#include <sys/promif.h>
#include <sys/note.h>
#include <sys/polled_io.h>
#include <sys/systm.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/esunddi.h>
#include <sys/sunldi.h>
#include <sys/debug.h>
#include <sys/console.h>
#include <sys/ddi_impldefs.h>
#include <sys/policy.h>
#include <sys/modctl.h>
#include <sys/tem.h>
#include <sys/wscons.h>
#include <sys/vt_impl.h>
_NOTE(SCHEME_PROTECTS_DATA("Unshared data", copyreq))
_NOTE(SCHEME_PROTECTS_DATA("Unshared data", copyresp))
_NOTE(SCHEME_PROTECTS_DATA("Unshared data", datab))
_NOTE(SCHEME_PROTECTS_DATA("Unshared data", iocblk))
_NOTE(SCHEME_PROTECTS_DATA("Unshared data", msgb))
_NOTE(SCHEME_PROTECTS_DATA("Unshared data", queue))
#define MINLINES 10
#define MAXLINES 48
#define LOSCREENLINES 34
#define HISCREENLINES 48
#define MINCOLS 10
#define MAXCOLS 120
#define LOSCREENCOLS 80
#define HISCREENCOLS 120
struct wscons_state {
dev_t wc_dev;
#ifdef _HAVE_TEM_FIRMWARE
int wc_defer_output;
#endif
queue_t *wc_kbdqueue;
cons_polledio_t wc_polledio;
cons_polledio_t *wc_kb_polledio;
unsigned int wc_kb_getpolledio_id;
queue_t *wc_pending_wq;
mblk_t *wc_pending_link;
} wscons;
_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", wscons))
_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", wscons_state))
_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vt_stat))
_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vc_waitactive_msg))
_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", tty_common))
_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vt_mode))
_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vt_dispinfo))
_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", winsize))
_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vc_last_console))
#ifdef _HAVE_TEM_FIRMWARE
ssize_t wc_cons_wrtvec(promif_redir_arg_t arg, uchar_t *s, size_t n);
#endif
static int wcopen(queue_t *, dev_t *, int, int, cred_t *);
static int wcclose(queue_t *, int, cred_t *);
static int wcuwsrv(queue_t *);
static int wcuwput(queue_t *, mblk_t *);
static int wclrput(queue_t *, mblk_t *);
static struct module_info wcm_info = {
0,
"wc",
0,
INFPSZ,
2048,
128
};
static struct qinit wcurinit = {
putq,
NULL,
wcopen,
wcclose,
NULL,
&wcm_info,
NULL
};
static struct qinit wcuwinit = {
wcuwput,
wcuwsrv,
wcopen,
wcclose,
NULL,
&wcm_info,
NULL
};
static struct qinit wclrinit = {
wclrput,
NULL,
NULL,
NULL,
NULL,
&wcm_info,
NULL
};
static struct qinit wclwinit = {
NULL,
NULL,
NULL,
NULL,
NULL,
&wcm_info,
NULL
};
static struct streamtab wcinfo = {
&wcurinit,
&wcuwinit,
&wclrinit,
&wclwinit,
};
static int wc_info(dev_info_t *, ddi_info_cmd_t, void *, void **result);
static int wc_attach(dev_info_t *, ddi_attach_cmd_t);
DDI_DEFINE_STREAM_OPS(wc_ops, nulldev, nulldev, wc_attach, nodev, nodev,
wc_info, D_MTPERMOD | D_MP, &wcinfo, ddi_quiesce_not_supported);
static void wcreioctl(void *);
static void wcioctl(queue_t *, mblk_t *);
#ifdef _HAVE_TEM_FIRMWARE
static void wcopoll(void *);
#endif
static void wcrstrt(void *);
static void wc_open_kb_polledio(struct wscons_state *wc, queue_t *q,
mblk_t *mp);
static void wc_close_kb_polledio(struct wscons_state *wc, queue_t *q,
mblk_t *mp);
static void wc_polled_putchar(cons_polledio_arg_t arg,
unsigned char c);
static boolean_t wc_polled_ischar(cons_polledio_arg_t arg);
static int wc_polled_getchar(cons_polledio_arg_t arg);
static void wc_polled_enter(cons_polledio_arg_t arg);
static void wc_polled_exit(cons_polledio_arg_t arg);
void wc_get_size(vc_state_t *pvc);
static void wc_modechg_cb(tem_modechg_cb_arg_t arg);
static tem_vt_state_t wc_get_screen_tem(vc_state_t *);
static struct dev_ops wc_ops;
#ifndef DPRINTF
#ifdef DEBUG
static void wc_dprintf(const char *fmt, ...) __KPRINTFLIKE(1);
#define DPRINTF(l, m, args) \
(((l) >= wc_errlevel) && ((m) & wc_errmask) ? \
wc_dprintf args : \
(void) 0)
#define PRINT_L0 0
#define PRINT_L1 1
#define PRINT_L2 2
#define PRINT_MASK_ALL 0xFFFFFFFFU
uint_t wc_errmask = PRINT_MASK_ALL;
uint_t wc_errlevel = PRINT_L2;
#else
#define DPRINTF(l, m, args)
#endif
#endif
static struct modldrv modldrv = {
&mod_driverops,
"Workstation multiplexer Driver 'wc'",
&wc_ops,
};
static struct modlinkage modlinkage = {
MODREV_1,
&modldrv,
NULL
};
int
_init(void)
{
int rc;
if ((rc = mod_install(&modlinkage)) == 0)
vt_init();
return (rc);
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int
wc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
if (ddi_create_minor_node(devi, "wscons", S_IFCHR,
0, DDI_PSEUDO, 0) == DDI_FAILURE) {
ddi_remove_minor_node(devi, NULL);
return (DDI_FAILURE);
}
mutex_enter(&vc_lock);
wc_dip = devi;
bzero(&(wscons.wc_polledio), sizeof (wscons.wc_polledio));
vt_resize(VC_DEFAULT_COUNT);
mutex_exit(&vc_lock);
return (DDI_SUCCESS);
}
static int
wc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
void **result)
{
int error;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
if (wc_dip == NULL) {
error = DDI_FAILURE;
} else {
*result = (void *) wc_dip;
error = DDI_SUCCESS;
}
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
static void
wc_init_polledio(void)
{
static boolean_t polledio_inited = B_FALSE;
_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data",
polledio_inited))
if (polledio_inited)
return;
polledio_inited = B_TRUE;
bzero(&(wscons.wc_polledio), sizeof (wscons.wc_polledio));
wscons.wc_polledio.cons_polledio_version =
CONSPOLLEDIO_V0;
wscons.wc_polledio.cons_polledio_argument =
(cons_polledio_arg_t)&wscons;
wscons.wc_polledio.cons_polledio_enter =
wc_polled_enter;
wscons.wc_polledio.cons_polledio_exit =
wc_polled_exit;
#ifdef _HAVE_TEM_FIRMWARE
wscons.wc_defer_output = prom_stdout_is_framebuffer();
#endif
}
static int
wcopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
{
int minor;
wc_init_polledio();
minor = (int)getminor(*devp);
return (vt_open(minor, q, crp));
}
static int
wcclose(queue_t *q, int flag, cred_t *crp)
{
vc_state_t *pvc = (vc_state_t *)q->q_ptr;
qprocsoff(q);
mutex_enter(&vc_lock);
if (vc_cons_user == pvc->vc_minor)
vc_cons_user = VT_MINOR_INVALID;
if (pvc->vc_minor == 0 || pvc->vc_minor == vc_active_console) {
if (pvc->vc_minor == 0 && pvc->vc_minor == vc_active_console) {
vc_active_console = VT_MINOR_INVALID;
vc_last_console = VT_MINOR_INVALID;
}
mutex_enter(&pvc->vc_state_lock);
vt_clean(q, pvc);
mutex_exit(&pvc->vc_state_lock);
mutex_exit(&vc_lock);
return (0);
}
vt_close(q, pvc, crp);
mutex_exit(&vc_lock);
return (0);
}
static int
wcuwsrv(queue_t *q)
{
vc_state_t *pvc = (vc_state_t *)q->q_ptr;
tem_vt_state_t ptem = NULL;
mblk_t *mp;
ssize_t cc;
while ((mp = getq(q)) != NULL) {
if (pvc->vc_flags & (WCS_DELAY|WCS_BUSY|WCS_STOPPED)) {
(void) putbq(q, mp);
return (0);
}
switch (mp->b_datap->db_type) {
default:
freemsg(mp);
continue;
case M_IOCTL:
wcioctl(q, mp);
continue;
case M_DELAY:
if (pvc->vc_timeoutid != 0)
(void) quntimeout(q, pvc->vc_timeoutid);
pvc->vc_timeoutid = qtimeout(q, wcrstrt, pvc,
(clock_t)(*(unsigned char *)mp->b_rptr + 6));
mutex_enter(&pvc->vc_state_lock);
pvc->vc_flags |= WCS_DELAY;
mutex_exit(&pvc->vc_state_lock);
freemsg(mp);
continue;
case M_DATA:
break;
}
if ((cc = mp->b_wptr - mp->b_rptr) == 0) {
freemsg(mp);
continue;
}
#ifdef _HAVE_TEM_FIRMWARE
if (consmode == CONS_KFB) {
#endif
ptem = wc_get_screen_tem(pvc);
if (ptem == NULL) {
freemsg(mp);
continue;
}
for (mblk_t *nbp = mp; nbp != NULL; nbp = nbp->b_cont) {
cc = nbp->b_wptr - nbp->b_rptr;
if (cc <= 0)
continue;
tem_write(ptem, nbp->b_rptr, cc, kcred);
}
freemsg(mp);
#ifdef _HAVE_TEM_FIRMWARE
continue;
}
if (pvc->vc_minor != 0) {
freemsg(mp);
continue;
}
if (wscons.wc_defer_output) {
mutex_enter(&pvc->vc_state_lock);
pvc->vc_flags |= WCS_BUSY;
mutex_exit(&pvc->vc_state_lock);
pvc->vc_pendc = -1;
for (mblk_t *nbp = mp; nbp != NULL; nbp = nbp->b_cont) {
cc = nbp->b_wptr - nbp->b_rptr;
if (cc <= 0)
continue;
console_puts((const char *)nbp->b_rptr, cc);
}
freemsg(mp);
mutex_enter(&pvc->vc_state_lock);
pvc->vc_flags &= ~WCS_BUSY;
mutex_exit(&pvc->vc_state_lock);
continue;
}
for (boolean_t done = B_FALSE; done != B_TRUE; ) {
int c;
c = *mp->b_rptr++;
cc--;
if (prom_mayput((char)c) != 0) {
mutex_enter(&pvc->vc_state_lock);
pvc->vc_flags |= WCS_BUSY;
mutex_exit(&pvc->vc_state_lock);
pvc->vc_pendc = c;
if (pvc->vc_timeoutid != 0)
(void) quntimeout(q,
pvc->vc_timeoutid);
pvc->vc_timeoutid = qtimeout(q, wcopoll,
pvc, 1);
if (mp != NULL) {
(void) putbq(q, mp);
return (0);
}
break;
}
while (cc <= 0) {
mblk_t *nbp = mp;
mp = mp->b_cont;
freeb(nbp);
if (mp == NULL) {
done = B_TRUE;
break;
}
cc = mp->b_wptr - mp->b_rptr;
}
}
#endif
}
return (0);
}
static int
wcuwput(queue_t *q, mblk_t *mp)
{
vc_state_t *pvc = (vc_state_t *)q->q_ptr;
switch (mp->b_datap->db_type) {
case M_STOP:
mutex_enter(&pvc->vc_state_lock);
pvc->vc_flags |= WCS_STOPPED;
mutex_exit(&pvc->vc_state_lock);
freemsg(mp);
break;
case M_START:
mutex_enter(&pvc->vc_state_lock);
pvc->vc_flags &= ~WCS_STOPPED;
mutex_exit(&pvc->vc_state_lock);
qenable(q);
freemsg(mp);
break;
case M_IOCTL: {
struct iocblk *iocp;
struct linkblk *linkp;
iocp = (struct iocblk *)(void *)mp->b_rptr;
switch (iocp->ioc_cmd) {
case I_LINK:
case I_PLINK:
if (wscons.wc_kbdqueue != NULL) {
miocnak(q, mp, 0, EINVAL);
return (0);
}
linkp = (struct linkblk *)(void *)mp->b_cont->b_rptr;
wscons.wc_kbdqueue = WR(linkp->l_qbot);
mp->b_datap->db_type = M_IOCACK;
iocp->ioc_count = 0;
wc_open_kb_polledio(&wscons, q, mp);
break;
case I_UNLINK:
case I_PUNLINK:
linkp = (struct linkblk *)(void *)mp->b_cont->b_rptr;
if (wscons.wc_kbdqueue != WR(linkp->l_qbot)) {
miocnak(q, mp, 0, EINVAL);
return (0);
}
mp->b_datap->db_type = M_IOCACK;
iocp->ioc_count = 0;
wc_close_kb_polledio(&wscons, q, mp);
break;
case TCSETSW:
case TCSETSF:
case TCSETAW:
case TCSETAF:
case TCSBRK:
if (putq(q, mp) == 0)
freemsg(mp);
break;
case CONSSETABORTENABLE:
case CONSGETABORTENABLE:
case KIOCSDIRECT:
if (wscons.wc_kbdqueue != NULL) {
wscons.wc_pending_wq = q;
(void) putnext(wscons.wc_kbdqueue, mp);
break;
}
default:
wcioctl(q, mp);
break;
}
break;
}
case M_FLUSH:
if (*mp->b_rptr & FLUSHW) {
flushq(q, FLUSHDATA);
*mp->b_rptr &= ~FLUSHW;
}
if (*mp->b_rptr & FLUSHR) {
flushq(RD(q), FLUSHDATA);
qreply(q, mp);
} else
freemsg(mp);
break;
case M_BREAK:
freemsg(mp);
break;
case M_DELAY:
case M_DATA:
if (putq(q, mp) == 0)
freemsg(mp);
break;
case M_IOCDATA:
vt_miocdata(q, mp);
break;
default:
freemsg(mp);
break;
}
return (0);
}
static void
wcreioctl(void *arg)
{
vc_state_t *pvc = (vc_state_t *)arg;
queue_t *q;
mblk_t *mp;
pvc->vc_bufcallid = 0;
q = pvc->vc_ttycommon.t_writeq;
if ((mp = pvc->vc_ttycommon.t_iocpending) != NULL) {
pvc->vc_ttycommon.t_iocpending = NULL;
wcioctl(q, mp);
}
}
static int
wc_getterm(mblk_t *mp)
{
char *term;
intptr_t arg;
int flag = ((struct iocblk *)(void *)mp->b_rptr)->ioc_flag;
STRUCT_DECL(cons_getterm, wcterm);
STRUCT_INIT(wcterm, flag);
arg = *((intptr_t *)(void *)mp->b_cont->b_rptr);
if (ddi_copyin((void *)arg, STRUCT_BUF(wcterm),
STRUCT_SIZE(wcterm), flag) != 0) {
return (EFAULT);
}
if (consmode == CONS_FW) {
term = "sun";
} else {
ASSERT(consmode == CONS_KFB);
term = "sun-color";
}
if (STRUCT_FGET(wcterm, cn_term_len) <
strlen(term) + 1) {
return (EOVERFLOW);
}
if (ddi_copyout(term,
STRUCT_FGETP(wcterm, cn_term_type),
strlen(term) + 1, flag) != 0) {
return (EFAULT);
}
return (0);
}
static void
wcioctl(queue_t *q, mblk_t *mp)
{
vc_state_t *pvc = (vc_state_t *)q->q_ptr;
struct iocblk *iocp;
size_t datasize;
int error;
long len;
iocp = (struct iocblk *)(void *)mp->b_rptr;
if ((iocp->ioc_cmd & VTIOC) == VTIOC ||
(iocp->ioc_cmd & KDIOC) == KDIOC) {
vt_ioctl(q, mp);
return;
}
switch (iocp->ioc_cmd) {
case TIOCSWINSZ:
iocp->ioc_count = 0;
mp->b_datap->db_type = M_IOCACK;
qreply(q, mp);
return;
case CONSOPENPOLLEDIO:
DPRINTF(PRINT_L1, PRINT_MASK_ALL,
("wcioctl: CONSOPENPOLLEDIO\n"));
error = miocpullup(mp, sizeof (struct cons_polledio *));
if (error != 0) {
miocnak(q, mp, 0, error);
return;
}
if (consmode == CONS_KFB)
wscons.wc_polledio.cons_polledio_putchar =
wc_polled_putchar;
*(struct cons_polledio **)(void *)mp->b_cont->b_rptr =
&wscons.wc_polledio;
mp->b_datap->db_type = M_IOCACK;
qreply(q, mp);
break;
case CONS_GETTERM:
if ((error = wc_getterm(mp)) != 0)
miocnak(q, mp, 0, error);
else
miocack(q, mp, 0, 0);
return;
case WC_OPEN_FB:
mp->b_datap->db_type = M_IOCNAK;
if ((iocp->ioc_error = secpolicy_console(iocp->ioc_cr)) != 0)
goto open_fail;
iocp->ioc_error = EINVAL;
if (mp->b_cont == NULL ||
mp->b_cont->b_cont != NULL)
goto open_fail;
len = mp->b_cont->b_wptr - mp->b_cont->b_rptr;
if (memchr(mp->b_cont->b_rptr, 0, len) == NULL)
goto open_fail;
iocp->ioc_error = tem_info_init((char *)mp->b_cont->b_rptr,
iocp->ioc_cr);
if (iocp->ioc_error != 0)
goto open_fail;
#ifdef _HAVE_TEM_FIRMWARE
if (prom_stdout_is_framebuffer()) {
prom_set_stdout_redirect(wc_cons_wrtvec,
(promif_redir_arg_t)NULL);
}
#endif
tem_register_modechg_cb(wc_modechg_cb,
(tem_modechg_cb_arg_t)&wscons);
mp->b_datap->db_type = M_IOCACK;
open_fail:
qreply(q, mp);
break;
case WC_CLOSE_FB:
mp->b_datap->db_type = M_IOCNAK;
if ((iocp->ioc_error = secpolicy_console(iocp->ioc_cr)) != 0)
goto close_fail;
iocp->ioc_error = EINVAL;
close_fail:
qreply(q, mp);
break;
default:
datasize = ttycommon_ioctl(&pvc->vc_ttycommon, q, mp, &error);
if (datasize != 0) {
if (pvc->vc_bufcallid != 0)
qunbufcall(q, pvc->vc_bufcallid);
pvc->vc_bufcallid = qbufcall(q, datasize, BPRI_HI,
wcreioctl, pvc);
return;
}
if (error < 0) {
if (iocp->ioc_cmd == TCSBRK)
error = 0;
else
error = EINVAL;
}
if (error != 0) {
iocp->ioc_error = error;
mp->b_datap->db_type = M_IOCNAK;
}
qreply(q, mp);
break;
}
}
static void
wc_open_kb_polledio(struct wscons_state *wscons, queue_t *q, mblk_t *mp)
{
mblk_t *mp2;
struct iocblk *iocp;
DPRINTF(PRINT_L1, PRINT_MASK_ALL,
("wc_open_kb_polledio: sending CONSOPENPOLLEDIO\n"));
mp2 = mkiocb(CONSOPENPOLLEDIO);
if (mp2 == NULL) {
goto nomem;
}
mp2->b_cont = allocb(sizeof (struct cons_polledio *), BPRI_HI);
if (mp2->b_cont == NULL) {
freemsg(mp2);
goto nomem;
}
iocp = (struct iocblk *)(void *)mp2->b_rptr;
iocp->ioc_count = sizeof (struct cons_polledio *);
mp2->b_cont->b_wptr = mp2->b_cont->b_rptr +
sizeof (struct cons_polledio *);
wscons->wc_pending_wq = q;
wscons->wc_pending_link = mp;
wscons->wc_kb_getpolledio_id = iocp->ioc_id;
putnext(wscons->wc_kbdqueue, mp2);
return;
nomem:
iocp = (struct iocblk *)(void *)mp->b_rptr;
iocp->ioc_error = ENOMEM;
mp->b_datap->db_type = M_IOCNAK;
qreply(q, mp);
}
static void
wc_close_kb_polledio(struct wscons_state *wscons, queue_t *q, mblk_t *mp)
{
mblk_t *mp2;
struct iocblk *iocp;
DPRINTF(PRINT_L1, PRINT_MASK_ALL,
("wc_close_kb_polledio: sending CONSCLOSEPOLLEDIO\n"));
mp2 = mkiocb(CONSCLOSEPOLLEDIO);
if (mp2 == NULL) {
goto nomem;
}
mp2->b_cont = allocb(sizeof (struct cons_polledio *), BPRI_HI);
if (mp2->b_cont == NULL) {
freemsg(mp2);
goto nomem;
}
iocp = (struct iocblk *)(void *)mp2->b_rptr;
iocp->ioc_count = 0;
wscons->wc_pending_wq = q;
wscons->wc_pending_link = mp;
wscons->wc_kb_getpolledio_id = iocp->ioc_id;
putnext(wscons->wc_kbdqueue, mp2);
return;
nomem:
iocp = (struct iocblk *)(void *)mp->b_rptr;
iocp->ioc_error = ENOMEM;
mp->b_datap->db_type = M_IOCNAK;
qreply(q, mp);
}
#ifdef _HAVE_TEM_FIRMWARE
static void
wcopoll(void *arg)
{
vc_state_t *pvc = (vc_state_t *)arg;
queue_t *q;
q = pvc->vc_ttycommon.t_writeq;
pvc->vc_timeoutid = 0;
mutex_enter(&pvc->vc_state_lock);
if ((pvc->vc_flags & WCS_BUSY) && pvc->vc_pendc != -1) {
if (prom_mayput((char)pvc->vc_pendc) == 0) {
pvc->vc_pendc = -1;
pvc->vc_flags &= ~WCS_BUSY;
if (!(pvc->vc_flags&(WCS_DELAY|WCS_STOPPED)))
qenable(q);
} else
pvc->vc_timeoutid = qtimeout(q, wcopoll, pvc, 1);
}
mutex_exit(&pvc->vc_state_lock);
}
#endif
static void
wcrstrt(void *arg)
{
vc_state_t *pvc = (vc_state_t *)arg;
ASSERT(pvc->vc_ttycommon.t_writeq != NULL);
mutex_enter(&pvc->vc_state_lock);
pvc->vc_flags &= ~WCS_DELAY;
mutex_exit(&pvc->vc_state_lock);
qenable(pvc->vc_ttycommon.t_writeq);
}
static tem_vt_state_t
wc_get_screen_tem(vc_state_t *pvc)
{
if (!tem_initialized(pvc->vc_tem) ||
tem_get_fbmode(pvc->vc_tem) != KD_TEXT)
return (NULL);
return (pvc->vc_tem);
}
static int
wclrput(queue_t *q, mblk_t *mp)
{
vc_state_t *pvc;
queue_t *upq;
struct iocblk *iocp;
pvc = vt_minor2vc(VT_ACTIVE);
DPRINTF(PRINT_L1, PRINT_MASK_ALL,
("wclrput: wclrput type = 0x%x\n", mp->b_datap->db_type));
switch (mp->b_datap->db_type) {
case M_FLUSH:
if (*mp->b_rptr == FLUSHW || *mp->b_rptr == FLUSHRW) {
flushq(WR(q), FLUSHDATA);
*mp->b_rptr = FLUSHR;
}
if (*mp->b_rptr == FLUSHR || *mp->b_rptr == FLUSHRW) {
flushq(q, FLUSHDATA);
*mp->b_rptr = FLUSHW;
qreply(q, mp);
} else
freemsg(mp);
break;
case M_DATA:
if (consmode == CONS_KFB && vt_check_hotkeys(mp)) {
freemsg(mp);
break;
}
if ((upq = pvc->vc_ttycommon.t_readq) != NULL) {
if (!canput(upq->q_next)) {
ttycommon_qfull(&pvc->vc_ttycommon, upq);
qenable(WR(upq));
freemsg(mp);
} else {
putnext(upq, mp);
}
} else
freemsg(mp);
break;
case M_IOCACK:
case M_IOCNAK:
iocp = (struct iocblk *)(void *)mp->b_rptr;
if (wscons.wc_pending_link != NULL &&
iocp->ioc_id == wscons.wc_kb_getpolledio_id) {
switch (mp->b_datap->db_type) {
case M_IOCACK:
switch (iocp->ioc_cmd) {
case CONSOPENPOLLEDIO:
DPRINTF(PRINT_L1, PRINT_MASK_ALL,
("wclrput: "
"ACK CONSOPENPOLLEDIO\n"));
wscons.wc_kb_polledio =
*(struct cons_polledio **)
(void *)mp->b_cont->b_rptr;
wscons.wc_polledio.
cons_polledio_getchar =
wc_polled_getchar;
wscons.wc_polledio.
cons_polledio_ischar =
wc_polled_ischar;
break;
case CONSCLOSEPOLLEDIO:
DPRINTF(PRINT_L1, PRINT_MASK_ALL,
("wclrput: "
"ACK CONSCLOSEPOLLEDIO\n"));
wscons.wc_kb_polledio = NULL;
wscons.wc_kbdqueue = NULL;
wscons.wc_polledio.
cons_polledio_getchar = NULL;
wscons.wc_polledio.
cons_polledio_ischar = NULL;
break;
default:
DPRINTF(PRINT_L1, PRINT_MASK_ALL,
("wclrput: "
"ACK UNKNOWN\n"));
}
break;
case M_IOCNAK:
DPRINTF(PRINT_L1, PRINT_MASK_ALL,
("wclrput: NAK\n"));
switch (iocp->ioc_cmd) {
case CONSCLOSEPOLLEDIO:
wscons.wc_kb_polledio = NULL;
wscons.wc_kbdqueue = NULL;
wscons.wc_polledio.
cons_polledio_getchar = NULL;
wscons.wc_polledio.
cons_polledio_ischar = NULL;
break;
}
break;
}
freemsg(mp);
mp = wscons.wc_pending_link;
wscons.wc_pending_link = NULL;
wscons.wc_kb_getpolledio_id = 0;
}
default:
if (wscons.wc_pending_wq != NULL) {
qreply(wscons.wc_pending_wq, mp);
wscons.wc_pending_wq = NULL;
break;
}
if ((upq = pvc->vc_ttycommon.t_readq) != NULL) {
putnext(upq, mp);
} else {
DPRINTF(PRINT_L1, PRINT_MASK_ALL,
("wclrput: Message DISCARDED\n"));
freemsg(mp);
}
break;
}
return (0);
}
#ifdef _HAVE_TEM_FIRMWARE
ssize_t
wc_cons_wrtvec(promif_redir_arg_t arg, uchar_t *s, size_t n)
{
vc_state_t *pvc;
pvc = vt_minor2vc(VT_ACTIVE);
if (pvc->vc_tem == NULL)
return (0);
ASSERT(consmode == CONS_KFB);
if (panicstr)
polled_io_cons_write(s, n);
else
(void) tem_write(pvc->vc_tem, s, n, kcred);
return (n);
}
#endif
static void
wc_polled_putchar(cons_polledio_arg_t arg, unsigned char c)
{
vc_state_t *pvc;
pvc = vt_minor2vc(VT_ACTIVE);
if (c == '\n')
wc_polled_putchar(arg, '\r');
if (pvc->vc_tem == NULL) {
return;
}
tem_safe_polled_write(pvc->vc_tem, &c, 1);
}
static int
wc_polled_getchar(cons_polledio_arg_t arg)
{
struct wscons_state *wscons = (struct wscons_state *)arg;
if (wscons->wc_kb_polledio == NULL) {
prom_printf("wscons: getchar with no keyboard support");
prom_printf("Halted...");
for (;;)
;
}
return (wscons->wc_kb_polledio->cons_polledio_getchar(
wscons->wc_kb_polledio->cons_polledio_argument));
}
static boolean_t
wc_polled_ischar(cons_polledio_arg_t arg)
{
struct wscons_state *wscons = (struct wscons_state *)arg;
if (wscons->wc_kb_polledio == NULL)
return (B_FALSE);
return (wscons->wc_kb_polledio->cons_polledio_ischar(
wscons->wc_kb_polledio->cons_polledio_argument));
}
static void
wc_polled_enter(cons_polledio_arg_t arg)
{
struct wscons_state *wscons = (struct wscons_state *)arg;
if (wscons->wc_kb_polledio == NULL)
return;
if (wscons->wc_kb_polledio->cons_polledio_enter != NULL) {
wscons->wc_kb_polledio->cons_polledio_enter(
wscons->wc_kb_polledio->cons_polledio_argument);
}
}
static void
wc_polled_exit(cons_polledio_arg_t arg)
{
struct wscons_state *wscons = (struct wscons_state *)arg;
if (wscons->wc_kb_polledio == NULL)
return;
if (wscons->wc_kb_polledio->cons_polledio_exit != NULL) {
wscons->wc_kb_polledio->cons_polledio_exit(
wscons->wc_kb_polledio->cons_polledio_argument);
}
}
#ifdef DEBUG
static void
wc_dprintf(const char *fmt, ...)
{
char buf[256];
va_list ap;
va_start(ap, fmt);
(void) vsprintf(buf, fmt, ap);
va_end(ap);
cmn_err(CE_WARN, "wc: %s", buf);
}
#endif
static void
update_property(vc_state_t *pvc, char *name, ushort_t value)
{
char data[8];
(void) snprintf(data, sizeof (data), "%u", value);
(void) ddi_prop_update_string(wscons.wc_dev, wc_dip, name, data);
}
void
wc_get_size(vc_state_t *pvc)
{
struct winsize *t = &pvc->vc_ttycommon.t_size;
ushort_t r = LOSCREENLINES, c = LOSCREENCOLS, x = 0, y = 0;
if (pvc->vc_tem != NULL)
tem_get_size(&r, &c, &x, &y);
#ifdef _HAVE_TEM_FIRMWARE
else
console_get_size(&r, &c, &x, &y);
#endif
mutex_enter(&pvc->vc_ttycommon.t_excl);
t->ws_col = c;
t->ws_row = r;
t->ws_xpixel = x;
t->ws_ypixel = y;
mutex_exit(&pvc->vc_ttycommon.t_excl);
if (pvc->vc_minor != 0)
return;
update_property(pvc, "screen-#cols", c);
update_property(pvc, "screen-#rows", r);
update_property(pvc, "screen-width", x);
update_property(pvc, "screen-height", y);
}
static void
wc_modechg_cb(tem_modechg_cb_arg_t arg)
{
minor_t index;
vc_state_t *pvc;
mutex_enter(&vc_lock);
for (index = 0; index < VC_INSTANCES_COUNT; index++) {
pvc = vt_minor2vc(index);
mutex_enter(&pvc->vc_state_lock);
if ((pvc->vc_flags & WCS_ISOPEN) &&
(pvc->vc_flags & WCS_INIT))
wc_get_size(pvc);
mutex_exit(&pvc->vc_state_lock);
}
mutex_exit(&vc_lock);
}