2
* GRUB -- GRand Unified Bootloader
3
* Copyright (C) 2000,2001,2002,2003,2004,2005 Free Software Foundation, Inc.
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
#include <grub/machine/serial.h>
21
#include <grub/machine/console.h>
22
#include <grub/term.h>
23
#include <grub/types.h>
25
#include <grub/misc.h>
26
#include <grub/normal.h>
28
#include <grub/terminfo.h>
31
#define TEXT_HEIGHT 25
33
static unsigned int xpos, ypos;
34
static unsigned int keep_track = 1;
35
static unsigned int registered = 0;
37
/* An input buffer. */
38
static char input_buf[8];
39
static unsigned int npending = 0;
41
/* Argument options. */
42
static const struct grub_arg_option options[] =
44
{"unit", 'u', 0, "Set the serial unit", 0, ARG_TYPE_INT},
45
{"port", 'p', 0, "Set the serial port address", 0, ARG_TYPE_STRING},
46
{"speed", 's', 0, "Set the serial port speed", 0, ARG_TYPE_INT},
47
{"word", 'w', 0, "Set the serial port word length", 0, ARG_TYPE_INT},
48
{"parity", 'r', 0, "Set the serial port parity", 0, ARG_TYPE_STRING},
49
{"stop", 't', 0, "Set the serial port stop bits", 0, ARG_TYPE_INT},
53
/* Serial port settings. */
57
unsigned short divisor;
58
unsigned short word_len;
60
unsigned short stop_bits;
63
/* Serial port settings. */
64
static struct serial_port serial_settings;
66
/* Read a byte from a port. */
67
static inline unsigned char
68
inb (const unsigned short port)
72
asm volatile ("inb %w1, %0" : "=a" (value) : "Nd" (port));
73
asm volatile ("outb %%al, $0x80" : : );
78
/* Write a byte to a port. */
80
outb (const unsigned short port, const unsigned char value)
82
asm volatile ("outb %b0, %w1" : : "a" (value), "Nd" (port));
83
asm volatile ("outb %%al, $0x80" : : );
86
/* Return the port number for the UNITth serial device. */
87
static inline unsigned short
88
serial_hw_get_port (const unsigned short unit)
90
/* The BIOS data area. */
91
const unsigned short *addr = (const unsigned short *) 0x0400;
97
serial_hw_fetch (void)
99
if (inb (serial_settings.port + UART_LSR) & UART_DATA_READY)
100
return inb (serial_settings.port + UART_RX);
105
/* Put a chararacter. */
107
serial_hw_put (const int c)
109
unsigned int timeout = 100000;
111
/* Wait until the transmitter holding register is empty. */
112
while ((inb (serial_settings.port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0)
115
/* There is something wrong. But what can I do? */
119
outb (serial_settings.port + UART_TX, c);
123
serial_translate_key_sequence (void)
148
{('1' | ('~' << 8)), 1},
149
{('3' | ('~' << 8)), 4},
150
{('5' | ('~' << 8)), 7},
151
{('6' | ('~' << 8)), 3}
154
/* The buffer must start with "ESC [". */
155
if (*((unsigned short *) input_buf) != ('\e' | ('[' << 8)))
163
i < sizeof (three_code_table) / sizeof (three_code_table[0]);
165
if (three_code_table[i].key == input_buf[2])
167
input_buf[0] = three_code_table[i].ascii;
169
grub_memmove (input_buf + 1, input_buf + 3, npending - 1);
177
short key = *((short *) (input_buf + 2));
180
i < sizeof (four_code_table) / sizeof (four_code_table[0]);
182
if (four_code_table[i].key == key)
184
input_buf[0] = four_code_table[i].ascii;
186
grub_memmove (input_buf + 1, input_buf + 4, npending - 1);
193
fill_input_buf (const int nowait)
197
for (i = 0; i < 10000 && npending < sizeof (input_buf); i++)
201
c = serial_hw_fetch ();
204
input_buf[npending++] = c;
206
/* Reset the counter to zero, to wait for the same interval. */
214
/* Translate some key sequences. */
215
serial_translate_key_sequence ();
220
/* Convert speed to divisor. */
221
static unsigned short
222
serial_get_divisor (unsigned int speed)
226
/* The structure for speed vs. divisor. */
233
/* The table which lists common configurations. */
234
/* 1843200 / (speed * 16) */
235
static struct divisor divisor_tab[] =
246
/* Set the baud rate. */
247
for (i = 0; i < sizeof (divisor_tab) / sizeof (divisor_tab[0]); i++)
248
if (divisor_tab[i].speed == speed)
249
return divisor_tab[i].div;
253
/* The serial version of checkkey. */
255
grub_serial_checkkey (void)
257
if (fill_input_buf (1))
263
/* The serial version of getkey. */
265
grub_serial_getkey (void)
269
while (! fill_input_buf (0))
273
grub_memmove (input_buf, input_buf + 1, --npending);
278
/* Initialize a serial device. PORT is the port number for a serial device.
279
SPEED is a DTE-DTE speed which must be one of these: 2400, 4800, 9600,
280
19200, 38400, 57600 and 115200. WORD_LEN is the word length to be used
281
for the device. Likewise, PARITY is the type of the parity and
282
STOP_BIT_LEN is the length of the stop bit. The possible values for
283
WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as
286
serial_hw_init (void)
288
unsigned char status = 0;
290
/* Turn off the interupt. */
291
outb (serial_settings.port + UART_IER, 0);
294
outb (serial_settings.port + UART_LCR, UART_DLAB);
296
/* Set the baud rate. */
297
outb (serial_settings.port + UART_DLL, serial_settings.divisor & 0xFF);
298
outb (serial_settings.port + UART_DLH, serial_settings.divisor >> 8 );
300
/* Set the line status. */
301
status |= (serial_settings.parity
302
| serial_settings.word_len
303
| serial_settings.stop_bits);
304
outb (serial_settings.port + UART_LCR, status);
306
/* Enable the FIFO. */
307
outb (serial_settings.port + UART_FCR, UART_ENABLE_FIFO);
309
/* Turn on DTR, RTS, and OUT2. */
310
outb (serial_settings.port + UART_MCR, UART_ENABLE_MODEM);
312
/* Drain the input buffer. */
313
while (grub_serial_checkkey () != -1)
314
(void) grub_serial_getkey ();
316
/* FIXME: should check if the serial terminal was found. */
318
return GRUB_ERR_NONE;
321
/* The serial version of putchar. */
323
grub_serial_putchar (grub_uint32_t c)
325
/* Keep track of the cursor. */
328
/* The serial terminal does not have VGA fonts. */
331
/* Better than nothing. */
334
case GRUB_TERM_DISP_LEFT:
338
case GRUB_TERM_DISP_UP:
342
case GRUB_TERM_DISP_RIGHT:
346
case GRUB_TERM_DISP_DOWN:
350
case GRUB_TERM_DISP_HLINE:
354
case GRUB_TERM_DISP_VLINE:
358
case GRUB_TERM_DISP_UL:
359
case GRUB_TERM_DISP_UR:
360
case GRUB_TERM_DISP_LL:
361
case GRUB_TERM_DISP_LR:
383
if (ypos < TEXT_HEIGHT)
392
if (xpos >= TEXT_WIDTH)
406
grub_serial_getcharwidth (grub_uint32_t c __attribute__ ((unused)))
412
grub_serial_getwh (void)
414
return (TEXT_WIDTH << 8) | TEXT_HEIGHT;
418
grub_serial_getxy (void)
420
return ((xpos << 8) | ypos);
424
grub_serial_gotoxy (grub_uint8_t x, grub_uint8_t y)
426
if (x > TEXT_WIDTH || y > TEXT_HEIGHT)
428
grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid point (%u,%u)", x, y);
433
grub_terminfo_gotoxy (x, y);
442
grub_serial_cls (void)
445
grub_terminfo_cls ();
452
grub_serial_setcolorstate (const grub_term_color_state state)
457
case GRUB_TERM_COLOR_STANDARD:
458
case GRUB_TERM_COLOR_NORMAL:
459
grub_terminfo_reverse_video_off ();
461
case GRUB_TERM_COLOR_HIGHLIGHT:
462
grub_terminfo_reverse_video_on ();
471
grub_serial_setcolor (grub_uint8_t normal_color __attribute__ ((unused)),
472
grub_uint8_t highlight_color __attribute__ ((unused)))
478
grub_serial_setcursor (const int on)
481
grub_terminfo_cursor_on ();
483
grub_terminfo_cursor_off ();
486
static struct grub_term grub_serial_term =
491
.putchar = grub_serial_putchar,
492
.getcharwidth = grub_serial_getcharwidth,
493
.checkkey = grub_serial_checkkey,
494
.getkey = grub_serial_getkey,
495
.getwh = grub_serial_getwh,
496
.getxy = grub_serial_getxy,
497
.gotoxy = grub_serial_gotoxy,
498
.cls = grub_serial_cls,
499
.setcolorstate = grub_serial_setcolorstate,
500
.setcolor = grub_serial_setcolor,
501
.setcursor = grub_serial_setcursor,
509
grub_cmd_serial (struct grub_arg_list *state,
510
int argc __attribute__ ((unused)),
511
char **args __attribute__ ((unused)))
513
struct serial_port backup_settings = serial_settings;
514
grub_err_t hwiniterr;
519
arg = grub_strtoul (state[0].arg, 0, 0);
520
if (arg >= 0 && arg < 4)
521
serial_settings.port = serial_hw_get_port ((int) arg);
523
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad unit number.");
527
serial_settings.port = (unsigned short) grub_strtoul (state[1].arg, 0, 0);
533
speed = grub_strtoul (state[2].arg, 0, 0);
534
serial_settings.divisor = serial_get_divisor ((unsigned int) speed);
535
if (serial_settings.divisor == 0)
537
serial_settings = backup_settings;
538
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad speed");
544
if (! grub_strcmp (state[3].arg, "5"))
545
serial_settings.word_len = UART_5BITS_WORD;
546
else if (! grub_strcmp (state[3].arg, "6"))
547
serial_settings.word_len = UART_6BITS_WORD;
548
else if (! grub_strcmp (state[3].arg, "7"))
549
serial_settings.word_len = UART_7BITS_WORD;
550
else if (! grub_strcmp (state[3].arg, "8"))
551
serial_settings.word_len = UART_8BITS_WORD;
554
serial_settings = backup_settings;
555
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad word length");
561
if (! grub_strcmp (state[4].arg, "no"))
562
serial_settings.parity = UART_NO_PARITY;
563
else if (! grub_strcmp (state[4].arg, "odd"))
564
serial_settings.parity = UART_ODD_PARITY;
565
else if (! grub_strcmp (state[4].arg, "even"))
566
serial_settings.parity = UART_EVEN_PARITY;
569
serial_settings = backup_settings;
570
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad parity");
576
if (! grub_strcmp (state[5].arg, "1"))
577
serial_settings.stop_bits = UART_1_STOP_BIT;
578
else if (! grub_strcmp (state[5].arg, "2"))
579
serial_settings.stop_bits = UART_2_STOP_BITS;
582
serial_settings = backup_settings;
583
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad number of stop bits");
587
/* Initialize with new settings. */
588
hwiniterr = serial_hw_init ();
590
if (hwiniterr == GRUB_ERR_NONE)
592
/* Register terminal if not yet registered. */
595
grub_term_register (&grub_serial_term);
601
/* Initialization with new settings failed. */
604
/* If the terminal is registered, attempt to restore previous
606
serial_settings = backup_settings;
607
if (serial_hw_init () != GRUB_ERR_NONE)
609
/* If unable to restore settings, unregister terminal. */
610
grub_term_unregister (&grub_serial_term);
619
GRUB_MOD_INIT(serial)
621
(void) mod; /* To stop warning. */
622
grub_register_command ("serial", grub_cmd_serial, GRUB_COMMAND_FLAG_BOTH,
623
"serial [OPTIONS...]", "Configure serial port.", options);
624
/* Set default settings. */
625
serial_settings.port = serial_hw_get_port (0);
626
serial_settings.divisor = serial_get_divisor (9600);
627
serial_settings.word_len = UART_8BITS_WORD;
628
serial_settings.parity = UART_NO_PARITY;
629
serial_settings.stop_bits = UART_1_STOP_BIT;
632
GRUB_MOD_FINI(serial)
634
grub_unregister_command ("serial");
635
if (registered == 1) /* Unregister terminal only if registered. */
636
grub_term_unregister (&grub_serial_term);