grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: x86 serial support


From: Omniflux
Subject: Re: x86 serial support
Date: Tue, 09 Aug 2005 18:41:20 -0600
User-agent: Mozilla Thunderbird 1.0 (Windows/20041206)

It's been a few months, but here are some updated patches.

The terminfo patch probably needs to be applied first.

Yoshinori K. Okuji wrote:
> - Naming issues. Since you copied the code from GRUB Legacy, some names 
> are not appropriate in GRUB 2. For example, the global function 
> declared in terminfo.h do not use the prefix 'grub_' at all.

I think I have fixed these all correctly...

> - Module separation. I think it would be better to have a separate 
> module for terminfo, because we might be able to use it for other 
> terminals as well as serial console, say, parallel console.

Done.

> - ChangeLog. Please write ChangeLog entries for your changes.

Attached.

> BTW, do you have any suggestion about the so-called "dumb terminal"? 
> Because supporting dumb terminal is horrible, I hesitate to support it 
> in GRUB 2, if there is no clean way to do it.

The only thing I can think of is to disable the menu and replace clear
screen with a newline, which is what Legacy does IIRC.



I need some input on how to complete the function
grub_terminfo_set_current in term/terminfo.c.

Feedback on anything else would also be appreciated.

Thanks!

-- 
Omniflux
diff -uNr grub2/conf/i386-pc.rmk grub2.terminfo/conf/i386-pc.rmk
--- grub2/conf/i386-pc.rmk      2005-08-09 08:39:50.000000000 -0600
+++ grub2.terminfo/conf/i386-pc.rmk     2005-08-09 17:46:45.000000000 -0600
@@ -110,7 +110,7 @@
        font.mod _multiboot.mod ls.mod boot.mod cmp.mod cat.mod         \
        terminal.mod fshelp.mod chain.mod multiboot.mod amiga.mod       \
        apple.mod pc.mod sun.mod loopback.mod reboot.mod halt.mod       \
-       help.mod default.mod timeout.mod configfile.mod
+       help.mod default.mod timeout.mod configfile.mod terminfo.mod
 
 # For _chain.mod.
 _chain_mod_SOURCES = loader/i386/pc/chainloader.c
@@ -211,6 +211,10 @@
 font_mod_SOURCES = font/manager.c
 font_mod_CFLAGS = $(COMMON_CFLAGS)
 
+# For terminfo.mod.
+terminfo_mod_SOURCES = term/terminfo.c term/tparm.c
+terminfo_mod_CFLAGS = $(COMMON_CFLAGS)
+
 # For _multiboot.mod.
 _multiboot_mod_SOURCES = loader/i386/pc/multiboot.c
 _multiboot_mod_CFLAGS = $(COMMON_CFLAGS)
