]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Restructure serial in order to prepare for usbserial. As a byproduct simultaneous...
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sat, 17 Jul 2010 23:37:19 +0000 (01:37 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sat, 17 Jul 2010 23:37:19 +0000 (01:37 +0200)
commands/terminal.c
conf/i386.rmk
include/grub/ns8250.h [new file with mode: 0644]
include/grub/serial.h
include/grub/terminfo.h
include/grub/usb.h
term/ns8250.c [new file with mode: 0644]
term/serial.c
term/terminfo.c
term/usbserial.c [new file with mode: 0644]

index d34602a1b550d980ddd8217e28fe2ef06ef798a4..c8b1b6315ededab5cc84a548eccc1b5b3aa98236 100644 (file)
@@ -59,11 +59,17 @@ handle_command (int argc, char **args, struct abstract_terminal **enabled,
       for (aut = autoloads; aut; aut = aut->next)
        {
          for (term = *disabled; term; term = term->next)
-           if (grub_strcmp (term->name, aut->name) == 0)
+           if (grub_strcmp (term->name, aut->name) == 0
+              || (aut->name[0] && aut->name[grub_strlen (aut->name) - 1] == '*'
+                  && grub_memcmp (term->name, aut->name,
+                                  grub_strlen (aut->name) - 1) == 0))
              break;
          if (!term)
            for (term = *enabled; term; term = term->next)
-             if (grub_strcmp (term->name, aut->name) == 0)
+             if (grub_strcmp (term->name, aut->name) == 0
+                || (aut->name[0] && aut->name[grub_strlen (aut->name) - 1] == '*'
+                    && grub_memcmp (term->name, aut->name,
+                                    grub_strlen (aut->name) - 1) == 0))
                break;
          if (!term)
            grub_printf ("%s ", aut->name);
@@ -98,7 +104,10 @@ handle_command (int argc, char **args, struct abstract_terminal **enabled,
            return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown terminal '%s'\n",
                               args[i]);
          for (aut = autoloads; aut; aut = aut->next)
-           if (grub_strcmp (args[i], aut->name) == 0)
+           if (grub_strcmp (args[i], aut->name) == 0
+              || (aut->name[0] && aut->name[grub_strlen (aut->name) - 1] == '*'
+                  && grub_memcmp (args[i], aut->name,
+                                  grub_strlen (aut->name) - 1) == 0))
              {
                grub_dl_t mod;
                mod = grub_dl_load (aut->modname);
index b1df584a60ef2b357365ff468e4123c46af390aa..6bf1b341075db9e9afe9f329ac88407bf4c956c1 100644 (file)
@@ -56,7 +56,7 @@ multiboot2_mod_ASFLAGS = $(COMMON_ASFLAGS)
 
 # For serial.mod.
 pkglib_MODULES += serial.mod
-serial_mod_SOURCES = term/serial.c
+serial_mod_SOURCES = term/serial.c term/ns8250.c
 serial_mod_CFLAGS = $(COMMON_CFLAGS)
 serial_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
diff --git a/include/grub/ns8250.h b/include/grub/ns8250.h
new file mode 100644 (file)
index 0000000..f21a1a3
--- /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_NS8250_HEADER
+#define GRUB_NS8250_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 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_TRIGGER14     0xC7
+
+/* Enable the FIFO.  */
+#define UART_ENABLE_FIFO_TRIGGER1       0x07
+
+/* Turn on DTR, RTS, and OUT2.  */
+#define UART_ENABLE_DTRRTS     0x03
+
+/* Turn on DTR, RTS, and OUT2.  */
+#define UART_ENABLE_OUT2       0x08
+
+#endif /* ! GRUB_SERIAL_MACHINE_HEADER */
index 758b6fb3ef7905faa16e2c996048a70615892a1d..06328211211b5c473b7215e9e1e10ff594d736ce 100644 (file)
@@ -1,7 +1,7 @@
 /* serial.h - serial device interface */
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2000,2001,2002,2005,2007  Free Software Foundation, Inc.
+ *  Copyright (C) 2010  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
 #ifndef GRUB_SERIAL_HEADER
 #define GRUB_SERIAL_HEADER     1
 
-/* Macros.  */
+#include <grub/types.h>
+#include <grub/cpu/io.h>
+#include <grub/usb.h>
+#include <grub/list.h>
 
-/* 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
+struct grub_serial_port;
 
-/* For LSR bits.  */
-#define UART_DATA_READY                0x01
-#define UART_EMPTY_TRANSMITTER 0x20
+struct grub_serial_driver
+{
+  grub_err_t (*configure) (struct grub_serial_port *port,
+                          unsigned speed,
+                          unsigned short word_len,
+                          unsigned int   parity,
+                          unsigned short stop_bits);
+  int (*fetch) (struct grub_serial_port *port);
+  void (*put) (struct grub_serial_port *port, const int c);
+};
 
-/* The type of parity.  */
-#define UART_NO_PARITY         0x00
-#define UART_ODD_PARITY                0x08
-#define UART_EVEN_PARITY       0x18
+struct grub_serial_port
+{
+  struct grub_serial_port *next;
+  char *name;
+  unsigned speed;
+  unsigned short word_len;
+  unsigned int   parity;
+  unsigned short stop_bits;
+  struct grub_serial_driver *driver;
+  /* This should be void *data but since serial is useful as an early console
+     when malloc isn't available it's a union.
+   */
+  union
+  {
+    grub_port_t port;
+    struct
+    {
+      grub_usb_device_t dev;
+      int in_endp;
+      int out_endp;
+    };
+  };
+};
+
+grub_err_t grub_serial_register (struct grub_serial_port *port);
+
+void grub_serial_unregister (struct grub_serial_port *port);
 
 /* The type of word length.  */
 #define UART_5BITS_WORD        0x00
 #define UART_7BITS_WORD        0x02
 #define UART_8BITS_WORD        0x03
 
+/* The type of parity.  */
+#define UART_NO_PARITY         0x00
+#define UART_ODD_PARITY                0x08
+#define UART_EVEN_PARITY       0x18
+
 /* 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_TRIGGER14     0xC7
-
-/* Enable the FIFO.  */
-#define UART_ENABLE_FIFO_TRIGGER1       0x07
-
-/* Turn on DTR, RTS, and OUT2.  */
-#define UART_ENABLE_DTRRTS     0x03
+static inline void
+grub_serial_fill_defaults (struct grub_serial_port *port)
+{
+  /* Set default settings.  */
+#ifdef GRUB_MACHINE_MIPS_YEELOONG
+  port->speed     = 115200;
+#else
+  port->speed     = 9600;
+#endif
+  port->word_len  = UART_8BITS_WORD;
+  port->parity    = UART_NO_PARITY;
+  port->stop_bits = UART_1_STOP_BIT;
+}
 
-/* Turn on DTR, RTS, and OUT2.  */
-#define UART_ENABLE_OUT2       0x08
+void grub_ns8250_init (void);
+char *grub_serial_ns8250_add_port (grub_port_t port);
 
-#endif /* ! GRUB_SERIAL_MACHINE_HEADER */
+#endif
index d6907d7f00ac639fcc0e2bed934c8bf7617cb996..85ddb5779880d6c17a0190f2493f94bdd80d3fb2 100644 (file)
@@ -32,7 +32,7 @@ struct grub_terminfo_input_state
 {
   int input_buf[GRUB_TERMINFO_READKEY_MAX_LEN];
   int npending;
-  int (*readkey) (void);
+  int (*readkey) (struct grub_term_input *term);
 };
 
 struct grub_terminfo_output_state
@@ -51,7 +51,7 @@ struct grub_terminfo_output_state
 
   unsigned int xpos, ypos;
 
-  void (*put) (const int c);
+  void (*put) (struct grub_term_output *term, const int c);
 };
 
 void EXPORT_FUNC(grub_terminfo_gotoxy) (grub_term_output_t term,
index 3c17318fc2595e1dac39f34f10eafe3a68ae4486..f9c3d61ff7d0e59a47c5721e89a44f20dc557f11 100644 (file)
@@ -19,6 +19,7 @@
 #ifndef        GRUB_USB_H
 #define        GRUB_USB_H      1
 
+#include <grub/err.h>
 #include <grub/usbdesc.h>
 #include <grub/usbtrans.h>
 
diff --git a/term/ns8250.c b/term/ns8250.c
new file mode 100644 (file)
index 0000000..4f09c76
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009,2010  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/memory.h>
+#include <grub/serial.h>
+#include <grub/ns8250.h>
+#include <grub/types.h>
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/cpu/io.h>
+#include <grub/mm.h>
+
+#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
+#include <grub/machine/serial.h>
+static const grub_port_t serial_hw_io_addr[] = GRUB_MACHINE_SERIAL_PORTS;
+#define GRUB_SERIAL_PORT_NUM (ARRAY_SIZE(serial_hw_io_addr))
+#endif
+
+/* Fetch a key.  */
+static int
+serial_hw_fetch (struct grub_serial_port *port)
+{
+  if (grub_inb (port->port + UART_LSR) & UART_DATA_READY)
+    return grub_inb (port->port + UART_RX);
+
+  return -1;
+}
+
+/* Put a character.  */
+static void
+serial_hw_put (struct grub_serial_port *port, const int c)
+{
+  unsigned int timeout = 100000;
+
+  /* Wait until the transmitter holding register is empty.  */
+  while ((grub_inb (port->port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0)
+    {
+      if (--timeout == 0)
+        /* There is something wrong. But what can I do?  */
+        return;
+    }
+
+  grub_outb (c, port->port + UART_TX);
+}
+
+/* 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)
+  /* UART in Yeeloong runs twice the usual rate.  */
+#ifdef GRUB_MACHINE_MIPS_YEELOONG
+      return 2 * divisor_tab[i].div;
+#else
+      return divisor_tab[i].div;
+#endif
+  return 0;
+}
+
+/* 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_configure (struct grub_serial_port *port,
+                    unsigned speed, unsigned short word_len,
+                    unsigned int   parity, unsigned short stop_bits)
+{
+  unsigned char status = 0;
+  unsigned short divisor;
+
+  divisor = serial_get_divisor (speed);
+  if (divisor == 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad speed");
+
+  port->speed = speed;
+  port->word_len = word_len;
+  port->parity = parity;
+  port->stop_bits = stop_bits;
+
+  /* Turn off the interrupt.  */
+  grub_outb (0, port->port + UART_IER);
+
+  /* Set DLAB.  */
+  grub_outb (UART_DLAB, port->port + UART_LCR);
+
+  /* Set the baud rate.  */
+  grub_outb (divisor & 0xFF, port->port + UART_DLL);
+  grub_outb (divisor >> 8, port->port + UART_DLH);
+
+  /* Set the line status.  */
+  status |= (port->parity | port->word_len | port->stop_bits);
+  grub_outb (status, port->port + UART_LCR);
+
+  /* In Yeeloong serial port has only 3 wires.  */
+#ifndef GRUB_MACHINE_MIPS_YEELOONG
+  /* Enable the FIFO.  */
+  grub_outb (UART_ENABLE_FIFO_TRIGGER1, port->port + UART_FCR);
+
+  /* Turn on DTR and RTS.  */
+  grub_outb (UART_ENABLE_DTRRTS, port->port + UART_MCR);
+#else
+  /* Enable the FIFO.  */
+  grub_outb (UART_ENABLE_FIFO_TRIGGER14, port->port + UART_FCR);
+
+  /* Turn on DTR, RTS, and OUT2.  */
+  grub_outb (UART_ENABLE_DTRRTS | UART_ENABLE_OUT2, port->port + UART_MCR);
+#endif
+
+  /* Drain the input buffer.  */
+  while (serial_hw_fetch (port) != -1);
+
+  /*  FIXME: should check if the serial terminal was found.  */
+
+  return GRUB_ERR_NONE;
+}
+
+static struct grub_serial_driver grub_ns8250_driver =
+  {
+    .configure = serial_hw_configure,
+    .fetch = serial_hw_fetch,
+    .put = serial_hw_put
+  };
+
+static char com_names[GRUB_SERIAL_PORT_NUM][20];
+static struct grub_serial_port com_ports[GRUB_SERIAL_PORT_NUM];
+
+void
+grub_ns8250_init (void)
+{
+  int i;
+  for (i = 0; i < GRUB_SERIAL_PORT_NUM; i++)
+    //if (serial_hw_io_addr[i])
+      {
+       grub_snprintf (com_names[i], sizeof (com_names[i]), "com%d", i);
+       com_ports[i].name = com_names[i];
+       com_ports[i].driver = &grub_ns8250_driver;
+       grub_serial_fill_defaults (&com_ports[i]);
+       com_ports[i].port = serial_hw_io_addr[i];
+       grub_serial_register (&com_ports[i]);
+      }
+}
+
+char *
+grub_serial_ns8250_add_port (grub_port_t port)
+{
+  struct grub_serial_port *p;
+  int i;
+  for (i = 0; i < GRUB_SERIAL_PORT_NUM; i++)
+    if (com_ports[i].port == port)
+      return com_names[i];
+  p = grub_malloc (sizeof (*p));
+  if (!p)
+    return NULL;
+  p->name = grub_xasprintf ("port%lx", (unsigned long) port);
+  if (!p->name)
+    {
+      grub_free (p);
+      return NULL;
+    }
+  p->driver = &grub_ns8250_driver;
+  grub_serial_fill_defaults (p);
+  p->port = port;
+  grub_serial_register (p);  
+
+  return p->name;
+}
index cf7759ef2f174cb068289ebe066c3de4cce62bbd..1f54a83ecfa083c3cc234a48ed55be725e76acf5 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009  Free Software Foundation, Inc.
+ *  Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009,2010  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
@@ -16,7 +16,6 @@
  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <grub/machine/memory.h>
 #include <grub/serial.h>
 #include <grub/term.h>
 #include <grub/types.h>
@@ -26,8 +25,9 @@
 #include <grub/cpu/io.h>
 #include <grub/extcmd.h>
 #include <grub/i18n.h>
+#include <grub/list.h>
 
-static unsigned int registered = 0;
+#define FOR_SERIAL_PORTS(var) FOR_LIST_ELEMENTS((var), (grub_serial_ports))
 
 /* Argument options.  */
 static const struct grub_arg_option options[] =
@@ -41,173 +41,60 @@ static const struct grub_arg_option options[] =
   {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
-#include <grub/machine/serial.h>
-static const grub_port_t serial_hw_io_addr[] = GRUB_MACHINE_SERIAL_PORTS;
-#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;
-}
+struct grub_serial_port *grub_serial_ports;
 
-/* Fetch a key.  */
-static int
-serial_hw_fetch (void)
+struct grub_serial_output_state
 {
-  if (grub_inb (serial_settings.port + UART_LSR) & UART_DATA_READY)
-    return grub_inb (serial_settings.port + UART_RX);
-
-  return -1;
-}
+  struct grub_terminfo_output_state tinfo;
+  struct grub_serial_port *port;
+};
 
-/* Put a character.  */
-static void
-serial_hw_put (const int c)
+struct grub_serial_input_state
 {
-  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);
-}
+  struct grub_terminfo_input_state tinfo;
+  struct grub_serial_port *port;
+};
 
-/* Convert speed to divisor.  */
-static unsigned short
-serial_get_divisor (unsigned int speed)
+static grub_uint16_t
+grub_serial_getwh (struct grub_term_output *term __attribute__ ((unused)))
 {
-  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)
-  /* UART in Yeeloong runs twice the usual rate.  */
-#ifdef GRUB_MACHINE_MIPS_YEELOONG
-      return 2 * divisor_tab[i].div;
-#else
-      return divisor_tab[i].div;
-#endif
-  return 0;
+  const grub_uint8_t TEXT_WIDTH = 80;
+  const grub_uint8_t TEXT_HEIGHT = 24;
+  return (TEXT_WIDTH << 8) | TEXT_HEIGHT;
 }
 
