xref: /onnv-gate/usr/src/uts/i86pc/boot/boot_console.c (revision 4088:9f37e7ee9af5)
13446Smrj /*
23446Smrj  * CDDL HEADER START
33446Smrj  *
43446Smrj  * The contents of this file are subject to the terms of the
53446Smrj  * Common Development and Distribution License (the "License").
63446Smrj  * You may not use this file except in compliance with the License.
73446Smrj  *
83446Smrj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93446Smrj  * or http://www.opensolaris.org/os/licensing.
103446Smrj  * See the License for the specific language governing permissions
113446Smrj  * and limitations under the License.
123446Smrj  *
133446Smrj  * When distributing Covered Code, include this CDDL HEADER in each
143446Smrj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153446Smrj  * If applicable, add the following below this CDDL HEADER, with the
163446Smrj  * fields enclosed by brackets "[]" replaced with your own identifying
173446Smrj  * information: Portions Copyright [yyyy] [name of copyright owner]
183446Smrj  *
193446Smrj  * CDDL HEADER END
203446Smrj  */
213446Smrj /*
223446Smrj  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
233446Smrj  * Use is subject to license terms.
243446Smrj  */
253446Smrj 
263446Smrj #pragma ident	"%Z%%M%	%I%	%E% SMI"
273446Smrj 
283446Smrj #include <sys/types.h>
293446Smrj #include <sys/systm.h>
303446Smrj #include <sys/archsystm.h>
313446Smrj #include <sys/boot_console.h>
32*4088Srscott #include <sys/ctype.h>
333446Smrj 
343446Smrj #include "boot_serial.h"
353446Smrj #include "boot_vga.h"
363446Smrj 
373446Smrj #if defined(_BOOT)
38*4088Srscott #include <dboot/dboot_xboot.h>
393446Smrj #else
403446Smrj #include <sys/bootconf.h>
413446Smrj static char *usbser_buf;
423446Smrj static char *usbser_cur;
433446Smrj #endif
443446Smrj 
453446Smrj static int cons_color = CONS_COLOR;
463446Smrj int console = CONS_SCREEN_TEXT;
473446Smrj /* or CONS_TTYA, CONS_TTYB */
483446Smrj static int serial_ischar(void);
493446Smrj static int serial_getchar(void);
503446Smrj static void serial_putchar(int);
513446Smrj static void serial_adjust_prop(void);
523446Smrj 
533446Smrj static char *boot_line = NULL;
543446Smrj 
55*4088Srscott /* Set if the console or mode are expressed in the boot line */
56*4088Srscott static int console_set, console_mode_set;
57*4088Srscott 
583446Smrj /* Clear the screen and initialize VIDEO, XPOS and YPOS. */
593446Smrj static void
603446Smrj clear_screen(void)
613446Smrj {
623446Smrj 	/*
633446Smrj 	 * XXX should set vga mode so we don't depend on the
643446Smrj 	 * state left by the boot loader
653446Smrj 	 */
663446Smrj 	vga_clear(cons_color);
673446Smrj 	vga_setpos(0, 0);
683446Smrj }
693446Smrj 
703446Smrj /* Put the character C on the screen. */
713446Smrj static void
723446Smrj screen_putchar(int c)
733446Smrj {
743446Smrj 	int row, col;
753446Smrj 
763446Smrj 	vga_getpos(&row, &col);
773446Smrj 	switch (c) {
783446Smrj 	case '\t':
793446Smrj 		col += 8 - (col % 8);
803446Smrj 		if (col == VGA_TEXT_COLS)
813446Smrj 			col = 79;
823446Smrj 		vga_setpos(row, col);
833446Smrj 		break;
843446Smrj 
853446Smrj 	case '\r':
863446Smrj 		vga_setpos(row, 0);
873446Smrj 		break;
883446Smrj 
893446Smrj 	case '\b':
903446Smrj 		if (col > 0)
913446Smrj 			vga_setpos(row, col - 1);
923446Smrj 		break;
933446Smrj 
943446Smrj 	case '\n':
953446Smrj 		if (row < VGA_TEXT_ROWS - 1)
963446Smrj 			vga_setpos(row + 1, col);
973446Smrj 		else
983446Smrj 			vga_scroll(cons_color);
993446Smrj 		break;
1003446Smrj 
1013446Smrj 	default:
1023446Smrj 		vga_drawc(c, cons_color);
1033446Smrj 		if (col < VGA_TEXT_COLS -1)
1043446Smrj 			vga_setpos(row, col + 1);
1053446Smrj 		else if (row < VGA_TEXT_ROWS - 1)
1063446Smrj 			vga_setpos(row + 1, 0);
1073446Smrj 		else {
1083446Smrj 			vga_setpos(row, 0);
1093446Smrj 			vga_scroll(cons_color);
1103446Smrj 		}
1113446Smrj 		break;
1123446Smrj 	}
1133446Smrj }
1143446Smrj 
1153446Smrj /* serial port stuff */
1163446Smrj static int port;
1173446Smrj 
1183446Smrj static void
1193446Smrj serial_init(void)
1203446Smrj {
1213446Smrj 	switch (console) {
1223446Smrj 	case CONS_TTYA:
1233446Smrj 		port = 0x3f8;
1243446Smrj 		break;
1253446Smrj 	case CONS_TTYB:
1263446Smrj 		port = 0x2f8;
1273446Smrj 		break;
1283446Smrj 	}
1293446Smrj 
1303446Smrj 	outb(port + ISR, 0x20);
1313446Smrj 	if (inb(port + ISR) & 0x20) {
1323446Smrj 		/*
1333446Smrj 		 * 82510 chip is present
1343446Smrj 		 */
1353446Smrj 		outb(port + DAT+7, 0x04);	/* clear status */
1363446Smrj 		outb(port + ISR, 0x40);  /* set to bank 2 */
1373446Smrj 		outb(port + MCR, 0x08);  /* IMD */
1383446Smrj 		outb(port + DAT, 0x21);  /* FMD */
1393446Smrj 		outb(port + ISR, 0x00);  /* set to bank 0 */
1403446Smrj 	} else {
1413446Smrj 		/*
1423446Smrj 		 * set the UART in FIFO mode if it has FIFO buffers.
1433446Smrj 		 * use 16550 fifo reset sequence specified in NS
1443446Smrj 		 * application note. disable fifos until chip is
1453446Smrj 		 * initialized.
1463446Smrj 		 */
1473446Smrj 		outb(port + FIFOR, 0x00);		/* clear */
1483446Smrj 		outb(port + FIFOR, FIFO_ON);		/* enable */
1493446Smrj 		outb(port + FIFOR, FIFO_ON|FIFORXFLSH);  /* reset */
1503446Smrj 		outb(port + FIFOR,
1513446Smrj 		    FIFO_ON|FIFODMA|FIFOTXFLSH|FIFORXFLSH|0x80);
1523446Smrj 		if ((inb(port + ISR) & 0xc0) != 0xc0) {
1533446Smrj 			/*
1543446Smrj 			 * no fifo buffers so disable fifos.
1553446Smrj 			 * this is true for 8250's
1563446Smrj 			 */
1573446Smrj 			outb(port + FIFOR, 0x00);
1583446Smrj 		}
1593446Smrj 	}
1603446Smrj 
1613446Smrj 	/* disable interrupts */
1623446Smrj 	outb(port + ICR, 0);
1633446Smrj 
1643446Smrj 	/* adjust setting based on tty properties */
1653446Smrj 	serial_adjust_prop();
1663446Smrj 
1673446Smrj #if defined(_BOOT)
1683446Smrj 	/*
1693446Smrj 	 * Do a full reset to match console behavior.
1703446Smrj 	 * 0x1B + c - reset everything
1713446Smrj 	 */
1723446Smrj 	serial_putchar(0x1B);
1733446Smrj 	serial_putchar('c');
1743446Smrj #endif
1753446Smrj }
1763446Smrj 
177*4088Srscott /* Advance str pointer past white space */
178*4088Srscott #define	EAT_WHITE_SPACE(str)	{			\
179*4088Srscott 	while ((*str != '\0') && ISSPACE(*str))		\
180*4088Srscott 		str++;					\
181*4088Srscott }
182*4088Srscott 
183*4088Srscott /*
184*4088Srscott  * boot_line is set when we call here.  Search it for the argument name,
185*4088Srscott  * and if found, return a pointer to it.
186*4088Srscott  */
187*4088Srscott static char *
188*4088Srscott find_boot_line_prop(const char *name)
189*4088Srscott {
190*4088Srscott 	char *ptr;
191*4088Srscott 	char end_char;
192*4088Srscott 	size_t len;
193*4088Srscott 
194*4088Srscott 	if (boot_line == NULL)
195*4088Srscott 		return (NULL);
196*4088Srscott 
197*4088Srscott 	len = strlen(name);
198*4088Srscott 
199*4088Srscott 	/*
200*4088Srscott 	 * We have two nested loops here: the outer loop discards all options
201*4088Srscott 	 * except -B, and the inner loop parses the -B options looking for
202*4088Srscott 	 * the one we're interested in.
203*4088Srscott 	 */
204*4088Srscott 	for (ptr = boot_line; *ptr != '\0'; ptr++) {
205*4088Srscott 		EAT_WHITE_SPACE(ptr);
206*4088Srscott 
207*4088Srscott 		if (*ptr == '-') {
208*4088Srscott 			ptr++;
209*4088Srscott 			while ((*ptr != '\0') && (*ptr != 'B') &&
210*4088Srscott 			    !ISSPACE(*ptr))
211*4088Srscott 				ptr++;
212*4088Srscott 			if (*ptr == '\0')
213*4088Srscott 				return (NULL);
214*4088Srscott 			else if (*ptr != 'B')
215*4088Srscott 				continue;
216*4088Srscott 		} else {
217*4088Srscott 			while ((*ptr != '\0') && !ISSPACE(*ptr))
218*4088Srscott 				ptr++;
219*4088Srscott 			if (*ptr == '\0')
220*4088Srscott 				return (NULL);
221*4088Srscott 			continue;
222*4088Srscott 		}
223*4088Srscott 
224*4088Srscott 		do {
225*4088Srscott 			ptr++;
226*4088Srscott 			EAT_WHITE_SPACE(ptr);
227*4088Srscott 
228*4088Srscott 			if ((strncmp(ptr, name, len) == 0) &&
229*4088Srscott 			    (ptr[len] == '=')) {
230*4088Srscott 				ptr += len + 1;
231*4088Srscott 				if ((*ptr == '\'') || (*ptr == '"'))
232*4088Srscott 					return (ptr + 1);
233*4088Srscott 				else
234*4088Srscott 					return (ptr);
235*4088Srscott 			}
236*4088Srscott 
237*4088Srscott 			/*
238*4088Srscott 			 * We have a property, and it's not the one we're
239*4088Srscott 			 * interested in.  Skip the property name.  A name
240*4088Srscott 			 * can end with '=', a comma, or white space.
241*4088Srscott 			 */
242*4088Srscott 			while ((*ptr != '\0') && (*ptr != '=') &&
243*4088Srscott 			    (*ptr != ',') && (!ISSPACE(*ptr)))
244*4088Srscott 				ptr++;
245*4088Srscott 
246*4088Srscott 			/*
247*4088Srscott 			 * We only want to go through the rest of the inner
248*4088Srscott 			 * loop if we have a comma.  If we have a property
249*4088Srscott 			 * name without a value, either continue or break.
250*4088Srscott 			 */
251*4088Srscott 			if (*ptr == '\0')
252*4088Srscott 				return (NULL);
253*4088Srscott 			else if (*ptr == ',')
254*4088Srscott 				continue;
255*4088Srscott 			else if (ISSPACE(*ptr))
256*4088Srscott 				break;
257*4088Srscott 			ptr++;
258*4088Srscott 
259*4088Srscott 			/*
260*4088Srscott 			 * Is the property quoted?
261*4088Srscott 			 */
262*4088Srscott 			if ((*ptr == '\'') || (*ptr == '"')) {
263*4088Srscott 				end_char = *ptr;
264*4088Srscott 			} else {
265*4088Srscott 				/*
266*4088Srscott 				 * Not quoted, so the string ends at a comma
267*4088Srscott 				 * or at white space.  Deal with white space
268*4088Srscott 				 * later.
269*4088Srscott 				 */
270*4088Srscott 				end_char = ',';
271*4088Srscott 			}
272*4088Srscott 
273*4088Srscott 			/*
274*4088Srscott 			 * Now, we can ignore any characters until we find
275*4088Srscott 			 * end_char.
276*4088Srscott 			 */
277*4088Srscott 			for (; (*ptr != '\0') && (*ptr != end_char); ptr++) {
278*4088Srscott 				if ((end_char == ',') && ISSPACE(*ptr))
279*4088Srscott 					break;
280*4088Srscott 			}
281*4088Srscott 			if (*ptr && (*ptr != ','))
282*4088Srscott 				ptr++;
283*4088Srscott 		} while (*ptr == ',');
284*4088Srscott 	}
285*4088Srscott 	return (NULL);
286*4088Srscott }
287*4088Srscott 
2883446Smrj 
2893446Smrj #define	MATCHES(p, pat)	\
2903446Smrj 	(strncmp(p, pat, strlen(pat)) == 0 ? (p += strlen(pat), 1) : 0)
2913446Smrj 
2923446Smrj #define	SKIP(p, c)				\
2933446Smrj 	while (*(p) != 0 && *p != (c))		\
2943446Smrj 		++(p);				\
2953446Smrj 	if (*(p) == (c))			\
2963446Smrj 		++(p);
2973446Smrj 
2983446Smrj /*
2993446Smrj  * find a tty mode property either from cmdline or from boot properties
3003446Smrj  */
3013446Smrj static char *
3023446Smrj get_mode_value(char *name)
3033446Smrj {
3043446Smrj 	/*
3053446Smrj 	 * when specified on boot line it looks like "name" "="....
3063446Smrj 	 */
3073446Smrj 	if (boot_line != NULL) {
308*4088Srscott 		return (find_boot_line_prop(name));
3093446Smrj 	}
3103446Smrj 
3113446Smrj #if defined(_BOOT)
3123446Smrj 	return (NULL);
3133446Smrj #else
3143446Smrj 	/*
3153446Smrj 	 * if we're running in the full kernel we check the bootenv.rc settings
3163446Smrj 	 */
3173446Smrj 	{
3183446Smrj 		static char propval[20];
3193446Smrj 
3203446Smrj 		propval[0] = 0;
321*4088Srscott 		if (do_bsys_getproplen(NULL, name) <= 0)
3223446Smrj 			return (NULL);
323*4088Srscott 		(void) do_bsys_getprop(NULL, name, propval);
3243446Smrj 		return (propval);
3253446Smrj 	}
3263446Smrj #endif
3273446Smrj }
3283446Smrj 
3293446Smrj /*
3303446Smrj  * adjust serial port based on properties
3313446Smrj  * These come either from the cmdline or from boot properties.
3323446Smrj  */
3333446Smrj static void
3343446Smrj serial_adjust_prop(void)
3353446Smrj {
3363446Smrj 	char propname[20];
3373446Smrj 	char *propval;
3383446Smrj 	char *p;
3393446Smrj 	ulong_t baud;
3403446Smrj 	uchar_t lcr = 0;
3413446Smrj 	uchar_t mcr = DTR | RTS;
3423446Smrj 
3433446Smrj 	(void) strcpy(propname, "ttyX-mode");
3443446Smrj 	propname[3] = 'a' + console - CONS_TTYA;
3453446Smrj 	propval = get_mode_value(propname);
3463446Smrj 	if (propval == NULL)
3473446Smrj 		propval = "9600,8,n,1,-";
348*4088Srscott 	else
349*4088Srscott 		console_mode_set = 1;
3503446Smrj 
3513446Smrj 	/* property is of the form: "9600,8,n,1,-" */
3523446Smrj 	p = propval;
3533446Smrj 	if (MATCHES(p, "110,"))
3543446Smrj 		baud = ASY110;
3553446Smrj 	else if (MATCHES(p, "150,"))
3563446Smrj 		baud = ASY150;
3573446Smrj 	else if (MATCHES(p, "300,"))
3583446Smrj 		baud = ASY300;
3593446Smrj 	else if (MATCHES(p, "600,"))
3603446Smrj 		baud = ASY600;
3613446Smrj 	else if (MATCHES(p, "1200,"))
3623446Smrj 		baud = ASY1200;
3633446Smrj 	else if (MATCHES(p, "2400,"))
3643446Smrj 		baud = ASY2400;
3653446Smrj 	else if (MATCHES(p, "4800,"))
3663446Smrj 		baud = ASY4800;
3673446Smrj 	else if (MATCHES(p, "19200,"))
3683446Smrj 		baud = ASY19200;
3693446Smrj 	else if (MATCHES(p, "38400,"))
3703446Smrj 		baud = ASY38400;
3713446Smrj 	else if (MATCHES(p, "57600,"))
3723446Smrj 		baud = ASY57600;
3733446Smrj 	else if (MATCHES(p, "115200,"))
3743446Smrj 		baud = ASY115200;
3753446Smrj 	else {
3763446Smrj 		baud = ASY9600;
3773446Smrj 		SKIP(p, ',');
3783446Smrj 	}
3793446Smrj 	outb(port + LCR, DLAB);
3803446Smrj 	outb(port + DAT + DLL, baud & 0xff);
3813446Smrj 	outb(port + DAT + DLH, (baud >> 8) & 0xff);
3823446Smrj 
3833446Smrj 	switch (*p) {
3843446Smrj 	case '5':
3853446Smrj 		lcr |= BITS5;
3863446Smrj 		++p;
3873446Smrj 		break;
3883446Smrj 	case '6':
3893446Smrj 		lcr |= BITS6;
3903446Smrj 		++p;
3913446Smrj 		break;
3923446Smrj 	case '7':
3933446Smrj 		lcr |= BITS7;
3943446Smrj 		++p;
3953446Smrj 		break;
3963446Smrj 	case '8':
3973446Smrj 		++p;
3983446Smrj 	default:
3993446Smrj 		lcr |= BITS8;
4003446Smrj 		break;
4013446Smrj 	}
4023446Smrj 
4033446Smrj 	SKIP(p, ',');
4043446Smrj 
4053446Smrj 	switch (*p) {
4063446Smrj 	case 'n':
4073446Smrj 		lcr |= PARITY_NONE;
4083446Smrj 		++p;
4093446Smrj 		break;
4103446Smrj 	case 'o':
4113446Smrj 		lcr |= PARITY_ODD;
4123446Smrj 		++p;
4133446Smrj 		break;
4143446Smrj 	case 'e':
4153446Smrj 		++p;
4163446Smrj 	default:
4173446Smrj 		lcr |= PARITY_EVEN;
4183446Smrj 		break;
4193446Smrj 	}
4203446Smrj 
4213446Smrj 
4223446Smrj 	SKIP(p, ',');
4233446Smrj 
4243446Smrj 	switch (*p) {
4253446Smrj 	case '1':
4263446Smrj 		/* STOP1 is 0 */
4273446Smrj 		++p;
4283446Smrj 		break;
4293446Smrj 	default:
4303446Smrj 		lcr |= STOP2;
4313446Smrj 		break;
4323446Smrj 	}
4333446Smrj 	/* set parity bits */
4343446Smrj 	outb(port + LCR, lcr);
4353446Smrj 
4363446Smrj 	(void) strcpy(propname, "ttyX-rts-dtr-off");
4373446Smrj 	propname[3] = 'a' + console - CONS_TTYA;
4383446Smrj 	propval = get_mode_value(propname);
4393446Smrj 	if (propval == NULL)
4403446Smrj 		propval = "false";
4413446Smrj 	if (propval[0] != 'f' && propval[0] != 'F')
4423446Smrj 		mcr = 0;
4433446Smrj 	/* set modem control bits */
4443446Smrj 	outb(port + MCR, mcr | OUT2);
4453446Smrj }
4463446Smrj 
447*4088Srscott /*
448*4088Srscott  * A structure to map console names to values.
449*4088Srscott  */
450*4088Srscott typedef struct {
451*4088Srscott 	char *name;
452*4088Srscott 	int value;
453*4088Srscott } console_value_t;
454*4088Srscott 
455*4088Srscott console_value_t console_devices[] = {
456*4088Srscott 	{ "ttya", CONS_TTYA },
457*4088Srscott 	{ "ttyb", CONS_TTYB },
458*4088Srscott 	{ "text", CONS_SCREEN_TEXT },
459*4088Srscott #if !defined(_BOOT)
460*4088Srscott 	{ "usb-serial", CONS_USBSER },
461*4088Srscott #endif
462*4088Srscott 	{ "", CONS_INVALID }
463*4088Srscott };
464*4088Srscott 
4653446Smrj void
4663446Smrj bcons_init(char *bootstr)
4673446Smrj {
468*4088Srscott 	console_value_t *consolep;
469*4088Srscott 	size_t len, cons_len;
470*4088Srscott 	char *cons_str;
471*4088Srscott 
4723446Smrj 	boot_line = bootstr;
4733446Smrj 	console = CONS_INVALID;
4743446Smrj 
475*4088Srscott 	cons_str = find_boot_line_prop("console");
476*4088Srscott 	if (cons_str == NULL)
477*4088Srscott 		cons_str = find_boot_line_prop("output-device");
478*4088Srscott 
479*4088Srscott 	/*
480*4088Srscott 	 * Go through the console_devices array trying to match the string
481*4088Srscott 	 * we were given.  The string on the command line must end with
482*4088Srscott 	 * a comma or white space.
483*4088Srscott 	 */
484*4088Srscott 	if (cons_str != NULL) {
485*4088Srscott 		cons_len = strlen(cons_str);
486*4088Srscott 		consolep = console_devices;
487*4088Srscott 		for (; consolep->name[0] != '\0'; consolep++) {
488*4088Srscott 			len = strlen(consolep->name);
489*4088Srscott 			if ((len <= cons_len) && ((cons_str[len] == '\0') ||
490*4088Srscott 			    (cons_str[len] == ',') || (cons_str[len] == '\'') ||
491*4088Srscott 			    (cons_str[len] == '"') || ISSPACE(cons_str[len])) &&
492*4088Srscott 			    (strncmp(cons_str, consolep->name, len) == 0)) {
493*4088Srscott 				console = consolep->value;
494*4088Srscott 				break;
495*4088Srscott 			}
496*4088Srscott 		}
497*4088Srscott 	}
4983446Smrj 
4993446Smrj 	/*
5003446Smrj 	 * If no console device specified, default to text.
5013446Smrj 	 * Remember what was specified for second phase.
5023446Smrj 	 */
5033446Smrj 	if (console == CONS_INVALID)
5043446Smrj 		console = CONS_SCREEN_TEXT;
505*4088Srscott 	else
506*4088Srscott 		console_set = 1;
5073446Smrj 
5083446Smrj 	switch (console) {
5093446Smrj 	case CONS_TTYA:
5103446Smrj 	case CONS_TTYB:
5113446Smrj 		serial_init();
5123446Smrj 		break;
5133446Smrj 
514*4088Srscott #if !defined(_BOOT)
515*4088Srscott 	case CONS_USBSER:
516*4088Srscott 		/*
517*4088Srscott 		 * We can't do anything with the usb serial
518*4088Srscott 		 * until we have memory management.
519*4088Srscott 		 */
520*4088Srscott 		break;
521*4088Srscott #endif
5223446Smrj 	case CONS_SCREEN_TEXT:
5233446Smrj 	default:
5243446Smrj #if defined(_BOOT)
5253446Smrj 		clear_screen();	/* clears the grub screen */
5263446Smrj #endif
5273446Smrj 		kb_init();
5283446Smrj 		break;
5293446Smrj 	}
5303446Smrj 	boot_line = NULL;
5313446Smrj }
5323446Smrj 
5333446Smrj /*
5343446Smrj  * 2nd part of console initialization.
5353446Smrj  * In the kernel (ie. fakebop), this can be used only to switch to
5363446Smrj  * using a serial port instead of screen based on the contents
5373446Smrj  * of the bootenv.rc file.
5383446Smrj  */
5393446Smrj /*ARGSUSED*/
5403446Smrj void
5413446Smrj bcons_init2(char *inputdev, char *outputdev, char *consoledev)
5423446Smrj {
5433446Smrj #if !defined(_BOOT)
5443446Smrj 	int cons = CONS_INVALID;
545*4088Srscott 	char *devnames[] = { consoledev, outputdev, inputdev, NULL };
546*4088Srscott 	console_value_t *consolep;
547*4088Srscott 	int i;
5483446Smrj 
549*4088Srscott 	if (console != CONS_USBSER) {
550*4088Srscott 		if (console_set) {
551*4088Srscott 			/*
552*4088Srscott 			 * If the console was set on the command line,
553*4088Srscott 			 * but the ttyX-mode was not, we only need to
554*4088Srscott 			 * check bootenv.rc for that setting.
555*4088Srscott 			 */
556*4088Srscott 			if ((!console_mode_set) &&
557*4088Srscott 			    (console == CONS_TTYA || console == CONS_TTYB))
558*4088Srscott 				serial_init();
559*4088Srscott 			return;
560*4088Srscott 		}
5613446Smrj 
562*4088Srscott 		for (i = 0; devnames[i] != NULL; i++) {
563*4088Srscott 			consolep = console_devices;
564*4088Srscott 			for (; consolep->name[0] != '\0'; consolep++) {
565*4088Srscott 				if (strcmp(devnames[i], consolep->name) == 0) {
566*4088Srscott 					cons = consolep->value;
567*4088Srscott 				}
568*4088Srscott 			}
569*4088Srscott 			if (cons != CONS_INVALID)
570*4088Srscott 				break;
571*4088Srscott 		}
5723446Smrj 
573*4088Srscott 		if (cons == CONS_INVALID)
574*4088Srscott 			return;
575*4088Srscott 		if (cons == console)
576*4088Srscott 			return;
5773446Smrj 
578*4088Srscott 		console = cons;
579*4088Srscott 		if (cons == CONS_TTYA || cons == CONS_TTYB) {
580*4088Srscott 			serial_init();
581*4088Srscott 			return;
582*4088Srscott 		}
5833446Smrj 	}
5843446Smrj 
5853446Smrj 	/*
5863446Smrj 	 * USB serial -- we just collect data into a buffer
5873446Smrj 	 */
588*4088Srscott 	if (console == CONS_USBSER) {
5893446Smrj 		extern void *usbser_init(size_t);
5903446Smrj 		usbser_buf = usbser_cur = usbser_init(MMU_PAGESIZE);
5913446Smrj 	}
5923446Smrj #endif	/* _BOOT */
5933446Smrj }
5943446Smrj 
5953446Smrj #if !defined(_BOOT)
5963446Smrj static void
5973446Smrj usbser_putchar(int c)
5983446Smrj {
5993446Smrj 	if (usbser_cur - usbser_buf < MMU_PAGESIZE)
6003446Smrj 		*usbser_cur++ = c;
6013446Smrj }
6023446Smrj #endif	/* _BOOT */
6033446Smrj 
6043446Smrj static void
6053446Smrj serial_putchar(int c)
6063446Smrj {
6073446Smrj 	int checks = 10000;
6083446Smrj 
6093446Smrj 	while (((inb(port + LSR) & XHRE) == 0) && checks--)
6103446Smrj 		;
6113446Smrj 	outb(port + DAT, (char)c);
6123446Smrj }
6133446Smrj 
6143446Smrj static int
6153446Smrj serial_getchar(void)
6163446Smrj {
6173446Smrj 	uchar_t lsr;
6183446Smrj 
6193446Smrj 	while (serial_ischar() == 0)
6203446Smrj 		;
6213446Smrj 
6223446Smrj 	lsr = inb(port + LSR);
6233446Smrj 	if (lsr & (SERIAL_BREAK | SERIAL_FRAME |
6243446Smrj 	    SERIAL_PARITY | SERIAL_OVERRUN)) {
6253446Smrj 		if (lsr & SERIAL_OVERRUN) {
6263446Smrj 			return (inb(port + DAT));
6273446Smrj 		} else {
6283446Smrj 			/* Toss the garbage */
6293446Smrj 			(void) inb(port + DAT);
6303446Smrj 			return (0);
6313446Smrj 		}
6323446Smrj 	}
6333446Smrj 	return (inb(port + DAT));
6343446Smrj }
6353446Smrj 
6363446Smrj static int
6373446Smrj serial_ischar(void)
6383446Smrj {
6393446Smrj 	return (inb(port + LSR) & RCA);
6403446Smrj }
6413446Smrj 
6423446Smrj static void
6433446Smrj _doputchar(int c)
6443446Smrj {
6453446Smrj 	switch (console) {
6463446Smrj 	case CONS_TTYA:
6473446Smrj 	case CONS_TTYB:
6483446Smrj 		serial_putchar(c);
6493446Smrj 		return;
6503446Smrj 	case CONS_SCREEN_TEXT:
6513446Smrj 		screen_putchar(c);
6523446Smrj 		return;
6533446Smrj #if !defined(_BOOT)
6543446Smrj 	case CONS_USBSER:
6553446Smrj 		usbser_putchar(c);
6563446Smrj 		return;
6573446Smrj #endif /* _BOOT */
6583446Smrj 	}
6593446Smrj }
6603446Smrj 
6613446Smrj void
6623446Smrj bcons_putchar(int c)
6633446Smrj {
6643446Smrj 	static int bhcharpos = 0;
6653446Smrj 
6663446Smrj 	if (c == '\t') {
6673446Smrj 		do {
6683446Smrj 			_doputchar(' ');
6693446Smrj 		} while (++bhcharpos % 8);
6703446Smrj 		return;
6713446Smrj 	} else  if (c == '\n' || c == '\r') {
6723446Smrj 		bhcharpos = 0;
6733446Smrj 		_doputchar('\r');
6743446Smrj 		_doputchar(c);
6753446Smrj 		return;
6763446Smrj 	} else if (c == '\b') {
6773446Smrj 		if (bhcharpos)
6783446Smrj 			bhcharpos--;
6793446Smrj 		_doputchar(c);
6803446Smrj 		return;
6813446Smrj 	}
6823446Smrj 
6833446Smrj 	bhcharpos++;
6843446Smrj 	_doputchar(c);
6853446Smrj }
6863446Smrj 
6873446Smrj /*
6883446Smrj  * kernel character input functions
6893446Smrj  */
6903446Smrj int
6913446Smrj bcons_getchar(void)
6923446Smrj {
6933446Smrj 	switch (console) {
6943446Smrj 	case CONS_TTYA:
6953446Smrj 	case CONS_TTYB:
6963446Smrj 		return (serial_getchar());
6973446Smrj 	default:
6983446Smrj 		return (kb_getchar());
6993446Smrj 	}
7003446Smrj }
7013446Smrj 
7023446Smrj #if !defined(_BOOT)
7033446Smrj 
7043446Smrj int
7053446Smrj bcons_ischar(void)
7063446Smrj {
7073446Smrj 	switch (console) {
7083446Smrj 	case CONS_TTYA:
7093446Smrj 	case CONS_TTYB:
7103446Smrj 		return (serial_ischar());
7113446Smrj 	default:
7123446Smrj 		return (kb_ischar());
7133446Smrj 	}
7143446Smrj }
7153446Smrj 
7163446Smrj #endif /* _BOOT */
717