#include <sys/types.h>
#include <sys/varargs.h>
#include <sys/modctl.h>
#include <sys/cmn_err.h>
#include <sys/console.h>
#include <sys/consdev.h>
#include <sys/promif.h>
#include <sys/note.h>
#include <sys/polled_io.h>
#include <sys/systm.h>
#include <sys/file.h>
#include <sys/conf.h>
#include <sys/kmem.h>
#include <sys/taskq.h>
#include <sys/log.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/esunddi.h>
#include <sys/fs/snode.h>
#include <sys/termios.h>
#include <sys/tem_impl.h>
#define MINLINES 10
#define MAXLINES 48
#define LOSCREENLINES 34
#define HISCREENLINES 48
#define MINCOLS 10
#define MAXCOLS 120
#define LOSCREENCOLS 80
#define HISCREENCOLS 120
vnode_t *console_vnode;
taskq_t *console_taskq;
struct cons_polledio *cons_polledio;
static krwlock_t console_lock;
static uint_t console_depth;
static int console_busy;
extern void pm_cfb_check_and_powerup(void);
extern void pm_cfb_rele(void);
static int
console_hold(void)
{
if (panicstr != NULL)
return (console_busy);
if (rw_owner(&console_lock) != curthread)
rw_enter(&console_lock, RW_WRITER);
if (console_depth++ != 0)
return (console_busy);
pm_cfb_check_and_powerup();
#ifdef _HAVE_TEM_FIRMWARE
if (consmode == CONS_FW && ncpus > 1 && fbvp != NULL) {
struct snode *csp = VTOS(VTOS(fbvp)->s_commonvp);
mutex_enter(&csp->s_lock);
console_busy = csp->s_mapcnt != 0;
if (csp->s_mapcnt == 0 && fbdip != NULL) {
mutex_enter(&DEVI(fbdip)->devi_lock);
console_busy = DEVI(fbdip)->devi_ref != 0;
}
}
#endif
return (console_busy);
}
static void
console_rele(void)
{
if (panicstr != NULL)
return;
ASSERT(RW_WRITE_HELD(&console_lock));
ASSERT(console_depth != 0);
if (--console_depth != 0)
return;
#ifdef _HAVE_TEM_FIRMWARE
if (consmode == CONS_FW && ncpus > 1 && fbvp != NULL) {
struct snode *csp = VTOS(VTOS(fbvp)->s_commonvp);
ASSERT(MUTEX_HELD(&csp->s_lock));
if (csp->s_mapcnt == 0 && fbdip != NULL)
mutex_exit(&DEVI(fbdip)->devi_lock);
mutex_exit(&csp->s_lock);
}
#endif
pm_cfb_rele();
console_busy = 0;
rw_exit(&console_lock);
}
static void
console_getprop(dev_t dev, dev_info_t *dip, char *name, ushort_t *sp)
{
uchar_t *data;
uint_t len;
uint_t i;
*sp = 0;
if (ddi_prop_lookup_byte_array(dev, dip, 0, name, &data, &len) ==
DDI_PROP_SUCCESS) {
for (i = 0; i < len; i++) {
if (data[i] < '0' || data[i] > '9')
break;
*sp = *sp * 10 + data[i] - '0';
}
ddi_prop_free(data);
}
}
void
console_get_size(ushort_t *r, ushort_t *c, ushort_t *x, ushort_t *y)
{
int rel_needed = 0;
dev_info_t *dip;
dev_t dev;
if (rwsconsvp == NULL || consmode == CONS_FW) {
dip = ddi_root_node();
dev = DDI_DEV_T_ANY;
} else {
dev = rwsconsvp->v_rdev;
dip = e_ddi_hold_devi_by_dev(dev, 0);
rel_needed = 1;
}
if (dip == NULL) {
*r = LOSCREENLINES;
*c = LOSCREENCOLS;
*x = *y = 0;
return;
}
console_getprop(DDI_DEV_T_ANY, dip, "screen-#columns", c);
console_getprop(DDI_DEV_T_ANY, dip, "screen-#rows", r);
console_getprop(DDI_DEV_T_ANY, dip, "screen-width", x);
console_getprop(DDI_DEV_T_ANY, dip, "screen-height", y);
if (*c < MINCOLS)
*c = LOSCREENCOLS;
else if (*c > MAXCOLS)
*c = HISCREENCOLS;
if (*r < MINLINES)
*r = LOSCREENLINES;
else if (*r > MAXLINES)
*r = HISCREENLINES;
if (rel_needed)
ddi_release_devi(dip);
}
typedef struct console_msg {
size_t cm_size;
char cm_text[1];
} console_msg_t;
static void
console_putmsg(console_msg_t *cm)
{
int busy, spl;
ssize_t res;
ASSERT(taskq_member(console_taskq, curthread));
if (rconsvp == NULL || panicstr ||
vn_rdwr(UIO_WRITE, console_vnode, cm->cm_text, strlen(cm->cm_text),
0, UIO_SYSSPACE, FAPPEND, (rlim64_t)LOG_HIWAT, kcred, &res) != 0) {
busy = console_hold();
spl = console_enter(busy);
prom_printf("%s", cm->cm_text);
console_exit(busy, spl);
console_rele();
}
kmem_free(cm, cm->cm_size);
}
void
console_vprintf(const char *fmt, va_list adx)
{
console_msg_t *cm;
size_t len = vsnprintf(NULL, 0, fmt, adx);
int busy, spl;
if (console_taskq != NULL && rconsvp != NULL && panicstr == NULL &&
(cm = kmem_alloc(sizeof (*cm) + len, KM_NOSLEEP)) != NULL) {
cm->cm_size = sizeof (*cm) + len;
(void) vsnprintf(cm->cm_text, len + 1, fmt, adx);
if (taskq_dispatch(console_taskq, (task_func_t *)console_putmsg,
cm, TQ_NOSLEEP) != TASKQID_INVALID)
return;
kmem_free(cm, cm->cm_size);
}
busy = console_hold();
spl = console_enter(busy);
prom_vprintf(fmt, adx);
console_exit(busy, spl);
console_rele();
}
void
console_printf(const char *fmt, ...)
{
va_list adx;
va_start(adx, fmt);
console_vprintf(fmt, adx);
va_end(adx);
}
void
console_puts(const char *s, size_t n)
{
int busy, spl;
busy = console_hold();
spl = console_enter(busy);
prom_writestr(s, n);
console_exit(busy, spl);
console_rele();
}
static void
console_putc(int c)
{
int busy = console_hold();
int spl = console_enter(busy);
if (c == '\n')
prom_putchar('\r');
prom_putchar(c);
console_exit(busy, spl);
console_rele();
}
void
console_gets(char *s, size_t len)
{
char *p = s;
char *q = s + len - 1;
int c;
ASSERT(rconsvp == NULL);
(void) console_hold();
for (;;) {
switch (c = (prom_getchar() & 0x7f)) {
case 0x7f:
if (p == s)
break;
console_putc(c);
c = '\b';
case '\b':
if (p == s)
break;
console_putc('\b');
console_putc(' ');
case '#':
console_putc(c);
if (p > s)
p--;
break;
case CTRL('u'):
console_putc(c);
console_putc('\n');
p = s;
break;
case '\r':
case '\n':
console_putc('\n');
goto done;
default:
if (p < q) {
console_putc(c);
*p++ = c;
} else
console_putc('\a');
}
}
done:
console_rele();
*p = '\0';
}
int
console_getc(void)
{
int c;
ASSERT(rconsvp == NULL);
c = prom_getchar();
if (c == '\r')
c = '\n';
console_putc(c);
return (c);
}