-/* 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)
+static void 
+serial_put (grub_term_output_t term, const int c)
 {
-  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);
-
-  /* In Yeeloong serial port has only 3 wires.  */
-#ifndef GRUB_MACHINE_MIPS_YEELOONG
-  /* Enable the FIFO.  */
-  grub_outb (UART_ENABLE_FIFO_TRIGGER1, serial_settings.port + UART_FCR);
-
-  /* Turn on DTR and RTS.  */
-  grub_outb (UART_ENABLE_DTRRTS, serial_settings.port + UART_MCR);
-#else
-  /* Enable the FIFO.  */
-  grub_outb (UART_ENABLE_FIFO_TRIGGER14, serial_settings.port + UART_FCR);
-
-  /* Turn on DTR, RTS, and OUT2.  */
-  grub_outb (UART_ENABLE_DTRRTS | UART_ENABLE_OUT2,
-            serial_settings.port + UART_MCR);
-#endif
-
-  /* Drain the input buffer.  */
-  while (serial_hw_fetch () != -1);
-
-  /*  FIXME: should check if the serial terminal was found.  */
-
-  return GRUB_ERR_NONE;
+  struct grub_serial_output_state *data = term->data;
+  data->port->driver->put (data->port, c);
 }
 
-static grub_uint16_t
-grub_serial_getwh (struct grub_term_output *term __attribute__ ((unused)))
+static int
+serial_fetch (grub_term_input_t term)
 {
-  const grub_uint8_t TEXT_WIDTH = 80;
-  const grub_uint8_t TEXT_HEIGHT = 24;
-  return (TEXT_WIDTH << 8) | TEXT_HEIGHT;
+  struct grub_serial_input_state *data = term->data;
+  return data->port->driver->fetch (data->port);
 }
 
