/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * PROM I/O backend
 */

#include <sys/param.h>
#include <sys/types.h>
#include <sys/obpdefs.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>

#include <mdb/mdb_modapi.h>
#include <mdb/mdb_io_impl.h>
#include <kmdb/kmdb_promif.h>
#include <kmdb/kmdb_io.h>
#include <mdb/mdb_debug.h>
#include <mdb/mdb_err.h>
#include <mdb/mdb.h>

#define	PIO_FL_TIO_READ	0x001

typedef struct pio_data {
	char		pio_name[MAXPATHLEN];
	ihandle_t	pio_fd;
	uint_t		pio_flags;
	struct termios	pio_ti;
} pio_data_t;

static pid_t pio_pgrp;

static ssize_t
pio_read(mdb_io_t *io, void *buf, size_t nbytes)
{
	pio_data_t *pdp = io->io_data;

	if (io->io_next == NULL)
		return (kmdb_prom_read(buf, nbytes, &pdp->pio_ti));

	return (IOP_READ(io->io_next, buf, nbytes));
}

static ssize_t
pio_write(mdb_io_t *io, const void *buf, size_t nbytes)
{
	pio_data_t *pdp = io->io_data;

	if (io->io_next == NULL)
		return (kmdb_prom_write(buf, nbytes, &pdp->pio_ti));

	return (IOP_WRITE(io->io_next, buf, nbytes));
}

static off64_t
pio_seek(mdb_io_t *io, off64_t offset, int whence)
{
	if (io->io_next == NULL)
		return (set_errno(ENOTSUP));

	return (IOP_SEEK(io->io_next, offset, whence));
}

static int
pio_ctl(mdb_io_t *io, int req, void *arg)
{
	pio_data_t *pdp = io->io_data;

	if (io->io_next != NULL)
		return (IOP_CTL(io->io_next, req, arg));

	switch (req) {
	case TIOCGWINSZ:
		return (kmdb_prom_term_ctl(TIOCGWINSZ, arg));

	case TCGETS: {
		struct termios *ti = arg;

		if (!(pdp->pio_flags & PIO_FL_TIO_READ)) {
			(void) kmdb_prom_term_ctl(TCGETS, &pdp->pio_ti);
			pdp->pio_flags |= PIO_FL_TIO_READ;
		}

		bcopy(&pdp->pio_ti, ti, sizeof (struct termios));

		mdb_dprintf(MDB_DBG_CMDBUF, "pio_ctl: gets: i: 0%o o: 0%o c: "
		    "0%o l: 0%o\n", ti->c_iflag, ti->c_oflag, ti->c_cflag,
		    ti->c_lflag);
		return (0);
	}

	case TCSETSW: {
		struct termios *ti = arg;

		mdb_dprintf(MDB_DBG_CMDBUF, "pio_ctl: setsw: i: 0%o o: 0%o c: "
		    "0%o l: 0%o\n", ti->c_iflag, ti->c_oflag, ti->c_cflag,
		    ti->c_lflag);

		bcopy(ti, &pdp->pio_ti, sizeof (struct termios));

		return (0);
	}

	case TIOCSPGRP:
		pio_pgrp = *(pid_t *)arg;
		mdb_dprintf(MDB_DBG_CMDBUF, "pio_ctl: spgrp: %ld\n",
		    (long)pio_pgrp);
		return (0);

	case TIOCGPGRP:
		mdb_dprintf(MDB_DBG_CMDBUF, "pio_ctl: gpgrp: %ld\n",
		    (long)pio_pgrp);
		*(pid_t *)arg = pio_pgrp;
		return (0);

	case MDB_IOC_CTTY:
		mdb_dprintf(MDB_DBG_CMDBUF, "pio_ctl: ignoring MDB_IOC_CTTY\n");
		return (0);

	case MDB_IOC_GETFD:
		return (set_errno(ENOTSUP));

	default:
		warn("Unknown ioctl %d\n", req);
		return (set_errno(EINVAL));
	}
}

void
pio_close(mdb_io_t *io)
{
	pio_data_t *pdp = io->io_data;

	mdb_free(pdp, sizeof (pio_data_t));
}

static const char *
pio_name(mdb_io_t *io)
{
	pio_data_t *pdp = io->io_data;

	if (io->io_next == NULL)
		return (pdp->pio_name);

	return (IOP_NAME(io->io_next));
}

static const mdb_io_ops_t promio_ops = {
	pio_read,
	pio_write,
	pio_seek,
	pio_ctl,
	pio_close,
	pio_name,
	no_io_link,
	no_io_unlink,
	no_io_setattr,
	no_io_suspend,
	no_io_resume
};

mdb_io_t *
kmdb_promio_create(char *name)
{
	mdb_io_t *io;
	pio_data_t *pdp;
	ihandle_t hdl = kmdb_prom_get_handle(name);

	if (hdl == -1)
		return (NULL);

	io = mdb_zalloc(sizeof (mdb_io_t), UM_SLEEP);
	pdp = mdb_zalloc(sizeof (pio_data_t), UM_SLEEP);

	(void) strlcpy(pdp->pio_name, name, MAXPATHLEN);
	pdp->pio_fd = hdl;

#ifdef __sparc
	pdp->pio_ti.c_oflag |= ONLCR;
	pdp->pio_ti.c_iflag |= ICRNL;
#endif
	pdp->pio_ti.c_lflag |= ECHO;

	io->io_data = pdp;
	io->io_ops = &promio_ops;

	return (io);
}

char
kmdb_getchar(void)
{
	char c;

	while (IOP_READ(mdb.m_term, &c, 1) != 1)
		continue;
	if (isprint(c) && c != '\n')
		mdb_iob_printf(mdb.m_out, "%c", c);
	mdb_iob_printf(mdb.m_out, "\n");

	return (c);
}