diff -uNr grub2/include/grub/terminfo.h grub2.terminfo/include/grub/terminfo.h
--- grub2/include/grub/terminfo.h       1969-12-31 17:00:00.000000000 -0700
+++ grub2.terminfo/include/grub/terminfo.h      2005-08-09 17:46:45.000000000 
-0600
@@ -0,0 +1,49 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2002,2003,2005  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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef GRUB_TERMINFO_HEADER
+#define GRUB_TERMINFO_HEADER   1
+
+#include <grub/err.h>
+#include <grub/symbol.h>
+#include <grub/types.h>
+
+typedef struct terminfo
+{
+  char *name;
+
+  char *gotoxy;
+  char *cls;
+  char *reverse_video_on;
+  char *reverse_video_off;
+  char *cursor_on;
+  char *cursor_off;
+} terminfo;
+
+char * EXPORT_FUNC(grub_terminfo_get_current) (void);
+grub_err_t EXPORT_FUNC(grub_terminfo_set_current) (const char *);
+
+void EXPORT_FUNC(grub_terminfo_gotoxy) (const grub_uint8_t x, const 
grub_uint8_t y);
+void EXPORT_FUNC(grub_terminfo_cls) (void);
+void EXPORT_FUNC(grub_terminfo_reverse_video_on) (void);
+void EXPORT_FUNC(grub_terminfo_reverse_video_off) (void);
+void EXPORT_FUNC(grub_terminfo_cursor_on) (void);
+void EXPORT_FUNC(grub_terminfo_cursor_off) (void);
+
+#endif /* ! GRUB_TERMINFO_HEADER */
diff -uNr grub2/include/grub/tparm.h grub2.terminfo/include/grub/tparm.h
--- grub2/include/grub/tparm.h  1969-12-31 17:00:00.000000000 -0700
+++ grub2.terminfo/include/grub/tparm.h 2005-08-09 17:46:45.000000000 -0600
@@ -0,0 +1,28 @@
+/* tparm.h - parameter formatting of terminfo */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2002,2005  Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef GRUB_TPARM_HEADER
+#define GRUB_TPARM_HEADER      1
+
+
+/* Function prototypes.  */
+char *grub_terminfo_tparm (const char *string, ...);
+
+#endif /* ! GRUB_TPARM_HEADER */
diff -uNr grub2/term/terminfo.c grub2.terminfo/term/terminfo.c
--- grub2/term/terminfo.c       1969-12-31 17:00:00.000000000 -0700
+++ grub2.terminfo/term/terminfo.c      2005-08-09 17:46:45.000000000 -0600
@@ -0,0 +1,157 @@
+/* terminfo.c - simple terminfo module */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2003,2004,2005  Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This file contains various functions dealing with different
+ * terminal capabilities. For example, vt52 and vt100.
+ */
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/normal.h>
+#include <grub/term.h>
+#include <grub/terminfo.h>
+#include <grub/tparm.h>
+
+static struct terminfo term;
+
+/* Get current terminfo name.  */
+inline char *
+grub_terminfo_get_current (void)
+{
+  return term.name;
+}
+
+/* Set current terminfo type.  */
+grub_err_t
+grub_terminfo_set_current (const char *str)
+{
+  /* TODO
+   * Lookup user specified terminfo type. If found, set term variables
+   * as appropriate. Otherwise return an error.
+   *
+   * How should this be done?
+   *  a. A static table included in this module.
+   *     - I do not like this idea.
+   *  b. A table stored in the configuration directory.
+   *     - Users must convert their terminfo settings if we have not already.
+   *  c. Look for terminfo files in the configuration directory.
+   *     - /usr/share/terminfo is 6.3M on my system.
+   *     - /usr/share/terminfo is not on most users boot partition.
+   *     + Copying the terminfo files you want to use to the grub
+   *       configuration directory is easier then (b).
+   *  d. Your idea here.
+   */
+
+  if (grub_strcmp ("vt100", str) == 0)
+    {
+      term.name              = grub_strdup ("vt100");
+      term.gotoxy            = grub_strdup ("\e[%i%p1%d;%p2%dH");
+      term.cls               = grub_strdup ("\e[H\e[J");
+      term.reverse_video_on  = grub_strdup ("\e[7m");
+      term.reverse_video_off = grub_strdup ("\e[m");
+      term.cursor_on         = grub_strdup ("\e[?25l");
+      term.cursor_off        = grub_strdup ("\e[?25h");
+      return GRUB_ERR_NONE;
+    }
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown terminfo type.");
+}
+
+/* Wrapper for grub_putchar to write strings.  */
+static void putstr (const char *str)
+{
+  while (*str)
+    grub_putchar (*str++);
+}
+
+/* Move the cursor to the given position starting with "0".  */
+void
+grub_terminfo_gotoxy (const grub_uint8_t x, const grub_uint8_t y)
+{
+  putstr (grub_terminfo_tparm (term.gotoxy, y, x));
+}
+
+/* Clear the screen.  */
+void
+grub_terminfo_cls (void)
+{
+  putstr (grub_terminfo_tparm (term.cls));
+}
+
+/* Set reverse video mode on.  */
+void
+grub_terminfo_reverse_video_on (void)
+{
+  putstr (grub_terminfo_tparm (term.reverse_video_on));
+}
+
+/* Set reverse video mode off.  */
+void
+grub_terminfo_reverse_video_off (void)
+{
+  putstr (grub_terminfo_tparm (term.reverse_video_off));
+}
+
+/* Show cursor.  */
+void
+grub_terminfo_cursor_on (void)
+{
+  putstr (grub_terminfo_tparm (term.cursor_on));
+}
+
+/* Hide cursor.  */
+void
+grub_terminfo_cursor_off (void)
+{
+  putstr (grub_terminfo_tparm (term.cursor_off));
+}
+
+/* GRUB Command.  */
+
+static grub_err_t
+grub_cmd_terminfo (struct grub_arg_list *state __attribute__ ((unused)),
+               int argc, char **args)
+{
+  if (argc == 0)
+  {
+    grub_printf ("Current terminfo type: %s\n", grub_terminfo_get_current());
+    return GRUB_ERR_NONE;
+  }
+  else if (argc != 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "too many parameters.");
+  else
+    return grub_terminfo_set_current (args[0]);
+}
+
+GRUB_MOD_INIT
+{
+  (void)mod;                   /* To stop warning. */
+  grub_register_command ("terminfo", grub_cmd_terminfo, GRUB_COMMAND_FLAG_BOTH,
+                        "terminfo [TERM...]", "Set terminfo type.", 0);
+  grub_terminfo_set_current ("vt100");
+}
+
+GRUB_MOD_FINI
+{
+  grub_unregister_command ("terminfo");
+}
diff -uNr grub2/term/tparm.c grub2.terminfo/term/tparm.c
--- grub2/term/tparm.c  1969-12-31 17:00:00.000000000 -0700
+++ grub2.terminfo/term/tparm.c 2005-08-09 17:46:45.000000000 -0600
@@ -0,0 +1,769 @@
+/****************************************************************************
+ * Copyright (c) 1998-2003,2004,2005 Free Software Foundation, Inc.         *
+ *                                                                          *
+ * Permission is hereby granted, free of charge, to any person obtaining a  *
+ * copy of this software and associated documentation files (the            *
+ * "Software"), to deal in the Software without restriction, including      *
+ * without limitation the rights to use, copy, modify, merge, publish,      *
+ * distribute, distribute with modifications, sublicense, and/or sell       *
+ * copies of the Software, and to permit persons to whom the Software is    *
+ * furnished to do so, subject to the following conditions:                 *
+ *                                                                          *
+ * The above copyright notice and this permission notice shall be included  *
+ * in all copies or substantial portions of the Software.                   *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
+ * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
+ *                                                                          *
+ * Except as contained in this notice, the name(s) of the above copyright   *
+ * holders shall not be used in advertising or otherwise to promote the     *
+ * sale, use or other dealings in this Software without prior written       *
+ * authorization.                                                           *
+ ****************************************************************************/
+
+/**********************************************************************
+ * This code is a modification of lib_tparm.c found in ncurses-5.2. The
+ * modification are for use in grub by replacing all libc function through
+ * special grub functions. This also meant to delete all dynamic memory
+ * allocation and replace it by a number of fixed buffers.
+ *
+ * Modifications by Tilmann Bubeck <address@hidden> 2002
+ *
+ * Resync with ncurses-5.4 by Omniflux <address@hidden> 2005
+ **********************************************************************/
+
+/****************************************************************************
+ *  Author: Zeyd M. Ben-Halim <address@hidden> 1992,1995               *
+ *     and: Eric S. Raymond <address@hidden>                         *
+ *     and: Thomas E. Dickey, 1996 on                                       *
+ ****************************************************************************/
+
+/*
+ *     tparm.c
+ *
+ */
+
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/types.h>
+#include <grub/tparm.h>
+
+/*
+ * Common/troublesome character definitions
+ */
+typedef char grub_bool_t;
+#ifndef FALSE
+# define FALSE (0)
+#endif
+#ifndef TRUE
+# define TRUE (!FALSE)
+#endif
+
+#define NUM_PARM 9
+#define NUM_VARS 26
+#define STACKSIZE 20
+#define MAX_FORMAT_LEN 256
+
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#define isdigit(c) ((c) >= '0' && (c) <= '9')
+#define isUPPER(c) ((c) >= 'A' && (c) <= 'Z')
+#define isLOWER(c) ((c) >= 'a' && (c) <= 'z')
+
+#define UChar(c) ((unsigned char)(c))
+
+//MODULE_ID("$Id: tparm.c,v 1.1 2002/11/29 20:39:24 okuji Exp $")
+
+/*
+ *     char *
+ *     tparm(string, ...)
+ *
+ *     Substitute the given parameters into the given string by the following
+ *     rules (taken from terminfo(5)):
+ *
+ *          Cursor addressing and other strings  requiring  parame-
+ *     ters in the terminal are described by a parameterized string
+ *     capability, with like escapes %x in  it.   For  example,  to
+ *     address  the  cursor, the cup capability is given, using two
+ *     parameters: the row and column to  address  to.   (Rows  and
+ *     columns  are  numbered  from  zero and refer to the physical
+ *     screen visible to the user, not to any  unseen  memory.)  If
+ *     the terminal has memory relative cursor addressing, that can
+ *     be indicated by
+ *
+ *          The parameter mechanism uses  a  stack  and  special  %
+ *     codes  to manipulate it.  Typically a sequence will push one
+ *     of the parameters onto the stack and then print it  in  some
+ *     format.  Often more complex operations are necessary.
+ *
+ *          The % encodings have the following meanings:
+ *
+ *          %%        outputs `%'
+ *          %c        print pop() like %c in printf()
+ *          %s        print pop() like %s in printf()
+ *           %[[:]flags][width[.precision]][doxXs]
+ *                     as in printf, flags are [-+#] and space
+ *                     The ':' is used to avoid making %+ or %-
+ *                     patterns (see below).
+ *
+ *          %p[1-9]   push ith parm
+ *          %P[a-z]   set dynamic variable [a-z] to pop()
+ *          %g[a-z]   get dynamic variable [a-z] and push it
+ *          %P[A-Z]   set static variable [A-Z] to pop()
+ *          %g[A-Z]   get static variable [A-Z] and push it
+ *          %l        push strlen(pop)
+ *          %'c'      push char constant c
+ *          %{nn}     push integer constant nn
+ *
+ *          %+ %- %* %/ %m
+ *                    arithmetic (%m is mod): push(pop() op pop())
+ *          %& %| %^  bit operations: push(pop() op pop())
+ *          %= %> %<  logical operations: push(pop() op pop())
+ *          %A %O     logical and & or operations for conditionals
+ *          %! %~     unary operations push(op pop())
+ *          %i        add 1 to first two parms (for ANSI terminals)
+ *
+ *          %? expr %t thenpart %e elsepart %;
+ *                    if-then-else, %e elsepart is optional.
+ *                    else-if's are possible ala Algol 68:
+ *                    %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %;
+ *
+ *     For those of the above operators which are binary and not commutative,
+ *     the stack works in the usual way, with
+ *                     %gx %gy %m
+ *     resulting in x mod y, not the reverse.
+ */
+
+typedef struct {
+    union {
+       int num;
+       char *str;
+    } data;
+    grub_bool_t num_type;
+} stack_frame;
+
+static stack_frame stack[STACKSIZE];
+static int stack_ptr;
+static const char *tparam_base = "";
+
+static char *out_buff;
+static grub_size_t out_size;
+static grub_size_t out_used;
+
+static char *fmt_buff;
+static grub_size_t fmt_size;
+
+static inline void
+get_space(grub_size_t need)
+{
+    need += out_used;
+    if (need > out_size) {
+       out_size = need * 2;
+       out_buff = grub_realloc(out_buff, out_size*sizeof(char));
+       if (out_buff == 0)
+           // FIX ME! OOM, what now?
+           ;
+    }
+}
+
+static inline void
+save_text(const char *fmt, const char *s, int len)
+{
+    grub_size_t s_len = grub_strlen(s);
+    if (len > (int) s_len)
+       s_len = len;
+
+    get_space(s_len + 1);
+
+    (void) grub_sprintf(out_buff + out_used, fmt, s);
+    out_used += grub_strlen(out_buff + out_used);
+}
+
+static inline void
+save_number(const char *fmt, int number, int len)
+{
+    if (len < 30)
+       len = 30;               /* actually log10(MAX_INT)+1 */
+
+    get_space((unsigned) len + 1);
+
+    (void) grub_sprintf(out_buff + out_used, fmt, number);
+    out_used += grub_strlen(out_buff + out_used);
+}
+
+static inline void
+save_char(int c)
+{
+    if (c == 0)
+       c = 0200;
+    get_space(1);
+    out_buff[out_used++] = c;
+}
+
+static inline void
+npush(int x)
+{
+    if (stack_ptr < STACKSIZE) {
+       stack[stack_ptr].num_type = TRUE;
+       stack[stack_ptr].data.num = x;
+       stack_ptr++;
+    }
+}
+
+static inline int
+npop(void)
+{
+    int result = 0;
+    if (stack_ptr > 0) {
+       stack_ptr--;
+       if (stack[stack_ptr].num_type)
+           result = stack[stack_ptr].data.num;
+    }
+    return result;
+}
+
+static inline void
+spush(char *x)
+{
+    if (stack_ptr < STACKSIZE) {
+       stack[stack_ptr].num_type = FALSE;
+       stack[stack_ptr].data.str = x;
+       stack_ptr++;
+    }
+}
+
+static inline char *
+spop(void)
+{
+    static char dummy[] = "";  /* avoid const-cast */
+    char *result = dummy;
+    if (stack_ptr > 0) {
+       stack_ptr--;
+       if (!stack[stack_ptr].num_type && stack[stack_ptr].data.str != 0)
+           result = stack[stack_ptr].data.str;
+    }
+    return result;
+}
+
+static inline const char *
+parse_format(const char *s, char *format, int *len)
+{
+    *len = 0;
+    if (format != 0) {
+       grub_bool_t done = FALSE;
+       grub_bool_t allowminus = FALSE;
+       grub_bool_t dot = FALSE;
+       grub_bool_t err = FALSE;
+       char *fmt = format;
+       int my_width = 0;
+       int my_prec = 0;
+       int value = 0;
+
+       *len = 0;
+       *format++ = '%';
+       while (*s != '\0' && !done) {
+           switch (*s) {
+           case 'c':           /* FALLTHRU */
+           case 'd':           /* FALLTHRU */
+           case 'o':           /* FALLTHRU */
+           case 'x':           /* FALLTHRU */
+           case 'X':           /* FALLTHRU */
+           case 's':
+               *format++ = *s;
+               done = TRUE;
+               break;
+           case '.':
+               *format++ = *s++;
+               if (dot) {
+                   err = TRUE;
+               } else {        /* value before '.' is the width */
+                   dot = TRUE;
+                   my_width = value;
+               }
+               value = 0;
+               break;
+           case '#':
+               *format++ = *s++;
+               break;
+           case ' ':
+               *format++ = *s++;
+               break;
+           case ':':
+               s++;
+               allowminus = TRUE;
+               break;
+           case '-':
+               if (allowminus) {
+                   *format++ = *s++;
+               } else {
+                   done = TRUE;
+               }
+               break;
+           default:
+               if (isdigit(UChar(*s))) {
+                   value = (value * 10) + (*s - '0');
+                   if (value > 10000)
+                       err = TRUE;
+                   *format++ = *s++;
+               } else {
+                   done = TRUE;
+               }
+           }
+       }
+
+       /*
+        * If we found an error, ignore (and remove) the flags.
+        */
+       if (err) {
+           my_width = my_prec = value = 0;
+           format = fmt;
+           *format++ = '%';
+           *format++ = *s;
+       }
+
+       /*
+        * Any value after '.' is the precision.  If we did not see '.', then
+        * the value is the width.
+        */
+       if (dot)
+           my_prec = value;
+       else
+           my_width = value;
+
+       *format = '\0';
+       /* return maximum string length in print */
+       *len = (my_width > my_prec) ? my_width : my_prec;
+    }
+    return s;
+}
+
+/*
+ * Analyze the string to see how many parameters we need from the varargs list,
+ * and what their types are.  We will only accept string parameters if they
+ * appear as a %l or %s format following an explicit parameter reference (e.g.,
+ * %p2%s).  All other parameters are numbers.
+ *
+ * 'number' counts coarsely the number of pop's we see in the string, and
+ * 'popcount' shows the highest parameter number in the string.  We would like
+ * to simply use the latter count, but if we are reading termcap strings, there
+ * may be cases that we cannot see the explicit parameter numbers.
+ */
+static inline int
+analyze(const char *string, char *p_is_s[NUM_PARM], int *popcount)
+{
+    grub_size_t len2;
+    int i;
+    int lastpop = -1;
+    int len;
+    int number = 0;
+    const char *cp = string;
+    static char dummy[] = "";
+
+    if (cp == 0)
+       return 0;
+
+    if ((len2 = grub_strlen(cp)) > fmt_size) {
+       fmt_size = len2 + fmt_size + 2;
+       if ((fmt_buff = grub_realloc(fmt_buff, fmt_size*sizeof(char))) == 0)
+             return 0;
+    }
+
+    grub_memset(p_is_s, 0, sizeof(p_is_s[0]) * NUM_PARM);
+    *popcount = 0;
+
+    while ((cp - string) < (int) len2) {
+       if (*cp == '%') {
+           cp++;
+           cp = parse_format(cp, fmt_buff, &len);
+           switch (*cp) {
+           default:
+               break;
+
+           case 'd':           /* FALLTHRU */
+           case 'o':           /* FALLTHRU */
+           case 'x':           /* FALLTHRU */
+           case 'X':           /* FALLTHRU */
+           case 'c':           /* FALLTHRU */
+               if (lastpop <= 0)
+                   number++;
+               lastpop = -1;
+               break;
+
+           case 'l':
+           case 's':
+               if (lastpop > 0)
+                   p_is_s[lastpop - 1] = dummy;
+               ++number;
+               break;
+
+           case 'p':
+               cp++;
+               i = (UChar(*cp) - '0');
+               if (i >= 0 && i <= NUM_PARM) {
+                   lastpop = i;
+                   if (lastpop > *popcount)
+                       *popcount = lastpop;
+               }
+               break;
+
+           case 'P':
+               ++number;
+               ++cp;
+               break;
+
+           case 'g':
+               cp++;
+               break;
+
+           case '\'':
+               cp += 2;
+               lastpop = -1;
+               break;
+
+           case '{':
+               cp++;
+               while (isdigit(UChar(*cp))) {
+                   cp++;
+               }
+               break;
+
+           case '+':
+           case '-':
+           case '*':
+           case '/':
+           case 'm':
+           case 'A':
+           case 'O':
+           case '&':
+           case '|':
+           case '^':
+           case '=':
+           case '<':
+           case '>':
+               lastpop = -1;
+               number += 2;
+               break;
+
+           case '!':
+           case '~':
+               lastpop = -1;
+               ++number;
+               break;
+
+           case 'i':
+               /* will add 1 to first (usually two) parameters */
+               break;
+           }
+       }
+       if (*cp != '\0')
+           cp++;
+    }
+
+    if (number > NUM_PARM)
+       number = NUM_PARM;
+    return number;
+}
+
+static inline char *
+tparam_internal(const char *string, va_list ap)
+{
+    char *p_is_s[NUM_PARM];
+    long param[NUM_PARM];
+    int popcount;
+    int number;
+    int len;
+    int level;
+    int x, y;
+    int i;
+    const char *cp = string;
+    grub_size_t len2;
+    static int dynamic_var[NUM_VARS];
+    static int static_vars[NUM_VARS];
+
+    if (cp == 0)
+       return 0;
+
+    out_used = out_size = fmt_size = 0;
+
+    len2 = (int) grub_strlen(cp);
+
+    /*
+     * Find the highest parameter-number referred to in the format string.
+     * Use this value to limit the number of arguments copied from the
+     * variable-length argument list.
+     */
+    number = analyze(cp, p_is_s, &popcount);
+    if (fmt_buff == 0)
+       return 0;
+
+    for (i = 0; i < max(popcount, number); i++) {
+       /*
+        * A few caps (such as plab_norm) have string-valued parms.
+        * We'll have to assume that the caller knows the difference, since
+        * a char* and an int may not be the same size on the stack.
+        */
+       if (p_is_s[i] != 0) {
+           p_is_s[i] = va_arg(ap, char *);
+       } else {
+           param[i] = va_arg(ap, long int);
+       }
+    }
+
+    /*
+     * This is a termcap compatibility hack.  If there are no explicit pop
+     * operations in the string, load the stack in such a way that
+     * successive pops will grab successive parameters.  That will make
+     * the expansion of (for example) \E[%d;%dH work correctly in termcap
+     * style, which means tparam() will expand termcap strings OK.
+     */
+    stack_ptr = 0;
+    if (popcount == 0) {
+       popcount = number;
+       for (i = number - 1; i >= 0; i--)
+           npush(param[i]);
+    }
+
+    while ((cp - string) < (int) len2) {
+       if (*cp != '%') {
+           save_char(UChar(*cp));
+       } else {
+           tparam_base = cp++;
+           cp = parse_format(cp, fmt_buff, &len);
+           switch (*cp) {
+           default:
+               break;
+           case '%':
+               save_char('%');
+               break;
+
+           case 'd':           /* FALLTHRU */
+           case 'o':           /* FALLTHRU */
+           case 'x':           /* FALLTHRU */
+           case 'X':           /* FALLTHRU */
+               save_number(fmt_buff, npop(), len);
+               break;
+
+           case 'c':           /* FALLTHRU */
+               save_char(npop());
+               break;
+
+           case 'l':
+               save_number("%d", (int) grub_strlen(spop()), 0);
+               break;
+
+           case 's':
+               save_text(fmt_buff, spop(), len);
+               break;
+
+           case 'p':
+               cp++;
+               i = (UChar(*cp) - '1');
+               if (i >= 0 && i < NUM_PARM) {
+                   if (p_is_s[i])
+                       spush(p_is_s[i]);
+                   else
+                       npush(param[i]);
+               }
+               break;
+
+           case 'P':
+               cp++;
+               if (isUPPER(*cp)) {
+                   i = (UChar(*cp) - 'A');
+                   static_vars[i] = npop();
+               } else if (isLOWER(*cp)) {
+                   i = (UChar(*cp) - 'a');
+                   dynamic_var[i] = npop();
+               }
+               break;
+
+           case 'g':
+               cp++;
+               if (isUPPER(*cp)) {
+                   i = (UChar(*cp) - 'A');
+                   npush(static_vars[i]);
+               } else if (isLOWER(*cp)) {
+                   i = (UChar(*cp) - 'a');
+                   npush(dynamic_var[i]);
+               }
+               break;
+
+           case '\'':
+               cp++;
+               npush(UChar(*cp));
+               cp++;
+               break;
+
+           case '{':
+               number = 0;
+               cp++;
+               while (isdigit(UChar(*cp))) {
+                   number = (number * 10) + (UChar(*cp) - '0');
+                   cp++;
+               }
+               npush(number);
+               break;
+
+           case '+':
+               npush(npop() + npop());
+               break;
+
+           case '-':
+               y = npop();
+               x = npop();
+               npush(x - y);
+               break;
+
+           case '*':
+               npush(npop() * npop());
+               break;
+
+           case '/':
+               y = npop();
+               x = npop();
+               npush(y ? (x / y) : 0);
+               break;
+
+           case 'm':
+               y = npop();
+               x = npop();
+               npush(y ? (x % y) : 0);
+               break;
+
+           case 'A':
+               npush(npop() && npop());
+               break;
+
+           case 'O':
+               npush(npop() || npop());
+               break;
+
+           case '&':
+               npush(npop() & npop());
+               break;
+
+           case '|':
+               npush(npop() | npop());
+               break;
+
+           case '^':
+               npush(npop() ^ npop());
+               break;
+
+           case '=':
+               y = npop();
+               x = npop();
+               npush(x == y);
+               break;
+
+           case '<':
+               y = npop();
+               x = npop();
+               npush(x < y);
+               break;
+
+           case '>':
+               y = npop();
+               x = npop();
+               npush(x > y);
+               break;
+
+           case '!':
+               npush(!npop());
+               break;
+
+           case '~':
+               npush(~npop());
+               break;
+
+           case 'i':
+               if (p_is_s[0] == 0)
+                   param[0]++;
+               if (p_is_s[1] == 0)
+                   param[1]++;
+               break;
+
+           case '?':
+               break;
+
+           case 't':
+               x = npop();
+               if (!x) {
+                   /* scan forward for %e or %; at level zero */
+                   cp++;
+                   level = 0;
+                   while (*cp) {
+                       if (*cp == '%') {
+                           cp++;
+                           if (*cp == '?')
+                               level++;
+                           else if (*cp == ';') {
+                               if (level > 0)
+                                   level--;
+                               else
+                                   break;
+                           } else if (*cp == 'e' && level == 0)
+                               break;
+                       }
+
+                       if (*cp)
+                           cp++;
+                   }
+               }
+               break;
+
+           case 'e':
+               /* scan forward for a %; at level zero */
+               cp++;
+               level = 0;
+               while (*cp) {
+                   if (*cp == '%') {
+                       cp++;
+                       if (*cp == '?')
+                           level++;
+                       else if (*cp == ';') {
+                           if (level > 0)
+                               level--;
+                           else
+                               break;
+                       }
+                   }
+
+                   if (*cp)
+                       cp++;
+               }
+               break;
+
+           case ';':
+               break;
+
+           }                   /* endswitch (*cp) */
+       }                       /* endelse (*cp == '%') */
+
+       if (*cp == '\0')
+           break;
+
+       cp++;
+    }                          /* endwhile (*cp) */
+
+    get_space(1);
+    out_buff[out_used] = '\0';
+
+    return (out_buff);
+}
+
+char *
+grub_terminfo_tparm(const char *string,...)
+{
+    va_list ap;
+    char *result;
+
+    va_start(ap, string);
+    result = tparam_internal(string, ap);
+    va_end(ap);
+    return result;
+}
diff -uNr grub2/conf/i386-pc.rmk grub2.serial/conf/i386-pc.rmk
--- grub2/conf/i386-pc.rmk      2005-08-09 17:37:01.000000000 -0600
+++ grub2.serial/conf/i386-pc.rmk       2005-08-09 17:32:00.000000000 -0600
@@ -110,7 +110,8 @@
        font.mod _multiboot.mod ls.mod boot.mod cmp.mod cat.mod         \
        terminal.mod fshelp.mod chain.mod multiboot.mod amiga.mod       \
        apple.mod pc.mod sun.mod loopback.mod reboot.mod halt.mod       \