-struct grub_terminfo_input_state grub_serial_terminfo_input =
+struct grub_serial_input_state grub_serial_terminfo_input =
   {
-    .readkey = serial_hw_fetch
+    .tinfo =
+    {
+      .readkey = serial_fetch
+    }
   };
 
-struct grub_terminfo_output_state grub_serial_terminfo_output =
+struct grub_serial_output_state grub_serial_terminfo_output =
   {
-    .put = serial_hw_put
+    .tinfo =
+    {
+      .put = serial_put
+    }
   };
 
+int registered = 0;
+
 static struct grub_term_input grub_serial_term_input =
 {
   .name = "serial",
@@ -235,122 +122,200 @@ static struct grub_term_output grub_serial_term_output =
 
 \f
 
+static struct grub_serial_port *
+grub_serial_find (char *name)
+{
+  struct grub_serial_port *port;
+
+  FOR_SERIAL_PORTS (port)
+    if (grub_strcmp (port->name, name) == 0)
+      break;
+
+  if (!port && grub_memcmp (name, "port", sizeof ("port") - 1) == 0
+      && grub_isdigit (name [sizeof ("port") - 1]))
+    {
+      name = grub_serial_ns8250_add_port (grub_strtoul (&name[sizeof ("port") - 1],
+                                                       0, 16));
+      if (!name)
+       return NULL;
+
+      FOR_SERIAL_PORTS (port)
+       if (grub_strcmp (port->name, name) == 0)
+         break;
+    }
+
+  return port;
+}
+
 static grub_err_t
-grub_cmd_serial (grub_extcmd_t cmd,
-                 int argc __attribute__ ((unused)),
-                char **args __attribute__ ((unused)))
+grub_cmd_serial (grub_extcmd_t cmd, int argc, char **args)
 {
   struct grub_arg_list *state = cmd->state;
-  struct serial_port backup_settings = serial_settings;
-  grub_err_t hwiniterr;
+  char pname[40];
+  char *name = NULL;
+  struct grub_serial_port *port;
+  signed speed = -1;
+  signed short word_len = -1;
+  signed int   parity = -1;
+  signed short stop_bits = -1;
+  grub_err_t err;
 
   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");
+      grub_snprintf (pname, sizeof (pname), "com%ld",
+                    grub_strtoul (state[0].arg, 0, 0));
+      name = pname;
     }
 
   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");
-       }
+      grub_snprintf (pname, sizeof (pname), "port%lx",
+                    grub_strtoul (state[1].arg, 0, 0));
+      name = pname;
     }
 
