Home | History | Annotate | Download | only in stage2
      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.  */
     40 
     41 #ifndef GRUB_UTIL
     42 /* The structure for speed vs. divisor.  */
     43 struct divisor
     44 {
     45   int speed;
     46   unsigned short div;
     47 };
     48 
     49 /* Store the port number of a serial unit.  */
     50 static unsigned short serial_hw_port = 0;
     51 
     52 /* The table which lists common configurations.  */
     53 static struct divisor divisor_tab[] =
     54   {
     55     { 2400,   0x0030 },
     56     { 4800,   0x0018 },
     57     { 9600,   0x000C },
     58     { 19200,  0x0006 },
     59     { 38400,  0x0003 },
     60     { 57600,  0x0002 },
     61     { 115200, 0x0001 }
     62   };
     63 
     64 /* Read a byte from a port.  */
     65 static inline unsigned char
     66 inb (unsigned short port)
     67 {
     68   unsigned char value;
     69 
     70   asm volatile ("inb	%w1, %0" : "=a" (value) : "Nd" (port));
     71   asm volatile ("outb	%%al, $0x80" : : );
     72 
     73   return value;
     74 }
     75 
     76 /* Write a byte to a port.  */
     77 static inline void
     78 outb (unsigned short port, unsigned char value)
     79 {
     80   asm volatile ("outb	%b0, %w1" : : "a" (value), "Nd" (port));
     81   asm volatile ("outb	%%al, $0x80" : : );
     82 }
     83 
     84 /* Fetch a key.  */
     85 int
     86 serial_hw_fetch (void)
     87 {
     88   if (inb (serial_hw_port + UART_LSR) & UART_DATA_READY)
     89     return inb (serial_hw_port + UART_RX);
     90 
     91   return -1;
     92 }
     93 
     94 /* Put a chararacter.  */
     95 void
     96 serial_hw_put (int c)
     97 {
     98   int timeout = 100000;
     99 
    100   /* Wait until the transmitter holding register is empty.  */
    101   while ((inb (serial_hw_port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0)
    102     {
    103       if (--timeout == 0)
    104 	/* There is something wrong. But what can I do?  */
    105 	return;
    106     }
    107 
    108   outb (serial_hw_port + UART_TX, c);
    109 }
    110 
    111 void
    112 serial_hw_delay (void)
    113 {
    114   outb (0x80, 0);
    115 }
    116 
    117 /* Return the port number for the UNITth serial device.  */
    118 unsigned short
    119 serial_hw_get_port (int unit)
    120 {
    121   /* The BIOS data area.  */
    122   const unsigned short *addr = (const unsigned short *) 0x0400;
    123 
    124   return addr[unit];
    125 }
    126 
    127 /* Initialize a serial device. PORT is the port number for a serial device.
    128    SPEED is a DTE-DTE speed which must be one of these: 2400, 4800, 9600,
    129    19200, 38400, 57600 and 115200. WORD_LEN is the word length to be used
    130    for the device. Likewise, PARITY is the type of the parity and
    131    STOP_BIT_LEN is the length of the stop bit. The possible values for
    132    WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as
    133    macros.  */
    134 int
    135 serial_hw_init (unsigned short port, unsigned int speed,
    136 		int word_len, int parity, int stop_bit_len)
    137 {
    138   int i;
    139   unsigned short div = 0;
    140   unsigned char status = 0;
    141 
    142   /* Turn off the interrupt.  */
    143   outb (port + UART_IER, 0);
    144 
    145   /* Set DLAB.  */
    146   outb (port + UART_LCR, UART_DLAB);
    147 
    148   /* Set the baud rate.  */
    149   for (i = 0; i < sizeof (divisor_tab) / sizeof (divisor_tab[0]); i++)
    150     if (divisor_tab[i].speed == speed)
    151       {
    152 	div = divisor_tab[i].div;
    153 	break;
    154       }
    155 
    156   if (div == 0)
    157     return 0;
    158 
    159   outb (port + UART_DLL, div & 0xFF);
    160   outb (port + UART_DLH, div >> 8);
    161 
    162   /* Set the line status.  */
    163   status |= parity | word_len | stop_bit_len;
    164   outb (port + UART_LCR, status);
    165 
    166   /* Enable the FIFO.  */
    167   outb (port + UART_FCR, UART_ENABLE_FIFO);
    168 
    169   /* Turn on DTR, RTS, and OUT2.  */
    170   outb (port + UART_MCR, UART_ENABLE_MODEM);
    171 
    172   /* Store the port number.  */
    173   serial_hw_port = port;
    174 
    175   /* Drain the input buffer.  */
    176   while (serial_checkkey () != -1)
    177     (void) serial_getkey ();
    178 
    179   /* Get rid of TERM_NEED_INIT from the serial terminal.  */
    180   for (i = 0; term_table[i].name; i++)
    181     if (grub_strcmp (term_table[i].name, "serial") == 0)
    182       {
    183 	term_table[i].flags &= ~TERM_NEED_INIT;
    184 	break;
    185       }
    186 
    187   /* FIXME: should check if the serial terminal was found.  */
    188 
    189   return 1;
    190 }
    191 #endif /* ! GRUB_UTIL */
    192 
    193 
    194 /* Generic definitions.  */
    196 
    197 static void
    198 serial_translate_key_sequence (void)
    199 {
    200   const struct
    201   {
    202     char key;
    203     char ascii;
    204   }
    205   three_code_table[] =
    206     {
    207       {'A', 16},
    208       {'B', 14},
    209       {'C', 6},
    210       {'D', 2},
    211       {'F', 5},
    212       {'H', 1},
    213       {'4', 4}
    214     };
    215 
    216   const struct
    217   {
    218     short key;
    219     char ascii;
    220   }
    221   four_code_table[] =
    222     {
    223       {('1' | ('~' << 8)), 1},
    224       {('3' | ('~' << 8)), 4},
    225       {('5' | ('~' << 8)), 7},
    226       {('6' | ('~' << 8)), 3},
    227     };
    228 
    229   /* The buffer must start with ``ESC [''.  */
    230   if (*((unsigned short *) input_buf) != ('\e' | ('[' << 8)))
    231     return;
    232 
    233   if (npending >= 3)
    234     {
    235       int i;
    236 
    237       for (i = 0;
    238 	   i < sizeof (three_code_table) / sizeof (three_code_table[0]);
    239 	   i++)
    240 	if (three_code_table[i].key == input_buf[2])
    241 	  {
    242 	    input_buf[0] = three_code_table[i].ascii;
    243 	    npending -= 2;
    244 	    grub_memmove (input_buf + 1, input_buf + 3, npending - 1);
    245 	    return;
    246 	  }
    247     }
    248 
    249   if (npending >= 4)
    250     {
    251       int i;
    252       short key = *((short *) (input_buf + 2));
    253 
    254       for (i = 0;
    255 	   i < sizeof (four_code_table) / sizeof (four_code_table[0]);
    256 	   i++)
    257 	if (four_code_table[i].key == key)
    258 	  {
    259 	    input_buf[0] = four_code_table[i].ascii;
    260 	    npending -= 3;
    261 	    grub_memmove (input_buf + 1, input_buf + 4, npending - 1);
    262 	    return;
    263 	  }
    264     }
    265 }
    266 
    267 static
    268 int fill_input_buf (int nowait)
    269 {
    270   int i;
    271 
    272   for (i = 0; i < 10000 && npending < sizeof (input_buf); i++)
    273     {
    274       int c;
    275 
    276       c = serial_hw_fetch ();
    277       if (c >= 0)
    278 	{
    279 	  input_buf[npending++] = c;
    280 
    281 	  /* Reset the counter to zero, to wait for the same interval.  */
    282 	  i = 0;
    283 	}
    284 
    285       if (nowait)
    286 	break;
    287     }
    288 
    289   /* Translate some key sequences.  */
    290   serial_translate_key_sequence ();
    291 
    292   return npending;
    293 }
    294 
    295 /* The serial version of getkey.  */
    296 int
    297 serial_getkey (void)
    298 {
    299   int c;
    300 
    301   while (! fill_input_buf (0))
    302     ;
    303 
    304   c = input_buf[0];
    305   npending--;
    306   grub_memmove (input_buf, input_buf + 1, npending);
    307 
    308   return c;
    309 }
    310 
    311 /* The serial version of checkkey.  */
    312 int
    313 serial_checkkey (void)
    314 {
    315   if (fill_input_buf (1))
    316     return input_buf[0];
    317 
    318   return -1;
    319 }
    320 
    321 /* The serial version of grub_putchar.  */
    322 void
    323 serial_putchar (int c)
    324 {
    325   /* Keep track of the cursor.  */
    326   if (keep_track)
    327     {
    328       /* The serial terminal doesn't have VGA fonts.  */
    329       switch (c)
    330 	{
    331 	case DISP_UL:
    332 	  c = ACS_ULCORNER;
    333 	  break;
    334 	case DISP_UR:
    335 	  c = ACS_URCORNER;
    336 	  break;
    337 	case DISP_LL:
    338 	  c = ACS_LLCORNER;
    339 	  break;
    340 	case DISP_LR:
    341 	  c = ACS_LRCORNER;
    342 	  break;
    343 	case DISP_HORIZ:
    344 	  c = ACS_HLINE;
    345 	  break;
    346 	case DISP_VERT:
    347 	  c = ACS_VLINE;
    348 	  break;
    349 	case DISP_LEFT:
    350 	  c = ACS_LARROW;
    351 	  break;
    352 	case DISP_RIGHT:
    353 	  c = ACS_RARROW;
    354 	  break;
    355 	case DISP_UP:
    356 	  c = ACS_UARROW;
    357 	  break;
    358 	case DISP_DOWN:
    359 	  c = ACS_DARROW;
    360 	  break;
    361 	default:
    362 	  break;
    363 	}
    364 
    365       switch (c)
    366 	{
    367 	case '\r':
    368 	  serial_x = 0;
    369 	  break;
    370 
    371 	case '\n':
    372 	  serial_y++;
    373 	  break;
    374 
    375 	case '\b':
    376 	case 127:
    377 	  if (serial_x > 0)
    378 	    serial_x--;
    379 	  break;
    380 
    381 	case '\a':
    382 	  break;
    383 
    384 	default:
    385 	  if (serial_x >= 79)
    386 	    {
    387 	      serial_putchar ('\r');
    388 	      serial_putchar ('\n');
    389 	    }
    390 	  serial_x++;
    391 	  break;
    392 	}
    393     }
    394 
    395   serial_hw_put (c);
    396 }
    397 
    398 int
    399 serial_getxy (void)
    400 {
    401   return (serial_x << 8) | serial_y;
    402 }
    403 
    404 void
    405 serial_gotoxy (int x, int y)
    406 {
    407   keep_track = 0;
    408   ti_cursor_address (x, y);
    409   keep_track = 1;
    410 
    411   serial_x = x;
    412   serial_y = y;
    413 }
    414 
    415 void
    416 serial_cls (void)
    417 {
    418   keep_track = 0;
    419   ti_clear_screen ();
    420   keep_track = 1;
    421 
    422   serial_x = serial_y = 0;
    423 }
    424 
    425 void
    426 serial_setcolorstate (color_state state)
    427 {
    428   keep_track = 0;
    429   if (state == COLOR_STATE_HIGHLIGHT)
    430     ti_enter_standout_mode ();
    431   else
    432     ti_exit_standout_mode ();
    433   keep_track = 1;
    434 }
    435 
    436 #endif /* SUPPORT_SERIAL */
    437