]> git.proxmox.com Git - grub2.git/commitdiff
split serial
authorphcoder <phcoder@debian.bg45.phnet>
Sat, 10 Oct 2009 18:59:18 +0000 (20:59 +0200)
committerphcoder <phcoder@debian.bg45.phnet>
Sat, 10 Oct 2009 18:59:18 +0000 (20:59 +0200)
conf/i386-coreboot.rmk
conf/i386-ieee1275.rmk
conf/i386-pc.rmk
include/grub/i386/io.h
include/grub/i386/pc/serial.h [deleted file]
include/grub/serial.h [new file with mode: 0644]
term/i386/pc/serial.c [deleted file]
term/serial.c [new file with mode: 0644]

index 09ec7787c4ebdbcd412288d881070009a135062e..59a9ed607760971506f1ab8c80327dd9b41fad2f 100644 (file)
@@ -187,7 +187,7 @@ halt_mod_CFLAGS = $(COMMON_CFLAGS)
 halt_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
 # For serial.mod.
-serial_mod_SOURCES = term/i386/pc/serial.c
+serial_mod_SOURCES = term/serial.c
 serial_mod_CFLAGS = $(COMMON_CFLAGS)
 serial_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
index 4b640de499d59cbfaaf5f7d33efa4144386570a6..48c3ce9d94f08198ce3f4ea05b621aeb4bfa33f8 100644 (file)
@@ -156,7 +156,7 @@ halt_mod_CFLAGS = $(COMMON_CFLAGS)
 halt_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
 # For serial.mod.
-serial_mod_SOURCES = term/i386/pc/serial.c
+serial_mod_SOURCES = term/serial.c
 serial_mod_CFLAGS = $(COMMON_CFLAGS)
 serial_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
index bf8fbfb9d765c334acabae0b5e7404ecd7fd0852..c4a4e7754ec68313c07828e60fb0b9ee00ca5836 100644 (file)
@@ -260,7 +260,7 @@ halt_mod_CFLAGS = $(COMMON_CFLAGS)
 halt_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
 # For serial.mod.
-serial_mod_SOURCES = term/i386/pc/serial.c
+serial_mod_SOURCES = term/serial.c
 serial_mod_CFLAGS = $(COMMON_CFLAGS)
 serial_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
index 0e567766b958981452d8f97392cf41e7dbb9e6e5..ae12a3e3dc12976f351069858f9f27f56d19df3d 100644 (file)
@@ -21,6 +21,8 @@
 #ifndef        GRUB_IO_H
 #define        GRUB_IO_H       1
 