+  if (argc >= 1)
+    name = args[0];
+
+  if (!name)
+    name = "com0";
+
+  port = grub_serial_find (name);
+  if (!port)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown serial port");
+
+  speed = port->speed;
+  word_len = port->word_len;
+  parity = port->parity;
+  stop_bits = port->stop_bits;
+
+  if (state[2].set)
+    speed = grub_strtoul (state[2].arg, 0, 0);
+
   if (state[3].set)
     {
       if (! grub_strcmp (state[3].arg, "5"))
-       serial_settings.word_len = UART_5BITS_WORD;
+       word_len = UART_5BITS_WORD;
       else if (! grub_strcmp (state[3].arg, "6"))
-       serial_settings.word_len = UART_6BITS_WORD;
+       word_len = UART_6BITS_WORD;
       else if (! grub_strcmp (state[3].arg, "7"))
-       serial_settings.word_len = UART_7BITS_WORD;
+       word_len = UART_7BITS_WORD;
       else if (! grub_strcmp (state[3].arg, "8"))
-       serial_settings.word_len = UART_8BITS_WORD;
+       word_len = UART_8BITS_WORD;
       else
-       {
-         serial_settings = backup_settings;
-         return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad word length");
-       }
+       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;
+       parity = UART_NO_PARITY;
       else if (! grub_strcmp (state[4].arg, "odd"))
-       serial_settings.parity = UART_ODD_PARITY;
+       parity = UART_ODD_PARITY;
       else if (! grub_strcmp (state[4].arg, "even"))
-       serial_settings.parity = UART_EVEN_PARITY;
+       parity = UART_EVEN_PARITY;
       else
-       {
-         serial_settings = backup_settings;
-         return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad parity");
-       }
+       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;
+       stop_bits = UART_1_STOP_BIT;
       else if (! grub_strcmp (state[5].arg, "2"))
-       serial_settings.stop_bits = UART_2_STOP_BITS;
+       stop_bits = UART_2_STOP_BITS;
       else
-       {
-         serial_settings = backup_settings;
-         return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad number of stop bits");
-       }
+       return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad number of stop bits");
     }
 
   /* Initialize with new settings.  */
