2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc.
5 * GRUB 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 3 of the License, or
8 * (at your option) any later version.
10 * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
19 #include <grub/machine/memory.h>
20 #include <grub/serial.h>
21 #include <grub/term.h>
22 #include <grub/types.h>
24 #include <grub/misc.h>
25 #include <grub/terminfo.h>
26 #include <grub/cpu/io.h>
27 #include <grub/extcmd.h>
28 #include <grub/i18n.h>
31 #define TEXT_HEIGHT 24
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 static struct grub_term_output grub_serial_term_output
;
43 /* Argument options. */
44 static const struct grub_arg_option options
[] =
46 {"unit", 'u', 0, N_("Set the serial unit."), 0, ARG_TYPE_INT
},
47 {"port", 'p', 0, N_("Set the serial port address."), 0, ARG_TYPE_STRING
},
48 {"speed", 's', 0, N_("Set the serial port speed."), 0, ARG_TYPE_INT
},
49 {"word", 'w', 0, N_("Set the serial port word length."), 0, ARG_TYPE_INT
},
50 {"parity", 'r', 0, N_("Set the serial port parity."), 0, ARG_TYPE_STRING
},
51 {"stop", 't', 0, N_("Set the serial port stop bits."), 0, ARG_TYPE_INT
},
55 /* Serial port settings. */
59 unsigned short divisor
;
60 unsigned short word_len
;
62 unsigned short stop_bits
;
65 /* Serial port settings. */
66 static struct serial_port serial_settings
;
68 #ifdef GRUB_MACHINE_PCBIOS
69 static const unsigned short *serial_hw_io_addr
= (const unsigned short *) GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR
;
70 #define GRUB_SERIAL_PORT_NUM 4
72 #include <grub/machine/serial.h>
73 static const grub_port_t serial_hw_io_addr
[] = GRUB_MACHINE_SERIAL_PORTS
;
74 #define GRUB_SERIAL_PORT_NUM (ARRAY_SIZE(serial_hw_io_addr))
77 /* Return the port number for the UNITth serial device. */
78 static inline grub_port_t
79 serial_hw_get_port (const unsigned int unit
)
81 if (unit
< GRUB_SERIAL_PORT_NUM
)
82 return serial_hw_io_addr
[unit
];
89 serial_hw_fetch (void)
91 if (grub_inb (serial_settings
.port
+ UART_LSR
) & UART_DATA_READY
)
92 return grub_inb (serial_settings
.port
+ UART_RX
);
97 /* Put a character. */
99 serial_hw_put (const int c
)
101 unsigned int timeout
= 100000;
103 /* Wait until the transmitter holding register is empty. */
104 while ((grub_inb (serial_settings
.port
+ UART_LSR
) & UART_EMPTY_TRANSMITTER
) == 0)
107 /* There is something wrong. But what can I do? */
111 grub_outb (c
, serial_settings
.port
+ UART_TX
);
115 serial_translate_key_sequence (void)
141 {('1' | ('~' << 8)), 1},
142 {('3' | ('~' << 8)), 4},
143 {('5' | ('~' << 8)), 7},
144 {('6' | ('~' << 8)), 3}
150 /* The buffer must start with "ESC [". */
151 if (input_buf
[0] != '\e' || input_buf
[1] != '[')
154 for (i
= 0; i
< ARRAY_SIZE (three_code_table
); i
++)
155 if (three_code_table
[i
].key
== input_buf
[2])
157 input_buf
[0] = three_code_table
[i
].ascii
;
159 grub_memmove (input_buf
+ 1, input_buf
+ 3, npending
- 1);
165 short key
= input_buf
[3] | (input_buf
[4] << 8);
167 for (i
= 0; i
< ARRAY_SIZE (four_code_table
); i
++)
168 if (four_code_table
[i
].key
== key
)
170 input_buf
[0] = four_code_table
[i
].ascii
;
172 grub_memmove (input_buf
+ 1, input_buf
+ 4, npending
- 1);
179 fill_input_buf (const int nowait
)
183 for (i
= 0; i
< 10000 && npending
< sizeof (input_buf
); i
++)
187 c
= serial_hw_fetch ();
190 input_buf
[npending
++] = c
;
192 /* Reset the counter to zero, to wait for the same interval. */
200 /* Translate some key sequences. */
201 serial_translate_key_sequence ();
206 /* Convert speed to divisor. */
207 static unsigned short
208 serial_get_divisor (unsigned int speed
)
212 /* The structure for speed vs. divisor. */
219 /* The table which lists common configurations. */
220 /* 1843200 / (speed * 16) */
221 static struct divisor divisor_tab
[] =
232 /* Set the baud rate. */
233 for (i
= 0; i
< sizeof (divisor_tab
) / sizeof (divisor_tab
[0]); i
++)
234 if (divisor_tab
[i
].speed
== speed
)
235 return divisor_tab
[i
].div
;
239 /* The serial version of checkkey. */
241 grub_serial_checkkey (void)
243 if (fill_input_buf (1))
249 /* The serial version of getkey. */
251 grub_serial_getkey (void)
255 while (! fill_input_buf (0))
260 c
= GRUB_TERM_BACKSPACE
;
262 grub_memmove (input_buf
, input_buf
+ 1, --npending
);
267 /* Initialize a serial device. PORT is the port number for a serial device.
268 SPEED is a DTE-DTE speed which must be one of these: 2400, 4800, 9600,
269 19200, 38400, 57600 and 115200. WORD_LEN is the word length to be used
270 for the device. Likewise, PARITY is the type of the parity and
271 STOP_BIT_LEN is the length of the stop bit. The possible values for
272 WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as
275 serial_hw_init (void)
277 unsigned char status
= 0;
279 /* Turn off the interrupt. */
280 grub_outb (0, serial_settings
.port
+ UART_IER
);
283 grub_outb (UART_DLAB
, serial_settings
.port
+ UART_LCR
);
285 /* Set the baud rate. */
286 grub_outb (serial_settings
.divisor
& 0xFF, serial_settings
.port
+ UART_DLL
);
287 grub_outb (serial_settings
.divisor
>> 8, serial_settings
.port
+ UART_DLH
);
289 /* Set the line status. */
290 status
|= (serial_settings
.parity
291 | serial_settings
.word_len
292 | serial_settings
.stop_bits
);
293 grub_outb (status
, serial_settings
.port
+ UART_LCR
);
295 /* Enable the FIFO. */
296 grub_outb (UART_ENABLE_FIFO
, serial_settings
.port
+ UART_FCR
);
298 /* Turn on DTR, RTS, and OUT2. */
299 grub_outb (UART_ENABLE_MODEM
, serial_settings
.port
+ UART_MCR
);
301 /* Drain the input buffer. */
302 while (grub_serial_checkkey () != -1)
303 (void) grub_serial_getkey ();
305 /* FIXME: should check if the serial terminal was found. */
307 return GRUB_ERR_NONE
;
310 /* The serial version of putchar. */
312 grub_serial_putchar (grub_uint32_t c
)
314 /* Keep track of the cursor. */
317 /* The serial terminal does not have VGA fonts. */
320 /* Better than nothing. */
323 case GRUB_TERM_DISP_LEFT
:
327 case GRUB_TERM_DISP_UP
:
331 case GRUB_TERM_DISP_RIGHT
:
335 case GRUB_TERM_DISP_DOWN
:
339 case GRUB_TERM_DISP_HLINE
:
343 case GRUB_TERM_DISP_VLINE
:
347 case GRUB_TERM_DISP_UL
:
348 case GRUB_TERM_DISP_UR
:
349 case GRUB_TERM_DISP_LL
:
350 case GRUB_TERM_DISP_LR
:
372 if (ypos
< TEXT_HEIGHT
- 1)
381 if (xpos
>= TEXT_WIDTH
)
395 grub_serial_getcharwidth (grub_uint32_t c
__attribute__ ((unused
)))
401 grub_serial_getwh (void)
403 return (TEXT_WIDTH
<< 8) | TEXT_HEIGHT
;
407 grub_serial_getxy (void)
409 return ((xpos
<< 8) | ypos
);
413 grub_serial_gotoxy (grub_uint8_t x
, grub_uint8_t y
)
415 if (x
> TEXT_WIDTH
|| y
> TEXT_HEIGHT
)
417 grub_error (GRUB_ERR_OUT_OF_RANGE
, "invalid point (%u,%u)", x
, y
);
422 grub_terminfo_gotoxy (x
, y
, &grub_serial_term_output
);
431 grub_serial_cls (void)
434 grub_terminfo_cls (&grub_serial_term_output
);
441 grub_serial_setcolorstate (const grub_term_color_state state
)
446 case GRUB_TERM_COLOR_STANDARD
:
447 case GRUB_TERM_COLOR_NORMAL
:
448 grub_terminfo_reverse_video_off (&grub_serial_term_output
);
450 case GRUB_TERM_COLOR_HIGHLIGHT
:
451 grub_terminfo_reverse_video_on (&grub_serial_term_output
);
460 grub_serial_setcursor (const int on
)
463 grub_terminfo_cursor_on (&grub_serial_term_output
);
465 grub_terminfo_cursor_off (&grub_serial_term_output
);
468 static struct grub_term_input grub_serial_term_input
=
471 .checkkey
= grub_serial_checkkey
,
472 .getkey
= grub_serial_getkey
,
475 static struct grub_term_output grub_serial_term_output
=
478 .putchar
= grub_serial_putchar
,
479 .getcharwidth
= grub_serial_getcharwidth
,
480 .getwh
= grub_serial_getwh
,
481 .getxy
= grub_serial_getxy
,
482 .gotoxy
= grub_serial_gotoxy
,
483 .cls
= grub_serial_cls
,
484 .setcolorstate
= grub_serial_setcolorstate
,
485 .setcursor
= grub_serial_setcursor
,
492 grub_cmd_serial (grub_extcmd_t cmd
,
493 int argc
__attribute__ ((unused
)),
494 char **args
__attribute__ ((unused
)))
496 struct grub_arg_list
*state
= cmd
->state
;
497 struct serial_port backup_settings
= serial_settings
;
498 grub_err_t hwiniterr
;
504 unit
= grub_strtoul (state
[0].arg
, 0, 0);
505 serial_settings
.port
= serial_hw_get_port (unit
);
506 if (!serial_settings
.port
)
507 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "bad unit number");
511 serial_settings
.port
= (grub_port_t
) grub_strtoul (state
[1].arg
, 0, 0);
517 speed
= grub_strtoul (state
[2].arg
, 0, 0);
518 serial_settings
.divisor
= serial_get_divisor ((unsigned int) speed
);
519 if (serial_settings
.divisor
== 0)
521 serial_settings
= backup_settings
;
522 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "bad speed");
528 if (! grub_strcmp (state
[3].arg
, "5"))
529 serial_settings
.word_len
= UART_5BITS_WORD
;
530 else if (! grub_strcmp (state
[3].arg
, "6"))
531 serial_settings
.word_len
= UART_6BITS_WORD
;
532 else if (! grub_strcmp (state
[3].arg
, "7"))
533 serial_settings
.word_len
= UART_7BITS_WORD
;
534 else if (! grub_strcmp (state
[3].arg
, "8"))
535 serial_settings
.word_len
= UART_8BITS_WORD
;
538 serial_settings
= backup_settings
;
539 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "bad word length");
545 if (! grub_strcmp (state
[4].arg
, "no"))
546 serial_settings
.parity
= UART_NO_PARITY
;
547 else if (! grub_strcmp (state
[4].arg
, "odd"))
548 serial_settings
.parity
= UART_ODD_PARITY
;
549 else if (! grub_strcmp (state
[4].arg
, "even"))
550 serial_settings
.parity
= UART_EVEN_PARITY
;
553 serial_settings
= backup_settings
;
554 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "bad parity");
560 if (! grub_strcmp (state
[5].arg
, "1"))
561 serial_settings
.stop_bits
= UART_1_STOP_BIT
;
562 else if (! grub_strcmp (state
[5].arg
, "2"))
563 serial_settings
.stop_bits
= UART_2_STOP_BITS
;
566 serial_settings
= backup_settings
;
567 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "bad number of stop bits");
571 /* Initialize with new settings. */
572 hwiniterr
= serial_hw_init ();
574 if (hwiniterr
== GRUB_ERR_NONE
)
576 /* Register terminal if not yet registered. */
579 grub_term_register_input ("serial", &grub_serial_term_input
);
580 grub_term_register_output ("serial", &grub_serial_term_output
);
586 /* Initialization with new settings failed. */
589 /* If the terminal is registered, attempt to restore previous
591 serial_settings
= backup_settings
;
592 if (serial_hw_init () != GRUB_ERR_NONE
)
594 /* If unable to restore settings, unregister terminal. */
595 grub_term_unregister_input (&grub_serial_term_input
);
596 grub_term_unregister_output (&grub_serial_term_output
);
605 static grub_extcmd_t cmd
;
607 GRUB_MOD_INIT(serial
)
609 cmd
= grub_register_extcmd ("serial", grub_cmd_serial
,
610 GRUB_COMMAND_FLAG_BOTH
,
611 "serial [OPTIONS...]",
612 "Configure serial port.", options
);
614 /* Set default settings. */
615 serial_settings
.port
= serial_hw_get_port (0);
616 serial_settings
.divisor
= serial_get_divisor (9600);
617 serial_settings
.word_len
= UART_8BITS_WORD
;
618 serial_settings
.parity
= UART_NO_PARITY
;
619 serial_settings
.stop_bits
= UART_1_STOP_BIT
;
622 GRUB_MOD_FINI(serial
)
624 grub_unregister_extcmd (cmd
);
625 if (registered
== 1) /* Unregister terminal only if registered. */
627 grub_term_unregister_input (&grub_serial_term_input
);
628 grub_term_unregister_output (&grub_serial_term_output
);