/* $NetBSD: main.c,v 1.7 2016/06/11 06:29:24 dholland Exp $ */

/*
 * Copyright (c) 2003 Naoto Shimazaki.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY NAOTO SHIMAZAKI AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE NAOTO OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Boot loader for L-Card+
 *
 * ROM Map
 * -------
 * ROM1
 * BFFF FFFF	------------------------------
 *
 *		reserved
 *
 * BF80 0000	------------------------------
 *
 * ROM0
 * BFFF FFFF	------------------------------
 *
 *		user storage (max 2Mbytes)
 *
 * BFE0 0000	------------------------------
 *
 *		reserved
 *
 * BFD4 0000	------------------------------
 *
 *		boot params
 *
 * BFD2 0000	------------------------------
 *
 *		second boot loader (mirror image)
 *		or Linux Kernel
 *
 * BFD0 0000	------------------------------
 *
 *		first boot loader (L-Card+ original loader)
 *
 *		reset vector
 * BFC0 0000	------------------------------
 *
 *		gziped kernel image (max 4Mbytes)
 *
 * BF80 0000	------------------------------
 *
 *
 *
 * RAM Map
 * -------
 *
 * 80FF FFFF	------------------------------
 *		ROM ICE work
 * 80FF FE00	------------------------------
 *		ROM ICE stack
 * 80FF FDA8	------------------------------
 *
 *
 *
 *		kernel
 * 8004 0000	------------------------------
 *		kernel stack (growing to lower)
 *
 *
 *		boot loader heap (growing to upper)
 *		boot loader text & data (at exec time)
 * 8000 1000	------------------------------
 *		vector table
 * 8000 0000	------------------------------
 *
 *		virtual memory space
 *
 * 0000 0000	------------------------------
 *
 *
 *
 * ROMCS0 <-> ROMCS3 mapping
 *
 *  ROMCS0        ROMCS3
 * BE7F FFFF <-> BFFF FFFF
 * BE40 0000 <-> BFC0 0000	reset vector
 * BE00 0000 <-> BF80 0000
 * 
 *
 */
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: main.c,v 1.7 2016/06/11 06:29:24 dholland Exp $");

#include <lib/libsa/stand.h>

#include <lib/libsa/loadfile.h>
#include <lib/libkern/libkern.h>

#include <hpcmips/vr/vripreg.h>
#include <hpcmips/vr/cmureg.h>
#include <hpcmips/vr/vr4181giureg.h>

#include "extern.h"
#include "i28f128reg.h"

/* XXX */
#define ISABRGCTL	0x00
#define ISABRGSTS	0x02
#define XISACTL		0x04

#define BOOTTIMEOUT	9	/* must less than 10 */
#define LINEBUFLEN	80

extern const char bootprog_rev[];
extern const char bootprog_name[];

static void command_help(char *opt);
static void command_dump(char *opt);
static void command_boot(char *opt);
static void command_load(char *opt);
static void command_fill(char *opt);
static void command_write(char *opt);
static void command_option(char *subcmd);
static void opt_subcmd_print(char *opt);
static void opt_subcmd_read(char *opt);
static void opt_subcmd_write(char *opt);
static void opt_subcmd_path(char *opt);
static void opt_subcmd_bootp(char *opt);
static void opt_subcmd_ip(char *opt);


struct boot_option	bootopts;

static struct bootmenu_command commands[] = {
	{ "?",		command_help },
	{ "h",		command_help },
	{ "d",		command_dump },
	{ "b",		command_boot },
	{ "l",		command_load },
	{ "f",		command_fill },
	{ "w",		command_write },
	{ "o",		command_option },
	{ NULL,		NULL },
};

static struct bootmenu_command opt_subcommands[] = {
	{ "p",		opt_subcmd_print },
	{ "r",		opt_subcmd_read },
	{ "w",		opt_subcmd_write },
	{ "path",	opt_subcmd_path },
	{ "bootp",	opt_subcmd_bootp },
	{ "ip",		opt_subcmd_ip },
	{ NULL,		NULL },
};

