xref: /illumos-gate/usr/src/grub/grub-0.97/stage2/serial.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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
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
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
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
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
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
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
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
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
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
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
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
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
397 serial_getxy (void)
398 {
399   return (serial_x << 8) | serial_y;
400 }
401 
402 void
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
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
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