1 /* serial.c - serial device interface */ 2 /* 3 * GRUB -- GRand Unified Bootloader 4 * Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 */ 20 21 #ifdef SUPPORT_SERIAL 22 23 #include <shared.h> 24 #include <serial.h> 25 #include <term.h> 26 #include <terminfo.h> 27 28 #define COMP_BS_SERIAL 0x01 29 #define COMP_BS_BIOS 0x02 30 31 /* An input buffer. */ 32 static char input_buf[8]; 33 static int npending = 0; 34 35 static int serial_x; 36 static int serial_y; 37 38 static int keep_track = 1; 39 static int composite_bitset = COMP_BS_SERIAL | COMP_BS_BIOS; 40 41 42 /* Hardware-dependent definitions. */ 43 44 #ifndef GRUB_UTIL 45 /* The structure for speed vs. divisor. */ 46 struct divisor 47 { 48 int speed; 49 unsigned short div; 50 }; 51 52 /* Store the port number of a serial unit. */ 53 static unsigned short serial_hw_port = 0; 54 55 /* The table which lists common configurations. */ 56 static struct divisor divisor_tab[] = 57 { 58 { 2400, 0x0030 }, 59 { 4800, 0x0018 }, 60 { 9600, 0x000C }, 61 { 19200, 0x0006 }, 62 { 38400, 0x0003 }, 63 { 57600, 0x0002 }, 64 { 115200, 0x0001 } 65 }; 66 67 /* Read a byte from a port. */ 68 static inline unsigned char 69 inb (unsigned short port) 70 { 71 unsigned char value; 72 73 asm volatile ("inb %w1, %0" : "=a" (value) : "Nd" (port)); 74 asm volatile ("outb %%al, $0x80" : : ); 75 76 return value; 77 } 78 79 /* Write a byte to a port. */ 80 static inline void 81 outb (unsigned short port, unsigned char value) 82 { 83 asm volatile ("outb %b0, %w1" : : "a" (value), "Nd" (port)); 84 asm volatile ("outb %%al, $0x80" : : ); 85 } 86 87 /* Fetch a key. */ 88 int 89 serial_hw_fetch (void) 90 { 91 if (inb (serial_hw_port + UART_LSR) & UART_DATA_READY) 92 return inb (serial_hw_port + UART_RX); 93 94 return -1; 95 } 96 97 /* Put a chararacter. */ 98 void 99 serial_hw_put (int c) 100 { 101 int timeout = 100000; 102 103 /* Wait until the transmitter holding register is empty. */ 104 while ((inb (serial_hw_port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0) 105 { 106 if (--timeout == 0) 107 /* There is something wrong. But what can I do? */ 108 return; 109 } 110 111 outb (serial_hw_port + UART_TX, c); 112 } 113 114 void 115 serial_hw_delay (void) 116 { 117 outb (0x80, 0); 118 } 119 120 /* Return the port number for the UNITth serial device. */ 121 unsigned short 122 serial_hw_get_port (int unit) 123 { 124 /* The BIOS data area. */ 125 const unsigned short *addr = (const unsigned short *) 0x0400; 126 127 return addr[unit]; 128 } 129 130 /* Initialize a serial device. PORT is the port number for a serial device. 131 SPEED is a DTE-DTE speed which must be one of these: 2400, 4800, 9600, 132 19200, 38400, 57600 and 115200. WORD_LEN is the word length to be used 133 for the device. Likewise, PARITY is the type of the parity and 134 STOP_BIT_LEN is the length of the stop bit. The possible values for 135 WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as 136 macros. */ 137 int 138 serial_hw_init (unsigned short port, unsigned int speed, 139 int word_len, int parity, int stop_bit_len) 140 { 141 int i; 142 unsigned short div = 0; 143 unsigned char status = 0; 144 145 if (port == 0) 146 return 0; 147 148 /* Make sure the port actually exists. */ 149 outb (port + UART_SR, UART_SR_TEST); 150 outb (port + UART_FCR, 0); 151 status = inb (port + UART_SR); 152 if (status != UART_SR_TEST) 153 return 0; 154 155 /* Turn off the interrupt. */ 156 outb (port + UART_IER, 0); 157 158 /* Set DLAB. */ 159 outb (port + UART_LCR, UART_DLAB); 160 161 /* Set the baud rate. */ 162 for (i = 0; i < sizeof (divisor_tab) / sizeof (divisor_tab[0]); i++) 163 if (divisor_tab[i].speed == speed) 164 { 165 div = divisor_tab[i].div; 166 break; 167 } 168 169 if (div == 0) 170 return 0; 171 172 outb (port + UART_DLL, div & 0xFF); 173 outb (port + UART_DLH, div >> 8); 174 175 /* Set the line status. */ 176 status = parity | word_len | stop_bit_len; 177 outb (port + UART_LCR, status); 178 179 /* Enable the FIFO. */ 180 outb (port + UART_FCR, UART_ENABLE_FIFO); 181 182 /* Turn on DTR, RTS, and OUT2. */ 183 outb (port + UART_MCR, UART_ENABLE_MODEM); 184 185 /* Store the port number. */ 186 serial_hw_port = port; 187 188 /* Drain the input buffer. */ 189 while (serial_checkkey () != -1) 190 (void) serial_getkey (); 191 192 /* Get rid of TERM_NEED_INIT from the serial terminal. */ 193 for (i = 0; term_table[i].name; i++) 194 if (grub_strcmp (term_table[i].name, "serial") == 0 || 195 grub_strcmp (term_table[i].name, "composite") == 0) 196 { 197 term_table[i].flags &= ~TERM_NEED_INIT; 198 } 199 200 return 1; 201 } 202 #endif /* ! GRUB_UTIL */ 203 204 205 /* Generic definitions. */ 206 207 static void 208 serial_translate_key_sequence (void) 209 { 210 const struct 211 { 212 char key; 213 char ascii; 214 } 215 three_code_table[] = 216 { 217 {'A', 16}, 218 {'B', 14}, 219 {'C', 6}, 220 {'D', 2}, 221 {'F', 5}, 222 {'H', 1}, 223 {'4', 4} 224 }; 225 226 const struct 227 { 228 short key; 229 char ascii; 230 } 231 four_code_table[] = 232 { 233 {('1' | ('~' << 8)), 1}, 234 {('3' | ('~' << 8)), 4}, 235 {('5' | ('~' << 8)), 7}, 236 {('6' | ('~' << 8)), 3}, 237 }; 238 239 /* The buffer must start with ``ESC [''. */ 240 if (*((unsigned short *) input_buf) != ('\e' | ('[' << 8))) 241 return; 242 243 if (npending >= 3) 244 { 245 int i; 246 247 for (i = 0; 248 i < sizeof (three_code_table) / sizeof (three_code_table[0]); 249 i++) 250 if (three_code_table[i].key == input_buf[2]) 251 { 252 input_buf[0] = three_code_table[i].ascii; 253 npending -= 2; 254 grub_memmove (input_buf + 1, input_buf + 3, npending - 1); 255 return; 256 } 257 } 258 259 if (npending >= 4) 260 { 261 int i; 262 short key = *((short *) (input_buf + 2)); 263 264 for (i = 0; 265 i < sizeof (four_code_table) / sizeof (four_code_table[0]); 266 i++) 267 if (four_code_table[i].key == key) 268 { 269 input_buf[0] = four_code_table[i].ascii; 270 npending -= 3; 271 grub_memmove (input_buf + 1, input_buf + 4, npending - 1); 272 return; 273 } 274 } 275 } 276 277 static 278 int fill_input_buf (int nowait) 279 { 280 int i; 281 282 for (i = 0; i < 10000 && npending < sizeof (input_buf); i++) 283 { 284 int c; 285 286 c = serial_hw_fetch (); 287 if (c >= 0) 288 { 289 input_buf[npending++] = c; 290 291 /* Reset the counter to zero, to wait for the same interval. */ 292 i = 0; 293 } 294 295 if (nowait) 296 break; 297 } 298 299 /* Translate some key sequences. */ 300 serial_translate_key_sequence (); 301 302 return npending; 303 } 304 305 /* The serial version of getkey. */ 306 int 307 serial_getkey (void) 308 { 309 int c; 310 311 while (! fill_input_buf (0)) 312 ; 313 314 c = input_buf[0]; 315 npending--; 316 grub_memmove (input_buf, input_buf + 1, npending); 317 318 return c; 319 } 320 321 /* The serial version of checkkey. */ 322 int 323 serial_checkkey (void) 324 { 325 if (fill_input_buf (1)) 326 return input_buf[0]; 327 328 return -1; 329 } 330 331 /* The serial version of grub_putchar. */ 332 void 333 serial_putchar (int c) 334 { 335 /* Keep track of the cursor. */ 336 if (keep_track) 337 { 338 /* The serial terminal doesn't have VGA fonts. */ 339 switch (c) 340 { 341 case DISP_UL: 342 c = ACS_ULCORNER; 343 break; 344 case DISP_UR: 345 c = ACS_URCORNER; 346 break; 347 case DISP_LL: 348 c = ACS_LLCORNER; 349 break; 350 case DISP_LR: 351 c = ACS_LRCORNER; 352 break; 353 case DISP_HORIZ: 354 c = ACS_HLINE; 355 break; 356 case DISP_VERT: 357 c = ACS_VLINE; 358 break; 359 case DISP_LEFT: 360 c = ACS_LARROW; 361 break; 362 case DISP_RIGHT: 363 c = ACS_RARROW; 364 break; 365 case DISP_UP: 366 c = ACS_UARROW; 367 break; 368 case DISP_DOWN: 369 c = ACS_DARROW; 370 break; 371 default: 372 break; 373 } 374 375 switch (c) 376 { 377 case '\r': 378 serial_x = 0; 379 break; 380 381 case '\n': 382 serial_y++; 383 break; 384 385 case '\b': 386 case 127: 387 if (serial_x > 0) 388 serial_x--; 389 break; 390 391 case '\a': 392 break; 393 394 default: 395 if (serial_x >= 79) 396 { 397 serial_putchar ('\r'); 398 serial_putchar ('\n'); 399 } 400 serial_x++; 401 break; 402 } 403 } 404 405 serial_hw_put (c); 406 } 407 408 int 409 serial_getxy (void) 410 { 411 return (serial_x << 8) | serial_y; 412 } 413 414 void 415 serial_gotoxy (int x, int y) 416 { 417 int saved_cbs = composite_bitset; 418 419 keep_track = 0; 420 composite_bitset &= ~COMP_BS_BIOS; 421 ti_cursor_address (x, y); 422 composite_bitset = saved_cbs; 423 keep_track = 1; 424 425 serial_x = x; 426 serial_y = y; 427 } 428 429 void 430 serial_cls (void) 431 { 432 int saved_cbs = composite_bitset; 433 434 keep_track = 0; 435 composite_bitset &= ~COMP_BS_BIOS; 436 ti_clear_screen (); 437 composite_bitset = saved_cbs; 438 keep_track = 1; 439 440 serial_x = serial_y = 0; 441 } 442 443 void 444 serial_setcolorstate (color_state state) 445 { 446 int saved_cbs = composite_bitset; 447 448 keep_track = 0; 449 composite_bitset &= ~COMP_BS_BIOS; 450 if (state == COLOR_STATE_HIGHLIGHT) 451 ti_enter_standout_mode (); 452 else 453 ti_exit_standout_mode (); 454 composite_bitset = saved_cbs; 455 keep_track = 1; 456 } 457 458 void 459 composite_putchar (int c) 460 { 461 if (composite_bitset & COMP_BS_SERIAL) 462 serial_putchar (c); 463 if (composite_bitset & COMP_BS_BIOS) 464 console_putchar (c); 465 } 466 467 int 468 composite_getkey (void) 469 { 470 for (;;) { 471 if (serial_checkkey () != -1) 472 return (serial_getkey ()); 473 if (console_checkkey () != -1) 474 return (console_getkey ()); 475 } 476 } 477 478 int 479 composite_checkkey (void) 480 { 481 int ch; 482 483 if ((ch = serial_checkkey ()) != -1) 484 return (ch); 485 return (console_checkkey ()); 486 } 487 488 void 489 composite_gotoxy (int x, int y) 490 { 491 serial_gotoxy (x, y); 492 console_gotoxy (x, y); 493 } 494 495 void 496 composite_cls (void) 497 { 498 serial_cls(); 499 console_cls(); 500 } 501 502 void 503 composite_setcolorstate (color_state state) 504 { 505 serial_setcolorstate (state); 506 console_setcolorstate (state); 507 } 508 509 #endif /* SUPPORT_SERIAL */ 510