Merge branch 'master' of git://github.com/illumos/illumos-gate
[unleashed.git] / usr / src / grub / grub-0.97 / stage2 / serial.c
blobb9f27bc5814d76e8335afa266d4d0ff7e8213b9b
1 /* serial.c - serial device interface */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2000,2001,2002 Free Software Foundation, Inc.
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.
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.
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.
21 #ifdef SUPPORT_SERIAL
23 #include <shared.h>
24 #include <serial.h>
25 #include <term.h>
26 #include <terminfo.h>
28 #define COMP_BS_SERIAL 0x01
29 #define COMP_BS_BIOS 0x02
31 /* An input buffer. */
32 static char input_buf[8];
33 static int npending = 0;
35 static int serial_x;
36 static int serial_y;
38 static int keep_track = 1;
39 static int composite_bitset = COMP_BS_SERIAL | COMP_BS_BIOS;
42 /* Hardware-dependent definitions. */
44 #ifndef GRUB_UTIL
45 /* The structure for speed vs. divisor. */
46 struct divisor
48 int speed;
49 unsigned short div;
52 /* Store the port number of a serial unit. */
53 static unsigned short serial_hw_port = 0;
55 /* The table which lists common configurations. */
56 static struct divisor divisor_tab[] =
58 { 2400, 0x0030 },
59 { 4800, 0x0018 },
60 { 9600, 0x000C },
61 { 19200, 0x0006 },
62 { 38400, 0x0003 },
63 { 57600, 0x0002 },
64 { 115200, 0x0001 }
67 /* Read a byte from a port. */
68 static inline unsigned char
69 inb (unsigned short port)
71 unsigned char value;
73 asm volatile ("inb %w1, %0" : "=a" (value) : "Nd" (port));
74 asm volatile ("outb %%al, $0x80" : : );
76 return value;
79 /* Write a byte to a port. */
80 static inline void
81 outb (unsigned short port, unsigned char value)
83 asm volatile ("outb %b0, %w1" : : "a" (value), "Nd" (port));
84 asm volatile ("outb %%al, $0x80" : : );
87 /* Fetch a key. */
88 int
89 serial_hw_fetch (void)
91 if (inb (serial_hw_port + UART_LSR) & UART_DATA_READY)
92 return inb (serial_hw_port + UART_RX);
94 return -1;
97 /* Put a chararacter. */
98 void
99 serial_hw_put (int c)
101 int timeout = 100000;
103 /* Wait until the transmitter holding register is empty. */
104 while ((inb (serial_hw_port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0)
106 if (--timeout == 0)
107 /* There is something wrong. But what can I do? */
108 return;
111 outb (serial_hw_port + UART_TX, c);
114 void
115 serial_hw_delay (void)
117 outb (0x80, 0);
120 /* Return the port number for the UNITth serial device. */
121 unsigned short
122 serial_hw_get_port (int unit)
124 /* The BIOS data area. */
125 const unsigned short *addr = (const unsigned short *) 0x0400;
127 return addr[unit];
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. */
138 serial_hw_init (unsigned short port, unsigned int speed,
139 int word_len, int parity, int stop_bit_len)
141 int i;
142 unsigned short div = 0;
143 unsigned char status = 0;
145 if (port == 0)
146 return 0;
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;
155 /* Turn off the interrupt. */
156 outb (port + UART_IER, 0);
158 /* Set DLAB. */
159 outb (port + UART_LCR, UART_DLAB);
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)
165 div = divisor_tab[i].div;
166 break;
169 if (div == 0)
170 return 0;
172 outb (port + UART_DLL, div & 0xFF);
173 outb (port + UART_DLH, div >> 8);
175 /* Set the line status. */
176 status = parity | word_len | stop_bit_len;
177 outb (port + UART_LCR, status);
179 /* Enable the FIFO. */
180 outb (port + UART_FCR, UART_ENABLE_FIFO);
182 /* Turn on DTR, RTS, and OUT2. */
183 outb (port + UART_MCR, UART_ENABLE_MODEM);
185 /* Store the port number. */
186 serial_hw_port = port;
188 /* Drain the input buffer. */
189 while (serial_checkkey () != -1)
190 (void) serial_getkey ();
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)
197 term_table[i].flags &= ~TERM_NEED_INIT;
200 return 1;
202 #endif /* ! GRUB_UTIL */
205 /* Generic definitions. */
207 static void
208 serial_translate_key_sequence (void)
210 const struct
212 char key;
213 char ascii;
215 three_code_table[] =
217 {'A', 16},
218 {'B', 14},
219 {'C', 6},
220 {'D', 2},
221 {'F', 5},
222 {'H', 1},
223 {'4', 4}
226 const struct
228 short key;
229 char ascii;
231 four_code_table[] =
233 {('1' | ('~' << 8)), 1},
234 {('3' | ('~' << 8)), 4},
235 {('5' | ('~' << 8)), 7},
236 {('6' | ('~' << 8)), 3},
239 /* The buffer must start with ``ESC [''. */
240 if (*((unsigned short *) input_buf) != ('\e' | ('[' << 8)))
241 return;
243 if (npending >= 3)
245 int i;
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])
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;
259 if (npending >= 4)
261 int i;
262 short key = *((short *) (input_buf + 2));
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)
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;
277 static
278 int fill_input_buf (int nowait)
280 int i;
282 for (i = 0; i < 10000 && npending < sizeof (input_buf); i++)
284 int c;
286 c = serial_hw_fetch ();
287 if (c >= 0)
289 input_buf[npending++] = c;
291 /* Reset the counter to zero, to wait for the same interval. */
292 i = 0;
295 if (nowait)
296 break;
299 /* Translate some key sequences. */
300 serial_translate_key_sequence ();
302 return npending;
305 /* The serial version of getkey. */
307 serial_getkey (void)
309 int c;
311 while (! fill_input_buf (0))
314 c = input_buf[0];
315 npending--;
316 grub_memmove (input_buf, input_buf + 1, npending);
318 return c;
321 /* The serial version of checkkey. */
323 serial_checkkey (void)
325 if (fill_input_buf (1))
326 return input_buf[0];
328 return -1;
331 /* The serial version of grub_putchar. */
332 void
333 serial_putchar (int c)
335 /* Keep track of the cursor. */
336 if (keep_track)
338 /* The serial terminal doesn't have VGA fonts. */
339 switch (c)
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;
375 switch (c)
377 case '\r':
378 serial_x = 0;
379 break;
381 case '\n':
382 serial_y++;
383 break;
385 case '\b':
386 case 127:
387 if (serial_x > 0)
388 serial_x--;
389 break;
391 case '\a':
392 break;
394 default:
395 if (serial_x >= 79)
397 serial_putchar ('\r');
398 serial_putchar ('\n');
400 serial_x++;
401 break;
405 serial_hw_put (c);
409 serial_getxy (void)
411 return (serial_x << 8) | serial_y;
414 void
415 serial_gotoxy (int x, int y)
417 int saved_cbs = composite_bitset;
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;
425 serial_x = x;
426 serial_y = y;
429 void
430 serial_cls (void)
432 int saved_cbs = composite_bitset;
434 keep_track = 0;
435 composite_bitset &= ~COMP_BS_BIOS;
436 ti_clear_screen ();
437 composite_bitset = saved_cbs;
438 keep_track = 1;
440 serial_x = serial_y = 0;
443 void
444 serial_setcolorstate (color_state state)
446 int saved_cbs = composite_bitset;
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;
458 void
459 composite_putchar (int c)
461 if (composite_bitset & COMP_BS_SERIAL)
462 serial_putchar (c);
463 if (composite_bitset & COMP_BS_BIOS)
464 console_putchar (c);
468 composite_getkey (void)
470 for (;;) {
471 if (serial_checkkey () != -1)
472 return (serial_getkey ());
473 if (console_checkkey () != -1)
474 return (console_getkey ());
479 composite_checkkey (void)
481 int ch;
483 if ((ch = serial_checkkey ()) != -1)
484 return (ch);
485 return (console_checkkey ());
488 void
489 composite_gotoxy (int x, int y)
491 serial_gotoxy (x, y);
492 console_gotoxy (x, y);
495 void
496 composite_cls (void)
498 serial_cls();
499 console_cls();
502 void
503 composite_setcolorstate (color_state state)
505 serial_setcolorstate (state);
506 console_setcolorstate (state);
509 #endif /* SUPPORT_SERIAL */