-  hwiniterr = serial_hw_init ();
+  err = port->driver->configure (port, speed, word_len, parity, stop_bits);
+  if (err)
+    return err;
+  if (!registered)
+    {
+      grub_term_register_input ("serial", &grub_serial_term_input);
+      grub_term_register_output ("serial", &grub_serial_term_output);
+    }
+  grub_serial_terminfo_output.port = port;
+  grub_serial_terminfo_input.port = port;
+  registered = 1;
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_serial_register (struct grub_serial_port *port)
+{
+  struct grub_term_input *in;
+  struct grub_term_output *out;
+  struct grub_serial_input_state *indata;
+  struct grub_serial_output_state *outdata;
 
-  if (hwiniterr == GRUB_ERR_NONE)
+  in = grub_malloc (sizeof (*in));
+  if (!in)
+    return grub_errno;
+
+  indata = grub_malloc (sizeof (*indata));
+  if (!indata)
     {
-      /* 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);
-         grub_terminfo_output_register (&grub_serial_term_output, "vt100");
-         registered = 1;
-       }
+      grub_free (in);
+      return grub_errno;
     }
-  else
+
+  grub_memcpy (in, &grub_serial_term_input, sizeof (*in));
+  in->data = indata;
+  in->name = grub_xasprintf ("serial_%s", port->name);
+  grub_memcpy (indata, &grub_serial_terminfo_input, sizeof (*indata));
+
+  if (!in->name)
+    {
+      grub_free (in);
+      grub_free (indata);
+      return grub_errno;
+    }
+  
+  out = grub_malloc (sizeof (*out));
+  if (!out)
+    {
+      grub_free (in);
+      grub_free (indata);
+      grub_free ((char *) in->name);
+      return grub_errno;
+    }
+
+  outdata = grub_malloc (sizeof (*outdata));
+  if (!outdata)
     {
-      /* 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);
-             grub_terminfo_output_unregister (&grub_serial_term_output);
-             registered = 0;
-           }
-       }
+      grub_free (in);
+      grub_free (indata);
+      grub_free ((char *) in->name);
+      grub_free (out);
+      return grub_errno;
     }
 
-  return hwiniterr;
+  grub_memcpy (out, &grub_serial_term_output, sizeof (*out));
+  out->data = outdata;
+  out->name = in->name;
+  grub_memcpy (outdata, &grub_serial_terminfo_output, sizeof (*outdata));
+
+  grub_list_push (GRUB_AS_LIST_P (&grub_serial_ports), GRUB_AS_LIST (port));
+  ((struct grub_serial_input_state *) in->data)->port = port;
+  ((struct grub_serial_output_state *) out->data)->port = port;
+  grub_term_register_input ("serial_*", in);
+  grub_term_register_output ("serial_*", out);
+  grub_terminfo_output_register (out, "vt100");
+
+  return GRUB_ERR_NONE;
+}
+
+void
+grub_serial_unregister (struct grub_serial_port *port)
+{
+  grub_list_remove (GRUB_AS_LIST_P (&grub_serial_ports), GRUB_AS_LIST (port));
+  /* FIXME */
 }
 
+
 static grub_extcmd_t cmd;
 
 GRUB_MOD_INIT(serial)
@@ -360,21 +325,13 @@ GRUB_MOD_INIT(serial)
                              N_("[OPTIONS...]"),
                              N_("Configure serial port."), options);
 
