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/* An input buffer. */ 29static char input_buf[8]; 30static int npending = 0; 31 32static int serial_x; 33static int serial_y; 34 35static int keep_track = 1; 36 37 38/* Hardware-dependent definitions. */ 39 40#ifndef GRUB_UTIL 41/* The structure for speed vs. divisor. */ 42struct divisor 43{ 44 int speed; 45 unsigned short div; 46}; 47 48/* Store the port number of a serial unit. */ 49static unsigned short serial_hw_port = 0; 50 51/* The table which lists common configurations. */ 52static struct divisor divisor_tab[] = 53 { 54 { 2400, 0x0030 }, 55 { 4800, 0x0018 }, 56 { 9600, 0x000C }, 57 { 19200, 0x0006 }, 58 { 38400, 0x0003 }, 59 { 57600, 0x0002 }, 60 { 115200, 0x0001 } 61 }; 62 63/* Read a byte from a port. */ 64static inline unsigned char 65inb (unsigned short port) 66{ 67 unsigned char value; 68 69 asm volatile ("inb %w1, %0" : "=a" (value) : "Nd" (port)); 70 asm volatile ("outb %%al, $0x80" : : ); 71 72 return value; 73} 74 75/* Write a byte to a port. */ 76static inline void 77outb (unsigned short port, unsigned char value) 78{ 79 asm volatile ("outb %b0, %w1" : : "a" (value), "Nd" (port)); 80 asm volatile ("outb %%al, $0x80" : : ); 81} 82 83/* Fetch a key. */ 84int 85serial_hw_fetch (void) 86{ 87 if (inb (serial_hw_port + UART_LSR) & UART_DATA_READY) 88 return inb (serial_hw_port + UART_RX); 89 90 return -1; 91} 92 93/* Put a chararacter. */ 94void 95serial_hw_put (int c) 96{ 97 int timeout = 100000; 98 99 /* Wait until the transmitter holding register is empty. */ 100 while ((inb (serial_hw_port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0) 101 { 102 if (--timeout == 0) 103 /* There is something wrong. But what can I do? */ 104 return; 105 } 106 107 outb (serial_hw_port + UART_TX, c); 108} 109 110void 111serial_hw_delay (void) 112{ 113 outb (0x80, 0); 114} 115 116/* Return the port number for the UNITth serial device. */ 117unsigned short 118serial_hw_get_port (int unit) 119{ 120 /* The BIOS data area. */ 121 const unsigned short *addr = (const unsigned short *) 0x0400; 122 123 return addr[unit]; 124} 125 126/* Initialize a serial device. PORT is the port number for a serial device. 127 SPEED is a DTE-DTE speed which must be one of these: 2400, 4800, 9600, 128 19200, 38400, 57600 and 115200. WORD_LEN is the word length to be used 129 for the device. Likewise, PARITY is the type of the parity and 130 STOP_BIT_LEN is the length of the stop bit. The possible values for 131 WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as 132 macros. */ 133int 134serial_hw_init (unsigned short port, unsigned int speed, 135 int word_len, int parity, int stop_bit_len) 136{ 137 int i; 138 unsigned short div = 0; 139 unsigned char status = 0; 140 141 /* Turn off the interrupt. */ 142 outb (port + UART_IER, 0); 143 144 /* Set DLAB. */ 145 outb (port + UART_LCR, UART_DLAB); 146 147 /* Set the baud rate. */ 148 for (i = 0; i < sizeof (divisor_tab) / sizeof (divisor_tab[0]); i++) 149 if (divisor_tab[i].speed == speed) 150 { 151 div = divisor_tab[i].div; 152 break; 153 } 154 155 if (div == 0) 156 return 0; 157 158 outb (port + UART_DLL, div & 0xFF); 159 outb (port + UART_DLH, div >> 8); 160 161 /* Set the line status. */ 162 status |= parity | word_len | stop_bit_len; 163 outb (port + UART_LCR, status); 164 165 /* Enable the FIFO. */ 166 outb (port + UART_FCR, UART_ENABLE_FIFO); 167 168 /* Turn on DTR, RTS, and OUT2. */ 169 outb (port + UART_MCR, UART_ENABLE_MODEM); 170 171 /* Store the port number. */ 172 serial_hw_port = port; 173 174 /* Drain the input buffer. */ 175 while (serial_checkkey () != -1) 176 (void) serial_getkey (); 177 178 /* Get rid of TERM_NEED_INIT from the serial terminal. */ 179 for (i = 0; term_table[i].name; i++) 180 if (grub_strcmp (term_table[i].name, "serial") == 0) 181 { 182 term_table[i].flags &= ~TERM_NEED_INIT; 183 break; 184 } 185 186 /* FIXME: should check if the serial terminal was found. */ 187 188 return 1; 189} 190#endif /* ! GRUB_UTIL */ 191 192 193/* Generic definitions. */ 194 195static void 196serial_translate_key_sequence (void) 197{ 198 const struct 199 { 200 char key; 201 char ascii; 202 } 203 three_code_table[] = 204 { 205 {'A', 16}, 206 {'B', 14}, 207 {'C', 6}, 208 {'D', 2}, 209 {'F', 5}, 210 {'H', 1}, 211 {'4', 4} 212 }; 213 214 const struct 215 { 216 short key; 217 char ascii; 218 } 219 four_code_table[] = 220 { 221 {('1' | ('~' << 8)), 1}, 222 {('3' | ('~' << 8)), 4}, 223 {('5' | ('~' << 8)), 7}, 224 {('6' | ('~' << 8)), 3}, 225 }; 226 227 /* The buffer must start with ``ESC [''. */ 228 if (*((unsigned short *) input_buf) != ('\e' | ('[' << 8))) 229 return; 230 231 if (npending >= 3) 232 { 233 int i; 234 235 for (i = 0; 236 i < sizeof (three_code_table) / sizeof (three_code_table[0]); 237 i++) 238 if (three_code_table[i].key == input_buf[2]) 239 { 240 input_buf[0] = three_code_table[i].ascii; 241 npending -= 2; 242 grub_memmove (input_buf + 1, input_buf + 3, npending - 1); 243 return; 244 } 245 } 246 247 if (npending >= 4) 248 { 249 int i; 250 short key = *((short *) (input_buf + 2)); 251 252 for (i = 0; 253 i < sizeof (four_code_table) / sizeof (four_code_table[0]); 254 i++) 255 if (four_code_table[i].key == key) 256 { 257 input_buf[0] = four_code_table[i].ascii; 258 npending -= 3; 259 grub_memmove (input_buf + 1, input_buf + 4, npending - 1); 260 return; 261 } 262 } 263} 264 265static 266int fill_input_buf (int nowait) 267{ 268 int i; 269 270 for (i = 0; i < 10000 && npending < sizeof (input_buf); i++) 271 { 272 int c; 273 274 c = serial_hw_fetch (); 275 if (c >= 0) 276 { 277 input_buf[npending++] = c; 278 279 /* Reset the counter to zero, to wait for the same interval. */ 280 i = 0; 281 } 282 283 if (nowait) 284 break; 285 } 286 287 /* Translate some key sequences. */ 288 serial_translate_key_sequence (); 289 290 return npending; 291} 292 293/* The serial version of getkey. */ 294int 295serial_getkey (void) 296{ 297 int c; 298 299 while (! fill_input_buf (0)) 300 ; 301 302 c = input_buf[0]; 303 npending--; 304 grub_memmove (input_buf, input_buf + 1, npending); 305 306 return c; 307} 308 309/* The serial version of checkkey. */ 310int 311serial_checkkey (void) 312{ 313 if (fill_input_buf (1)) 314 return input_buf[0]; 315 316 return -1; 317} 318 319/* The serial version of grub_putchar. */ 320void 321serial_putchar (int c) 322{ 323 /* Keep track of the cursor. */ 324 if (keep_track) 325 { 326 /* The serial terminal doesn't have VGA fonts. */ 327 switch (c) 328 { 329 case DISP_UL: 330 c = ACS_ULCORNER; 331 break; 332 case DISP_UR: 333 c = ACS_URCORNER; 334 break; 335 case DISP_LL: 336 c = ACS_LLCORNER; 337 break; 338 case DISP_LR: 339 c = ACS_LRCORNER; 340 break; 341 case DISP_HORIZ: 342 c = ACS_HLINE; 343 break; 344 case DISP_VERT: 345 c = ACS_VLINE; 346 break; 347 case DISP_LEFT: 348 c = ACS_LARROW; 349 break; 350 case DISP_RIGHT: 351 c = ACS_RARROW; 352 break; 353 case DISP_UP: 354 c = ACS_UARROW; 355 break; 356 case DISP_DOWN: 357 c = ACS_DARROW; 358 break; 359 default: 360 break; 361 } 362 363 switch (c) 364 { 365 case '\r': 366 serial_x = 0; 367 break; 368 369 case '\n': 370 serial_y++; 371 break; 372 373 case '\b': 374 case 127: 375 if (serial_x > 0) 376 serial_x--; 377 break; 378 379 case '\a': 380 break; 381 382 default: 383 if (serial_x >= 79) 384 { 385 serial_putchar ('\r'); 386 serial_putchar ('\n'); 387 } 388 serial_x++; 389 break; 390 } 391 } 392 393 serial_hw_put (c); 394} 395 396int 397serial_getxy (void) 398{ 399 return (serial_x << 8) | serial_y; 400} 401 402void 403serial_gotoxy (int x, int y) 404{ 405 keep_track = 0; 406 ti_cursor_address (x, y); 407 keep_track = 1; 408 409 serial_x = x; 410 serial_y = y; 411} 412 413void 414serial_cls (void) 415{ 416 keep_track = 0; 417 ti_clear_screen (); 418 keep_track = 1; 419 420 serial_x = serial_y = 0; 421} 422 423void 424serial_setcolorstate (color_state state) 425{ 426 keep_track = 0; 427 if (state == COLOR_STATE_HIGHLIGHT) 428 ti_enter_standout_mode (); 429 else 430 ti_exit_standout_mode (); 431 keep_track = 1; 432} 433 434#endif /* SUPPORT_SERIAL */ 435