-       help.mod default.mod timeout.mod configfile.mod terminfo.mod
+       help.mod default.mod timeout.mod configfile.mod terminfo.mod    \
+       serial.mod
 
 # For _chain.mod.
 _chain_mod_SOURCES = loader/i386/pc/chainloader.c
@@ -215,6 +216,10 @@
 terminfo_mod_SOURCES = term/terminfo.c term/tparm.c
 terminfo_mod_CFLAGS = $(COMMON_CFLAGS)
 
+# For serial.mod.
+serial_mod_SOURCES = term/i386/pc/serial.c
+serial_mod_CFLAGS = $(COMMON_CFLAGS)
+
 # For _multiboot.mod.
 _multiboot_mod_SOURCES = loader/i386/pc/multiboot.c
 _multiboot_mod_CFLAGS = $(COMMON_CFLAGS)
diff -uNr grub2/conf/i386-pc.rmk.orig grub2.serial/conf/i386-pc.rmk.orig
--- grub2/conf/i386-pc.rmk.orig 2005-08-09 08:39:50.000000000 -0600
+++ grub2.serial/conf/i386-pc.rmk.orig  2005-08-09 17:31:56.000000000 -0600
@@ -110,7 +110,7 @@
        font.mod _multiboot.mod ls.mod boot.mod cmp.mod cat.mod         \
        terminal.mod fshelp.mod chain.mod multiboot.mod amiga.mod       \
        apple.mod pc.mod sun.mod loopback.mod reboot.mod halt.mod       \
