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
inb(unsigned short port)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
outb(unsigned short port,unsigned char value)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
serial_hw_fetch(void)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
serial_hw_put(int c)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
serial_hw_delay(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
serial_hw_get_port(int unit)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
serial_hw_init(unsigned short port,unsigned int speed,int word_len,int parity,int stop_bit_len)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 /* Turn off the interrupt. */
146 outb (port + UART_IER, 0);
147
148 /* Set DLAB. */
149 outb (port + UART_LCR, UART_DLAB);
150
151 /* Set the baud rate. */
152 for (i = 0; i < sizeof (divisor_tab) / sizeof (divisor_tab[0]); i++)
153 if (divisor_tab[i].speed == speed)
154 {
155 div = divisor_tab[i].div;
156 break;
157 }
158
159 if (div == 0)
160 return 0;
161
162 outb (port + UART_DLL, div & 0xFF);
163 outb (port + UART_DLH, div >> 8);
164
165 /* Set the line status. */
166 status |= parity | word_len | stop_bit_len;
167 outb (port + UART_LCR, status);
168
169 /* Enable the FIFO. */
170 outb (port + UART_FCR, UART_ENABLE_FIFO);
171
172 /* Turn on DTR, RTS, and OUT2. */
173 outb (port + UART_MCR, UART_ENABLE_MODEM);
174
175 /* Store the port number. */
176 serial_hw_port = port;
177
178 /* Drain the input buffer. */
179 while (serial_checkkey () != -1)
180 (void) serial_getkey ();
181
182 /* Get rid of TERM_NEED_INIT from the serial terminal. */
183 for (i = 0; term_table[i].name; i++)
184 if (grub_strcmp (term_table[i].name, "serial") == 0 ||
185 grub_strcmp (term_table[i].name, "composite") == 0)
186 {
187 term_table[i].flags &= ~TERM_NEED_INIT;
188 }
189
190 /* FIXME: should check if the serial terminal was found. */
191
192 return 1;
193 }
194 #endif /* ! GRUB_UTIL */
195
196
197 /* Generic definitions. */
198
199 static void
serial_translate_key_sequence(void)200 serial_translate_key_sequence (void)
201 {
202 const struct
203 {
204 char key;
205 char ascii;
206 }
207 three_code_table[] =
208 {
209 {'A', 16},
210 {'B', 14},
211 {'C', 6},
212 {'D', 2},
213 {'F', 5},
214 {'H', 1},
215 {'4', 4}
216 };
217
218 const struct
219 {
220 short key;
221 char ascii;
222 }
223 four_code_table[] =
224 {
225 {('1' | ('~' << 8)), 1},
226 {('3' | ('~' << 8)), 4},
227 {('5' | ('~' << 8)), 7},
228 {('6' | ('~' << 8)), 3},
229 };
230
231 /* The buffer must start with ``ESC [''. */
232 if (*((unsigned short *) input_buf) != ('\e' | ('[' << 8)))
233 return;
234
235 if (npending >= 3)
236 {
237 int i;
238
239 for (i = 0;
240 i < sizeof (three_code_table) / sizeof (three_code_table[0]);
241 i++)
242 if (three_code_table[i].key == input_buf[2])
243 {
244 input_buf[0] = three_code_table[i].ascii;
245 npending -= 2;
246 grub_memmove (input_buf + 1, input_buf + 3, npending - 1);
247 return;
248 }
249 }
250
251 if (npending >= 4)
252 {
253 int i;
254 short key = *((short *) (input_buf + 2));
255
256 for (i = 0;
257 i < sizeof (four_code_table) / sizeof (four_code_table[0]);
258 i++)
259 if (four_code_table[i].key == key)
260 {
261 input_buf[0] = four_code_table[i].ascii;
262 npending -= 3;
263 grub_memmove (input_buf + 1, input_buf + 4, npending - 1);
264 return;
265 }
266 }
267 }
268
269 static
fill_input_buf(int nowait)270 int fill_input_buf (int nowait)
271 {
272 int i;
273
274 for (i = 0; i < 10000 && npending < sizeof (input_buf); i++)
275 {
276 int c;
277
278 c = serial_hw_fetch ();
279 if (c >= 0)
280 {
281 input_buf[npending++] = c;
282
283 /* Reset the counter to zero, to wait for the same interval. */
284 i = 0;
285 }
286
287 if (nowait)
288 break;
289 }
290
291 /* Translate some key sequences. */
292 serial_translate_key_sequence ();
293
294 return npending;
295 }
296
297 /* The serial version of getkey. */
298 int
serial_getkey(void)299 serial_getkey (void)
300 {
301 int c;
302
303 while (! fill_input_buf (0))
304 ;
305
306 c = input_buf[0];
307 npending--;
308 grub_memmove (input_buf, input_buf + 1, npending);
309
310 return c;
311 }
312
313 /* The serial version of checkkey. */
314 int
serial_checkkey(void)315 serial_checkkey (void)
316 {
317 if (fill_input_buf (1))
318 return input_buf[0];
319
320 return -1;
321 }
322
323 /* The serial version of grub_putchar. */
324 void
serial_putchar(int c)325 serial_putchar (int c)
326 {
327 /* Keep track of the cursor. */
328 if (keep_track)
329 {
330 /* The serial terminal doesn't have VGA fonts. */
331 switch (c)
332 {
333 case DISP_UL:
334 c = ACS_ULCORNER;
335 break;
336 case DISP_UR:
337 c = ACS_URCORNER;
338 break;
339 case DISP_LL:
340 c = ACS_LLCORNER;
341 break;
342 case DISP_LR:
343 c = ACS_LRCORNER;
344 break;
345 case DISP_HORIZ:
346 c = ACS_HLINE;
347 break;
348 case DISP_VERT:
349 c = ACS_VLINE;
350 break;
351 case DISP_LEFT:
352 c = ACS_LARROW;
353 break;
354 case DISP_RIGHT:
355 c = ACS_RARROW;
356 break;
357 case DISP_UP:
358 c = ACS_UARROW;
359 break;
360 case DISP_DOWN:
361 c = ACS_DARROW;
362 break;
363 default:
364 break;
365 }
366
367 switch (c)
368 {
369 case '\r':
370 serial_x = 0;
371 break;
372
373 case '\n':
374 serial_y++;
375 break;
376
377 case '\b':
378 case 127:
379 if (serial_x > 0)
380 serial_x--;
381 break;
382
383 case '\a':
384 break;
385
386 default:
387 if (serial_x >= 79)
388 {
389 serial_putchar ('\r');
390 serial_putchar ('\n');
391 }
392 serial_x++;
393 break;
394 }
395 }
396
397 serial_hw_put (c);
398 }
399
400 int
serial_getxy(void)401 serial_getxy (void)
402 {
403 return (serial_x << 8) | serial_y;
404 }
405
406 void
serial_gotoxy(int x,int y)407 serial_gotoxy (int x, int y)
408 {
409 int saved_cbs = composite_bitset;
410
411 keep_track = 0;
412 composite_bitset &= ~COMP_BS_BIOS;
413 ti_cursor_address (x, y);
414 composite_bitset = saved_cbs;
415 keep_track = 1;
416
417 serial_x = x;
418 serial_y = y;
419 }
420
421 void
serial_cls(void)422 serial_cls (void)
423 {
424 int saved_cbs = composite_bitset;
425
426 keep_track = 0;
427 composite_bitset &= ~COMP_BS_BIOS;
428 ti_clear_screen ();
429 composite_bitset = saved_cbs;
430 keep_track = 1;
431
432 serial_x = serial_y = 0;
433 }
434
435 void
serial_setcolorstate(color_state state)436 serial_setcolorstate (color_state state)
437 {
438 int saved_cbs = composite_bitset;
439
440 keep_track = 0;
441 composite_bitset &= ~COMP_BS_BIOS;
442 if (state == COLOR_STATE_HIGHLIGHT)
443 ti_enter_standout_mode ();
444 else
445 ti_exit_standout_mode ();
446 composite_bitset = saved_cbs;
447 keep_track = 1;
448 }
449
450 void
composite_putchar(int c)451 composite_putchar (int c)
452 {
453 if (composite_bitset & COMP_BS_SERIAL)
454 serial_putchar (c);
455 if (composite_bitset & COMP_BS_BIOS)
456 console_putchar (c);
457 }
458
459 int
composite_getkey(void)460 composite_getkey (void)
461 {
462 for (;;) {
463 if (serial_checkkey () != -1)
464 return (serial_getkey ());
465 if (console_checkkey () != -1)
466 return (console_getkey ());
467 }
468 }
469
470 int
composite_checkkey(void)471 composite_checkkey (void)
472 {
473 int ch;
474
475 if ((ch = serial_checkkey ()) != -1)
476 return (ch);
477 return (console_checkkey ());
478 }
479
480 void
composite_gotoxy(int x,int y)481 composite_gotoxy (int x, int y)
482 {
483 serial_gotoxy (x, y);
484 console_gotoxy (x, y);
485 }
486
487 void
composite_cls(void)488 composite_cls (void)
489 {
490 serial_cls();
491 console_cls();
492 }
493
494 void
composite_setcolorstate(color_state state)495 composite_setcolorstate (color_state state)
496 {
497 serial_setcolorstate (state);
498 console_setcolorstate (state);
499 }
500
501 #endif /* SUPPORT_SERIAL */
502