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. */
29 static char input_buf[8];
30 static int npending = 0;
31
32 static int serial_x;
33 static int serial_y;
34
35 static int keep_track = 1;
36
37
38 /* Hardware-dependent definitions. */
39
40 #ifndef GRUB_UTIL
41 /* The structure for speed vs. divisor. */
42 struct divisor
43 {
44 int speed;
45 unsigned short div;
46 };
47
48 /* Store the port number of a serial unit. */
49 static unsigned short serial_hw_port = 0;
50
51 /* The table which lists common configurations. */
52 static 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. */
64 static inline unsigned char
inb(unsigned short port)65 inb (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. */
76 static inline void
outb(unsigned short port,unsigned char value)77 outb (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. */
84 int
serial_hw_fetch(void)85 serial_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. */
94 void
serial_hw_put(int c)95 serial_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
110 void
serial_hw_delay(void)111 serial_hw_delay (void)
112 {
113 outb (0x80, 0);
114 }
115
116 /* Return the port number for the UNITth serial device. */
117 unsigned short
serial_hw_get_port(int unit)118 serial_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. */
133 int
serial_hw_init(unsigned short port,unsigned int speed,int word_len,int parity,int stop_bit_len)134 serial_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
195 static void
serial_translate_key_sequence(void)196 serial_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
265 static
fill_input_buf(int nowait)266 int 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. */
294 int
serial_getkey(void)295 serial_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. */
310 int
serial_checkkey(void)311 serial_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. */
320 void
serial_putchar(int c)321 serial_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
396 int
serial_getxy(void)397 serial_getxy (void)
398 {
399 return (serial_x << 8) | serial_y;
400 }
401
402 void
serial_gotoxy(int x,int y)403 serial_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
413 void
serial_cls(void)414 serial_cls (void)
415 {
416 keep_track = 0;
417 ti_clear_screen ();
418 keep_track = 1;
419
420 serial_x = serial_y = 0;
421 }
422
423 void
serial_setcolorstate(color_state state)424 serial_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