+typedef unsigned short int grub_port_t;
+
 static __inline unsigned char
 grub_inb (unsigned short int port)
 {
diff --git a/include/grub/i386/pc/serial.h b/include/grub/i386/pc/serial.h
deleted file mode 100644 (file)
index 0632ff7..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/* serial.h - serial device interface */
-/*
- *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2000,2001,2002,2005,2007  Free Software Foundation, Inc.
- *
- *  GRUB is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation, either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  GRUB is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef GRUB_SERIAL_MACHINE_HEADER
-#define GRUB_SERIAL_MACHINE_HEADER     1
-
-/* Macros.  */
-
-/* The offsets of UART registers.  */
-#define UART_TX                0
-#define UART_RX                0
-#define UART_DLL       0
-#define UART_IER       1
-#define UART_DLH       1
-#define UART_IIR       2
-#define UART_FCR       2
-#define UART_LCR       3
-#define UART_MCR       4
-#define UART_LSR       5
-#define UART_MSR       6
-#define UART_SR                7
-
-/* For LSR bits.  */
-#define UART_DATA_READY                0x01
-#define UART_EMPTY_TRANSMITTER 0x20
-
-/* The type of parity.  */
-#define UART_NO_PARITY         0x00
-#define UART_ODD_PARITY                0x08
-#define UART_EVEN_PARITY       0x18
-
-/* The type of word length.  */
-#define UART_5BITS_WORD        0x00
-#define UART_6BITS_WORD        0x01
-#define UART_7BITS_WORD        0x02
-#define UART_8BITS_WORD        0x03
-
-/* The type of the length of stop bit.  */
-#define UART_1_STOP_BIT                0x00
-#define UART_2_STOP_BITS       0x04
-
-/* the switch of DLAB.  */
-#define UART_DLAB      0x80
-
-/* Enable the FIFO.  */
-#define UART_ENABLE_FIFO       0xC7
-
-/* Turn on DTR, RTS, and OUT2.  */
-#define UART_ENABLE_MODEM      0x0B
-
-#endif /* ! GRUB_SERIAL_MACHINE_HEADER */
diff --git a/include/grub/serial.h b/include/grub/serial.h
new file mode 100644 (file)
index 0000000..1c35b40
--- /dev/null
@@ -0,0 +1,67 @@
+/* serial.h - serial device interface */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2000,2001,2002,2005,2007  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_SERIAL_HEADER
+#define GRUB_SERIAL_HEADER     1
+
+/* Macros.  */
+
+/* The offsets of UART registers.  */
+#define UART_TX                0
+#define UART_RX                0
+#define UART_DLL       0
+#define UART_IER       1
+#define UART_DLH       1
+#define UART_IIR       2
+#define UART_FCR       2
+#define UART_LCR       3
+#define UART_MCR       4
+#define UART_LSR       5
+#define UART_MSR       6
+#define UART_SR                7
+
+/* For LSR bits.  */
+#define UART_DATA_READY                0x01
+#define UART_EMPTY_TRANSMITTER 0x20
+
+/* The type of parity.  */
+#define UART_NO_PARITY         0x00
+#define UART_ODD_PARITY                0x08
+#define UART_EVEN_PARITY       0x18
+
+/* The type of word length.  */
+#define UART_5BITS_WORD        0x00
+#define UART_6BITS_WORD        0x01
+#define UART_7BITS_WORD        0x02
+#define UART_8BITS_WORD        0x03
+
+/* The type of the length of stop bit.  */
+#define UART_1_STOP_BIT                0x00
+#define UART_2_STOP_BITS       0x04
+
+/* the switch of DLAB.  */
+#define UART_DLAB      0x80
+
+/* Enable the FIFO.  */
+#define UART_ENABLE_FIFO       0xC7
+
+/* Turn on DTR, RTS, and OUT2.  */
+#define UART_ENABLE_MODEM      0x0B
+
+#endif /* ! GRUB_SERIAL_MACHINE_HEADER */
diff --git a/term/i386/pc/serial.c b/term/i386/pc/serial.c
deleted file mode 100644 (file)
index 1d74dbb..0000000
+++ /dev/null
@@ -1,625 +0,0 @@
-/*
- *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009  Free Software Foundation, Inc.
- *
- *  GRUB is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation, either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  GRUB is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <grub/machine/machine.h>
-#include <grub/machine/memory.h>
-#include <grub/machine/serial.h>
-#include <grub/machine/console.h>
-#include <grub/term.h>
-#include <grub/types.h>
-#include <grub/dl.h>
-#include <grub/misc.h>
-#include <grub/terminfo.h>
-#include <grub/cpu/io.h>
-#include <grub/extcmd.h>
-
-#define TEXT_WIDTH     80
-#define TEXT_HEIGHT    25
-
-static unsigned int xpos, ypos;
-static unsigned int keep_track = 1;
-static unsigned int registered = 0;
-
-/* An input buffer.  */
-static char input_buf[8];
-static unsigned int npending = 0;
-
-/* Argument options.  */
-static const struct grub_arg_option options[] =
-{
-  {"unit",   'u', 0, "Set the serial unit",             0, ARG_TYPE_INT},
-  {"port",   'p', 0, "Set the serial port address",     0, ARG_TYPE_STRING},
-  {"speed",  's', 0, "Set the serial port speed",       0, ARG_TYPE_INT},
-  {"word",   'w', 0, "Set the serial port word length", 0, ARG_TYPE_INT},
-  {"parity", 'r', 0, "Set the serial port parity",      0, ARG_TYPE_STRING},
-  {"stop",   't', 0, "Set the serial port stop bits",   0, ARG_TYPE_INT},
-  {0, 0, 0, 0, 0, 0}
-};
-
-/* Serial port settings.  */
-struct serial_port
-{
-  unsigned short port;
-  unsigned short divisor;
-  unsigned short word_len;
-  unsigned int   parity;
-  unsigned short stop_bits;
-};
-
-/* Serial port settings.  */
-static struct serial_port serial_settings;
-
-#ifdef GRUB_MACHINE_PCBIOS
-static const unsigned short *serial_hw_io_addr = (const unsigned short *) GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR;
-#define GRUB_SERIAL_PORT_NUM 4
-#else
-static const unsigned short serial_hw_io_addr[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
-#define GRUB_SERIAL_PORT_NUM (ARRAY_SIZE(serial_hw_io_addr))
-#endif
-
-/* Return the port number for the UNITth serial device.  */
-static inline unsigned short
-serial_hw_get_port (const unsigned int unit)
-{
-  if (unit < GRUB_SERIAL_PORT_NUM)
-    return serial_hw_io_addr[unit];
-  else
-    return 0;
-}
-
-/* Fetch a key.  */
-static int
-serial_hw_fetch (void)
-{
-  if (grub_inb (serial_settings.port + UART_LSR) & UART_DATA_READY)
-    return grub_inb (serial_settings.port + UART_RX);
-
-  return -1;
-}
-
-/* Put a character.  */
-static void
-serial_hw_put (const int c)
-{
-  unsigned int timeout = 100000;
-
-  /* Wait until the transmitter holding register is empty.  */
-  while ((grub_inb (serial_settings.port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0)
-    {
-      if (--timeout == 0)
-        /* There is something wrong. But what can I do?  */
-        return;
-    }
-
-  grub_outb (c, serial_settings.port + UART_TX);
-}
-
-static void
-serial_translate_key_sequence (void)
-{
-  unsigned int i;
-  static struct
-  {
-    char key;
-    char ascii;
-  }
-  three_code_table[] =
-    {
-      {'A', 16},
-      {'B', 14},
-      {'C', 6},
-      {'D', 2},
-      {'F', 5},
-      {'H', 1},
-      {'4', 4}
-    };
-
-  static struct
-  {
-      short key;
-      char ascii;
-  }
-  four_code_table[] =
-    {
-      {('1' | ('~' << 8)), 1},
-      {('3' | ('~' << 8)), 4},
-      {('5' | ('~' << 8)), 7},
-      {('6' | ('~' << 8)), 3}
-    };
-
-  if (npending < 3)
-    return;
-
-  /* The buffer must start with "ESC [".  */
-  if (input_buf[0] != '\e' || input_buf[1] != '[')
-    return;
-
-  for (i = 0; ARRAY_SIZE (three_code_table); i++)
-    if (three_code_table[i].key == input_buf[2])
-      {
-       input_buf[0] = three_code_table[i].ascii;
-       npending -= 2;
-       grub_memmove (input_buf + 1, input_buf + 3, npending - 1);
-       return;
-      }
-
-  if (npending >= 4)
-    {
-      short key = input_buf[3] | (input_buf[4] << 8);
-
-      for (i = 0; i < ARRAY_SIZE (four_code_table); i++)
-       if (four_code_table[i].key == key)
-         {
-           input_buf[0] = four_code_table[i].ascii;
-           npending -= 3;
-           grub_memmove (input_buf + 1, input_buf + 4, npending - 1);
-           return;
-         }
-    }
-}
-
-static int
-fill_input_buf (const int nowait)
-{
-  int i;
-
-  for (i = 0; i < 10000 && npending < sizeof (input_buf); i++)
-    {
-      int c;
-
-      c = serial_hw_fetch ();
-      if (c >= 0)
-       {
-         input_buf[npending++] = c;
-
-         /* Reset the counter to zero, to wait for the same interval.  */
-         i = 0;
-       }
-
-      if (nowait)
-       break;
-    }
-
-  /* Translate some key sequences.  */
-  serial_translate_key_sequence ();
-
-  return npending;
-}
-
-/* Convert speed to divisor.  */
-static unsigned short
-serial_get_divisor (unsigned int speed)
-{
-  unsigned int i;
-
-  /* The structure for speed vs. divisor.  */
-  struct divisor
-  {
-    unsigned int speed;
-    unsigned short div;
-  };
-
-  /* The table which lists common configurations.  */
-  /* 1843200 / (speed * 16)  */
-  static struct divisor divisor_tab[] =
-    {
-      { 2400,   0x0030 },
-      { 4800,   0x0018 },
-      { 9600,   0x000C },
-      { 19200,  0x0006 },
-      { 38400,  0x0003 },
-      { 57600,  0x0002 },
-      { 115200, 0x0001 }
-    };
-
-  /* Set the baud rate.  */
-  for (i = 0; i < sizeof (divisor_tab) / sizeof (divisor_tab[0]); i++)
-    if (divisor_tab[i].speed == speed)
-      return divisor_tab[i].div;
-  return 0;
-}
-
-/* The serial version of checkkey.  */
-static int
-grub_serial_checkkey (void)
-{
-  if (fill_input_buf (1))
-    return input_buf[0];
-  else
-    return -1;
-}
-
-/* The serial version of getkey.  */
-static int
-grub_serial_getkey (void)
-{
-  int c;
-
-  while (! fill_input_buf (0))
-    ;
-
-  c = input_buf[0];
-  grub_memmove (input_buf, input_buf + 1, --npending);
-
-  return c;
-}
-
-/* Initialize a serial device. PORT is the port number for a serial device.
-   SPEED is a DTE-DTE speed which must be one of these: 2400, 4800, 9600,
-   19200, 38400, 57600 and 115200. WORD_LEN is the word length to be used
-   for the device. Likewise, PARITY is the type of the parity and
-   STOP_BIT_LEN is the length of the stop bit. The possible values for
-   WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as
-   macros.  */
-static grub_err_t
-serial_hw_init (void)
-{
-  unsigned char status = 0;
-
-  /* Turn off the interrupt.  */
-  grub_outb (0, serial_settings.port + UART_IER);
-
-  /* Set DLAB.  */
-  grub_outb (UART_DLAB, serial_settings.port + UART_LCR);
-
-  /* Set the baud rate.  */
-  grub_outb (serial_settings.divisor & 0xFF, serial_settings.port + UART_DLL);
-  grub_outb (serial_settings.divisor >> 8, serial_settings.port + UART_DLH);
-
-  /* Set the line status.  */
-  status |= (serial_settings.parity
-            | serial_settings.word_len
-            | serial_settings.stop_bits);
-  grub_outb (status, serial_settings.port + UART_LCR);
-
-  /* Enable the FIFO.  */
-  grub_outb (UART_ENABLE_FIFO, serial_settings.port + UART_FCR);
-
-  /* Turn on DTR, RTS, and OUT2.  */
-  grub_outb (UART_ENABLE_MODEM, serial_settings.port + UART_MCR);
-
-  /* Drain the input buffer.  */
-  while (grub_serial_checkkey () != -1)
-    (void) grub_serial_getkey ();
-
-  /*  FIXME: should check if the serial terminal was found.  */
-
-  return GRUB_ERR_NONE;
-}
-
-/* The serial version of putchar.  */
-static void
-grub_serial_putchar (grub_uint32_t c)
-{
-  /* Keep track of the cursor.  */
-  if (keep_track)
-    {
-      /* The serial terminal does not have VGA fonts.  */
-      if (c > 0x7F)
-       {
-         /* Better than nothing.  */
-         switch (c)
-           {
-           case GRUB_TERM_DISP_LEFT:
-             c = '<';
-             break;
-
-           case GRUB_TERM_DISP_UP:
-             c = '^';
-             break;
-
-           case GRUB_TERM_DISP_RIGHT:
-             c = '>';
-             break;
-
-           case GRUB_TERM_DISP_DOWN:
-             c = 'v';
-             break;
-
-           case GRUB_TERM_DISP_HLINE:
-             c = '-';
-             break;
-
-           case GRUB_TERM_DISP_VLINE:
-             c = '|';
-             break;
-
-           case GRUB_TERM_DISP_UL:
-           case GRUB_TERM_DISP_UR:
-           case GRUB_TERM_DISP_LL:
-           case GRUB_TERM_DISP_LR:
-             c = '+';
-             break;
-
-           default:
-             c = '?';
-             break;
-           }
-       }
-
-      switch (c)
-       {
-       case '\a':
-         break;
-
-       case '\b':
-       case 127:
-         if (xpos > 0)
-           xpos--;
-         break;
-
-       case '\n':
-         if (ypos < TEXT_HEIGHT)
-           ypos++;
-         break;
-
-       case '\r':
-         xpos = 0;
-         break;
-
-       default:
-         if (xpos >= TEXT_WIDTH)
-           {
-             grub_putchar ('\r');
-             grub_putchar ('\n');
-           }
-         xpos++;
-         break;
-       }
-    }
-
-  serial_hw_put (c);
-}
-
-static grub_ssize_t
-grub_serial_getcharwidth (grub_uint32_t c __attribute__ ((unused)))
-{
-  return 1;
-}
-
-static grub_uint16_t
-grub_serial_getwh (void)
-{
-  return (TEXT_WIDTH << 8) | TEXT_HEIGHT;
-}
-
-static grub_uint16_t
-grub_serial_getxy (void)
-{
-  return ((xpos << 8) | ypos);
-}
-
-static void
-grub_serial_gotoxy (grub_uint8_t x, grub_uint8_t y)
-{
-  if (x > TEXT_WIDTH || y > TEXT_HEIGHT)
-    {
-      grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid point (%u,%u)", x, y);
-    }
-  else
-    {
-      keep_track = 0;
-      grub_terminfo_gotoxy (x, y);
-      keep_track = 1;
-
-      xpos = x;
-      ypos = y;
-    }
-}
-
-static void
-grub_serial_cls (void)
-{
-  keep_track = 0;
-  grub_terminfo_cls ();
-  keep_track = 1;
-
-  xpos = ypos = 0;
-}
-
-static void
-grub_serial_setcolorstate (const grub_term_color_state state)
-{
-  keep_track = 0;
-  switch (state)
-    {
-    case GRUB_TERM_COLOR_STANDARD:
-    case GRUB_TERM_COLOR_NORMAL:
-      grub_terminfo_reverse_video_off ();
-      break;
-    case GRUB_TERM_COLOR_HIGHLIGHT:
-      grub_terminfo_reverse_video_on ();
-      break;
-    default:
-      break;
-    }
-  keep_track = 1;
-}
-
-static void
-grub_serial_setcursor (const int on)
-{
-  if (on)
-    grub_terminfo_cursor_on ();
-  else
-    grub_terminfo_cursor_off ();
-}
-
-static struct grub_term_input grub_serial_term_input =
-{
-  .name = "serial",
-  .checkkey = grub_serial_checkkey,
-  .getkey = grub_serial_getkey,
-};
-
-static struct grub_term_output grub_serial_term_output =
-{
-  .name = "serial",
-  .putchar = grub_serial_putchar,
-  .getcharwidth = grub_serial_getcharwidth,
-  .getwh = grub_serial_getwh,
-  .getxy = grub_serial_getxy,
-  .gotoxy = grub_serial_gotoxy,
-  .cls = grub_serial_cls,
-  .setcolorstate = grub_serial_setcolorstate,
-  .setcursor = grub_serial_setcursor,
-  .flags = 0,
-};
-
-\f
-
-static grub_err_t
-grub_cmd_serial (grub_extcmd_t cmd,
-                 int argc __attribute__ ((unused)),
-                char **args __attribute__ ((unused)))
-{
-  struct grub_arg_list *state = cmd->state;
-  struct serial_port backup_settings = serial_settings;
-  grub_err_t hwiniterr;
-
-  if (state[0].set)
-    {
-      unsigned int unit;
-
-      unit = grub_strtoul (state[0].arg, 0, 0);
-      serial_settings.port = serial_hw_get_port (unit);
-      if (!serial_settings.port)
-       return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad unit number.");
-    }
-
-  if (state[1].set)
-    serial_settings.port = (unsigned short) grub_strtoul (state[1].arg, 0, 0);
-
-  if (state[2].set)
-    {
-      unsigned long speed;
-
-      speed = grub_strtoul (state[2].arg, 0, 0);
-      serial_settings.divisor = serial_get_divisor ((unsigned int) speed);
-      if (serial_settings.divisor == 0)
-       {
-         serial_settings = backup_settings;
-         return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad speed");
-       }
-    }
-
-  if (state[3].set)
-    {
-      if (! grub_strcmp (state[3].arg, "5"))
-       serial_settings.word_len = UART_5BITS_WORD;
-      else if (! grub_strcmp (state[3].arg, "6"))
-       serial_settings.word_len = UART_6BITS_WORD;
-      else if (! grub_strcmp (state[3].arg, "7"))
-       serial_settings.word_len = UART_7BITS_WORD;
-      else if (! grub_strcmp (state[3].arg, "8"))
-       serial_settings.word_len = UART_8BITS_WORD;
-      else
-       {
-         serial_settings = backup_settings;
-         return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad word length");
-       }
-    }
-
-  if (state[4].set)
-    {
-      if (! grub_strcmp (state[4].arg, "no"))
-       serial_settings.parity = UART_NO_PARITY;
-      else if (! grub_strcmp (state[4].arg, "odd"))
-       serial_settings.parity = UART_ODD_PARITY;
-      else if (! grub_strcmp (state[4].arg, "even"))
-       serial_settings.parity = UART_EVEN_PARITY;
-      else
-       {
-         serial_settings = backup_settings;
-         return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad parity");
-       }
-    }
-
-  if (state[5].set)
-    {
-      if (! grub_strcmp (state[5].arg, "1"))
-       serial_settings.stop_bits = UART_1_STOP_BIT;
-      else if (! grub_strcmp (state[5].arg, "2"))
-       serial_settings.stop_bits = UART_2_STOP_BITS;
-      else
-       {
-         serial_settings = backup_settings;
-         return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad number of stop bits");
-       }
-    }
-
-  /* Initialize with new settings.  */
-  hwiniterr = serial_hw_init ();
-
-  if (hwiniterr == GRUB_ERR_NONE)
-    {
-      /* Register terminal if not yet registered.  */
-      if (registered == 0)
-       {
-         grub_term_register_input ("serial", &grub_serial_term_input);
-         grub_term_register_output ("serial", &grub_serial_term_output);
-         registered = 1;
-       }
-    }
-  else
-    {
-      /* Initialization with new settings failed.  */
-      if (registered == 1)
-       {
-         /* If the terminal is registered, attempt to restore previous
-            settings.  */
-         serial_settings = backup_settings;
-         if (serial_hw_init () != GRUB_ERR_NONE)
-           {
-             /* If unable to restore settings, unregister terminal.  */
-             grub_term_unregister_input (&grub_serial_term_input);
-             grub_term_unregister_output (&grub_serial_term_output);
-             registered = 0;
-           }
-       }
-    }
-
-  return hwiniterr;
-}
-
-static grub_extcmd_t cmd;
-
-GRUB_MOD_INIT(serial)
-{
-  cmd = grub_register_extcmd ("serial", grub_cmd_serial,
-                             GRUB_COMMAND_FLAG_BOTH,
-                             "serial [OPTIONS...]",
-                             "Configure serial port.", options);
-
-  /* Set default settings.  */
-  serial_settings.port      = serial_hw_get_port (0);
-  serial_settings.divisor   = serial_get_divisor (9600);
-  serial_settings.word_len  = UART_8BITS_WORD;
-  serial_settings.parity    = UART_NO_PARITY;
-  serial_settings.stop_bits = UART_1_STOP_BIT;
-}
-
-GRUB_MOD_FINI(serial)
-{
-  grub_unregister_extcmd (cmd);
-  if (registered == 1)         /* Unregister terminal only if registered. */
-    {
-      grub_term_unregister_input (&grub_serial_term_input);
-      grub_term_unregister_output (&grub_serial_term_output);
-    }
-}
diff --git a/term/serial.c b/term/serial.c
new file mode 100644 (file)
index 0000000..eac43bf
--- /dev/null
@@ -0,0 +1,625 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/machine/machine.h>
+#include <grub/machine/memory.h>
+#include <grub/machine/serial.h>
+#include <grub/machine/console.h>
+#include <grub/term.h>
+#include <grub/types.h>
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/terminfo.h>
+#include <grub/cpu/io.h>
+#include <grub/extcmd.h>
+
+#define TEXT_WIDTH     80
+#define TEXT_HEIGHT    25
+
+static unsigned int xpos, ypos;
+static unsigned int keep_track = 1;
+static unsigned int registered = 0;
+
+/* An input buffer.  */
+static char input_buf[8];
+static unsigned int npending = 0;
+
+/* Argument options.  */
+static const struct grub_arg_option options[] =
+{
+  {"unit",   'u', 0, "Set the serial unit",             0, ARG_TYPE_INT},
+  {"port",   'p', 0, "Set the serial port address",     0, ARG_TYPE_STRING},
+  {"speed",  's', 0, "Set the serial port speed",       0, ARG_TYPE_INT},
+  {"word",   'w', 0, "Set the serial port word length", 0, ARG_TYPE_INT},
+  {"parity", 'r', 0, "Set the serial port parity",      0, ARG_TYPE_STRING},
+  {"stop",   't', 0, "Set the serial port stop bits",   0, ARG_TYPE_INT},
+  {0, 0, 0, 0, 0, 0}
+};
+
+/* Serial port settings.  */
+struct serial_port
+{
+  grub_port_t port;
+  unsigned short divisor;
+  unsigned short word_len;
+  unsigned int   parity;
+  unsigned short stop_bits;
+};
+
+/* Serial port settings.  */
+static struct serial_port serial_settings;
+
+#ifdef GRUB_MACHINE_PCBIOS
+static const unsigned short *serial_hw_io_addr = (const unsigned short *) GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR;
+#define GRUB_SERIAL_PORT_NUM 4
+#else
+static const grub_port_t serial_hw_io_addr[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
+#define GRUB_SERIAL_PORT_NUM (ARRAY_SIZE(serial_hw_io_addr))
+#endif
+
+/* Return the port number for the UNITth serial device.  */
+static inline grub_port_t
+serial_hw_get_port (const unsigned int unit)
+{
+  if (unit < GRUB_SERIAL_PORT_NUM)
+    return serial_hw_io_addr[unit];
+  else
+    return 0;
+}
+
+/* Fetch a key.  */
+static int
+serial_hw_fetch (void)
+{
+  if (grub_inb (serial_settings.port + UART_LSR) & UART_DATA_READY)
+    return grub_inb (serial_settings.port + UART_RX);
+
+  return -1;
+}
+
+/* Put a character.  */
+static void
+serial_hw_put (const int c)
+{
+  unsigned int timeout = 100000;
+
+  /* Wait until the transmitter holding register is empty.  */
+  while ((grub_inb (serial_settings.port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0)
+    {
+      if (--timeout == 0)
+        /* There is something wrong. But what can I do?  */
+        return;
+    }
+
+  grub_outb (c, serial_settings.port + UART_TX);
+}
+
+static void
+serial_translate_key_sequence (void)
+{
+  unsigned int i;
+  static struct
+  {
+    char key;
+    char ascii;
+  }
+  three_code_table[] =
+    {
+      {'A', 16},
+      {'B', 14},
+      {'C', 6},
+      {'D', 2},
+      {'F', 5},
+      {'H', 1},
+      {'4', 4}
+    };
+
+  static struct
+  {
+      short key;
+      char ascii;
+  }
+  four_code_table[] =
+    {
+      {('1' | ('~' << 8)), 1},
+      {('3' | ('~' << 8)), 4},
+      {('5' | ('~' << 8)), 7},
+      {('6' | ('~' << 8)), 3}
+    };
+
+  if (npending < 3)
+    return;
+
+  /* The buffer must start with "ESC [".  */
+  if (input_buf[0] != '\e' || input_buf[1] != '[')
+    return;
+
+  for (i = 0; ARRAY_SIZE (three_code_table); i++)
+    if (three_code_table[i].key == input_buf[2])
+      {
+       input_buf[0] = three_code_table[i].ascii;
+       npending -= 2;
+       grub_memmove (input_buf + 1, input_buf + 3, npending - 1);
+       return;
+      }
+
+  if (npending >= 4)
+    {
+      short key = input_buf[3] | (input_buf[4] << 8);
+
+      for (i = 0; i < ARRAY_SIZE (four_code_table); i++)
+       if (four_code_table[i].key == key)
+         {
+           input_buf[0] = four_code_table[i].ascii;
+           npending -= 3;
+           grub_memmove (input_buf + 1, input_buf + 4, npending - 1);
+           return;
+         }
+    }
+}
+
+static int
+fill_input_buf (const int nowait)
+{
+  int i;
+
+  for (i = 0; i < 10000 && npending < sizeof (input_buf); i++)
+    {
+      int c;
+
+      c = serial_hw_fetch ();
+      if (c >= 0)
+       {
+         input_buf[npending++] = c;
+
+         /* Reset the counter to zero, to wait for the same interval.  */
+         i = 0;
+       }
+
+      if (nowait)
+       break;
+    }
+
+  /* Translate some key sequences.  */
+  serial_translate_key_sequence ();
+
+  return npending;
+}
+
+/* Convert speed to divisor.  */
+static unsigned short
+serial_get_divisor (unsigned int speed)
+{
+  unsigned int i;
+
+  /* The structure for speed vs. divisor.  */
+  struct divisor
+  {
+    unsigned int speed;
+    unsigned short div;
+  };
+
+  /* The table which lists common configurations.  */
+  /* 1843200 / (speed * 16)  */
+  static struct divisor divisor_tab[] =
+    {
+      { 2400,   0x0030 },
+      { 4800,   0x0018 },
+      { 9600,   0x000C },
+      { 19200,  0x0006 },
+      { 38400,  0x0003 },
+      { 57600,  0x0002 },
+      { 115200, 0x0001 }
+    };
+
+  /* Set the baud rate.  */
+  for (i = 0; i < sizeof (divisor_tab) / sizeof (divisor_tab[0]); i++)
+    if (divisor_tab[i].speed == speed)
+      return divisor_tab[i].div;
+  return 0;
+}
+
+/* The serial version of checkkey.  */
+static int
+grub_serial_checkkey (void)
+{
+  if (fill_input_buf (1))
+    return input_buf[0];
+  else
+    return -1;
+}
+
+/* The serial version of getkey.  */
+static int
+grub_serial_getkey (void)
+{
+  int c;
+
+  while (! fill_input_buf (0))
+    ;
+
+  c = input_buf[0];
+  grub_memmove (input_buf, input_buf + 1, --npending);
+
+  return c;
+}
+
+/* Initialize a serial device. PORT is the port number for a serial device.
+   SPEED is a DTE-DTE speed which must be one of these: 2400, 4800, 9600,
+   19200, 38400, 57600 and 115200. WORD_LEN is the word length to be used
+   for the device. Likewise, PARITY is the type of the parity and
+   STOP_BIT_LEN is the length of the stop bit. The possible values for
+   WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as
+   macros.  */
+static grub_err_t
+serial_hw_init (void)
+{
+  unsigned char status = 0;
+
+  /* Turn off the interrupt.  */
+  grub_outb (0, serial_settings.port + UART_IER);
+
+  /* Set DLAB.  */
+  grub_outb (UART_DLAB, serial_settings.port + UART_LCR);
+
+  /* Set the baud rate.  */
+  grub_outb (serial_settings.divisor & 0xFF, serial_settings.port + UART_DLL);
+  grub_outb (serial_settings.divisor >> 8, serial_settings.port + UART_DLH);
+
+  /* Set the line status.  */
+  status |= (serial_settings.parity
+            | serial_settings.word_len
+            | serial_settings.stop_bits);
+  grub_outb (status, serial_settings.port + UART_LCR);
+
+  /* Enable the FIFO.  */
+  grub_outb (UART_ENABLE_FIFO, serial_settings.port + UART_FCR);
+
+  /* Turn on DTR, RTS, and OUT2.  */
+  grub_outb (UART_ENABLE_MODEM, serial_settings.port + UART_MCR);
+
+  /* Drain the input buffer.  */
+  while (grub_serial_checkkey () != -1)
+    (void) grub_serial_getkey ();
+
+  /*  FIXME: should check if the serial terminal was found.  */
+
+  return GRUB_ERR_NONE;
+}
+
+/* The serial version of putchar.  */
+static void
+grub_serial_putchar (grub_uint32_t c)
+{
+  /* Keep track of the cursor.  */
+  if (keep_track)
+    {
+      /* The serial terminal does not have VGA fonts.  */
+      if (c > 0x7F)
+       {
+         /* Better than nothing.  */
+         switch (c)
+           {
+           case GRUB_TERM_DISP_LEFT:
+             c = '<';
+             break;
+
+           case GRUB_TERM_DISP_UP:
+             c = '^';
+             break;
+
+           case GRUB_TERM_DISP_RIGHT:
+             c = '>';
+             break;
+
+           case GRUB_TERM_DISP_DOWN:
+             c = 'v';
+             break;
+
+           case GRUB_TERM_DISP_HLINE:
+             c = '-';
+             break;
+
+           case GRUB_TERM_DISP_VLINE:
+             c = '|';
+             break;
+
+           case GRUB_TERM_DISP_UL:
+           case GRUB_TERM_DISP_UR:
+           case GRUB_TERM_DISP_LL:
+           case GRUB_TERM_DISP_LR:
+             c = '+';
+             break;
+
+           default:
+             c = '?';
+             break;
+           }
+       }
+
+      switch (c)
+       {
+       case '\a':
+         break;
+
+       case '\b':
+       case 127:
+         if (xpos > 0)
+           xpos--;
+         break;
+
+       case '\n':
+         if (ypos < TEXT_HEIGHT)
+           ypos++;
+         break;
+
+       case '\r':
+         xpos = 0;
+         break;
+
+       default:
+         if (xpos >= TEXT_WIDTH)
+           {
+             grub_putchar ('\r');
+             grub_putchar ('\n');
+           }
+         xpos++;
+         break;
+       }
+    }
+
+  serial_hw_put (c);
+}
+
+static grub_ssize_t
+grub_serial_getcharwidth (grub_uint32_t c __attribute__ ((unused)))
+{
+  return 1;
+}
+
+static grub_uint16_t
+grub_serial_getwh (void)
+{
+  return (TEXT_WIDTH << 8) | TEXT_HEIGHT;
+}
+
+static grub_uint16_t
+grub_serial_getxy (void)
+{
+  return ((xpos << 8) | ypos);
+}
+
+static void
+grub_serial_gotoxy (grub_uint8_t x, grub_uint8_t y)
+{
+  if (x > TEXT_WIDTH || y > TEXT_HEIGHT)
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid point (%u,%u)", x, y);
+    }
+  else
+    {
+      keep_track = 0;
+      grub_terminfo_gotoxy (x, y);
+      keep_track = 1;
+
+      xpos = x;
+      ypos = y;
+    }
+}
+
+static void
+grub_serial_cls (void)
+{
+  keep_track = 0;
+  grub_terminfo_cls ();
+  keep_track = 1;
+
+  xpos = ypos = 0;
+}
+
+static void
+grub_serial_setcolorstate (const grub_term_color_state state)
+{
+  keep_track = 0;
+  switch (state)
+    {
+    case GRUB_TERM_COLOR_STANDARD:
+    case GRUB_TERM_COLOR_NORMAL:
+      grub_terminfo_reverse_video_off ();
+      break;
+    case GRUB_TERM_COLOR_HIGHLIGHT:
+      grub_terminfo_reverse_video_on ();
+      break;
+    default:
+      break;
+    }
+  keep_track = 1;
+}
+
+static void
+grub_serial_setcursor (const int on)
+{
+  if (on)
+    grub_terminfo_cursor_on ();
+  else
+    grub_terminfo_cursor_off ();
+}
+
+static struct grub_term_input grub_serial_term_input =
+{
+  .name = "serial",
+  .checkkey = grub_serial_checkkey,
+  .getkey = grub_serial_getkey,
+};
+
+static struct grub_term_output grub_serial_term_output =
+{
+  .name = "serial",
+  .putchar = grub_serial_putchar,
+  .getcharwidth = grub_serial_getcharwidth,
+  .getwh = grub_serial_getwh,
+  .getxy = grub_serial_getxy,
+  .gotoxy = grub_serial_gotoxy,
+  .cls = grub_serial_cls,
+  .setcolorstate = grub_serial_setcolorstate,
+  .setcursor = grub_serial_setcursor,
+  .flags = 0,
+};
+
+\f
+
+static grub_err_t
+grub_cmd_serial (grub_extcmd_t cmd,
+                 int argc __attribute__ ((unused)),
+                char **args __attribute__ ((unused)))
+{
+  struct grub_arg_list *state = cmd->state;
+  struct serial_port backup_settings = serial_settings;
+  grub_err_t hwiniterr;
+
+  if (state[0].set)
+    {
+      unsigned int unit;
+
+      unit = grub_strtoul (state[0].arg, 0, 0);
+      serial_settings.port = serial_hw_get_port (unit);
+      if (!serial_settings.port)
+       return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad unit number.");
+    }
+
+  if (state[1].set)
+    serial_settings.port = (grub_port_t) grub_strtoul (state[1].arg, 0, 0);
+
+  if (state[2].set)
+    {
+      unsigned long speed;
+
+      speed = grub_strtoul (state[2].arg, 0, 0);
+      serial_settings.divisor = serial_get_divisor ((unsigned int) speed);
+      if (serial_settings.divisor == 0)
+       {
+         serial_settings = backup_settings;
+         return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad speed");
+       }
+    }
+
+  if (state[3].set)
+    {
+      if (! grub_strcmp (state[3].arg, "5"))
+       serial_settings.word_len = UART_5BITS_WORD;
+      else if (! grub_strcmp (state[3].arg, "6"))
+       serial_settings.word_len = UART_6BITS_WORD;
+      else if (! grub_strcmp (state[3].arg, "7"))
+       serial_settings.word_len = UART_7BITS_WORD;
+      else if (! grub_strcmp (state[3].arg, "8"))
+       serial_settings.word_len = UART_8BITS_WORD;
+      else
+       {
+         serial_settings = backup_settings;
+         return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad word length");
+       }
+    }
+
+  if (state[4].set)
+    {
+      if (! grub_strcmp (state[4].arg, "no"))
+       serial_settings.parity = UART_NO_PARITY;
+      else if (! grub_strcmp (state[4].arg, "odd"))
+       serial_settings.parity = UART_ODD_PARITY;
+      else if (! grub_strcmp (state[4].arg, "even"))
+       serial_settings.parity = UART_EVEN_PARITY;
+      else
+       {
+         serial_settings = backup_settings;
+         return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad parity");
+       }
+    }
+
+  if (state[5].set)
+    {
+      if (! grub_strcmp (state[5].arg, "1"))
+       serial_settings.stop_bits = UART_1_STOP_BIT;
+      else if (! grub_strcmp (state[5].arg, "2"))
+       serial_settings.stop_bits = UART_2_STOP_BITS;
+      else
+       {
+         serial_settings = backup_settings;
+         return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad number of stop bits");
+       }
+    }
+
+  /* Initialize with new settings.  */
+  hwiniterr = serial_hw_init ();
+
+  if (hwiniterr == GRUB_ERR_NONE)
+    {
+      /* Register terminal if not yet registered.  */
+      if (registered == 0)
+       {
+         grub_term_register_input ("serial", &grub_serial_term_input);
+         grub_term_register_output ("serial", &grub_serial_term_output);
+         registered = 1;
+       }
+    }
+  else
+    {
+      /* Initialization with new settings failed.  */
+      if (registered == 1)
+       {
+         /* If the terminal is registered, attempt to restore previous
+            settings.  */
+         serial_settings = backup_settings;
+         if (serial_hw_init () != GRUB_ERR_NONE)
+           {
+             /* If unable to restore settings, unregister terminal.  */
+             grub_term_unregister_input (&grub_serial_term_input);
+             grub_term_unregister_output (&grub_serial_term_output);
+             registered = 0;
+           }
+       }
+    }
+
+  return hwiniterr;
+}
+
+static grub_extcmd_t cmd;
+
+GRUB_MOD_INIT(serial)
+{
+  cmd = grub_register_extcmd ("serial", grub_cmd_serial,
+                             GRUB_COMMAND_FLAG_BOTH,
+                             "serial [OPTIONS...]",
+                             "Configure serial port.", options);
+
+  /* Set default settings.  */
+  serial_settings.port      = serial_hw_get_port (0);
+  serial_settings.divisor   = serial_get_divisor (9600);
+  serial_settings.word_len  = UART_8BITS_WORD;
+  serial_settings.parity    = UART_NO_PARITY;
+  serial_settings.stop_bits = UART_1_STOP_BIT;
+}
+
+GRUB_MOD_FINI(serial)
+{
+  grub_unregister_extcmd (cmd);
+  if (registered == 1)         /* Unregister terminal only if registered. */
+    {
+      grub_term_unregister_input (&grub_serial_term_input);
+      grub_term_unregister_output (&grub_serial_term_output);
+    }
+}