-  /* Set default settings.  */
-  serial_settings.port      = serial_hw_get_port (0);
-#ifdef GRUB_MACHINE_MIPS_YEELOONG
-  serial_settings.divisor   = serial_get_divisor (115200);
-#else
-  serial_settings.divisor   = serial_get_divisor (9600);
-#endif
-  serial_settings.word_len  = UART_8BITS_WORD;
-  serial_settings.parity    = UART_NO_PARITY;
-  serial_settings.stop_bits = UART_1_STOP_BIT;
+  grub_ns8250_init ();
 
 #ifdef GRUB_MACHINE_MIPS_YEELOONG
   {
     grub_err_t hwiniterr;
-    hwiniterr = serial_hw_init ();
+    hwiniterr = grub_ns8250_driver.init ("com0", &serial_settings);
+    serial_settings.driver = &grub_ns8250_driver;
 
     if (hwiniterr == GRUB_ERR_NONE)
       {
@@ -389,11 +346,7 @@ GRUB_MOD_INIT(serial)
 
 GRUB_MOD_FINI(serial)
 {
+  while (grub_serial_ports)
+    grub_serial_unregister (grub_serial_ports);
   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);
-      grub_terminfo_output_unregister (&grub_serial_term_output);
-    }
 }
index ff54e5dba38e5a5061fb352db8c3660ba6c3348f..5691b9cc681291abc84fcd7f4388a64ff5d624ca 100644 (file)
@@ -195,7 +195,7 @@ putstr (struct grub_term_output *term, const char *str)
   struct grub_terminfo_output_state *data
     = (struct grub_terminfo_output_state *) term->data;
   while (*str)