-       help.mod default.mod timeout.mod configfile.mod
+       help.mod default.mod timeout.mod configfile.mod terminfo.mod
 
 # For _chain.mod.
 _chain_mod_SOURCES = loader/i386/pc/chainloader.c
@@ -211,6 +211,10 @@
 font_mod_SOURCES = font/manager.c
 font_mod_CFLAGS = $(COMMON_CFLAGS)
 
+# For terminfo.mod.
+terminfo_mod_SOURCES = term/terminfo.c term/tparm.c
+terminfo_mod_CFLAGS = $(COMMON_CFLAGS)
+
 # For _multiboot.mod.
 _multiboot_mod_SOURCES = loader/i386/pc/multiboot.c
 _multiboot_mod_CFLAGS = $(COMMON_CFLAGS)
diff -uNr grub2/include/grub/i386/pc/serial.h 
grub2.serial/include/grub/i386/pc/serial.h
--- grub2/include/grub/i386/pc/serial.h 1969-12-31 17:00:00.000000000 -0700
+++ grub2.serial/include/grub/i386/pc/serial.h  2005-08-09 17:32:00.000000000 
-0600
@@ -0,0 +1,97 @@
+/* serial.h - serial device interface */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2000,2001,2002,2005  Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#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
+
+/* Read a byte from a port.  */
+static inline unsigned char
+inb (const unsigned short port)
+{
+  unsigned char value;
+
+  asm volatile ("inb    %w1, %0" : "=a" (value) : "Nd" (port));
+  asm volatile ("outb   %%al, $0x80" : : );
+
+  return value;
+}
+
+/* Write a byte to a port.  */
+static inline void
+outb (const unsigned short port, const unsigned char value)
+{
+  asm volatile ("outb   %b0, %w1" : : "a" (value), "Nd" (port));
+  asm volatile ("outb   %%al, $0x80" : : );
+}
+
+/* Return the port number for the UNITth serial device.  */
+static inline unsigned short
+serial_hw_get_port (const unsigned short unit)
+{
+  /* The BIOS data area.  */
+  const unsigned short *addr = (const unsigned short *) 0x0400;
+  return addr[unit];
+}
+
+#endif /* ! GRUB_SERIAL_MACHINE_HEADER */
diff -uNr grub2/term/i386/pc/serial.c grub2.serial/term/i386/pc/serial.c
--- grub2/term/i386/pc/serial.c 1969-12-31 17:00:00.000000000 -0700
+++ grub2.serial/term/i386/pc/serial.c  2005-08-09 17:36:25.000000000 -0600
@@ -0,0 +1,575 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2000,2001,2002,2003,2004,2005  Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#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/normal.h>
+#include <grub/arg.h>
+#include <grub/terminfo.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;
+
+/* Fetch a key.  */
+static int
+serial_hw_fetch (void)
+{
+  if (inb (serial_settings.port + UART_LSR) & UART_DATA_READY)
+    return inb (serial_settings.port + UART_RX);
+
+  return -1;
+}
+
+/* Put a chararacter.  */
+static void
+serial_hw_put (const int c)
+{
+  unsigned int timeout = 100000;
+
+  /* Wait until the transmitter holding register is empty.  */
+  while ((inb (serial_settings.port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0)
+    {
+      if (--timeout == 0)
+        /* There is something wrong. But what can I do?  */
+        return;
+    }
+
+  outb (serial_settings.port + UART_TX, c);
+}
+
+static void
+serial_translate_key_sequence (void)
+{
+  const struct
+  {
+    char key;
+    char ascii;
+  }
+
+  three_code_table[] =
+  {
+    {'A', 16},
+    {'B', 14},
+    {'C', 6},
+    {'D', 2},
+    {'F', 5},
+    {'H', 1},
+    {'4', 4}
+  };
+
+  const struct
+  {
+    short key;
+    char ascii;
+  }
+
+  four_code_table[] =
+  {
+    {('1' | ('~' << 8)), 1},
+    {('3' | ('~' << 8)), 4},
+    {('5' | ('~' << 8)), 7},
+    {('6' | ('~' << 8)), 3}
+  };
+
+  /* The buffer must start with "ESC [".  */
+  if (*((unsigned short *) input_buf) != ('\e' | ('[' << 8)))
+    return;
+
+  if (npending >= 3)
+  {
+    unsigned int i;
+
+    for (i = 0; i < sizeof (three_code_table) / sizeof (three_code_table[0]); 
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)
+  {
+    unsigned int i;
+    short key = *((short *) (input_buf + 2));
+
+    for (i = 0; i < sizeof (four_code_table) / sizeof (four_code_table[0]); 
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 (const 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 interupt.  */
+  outb (serial_settings.port + UART_IER, 0);
+
+  /* Set DLAB.  */
+  outb (serial_settings.port + UART_LCR, UART_DLAB);
+
+  /* Set the baud rate.  */
+  outb (serial_settings.port + UART_DLL, serial_settings.divisor & 0xFF);
+  outb (serial_settings.port + UART_DLH, serial_settings.divisor >> 8 );
+
+  /* Set the line status.  */
+  status |= serial_settings.parity
+          | serial_settings.word_len
+          | serial_settings.stop_bits;
+  outb (serial_settings.port + UART_LCR, status);
+
+  /* Enable the FIFO.  */
+  outb (serial_settings.port + UART_FCR, UART_ENABLE_FIFO);
+
+  /* Turn on DTR, RTS, and OUT2.  */
+  outb (serial_settings.port + UART_MCR, UART_ENABLE_MODEM);
+
+  /* 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:
+          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_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 (const grub_uint8_t x, const 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_setcolor (const grub_uint8_t normal_color    __attribute__ 
((unused)),
+                      const grub_uint8_t highlight_color __attribute__ 
((unused)))
+{
+  /* FIXME */
+}
+
+static void
+grub_serial_setcursor (const int on)
+{
+  if (on)
+    grub_terminfo_cursor_on ();
+  else
+    grub_terminfo_cursor_off ();
+}
+
+static struct grub_term grub_serial_term =
+{
+  .name = "serial",
+  .init = 0,
+  .fini = 0,
+  .putchar = grub_serial_putchar,
+  .checkkey = grub_serial_checkkey,
+  .getkey = grub_serial_getkey,
+  .getwh = grub_serial_getwh,
+  .getxy = grub_serial_getxy,
+  .gotoxy = grub_serial_gotoxy,
+  .cls = grub_serial_cls,
+  .setcolorstate = grub_serial_setcolorstate,
+  .setcolor = grub_serial_setcolor,
+  .setcursor = grub_serial_setcursor,
+  .flags = 0,
+  .next = 0
+};
+
+static grub_err_t
+grub_cmd_serial (struct grub_arg_list *state,
+                 int argc __attribute__ ((unused)),
+                char **args __attribute__ ((unused)))
+{
+  struct serial_port backup_settings = serial_settings;
+  grub_err_t hwiniterr;
+  int arg;
+
+  if (state[0].set)
+  {
+    arg = grub_strtoul (state[0].arg, 0, 0);
+    if (arg >= 0 && arg < 4)
+      serial_settings.port = serial_hw_get_port ((int) arg);
+    else
+      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) {
+    serial_settings.divisor = serial_get_divisor ((unsigned int) grub_strtoul 
(state[2].arg, 0, 0));
+    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.");
+    }
+  }
+
+  hwiniterr = serial_hw_init ();       /* Initialize with new settings.  */
+  if (hwiniterr == GRUB_ERR_NONE)
+  {
+    if (registered == 0)       /* Register terminal if not yet registered.  */
+    {
+      grub_term_register (&grub_serial_term);
+      registered = 1;
+    }
+  }
+  else                         /* Initialization with new settings failed.  */
+    if (registered == 1)       /* If 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 (&grub_serial_term);
+        registered = 0;
+      }
+    }
+  return hwiniterr;
+}
+
+GRUB_MOD_INIT
+{
+  (void)mod;                   /* To stop warning. */
+  grub_register_command ("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
+{
+  grub_unregister_command ("serial");
+  if (registered == 1)         /* Unregister terminal only if registered. */
+    grub_term_unregister (&grub_serial_term);
+}
* term/terminfo.c: Copy from GRUB Legacy.
include/grub/terminfo.h: Likewise
Update for GRUB 2.
Remove unused functions.
Remove dependency on serial module.
Rename functions to match GRUB 2 coding style.
Add preliminary support for multiple terminfo types.
Add GRUB 2 command.

* term/tparm.c: Copy from GRUB Legacy.
include/grub/tparm.h: Likewise
Resync with ncurses-5.4.
Rename exported function to match GRUB 2 coding style

* conf/i386-pc.rmk (pkgdata_MODULES): Added terminfo.mod.
(terminfo_mod_SOURCES): New variable.
(terminfo_mod_CFLAGS): Likewise.
* term/i386/pc/serial.c: Copy from GRUB Legacy.
include/grub/i386/pc/serial.h: Likewise
Update for GRUB 2.
Rename functions to match GRUB 2 coding style.
Restructure code to minimize scopes and simplify functions.
Make assembly functions inline.
Add GRUB 2 command.

* conf/i386-pc.rmk (pkgdata_MODULES): Added serial.mod.
(serial_mod_SOURCES): New variable.
(serial_mod_CFLAGS): Likewise.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]