]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
ESCC serial driver for conducting sautomated tests in qemu.
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Fri, 8 Jun 2012 17:42:59 +0000 (19:42 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Fri, 8 Jun 2012 17:42:59 +0000 (19:42 +0200)
Not tested on real hardware.

* include/grub/serial.h (grub_serial_port): New field escc_desc.
* grub-core/term/ieee1275/escc.c: New file.
* grub-core/Makefile.core.def (escc): New module.

ChangeLog
grub-core/Makefile.core.def
grub-core/term/ieee1275/escc.c [new file with mode: 0644]
include/grub/serial.h

index aa2e076977aab3151c4d4b5f08fcfab392095bd3..a8b8374b74c474e5ee5a74e7ad4f802dd077a3fe 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2012-06-08  Vladimir Serbinenko  <phcoder@gmail.com>
+
+       ESCC serial driver for conducting sautomated tests in qemu.
+       Not tested on real hardware.
+
+       * include/grub/serial.h (grub_serial_port): New field escc_desc.
+       * grub-core/term/ieee1275/escc.c: New file.
+       * grub-core/Makefile.core.def (escc): New module.
+
 2012-06-08  Vladimir Serbinenko  <phcoder@gmail.com>
 
        * grub-core/term/ieee1275/serial.c (do_real_config): Set handle to
index 62b9ef25ff619ea159eb4fc99a73e45bafc584e4..18ace2f5390d77468af6c0e8e6cf58d4fb4f3593 100644 (file)
@@ -815,6 +815,12 @@ module = {
   enable = powerpc_ieee1275;
 };
 
+module = {
+  name = escc;
+  ieee1275 = term/ieee1275/escc.c;
+  enable = powerpc_ieee1275;
+};
+
 module = {
   name = terminal;
   common = commands/terminal.c;
diff --git a/grub-core/term/ieee1275/escc.c b/grub-core/term/ieee1275/escc.c
new file mode 100644 (file)
index 0000000..6d7b636
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2012  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/serial.h>
+#include <grub/types.h>
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/time.h>
+#include <grub/i18n.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+struct grub_escc_descriptor
+{
+  volatile grub_uint8_t *escc_ctrl;
+  volatile grub_uint8_t *escc_data;
+};
+
+static void
+do_real_config (struct grub_serial_port *port)
+{
+  grub_uint8_t bitsspec;
+  grub_uint8_t parity_stop_spec;
+  if (port->configured)
+    return;
+
+  /* Make sure the port is waiting for address now.  */
+  (void) *port->escc_desc->escc_ctrl;
+  switch (port->config.speed)
+    {
+    case 57600:
+      *port->escc_desc->escc_ctrl = 13;
+      *port->escc_desc->escc_ctrl = 0;
+      *port->escc_desc->escc_ctrl = 12;
+      *port->escc_desc->escc_ctrl = 0;
+      *port->escc_desc->escc_ctrl = 14;
+      *port->escc_desc->escc_ctrl = 1;
+      *port->escc_desc->escc_ctrl = 11;
+      *port->escc_desc->escc_ctrl = 0x50;
+      break;
+    case 38400:
+      *port->escc_desc->escc_ctrl = 13;
+      *port->escc_desc->escc_ctrl = 0;
+      *port->escc_desc->escc_ctrl = 12;
+      *port->escc_desc->escc_ctrl = 1;
+      *port->escc_desc->escc_ctrl = 14;
+      *port->escc_desc->escc_ctrl = 1;
+      *port->escc_desc->escc_ctrl = 11;
+      *port->escc_desc->escc_ctrl = 0x50;
+      break;
+    }
+
+  parity_stop_spec = 0;
+  switch (port->config.parity)
+    {
+    case GRUB_SERIAL_PARITY_NONE:
+      parity_stop_spec |= 0;
+      break;
+    case GRUB_SERIAL_PARITY_ODD:
+      parity_stop_spec |= 1;
+      break;
+    case GRUB_SERIAL_PARITY_EVEN:
+      parity_stop_spec |= 3;
+      break;
+    }
+
+  switch (port->config.stop_bits)
+    {
+    case GRUB_SERIAL_STOP_BITS_1:
+      parity_stop_spec |= 0x4;
+      break;
+    case GRUB_SERIAL_STOP_BITS_1_5:
+      parity_stop_spec |= 0x8;
+      break;
+    case GRUB_SERIAL_STOP_BITS_2:
+      parity_stop_spec |= 0xc;
+      break;      
+    }
+
+  *port->escc_desc->escc_ctrl = 4;
+  *port->escc_desc->escc_ctrl = 0x40 | parity_stop_spec;
+
+  bitsspec = port->config.word_len - 5;
+  bitsspec = ((bitsspec >> 1) | (bitsspec << 1)) & 3;
+
+  *port->escc_desc->escc_ctrl = 3;
+  *port->escc_desc->escc_ctrl = (bitsspec << 6) | 0x1;
+
+  port->configured = 1;
+
+  return;
+}
+
+/* Fetch a key.  */
+static int
+serial_hw_fetch (struct grub_serial_port *port)
+{
+  do_real_config (port);
+
+  *port->escc_desc->escc_ctrl = 0;
+  if (*port->escc_desc->escc_ctrl & 1)
+    return *port->escc_desc->escc_data;
+  return -1;
+}
+
+/* Put a character.  */
+static void
+serial_hw_put (struct grub_serial_port *port, const int c)
+{
+  grub_uint64_t endtime;
+
+  do_real_config (port);
+
+  if (port->broken > 5)
+    endtime = grub_get_time_ms ();
+  else if (port->broken > 1)
+    endtime = grub_get_time_ms () + 50;
+  else
+    endtime = grub_get_time_ms () + 200;
+  /* Wait until the transmitter holding register is empty.  */
+  while (1)
+    {
+      *port->escc_desc->escc_ctrl = 0;
+      if (*port->escc_desc->escc_ctrl & 4)
+       break;
+      if (grub_get_time_ms () > endtime)
+       {
+         port->broken++;
+         /* There is something wrong. But what can I do?  */
+         return;
+       }
+    }
+
+  if (port->broken)
+    port->broken--;
+
+  *port->escc_desc->escc_data = 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_configure (struct grub_serial_port *port __attribute__ ((unused)),
+                    struct grub_serial_config *config __attribute__ ((unused)))
+{
+  if (config->speed != 38400 && config->speed != 57600)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                      N_("unsupported serial port speed"));
+
+  if (config->parity != GRUB_SERIAL_PARITY_NONE
+      && config->parity != GRUB_SERIAL_PARITY_ODD
+      && config->parity != GRUB_SERIAL_PARITY_EVEN)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                      N_("unsupported serial port parity"));
+
+  if (config->stop_bits != GRUB_SERIAL_STOP_BITS_1
+      && config->stop_bits != GRUB_SERIAL_STOP_BITS_1_5
+      && config->stop_bits != GRUB_SERIAL_STOP_BITS_2)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                      N_("unsupported serial port stop bits number"));
+
+  if (config->word_len < 5 || config->word_len > 8)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                      N_("unsupported serial port word length"));
+
+  port->config = *config;
+  port->configured = 0;
+
+  /*  FIXME: should check if the serial terminal was found.  */
+
+  return GRUB_ERR_NONE;
+}
+
+struct grub_serial_driver grub_escc_driver =
+  {
+    .configure = serial_hw_configure,
+    .fetch = serial_hw_fetch,
+    .put = serial_hw_put
+  };
+
+static struct grub_escc_descriptor escc_descs[2];
+
+static void
+add_device (grub_addr_t addr, int channel)
+{
+  struct grub_serial_port *port;
+  grub_err_t err;
+  struct grub_serial_config config =
+    {
+      .speed = 38400,
+      .word_len = 8,
+      .parity = GRUB_SERIAL_PARITY_NONE,
+      .stop_bits = GRUB_SERIAL_STOP_BITS_1
+    };
+
+  escc_descs[channel].escc_ctrl
+    = (volatile grub_uint8_t *) (grub_addr_t) addr;
+  escc_descs[channel].escc_data = escc_descs[channel].escc_ctrl + 16;
+
+  port = grub_zalloc (sizeof (*port));
+  if (!port)
+    {
+      grub_errno = 0;
+      return;
+    }
+
+  port->name = grub_xasprintf ("escc-ch-%c", channel + 'a');
+  if (!port->name)
+    {
+      grub_errno = 0;
+      return;
+    }
+
+  port->escc_desc = &escc_descs[channel];
+
+  port->driver = &grub_escc_driver;
+
+  err = port->driver->configure (port, &config);
+  if (err)
+    grub_print_error ();
+
+  grub_serial_register (port);
+}
+
+GRUB_MOD_INIT (escc)
+{
+  char *macio = 0;
+  char *escc = 0;
+  grub_uint32_t macio_addr[4];
+  grub_uint32_t escc_addr[2];
+  grub_ieee1275_phandle_t dev;
+
+  auto int find_macio (struct grub_ieee1275_devalias *alias);
+  auto int find_escc (struct grub_ieee1275_devalias *alias);
+
+  int find_macio (struct grub_ieee1275_devalias *alias)
+    {
+      if (grub_strcmp (alias->type, "mac-io") != 0)
+       return 0;
+      macio = grub_strdup (alias->path);
+      return 1;
+    }
+
+  int find_escc (struct grub_ieee1275_devalias *alias)
+    {
+      if (grub_strcmp (alias->type, "escc") != 0)
+       return 0;
+      escc = grub_strdup (alias->path);
+      return 1;
+    }
+
+  grub_ieee1275_devices_iterate (find_macio);
+  if (!macio)
+    return;
+
+  grub_children_iterate (macio, find_escc);
+  if (!escc)
+    {
+      grub_free (macio);
+      return;
+    }
+
+  if (grub_ieee1275_finddevice (macio, &dev))
+    {
+      grub_free (macio);
+      grub_free (escc);
+      return;
+    }
+  if (grub_ieee1275_get_integer_property (dev, "assigned-addresses",
+                                         macio_addr, sizeof (macio_addr), 0))
+    {
+      grub_free (macio);
+      grub_free (escc);
+      return;
+    }
+
+  if (grub_ieee1275_finddevice (escc, &dev))
+    {
+      grub_free (macio);
+      grub_free (escc);
+      return;
+    }
+
+  if (grub_ieee1275_get_integer_property (dev, "reg",
+                                         escc_addr, sizeof (escc_addr), 0))
+    {
+      grub_free (macio);
+      grub_free (escc);
+      return;
+    }
+
+  add_device (macio_addr[2] + escc_addr[0] + 32, 1);
+  add_device (macio_addr[2] + escc_addr[0], 0);
+
+  grub_free (macio);
+  grub_free (escc);
+}
+
+GRUB_MOD_FINI (escc)
+{
+}
index d68a255ebc8091e7610550f19d0525c1c676c4c5..32f507c6e1d0360038982fcec4eb8737075a6678 100644 (file)
@@ -94,6 +94,7 @@ struct grub_serial_port
       struct grub_usb_desc_endp *in_endp;
       struct grub_usb_desc_endp *out_endp;
     };
+    struct grub_escc_descriptor *escc_desc;
 #ifdef GRUB_MACHINE_IEEE1275
     struct
     {