static void
print_banner(void)
{
	printf("\n");
	printf(">> %s, Revision %s\n", bootprog_name, bootprog_rev);
#if 0
	printf(">> Memory: %d/%d k\n", getbasemem(), getextmem());
#endif
}

static void
init_devices(void)
{
	/* Init RTC */
	REGWRITE_2(VRETIMEH, 0, 0);
	REGWRITE_2(VRETIMEM, 0, 0);
	REGWRITE_2(VRETIMEL, 0, 0);


	/*
	 * CLKSPEEDREG	0x6012
	 *	DIV	DIV2 mode
	 *	CLKSP	18 (0x12)
	 *	PClock (CPU clock)		65.536MHz
	 *		PClock = (18.432MHz / CLKSP) x 64
	 *		       = (18.432MHz / 18) x 64
	 *		       = 65.536MHz
	 *	TClock (peripheral clock)	32.768MHz
	 *		TClock = PClock / DIV
	 *		       = 65.536MHz / 2
	 *		       = 32.768MHz
	 */

	/*
	 * setup ISA BUS clock freqency
	 *
	 * set PCLK (internal peripheral clock) to 32.768MHz (TClock / 1)
	 * set External ISA bus clock to 10.922MHz (TClock / 3)
	 */
	REGWRITE_2(VR4181_ISABRG_ADDR, ISABRGCTL, 0x0003);
	REGWRITE_2(VR4181_ISABRG_ADDR, XISACTL, 0x0401);

	/*
	 * setup peripheral's clock supply
	 *
	 * CSU: disable
	 * AIU: enable (AIU, ADU, ADU18M)
	 * PIU: disable
	 * SIU: enable (SIU18M)
	 */
	REGWRITE_2(VR4181_CMU_ADDR, 0, CMUMASK_SIU | CMUMASK_AIU);

	/*
	 * setup GPIO
	 */
#if 0
	/* L-Card+ generic setup */
	/*
	 * pin   mode	comment
	 * GP0 : GPI	not used
	 * GP1 : GPI	not used
	 * GP2 : GPO	LED6 (0: on  1: off)
	 * GP3 : PCS0	chip select for CS8900A Lan controller
	 * GP4 : GPI	IRQ input from CS8900A
	 * GP5 : GPI	not used
	 * GP6 : GPI	not used
	 * GP7 : GPI	reserved by TANBAC TB0193
	 */
	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PIOD_L_REG_W, 0xffff);
	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE0_REG_W,
		   GP3_PCS0 | GP2_GPO);
	/*
	 * pin   mode	comment
	 * GP8 : GPO	LED5 (0: on  1: off)
	 * GP9 : GPI	CD2
	 * GP10: GPI	CD1
	 * GP11: GPI	not used
	 * GP12: GPI	not used
	 * GP13: GPI	not used
	 * GP14: GPI	not used
	 * GP15: GPI	not used
	 */
	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE1_REG_W, GP8_GPO);
	/*
	 * pin   mode	comment
	 * GP16: IORD	ISA bus
	 * GP17: IOWR	ISA bus
	 * GP18: IORDY	ISA bus
	 * GP19: GPI	not used
	 * GP20: GPI	not used
	 * GP21: RESET	resets CS8900A
	 * GP22: ROMCS0	ROM chip select
	 * GP23: ROMCS1	ROM chip select
	 */
	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE2_REG_W,
		   GP23_ROMCS1 | GP22_ROMCS0 | GP21_RESET
		   | GP18_IORDY | GP17_IOWR | GP16_IORD);
	/*
	 * GP24: ROMCS2	ROM chip select
	 * GP25: RxD1	SIU1
	 * GP26: TxD1	SIU1
	 * GP27: RTS1	SIU1
	 * GP28: CTS1	SIU1
	 * GP29: GPI	LED3
	 * GP30: GPI	reserved by TANBAC TB0193
	 * GP31: GPI	LED4
	 */
	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE3_REG_W,
		   GP30_GPI
		   | GP28_CTS1 | GP27_RTS1 | GP26_TxD1 | GP25_RxD1
		   | GP24_ROMCS2);
