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   if (port == 0)
146     return 0;
147 
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;
154 
155   /* Turn off the interrupt.  */
156   outb (port + UART_IER, 0);
157 
158   /* Set DLAB.  */
159   outb (port + UART_LCR, UART_DLAB);
160 
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)
164       {
165 	div = divisor_tab[i].div;
166 	break;
167       }
168 
169   if (div == 0)
170     return 0;
171 
172   outb (port + UART_DLL, div & 0xFF);
173   outb (port + UART_DLH, div >> 8);
174 
175   /* Set the line status.  */
176   status = parity | word_len | stop_bit_len;
177   outb (port + UART_LCR, status);
178 
179   /* Enable the FIFO.  */
180   outb (port + UART_FCR, UART_ENABLE_FIFO);
181 
182   /* Turn on DTR, RTS, and OUT2.  */
183   outb (port + UART_MCR, UART_ENABLE_MODEM);
184 
185   /* Store the port number.  */
186   serial_hw_port = port;
187 
188   /* Drain the input buffer.  */
189   while (serial_checkkey () != -1)
190     (void) serial_getkey ();
191 
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)
196       {
197 	term_table[i].flags &= ~TERM_NEED_INIT;
198       }
199 
200   return 1;
201 }
202 #endif /* ! GRUB_UTIL */
203 
204 
205 /* Generic definitions.  */
206 
207 static void
serial_translate_key_sequence(void)208 serial_translate_key_sequence (void)
209 {
210   const struct
211   {
212     char key;
213     char ascii;
214   }
215   three_code_table[] =
216     {
217       {'A', 16},
218       {'B', 14},
219       {'C', 6},
220       {'D', 2},
221       {'F', 5},
222       {'H', 1},
223       {'4', 4}
224     };
225 
226   const struct
227   {
228     short key;
229     char ascii;
230   }
231   four_code_table[] =
232     {
233       {('1' | ('~' << 8)), 1},
234       {('3' | ('~' << 8)), 4},
235       {('5' | ('~' << 8)), 7},
236       {('6' | ('~' << 8)), 3},
237     };
238 
239   /* The buffer must start with ``ESC [''.  */
240   if (*((unsigned short *) input_buf) != ('\e' | ('[' << 8)))
241     return;
242 
243   if (npending >= 3)
244     {
245       int i;
246 
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])
251 	  {
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;
256 	  }
257     }
258 
259   if (npending >= 4)
260     {
261       int i;
262       short key = *((short *) (input_buf + 2));
263 
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)
268 	  {
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;
273 	  }
274     }
275 }
276 
277 static
fill_input_buf(int nowait)278 int fill_input_buf (int nowait)
279 {
280   int i;
281 
282   for (i = 0; i < 10000 && npending < sizeof (input_buf); i++)
283     {
284       int c;
285 
286       c = serial_hw_fetch ();
287       if (c >= 0)
288 	{
289 	  input_buf[npending++] = c;
290 
291 	  /* Reset the counter to zero, to wait for the same interval.  */
292 	  i = 0;
293 	}
294 
295       if (nowait)
296 	break;
297     }
298 
299   /* Translate some key sequences.  */
300   serial_translate_key_sequence ();
301 
302   return npending;
303 }
304 
305 /* The serial version of getkey.  */
306 int
serial_getkey(void)307 serial_getkey (void)
308 {
309   int c;
310 
311   while (! fill_input_buf (0))
312     ;
313 
314   c = input_buf[0];
315   npending--;
316   grub_memmove (input_buf, input_buf + 1, npending);
317 
318   return c;
319 }
320 
321 /* The serial version of checkkey.  */
322 int
serial_checkkey(void)323 serial_checkkey (void)
324 {
325   if (fill_input_buf (1))
326     return input_buf[0];
327 
328   return -1;
329 }
330 
331 /* The serial version of grub_putchar.  */
332 void
serial_putchar(int c)333 serial_putchar (int c)
334 {
335   /* Keep track of the cursor.  */
336   if (keep_track)
337     {
338       /* The serial terminal doesn't have VGA fonts.  */
339       switch (c)
340 	{
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;
373 	}
374 
375       switch (c)
376 	{
377 	case '\r':
378 	  serial_x = 0;
379 	  break;
380 
381 	case '\n':
382 	  serial_y++;
383 	  break;
384 
385 	case '\b':
386 	case 127:
387 	  if (serial_x > 0)
388 	    serial_x--;
389 	  break;
390 
391 	case '\a':
392 	  break;
393 
394 	default:
395 	  if (serial_x >= 79)
396 	    {
397 	      serial_putchar ('\r');
398 	      serial_putchar ('\n');
399 	    }
400 	  serial_x++;
401 	  break;
402 	}
403     }
404 
405   serial_hw_put (c);
406 }
407 
408 int
serial_getxy(void)409 serial_getxy (void)
410 {
411   return (serial_x << 8) | serial_y;
412 }
413 
414 void
serial_gotoxy(int x,int y)415 serial_gotoxy (int x, int y)
416 {
417   int saved_cbs = composite_bitset;
418 
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;
424 
425   serial_x = x;
426   serial_y = y;
427 }
428 
429 void
serial_cls(void)430 serial_cls (void)
431 {
432   int saved_cbs = composite_bitset;
433 
434   keep_track = 0;
435   composite_bitset &= ~COMP_BS_BIOS;
436   ti_clear_screen ();
437   composite_bitset = saved_cbs;
438   keep_track = 1;
439 
440   serial_x = serial_y = 0;
441 }
442 
443 void
serial_setcolorstate(color_state state)444 serial_setcolorstate (color_state state)
445 {
446   int saved_cbs = composite_bitset;
447 
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;
456 }
457 
458 void
composite_putchar(int c)459 composite_putchar (int c)
460 {
461   if (composite_bitset & COMP_BS_SERIAL)
462     serial_putchar (c);
463   if (composite_bitset & COMP_BS_BIOS)
464     console_putchar (c);
465 }
466 
467 int
composite_getkey(void)468 composite_getkey (void)
469 {
470   for (;;) {
471     if (serial_checkkey () != -1)
472       return (serial_getkey ());
473     if (console_checkkey () != -1)
474       return (console_getkey ());
475   }
476 }
477 
478 int
composite_checkkey(void)479 composite_checkkey (void)
480 {
481   int ch;
482 
483   if ((ch = serial_checkkey ()) != -1)
484     return (ch);
485   return (console_checkkey ());
486 }
487 
488 void
composite_gotoxy(int x,int y)489 composite_gotoxy (int x, int y)
490 {
491   serial_gotoxy (x, y);
492   console_gotoxy (x, y);
493 }
494 
495 void
composite_cls(void)496 composite_cls (void)
497 {
498   serial_cls();
499   console_cls();
500 }
501 
502 void
composite_setcolorstate(color_state state)503 composite_setcolorstate (color_state state)
504 {
505   serial_setcolorstate (state);
506   console_setcolorstate (state);
507 }
508 
509 #endif /* SUPPORT_SERIAL */
510