-    data->put (*str++);
+    data->put (term, *str++);
 }
 
 grub_uint16_t
@@ -225,7 +225,7 @@ grub_terminfo_gotoxy (struct grub_term_output *term,
   else
     {
       if ((y == data->ypos) && (x == data->xpos - 1))
-       data->put ('\b');
+       data->put (term, '\b');
     }
 
   data->xpos = x;
@@ -348,20 +348,21 @@ grub_terminfo_putchar (struct grub_term_output *term,
          data->xpos = 0;
          if (data->ypos < grub_term_height (term) - 1)
            data->ypos++;
-         data->put ('\r');
-         data->put ('\n');
+         data->put (term, '\r');
+         data->put (term, '\n');
        }
       data->xpos += c->estimated_width;
       break;
     }
 
-  data->put (c->base);
+  data->put (term, c->base);
 }
 
 #define ANSI_C0 0x9b
 
 static void
-grub_terminfo_readkey (int *keys, int *len, int (*readkey) (void))
+grub_terminfo_readkey (struct grub_term_input *term, int *keys, int *len,
+                      int (*readkey) (struct grub_term_input *term))
 {
   int c;
 
@@ -371,7 +372,7 @@ grub_terminfo_readkey (int *keys, int *len, int (*readkey) (void))
     /* On 9600 we have to wait up to 12 milliseconds.  */      \
     start = grub_get_time_ms ();                               \
     do                                                         \
-      c = readkey ();                                          \
+      c = readkey (term);                                      \
     while (c == -1 && grub_get_time_ms () - start < 12);       \
     if (c == -1)                                               \
       return;                                                  \
@@ -380,7 +381,7 @@ grub_terminfo_readkey (int *keys, int *len, int (*readkey) (void))
     (*len)++;                                                  \
   }
 