#else
	/* e-care node specific setup */
	/*
	 * pin   mode	comment
	 * GP0 : GPO	ECNRTC_RST
	 * GP1 : GPO	ECNRTC_CLK
	 * GP2 : GPO	LED6 (0: on  1: off)
	 * GP3 : PCS0	chip select for CS8900A Lan controller
	 * GP4 : GPI	IRQ input from CS8900A
	 * GP5 : GPO	ECNRTC_DIR
	 * GP6 : GPO	ECNRTC_OUT
	 * GP7 : GPI	reserved by TANBAC TB0193
	 */
	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PIOD_L_REG_W, 0xffff);
	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE0_REG_W,
		   GP6_GPO | GP5_GPO | GP3_PCS0
		   | GP2_GPO | GP1_GPO | GP0_GPO);

	/*
	 * pin   mode	comment
	 * GP8 : GPO	LED5 (0: on  1: off)
	 * GP9 : GPI	CD2
	 * GP10: GPI	CD1
	 * GP11: GPI	not used
	 * GP12: GPI	ECNRTC_IN
	 * GP13: GPI	not used
	 * GP14: GPI	not used
	 * GP15: GPI	not used
	 */
	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE1_REG_W,
		   GP12_GPI | GP8_GPO);

	/*
	 * pin   mode	comment
	 * GP16: IORD	ISA bus
	 * GP17: IOWR	ISA bus
	 * GP18: IORDY	ISA bus
	 * GP19: GPI	not used
	 * GP20: GPI	not used
	 * GP21: RESET	resets CS8900A
	 * GP22: ROMCS0	ROM chip select
	 * GP23: ROMCS1	ROM chip select
	 */
	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE2_REG_W,
		   GP23_ROMCS1 | GP22_ROMCS0 | GP21_RESET
		   | GP18_IORDY | GP17_IOWR | GP16_IORD);
	/*
	 * GP24: ROMCS2	ROM chip select
	 * GP25: RxD1	SIU1
	 * GP26: TxD1	SIU1
	 * GP27: RTS1	SIU1
	 * GP28: CTS1	SIU1
	 * GP29: GPI	LED3
	 * GP30: GPI	reserved by TANBAC TB0193
	 * GP31: GPI	LED4
	 */
	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE3_REG_W,
		   GP30_GPI
		   | GP28_CTS1 | GP27_RTS1 | GP26_TxD1 | GP25_RxD1
		   | GP24_ROMCS2);
#endif

#if 0
	/*
	 * setup interrupt
	 *
	 * I4TYP:  falling edge trigger
	 * GIMSK4: unmask
	 * GIEN4:  enable
	 * other:  unused, mask, disable
	 */
	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_INTTYP_L_REG_W,
		   I4TYP_HIGH_LEVEL);
	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_INTMASK_REG_W,
		   0xffffU & ~GIMSK4);
	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_INTEN_REG_W, GIEN4);
#endif

	/*
	 * programmable chip select
	 *
	 * PCS0 is used to select CS8900A Ethernet controller
	 * on TB0193
	 *
	 * PCS0:
	 *	0x14010000 - 0x14010fff
	 *	I/O access, 16bit cycle, both of read/write
	 * PCS1: unused
	 */
	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PCS0STRA_REG_W, 0x0000);
	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PCS0STPA_REG_W, 0x0fff);
	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PCS0HIA_REG_W, 0x1401);
	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PCSMODE_REG_W,
		   PCS0MIOB_IO | PCS0DSIZE_16BIT | PCS0MD_READWRITE);
}

/*
 * chops the head from the arguments and returns the arguments if any,
 * or possibly an empty string.
 */
static char *
get_next_arg(char *arg)
{
	char *opt;

	if ((opt = strchr(arg, ' ')) == NULL) {
		opt = "";
	} else {
		*opt++ = '\0';
	}

        /* trim leading blanks */
	while (*opt == ' ')
		opt++;

	return opt;
}

