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