-  c = readkey ();
+  c = readkey (term);
   if (c < 0)
     {
       *len = 0;
@@ -475,7 +476,8 @@ grub_terminfo_checkkey (struct grub_term_input *termi)
   if (data->npending)
     return data->input_buf[0];
 
-  grub_terminfo_readkey (data->input_buf, &data->npending, data->readkey);
+  grub_terminfo_readkey (termi, data->input_buf,
+                        &data->npending, data->readkey);
 
   if (data->npending)
     return data->input_buf[0];
@@ -491,7 +493,8 @@ grub_terminfo_getkey (struct grub_term_input *termi)
     = (struct grub_terminfo_input_state *) (termi->data);
   int ret;
   while (! data->npending)
-    grub_terminfo_readkey (data->input_buf, &data->npending, data->readkey);
+    grub_terminfo_readkey (termi, data->input_buf, &data->npending,
+                          data->readkey);
 
   ret = data->input_buf[0];
   data->npending--;
diff --git a/term/usbserial.c b/term/usbserial.c
new file mode 100644 (file)
index 0000000..5a63348
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ *  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/memory.h>
+#include <grub/serial.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>
+#include <grub/i18n.h>
+
+static unsigned int registered = 0;
+
+/* Argument options.  */
+static const struct grub_arg_option options[] =
+{
+  {"unit",   'u', 0, N_("Set the serial unit."),             0, ARG_TYPE_INT},
+  {"speed",  's', 0, N_("Set the serial port speed."),       0, ARG_TYPE_INT},
+  {"word",   'w', 0, N_("Set the serial port word length."), 0, ARG_TYPE_INT},
+  {"parity", 'r', 0, N_("Set the serial port parity."),      0, ARG_TYPE_STRING},
+  {"stop",   't', 0, N_("Set the serial port stop bits."),   0, ARG_TYPE_INT},
+  {0, 0, 0, 0, 0, 0}
+};
+
+/* Serial port settings.  */
+struct usbserial_port
+{
+  grub_usb_device_t usbdev;
+  int in_endp;
+  int out_endp;
+  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)
+{
+  /* FIXME */
+  return -1;
+}
+
+/* Put a character.  */
+static void
+usbserial_hw_put (struct usbserial_port *port, const int c)
+{
+  char cc = c;
+  grub_usb_bulk_write (port->usbdev, port->out_endp, 1, &cc);
+}
+
+static grub_uint16_t
+grub_serial_getwh (struct grub_term_output *term __attribute__ ((unused)))
+{
+  const grub_uint8_t TEXT_WIDTH = 80;
+  const grub_uint8_t TEXT_HEIGHT = 24;
+  return (TEXT_WIDTH << 8) | TEXT_HEIGHT;
+}
+
+struct grub_terminfo_input_state grub_serial_terminfo_input =
+  {
+    .readkey = serial_hw_fetch
+  };
+
+struct grub_terminfo_output_state grub_serial_terminfo_output =
+  {
+    .put = usbserial_hw_put
+  };
+
+static struct grub_term_input grub_serial_term_input =
+{
+  .name = "usbserial",
+  .init = grub_terminfo_input_init,
+  .checkkey = grub_terminfo_checkkey,
+  .getkey = grub_terminfo_getkey,
+  .data = &grub_serial_terminfo_input
+};
+
+static struct grub_term_output grub_serial_term_output =
+{
+  .name = "usbserial",
+  .putchar = grub_terminfo_putchar,
+  .getwh = grub_serial_getwh,
+  .getxy = grub_terminfo_getxy,
+  .gotoxy = grub_terminfo_gotoxy,
+  .cls = grub_terminfo_cls,
+  .setcolorstate = grub_terminfo_setcolorstate,
+  .setcursor = grub_terminfo_setcursor,
+  .flags = GRUB_TERM_CODE_TYPE_ASCII,
+  .data = &grub_serial_terminfo_output,
+  .normal_color = GRUB_TERM_DEFAULT_NORMAL_COLOR,
+  .highlight_color = GRUB_TERM_DEFAULT_HIGHLIGHT_COLOR,
+};
+
+\f
+
+
+static grub_extcmd_t cmd;
+
+GRUB_MOD_INIT(serial)
+{
+  cmd = grub_register_extcmd ("serial", grub_cmd_serial,
+                             GRUB_COMMAND_FLAG_BOTH,
+                             N_("[OPTIONS...]"),
+                             N_("Configure serial port."), options);
+
+  /* Set default settings.  */
+  serial_settings.port      = serial_hw_get_port (0);
+#ifdef GRUB_MACHINE_MIPS_YEELOONG
+  serial_settings.divisor   = serial_get_divisor (115200);
+#else
+  serial_settings.divisor   = serial_get_divisor (9600);
+#endif
+  serial_settings.word_len  = UART_8BITS_WORD;
+  serial_settings.parity    = UART_NO_PARITY;
+  serial_settings.stop_bits = UART_1_STOP_BIT;
+
+#ifdef GRUB_MACHINE_MIPS_YEELOONG
+  {
+    grub_err_t hwiniterr;
+    hwiniterr = serial_hw_init ();
+
+    if (hwiniterr == GRUB_ERR_NONE)
+      {
+       grub_term_register_input_active ("serial", &grub_serial_term_input);
+       grub_term_register_output_active ("serial", &grub_serial_term_output);
+
+       registered = 1;
+      }
+  }
+#endif
+}
+
+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);
+      grub_terminfo_output_unregister (&grub_serial_term_output);
+    }
+}