static void
command_help(char *opt)
{
	printf("commands are:\n"
	       "boot:\tb\n"
	       "dump:\td addr [addr]\n"
	       "fill:\tf addr addr char\n"
	       "load:\tl [offset] (with following S-Record)\n"
	       "write:\tw dst src len\n"
	       "option:\to subcommand [params]\n"
	       "help:\th|?\n"
	       "\n"
	       "option subcommands are:\n"
	       "print:\to p\n"
	       "read:\to r\n"
	       "write:\to w\n"
	       "path:\to path pathname\n"
	       "bootp:\to bootp yes|no\n"
	       "ip:\to ip remote local netmask gateway\n"
		);
}

static void
bad_param(void)
{
	printf("bad param\n");
	command_help(NULL);
}

static const u_int8_t print_cnv[] = {
	'0', '1', '2', '3', '4', '5', '6', '7',
	'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

static void
printhexul(u_int32_t n)
{
	int	i;

	for (i = 28; i >= 0; i -= 4)
		putchar(print_cnv[(n >> i) & 0x0f]);
}

static void
printhexuc(u_int8_t n)
{
	int	i;

	for (i = 4; i >= 0; i -= 4)
		putchar(print_cnv[(n >> i) & 0x0f]);
}

static void
command_dump(char *opt)
{
	char		*endptr;
	const char	*p;
	const char	*line_fence;
	const char	*limit;

	p = (const char *) strtoul(opt, &endptr, 16);
	if (opt == endptr) {
		bad_param();
		return;
	}

	opt = get_next_arg(opt);
	limit = (const char *) strtoul(opt, &endptr, 16);
	if (opt == endptr) {
		limit = p + 256;
	}

	for (;;) {
		printhexul((u_int32_t) p);
		putchar(' ');
		line_fence = p + 16;
		while (p < line_fence) {
			printhexuc(*p++);
			putchar(' ');
			if (p >= limit) {
				putchar('\n');
				return;
			}
		}
		putchar('\n');
		if (ISKEY) {
			if (getchar() == '\x03')
				break;
		}
	}
}

static void
command_boot(char *opt)
{
	u_long	marks[MARK_MAX];

	marks[MARK_START] = 0;
	if (loadfile(bootopts.b_pathname, marks, LOAD_KERNEL)) {
		printf("loadfile failed\n");
		return;
	}
	start_netbsd();
	/* no return */
}

/*
 * loading S-Record
 */
static int
load_srec(char *offset)
{
	char		s2lbuf[9];
	char		c;
	char		rectype;
	u_int32_t	reclen;
	u_int32_t	reclen_bk;
	u_int32_t	recaddr;
	char		*endptr;
	char		*p;
	u_int32_t	sum;
	int		err = 0;

	for (;;) {
		/*
		 * the first step is to read a S-Record.
		 */
		if ((c = getchar()) != 'S')
			goto out;

		rectype = getchar();

		s2lbuf[0] = getchar();
		s2lbuf[1] = getchar();
		s2lbuf[2] = '\0';
		reclen_bk = reclen = strtoul(s2lbuf, &endptr, 16);
		if (endptr != &s2lbuf[2])
			goto out;
		sum = reclen;

		p = s2lbuf;

		switch (rectype) {
		case '0':
			/* just ignore */
			do {
				c = getchar();
			} while (c != '\r' && c != '\n');
			continue;

		case '3':
			*p++ = getchar();
			*p++ = getchar();
			reclen--;
			/* FALLTHRU */
		case '2':
			*p++ = getchar();
			*p++ = getchar();
			reclen--;
			/* FALLTHRU */
		case '1':
			*p++ = getchar();
			*p++ = getchar();
			*p++ = getchar();
			*p++ = getchar();
			*p = '\0';
			reclen -= 2;

			recaddr = strtoul(s2lbuf, &endptr, 16);
			if (endptr != p)
				goto out;
			sum += (recaddr >> 24) & 0xff;
			sum += (recaddr >> 16) & 0xff;
			sum += (recaddr >> 8) & 0xff;
			sum += recaddr & 0xff;

			p = offset + recaddr;
			/*
			 * XXX
			 * address range is must be chaked here!
			 */
			reclen--;
			s2lbuf[2] = '\0';
			while (reclen > 0) {
				s2lbuf[0] = getchar();
				s2lbuf[1] = getchar();
				*p = (u_int8_t) strtoul(s2lbuf, &endptr, 16);
				if (endptr != &s2lbuf[2])
					goto out;
				sum += *p++;
				reclen--;
			}
			break;

		case '7':
		case '8':
		case '9':
			goto out2;

		default:
			goto out;
		}

		s2lbuf[0] = getchar();
		s2lbuf[1] = getchar();
		s2lbuf[2] = '\0';
		sum += (strtoul(s2lbuf, &endptr, 16) & 0xff);
		sum &= 0xff;
		if (sum != 0xff) {
			printf("checksum error\n");
			err = 1;
			goto out2;
		}

		c = getchar();
		if (c != '\r' && c != '\n')
			goto out;
	}
	/* never reach */
	return 1;

out:
	printf("invalid S-Record\n");
	err = 1;

out2:
	do {
		c = getchar();
	} while (c != '\r' && c != '\n');

	return err;
}

static void
command_load(char *opt)
{
	char	*endptr;
	char	*offset;
	
	offset = (char *) strtoul(opt, &endptr, 16);
	if (opt == endptr)
		offset = 0;
	load_srec(offset);
}

static void
command_fill(char *opt)
{
	char	*endptr;
	char	*p;
	char	*limit;
	int	c;

	p = (char *) strtoul(opt, &endptr, 16);
	if (opt == endptr) {
		bad_param();
		return;
	}

	opt = get_next_arg(opt);
	limit = (char *) strtoul(opt, &endptr, 16);
	if (opt == endptr) {
		bad_param();
		return;
	}

	opt = get_next_arg(opt);
	c = strtoul(opt, &endptr, 16);
	if (opt == endptr)
		c = '\0';

	memset(p, c, limit - p);
}

static void
check_write_verify_flash(u_int32_t src, u_int32_t dst, size_t len)
{
	int		status;

	if ((dst & I28F128_BLOCK_MASK) != 0) {
		printf("dst addr must be aligned to block boundary (0x%x)\n",
		       I28F128_BLOCK_SIZE);
		return;
	}

	if (i28f128_probe((void *) dst)) {
		printf("dst addr is not a intel 28F128\n");
	} else {
		printf("intel 28F128 detected\n");
	}

	if ((status = i28f128_region_write((void *) dst, (void *) src, len))
	    != 0) {
		printf("write mem to flash failed status = %x\n", status);
		return;
	}

	printf("verifying...");
	if (memcmp((void *) dst, (void *) src, len)) {
		printf("verify error\n");
		return;
	}
	printf("ok\n");

	printf("writing memory to flash succeeded\n");
}

static void
command_write(char *opt)
{
	char		*endptr;
	u_int32_t	src;
	u_int32_t	dst;
	size_t		len;

	dst = strtoul(opt, &endptr, 16);
	if (opt == endptr)
		goto out;

	opt = get_next_arg(opt);
	src = strtoul(opt, &endptr, 16);
	if (opt == endptr)
		goto out;

	opt = get_next_arg(opt);
	len = strtoul(opt, &endptr, 16);
	if (opt == endptr)
		goto out;

	check_write_verify_flash(src, dst, len);
	return;

out:
	bad_param();
	return;
}

static void
command_option(char *subcmd)
{
	char	*opt;
	int	i;

	opt = get_next_arg(subcmd);

	/* dispatch subcommand */
	for (i = 0; opt_subcommands[i].c_name != NULL; i++) {
		if (strcmp(subcmd, opt_subcommands[i].c_name) == 0) {
			opt_subcommands[i].c_fn(opt);
			break;
		}
	}
	if (opt_subcommands[i].c_name == NULL) {
		printf("unknown option subcommand\n");
		command_help(NULL);
	}
}

static void
opt_subcmd_print(char *opt)
{
	printf("boot options:\n"
	       "magic:\t\t%s\n"
	       "pathname:\t`%s'\n"
	       "bootp:\t\t%s\n",
	       bootopts.b_magic == BOOTOPT_MAGIC ? "ok" : "bad",
	       bootopts.b_pathname,
	       bootopts.b_flags & B_F_USE_BOOTP ? "yes" : "no");
	printf("remote IP:\t%s\n", inet_ntoa(bootopts.b_remote_ip));
	printf("local IP:\t%s\n", inet_ntoa(bootopts.b_local_ip));
	printf("netmask:\t%s\n", intoa(bootopts.b_netmask));
	printf("gateway IP:\t%s\n", inet_ntoa(bootopts.b_gate_ip));
}

static void
opt_subcmd_read(char *opt)
{
	bootopts = *((struct boot_option *) BOOTOPTS_BASE);
	if (bootopts.b_magic != BOOTOPT_MAGIC)
		bootopts.b_pathname[0] = '\0';
}

static void
opt_subcmd_write(char *opt)
{
	bootopts.b_magic = BOOTOPT_MAGIC;

	check_write_verify_flash((u_int32_t) &bootopts, BOOTOPTS_BASE,
				 sizeof bootopts);
}

static void
opt_subcmd_path(char *opt)
{
	strlcpy(bootopts.b_pathname, opt, sizeof bootopts.b_pathname);
}

static void
opt_subcmd_bootp(char *opt)
{
	if (strcmp(opt, "yes") == 0) {
		bootopts.b_flags |= B_F_USE_BOOTP;
	} else if (strcmp(opt, "no") == 0) {
		bootopts.b_flags &= ~B_F_USE_BOOTP;
	} else {
		bad_param();
	}
}

static void
opt_subcmd_ip(char *opt)
{
	bootopts.b_remote_ip.s_addr = inet_addr(opt);
	opt = get_next_arg(opt);
	bootopts.b_local_ip.s_addr = inet_addr(opt);
	opt = get_next_arg(opt);
	bootopts.b_netmask = inet_addr(opt);
	opt = get_next_arg(opt);
	bootopts.b_gate_ip.s_addr = inet_addr(opt);
}

static void
bootmenu(void)
{
	char	input[LINEBUFLEN];
	char	*cmd;
	char	*opt;
	int	i;

	for (;;) {

		/* input a line */
		input[0] = '\0';
		printf("> ");
		kgets(input, sizeof(input));
		cmd = input;

		/* skip leading whitespace. */
		while(*cmd == ' ')
			cmd++;

		if(*cmd) {
			/* here, some command entered */

			opt = get_next_arg(cmd);

			/* dispatch command */
			for (i = 0; commands[i].c_name != NULL; i++) {
				if (strcmp(cmd, commands[i].c_name) == 0) {
					commands[i].c_fn(opt);
					break;
				}
			}
			if (commands[i].c_name == NULL) {
				printf("unknown command\n");
				command_help(NULL);
			}
		}
		
	}
}

static char
awaitkey(void)
{
	int	i;
	int	j;
	char	c = 0;

	while (ISKEY)
		getchar();

	for (i = BOOTTIMEOUT; i > 0; i--) {
		printf("%d\b", i);
		for (j = 0; j < 1000000; j++) {
			if (ISKEY) {
				while (ISKEY)
					c = getchar();
				goto out;
			}
		}
	}

out:
	printf("0\n");
	return(c);
}

__dead void
_rtt(void)
{
	for (;;)
		;
}

int
main(void)
{
	char	c;

	init_devices();

	comcninit();

	opt_subcmd_read(NULL);

	print_banner();

	c = awaitkey();
	if (c != '\r' && c != '\n' && c != '\0') {
		printf("type \"?\" or \"h\" for help.\n");
		bootmenu(); /* does not return */
	}

	command_boot(NULL);
	/*
	 * command_boot() returns only if it failed to boot.
	 * we enter to boot menu in this case.
	 */
	bootmenu();
	
	return 0;
}