summaryrefslogtreecommitdiffstats
path: root/grub-core/term
diff options
context:
space:
mode:
Diffstat (limited to 'grub-core/term')
-rw-r--r--grub-core/term/arc/console.c209
-rw-r--r--grub-core/term/arc/serial.c147
-rw-r--r--grub-core/term/arm/cros.c125
-rw-r--r--grub-core/term/arm/cros_ec.c238
-rw-r--r--grub-core/term/arm/pl050.c189
-rw-r--r--grub-core/term/at_keyboard.c332
-rw-r--r--grub-core/term/efi/console.c470
-rw-r--r--grub-core/term/efi/serial.c195
-rw-r--r--grub-core/term/gfxterm.c1160
-rw-r--r--grub-core/term/gfxterm_background.c190
-rw-r--r--grub-core/term/i386/coreboot/cbmemc.c147
-rw-r--r--grub-core/term/i386/pc/console.c310
-rw-r--r--grub-core/term/i386/pc/mda_text.c13
-rw-r--r--grub-core/term/i386/pc/vga_text.c288
-rw-r--r--grub-core/term/ieee1275/console.c267
-rw-r--r--grub-core/term/ieee1275/escc.c319
-rw-r--r--grub-core/term/ieee1275/serial.c287
-rw-r--r--grub-core/term/morse.c133
-rw-r--r--grub-core/term/ns8250.c318
-rw-r--r--grub-core/term/ps2.c387
-rw-r--r--grub-core/term/serial.c463
-rw-r--r--grub-core/term/spkmodem.c141
-rw-r--r--grub-core/term/terminfo.c796
-rw-r--r--grub-core/term/tparm.c767
-rw-r--r--grub-core/term/uboot/console.c132
-rw-r--r--grub-core/term/usb_keyboard.c471
-rw-r--r--grub-core/term/xen/console.c122
27 files changed, 8616 insertions, 0 deletions
diff --git a/grub-core/term/arc/console.c b/grub-core/term/arc/console.c
new file mode 100644
index 0000000..c944ffc
--- /dev/null
+++ b/grub-core/term/arc/console.c
@@ -0,0 +1,209 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2011 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/arc/arc.h>
+#include <grub/arc/console.h>
+#include <grub/term.h>
+#include <grub/terminfo.h>
+
+/* FIXME: use unicode. */
+
+static int
+readkey (struct grub_term_input *term __attribute__ ((unused)))
+{
+ unsigned long count;
+ char chr;
+
+ if (GRUB_ARC_FIRMWARE_VECTOR->get_read_status (GRUB_ARC_STDIN))
+ return -1;
+
+ if (GRUB_ARC_FIRMWARE_VECTOR->read (GRUB_ARC_STDIN, &chr, 1, &count))
+ return -1;
+ if (!count)
+ return -1;
+ return chr;
+}
+
+static void
+put (struct grub_term_output *term __attribute__ ((unused)), const int c)
+{
+ unsigned long count;
+ char chr = c;
+
+ GRUB_ARC_FIRMWARE_VECTOR->write (GRUB_ARC_STDOUT, &chr, 1, &count);
+}
+
+static struct grub_terminfo_output_state grub_console_terminfo_output;
+
+int
+grub_arc_is_device_serial (const char *name, int alt_names)
+{
+ if (name[0] == '\0')
+ return 0;
+
+ const char *ptr = name + grub_strlen (name) - 1;
+ int i;
+ /*
+ Recognize:
+ serial(N)
+ serial(N)line(M)
+ */
+ for (i = 0; i < 2; i++)
+ {
+ if (!alt_names)
+ {
+ if (*ptr != ')')
+ return 0;
+ ptr--;
+ }
+ for (; ptr >= name && grub_isdigit (*ptr); ptr--);
+ if (ptr < name)
+ return 0;
+ if (!alt_names)
+ {
+ if (*ptr != '(')
+ return 0;
+ ptr--;
+ }
+ if (ptr + 1 >= name + sizeof ("serial") - 1
+ && grub_memcmp (ptr + 1 - (sizeof ("serial") - 1),
+ "serial", sizeof ("serial") - 1) == 0)
+ return 1;
+ if (!(ptr + 1 >= name + sizeof ("line") - 1
+ && grub_memcmp (ptr + 1 - (sizeof ("line") - 1),
+ "line", sizeof ("line") - 1) == 0))
+ return 0;
+ ptr -= sizeof ("line") - 1;
+ if (alt_names)
+ {
+ if (*ptr != '/')
+ return 0;
+ ptr--;
+ }
+ }
+ return 0;
+}
+
+static int
+check_is_serial (void)
+{
+ static int is_serial = -1;
+
+ if (is_serial != -1)
+ return is_serial;
+
+ const char *consout = 0;
+
+ /* Check for serial. It works unless user manually overrides ConsoleOut
+ variable. If he does there is nothing we can do. Fortunately failure
+ isn't critical.
+ */
+ if (GRUB_ARC_SYSTEM_PARAMETER_BLOCK->firmware_vector_length
+ >= (unsigned) ((char *) (&GRUB_ARC_FIRMWARE_VECTOR->getenvironmentvariable + 1)
+ - (char *) GRUB_ARC_FIRMWARE_VECTOR)
+ && GRUB_ARC_FIRMWARE_VECTOR->getenvironmentvariable)
+ consout = GRUB_ARC_FIRMWARE_VECTOR->getenvironmentvariable ("ConsoleOut");
+ if (!consout)
+ return is_serial = 0;
+ return is_serial = grub_arc_is_device_serial (consout, 0);
+}
+
+static void
+set_console_dimensions (void)
+{
+ struct grub_arc_display_status *info = NULL;
+
+ if (check_is_serial ())
+ {
+ grub_console_terminfo_output.size.x = 80;
+ grub_console_terminfo_output.size.y = 24;
+ return;
+ }
+
+ if (GRUB_ARC_SYSTEM_PARAMETER_BLOCK->firmware_vector_length
+ >= (unsigned) ((char *) (&GRUB_ARC_FIRMWARE_VECTOR->getdisplaystatus + 1)
+ - (char *) GRUB_ARC_FIRMWARE_VECTOR)
+ && GRUB_ARC_FIRMWARE_VECTOR->getdisplaystatus)
+ info = GRUB_ARC_FIRMWARE_VECTOR->getdisplaystatus (GRUB_ARC_STDOUT);
+ if (info)
+ {
+ grub_console_terminfo_output.size.x = info->w + 1;
+ grub_console_terminfo_output.size.y = info->h + 1;
+ }
+}
+
+static grub_err_t
+grub_console_init_output (struct grub_term_output *term)
+{
+ set_console_dimensions ();
+ grub_terminfo_output_init (term);
+
+ return 0;
+}
+
+static struct grub_terminfo_input_state grub_console_terminfo_input =
+ {
+ .readkey = readkey
+ };
+
+static struct grub_terminfo_output_state grub_console_terminfo_output =
+ {
+ .put = put,
+ .size = { 80, 20 }
+ };
+
+static struct grub_term_input grub_console_term_input =
+ {
+ .name = "console",
+ .init = grub_terminfo_input_init,
+ .getkey = grub_terminfo_getkey,
+ .data = &grub_console_terminfo_input
+ };
+
+static struct grub_term_output grub_console_term_output =
+ {
+ .name = "console",
+ .init = grub_console_init_output,
+ .putchar = grub_terminfo_putchar,
+ .getxy = grub_terminfo_getxy,
+ .getwh = grub_terminfo_getwh,
+ .gotoxy = grub_terminfo_gotoxy,
+ .cls = grub_terminfo_cls,
+ .setcolorstate = grub_terminfo_setcolorstate,
+ .setcursor = grub_terminfo_setcursor,
+ .flags = GRUB_TERM_CODE_TYPE_ASCII,
+ .data = &grub_console_terminfo_output,
+ .progress_update_divisor = GRUB_PROGRESS_FAST
+ };
+
+void
+grub_console_init_early (void)
+{
+ grub_term_register_input ("console", &grub_console_term_input);
+ grub_term_register_output ("console", &grub_console_term_output);
+}
+
+void
+grub_console_init_lately (void)
+{
+ grub_terminfo_init ();
+ if (check_is_serial ())
+ grub_terminfo_output_register (&grub_console_term_output, "vt100");
+ else
+ grub_terminfo_output_register (&grub_console_term_output, "arc");
+}
diff --git a/grub-core/term/arc/serial.c b/grub-core/term/arc/serial.c
new file mode 100644
index 0000000..87d1ce8
--- /dev/null
+++ b/grub-core/term/arc/serial.c
@@ -0,0 +1,147 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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>
+#include <grub/arc/arc.h>
+
+
+static void
+do_real_config (struct grub_serial_port *port)
+{
+ char *name;
+ if (port->configured)
+ return;
+
+ name = grub_arc_alt_name_to_norm (port->name, "");
+
+ if (GRUB_ARC_FIRMWARE_VECTOR->open (name,GRUB_ARC_FILE_ACCESS_OPEN_RW,
+ &port->handle))
+ port->handle_valid = 0;
+ else
+ port->handle_valid = 1;
+
+ port->configured = 1;
+}
+
+/* Fetch a key. */
+static int
+serial_hw_fetch (struct grub_serial_port *port)
+{
+ unsigned long actual;
+ char c;
+
+ do_real_config (port);
+
+ if (!port->handle_valid)
+ return -1;
+ if (GRUB_ARC_FIRMWARE_VECTOR->read (port->handle, &c,
+ 1, &actual) || actual <= 0)
+ return -1;
+ return c;
+}
+
+/* Put a character. */
+static void
+serial_hw_put (struct grub_serial_port *port, const int c)
+{
+ unsigned long actual;
+ char c0 = c;
+
+ do_real_config (port);
+
+ if (!port->handle_valid)
+ return;
+
+ GRUB_ARC_FIRMWARE_VECTOR->write (port->handle, &c0,
+ 1, &actual);
+}
+
+/* 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)))
+{
+ /* FIXME: no ARC serial config available. */
+
+ return GRUB_ERR_NONE;
+}
+
+struct grub_serial_driver grub_arcserial_driver =
+ {
+ .configure = serial_hw_configure,
+ .fetch = serial_hw_fetch,
+ .put = serial_hw_put
+ };
+
+const char *
+grub_arcserial_add_port (const char *path)
+{
+ struct grub_serial_port *port;
+ grub_err_t err;
+
+ port = grub_zalloc (sizeof (*port));
+ if (!port)
+ return NULL;
+ port->name = grub_strdup (path);
+ if (!port->name)
+ return NULL;
+
+ port->driver = &grub_arcserial_driver;
+ err = grub_serial_config_defaults (port);
+ if (err)
+ grub_print_error ();
+
+ grub_serial_register (port);
+
+ return port->name;
+}
+
+static int
+dev_iterate (const char *name,
+ const struct grub_arc_component *comp __attribute__ ((unused)),
+ void *data __attribute__ ((unused)))
+{
+ /* We should check consolein/consoleout flags as
+ well but some implementations are buggy. */
+ if ((comp->flags & (GRUB_ARC_COMPONENT_FLAG_IN | GRUB_ARC_COMPONENT_FLAG_OUT))
+ != (GRUB_ARC_COMPONENT_FLAG_IN | GRUB_ARC_COMPONENT_FLAG_OUT))
+ return 0;
+ if (!grub_arc_is_device_serial (name, 1))
+ return 0;
+ grub_arcserial_add_port (name);
+ return 0;
+}
+
+void
+grub_arcserial_init (void)
+{
+ grub_arc_iterate_devs (dev_iterate, 0, 1);
+}
+
diff --git a/grub-core/term/arm/cros.c b/grub-core/term/arm/cros.c
new file mode 100644
index 0000000..a17e49c
--- /dev/null
+++ b/grub-core/term/arm/cros.c
@@ -0,0 +1,125 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ *
+ * Copyright (C) 2012 Google Inc.
+ * Copyright (C) 2016 Free Software Foundation, Inc.
+ *
+ * This is based on depthcharge code.
+ *
+ * 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/ps2.h>
+#include <grub/fdtbus.h>
+#include <grub/err.h>
+#include <grub/machine/kernel.h>
+#include <grub/misc.h>
+#include <grub/term.h>
+#include <grub/time.h>
+#include <grub/fdtbus.h>
+#include <grub/arm/cros_ec.h>
+
+static struct grub_ps2_state ps2_state;
+
+struct grub_cros_ec_keyscan old_scan;
+
+static const struct grub_fdtbus_dev *cros_ec;
+
+static grub_uint8_t map_code[GRUB_CROS_EC_KEYSCAN_COLS][GRUB_CROS_EC_KEYSCAN_ROWS];
+
+static grub_uint8_t e0_translate[16] =
+ {
+ 0x1c, 0x1d, 0x35, 0x00,
+ 0x38, 0x00, 0x47, 0x48,
+ 0x49, 0x4b, 0x4d, 0x4f,
+ 0x50, 0x51, 0x52, 0x53,
+ };
+
+/* If there is a character pending, return it;
+ otherwise return GRUB_TERM_NO_KEY. */
+static int
+grub_cros_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused)))
+{
+ struct grub_cros_ec_keyscan scan;
+ int i, j;
+ if (grub_cros_ec_scan_keyboard (cros_ec, &scan) < 0)
+ return GRUB_TERM_NO_KEY;
+ for (i = 0; i < GRUB_CROS_EC_KEYSCAN_COLS; i++)
+ if (scan.data[i] ^ old_scan.data[i])
+ for (j = 0; j < GRUB_CROS_EC_KEYSCAN_ROWS; j++)
+ if ((scan.data[i] ^ old_scan.data[i]) & (1 << j))
+ {
+ grub_uint8_t code = map_code[i][j];
+ int ret;
+ grub_uint8_t brk = 0;
+ if (!(scan.data[i] & (1 << j)))
+ brk = 0x80;
+ grub_dprintf ("cros_keyboard", "key <%d, %d> code %x\n", i, j, code);
+ if (code < 0x60)
+ ret = grub_ps2_process_incoming_byte (&ps2_state, code | brk);
+ else if (code >= 0x60 && code < 0x70 && e0_translate[code - 0x60])
+ {
+ grub_ps2_process_incoming_byte (&ps2_state, 0xe0);
+ ret = grub_ps2_process_incoming_byte (&ps2_state, e0_translate[code - 0x60] | brk);
+ }
+ else
+ ret = GRUB_TERM_NO_KEY;
+ old_scan.data[i] ^= (1 << j);
+ if (ret != GRUB_TERM_NO_KEY)
+ return ret;
+ }
+ return GRUB_TERM_NO_KEY;
+}
+
+static struct grub_term_input grub_cros_keyboard_term =
+ {
+ .name = "cros_keyboard",
+ .getkey = grub_cros_keyboard_getkey
+ };
+
+static grub_err_t
+cros_attach (const struct grub_fdtbus_dev *dev)
+{
+ grub_size_t keymap_size, i;
+ const grub_uint8_t *keymap = grub_fdtbus_get_prop (dev, "linux,keymap", &keymap_size);
+
+ if (!dev->parent || !grub_cros_ec_validate (dev->parent))
+ return GRUB_ERR_IO;
+
+ if (keymap)
+ {
+ for (i = 0; i + 3 < keymap_size; i += 4)
+ if (keymap[i+1] < GRUB_CROS_EC_KEYSCAN_COLS && keymap[i] < GRUB_CROS_EC_KEYSCAN_ROWS
+ && keymap[i+2] == 0 && keymap[i+3] < 0x80)
+ map_code[keymap[i+1]][keymap[i]] = keymap[i+3];
+ }
+
+ cros_ec = dev->parent;
+ ps2_state.current_set = 1;
+ ps2_state.at_keyboard_status = 0;
+ grub_term_register_input ("cros_keyboard", &grub_cros_keyboard_term);
+ return GRUB_ERR_NONE;
+}
+
+static struct grub_fdtbus_driver cros =
+{
+ .compatible = "google,cros-ec-keyb",
+ .attach = cros_attach
+};
+
+void
+grub_cros_init (void)
+{
+ grub_fdtbus_register (&cros);
+}
diff --git a/grub-core/term/arm/cros_ec.c b/grub-core/term/arm/cros_ec.c
new file mode 100644
index 0000000..f414481
--- /dev/null
+++ b/grub-core/term/arm/cros_ec.c
@@ -0,0 +1,238 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ *
+ * Copyright (C) 2012 Google Inc.
+ * Copyright (C) 2016 Free Software Foundation, Inc.
+ *
+ * This is based on depthcharge code.
+ *
+ * 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/mm.h>
+#include <grub/time.h>
+#include <grub/misc.h>
+#include <grub/arm/cros_ec.h>
+#include <grub/fdtbus.h>
+
+static const grub_uint64_t FRAMING_TIMEOUT_MS = 300;
+
+static const grub_uint8_t EC_FRAMING_BYTE = 0xec;
+
+#define EC_CMD_MKBP_STATE 0x60
+#define EC_CMD_VERSION0 0xdc
+
+static grub_uint64_t last_transfer;
+
+static void
+stop_bus (const struct grub_fdtbus_dev *spi)
+{
+ spi->driver->stop (spi);
+ last_transfer = grub_get_time_ms ();
+}
+
+static int
+wait_for_frame (const struct grub_fdtbus_dev *spi)
+{
+ grub_uint64_t start = grub_get_time_ms ();
+ grub_uint8_t byte;
+ do
+ {
+ if (spi->driver->receive (spi, &byte, 1))
+ return -1;
+ if (byte != EC_FRAMING_BYTE &&
+ grub_get_time_ms () - start > FRAMING_TIMEOUT_MS)
+ {
+ grub_dprintf ("cros", "Timeout waiting for framing byte.\n");
+ return -1;
+ }
+ }
+ while (byte != EC_FRAMING_BYTE);
+ return 0;
+}
+
+/*
+ * Calculate a simple 8-bit checksum of a data block
+ *
+ * @param data Data block to checksum
+ * @param size Size of data block in bytes
+ * @return checksum value (0 to 255)
+ */
+static grub_uint8_t
+cros_ec_calc_checksum (const void *data, int size)
+{
+ grub_uint8_t csum;
+ const grub_uint8_t *bytes = data;
+ int i;
+
+ for (i = csum = 0; i < size; i++)
+ csum += bytes[i];
+ return csum & 0xff;
+}
+
+enum
+{
+ /* response, arglen */
+ CROS_EC_SPI_IN_HDR_SIZE = 2,
+ /* version, cmd, arglen */
+ CROS_EC_SPI_OUT_HDR_SIZE = 3
+};
+
+static grub_uint8_t busbuf[256];
+#define MSG_BYTES ((int)sizeof (busbuf))
+
+static int
+ec_command (const struct grub_fdtbus_dev *dev, int cmd, int cmd_version,
+ const void *dout, int dout_len, void *din, int din_len)
+{
+ const struct grub_fdtbus_dev *spi = dev->parent;
+ grub_uint8_t *bytes;
+
+ /* Header + data + checksum. */
+ grub_uint32_t out_bytes = CROS_EC_SPI_OUT_HDR_SIZE + dout_len + 1;
+ grub_uint32_t in_bytes = CROS_EC_SPI_IN_HDR_SIZE + din_len + 1;
+
+ /*
+ * Sanity-check I/O sizes given transaction overhead in internal
+ * buffers.
+ */
+ if (out_bytes > MSG_BYTES)
+ {
+ grub_dprintf ("cros", "Cannot send %d bytes\n", dout_len);
+ return -1;
+ }
+ if (in_bytes > MSG_BYTES)
+ {
+ grub_dprintf ("cros", "Cannot receive %d bytes\n", din_len);
+ return -1;
+ }
+
+ /* Prepare the output. */
+ bytes = busbuf;
+ *bytes++ = EC_CMD_VERSION0 + cmd_version;
+ *bytes++ = cmd;
+ *bytes++ = dout_len;
+ grub_memcpy (bytes, dout, dout_len);
+ bytes += dout_len;
+
+ *bytes++ = cros_ec_calc_checksum (busbuf,
+ CROS_EC_SPI_OUT_HDR_SIZE + dout_len);
+
+ /* Depthcharge uses 200 us here but GRUB timer resolution is only 1ms,
+ decrease this when we increase timer resolution. */
+ while (grub_get_time_ms () - last_transfer < 1)
+ ;
+
+ if (spi->driver->start (spi))
+ return -1;
+
+ /* Allow EC to ramp up clock after being awoken. */
+ /* Depthcharge only waits 100 us here but GRUB timer resolution is only 1ms,
+ decrease this when we increase timer resolution. */
+ grub_millisleep (1);
+
+ if (spi->driver->send (spi, busbuf, out_bytes))
+ {
+ stop_bus (spi);
+ return -1;
+ }
+
+ /* Wait until the EC is ready. */
+ if (wait_for_frame (spi))
+ {
+ stop_bus (spi);
+ return -1;
+ }
+
+ /* Read the response code and the data length. */
+ bytes = busbuf;
+ if (spi->driver->receive (spi, bytes, 2))
+ {
+ stop_bus (spi);
+ return -1;
+ }
+ grub_uint8_t result = *bytes++;
+ grub_uint8_t length = *bytes++;
+
+ /* Make sure there's enough room for the data. */
+ if (CROS_EC_SPI_IN_HDR_SIZE + length + 1 > MSG_BYTES)
+ {
+ grub_dprintf ("cros", "Received length %#02x too large\n", length);
+ stop_bus (spi);
+ return -1;
+ }
+
+ /* Read the data and the checksum, and finish up. */
+ if (spi->driver->receive (spi, bytes, length + 1))
+ {
+ stop_bus (spi);
+ return -1;
+ }
+ bytes += length;
+ int expected = *bytes++;
+ stop_bus (spi);
+
+ /* Check the integrity of the response. */
+ if (result != 0)
+ {
+ grub_dprintf ("cros", "Received bad result code %d\n", result);
+ return -result;
+ }
+
+ int csum = cros_ec_calc_checksum (busbuf,
+ CROS_EC_SPI_IN_HDR_SIZE + length);
+
+ if (csum != expected)
+ {
+ grub_dprintf ("cros", "Invalid checksum rx %#02x, calced %#02x\n",
+ expected, csum);
+ return -1;
+ }
+
+ /* If the caller wants the response, copy it out for them. */
+ if (length < din_len)
+ din_len = length;
+ if (din)
+ {
+ grub_memcpy (din, (grub_uint8_t *) busbuf + CROS_EC_SPI_IN_HDR_SIZE, din_len);
+ }
+
+ return din_len;
+}
+
+int
+grub_cros_ec_scan_keyboard (const struct grub_fdtbus_dev *dev, struct grub_cros_ec_keyscan *scan)
+{
+ if (ec_command (dev, EC_CMD_MKBP_STATE, 0, NULL, 0, scan,
+ sizeof (*scan)) < (int) sizeof (*scan))
+ return -1;
+
+ return 0;
+}
+
+int
+grub_cros_ec_validate (const struct grub_fdtbus_dev *dev)
+{
+ if (!grub_fdtbus_is_compatible("google,cros-ec-spi", dev))
+ return 0;
+ if (!dev->parent)
+ return 0;
+ if (!dev->parent->driver)
+ return 0;
+ if (!dev->parent->driver->send
+ || !dev->parent->driver->receive)
+ return 0;
+ return 1;
+}
+
diff --git a/grub-core/term/arm/pl050.c b/grub-core/term/arm/pl050.c
new file mode 100644
index 0000000..b082243
--- /dev/null
+++ b/grub-core/term/arm/pl050.c
@@ -0,0 +1,189 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/ps2.h>
+#include <grub/fdtbus.h>
+#include <grub/err.h>
+#include <grub/machine/kernel.h>
+#include <grub/at_keyboard.h>
+#include <grub/misc.h>
+#include <grub/term.h>
+#include <grub/time.h>
+#include <grub/ps2.h>
+#include <grub/fdtbus.h>
+
+static volatile grub_uint32_t *pl050_regs;
+
+static struct grub_ps2_state ps2_state;
+
+static void
+keyboard_controller_wait_until_ready (void)
+{
+ while (! (pl050_regs[1] & 0x40));
+}
+
+static grub_uint8_t
+wait_ack (void)
+{
+ grub_uint64_t endtime;
+ grub_uint8_t ack;
+
+ endtime = grub_get_time_ms () + 20;
+ do
+ ack = pl050_regs[2];
+ while (ack != GRUB_AT_ACK && ack != GRUB_AT_NACK
+ && grub_get_time_ms () < endtime);
+ return ack;
+}
+
+
+static int
+write_mode (int mode)
+{
+ unsigned i;
+ for (i = 0; i < GRUB_AT_TRIES; i++)
+ {
+ grub_uint8_t ack;
+ keyboard_controller_wait_until_ready ();
+ pl050_regs[2] = 0xf0;
+ keyboard_controller_wait_until_ready ();
+ pl050_regs[2] = mode;
+ keyboard_controller_wait_until_ready ();
+ ack = wait_ack ();
+ if (ack == GRUB_AT_NACK)
+ continue;
+ if (ack == GRUB_AT_ACK)
+ break;
+ return 0;
+ }
+
+ return (i != GRUB_AT_TRIES);
+}
+
+static int
+query_mode (void)
+{
+ grub_uint8_t ret;
+ int e;
+
+ e = write_mode (0);
+ if (!e)
+ return 0;
+
+ keyboard_controller_wait_until_ready ();
+
+ do
+ ret = pl050_regs[2];
+ while (ret == GRUB_AT_ACK);
+
+ /* QEMU translates the set even in no-translate mode. */
+ if (ret == 0x43 || ret == 1)
+ return 1;
+ if (ret == 0x41 || ret == 2)
+ return 2;
+ if (ret == 0x3f || ret == 3)
+ return 3;
+ return 0;
+}
+
+static void
+set_scancodes (void)
+{
+ write_mode (2);
+ ps2_state.current_set = query_mode ();
+ grub_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set);
+ if (ps2_state.current_set == 2)
+ return;
+
+ write_mode (1);
+ ps2_state.current_set = query_mode ();
+ grub_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set);
+ if (ps2_state.current_set == 1)
+ return;
+ grub_dprintf ("atkeyb", "no supported scancode set found\n");
+}
+
+static void
+keyboard_controller_led (grub_uint8_t leds)
+{
+ keyboard_controller_wait_until_ready ();
+ pl050_regs[2] = 0xed;
+ keyboard_controller_wait_until_ready ();
+ pl050_regs[2] = leds & 0x7;
+}
+
+/* If there is a character pending, return it;
+ otherwise return GRUB_TERM_NO_KEY. */
+static int
+grub_pl050_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused)))
+{
+ grub_uint8_t at_key;
+ int ret;
+ grub_uint8_t old_led;
+
+ if (!(pl050_regs[1] & 0x10))
+ return -1;
+ at_key = pl050_regs[2];
+ old_led = ps2_state.led_status;
+
+ ret = grub_ps2_process_incoming_byte (&ps2_state, at_key);
+ if (old_led != ps2_state.led_status)
+ keyboard_controller_led (ps2_state.led_status);
+ return ret;
+}
+
+static struct grub_term_input grub_pl050_keyboard_term =
+ {
+ .name = "pl050_keyboard",
+ .getkey = grub_pl050_keyboard_getkey
+ };
+
+static grub_err_t
+pl050_attach(const struct grub_fdtbus_dev *dev)
+{
+ const grub_uint32_t *reg;
+ reg = grub_fdtbus_get_prop (dev, "reg", 0);
+
+ /* Mouse. Nothing to do. */
+ if (grub_be_to_cpu32 (*reg) == 0x7000)
+ return 0;
+
+ pl050_regs = grub_fdtbus_map_reg (dev, 0, 0);
+
+ if (!grub_fdtbus_is_mapping_valid (pl050_regs))
+ return grub_error (GRUB_ERR_IO, "could not map pl050");
+
+ ps2_state.at_keyboard_status = 0;
+ set_scancodes ();
+ keyboard_controller_led (ps2_state.led_status);
+
+ grub_term_register_input ("pl050_keyboard", &grub_pl050_keyboard_term);
+ return GRUB_ERR_NONE;
+}
+
+struct grub_fdtbus_driver pl050 =
+{
+ .compatible = "arm,pl050",
+ .attach = pl050_attach
+};
+
+void
+grub_pl050_init (void)
+{
+ grub_fdtbus_register (&pl050);
+}
diff --git a/grub-core/term/at_keyboard.c b/grub-core/term/at_keyboard.c
new file mode 100644
index 0000000..5971110
--- /dev/null
+++ b/grub-core/term/at_keyboard.c
@@ -0,0 +1,332 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/dl.h>
+#include <grub/at_keyboard.h>
+#include <grub/cpu/at_keyboard.h>
+#include <grub/cpu/io.h>
+#include <grub/misc.h>
+#include <grub/term.h>
+#include <grub/time.h>
+#include <grub/loader.h>
+#include <grub/ps2.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_uint8_t grub_keyboard_controller_orig;
+static grub_uint8_t grub_keyboard_orig_set;
+struct grub_ps2_state ps2_state;
+
+static int ping_sent;
+
+static void
+grub_keyboard_controller_init (void);
+
+static void
+keyboard_controller_wait_until_ready (void)
+{
+ /* 50 us would be enough but our current time resolution is 1ms. */
+ grub_millisleep (1);
+ while (! KEYBOARD_COMMAND_ISREADY (grub_inb (KEYBOARD_REG_STATUS)));
+}
+
+static grub_uint8_t
+wait_ack (void)
+{
+ grub_uint64_t endtime;
+ grub_uint8_t ack;
+
+ endtime = grub_get_time_ms () + 20;
+ do {
+ keyboard_controller_wait_until_ready ();
+ ack = grub_inb (KEYBOARD_REG_DATA);
+ } while (ack != GRUB_AT_ACK && ack != GRUB_AT_NACK
+ && grub_get_time_ms () < endtime);
+ return ack;
+}
+
+static int
+at_command (grub_uint8_t data)
+{
+ unsigned i;
+ for (i = 0; i < GRUB_AT_TRIES; i++)
+ {
+ grub_uint8_t ack;
+ keyboard_controller_wait_until_ready ();
+ grub_outb (data, KEYBOARD_REG_STATUS);
+ ack = wait_ack ();
+ if (ack == GRUB_AT_NACK)
+ continue;
+ if (ack == GRUB_AT_ACK)
+ break;
+ return 0;
+ }
+ return (i != GRUB_AT_TRIES);
+}
+
+static void
+grub_keyboard_controller_write (grub_uint8_t c)
+{
+ at_command (KEYBOARD_COMMAND_WRITE);
+ keyboard_controller_wait_until_ready ();
+ grub_outb (c, KEYBOARD_REG_DATA);
+}
+
+#if defined (GRUB_MACHINE_MIPS_LOONGSON) || defined (GRUB_MACHINE_QEMU) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_MIPS_QEMU_MIPS)
+#define USE_SCANCODE_SET 1
+#else
+#define USE_SCANCODE_SET 0
+#endif
+
+#if !USE_SCANCODE_SET
+
+static grub_uint8_t
+grub_keyboard_controller_read (void)
+{
+ at_command (KEYBOARD_COMMAND_READ);
+ keyboard_controller_wait_until_ready ();
+ return grub_inb (KEYBOARD_REG_DATA);
+}
+
+#endif
+
+static int
+write_mode (int mode)
+{
+ unsigned i;
+ for (i = 0; i < GRUB_AT_TRIES; i++)
+ {
+ grub_uint8_t ack;
+ keyboard_controller_wait_until_ready ();
+ grub_outb (0xf0, KEYBOARD_REG_DATA);
+ keyboard_controller_wait_until_ready ();
+ grub_outb (mode, KEYBOARD_REG_DATA);
+ keyboard_controller_wait_until_ready ();
+ ack = wait_ack ();
+ if (ack == GRUB_AT_NACK)
+ continue;
+ if (ack == GRUB_AT_ACK)
+ break;
+ return 0;
+ }
+
+ return (i != GRUB_AT_TRIES);
+}
+
+static int
+query_mode (void)
+{
+ grub_uint8_t ret;
+ int e;
+
+ e = write_mode (0);
+ if (!e)
+ return 0;
+
+ do {
+ keyboard_controller_wait_until_ready ();
+ ret = grub_inb (KEYBOARD_REG_DATA);
+ } while (ret == GRUB_AT_ACK);
+ /* QEMU translates the set even in no-translate mode. */
+ if (ret == 0x43 || ret == 1)
+ return 1;
+ if (ret == 0x41 || ret == 2)
+ return 2;
+ if (ret == 0x3f || ret == 3)
+ return 3;
+ return 0;
+}
+
+static void
+set_scancodes (void)
+{
+ /* You must have visited computer museum. Keyboard without scancode set
+ knowledge. Assume XT. */
+ if (!grub_keyboard_orig_set)
+ {
+ grub_dprintf ("atkeyb", "No sets support assumed\n");
+ ps2_state.current_set = 1;
+ return;
+ }
+
+#if !USE_SCANCODE_SET
+ ps2_state.current_set = 1;
+ return;
+#else
+
+ grub_keyboard_controller_write (grub_keyboard_controller_orig
+ & ~KEYBOARD_AT_TRANSLATE
+ & ~KEYBOARD_AT_DISABLE);
+
+ keyboard_controller_wait_until_ready ();
+ grub_outb (KEYBOARD_COMMAND_ENABLE, KEYBOARD_REG_DATA);
+
+ write_mode (2);
+ ps2_state.current_set = query_mode ();
+ grub_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set);
+ if (ps2_state.current_set == 2)
+ return;
+
+ write_mode (1);
+ ps2_state.current_set = query_mode ();
+ grub_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set);
+ if (ps2_state.current_set == 1)
+ return;
+ grub_dprintf ("atkeyb", "no supported scancode set found\n");
+#endif
+}
+
+static void
+keyboard_controller_led (grub_uint8_t leds)
+{
+ keyboard_controller_wait_until_ready ();
+ grub_outb (0xed, KEYBOARD_REG_DATA);
+ keyboard_controller_wait_until_ready ();
+ grub_outb (leds & 0x7, KEYBOARD_REG_DATA);
+}
+
+int
+grub_at_keyboard_is_alive (void)
+{
+ if (ps2_state.current_set != 0)
+ return 1;
+ if (ping_sent
+ && KEYBOARD_COMMAND_ISREADY (grub_inb (KEYBOARD_REG_STATUS))
+ && grub_inb (KEYBOARD_REG_DATA) == 0x55)
+ {
+ grub_keyboard_controller_init ();
+ return 1;
+ }
+
+ if (KEYBOARD_COMMAND_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
+ {
+ grub_outb (0xaa, KEYBOARD_REG_STATUS);
+ ping_sent = 1;
+ }
+ return 0;
+}
+
+/* If there is a character pending, return it;
+ otherwise return GRUB_TERM_NO_KEY. */
+static int
+grub_at_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused)))
+{
+ grub_uint8_t at_key;
+ int ret;
+ grub_uint8_t old_led;
+
+ if (!grub_at_keyboard_is_alive ())
+ return GRUB_TERM_NO_KEY;
+
+ if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
+ return GRUB_TERM_NO_KEY;
+ at_key = grub_inb (KEYBOARD_REG_DATA);
+ old_led = ps2_state.led_status;
+
+ ret = grub_ps2_process_incoming_byte (&ps2_state, at_key);
+ if (old_led != ps2_state.led_status)
+ keyboard_controller_led (ps2_state.led_status);
+ return ret;
+}
+
+static void
+grub_keyboard_controller_init (void)
+{
+ ps2_state.at_keyboard_status = 0;
+ /* Drain input buffer. */
+ while (1)
+ {
+ keyboard_controller_wait_until_ready ();
+ if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
+ break;
+ keyboard_controller_wait_until_ready ();
+ grub_inb (KEYBOARD_REG_DATA);
+ }
+#if defined (GRUB_MACHINE_MIPS_LOONGSON) || defined (GRUB_MACHINE_MIPS_QEMU_MIPS)
+ grub_keyboard_controller_orig = 0;
+ grub_keyboard_orig_set = 2;
+#elif defined (GRUB_MACHINE_QEMU) || defined (GRUB_MACHINE_COREBOOT)
+ /* *BSD relies on those settings. */
+ grub_keyboard_controller_orig = KEYBOARD_AT_TRANSLATE;
+ grub_keyboard_orig_set = 2;
+#else
+ grub_keyboard_controller_orig = grub_keyboard_controller_read ();
+ grub_keyboard_orig_set = query_mode ();
+#endif
+ set_scancodes ();
+ keyboard_controller_led (ps2_state.led_status);
+}
+
+static grub_err_t
+grub_keyboard_controller_fini (struct grub_term_input *term __attribute__ ((unused)))
+{
+ if (ps2_state.current_set == 0)
+ return GRUB_ERR_NONE;
+ if (grub_keyboard_orig_set)
+ write_mode (grub_keyboard_orig_set);
+ grub_keyboard_controller_write (grub_keyboard_controller_orig);
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_at_fini_hw (int noreturn __attribute__ ((unused)))
+{
+ return grub_keyboard_controller_fini (NULL);
+}
+
+static grub_err_t
+grub_at_restore_hw (void)
+{
+ if (ps2_state.current_set == 0)
+ return GRUB_ERR_NONE;
+
+ /* Drain input buffer. */
+ while (1)
+ {
+ keyboard_controller_wait_until_ready ();
+ if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
+ break;
+ keyboard_controller_wait_until_ready ();
+ grub_inb (KEYBOARD_REG_DATA);
+ }
+ set_scancodes ();
+ keyboard_controller_led (ps2_state.led_status);
+
+ return GRUB_ERR_NONE;
+}
+
+
+static struct grub_term_input grub_at_keyboard_term =
+ {
+ .name = "at_keyboard",
+ .fini = grub_keyboard_controller_fini,
+ .getkey = grub_at_keyboard_getkey
+ };
+
+GRUB_MOD_INIT(at_keyboard)
+{
+ grub_term_register_input ("at_keyboard", &grub_at_keyboard_term);
+ grub_loader_register_preboot_hook (grub_at_fini_hw, grub_at_restore_hw,
+ GRUB_LOADER_PREBOOT_HOOK_PRIO_CONSOLE);
+}
+
+GRUB_MOD_FINI(at_keyboard)
+{
+ grub_keyboard_controller_fini (NULL);
+ grub_term_unregister_input (&grub_at_keyboard_term);
+}
diff --git a/grub-core/term/efi/console.c b/grub-core/term/efi/console.c
new file mode 100644
index 0000000..2f1ae85
--- /dev/null
+++ b/grub-core/term/efi/console.c
@@ -0,0 +1,470 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2006,2007,2008 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/term.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/efi/efi.h>
+#include <grub/efi/api.h>
+#include <grub/efi/console.h>
+
+typedef enum {
+ GRUB_TEXT_MODE_UNDEFINED = -1,
+ GRUB_TEXT_MODE_UNAVAILABLE = 0,
+ GRUB_TEXT_MODE_AVAILABLE
+}
+grub_text_mode;
+
+static grub_text_mode text_mode = GRUB_TEXT_MODE_UNDEFINED;
+static grub_term_color_state text_colorstate = GRUB_TERM_COLOR_UNDEFINED;
+
+static grub_uint32_t
+map_char (grub_uint32_t c)
+{
+ /* Map some unicode characters to the EFI character. */
+ switch (c)
+ {
+ case GRUB_UNICODE_LEFTARROW:
+ c = GRUB_UNICODE_BLACK_LEFT_TRIANGLE;
+ break;
+ case GRUB_UNICODE_UPARROW:
+ c = GRUB_UNICODE_BLACK_UP_TRIANGLE;
+ break;
+ case GRUB_UNICODE_RIGHTARROW:
+ c = GRUB_UNICODE_BLACK_RIGHT_TRIANGLE;
+ break;
+ case GRUB_UNICODE_DOWNARROW:
+ c = GRUB_UNICODE_BLACK_DOWN_TRIANGLE;
+ break;
+ case GRUB_UNICODE_HLINE:
+ c = GRUB_UNICODE_LIGHT_HLINE;
+ break;
+ case GRUB_UNICODE_VLINE:
+ c = GRUB_UNICODE_LIGHT_VLINE;
+ break;
+ case GRUB_UNICODE_CORNER_UL:
+ c = GRUB_UNICODE_LIGHT_CORNER_UL;
+ break;
+ case GRUB_UNICODE_CORNER_UR:
+ c = GRUB_UNICODE_LIGHT_CORNER_UR;
+ break;
+ case GRUB_UNICODE_CORNER_LL:
+ c = GRUB_UNICODE_LIGHT_CORNER_LL;
+ break;
+ case GRUB_UNICODE_CORNER_LR:
+ c = GRUB_UNICODE_LIGHT_CORNER_LR;
+ break;
+ }
+
+ return c;
+}
+
+static void
+grub_console_setcolorstate (struct grub_term_output *term
+ __attribute__ ((unused)),
+ grub_term_color_state state)
+{
+ grub_efi_simple_text_output_interface_t *o;
+
+ if (grub_efi_is_finished)
+ return;
+
+ o = grub_efi_system_table->con_out;
+
+ switch (state) {
+ case GRUB_TERM_COLOR_STANDARD:
+ efi_call_2 (o->set_attributes, o, GRUB_TERM_DEFAULT_STANDARD_COLOR
+ & 0x7f);
+ break;
+ case GRUB_TERM_COLOR_NORMAL:
+ efi_call_2 (o->set_attributes, o, grub_term_normal_color & 0x7f);
+ break;
+ case GRUB_TERM_COLOR_HIGHLIGHT:
+ efi_call_2 (o->set_attributes, o, grub_term_highlight_color & 0x7f);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+grub_console_setcursor (struct grub_term_output *term __attribute__ ((unused)),
+ int on)
+{
+ grub_efi_simple_text_output_interface_t *o;
+
+ if (grub_efi_is_finished)
+ return;
+
+ o = grub_efi_system_table->con_out;
+ efi_call_2 (o->enable_cursor, o, on);
+}
+
+static grub_err_t
+grub_prepare_for_text_output (struct grub_term_output *term)
+{
+ if (grub_efi_is_finished)
+ return GRUB_ERR_BAD_DEVICE;
+
+ if (text_mode != GRUB_TEXT_MODE_UNDEFINED)
+ return text_mode ? GRUB_ERR_NONE : GRUB_ERR_BAD_DEVICE;
+
+ if (! grub_efi_set_text_mode (1))
+ {
+ /* This really should never happen */
+ grub_error (GRUB_ERR_BAD_DEVICE, "cannot set text mode");
+ text_mode = GRUB_TEXT_MODE_UNAVAILABLE;
+ return GRUB_ERR_BAD_DEVICE;
+ }
+
+ grub_console_setcursor (term, 1);
+ if (text_colorstate != GRUB_TERM_COLOR_UNDEFINED)
+ grub_console_setcolorstate (term, text_colorstate);
+ text_mode = GRUB_TEXT_MODE_AVAILABLE;
+ return GRUB_ERR_NONE;
+}
+
+static void
+grub_console_putchar (struct grub_term_output *term,
+ const struct grub_unicode_glyph *c)
+{
+ grub_efi_char16_t str[2 + 30];
+ grub_efi_simple_text_output_interface_t *o;
+ unsigned i, j;
+
+ if (grub_prepare_for_text_output (term) != GRUB_ERR_NONE)
+ return;
+
+ o = grub_efi_system_table->con_out;
+
+ /* For now, do not try to use a surrogate pair. */
+ if (c->base > 0xffff)
+ str[0] = '?';
+ else
+ str[0] = (grub_efi_char16_t) map_char (c->base & 0xffff);
+ j = 1;
+ for (i = 0; i < c->ncomb && j + 1 < ARRAY_SIZE (str); i++)
+ if (c->base < 0xffff)
+ str[j++] = grub_unicode_get_comb (c)[i].code;
+ str[j] = 0;
+
+ /* Should this test be cached? */
+ if ((c->base > 0x7f || c->ncomb)
+ && efi_call_2 (o->test_string, o, str) != GRUB_EFI_SUCCESS)
+ return;
+
+ efi_call_2 (o->output_string, o, str);
+}
+
+const unsigned efi_codes[] =
+ {
+ 0, GRUB_TERM_KEY_UP, GRUB_TERM_KEY_DOWN, GRUB_TERM_KEY_RIGHT,
+ GRUB_TERM_KEY_LEFT, GRUB_TERM_KEY_HOME, GRUB_TERM_KEY_END, GRUB_TERM_KEY_INSERT,
+ GRUB_TERM_KEY_DC, GRUB_TERM_KEY_PPAGE, GRUB_TERM_KEY_NPAGE, GRUB_TERM_KEY_F1,
+ GRUB_TERM_KEY_F2, GRUB_TERM_KEY_F3, GRUB_TERM_KEY_F4, GRUB_TERM_KEY_F5,
+ GRUB_TERM_KEY_F6, GRUB_TERM_KEY_F7, GRUB_TERM_KEY_F8, GRUB_TERM_KEY_F9,
+ GRUB_TERM_KEY_F10, GRUB_TERM_KEY_F11, GRUB_TERM_KEY_F12, GRUB_TERM_ESC
+ };
+
+static int
+grub_efi_translate_key (grub_efi_input_key_t key)
+{
+ if (key.scan_code == 0)
+ {
+ /* Some firmware implementations use VT100-style codes against the spec.
+ This is especially likely if driven by serial.
+ */
+ if (key.unicode_char < 0x20 && key.unicode_char != 0
+ && key.unicode_char != '\t' && key.unicode_char != '\b'
+ && key.unicode_char != '\n' && key.unicode_char != '\r')
+ return GRUB_TERM_CTRL | (key.unicode_char - 1 + 'a');
+ else
+ return key.unicode_char;
+ }
+ /* Some devices send enter with scan_code 0x0d (F3) and unicode_char 0x0d. */
+ else if (key.scan_code == '\r' && key.unicode_char == '\r')
+ return key.unicode_char;
+ else if (key.scan_code < ARRAY_SIZE (efi_codes))
+ return efi_codes[key.scan_code];
+
+ if ((key.unicode_char >= 0x20 && key.unicode_char <= 0x7f)
+ || key.unicode_char == '\t' || key.unicode_char == '\b'
+ || key.unicode_char == '\n' || key.unicode_char == '\r')
+ return key.unicode_char;
+
+ return GRUB_TERM_NO_KEY;
+}
+
+static int
+grub_console_getkey_con (struct grub_term_input *term __attribute__ ((unused)))
+{
+ grub_efi_simple_input_interface_t *i;
+ grub_efi_input_key_t key;
+ grub_efi_status_t status;
+
+ i = grub_efi_system_table->con_in;
+ status = efi_call_2 (i->read_key_stroke, i, &key);
+
+ if (status != GRUB_EFI_SUCCESS)
+ return GRUB_TERM_NO_KEY;
+
+ return grub_efi_translate_key(key);
+}
+
+/*
+ * When more then just modifiers are pressed, our getkeystatus() consumes a
+ * press from the queue, this function buffers the press for the regular
+ * getkey() so that it does not get lost.
+ */
+static grub_err_t
+grub_console_read_key_stroke (
+ grub_efi_simple_text_input_ex_interface_t *text_input,
+ grub_efi_key_data_t *key_data_ret, int *key_ret,
+ int consume)
+{
+ static grub_efi_key_data_t key_data;
+ grub_efi_status_t status;
+ int key;
+
+ if (!text_input)
+ return GRUB_ERR_EOF;
+
+ key = grub_efi_translate_key (key_data.key);
+ if (key == GRUB_TERM_NO_KEY) {
+ status = efi_call_2 (text_input->read_key_stroke, text_input, &key_data);
+ if (status != GRUB_EFI_SUCCESS)
+ return GRUB_ERR_EOF;
+
+ key = grub_efi_translate_key (key_data.key);
+ }
+
+ *key_data_ret = key_data;
+ *key_ret = key;
+
+ if (consume) {
+ key_data.key.scan_code = 0;
+ key_data.key.unicode_char = 0;
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static int
+grub_console_getkey_ex (struct grub_term_input *term)
+{
+ grub_efi_key_data_t key_data;
+ grub_efi_uint32_t kss;
+ grub_err_t err;
+ int key = -1;
+
+ err = grub_console_read_key_stroke (term->data, &key_data, &key, 1);
+ if (err != GRUB_ERR_NONE || key == GRUB_TERM_NO_KEY)
+ return GRUB_TERM_NO_KEY;
+
+ kss = key_data.key_state.key_shift_state;
+ if (kss & GRUB_EFI_SHIFT_STATE_VALID)
+ {
+ if ((kss & GRUB_EFI_LEFT_SHIFT_PRESSED
+ || kss & GRUB_EFI_RIGHT_SHIFT_PRESSED)
+ && (key & GRUB_TERM_EXTENDED))
+ key |= GRUB_TERM_SHIFT;
+ if (kss & GRUB_EFI_LEFT_ALT_PRESSED || kss & GRUB_EFI_RIGHT_ALT_PRESSED)
+ key |= GRUB_TERM_ALT;
+ if (kss & GRUB_EFI_LEFT_CONTROL_PRESSED
+ || kss & GRUB_EFI_RIGHT_CONTROL_PRESSED)
+ key |= GRUB_TERM_CTRL;
+ }
+
+ return key;
+}
+
+static int
+grub_console_getkeystatus (struct grub_term_input *term)
+{
+ grub_efi_key_data_t key_data;
+ grub_efi_uint32_t kss;
+ int key, mods = 0;
+
+ if (grub_efi_is_finished)
+ return 0;
+
+ if (grub_console_read_key_stroke (term->data, &key_data, &key, 0))
+ return 0;
+
+ kss = key_data.key_state.key_shift_state;
+ if (kss & GRUB_EFI_SHIFT_STATE_VALID)
+ {
+ if (kss & GRUB_EFI_LEFT_SHIFT_PRESSED)
+ mods |= GRUB_TERM_STATUS_LSHIFT;
+ if (kss & GRUB_EFI_RIGHT_SHIFT_PRESSED)
+ mods |= GRUB_TERM_STATUS_RSHIFT;
+ if (kss & GRUB_EFI_LEFT_ALT_PRESSED)
+ mods |= GRUB_TERM_STATUS_LALT;
+ if (kss & GRUB_EFI_RIGHT_ALT_PRESSED)
+ mods |= GRUB_TERM_STATUS_RALT;
+ if (kss & GRUB_EFI_LEFT_CONTROL_PRESSED)
+ mods |= GRUB_TERM_STATUS_LCTRL;
+ if (kss & GRUB_EFI_RIGHT_CONTROL_PRESSED)
+ mods |= GRUB_TERM_STATUS_RCTRL;
+ }
+
+ return mods;
+}
+
+static grub_err_t
+grub_efi_console_input_init (struct grub_term_input *term)
+{
+ grub_efi_guid_t text_input_ex_guid =
+ GRUB_EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
+
+ if (grub_efi_is_finished)
+ return 0;
+
+ grub_efi_simple_text_input_ex_interface_t *text_input = term->data;
+ if (text_input)
+ return 0;
+
+ text_input = grub_efi_open_protocol(grub_efi_system_table->console_in_handler,
+ &text_input_ex_guid,
+ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ term->data = (void *)text_input;
+
+ return 0;
+}
+
+static int
+grub_console_getkey (struct grub_term_input *term)
+{
+ if (grub_efi_is_finished)
+ return 0;
+
+ if (term->data)
+ return grub_console_getkey_ex(term);
+ else
+ return grub_console_getkey_con(term);
+}
+
+static struct grub_term_coordinate
+grub_console_getwh (struct grub_term_output *term)
+{
+ grub_efi_simple_text_output_interface_t *o;
+ grub_efi_uintn_t columns, rows;
+
+ o = grub_efi_system_table->con_out;
+ if (grub_prepare_for_text_output (term) != GRUB_ERR_NONE ||
+ efi_call_4 (o->query_mode, o, o->mode->mode,
+ &columns, &rows) != GRUB_EFI_SUCCESS)
+ {
+ /* Why does this fail? */
+ columns = 80;
+ rows = 25;
+ }
+
+ return (struct grub_term_coordinate) { columns, rows };
+}
+
+static struct grub_term_coordinate
+grub_console_getxy (struct grub_term_output *term __attribute__ ((unused)))
+{
+ grub_efi_simple_text_output_interface_t *o;
+
+ if (grub_efi_is_finished || text_mode != GRUB_TEXT_MODE_AVAILABLE)
+ return (struct grub_term_coordinate) { 0, 0 };
+
+ o = grub_efi_system_table->con_out;
+ return (struct grub_term_coordinate) { o->mode->cursor_column, o->mode->cursor_row };
+}
+
+static void
+grub_console_gotoxy (struct grub_term_output *term,
+ struct grub_term_coordinate pos)
+{
+ grub_efi_simple_text_output_interface_t *o;
+
+ if (grub_prepare_for_text_output (term) != GRUB_ERR_NONE)
+ return;
+
+ o = grub_efi_system_table->con_out;
+ efi_call_3 (o->set_cursor_position, o, pos.x, pos.y);
+}
+
+static void
+grub_console_cls (struct grub_term_output *term __attribute__ ((unused)))
+{
+ grub_efi_simple_text_output_interface_t *o;
+ grub_efi_int32_t orig_attr;
+
+ if (grub_efi_is_finished || text_mode != GRUB_TEXT_MODE_AVAILABLE)
+ return;
+
+ o = grub_efi_system_table->con_out;
+ orig_attr = o->mode->attribute;
+ efi_call_2 (o->set_attributes, o, GRUB_EFI_BACKGROUND_BLACK);
+ efi_call_1 (o->clear_screen, o);
+ efi_call_2 (o->set_attributes, o, orig_attr);
+}
+
+static grub_err_t
+grub_efi_console_output_fini (struct grub_term_output *term)
+{
+ if (text_mode != GRUB_TEXT_MODE_AVAILABLE)
+ return 0;
+
+ grub_console_setcursor (term, 0);
+ grub_efi_set_text_mode (0);
+ text_mode = GRUB_TEXT_MODE_UNDEFINED;
+ return 0;
+}
+
+static struct grub_term_input grub_console_term_input =
+ {
+ .name = "console",
+ .getkey = grub_console_getkey,
+ .getkeystatus = grub_console_getkeystatus,
+ .init = grub_efi_console_input_init,
+ };
+
+static struct grub_term_output grub_console_term_output =
+ {
+ .name = "console",
+ .fini = grub_efi_console_output_fini,
+ .putchar = grub_console_putchar,
+ .getwh = grub_console_getwh,
+ .getxy = grub_console_getxy,
+ .gotoxy = grub_console_gotoxy,
+ .cls = grub_console_cls,
+ .setcolorstate = grub_console_setcolorstate,
+ .setcursor = grub_console_setcursor,
+ .flags = GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS,
+ .progress_update_divisor = GRUB_PROGRESS_FAST
+ };
+
+void
+grub_console_init (void)
+{
+ grub_term_register_output ("console", &grub_console_term_output);
+ grub_term_register_input ("console", &grub_console_term_input);
+}
+
+void
+grub_console_fini (void)
+{
+ grub_term_unregister_input (&grub_console_term_input);
+ grub_term_unregister_output (&grub_console_term_output);
+}
diff --git a/grub-core/term/efi/serial.c b/grub-core/term/efi/serial.c
new file mode 100644
index 0000000..4c94723
--- /dev/null
+++ b/grub-core/term/efi/serial.c
@@ -0,0 +1,195 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2006,2007,2008,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/disk.h>
+#include <grub/partition.h>
+#include <grub/mm.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/err.h>
+#include <grub/term.h>
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/efi/disk.h>
+#include <grub/serial.h>
+#include <grub/types.h>
+#include <grub/i18n.h>
+
+/* GUID. */
+static grub_efi_guid_t serial_io_guid = GRUB_EFI_SERIAL_IO_GUID;
+
+static void
+do_real_config (struct grub_serial_port *port)
+{
+ grub_efi_status_t status = GRUB_EFI_SUCCESS;
+ const grub_efi_parity_type_t parities[] = {
+ [GRUB_SERIAL_PARITY_NONE] = GRUB_EFI_SERIAL_NO_PARITY,
+ [GRUB_SERIAL_PARITY_ODD] = GRUB_EFI_SERIAL_ODD_PARITY,
+ [GRUB_SERIAL_PARITY_EVEN] = GRUB_EFI_SERIAL_EVEN_PARITY
+ };
+ const grub_efi_stop_bits_t stop_bits[] = {
+ [GRUB_SERIAL_STOP_BITS_1] = GRUB_EFI_SERIAL_1_STOP_BIT,
+ [GRUB_SERIAL_STOP_BITS_1_5] = GRUB_EFI_SERIAL_1_5_STOP_BITS,
+ [GRUB_SERIAL_STOP_BITS_2] = GRUB_EFI_SERIAL_2_STOP_BITS,
+ };
+
+ if (port->configured)
+ return;
+
+ status = efi_call_7 (port->interface->set_attributes, port->interface,
+ port->config.speed,
+ 0, 0, parities[port->config.parity],
+ port->config.word_len,
+ stop_bits[port->config.stop_bits]);
+ if (status != GRUB_EFI_SUCCESS)
+ port->broken = 1;
+
+ status = efi_call_2 (port->interface->set_control_bits, port->interface,
+ port->config.rtscts ? 0x4002 : 0x2);
+
+ port->configured = 1;
+}
+
+/* Fetch a key. */
+static int
+serial_hw_fetch (struct grub_serial_port *port)
+{
+ grub_efi_uintn_t bufsize = 1;
+ char c;
+ grub_efi_status_t status = GRUB_EFI_SUCCESS;
+ do_real_config (port);
+ if (port->broken)
+ return -1;
+
+ status = efi_call_3 (port->interface->read, port->interface, &bufsize, &c);
+ if (status != GRUB_EFI_SUCCESS || bufsize == 0)
+ return -1;
+
+ return c;
+}
+
+/* Put a character. */
+static void
+serial_hw_put (struct grub_serial_port *port, const int c)
+{
+ grub_efi_uintn_t bufsize = 1;
+ char c0 = c;
+
+ do_real_config (port);
+
+ if (port->broken)
+ return;
+
+ efi_call_3 (port->interface->write, port->interface, &bufsize, &c0);
+}
+
+/* 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,
+ struct grub_serial_config *config)
+{
+ 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_efiserial_driver =
+ {
+ .configure = serial_hw_configure,
+ .fetch = serial_hw_fetch,
+ .put = serial_hw_put
+ };
+
+void
+grub_efiserial_init (void)
+{
+ grub_efi_uintn_t num_handles;
+ grub_efi_handle_t *handles;
+ grub_efi_handle_t *handle;
+ int num_serial = 0;
+ grub_err_t err;
+
+ /* Find handles which support the disk io interface. */
+ handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &serial_io_guid,
+ 0, &num_handles);
+ if (! handles)
+ return;
+
+ /* Make a linked list of devices. */
+ for (handle = handles; num_handles--; handle++)
+ {
+ struct grub_serial_port *port;
+ struct grub_efi_serial_io_interface *sio;
+
+ sio = grub_efi_open_protocol (*handle, &serial_io_guid,
+ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (! sio)
+ /* This should not happen... Why? */
+ continue;
+
+ port = grub_zalloc (sizeof (*port));
+ if (!port)
+ return;
+
+ port->name = grub_malloc (sizeof ("efiXXXXXXXXXXXXXXXXXXXX"));
+ if (!port->name)
+ {
+ grub_free (port);
+ return;
+ }
+ grub_snprintf (port->name, sizeof ("efiXXXXXXXXXXXXXXXXXXXX"),
+ "efi%d", num_serial++);
+
+ port->driver = &grub_efiserial_driver;
+ port->interface = sio;
+ err = grub_serial_config_defaults (port);
+ if (err)
+ grub_print_error ();
+
+ grub_serial_register (port);
+ }
+
+ grub_free (handles);
+
+ return;
+}
diff --git a/grub-core/term/gfxterm.c b/grub-core/term/gfxterm.c
new file mode 100644
index 0000000..b40fcce
--- /dev/null
+++ b/grub-core/term/gfxterm.c
@@ -0,0 +1,1160 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2006,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/term.h>
+#include <grub/types.h>
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/font.h>
+#include <grub/mm.h>
+#include <grub/env.h>
+#include <grub/video.h>
+#include <grub/gfxterm.h>
+#include <grub/bitmap.h>
+#include <grub/command.h>
+#include <grub/extcmd.h>
+#include <grub/i18n.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define DEFAULT_VIDEO_MODE "auto"
+#define DEFAULT_BORDER_WIDTH 10
+
+#define DEFAULT_STANDARD_COLOR 0x07
+
+struct grub_dirty_region
+{
+ int top_left_x;
+ int top_left_y;
+ int bottom_right_x;
+ int bottom_right_y;
+};
+
+struct grub_colored_char
+{
+ /* An Unicode codepoint. */
+ struct grub_unicode_glyph code;
+
+ /* Color values. */
+ grub_video_color_t fg_color;
+ grub_video_color_t bg_color;
+};
+
+struct grub_virtual_screen
+{
+ /* Dimensions of the virtual screen in pixels. */
+ unsigned int width;
+ unsigned int height;
+
+ /* Offset in the display in pixels. */
+ unsigned int offset_x;
+ unsigned int offset_y;
+
+ /* TTY Character sizes in pixes. */
+ unsigned int normal_char_width;
+ unsigned int normal_char_height;
+
+ /* Virtual screen TTY size in characters. */
+ unsigned int columns;
+ unsigned int rows;
+
+ /* Current cursor location in characters. */
+ unsigned int cursor_x;
+ unsigned int cursor_y;
+
+ /* Current cursor state. */
+ int cursor_state;
+
+ /* Font settings. */
+ grub_font_t font;
+
+ /* Terminal color settings. */
+ grub_uint8_t standard_color_setting;
+ grub_uint8_t term_color;
+
+ /* Color settings. */
+ grub_video_color_t fg_color;
+ grub_video_color_t bg_color;
+ grub_video_color_t bg_color_display;
+
+ /* Text buffer for virtual screen. Contains (columns * rows) number
+ of entries. */
+ struct grub_colored_char *text_buffer;
+
+ int total_scroll;
+
+ int functional;
+};
+
+struct grub_gfxterm_window
+{
+ unsigned x;
+ unsigned y;
+ unsigned width;
+ unsigned height;
+ int double_repaint;
+};
+
+static struct grub_video_render_target *render_target;
+void (*grub_gfxterm_decorator_hook) (void) = NULL;
+static struct grub_gfxterm_window window;
+static struct grub_virtual_screen virtual_screen;
+static int repaint_scheduled = 0;
+static int repaint_was_scheduled = 0;
+
+static void destroy_window (void);
+
+static struct grub_video_render_target *text_layer;
+
+struct grub_gfxterm_background grub_gfxterm_background;
+
+static struct grub_dirty_region dirty_region;
+
+static void dirty_region_reset (void);
+
+static int dirty_region_is_empty (void);
+
+static void dirty_region_add (int x, int y,
+ unsigned int width, unsigned int height);
+
+static unsigned int calculate_normal_character_width (grub_font_t font);
+
+static unsigned char calculate_character_width (struct grub_font_glyph *glyph);
+
+static void grub_gfxterm_refresh (struct grub_term_output *term __attribute__ ((unused)));
+
+static grub_size_t
+grub_gfxterm_getcharwidth (struct grub_term_output *term __attribute__ ((unused)),
+ const struct grub_unicode_glyph *c);
+
+static void
+set_term_color (grub_uint8_t term_color)
+{
+ struct grub_video_render_target *old_target;
+
+ /* Save previous target and switch to text layer. */
+ grub_video_get_active_render_target (&old_target);
+ grub_video_set_active_render_target (text_layer);
+
+ /* Map terminal color to text layer compatible video colors. */
+ virtual_screen.fg_color = grub_video_map_color(term_color & 0x0f);
+
+ /* Special case: use black as transparent color. */
+ if (((term_color >> 4) & 0x0f) == 0)
+ {
+ virtual_screen.bg_color = grub_video_map_rgba(0, 0, 0, 0);
+ }
+ else
+ {
+ virtual_screen.bg_color = grub_video_map_color((term_color >> 4) & 0x0f);
+ }
+
+ /* Restore previous target. */
+ grub_video_set_active_render_target (old_target);
+}
+
+static void
+clear_char (struct grub_colored_char *c)
+{
+ grub_unicode_destroy_glyph (&c->code);
+ grub_unicode_set_glyph_from_code (&c->code, ' ');
+ c->fg_color = virtual_screen.fg_color;
+ c->bg_color = virtual_screen.bg_color;
+}
+
+static void
+grub_virtual_screen_free (void)
+{
+ virtual_screen.functional = 0;
+
+ /* If virtual screen has been allocated, free it. */
+ if (virtual_screen.text_buffer != 0)
+ {
+ unsigned i;
+ for (i = 0;
+ i < virtual_screen.columns * virtual_screen.rows;
+ i++)
+ grub_unicode_destroy_glyph (&virtual_screen.text_buffer[i].code);
+ grub_free (virtual_screen.text_buffer);
+ }
+
+ /* Reset virtual screen data. */
+ grub_memset (&virtual_screen, 0, sizeof (virtual_screen));
+
+ /* Free render targets. */
+ grub_video_delete_render_target (text_layer);
+ text_layer = 0;
+}
+
+static grub_err_t
+grub_virtual_screen_setup (unsigned int x, unsigned int y,
+ unsigned int width, unsigned int height,
+ grub_font_t font)
+{
+ unsigned int i;
+
+ /* Free old virtual screen. */
+ grub_virtual_screen_free ();
+
+ /* Initialize with default data. */
+ virtual_screen.font = font;
+ virtual_screen.width = width;
+ virtual_screen.height = height;
+ virtual_screen.offset_x = x;
+ virtual_screen.offset_y = y;
+ virtual_screen.normal_char_width =
+ calculate_normal_character_width (virtual_screen.font);
+ virtual_screen.normal_char_height =
+ grub_font_get_max_char_height (virtual_screen.font);
+ if (virtual_screen.normal_char_height == 0)
+ virtual_screen.normal_char_height = 16;
+ virtual_screen.cursor_x = 0;
+ virtual_screen.cursor_y = 0;
+ virtual_screen.cursor_state = 1;
+ virtual_screen.total_scroll = 0;
+
+ /* Calculate size of text buffer. */
+ virtual_screen.columns = virtual_screen.width / virtual_screen.normal_char_width;
+ virtual_screen.rows = virtual_screen.height / virtual_screen.normal_char_height;
+
+ /*
+ * There must be a minimum number of rows and columns for the screen to
+ * make sense. Arbitrarily pick half of 80x24. If either dimensions is 0
+ * we would allocate 0 bytes for the text_buffer.
+ */
+ if (virtual_screen.columns < 40 || virtual_screen.rows < 12)
+ return grub_error (GRUB_ERR_BAD_FONT,
+ "font: glyphs too large to fit on screen");
+
+ /* Allocate memory for text buffer. */
+ virtual_screen.text_buffer =
+ (struct grub_colored_char *) grub_malloc (virtual_screen.columns
+ * virtual_screen.rows
+ * sizeof (*virtual_screen.text_buffer));
+ if (grub_errno != GRUB_ERR_NONE)
+ return grub_errno;
+
+ /* Create new render target for text layer. */
+ grub_video_create_render_target (&text_layer,
+ virtual_screen.width,
+ virtual_screen.height,
+ GRUB_VIDEO_MODE_TYPE_INDEX_COLOR
+ | GRUB_VIDEO_MODE_TYPE_ALPHA);
+ if (grub_errno != GRUB_ERR_NONE)
+ return grub_errno;
+
+ /* As we want to have colors compatible with rendering target,
+ we can only have those after mode is initialized. */
+ grub_video_set_active_render_target (text_layer);
+
+ virtual_screen.standard_color_setting = DEFAULT_STANDARD_COLOR;
+
+ virtual_screen.term_color = virtual_screen.standard_color_setting;
+
+ set_term_color (virtual_screen.term_color);
+
+ grub_video_set_active_render_target (render_target);
+
+ virtual_screen.bg_color_display =
+ grub_video_map_rgba_color (grub_gfxterm_background.default_bg_color);
+
+ /* Clear out text buffer. */
+ for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++)
+ {
+ virtual_screen.text_buffer[i].code.ncomb = 0;
+ clear_char (&(virtual_screen.text_buffer[i]));
+ }
+ if (grub_errno)
+ return grub_errno;
+
+ virtual_screen.functional = 1;
+
+ return GRUB_ERR_NONE;
+}
+
+void
+grub_gfxterm_schedule_repaint (void)
+{
+ repaint_scheduled = 1;
+}
+
+grub_err_t
+grub_gfxterm_set_window (struct grub_video_render_target *target,
+ int x, int y, int width, int height,
+ int double_repaint,
+ grub_font_t font, int border_width)
+{
+ /* Clean up any prior instance. */
+ destroy_window ();
+
+ /* Set the render target. */
+ render_target = target;
+
+ /* Create virtual screen. */
+ if (grub_virtual_screen_setup (border_width, border_width,
+ width - 2 * border_width,
+ height - 2 * border_width,
+ font)
+ != GRUB_ERR_NONE)
+ {
+ return grub_errno;
+ }
+
+ /* Set window bounds. */
+ window.x = x;
+ window.y = y;
+ window.width = width;
+ window.height = height;
+ window.double_repaint = double_repaint;
+
+ dirty_region_reset ();
+ grub_gfxterm_schedule_repaint ();
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_gfxterm_fullscreen (void)
+{
+ const char *font_name;
+ struct grub_video_mode_info mode_info;
+ grub_video_color_t color;
+ grub_err_t err;
+ int double_redraw;
+ grub_font_t font;
+
+ err = grub_video_get_info (&mode_info);
+ /* Figure out what mode we ended up. */
+ if (err)
+ return err;
+
+ grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
+
+ double_redraw = mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED
+ && !(mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP);
+
+ /* Make sure screen is set to the default background color. */
+ color = grub_video_map_rgba_color (grub_gfxterm_background.default_bg_color);
+ grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height);
+ if (double_redraw)
+ {
+ grub_video_swap_buffers ();
+ grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height);
+ }
+
+ /* Select the font to use. */
+ font_name = grub_env_get ("gfxterm_font");
+ if (! font_name)
+ font_name = ""; /* Allow fallback to any font. */
+
+ font = grub_font_get (font_name);
+ if (!font)
+ return grub_error (GRUB_ERR_BAD_FONT, "no font loaded");
+
+ grub_gfxterm_decorator_hook = NULL;
+
+ return grub_gfxterm_set_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY,
+ 0, 0, mode_info.width, mode_info.height,
+ double_redraw,
+ font, DEFAULT_BORDER_WIDTH);
+}
+
+static grub_err_t
+grub_gfxterm_term_init (struct grub_term_output *term __attribute__ ((unused)))
+{
+ char *tmp;
+ grub_err_t err;
+ const char *modevar;
+
+ /* Parse gfxmode environment variable if set. */
+ modevar = grub_env_get ("gfxmode");
+ if (! modevar || *modevar == 0)
+ err = grub_video_set_mode (DEFAULT_VIDEO_MODE,
+ GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
+ else
+ {
+ tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar);
+ if (!tmp)
+ return grub_errno;
+ err = grub_video_set_mode (tmp, GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
+ grub_free (tmp);
+ }
+
+ if (err)
+ return err;
+
+ err = grub_gfxterm_fullscreen ();
+ if (err)
+ grub_video_restore ();
+
+ return err;
+}
+
+static void
+destroy_window (void)
+{
+ grub_virtual_screen_free ();
+}
+
+static grub_err_t
+grub_gfxterm_term_fini (struct grub_term_output *term __attribute__ ((unused)))
+{
+ unsigned i;
+ destroy_window ();
+ grub_video_restore ();
+
+ for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++)
+ {
+ grub_unicode_destroy_glyph (&virtual_screen.text_buffer[i].code);
+ virtual_screen.text_buffer[i].code.ncomb = 0;
+ virtual_screen.text_buffer[i].code.base = 0;
+ }
+
+ /* Clear error state. */
+ grub_errno = GRUB_ERR_NONE;
+ return GRUB_ERR_NONE;
+}
+
+static void
+redraw_screen_rect (unsigned int x, unsigned int y,
+ unsigned int width, unsigned int height)
+{
+ grub_video_color_t color;
+ grub_video_rect_t saved_view;
+
+ grub_video_set_active_render_target (render_target);
+ /* Save viewport and set it to our window. */
+ grub_video_get_viewport ((unsigned *) &saved_view.x,
+ (unsigned *) &saved_view.y,
+ (unsigned *) &saved_view.width,
+ (unsigned *) &saved_view.height);
+ grub_video_set_viewport (window.x, window.y, window.width, window.height);
+
+ if (grub_gfxterm_background.bitmap)
+ {
+ /* Render bitmap as background. */
+ grub_video_blit_bitmap (grub_gfxterm_background.bitmap,
+ GRUB_VIDEO_BLIT_REPLACE, x, y,
+ x, y,
+ width, height);
+
+ /* If bitmap is smaller than requested blit area, use background
+ color. */
+ color = virtual_screen.bg_color_display;
+
+ /* Fill right side of the bitmap if needed. */
+ if ((x + width >= grub_gfxterm_background.bitmap->mode_info.width)
+ && (y < grub_gfxterm_background.bitmap->mode_info.height))
+ {
+ int w = (x + width) - grub_gfxterm_background.bitmap->mode_info.width;
+ int h = height;
+ unsigned int tx = x;
+
+ if (y + height >= grub_gfxterm_background.bitmap->mode_info.height)
+ {
+ h = grub_gfxterm_background.bitmap->mode_info.height - y;
+ }
+
+ if (grub_gfxterm_background.bitmap->mode_info.width > tx)
+ {
+ tx = grub_gfxterm_background.bitmap->mode_info.width;
+ }
+
+ /* Render background layer. */
+ grub_video_fill_rect (color, tx, y, w, h);
+ }
+
+ /* Fill bottom side of the bitmap if needed. */
+ if (y + height >= grub_gfxterm_background.bitmap->mode_info.height)
+ {
+ int h = (y + height) - grub_gfxterm_background.bitmap->mode_info.height;
+ unsigned int ty = y;
+
+ if (grub_gfxterm_background.bitmap->mode_info.height > ty)
+ {
+ ty = grub_gfxterm_background.bitmap->mode_info.height;
+ }
+
+ /* Render background layer. */
+ grub_video_fill_rect (color, x, ty, width, h);
+ }
+ }
+ else
+ {
+ /* Render background layer. */
+ color = virtual_screen.bg_color_display;
+ grub_video_fill_rect (color, x, y, width, height);
+ }
+
+ if (grub_gfxterm_background.blend_text_bg)
+ /* Render text layer as blended. */
+ grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_BLEND, x, y,
+ x - virtual_screen.offset_x,
+ y - virtual_screen.offset_y,
+ width, height);
+ else
+ /* Render text layer as replaced (to get texts background color). */
+ grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_REPLACE, x, y,
+ x - virtual_screen.offset_x,
+ y - virtual_screen.offset_y,
+ width, height);
+
+ /* Restore saved viewport. */
+ grub_video_set_viewport (saved_view.x, saved_view.y,
+ saved_view.width, saved_view.height);
+ grub_video_set_active_render_target (render_target);
+}
+
+static void
+dirty_region_reset (void)
+{
+ dirty_region.top_left_x = -1;
+ dirty_region.top_left_y = -1;
+ dirty_region.bottom_right_x = -1;
+ dirty_region.bottom_right_y = -1;
+ repaint_was_scheduled = 0;
+}
+
+static int
+dirty_region_is_empty (void)
+{
+ if ((dirty_region.top_left_x == -1)
+ || (dirty_region.top_left_y == -1)
+ || (dirty_region.bottom_right_x == -1)
+ || (dirty_region.bottom_right_y == -1))
+ return 1;
+ return 0;
+}
+
+static void
+dirty_region_add_real (int x, int y, unsigned int width, unsigned int height)
+{
+ if (dirty_region_is_empty ())
+ {
+ dirty_region.top_left_x = x;
+ dirty_region.top_left_y = y;
+ dirty_region.bottom_right_x = x + width - 1;
+ dirty_region.bottom_right_y = y + height - 1;
+ }
+ else
+ {
+ if (x < dirty_region.top_left_x)
+ dirty_region.top_left_x = x;
+ if (y < dirty_region.top_left_y)
+ dirty_region.top_left_y = y;
+ if ((x + (int)width - 1) > dirty_region.bottom_right_x)
+ dirty_region.bottom_right_x = x + width - 1;
+ if ((y + (int)height - 1) > dirty_region.bottom_right_y)
+ dirty_region.bottom_right_y = y + height - 1;
+ }
+}
+
+static void
+dirty_region_add (int x, int y, unsigned int width, unsigned int height)
+{
+ if ((width == 0) || (height == 0))
+ return;
+
+ if (repaint_scheduled)
+ {
+ dirty_region_add_real (0, 0,
+ window.width, window.height);
+ repaint_scheduled = 0;
+ repaint_was_scheduled = 1;
+ }
+ dirty_region_add_real (x, y, width, height);
+}
+
+static void
+dirty_region_add_virtualscreen (void)
+{
+ /* Mark virtual screen as dirty. */
+ dirty_region_add (virtual_screen.offset_x, virtual_screen.offset_y,
+ virtual_screen.width, virtual_screen.height);
+}
+
+
+static void
+dirty_region_redraw (void)
+{
+ int x;
+ int y;
+ int width;
+ int height;
+
+ if (dirty_region_is_empty ())
+ return;
+
+ x = dirty_region.top_left_x;
+ y = dirty_region.top_left_y;
+
+ width = dirty_region.bottom_right_x - x + 1;
+ height = dirty_region.bottom_right_y - y + 1;
+
+ if (repaint_was_scheduled && grub_gfxterm_decorator_hook)
+ grub_gfxterm_decorator_hook ();
+
+ redraw_screen_rect (x, y, width, height);
+}
+
+static inline void
+paint_char (unsigned cx, unsigned cy)
+{
+ struct grub_colored_char *p;
+ struct grub_font_glyph *glyph;
+ grub_video_color_t color;
+ grub_video_color_t bgcolor;
+ unsigned int x;
+ unsigned int y;
+ int ascent;
+ unsigned int height;
+ unsigned int width;
+
+ if (cy + virtual_screen.total_scroll >= virtual_screen.rows)
+ return;
+
+ /* Find out active character. */
+ p = (virtual_screen.text_buffer
+ + cx + (cy * virtual_screen.columns));
+
+ if (!p->code.base)
+ return;
+
+ /* Get glyph for character. */
+ glyph = grub_font_construct_glyph (virtual_screen.font, &p->code);
+ if (!glyph)
+ {
+ grub_errno = GRUB_ERR_NONE;
+ return;
+ }
+ ascent = grub_font_get_ascent (virtual_screen.font);
+
+ width = virtual_screen.normal_char_width * calculate_character_width(glyph);
+ height = virtual_screen.normal_char_height;
+
+ color = p->fg_color;
+ bgcolor = p->bg_color;
+
+ x = cx * virtual_screen.normal_char_width;
+ y = (cy + virtual_screen.total_scroll) * virtual_screen.normal_char_height;
+
+ /* Render glyph to text layer. */
+ grub_video_set_active_render_target (text_layer);
+ grub_video_fill_rect (bgcolor, x, y, width, height);
+ grub_font_draw_glyph (glyph, color, x, y + ascent);
+ grub_video_set_active_render_target (render_target);
+
+ /* Mark character to be drawn. */
+ dirty_region_add (virtual_screen.offset_x + x, virtual_screen.offset_y + y,
+ width, height);
+}
+
+static inline void
+write_char (void)
+{
+ paint_char (virtual_screen.cursor_x, virtual_screen.cursor_y);
+}
+
+static inline void
+draw_cursor (int show)
+{
+ unsigned int x;
+ unsigned int y;
+ unsigned int width;
+ unsigned int height;
+ unsigned int ascent;
+ grub_video_color_t color;
+
+ write_char ();
+
+ if (!show)
+ return;
+
+ if (virtual_screen.cursor_y + virtual_screen.total_scroll
+ >= virtual_screen.rows)
+ return;
+
+ /* Ensure that cursor doesn't go outside of character box. */
+ ascent = grub_font_get_ascent(virtual_screen.font);
+ if (ascent > virtual_screen.normal_char_height - 2)
+ ascent = virtual_screen.normal_char_height - 2;
+
+ /* Determine cursor properties and position on text layer. */
+ x = virtual_screen.cursor_x * virtual_screen.normal_char_width;
+ width = virtual_screen.normal_char_width;
+ color = virtual_screen.fg_color;
+ y = ((virtual_screen.cursor_y + virtual_screen.total_scroll)
+ * virtual_screen.normal_char_height
+ + ascent);
+ height = 2;
+
+ /* Render cursor to text layer. */
+ grub_video_set_active_render_target (text_layer);
+ grub_video_fill_rect (color, x, y, width, height);
+ grub_video_set_active_render_target (render_target);
+
+ /* Mark cursor to be redrawn. */
+ dirty_region_add (virtual_screen.offset_x + x,
+ virtual_screen.offset_y + y,
+ width, height);
+}
+
+static void
+real_scroll (void)
+{
+ unsigned int i, j, was_scroll;
+ grub_video_color_t color;
+
+ if (!virtual_screen.total_scroll)
+ return;
+
+ /* If we have bitmap, re-draw screen, otherwise scroll physical screen too. */
+ if (grub_gfxterm_background.bitmap)
+ {
+ /* Scroll physical screen. */
+ grub_video_set_active_render_target (text_layer);
+ color = virtual_screen.bg_color;
+ grub_video_scroll (color, 0, -virtual_screen.normal_char_height
+ * virtual_screen.total_scroll);
+
+ /* Mark virtual screen to be redrawn. */
+ dirty_region_add_virtualscreen ();
+ }
+ else
+ {
+ grub_video_rect_t saved_view;
+
+ /* Remove cursor. */
+ draw_cursor (0);
+
+ grub_video_set_active_render_target (render_target);
+
+ i = window.double_repaint ? 2 : 1;
+
+ color = virtual_screen.bg_color_display;
+
+ while (i--)
+ {
+ /* Save viewport and set it to our window. */
+ grub_video_get_viewport ((unsigned *) &saved_view.x,
+ (unsigned *) &saved_view.y,
+ (unsigned *) &saved_view.width,
+ (unsigned *) &saved_view.height);
+
+ grub_video_set_viewport (window.x, window.y, window.width,
+ window.height);
+
+ /* Clear new border area. */
+ grub_video_fill_rect (color,
+ virtual_screen.offset_x,
+ virtual_screen.offset_y,
+ virtual_screen.width,
+ virtual_screen.normal_char_height
+ * virtual_screen.total_scroll);
+
+ grub_video_set_active_render_target (render_target);
+ dirty_region_redraw ();
+
+ /* Scroll physical screen. */
+ grub_video_scroll (color, 0, -virtual_screen.normal_char_height
+ * virtual_screen.total_scroll);
+
+ /* Restore saved viewport. */
+ grub_video_set_viewport (saved_view.x, saved_view.y,
+ saved_view.width, saved_view.height);
+
+ if (i)
+ grub_video_swap_buffers ();
+ }
+ dirty_region_reset ();
+
+ /* Scroll physical screen. */
+ grub_video_set_active_render_target (text_layer);
+ color = virtual_screen.bg_color;
+ grub_video_scroll (color, 0, -virtual_screen.normal_char_height
+ * virtual_screen.total_scroll);
+
+ grub_video_set_active_render_target (render_target);
+
+ }
+
+ was_scroll = virtual_screen.total_scroll;
+ virtual_screen.total_scroll = 0;
+
+ if (was_scroll > virtual_screen.rows)
+ was_scroll = virtual_screen.rows;
+
+ /* Draw shadow part. */
+ for (i = virtual_screen.rows - was_scroll;
+ i < virtual_screen.rows; i++)
+ for (j = 0; j < virtual_screen.columns; j++)
+ paint_char (j, i);
+
+ /* Draw cursor if visible. */
+ if (virtual_screen.cursor_state)
+ draw_cursor (1);
+}
+
+static void
+scroll_up (void)
+{
+ unsigned int i;
+
+ /* Clear first line in text buffer. */
+ for (i = 0; i < virtual_screen.columns; i++)
+ grub_unicode_destroy_glyph (&virtual_screen.text_buffer[i].code);
+
+ /* Scroll text buffer with one line to up. */
+ grub_memmove (virtual_screen.text_buffer,
+ virtual_screen.text_buffer + virtual_screen.columns,
+ sizeof (*virtual_screen.text_buffer)
+ * virtual_screen.columns
+ * (virtual_screen.rows - 1));
+
+ /* Clear last line in text buffer. */
+ for (i = virtual_screen.columns * (virtual_screen.rows - 1);
+ i < virtual_screen.columns * virtual_screen.rows;
+ i++)
+ clear_char (&(virtual_screen.text_buffer[i]));
+
+ virtual_screen.total_scroll++;
+}
+
+static void
+grub_gfxterm_putchar (struct grub_term_output *term,
+ const struct grub_unicode_glyph *c)
+{
+ if (!virtual_screen.functional)
+ return;
+
+ if (c->base == '\a')
+ /* FIXME */
+ return;
+
+ /* Erase current cursor, if any. */
+ if (virtual_screen.cursor_state)
+ draw_cursor (0);
+
+ if (c->base == '\b' || c->base == '\n' || c->base == '\r')
+ {
+ switch (c->base)
+ {
+ case '\b':
+ if (virtual_screen.cursor_x > 0)
+ virtual_screen.cursor_x--;
+ break;
+
+ case '\n':
+ if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
+ scroll_up ();
+ else
+ virtual_screen.cursor_y++;
+ break;
+
+ case '\r':
+ virtual_screen.cursor_x = 0;
+ break;
+ }
+ }
+ else
+ {
+ struct grub_colored_char *p;
+ unsigned char char_width;
+
+ /* Calculate actual character width for glyph. This is number of
+ times of normal_font_width. */
+ char_width = grub_gfxterm_getcharwidth (term, c);
+
+ /* If we are about to exceed line length, wrap to next line. */
+ if (virtual_screen.cursor_x + char_width > virtual_screen.columns)
+ {
+ if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
+ scroll_up ();
+ else
+ virtual_screen.cursor_y++;
+ }
+
+ /* Find position on virtual screen, and fill information. */
+ p = (virtual_screen.text_buffer +
+ virtual_screen.cursor_x +
+ virtual_screen.cursor_y * virtual_screen.columns);
+ grub_unicode_destroy_glyph (&p->code);
+ grub_unicode_set_glyph (&p->code, c);
+ grub_errno = GRUB_ERR_NONE;
+ p->fg_color = virtual_screen.fg_color;
+ p->bg_color = virtual_screen.bg_color;
+
+ /* If we have large glyph, add fixup info. */
+ if (char_width > 1)
+ {
+ unsigned i;
+
+ for (i = 1; i < char_width && p + i <
+ virtual_screen.text_buffer + virtual_screen.columns
+ * virtual_screen.rows; i++)
+ {
+ grub_unicode_destroy_glyph (&p[i].code);
+ p[i].code.base = 0;
+ }
+ }
+
+ /* Draw glyph. */
+ write_char ();
+
+ /* Make sure we scroll screen when needed and wrap line correctly. */
+ virtual_screen.cursor_x += char_width;
+ if (virtual_screen.cursor_x >= virtual_screen.columns)
+ {
+ virtual_screen.cursor_x = 0;
+
+ if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
+ scroll_up ();
+ else
+ virtual_screen.cursor_y++;
+ }
+ }
+
+ /* Redraw cursor if it should be visible. */
+ /* Note: This will redraw the character as well, which means that the
+ above call to write_char is redundant when the cursor is showing. */
+ if (virtual_screen.cursor_state)
+ draw_cursor (1);
+}
+
+/* Use ASCII characters to determine normal character width. */
+static unsigned int
+calculate_normal_character_width (grub_font_t font)
+{
+ struct grub_font_glyph *glyph;
+ unsigned int width = 0;
+ unsigned int i;
+
+ /* Get properties of every printable ASCII character. */
+ for (i = 32; i < 127; i++)
+ {
+ glyph = grub_font_get_glyph (font, i);
+
+ /* Skip unknown characters. Should never happen on normal conditions. */
+ if (! glyph)
+ continue;
+
+ if (glyph->device_width > width)
+ width = glyph->device_width;
+ }
+ if (!width)
+ return 8;
+
+ return width;
+}
+
+static unsigned char
+calculate_character_width (struct grub_font_glyph *glyph)
+{
+ if (! glyph || glyph->device_width == 0)
+ return 1;
+
+ return (glyph->device_width
+ + (virtual_screen.normal_char_width - 1))
+ / virtual_screen.normal_char_width;
+}
+
+static grub_size_t
+grub_gfxterm_getcharwidth (struct grub_term_output *term __attribute__ ((unused)),
+ const struct grub_unicode_glyph *c)
+{
+ int dev_width;
+ dev_width = grub_font_get_constructed_device_width (virtual_screen.font, c);
+
+ if (dev_width == 0)
+ return 1;
+
+ return (dev_width + (virtual_screen.normal_char_width - 1))
+ / virtual_screen.normal_char_width;
+}
+
+static struct grub_term_coordinate
+grub_virtual_screen_getwh (struct grub_term_output *term __attribute__ ((unused)))
+{
+ return (struct grub_term_coordinate) { virtual_screen.columns, virtual_screen.rows };
+}
+
+static struct grub_term_coordinate
+grub_virtual_screen_getxy (struct grub_term_output *term __attribute__ ((unused)))
+{
+ return (struct grub_term_coordinate) { virtual_screen.cursor_x, virtual_screen.cursor_y };
+}
+
+static void
+grub_gfxterm_gotoxy (struct grub_term_output *term __attribute__ ((unused)),
+ struct grub_term_coordinate pos)
+{
+ if (pos.x >= virtual_screen.columns)
+ pos.x = virtual_screen.columns - 1;
+
+ if (pos.y >= virtual_screen.rows)
+ pos.y = virtual_screen.rows - 1;
+
+ /* Erase current cursor, if any. */
+ if (virtual_screen.cursor_state)
+ draw_cursor (0);
+
+ virtual_screen.cursor_x = pos.x;
+ virtual_screen.cursor_y = pos.y;
+
+ /* Draw cursor if visible. */
+ if (virtual_screen.cursor_state)
+ draw_cursor (1);
+}
+
+static void
+grub_virtual_screen_cls (struct grub_term_output *term __attribute__ ((unused)))
+{
+ grub_uint32_t i;
+
+ for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++)
+ clear_char (&(virtual_screen.text_buffer[i]));
+
+ virtual_screen.cursor_x = virtual_screen.cursor_y = 0;
+}
+
+static void
+grub_gfxterm_cls (struct grub_term_output *term)
+{
+ grub_video_color_t color;
+
+ /* Clear virtual screen. */
+ grub_virtual_screen_cls (term);
+
+ /* Clear text layer. */
+ grub_video_set_active_render_target (text_layer);
+ color = virtual_screen.bg_color;
+ grub_video_fill_rect (color, 0, 0,
+ virtual_screen.width, virtual_screen.height);
+ grub_video_set_active_render_target (render_target);
+
+ /* Mark virtual screen to be redrawn. */
+ dirty_region_add_virtualscreen ();
+
+ grub_gfxterm_refresh (term);
+}
+
+static void
+grub_virtual_screen_setcolorstate (struct grub_term_output *term __attribute__ ((unused)),
+ grub_term_color_state state)
+{
+ switch (state)
+ {
+ case GRUB_TERM_COLOR_STANDARD:
+ virtual_screen.term_color = virtual_screen.standard_color_setting;
+ break;
+
+ case GRUB_TERM_COLOR_NORMAL:
+ virtual_screen.term_color = grub_term_normal_color;
+ break;
+
+ case GRUB_TERM_COLOR_HIGHLIGHT:
+ virtual_screen.term_color = grub_term_highlight_color;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Change color to virtual terminal. */
+ set_term_color (virtual_screen.term_color);
+}
+
+static void
+grub_gfxterm_setcursor (struct grub_term_output *term __attribute__ ((unused)),
+ int on)
+{
+ if (virtual_screen.cursor_state != on)
+ {
+ if (virtual_screen.cursor_state)
+ draw_cursor (0);
+ else
+ draw_cursor (1);
+
+ virtual_screen.cursor_state = on;
+ }
+}
+
+static void
+grub_gfxterm_refresh (struct grub_term_output *term __attribute__ ((unused)))
+{
+ real_scroll ();
+
+ /* Redraw only changed regions. */
+ dirty_region_redraw ();
+
+ grub_video_swap_buffers ();
+
+ if (window.double_repaint)
+ dirty_region_redraw ();
+ dirty_region_reset ();
+}
+
+static struct grub_term_output grub_video_term =
+ {
+ .name = "gfxterm",
+ .init = grub_gfxterm_term_init,
+ .fini = grub_gfxterm_term_fini,
+ .putchar = grub_gfxterm_putchar,
+ .getcharwidth = grub_gfxterm_getcharwidth,
+ .getwh = grub_virtual_screen_getwh,
+ .getxy = grub_virtual_screen_getxy,
+ .gotoxy = grub_gfxterm_gotoxy,
+ .cls = grub_gfxterm_cls,
+ .setcolorstate = grub_virtual_screen_setcolorstate,
+ .setcursor = grub_gfxterm_setcursor,
+ .refresh = grub_gfxterm_refresh,
+ .fullscreen = grub_gfxterm_fullscreen,
+ .flags = GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS,
+ .progress_update_divisor = GRUB_PROGRESS_SLOW,
+ .next = 0
+ };
+
+void
+grub_gfxterm_video_update_color (void)
+{
+ struct grub_video_render_target *old_target;
+
+ grub_video_get_active_render_target (&old_target);
+ grub_video_set_active_render_target (text_layer);
+ virtual_screen.bg_color = grub_video_map_rgba_color (grub_gfxterm_background.default_bg_color);
+ grub_video_set_active_render_target (old_target);
+ virtual_screen.bg_color_display =
+ grub_video_map_rgba_color (grub_gfxterm_background.default_bg_color);
+}
+
+void
+grub_gfxterm_get_dimensions (unsigned *width, unsigned *height)
+{
+ *width = window.width;
+ *height = window.height;
+}
+
+GRUB_MOD_INIT(gfxterm)
+{
+ grub_term_register_output ("gfxterm", &grub_video_term);
+}
+
+GRUB_MOD_FINI(gfxterm)
+{
+ grub_term_unregister_output (&grub_video_term);
+}
diff --git a/grub-core/term/gfxterm_background.c b/grub-core/term/gfxterm_background.c
new file mode 100644
index 0000000..8da71a8
--- /dev/null
+++ b/grub-core/term/gfxterm_background.c
@@ -0,0 +1,190 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2006,2007,2008,2009,2013 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/term.h>
+#include <grub/types.h>
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/font.h>
+#include <grub/mm.h>
+#include <grub/env.h>
+#include <grub/video.h>
+#include <grub/gfxterm.h>
+#include <grub/bitmap.h>
+#include <grub/command.h>
+#include <grub/extcmd.h>
+#include <grub/bitmap_scale.h>
+#include <grub/i18n.h>
+#include <grub/color.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+/* Option array indices. */
+enum
+ {
+ BACKGROUND_CMD_ARGINDEX_MODE = 0
+ };
+
+static const struct grub_arg_option background_image_cmd_options[] =
+ {
+ {"mode", 'm', 0, N_("Background image mode."),
+ /* TRANSLATORS: This refers to background image mode (stretched or
+ in left-top corner). Note that GRUB will accept only original
+ keywords stretch and normal, not the translated ones.
+ So please put both in translation
+ e.g. stretch(=%STRETCH%)|normal(=%NORMAL%).
+ The percents mark the translated version. Since many people
+ may not know the word stretch or normal I recommend
+ putting the translation either here or in "Background image mode."
+ string. */
+ N_("stretch|normal"),
+ ARG_TYPE_STRING},
+ {0, 0, 0, 0, 0, 0}
+ };
+
+static grub_err_t
+grub_gfxterm_background_image_cmd (grub_extcmd_context_t ctxt,
+ int argc, char **args)
+{
+ struct grub_arg_list *state = ctxt->state;
+
+ /* Check that we have video adapter active. */
+ if (grub_video_get_info(NULL) != GRUB_ERR_NONE)
+ return grub_errno;
+
+ /* Destroy existing background bitmap if loaded. */
+ if (grub_gfxterm_background.bitmap)
+ {
+ grub_video_bitmap_destroy (grub_gfxterm_background.bitmap);
+ grub_gfxterm_background.bitmap = 0;
+ grub_gfxterm_background.blend_text_bg = 0;
+
+ /* Mark whole screen as dirty. */
+ grub_gfxterm_schedule_repaint ();
+ }
+
+ /* If filename was provided, try to load that. */
+ if (argc >= 1)
+ {
+ /* Try to load new one. */
+ grub_video_bitmap_load (&grub_gfxterm_background.bitmap, args[0]);
+ if (grub_errno != GRUB_ERR_NONE)
+ return grub_errno;
+
+ /* Determine if the bitmap should be scaled to fit the screen. */
+ if (!state[BACKGROUND_CMD_ARGINDEX_MODE].set
+ || grub_strcmp (state[BACKGROUND_CMD_ARGINDEX_MODE].arg,
+ "stretch") == 0)
+ {
+ unsigned int width, height;
+ grub_gfxterm_get_dimensions (&width, &height);
+ if (width
+ != grub_video_bitmap_get_width (grub_gfxterm_background.bitmap)
+ || height
+ != grub_video_bitmap_get_height (grub_gfxterm_background.bitmap))
+ {
+ struct grub_video_bitmap *scaled_bitmap;
+ grub_video_bitmap_create_scaled (&scaled_bitmap,
+ width,
+ height,
+ grub_gfxterm_background.bitmap,
+ GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+ if (grub_errno == GRUB_ERR_NONE)
+ {
+ /* Replace the original bitmap with the scaled one. */
+ grub_video_bitmap_destroy (grub_gfxterm_background.bitmap);
+ grub_gfxterm_background.bitmap = scaled_bitmap;
+ }
+ }
+ }
+
+ /* If bitmap was loaded correctly, display it. */
+ if (grub_gfxterm_background.bitmap)
+ {
+ grub_gfxterm_background.blend_text_bg = 1;
+
+ /* Mark whole screen as dirty. */
+ grub_gfxterm_schedule_repaint ();
+ }
+ }
+
+ /* All was ok. */
+ grub_errno = GRUB_ERR_NONE;
+ return grub_errno;
+}
+
+static grub_err_t
+grub_gfxterm_background_color_cmd (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char **args)
+{
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
+
+ /* Check that we have video adapter active. */
+ if (grub_video_get_info (NULL) != GRUB_ERR_NONE)
+ return grub_errno;
+
+ if (grub_video_parse_color (args[0],
+ &grub_gfxterm_background.default_bg_color)
+ != GRUB_ERR_NONE)
+ return grub_errno;
+
+ /* Destroy existing background bitmap if loaded. */
+ if (grub_gfxterm_background.bitmap)
+ {
+ grub_video_bitmap_destroy (grub_gfxterm_background.bitmap);
+ grub_gfxterm_background.bitmap = 0;
+
+ /* Mark whole screen as dirty. */
+ grub_gfxterm_schedule_repaint ();
+ }
+
+ /* Set the background and border colors. The background color needs to be
+ compatible with the text layer. */
+ grub_gfxterm_video_update_color ();
+ grub_gfxterm_background.blend_text_bg = 1;
+
+ /* Mark whole screen as dirty. */
+ grub_gfxterm_schedule_repaint ();
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_extcmd_t background_image_cmd_handle;
+static grub_command_t background_color_cmd_handle;
+
+GRUB_MOD_INIT(gfxterm_background)
+{
+ background_image_cmd_handle =
+ grub_register_extcmd ("background_image",
+ grub_gfxterm_background_image_cmd, 0,
+ N_("[-m (stretch|normal)] FILE"),
+ N_("Load background image for active terminal."),
+ background_image_cmd_options);
+ background_color_cmd_handle =
+ grub_register_command ("background_color",
+ grub_gfxterm_background_color_cmd,
+ N_("COLOR"),
+ N_("Set background color for active terminal."));
+}
+
+GRUB_MOD_FINI(gfxterm_background)
+{
+ grub_unregister_command (background_color_cmd_handle);
+ grub_unregister_extcmd (background_image_cmd_handle);
+}
diff --git a/grub-core/term/i386/coreboot/cbmemc.c b/grub-core/term/i386/coreboot/cbmemc.c
new file mode 100644
index 0000000..cea9b84
--- /dev/null
+++ b/grub-core/term/i386/coreboot/cbmemc.c
@@ -0,0 +1,147 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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/term.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/time.h>
+#include <grub/terminfo.h>
+#include <grub/dl.h>
+#include <grub/coreboot/lbio.h>
+#include <grub/command.h>
+#include <grub/normal.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define CURSOR_MASK ((1 << 28) - 1)
+#define OVERFLOW (1 << 31)
+
+struct grub_linuxbios_cbmemc
+{
+ grub_uint32_t size;
+ grub_uint32_t cursor;
+ char body[0];
+};
+
+static struct grub_linuxbios_cbmemc *cbmemc;
+
+static void
+put (struct grub_term_output *term __attribute__ ((unused)), const int c)
+{
+ grub_uint32_t flags, cursor;
+ if (!cbmemc)
+ return;
+ flags = cbmemc->cursor & ~CURSOR_MASK;
+ cursor = cbmemc->cursor & CURSOR_MASK;
+ if (cursor >= cbmemc->size)
+ return;
+ cbmemc->body[cursor++] = c;
+ if (cursor >= cbmemc->size)
+ {
+ cursor = 0;
+ flags |= OVERFLOW;
+ }
+ cbmemc->cursor = flags | cursor;
+}
+
+struct grub_terminfo_output_state grub_cbmemc_terminfo_output =
+ {
+ .put = put,
+ .size = { 80, 24 }
+ };
+
+static struct grub_term_output grub_cbmemc_term_output =
+ {
+ .name = "cbmemc",
+ .init = grub_terminfo_output_init,
+ .fini = 0,
+ .putchar = grub_terminfo_putchar,
+ .getxy = grub_terminfo_getxy,
+ .getwh = grub_terminfo_getwh,
+ .gotoxy = grub_terminfo_gotoxy,
+ .cls = grub_terminfo_cls,
+ .setcolorstate = grub_terminfo_setcolorstate,
+ .setcursor = grub_terminfo_setcursor,
+ .flags = GRUB_TERM_CODE_TYPE_ASCII,
+ .data = &grub_cbmemc_terminfo_output,
+ .progress_update_divisor = GRUB_PROGRESS_NO_UPDATE
+ };
+
+static int
+iterate_linuxbios_table (grub_linuxbios_table_item_t table_item,
+ void *data __attribute__ ((unused)))
+{
+ if (table_item->tag != GRUB_LINUXBIOS_MEMBER_CBMEMC)
+ return 0;
+ cbmemc = (struct grub_linuxbios_cbmemc *) (grub_addr_t)
+ *(grub_uint64_t *) (table_item + 1);
+ return 1;
+}
+
+static grub_err_t
+grub_cmd_cbmemc (struct grub_command *cmd __attribute__ ((unused)),
+ int argc __attribute__ ((unused)),
+ char *argv[] __attribute__ ((unused)))
+{
+ grub_size_t size, cursor;
+ struct grub_linuxbios_cbmemc *real_cbmemc;
+
+ if (!cbmemc)
+ return grub_error (GRUB_ERR_IO, "no CBMEM console found");
+
+ real_cbmemc = cbmemc;
+ cbmemc = 0;
+ cursor = real_cbmemc->cursor & CURSOR_MASK;
+ if (!(real_cbmemc->cursor & OVERFLOW) && cursor < real_cbmemc->size)
+ size = cursor;
+ else
+ size = real_cbmemc->size;
+ if (real_cbmemc->cursor & OVERFLOW)
+ {
+ if (cursor > size)
+ cursor = 0;
+ grub_xnputs(real_cbmemc->body + cursor, size - cursor);
+ grub_xnputs(real_cbmemc->body, cursor);
+ }
+ else
+ grub_xnputs(real_cbmemc->body, size);
+ cbmemc = real_cbmemc;
+ return 0;
+}
+
+static grub_command_t cmd;
+
+GRUB_MOD_INIT (cbmemc)
+{
+ grub_linuxbios_table_iterate (iterate_linuxbios_table, 0);
+
+ if (cbmemc)
+ grub_term_register_output ("cbmemc", &grub_cbmemc_term_output);
+
+ cmd =
+ grub_register_command ("cbmemc", grub_cmd_cbmemc,
+ 0, N_("Show CBMEM console content."));
+}
+
+
+GRUB_MOD_FINI (cbmemc)
+{
+ grub_term_unregister_output (&grub_cbmemc_term_output);
+ grub_unregister_command (cmd);
+}
diff --git a/grub-core/term/i386/pc/console.c b/grub-core/term/i386/pc/console.c
new file mode 100644
index 0000000..f6142a2
--- /dev/null
+++ b/grub-core/term/i386/pc/console.c
@@ -0,0 +1,310 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2003,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/machine/console.h>
+#include <grub/term.h>
+#include <grub/types.h>
+#include <grub/machine/int.h>
+
+static grub_uint8_t grub_console_cur_color = 0x7;
+
+static void
+int10_9 (grub_uint8_t ch, grub_uint16_t n)
+{
+ struct grub_bios_int_registers regs;
+
+ regs.eax = ch | 0x0900;
+ regs.ebx = grub_console_cur_color & 0xff;
+ regs.ecx = n;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x10, &regs);
+}
+
+/*
+ * BIOS call "INT 10H Function 03h" to get cursor position
+ * Call with %ah = 0x03
+ * %bh = page
+ * Returns %ch = starting scan line
+ * %cl = ending scan line
+ * %dh = row (0 is top)
+ * %dl = column (0 is left)
+ */
+
+
+static struct grub_term_coordinate
+grub_console_getxy (struct grub_term_output *term __attribute__ ((unused)))
+{
+ struct grub_bios_int_registers regs;
+
+ regs.eax = 0x0300;
+ regs.ebx = 0;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x10, &regs);
+
+ return (struct grub_term_coordinate) {
+ (regs.edx & 0xff), ((regs.edx & 0xff00) >> 8) };
+}
+
+/*
+ * BIOS call "INT 10H Function 02h" to set cursor position
+ * Call with %ah = 0x02
+ * %bh = page
+ * %dh = row (0 is top)
+ * %dl = column (0 is left)
+ */
+static void
+grub_console_gotoxy (struct grub_term_output *term __attribute__ ((unused)),
+ struct grub_term_coordinate pos)
+{
+ struct grub_bios_int_registers regs;
+
+ /* set page to 0 */
+ regs.ebx = 0;
+ regs.eax = 0x0200;
+ regs.edx = (pos.y << 8) | pos.x;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x10, &regs);
+}
+
+/*
+ *
+ * Put the character C on the console. Because GRUB wants to write a
+ * character with an attribute, this implementation is a bit tricky.
+ * If C is a control character (CR, LF, BEL, BS), use INT 10, AH = 0Eh
+ * (TELETYPE OUTPUT). Otherwise, use INT 10, AH = 9 to write character
+ * with attributes and advance cursor. If we are on the last column,
+ * let BIOS to wrap line correctly.
+ */
+static void
+grub_console_putchar_real (grub_uint8_t c)
+{
+ struct grub_bios_int_registers regs;
+ struct grub_term_coordinate pos;
+
+ if (c == 7 || c == 8 || c == 0xa || c == 0xd)
+ {
+ regs.eax = c | 0x0e00;
+ regs.ebx = 0x0001;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x10, &regs);
+ return;
+ }
+
+ /* get the current position */
+ pos = grub_console_getxy (NULL);
+
+ /* write the character with the attribute */
+ int10_9 (c, 1);
+
+ /* check the column with the width */
+ if (pos.x >= 79)
+ {
+ grub_console_putchar_real (0x0d);
+ grub_console_putchar_real (0x0a);
+ }
+ else
+ grub_console_gotoxy (NULL, (struct grub_term_coordinate) { pos.x + 1,
+ pos.y });
+}
+
+static void
+grub_console_putchar (struct grub_term_output *term __attribute__ ((unused)),
+ const struct grub_unicode_glyph *c)
+{
+ grub_console_putchar_real (c->base);
+}
+
+/*
+ * BIOS call "INT 10H Function 09h" to write character and attribute
+ * Call with %ah = 0x09
+ * %al = (character)
+ * %bh = (page number)
+ * %bl = (attribute)
+ * %cx = (number of times)
+ */
+static void
+grub_console_cls (struct grub_term_output *term)
+{
+ /* move the cursor to the beginning */
+ grub_console_gotoxy (term, (struct grub_term_coordinate) { 0, 0 });
+
+ /* write spaces to the entire screen */
+ int10_9 (' ', 80 * 25);
+
+ /* move back the cursor */
+ grub_console_gotoxy (term, (struct grub_term_coordinate) { 0, 0 });
+}
+
+/*
+ * void grub_console_setcursor (int on)
+ * BIOS call "INT 10H Function 01h" to set cursor type
+ * Call with %ah = 0x01
+ * %ch = cursor starting scanline
+ * %cl = cursor ending scanline
+ */
+static void
+grub_console_setcursor (struct grub_term_output *term __attribute__ ((unused)),
+ int on)
+{
+ static grub_uint16_t console_cursor_shape = 0;
+ struct grub_bios_int_registers regs;
+
+ /* check if the standard cursor shape has already been saved */
+ if (!console_cursor_shape)
+ {
+ regs.eax = 0x0300;
+ regs.ebx = 0;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x10, &regs);
+ console_cursor_shape = regs.ecx;
+ if ((console_cursor_shape >> 8) >= (console_cursor_shape & 0xff))
+ console_cursor_shape = 0x0d0e;
+ }
+ /* set %cx to the designated cursor shape */
+ regs.ecx = on ? console_cursor_shape : 0x2000;
+ regs.eax = 0x0100;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x10, &regs);
+}
+
+/*
+ * if there is a character pending, return it; otherwise return -1
+ * BIOS call "INT 16H Function 01H" to check whether a character is pending
+ * Call with %ah = 0x1
+ * Return:
+ * If key waiting to be input:
+ * %ah = keyboard scan code
+ * %al = ASCII character
+ * Zero flag = clear
+ * else
+ * Zero flag = set
+ * BIOS call "INT 16H Function 00H" to read character from keyboard
+ * Call with %ah = 0x0
+ * Return: %ah = keyboard scan code
+ * %al = ASCII character
+ */
+
+static int
+grub_console_getkey (struct grub_term_input *term __attribute__ ((unused)))
+{
+ const grub_uint16_t bypass_table[] = {
+ 0x0100 | GRUB_TERM_ESC, 0x0f00 | GRUB_TERM_TAB, 0x0e00 | GRUB_TERM_BACKSPACE, 0x1c00 | '\r', 0x1c00 | '\n'
+ };
+ struct grub_bios_int_registers regs;
+ unsigned i;
+
+ /*
+ * Due to a bug in apple's bootcamp implementation, INT 16/AH = 0 would
+ * cause the machine to hang at the second keystroke. However, we can
+ * work around this problem by ensuring the presence of keystroke with
+ * INT 16/AH = 1 before calling INT 16/AH = 0.
+ */
+
+ regs.eax = 0x0100;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x16, &regs);
+ if (regs.flags & GRUB_CPU_INT_FLAGS_ZERO)
+ return GRUB_TERM_NO_KEY;
+
+ regs.eax = 0x0000;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x16, &regs);
+ if (!(regs.eax & 0xff))
+ return ((regs.eax >> 8) & 0xff) | GRUB_TERM_EXTENDED;
+
+ if ((regs.eax & 0xff) >= ' ')
+ return regs.eax & 0xff;
+
+ for (i = 0; i < ARRAY_SIZE (bypass_table); i++)
+ if (bypass_table[i] == (regs.eax & 0xffff))
+ return regs.eax & 0xff;
+
+ return (regs.eax & 0xff) + (('a' - 1) | GRUB_TERM_CTRL);
+}
+
+static const struct grub_machine_bios_data_area *bios_data_area =
+ (struct grub_machine_bios_data_area *) GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR;
+
+static int
+grub_console_getkeystatus (struct grub_term_input *term __attribute__ ((unused)))
+{
+ /* conveniently GRUB keystatus is modelled after BIOS one. */
+ return bios_data_area->keyboard_flag_lower & ~0x80;
+}
+
+static struct grub_term_coordinate
+grub_console_getwh (struct grub_term_output *term __attribute__ ((unused)))
+{
+ return (struct grub_term_coordinate) { 80, 25 };
+}
+
+static void
+grub_console_setcolorstate (struct grub_term_output *term
+ __attribute__ ((unused)),
+ grub_term_color_state state)
+{
+ switch (state) {
+ case GRUB_TERM_COLOR_STANDARD:
+ grub_console_cur_color = GRUB_TERM_DEFAULT_STANDARD_COLOR & 0x7f;
+ break;
+ case GRUB_TERM_COLOR_NORMAL:
+ grub_console_cur_color = grub_term_normal_color & 0x7f;
+ break;
+ case GRUB_TERM_COLOR_HIGHLIGHT:
+ grub_console_cur_color = grub_term_highlight_color & 0x7f;
+ break;
+ default:
+ break;
+ }
+}
+
+static struct grub_term_input grub_console_term_input =
+ {
+ .name = "console",
+ .getkey = grub_console_getkey,
+ .getkeystatus = grub_console_getkeystatus
+ };
+
+static struct grub_term_output grub_console_term_output =
+ {
+ .name = "console",
+ .putchar = grub_console_putchar,
+ .getwh = grub_console_getwh,
+ .getxy = grub_console_getxy,
+ .gotoxy = grub_console_gotoxy,
+ .cls = grub_console_cls,
+ .setcolorstate = grub_console_setcolorstate,
+ .setcursor = grub_console_setcursor,
+ .flags = GRUB_TERM_CODE_TYPE_CP437,
+ .progress_update_divisor = GRUB_PROGRESS_FAST
+ };
+
+void
+grub_console_init (void)
+{
+ grub_term_register_output ("console", &grub_console_term_output);
+ grub_term_register_input ("console", &grub_console_term_input);
+}
+
+void
+grub_console_fini (void)
+{
+ grub_term_unregister_input (&grub_console_term_input);
+ grub_term_unregister_output (&grub_console_term_output);
+}
diff --git a/grub-core/term/i386/pc/mda_text.c b/grub-core/term/i386/pc/mda_text.c
new file mode 100644
index 0000000..c3a5a17
--- /dev/null
+++ b/grub-core/term/i386/pc/mda_text.c
@@ -0,0 +1,13 @@
+#define MODE_MDA 1
+#include "vga_text.c"
+
+GRUB_MOD_INIT(mda_text)
+{
+ grub_term_register_output ("mda_text", &grub_vga_text_term);
+}
+
+GRUB_MOD_FINI(mda_text)
+{
+ grub_term_unregister_output (&grub_vga_text_term);
+}
+
diff --git a/grub-core/term/i386/pc/vga_text.c b/grub-core/term/i386/pc/vga_text.c
new file mode 100644
index 0000000..88fecc5
--- /dev/null
+++ b/grub-core/term/i386/pc/vga_text.c
@@ -0,0 +1,288 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2007, 2008, 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/dl.h>
+#include <grub/cpu/io.h>
+#include <grub/types.h>
+#include <grub/vga.h>
+#include <grub/term.h>
+
+#if defined (GRUB_MACHINE_COREBOOT)
+#include <grub/machine/console.h>
+#endif
+
+/* MODESET is used for testing to force monochrome or colour mode.
+ You shouldn't use mda_text on vga.
+ */
+#ifdef MODESET
+#include <grub/machine/int.h>
+#endif
+
+#if defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU) || defined (GRUB_MACHINE_MIPS_QEMU_MIPS) || defined (GRUB_MACHINE_MULTIBOOT)
+#include <grub/machine/console.h>
+#endif
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define COLS 80
+#define ROWS 25
+
+static struct grub_term_coordinate grub_curr_pos;
+
+#ifdef __mips__
+#define VGA_TEXT_SCREEN ((grub_uint16_t *) 0xb00b8000)
+#define cr_read grub_vga_cr_read
+#define cr_write grub_vga_cr_write
+#elif defined (MODE_MDA)
+#define VGA_TEXT_SCREEN ((grub_uint16_t *) 0xb0000)
+#define cr_read grub_vga_cr_bw_read
+#define cr_write grub_vga_cr_bw_write
+#else
+#define VGA_TEXT_SCREEN ((grub_uint16_t *) 0xb8000)
+#define cr_read grub_vga_cr_read
+#define cr_write grub_vga_cr_write
+#endif
+
+static grub_uint8_t cur_color = 0x7;
+
+static void
+screen_write_char (int x, int y, short c)
+{
+ VGA_TEXT_SCREEN[y * COLS + x] = grub_cpu_to_le16 (c);
+}
+
+static short
+screen_read_char (int x, int y)
+{
+ return grub_le_to_cpu16 (VGA_TEXT_SCREEN[y * COLS + x]);
+}
+
+static void
+update_cursor (void)
+{
+ unsigned int pos = grub_curr_pos.y * COLS + grub_curr_pos.x;
+ cr_write (pos >> 8, GRUB_VGA_CR_CURSOR_ADDR_HIGH);
+ cr_write (pos & 0xFF, GRUB_VGA_CR_CURSOR_ADDR_LOW);
+}
+
+static void
+inc_y (void)
+{
+ grub_curr_pos.x = 0;
+ if (grub_curr_pos.y < ROWS - 1)
+ grub_curr_pos.y++;
+ else
+ {
+ int x, y;
+ for (y = 0; y < ROWS - 1; y++)
+ for (x = 0; x < COLS; x++)
+ screen_write_char (x, y, screen_read_char (x, y + 1));
+ for (x = 0; x < COLS; x++)
+ screen_write_char (x, ROWS - 1, ' ' | (cur_color << 8));
+ }
+}
+
+static void
+inc_x (void)
+{
+ if (grub_curr_pos.x >= COLS - 1)
+ inc_y ();
+ else
+ grub_curr_pos.x++;
+}
+
+static void
+grub_vga_text_putchar (struct grub_term_output *term __attribute__ ((unused)),
+ const struct grub_unicode_glyph *c)
+{
+ switch (c->base)
+ {
+ case '\b':
+ if (grub_curr_pos.x != 0)
+ screen_write_char (grub_curr_pos.x--, grub_curr_pos.y, ' ');
+ break;
+ case '\n':
+ inc_y ();
+ break;
+ case '\r':
+ grub_curr_pos.x = 0;
+ break;
+ default:
+ screen_write_char (grub_curr_pos.x, grub_curr_pos.y,
+ c->base | (cur_color << 8));
+ inc_x ();
+ }
+
+ update_cursor ();
+}
+
+static struct grub_term_coordinate
+grub_vga_text_getxy (struct grub_term_output *term __attribute__ ((unused)))
+{
+ return grub_curr_pos;
+}
+
+static void
+grub_vga_text_gotoxy (struct grub_term_output *term __attribute__ ((unused)),
+ struct grub_term_coordinate pos)
+{
+ grub_curr_pos = pos;
+ update_cursor ();
+}
+
+static void
+grub_vga_text_cls (struct grub_term_output *term)
+{
+ int i;
+ for (i = 0; i < ROWS * COLS; i++)
+ VGA_TEXT_SCREEN[i] = grub_cpu_to_le16 (' ' | (cur_color << 8));
+ grub_vga_text_gotoxy (term, (struct grub_term_coordinate) { 0, 0 });
+}
+
+static void
+grub_vga_text_setcursor (struct grub_term_output *term __attribute__ ((unused)),
+ int on)
+{
+ grub_uint8_t old;
+ old = cr_read (GRUB_VGA_CR_CURSOR_START);
+ if (on)
+ cr_write (old & ~GRUB_VGA_CR_CURSOR_START_DISABLE,
+ GRUB_VGA_CR_CURSOR_START);
+ else
+ cr_write (old | GRUB_VGA_CR_CURSOR_START_DISABLE,
+ GRUB_VGA_CR_CURSOR_START);
+}
+
+static grub_err_t
+grub_vga_text_init_real (struct grub_term_output *term)
+{
+#ifdef MODESET
+ struct grub_bios_int_registers regs;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+
+#ifdef MODE_MDA
+ regs.eax = 7;
+#else
+ regs.eax = 3;
+#endif
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x10, &regs);
+#endif
+ grub_vga_text_cls (term);
+ return 0;
+}
+
+static grub_err_t
+grub_vga_text_fini_real (struct grub_term_output *term)
+{
+#ifdef MODESET
+ struct grub_bios_int_registers regs;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+
+ regs.eax = 3;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x10, &regs);
+#endif
+ grub_vga_text_cls (term);
+ return 0;
+}
+
+static struct grub_term_coordinate
+grub_vga_text_getwh (struct grub_term_output *term __attribute__ ((unused)))
+{
+ return (struct grub_term_coordinate) { 80, 25 };
+}
+
+#ifndef MODE_MDA
+
+static void
+grub_vga_text_setcolorstate (struct grub_term_output *term __attribute__ ((unused)),
+ grub_term_color_state state)
+{
+ switch (state) {
+ case GRUB_TERM_COLOR_STANDARD:
+ cur_color = GRUB_TERM_DEFAULT_STANDARD_COLOR & 0x7f;
+ break;
+ case GRUB_TERM_COLOR_NORMAL:
+ cur_color = grub_term_normal_color & 0x7f;
+ break;
+ case GRUB_TERM_COLOR_HIGHLIGHT:
+ cur_color = grub_term_highlight_color & 0x7f;
+ break;
+ default:
+ break;
+ }
+}
+
+#else
+static void
+grub_vga_text_setcolorstate (struct grub_term_output *term __attribute__ ((unused)),
+ grub_term_color_state state)
+{
+ switch (state) {
+ case GRUB_TERM_COLOR_STANDARD:
+ cur_color = 0x07;
+ break;
+ case GRUB_TERM_COLOR_NORMAL:
+ cur_color = 0x07;
+ break;
+ case GRUB_TERM_COLOR_HIGHLIGHT:
+ cur_color = 0x70;
+ break;
+ default:
+ break;
+ }
+}
+#endif
+
+static struct grub_term_output grub_vga_text_term =
+ {
+#ifdef MODE_MDA
+ .name = "mda_text",
+#else
+ .name = "vga_text",
+#endif
+ .init = grub_vga_text_init_real,
+ .fini = grub_vga_text_fini_real,
+ .putchar = grub_vga_text_putchar,
+ .getwh = grub_vga_text_getwh,
+ .getxy = grub_vga_text_getxy,
+ .gotoxy = grub_vga_text_gotoxy,
+ .cls = grub_vga_text_cls,
+ .setcolorstate = grub_vga_text_setcolorstate,
+ .setcursor = grub_vga_text_setcursor,
+ .flags = GRUB_TERM_CODE_TYPE_CP437,
+ .progress_update_divisor = GRUB_PROGRESS_FAST
+ };
+
+#ifndef MODE_MDA
+
+GRUB_MOD_INIT(vga_text)
+{
+#ifdef GRUB_MACHINE_COREBOOT
+ if (!grub_video_coreboot_fbtable)
+#endif
+ grub_term_register_output ("vga_text", &grub_vga_text_term);
+}
+
+GRUB_MOD_FINI(vga_text)
+{
+ grub_term_unregister_output (&grub_vga_text_term);
+}
+
+#endif
diff --git a/grub-core/term/ieee1275/console.c b/grub-core/term/ieee1275/console.c
new file mode 100644
index 0000000..7e797a7
--- /dev/null
+++ b/grub-core/term/ieee1275/console.c
@@ -0,0 +1,267 @@
+/* console.c -- Open Firmware console for GRUB. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/term.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/time.h>
+#include <grub/terminfo.h>
+#include <grub/ieee1275/console.h>
+#include <grub/ieee1275/ieee1275.h>
+
+static grub_ieee1275_ihandle_t stdout_ihandle;
+static grub_ieee1275_ihandle_t stdin_ihandle;
+
+extern struct grub_terminfo_output_state grub_console_terminfo_output;
+
+struct color
+{
+ int red;
+ int green;
+ int blue;
+};
+
+/* Use serial colors as they are default on most firmwares and some firmwares
+ ignore set-color!. Additionally output may be redirected to serial. */
+static struct color colors[] =
+ {
+ // {R, G, B}
+ {0x00, 0x00, 0x00}, // 0 = black
+ {0xA8, 0x00, 0x00}, // 1 = red
+ {0x00, 0xA8, 0x00}, // 2 = green
+ {0xFE, 0xFE, 0x54}, // 3 = yellow
+ {0x00, 0x00, 0xA8}, // 4 = blue
+ {0xA8, 0x00, 0xA8}, // 5 = magenta
+ {0x00, 0xA8, 0xA8}, // 6 = cyan
+ {0xFE, 0xFE, 0xFE} // 7 = white
+ };
+
+static void
+put (struct grub_term_output *term __attribute__ ((unused)), const int c)
+{
+ char chr = c;
+
+ grub_ieee1275_write (stdout_ihandle, &chr, 1, 0);
+}
+
+static int
+readkey (struct grub_term_input *term __attribute__ ((unused)))
+{
+ grub_uint8_t c;
+ grub_ssize_t actual = 0;
+
+ grub_ieee1275_read (stdin_ihandle, &c, 1, &actual);
+ if (actual > 0)
+ return c;
+ return -1;
+}
+
+static void
+grub_console_dimensions (void)
+{
+ grub_ieee1275_ihandle_t options;
+ grub_ieee1275_phandle_t stdout_phandle;
+ char val[1024];
+
+ /* Always assume 80x24 on serial since screen-#rows/screen-#columns is often
+ garbage for such devices. */
+ if (! grub_ieee1275_instance_to_package (stdout_ihandle,
+ &stdout_phandle)
+ && ! grub_ieee1275_package_to_path (stdout_phandle,
+ val, sizeof (val) - 1, 0))
+ {
+ grub_ieee1275_ihandle_t stdout_options;
+ val[sizeof (val) - 1] = 0;
+
+ if (! grub_ieee1275_finddevice (val, &stdout_options)
+ && ! grub_ieee1275_get_property (stdout_options, "device_type",
+ val, sizeof (val) - 1, 0))
+ {
+ val[sizeof (val) - 1] = 0;
+ if (grub_strcmp (val, "serial") == 0)
+ {
+ grub_console_terminfo_output.size.x = 80;
+ grub_console_terminfo_output.size.y = 24;
+ return;
+ }
+ }
+ }
+
+ if (! grub_ieee1275_finddevice ("/options", &options)
+ && options != (grub_ieee1275_ihandle_t) -1)
+ {
+ if (! grub_ieee1275_get_property (options, "screen-#columns",
+ val, sizeof (val) - 1, 0))
+ {
+ val[sizeof (val) - 1] = 0;
+ grub_console_terminfo_output.size.x
+ = (grub_uint8_t) grub_strtoul (val, 0, 10);
+ }
+ if (! grub_ieee1275_get_property (options, "screen-#rows",
+ val, sizeof (val) - 1, 0))
+ {
+ val[sizeof (val) - 1] = 0;
+ grub_console_terminfo_output.size.y
+ = (grub_uint8_t) grub_strtoul (val, 0, 10);
+ }
+ }
+
+ /* Bogus default value on SLOF in QEMU. */
+ if (grub_console_terminfo_output.size.x == 200
+ && grub_console_terminfo_output.size.y == 200)
+ {
+ grub_console_terminfo_output.size.x = 80;
+ grub_console_terminfo_output.size.y = 24;
+ }
+
+ /* Use a small console by default. */
+ if (! grub_console_terminfo_output.size.x)
+ grub_console_terminfo_output.size.x = 80;
+ if (! grub_console_terminfo_output.size.y)
+ grub_console_terminfo_output.size.y = 24;
+}
+
+static void
+grub_console_setcursor (struct grub_term_output *term,
+ int on)
+{
+ grub_terminfo_setcursor (term, on);
+
+ if (!grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_HAS_CURSORONOFF))
+ return;
+
+ /* Understood by the Open Firmware flavour in OLPC. */
+ if (on)
+ grub_ieee1275_interpret ("cursor-on", 0);
+ else
+ grub_ieee1275_interpret ("cursor-off", 0);
+}
+
+static grub_err_t
+grub_console_init_input (struct grub_term_input *term)
+{
+ grub_ssize_t actual;
+
+ if (grub_ieee1275_get_integer_property (grub_ieee1275_chosen, "stdin", &stdin_ihandle,
+ sizeof stdin_ihandle, &actual)
+ || actual != sizeof stdin_ihandle)
+ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot find stdin");
+
+ return grub_terminfo_input_init (term);
+}
+
+static grub_err_t
+grub_console_init_output (struct grub_term_output *term)
+{
+ grub_ssize_t actual;
+
+ /* The latest PowerMacs don't actually initialize the screen for us, so we
+ * use this trick to re-open the output device (but we avoid doing this on
+ * platforms where it's known to be broken). */
+ if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_BROKEN_OUTPUT))
+ grub_ieee1275_interpret ("output-device output", 0);
+
+ if (grub_ieee1275_get_integer_property (grub_ieee1275_chosen, "stdout", &stdout_ihandle,
+ sizeof stdout_ihandle, &actual)
+ || actual != sizeof stdout_ihandle)
+ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot find stdout");
+
+ /* Initialize colors. */
+ if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CANNOT_SET_COLORS))
+ {
+ unsigned col;
+ for (col = 0; col < ARRAY_SIZE (colors); col++)
+ grub_ieee1275_set_color (stdout_ihandle, col, colors[col].red,
+ colors[col].green, colors[col].blue);
+
+ /* Set the right fg and bg colors. */
+ grub_terminfo_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
+ }
+
+ grub_console_dimensions ();
+
+ grub_terminfo_output_init (term);
+
+ return 0;
+}
+
+
+
+struct grub_terminfo_input_state grub_console_terminfo_input =
+ {
+ .readkey = readkey
+ };
+
+struct grub_terminfo_output_state grub_console_terminfo_output =
+ {
+ .put = put,
+ .size = { 80, 24 }
+ };
+
+static struct grub_term_input grub_console_term_input =
+ {
+ .name = "console",
+ .init = grub_console_init_input,
+ .getkey = grub_terminfo_getkey,
+ .data = &grub_console_terminfo_input
+ };
+
+static struct grub_term_output grub_console_term_output =
+ {
+ .name = "console",
+ .init = grub_console_init_output,
+ .putchar = grub_terminfo_putchar,
+ .getxy = grub_terminfo_getxy,
+ .getwh = grub_terminfo_getwh,
+ .gotoxy = grub_terminfo_gotoxy,
+ .cls = grub_terminfo_cls,
+ .setcolorstate = grub_terminfo_setcolorstate,
+ .setcursor = grub_console_setcursor,
+ .flags = GRUB_TERM_CODE_TYPE_ASCII,
+ .data = &grub_console_terminfo_output,
+ .progress_update_divisor = GRUB_PROGRESS_FAST
+ };
+
+void
+grub_console_init_early (void)
+{
+ grub_term_register_input ("console", &grub_console_term_input);
+ grub_term_register_output ("console", &grub_console_term_output);
+}
+
+void
+grub_console_init_lately (void)
+{
+ const char *type;
+
+ if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_ANSI))
+ type = "dumb";
+ else if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CURSORONOFF_ANSI_BROKEN))
+ type = "ieee1275-nocursor";
+ else
+ type = "ieee1275";
+ grub_terminfo_init ();
+ grub_terminfo_output_register (&grub_console_term_output, type);
+}
+
+void
+grub_console_fini (void)
+{
+}
diff --git a/grub-core/term/ieee1275/escc.c b/grub-core/term/ieee1275/escc.c
new file mode 100644
index 0000000..e605ff2
--- /dev/null
+++ b/grub-core/term/ieee1275/escc.c
@@ -0,0 +1,319 @@
+/*
+ * 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 char *macio = 0;
+
+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);
+}
+
+static 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;
+}
+
+GRUB_MOD_INIT (escc)
+{
+ grub_uint32_t macio_addr[4];
+ grub_uint32_t escc_addr[2];
+ grub_ieee1275_phandle_t dev;
+ struct grub_ieee1275_devalias alias;
+ char *escc = 0;
+
+ grub_ieee1275_devices_iterate (find_macio);
+ if (!macio)
+ return;
+
+ FOR_IEEE1275_DEVCHILDREN(macio, alias)
+ if (grub_strcmp (alias.type, "escc") == 0)
+ {
+ escc = grub_strdup (alias.path);
+ break;
+ }
+ grub_ieee1275_devalias_free (&alias);
+ 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, 0);
+ add_device (macio_addr[2] + escc_addr[0], 1);
+
+ grub_free (macio);
+ grub_free (escc);
+}
+
+GRUB_MOD_FINI (escc)
+{
+}
diff --git a/grub-core/term/ieee1275/serial.c b/grub-core/term/ieee1275/serial.c
new file mode 100644
index 0000000..9e71ca4
--- /dev/null
+++ b/grub-core/term/ieee1275/serial.c
@@ -0,0 +1,287 @@
+/*
+ * 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>
+#include <grub/ieee1275/console.h>
+
+#define IEEE1275_IHANDLE_INVALID ((grub_ieee1275_cell_t) 0)
+
+struct ofserial_hash_ent
+{
+ char *devpath;
+ /* Pointer to shortest available name on nodes representing canonical names,
+ otherwise NULL. */
+ const char *shortest;
+ struct ofserial_hash_ent *next;
+};
+
+static void
+do_real_config (struct grub_serial_port *port)
+{
+ if (port->configured)
+ return;
+
+ if (grub_ieee1275_open (port->elem->devpath, &port->handle)
+ || port->handle == (grub_ieee1275_ihandle_t) -1)
+ port->handle = IEEE1275_IHANDLE_INVALID;
+
+ port->configured = 1;
+}
+
+/* Fetch a key. */
+static int
+serial_hw_fetch (struct grub_serial_port *port)
+{
+ grub_ssize_t actual;
+ char c;
+
+ do_real_config (port);
+
+ if (port->handle == IEEE1275_IHANDLE_INVALID)
+ return -1;
+ grub_ieee1275_read (port->handle, &c, 1, &actual);
+
+ if (actual <= 0)
+ return -1;
+ return c;
+}
+
+/* Put a character. */
+static void
+serial_hw_put (struct grub_serial_port *port, const int c)
+{
+ grub_ssize_t actual;
+ char c0 = c;
+
+ do_real_config (port);
+
+ if (port->handle == IEEE1275_IHANDLE_INVALID)
+ return;
+
+ grub_ieee1275_write (port->handle, &c0, 1, &actual);
+}
+
+/* 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)))
+{
+ /* FIXME: no IEEE1275 serial config available. */
+
+ return GRUB_ERR_NONE;
+}
+
+struct grub_serial_driver grub_ofserial_driver =
+ {
+ .configure = serial_hw_configure,
+ .fetch = serial_hw_fetch,
+ .put = serial_hw_put
+ };
+
+#define OFSERIAL_HASH_SZ 8
+static struct ofserial_hash_ent *ofserial_hash[OFSERIAL_HASH_SZ];
+
+static int
+ofserial_hash_fn (const char *devpath)
+{
+ int hash = 0;
+ while (*devpath)
+ hash ^= *devpath++;
+ return (hash & (OFSERIAL_HASH_SZ - 1));
+}
+
+static struct ofserial_hash_ent *
+ofserial_hash_find (const char *devpath)
+{
+ struct ofserial_hash_ent *p = ofserial_hash[ofserial_hash_fn(devpath)];
+
+ while (p)
+ {
+ if (!grub_strcmp (p->devpath, devpath))
+ break;
+ p = p->next;
+ }
+ return p;
+}
+
+static struct ofserial_hash_ent *
+ofserial_hash_add_real (char *devpath)
+{
+ struct ofserial_hash_ent *p;
+ struct ofserial_hash_ent **head = &ofserial_hash[ofserial_hash_fn(devpath)];
+
+ p = grub_malloc(sizeof (*p));
+ if (!p)
+ return NULL;
+
+ p->devpath = devpath;
+ p->next = *head;
+ p->shortest = 0;
+ *head = p;
+ return p;
+}
+
+static struct ofserial_hash_ent *
+ofserial_hash_add (char *devpath, char *curcan)
+{
+ struct ofserial_hash_ent *p, *pcan;
+
+ p = ofserial_hash_add_real (devpath);
+
+ grub_dprintf ("serial", "devpath = %s, canonical = %s\n", devpath, curcan);
+
+ if (!curcan)
+ {
+ p->shortest = devpath;
+ return p;
+ }
+
+ pcan = ofserial_hash_find (curcan);
+ if (!pcan)
+ pcan = ofserial_hash_add_real (curcan);
+ else
+ grub_free (curcan);
+
+ if (!pcan)
+ grub_errno = GRUB_ERR_NONE;
+ else
+ {
+ if (!pcan->shortest
+ || grub_strlen (pcan->shortest) > grub_strlen (devpath))
+ pcan->shortest = devpath;
+ }
+
+ return p;
+}
+
+static void
+dev_iterate_real (struct grub_ieee1275_devalias *alias,
+ int use_name)
+{
+ struct ofserial_hash_ent *op;
+
+ if (grub_strcmp (alias->type, "serial") != 0)
+ return;
+
+ grub_dprintf ("serial", "serial name = %s, path = %s\n", alias->name,
+ alias->path);
+
+ op = ofserial_hash_find (alias->path);
+ if (!op)
+ {
+ char *name = grub_strdup (use_name ? alias->name : alias->path);
+ char *can = grub_strdup (alias->path);
+ if (!name || !can)
+ {
+ grub_errno = GRUB_ERR_NONE;
+ grub_free (name);
+ grub_free (can);
+ return;
+ }
+ op = ofserial_hash_add (name, can);
+ }
+ return;
+}
+
+static int
+dev_iterate (struct grub_ieee1275_devalias *alias)
+{
+ dev_iterate_real (alias, 0);
+ return 0;
+}
+
+static const char *
+add_port (struct ofserial_hash_ent *ent)
+{
+ struct grub_serial_port *port;
+ char *ptr;
+ grub_err_t err;
+
+ if (!ent->shortest)
+ return NULL;
+
+ port = grub_zalloc (sizeof (*port));
+ if (!port)
+ return NULL;
+ port->name = grub_malloc (sizeof ("ieee1275/")
+ + grub_strlen (ent->shortest));
+ port->elem = ent;
+ if (!port->name)
+ return NULL;
+ ptr = grub_stpcpy (port->name, "ieee1275/");
+ grub_strcpy (ptr, ent->shortest);
+
+ port->driver = &grub_ofserial_driver;
+ err = grub_serial_config_defaults (port);
+ if (err)
+ grub_print_error ();
+
+ grub_serial_register (port);
+
+ return port->name;
+}
+
+const char *
+grub_ofserial_add_port (const char *path)
+{
+ struct ofserial_hash_ent *ent;
+ char *name = grub_strdup (path);
+ char *can = grub_strdup (path);
+
+ if (!name || ! can)
+ {
+ grub_free (name);
+ grub_free (can);
+ return NULL;
+ }
+
+ ent = ofserial_hash_add (name, can);
+ return add_port (ent);
+}
+
+void
+grub_ofserial_init (void)
+{
+ unsigned i;
+ struct grub_ieee1275_devalias alias;
+
+ FOR_IEEE1275_DEVALIASES(alias)
+ dev_iterate_real (&alias, 1);
+
+ grub_ieee1275_devices_iterate (dev_iterate);
+
+ for (i = 0; i < ARRAY_SIZE (ofserial_hash); i++)
+ {
+ struct ofserial_hash_ent *ent;
+ for (ent = ofserial_hash[i]; ent; ent = ent->next)
+ add_port (ent);
+ }
+}
+
diff --git a/grub-core/term/morse.c b/grub-core/term/morse.c
new file mode 100644
index 0000000..13b8e86
--- /dev/null
+++ b/grub-core/term/morse.c
@@ -0,0 +1,133 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2011,2012,2013 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/term.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/time.h>
+#include <grub/speaker.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define BASE_TIME 250
+#define DIH 1
+#define DAH 3
+#define END 0
+
+static const char codes[0x80][6] =
+ {
+ ['0'] = { DAH, DAH, DAH, DAH, DAH, END },
+ ['1'] = { DIH, DAH, DAH, DAH, DAH, END },
+ ['2'] = { DIH, DIH, DAH, DAH, DAH, END },
+ ['3'] = { DIH, DIH, DIH, DAH, DAH, END },
+ ['4'] = { DIH, DIH, DIH, DIH, DAH, END },
+ ['5'] = { DIH, DIH, DIH, DIH, DIH, END },
+ ['6'] = { DAH, DIH, DIH, DIH, DIH, END },
+ ['7'] = { DAH, DAH, DIH, DIH, DIH, END },
+ ['8'] = { DAH, DAH, DAH, DIH, DIH, END },
+ ['9'] = { DAH, DAH, DAH, DAH, DIH, END },
+ ['a'] = { DIH, DAH, END },
+ ['b'] = { DAH, DIH, DIH, DIH, END },
+ ['c'] = { DAH, DIH, DAH, DIH, END },
+ ['d'] = { DAH, DIH, DIH, END },
+ ['e'] = { DIH, END },
+ ['f'] = { DIH, DIH, DAH, DIH, END },
+ ['g'] = { DAH, DAH, DIH, END },
+ ['h'] = { DIH, DIH, DIH, DIH, END },
+ ['i'] = { DIH, DIH, END },
+ ['j'] = { DIH, DAH, DAH, DAH, END },
+ ['k'] = { DAH, DIH, DAH, END },
+ ['l'] = { DIH, DAH, DIH, DIH, END },
+ ['m'] = { DAH, DAH, END },
+ ['n'] = { DAH, DIH, END },
+ ['o'] = { DAH, DAH, DAH, END },
+ ['p'] = { DIH, DAH, DAH, DIH, END },
+ ['q'] = { DAH, DAH, DIH, DAH, END },
+ ['r'] = { DIH, DAH, DIH, END },
+ ['s'] = { DIH, DIH, DIH, END },
+ ['t'] = { DAH, END },
+ ['u'] = { DIH, DIH, DAH, END },
+ ['v'] = { DIH, DIH, DIH, DAH, END },
+ ['w'] = { DIH, DAH, DAH, END },
+ ['x'] = { DAH, DIH, DIH, DAH, END },
+ ['y'] = { DAH, DIH, DAH, DAH, END },
+ ['z'] = { DAH, DAH, DIH, DIH, END }
+ };
+
+static void
+grub_audio_tone (int length)
+{
+ grub_speaker_beep_on (1000);
+ grub_millisleep (length);
+ grub_speaker_beep_off ();
+}
+
+static void
+grub_audio_putchar (struct grub_term_output *term __attribute__ ((unused)),
+ const struct grub_unicode_glyph *c_in)
+{
+ grub_uint8_t c;
+ int i;
+
+ /* For now, do not try to use a surrogate pair. */
+ if (c_in->base > 0x7f)
+ c = '?';
+ else
+ c = grub_tolower (c_in->base);
+ for (i = 0; codes[c][i]; i++)
+ {
+ grub_audio_tone (codes[c][i] * BASE_TIME);
+ grub_millisleep (BASE_TIME);
+ }
+ grub_millisleep (2 * BASE_TIME);
+}
+
+
+static int
+dummy (void)
+{
+ return 0;
+}
+
+static struct grub_term_output grub_audio_term_output =
+ {
+ .name = "morse",
+ .init = (void *) dummy,
+ .fini = (void *) dummy,
+ .putchar = grub_audio_putchar,
+ .getwh = (void *) dummy,
+ .getxy = (void *) dummy,
+ .gotoxy = (void *) dummy,
+ .cls = (void *) dummy,
+ .setcolorstate = (void *) dummy,
+ .setcursor = (void *) dummy,
+ .flags = GRUB_TERM_CODE_TYPE_ASCII | GRUB_TERM_DUMB,
+ .progress_update_divisor = GRUB_PROGRESS_NO_UPDATE
+ };
+
+GRUB_MOD_INIT (morse)
+{
+ grub_term_register_output ("audio", &grub_audio_term_output);
+}
+
+GRUB_MOD_FINI (morse)
+{
+ grub_term_unregister_output (&grub_audio_term_output);
+}
diff --git a/grub-core/term/ns8250.c b/grub-core/term/ns8250.c
new file mode 100644
index 0000000..39809d0
--- /dev/null
+++ b/grub-core/term/ns8250.c
@@ -0,0 +1,318 @@
+/*
+ * 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/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>
+#include <grub/time.h>
+#include <grub/i18n.h>
+
+#ifdef GRUB_MACHINE_PCBIOS
+#include <grub/machine/memory.h>
+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
+
+static int dead_ports = 0;
+
+#ifdef GRUB_MACHINE_MIPS_LOONGSON
+#define DEFAULT_BASE_CLOCK (2 * 115200)
+#else
+#define DEFAULT_BASE_CLOCK 115200
+#endif
+
+
+/* Convert speed to divisor. */
+static unsigned short
+serial_get_divisor (const struct grub_serial_port *port __attribute__ ((unused)),
+ const struct grub_serial_config *config)
+{
+ grub_uint32_t base_clock;
+ grub_uint32_t divisor;
+ grub_uint32_t actual_speed, error;
+
+ base_clock = config->base_clock ? (config->base_clock >> 4) : DEFAULT_BASE_CLOCK;
+
+ divisor = (base_clock + (config->speed / 2)) / config->speed;
+ if (config->speed == 0)
+ return 0;
+ if (divisor > 0xffff || divisor == 0)
+ return 0;
+ actual_speed = base_clock / divisor;
+ error = actual_speed > config->speed ? (actual_speed - config->speed)
+ : (config->speed - actual_speed);
+ if (error > (config->speed / 30 + 1))
+ return 0;
+ return divisor;
+}
+
+static void
+do_real_config (struct grub_serial_port *port)
+{
+ int divisor;
+ unsigned char status = 0;
+ grub_uint64_t endtime;
+
+ const unsigned char parities[] = {
+ [GRUB_SERIAL_PARITY_NONE] = UART_NO_PARITY,
+ [GRUB_SERIAL_PARITY_ODD] = UART_ODD_PARITY,
+ [GRUB_SERIAL_PARITY_EVEN] = UART_EVEN_PARITY
+ };
+ const unsigned char stop_bits[] = {
+ [GRUB_SERIAL_STOP_BITS_1] = UART_1_STOP_BIT,
+ [GRUB_SERIAL_STOP_BITS_2] = UART_2_STOP_BITS,
+ };
+
+ if (port->configured)
+ return;
+
+ port->broken = 0;
+
+ divisor = serial_get_divisor (port, &port->config);
+
+ /* 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 |= (parities[port->config.parity]
+ | (port->config.word_len - 5)
+ | stop_bits[port->config.stop_bits]);
+ grub_outb (status, port->port + UART_LCR);
+
+ if (port->config.rtscts)
+ {
+ /* 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);
+ }
+
+ /* Drain the input buffer. */
+ endtime = grub_get_time_ms () + 1000;
+ while (grub_inb (port->port + UART_LSR) & UART_DATA_READY)
+ {
+ grub_inb (port->port + UART_RX);
+ if (grub_get_time_ms () > endtime)
+ {
+ port->broken = 1;
+ break;
+ }
+ }
+
+ port->configured = 1;
+}
+
+/* Fetch a key. */
+static int
+serial_hw_fetch (struct grub_serial_port *port)
+{
+ do_real_config (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)
+{
+ 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 ((grub_inb (port->port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0)
+ {
+ if (grub_get_time_ms () > endtime)
+ {
+ port->broken++;
+ /* There is something wrong. But what can I do? */
+ return;
+ }
+ }
+
+ if (port->broken)
+ port->broken--;
+
+ grub_outb (c, port->port + UART_TX);
+}
+
+/* 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,
+ struct grub_serial_config *config)
+{
+ unsigned short divisor;
+
+ divisor = serial_get_divisor (port, config);
+ if (divisor == 0)
+ 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_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_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)
+{
+ unsigned i;
+ for (i = 0; i < GRUB_SERIAL_PORT_NUM; i++)
+ if (serial_hw_io_addr[i])
+ {
+ grub_err_t err;
+
+ grub_outb (0x5a, serial_hw_io_addr[i] + UART_SR);
+ if (grub_inb (serial_hw_io_addr[i] + UART_SR) != 0x5a)
+ {
+ dead_ports |= (1 << i);
+ continue;
+ }
+ grub_outb (0xa5, serial_hw_io_addr[i] + UART_SR);
+ if (grub_inb (serial_hw_io_addr[i] + UART_SR) != 0xa5)
+ {
+ dead_ports |= (1 << i);
+ continue;
+ }
+
+ 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;
+ com_ports[i].port = serial_hw_io_addr[i];
+ err = grub_serial_config_defaults (&com_ports[i]);
+ if (err)
+ grub_print_error ();
+
+ grub_serial_register (&com_ports[i]);
+ }
+}
+
+/* Return the port number for the UNITth serial device. */
+grub_port_t
+grub_ns8250_hw_get_port (const unsigned int unit)
+{
+ if (unit < GRUB_SERIAL_PORT_NUM
+ && !(dead_ports & (1 << unit)))
+ return serial_hw_io_addr[unit];
+ else
+ return 0;
+}
+
+char *
+grub_serial_ns8250_add_port (grub_port_t port)
+{
+ struct grub_serial_port *p;
+ unsigned i;
+ for (i = 0; i < GRUB_SERIAL_PORT_NUM; i++)
+ if (com_ports[i].port == port)
+ {
+ if (dead_ports & (1 << i))
+ return NULL;
+ return com_names[i];
+ }
+
+ grub_outb (0x5a, port + UART_SR);
+ if (grub_inb (port + UART_SR) != 0x5a)
+ return NULL;
+
+ grub_outb (0xa5, port + UART_SR);
+ if (grub_inb (port + UART_SR) != 0xa5)
+ return NULL;
+
+ 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_config_defaults (p);
+ p->port = port;
+ grub_serial_register (p);
+
+ return p->name;
+}
diff --git a/grub-core/term/ps2.c b/grub-core/term/ps2.c
new file mode 100644
index 0000000..7ae4e9f
--- /dev/null
+++ b/grub-core/term/ps2.c
@@ -0,0 +1,387 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/dl.h>
+#include <grub/misc.h>
+#include <grub/term.h>
+#include <grub/keyboard_layouts.h>
+#include <grub/ps2.h>
+
+#define KEYBOARD_LED_SCROLL (1 << 0)
+#define KEYBOARD_LED_NUM (1 << 1)
+#define KEYBOARD_LED_CAPS (1 << 2)
+
+static const grub_uint8_t set1_mapping[128] =
+ {
+ /* 0x00 */ 0 /* Unused */, GRUB_KEYBOARD_KEY_ESCAPE,
+ /* 0x02 */ GRUB_KEYBOARD_KEY_1, GRUB_KEYBOARD_KEY_2,
+ /* 0x04 */ GRUB_KEYBOARD_KEY_3, GRUB_KEYBOARD_KEY_4,
+ /* 0x06 */ GRUB_KEYBOARD_KEY_5, GRUB_KEYBOARD_KEY_6,
+ /* 0x08 */ GRUB_KEYBOARD_KEY_7, GRUB_KEYBOARD_KEY_8,
+ /* 0x0a */ GRUB_KEYBOARD_KEY_9, GRUB_KEYBOARD_KEY_0,
+ /* 0x0c */ GRUB_KEYBOARD_KEY_DASH, GRUB_KEYBOARD_KEY_EQUAL,
+ /* 0x0e */ GRUB_KEYBOARD_KEY_BACKSPACE, GRUB_KEYBOARD_KEY_TAB,
+ /* 0x10 */ GRUB_KEYBOARD_KEY_Q, GRUB_KEYBOARD_KEY_W,
+ /* 0x12 */ GRUB_KEYBOARD_KEY_E, GRUB_KEYBOARD_KEY_R,
+ /* 0x14 */ GRUB_KEYBOARD_KEY_T, GRUB_KEYBOARD_KEY_Y,
+ /* 0x16 */ GRUB_KEYBOARD_KEY_U, GRUB_KEYBOARD_KEY_I,
+ /* 0x18 */ GRUB_KEYBOARD_KEY_O, GRUB_KEYBOARD_KEY_P,
+ /* 0x1a */ GRUB_KEYBOARD_KEY_LBRACKET, GRUB_KEYBOARD_KEY_RBRACKET,
+ /* 0x1c */ GRUB_KEYBOARD_KEY_ENTER, GRUB_KEYBOARD_KEY_LEFT_CTRL,
+ /* 0x1e */ GRUB_KEYBOARD_KEY_A, GRUB_KEYBOARD_KEY_S,
+ /* 0x20 */ GRUB_KEYBOARD_KEY_D, GRUB_KEYBOARD_KEY_F,
+ /* 0x22 */ GRUB_KEYBOARD_KEY_G, GRUB_KEYBOARD_KEY_H,
+ /* 0x24 */ GRUB_KEYBOARD_KEY_J, GRUB_KEYBOARD_KEY_K,
+ /* 0x26 */ GRUB_KEYBOARD_KEY_L, GRUB_KEYBOARD_KEY_SEMICOLON,
+ /* 0x28 */ GRUB_KEYBOARD_KEY_DQUOTE, GRUB_KEYBOARD_KEY_RQUOTE,
+ /* 0x2a */ GRUB_KEYBOARD_KEY_LEFT_SHIFT, GRUB_KEYBOARD_KEY_BACKSLASH,
+ /* 0x2c */ GRUB_KEYBOARD_KEY_Z, GRUB_KEYBOARD_KEY_X,
+ /* 0x2e */ GRUB_KEYBOARD_KEY_C, GRUB_KEYBOARD_KEY_V,
+ /* 0x30 */ GRUB_KEYBOARD_KEY_B, GRUB_KEYBOARD_KEY_N,
+ /* 0x32 */ GRUB_KEYBOARD_KEY_M, GRUB_KEYBOARD_KEY_COMMA,
+ /* 0x34 */ GRUB_KEYBOARD_KEY_DOT, GRUB_KEYBOARD_KEY_SLASH,
+ /* 0x36 */ GRUB_KEYBOARD_KEY_RIGHT_SHIFT, GRUB_KEYBOARD_KEY_NUMMUL,
+ /* 0x38 */ GRUB_KEYBOARD_KEY_LEFT_ALT, GRUB_KEYBOARD_KEY_SPACE,
+ /* 0x3a */ GRUB_KEYBOARD_KEY_CAPS_LOCK, GRUB_KEYBOARD_KEY_F1,
+ /* 0x3c */ GRUB_KEYBOARD_KEY_F2, GRUB_KEYBOARD_KEY_F3,
+ /* 0x3e */ GRUB_KEYBOARD_KEY_F4, GRUB_KEYBOARD_KEY_F5,
+ /* 0x40 */ GRUB_KEYBOARD_KEY_F6, GRUB_KEYBOARD_KEY_F7,
+ /* 0x42 */ GRUB_KEYBOARD_KEY_F8, GRUB_KEYBOARD_KEY_F9,
+ /* 0x44 */ GRUB_KEYBOARD_KEY_F10, GRUB_KEYBOARD_KEY_NUM_LOCK,
+ /* 0x46 */ GRUB_KEYBOARD_KEY_SCROLL_LOCK, GRUB_KEYBOARD_KEY_NUM7,
+ /* 0x48 */ GRUB_KEYBOARD_KEY_NUM8, GRUB_KEYBOARD_KEY_NUM9,
+ /* 0x4a */ GRUB_KEYBOARD_KEY_NUMMINUS, GRUB_KEYBOARD_KEY_NUM4,
+ /* 0x4c */ GRUB_KEYBOARD_KEY_NUM5, GRUB_KEYBOARD_KEY_NUM6,
+ /* 0x4e */ GRUB_KEYBOARD_KEY_NUMPLUS, GRUB_KEYBOARD_KEY_NUM1,
+ /* 0x50 */ GRUB_KEYBOARD_KEY_NUM2, GRUB_KEYBOARD_KEY_NUM3,
+ /* 0x52 */ GRUB_KEYBOARD_KEY_NUM0, GRUB_KEYBOARD_KEY_NUMDOT,
+ /* 0x54 */ 0, 0,
+ /* 0x56 */ GRUB_KEYBOARD_KEY_102ND, GRUB_KEYBOARD_KEY_F11,
+ /* 0x58 */ GRUB_KEYBOARD_KEY_F12, 0,
+ /* 0x5a */ 0, 0,
+ /* 0x5c */ 0, 0,
+ /* 0x5e */ 0, 0,
+ /* 0x60 */ 0, 0,
+ /* 0x62 */ 0, 0,
+ /* OLPC keys. Just mapped to normal keys. */
+ /* 0x64 */ 0, GRUB_KEYBOARD_KEY_UP,
+ /* 0x66 */ GRUB_KEYBOARD_KEY_DOWN, GRUB_KEYBOARD_KEY_LEFT,
+ /* 0x68 */ GRUB_KEYBOARD_KEY_RIGHT, 0,
+ /* 0x6a */ 0, 0,
+ /* 0x6c */ 0, 0,
+ /* 0x6e */ 0, 0,
+ /* 0x70 */ 0, 0,
+ /* 0x72 */ 0, GRUB_KEYBOARD_KEY_JP_RO,
+ /* 0x74 */ 0, 0,
+ /* 0x76 */ 0, 0,
+ /* 0x78 */ 0, 0,
+ /* 0x7a */ 0, 0,
+ /* 0x7c */ 0, GRUB_KEYBOARD_KEY_JP_YEN,
+ /* 0x7e */ GRUB_KEYBOARD_KEY_KPCOMMA
+ };
+
+static const struct
+{
+ grub_uint8_t from, to;
+} set1_e0_mapping[] =
+ {
+ {0x1c, GRUB_KEYBOARD_KEY_NUMENTER},
+ {0x1d, GRUB_KEYBOARD_KEY_RIGHT_CTRL},
+ {0x35, GRUB_KEYBOARD_KEY_NUMSLASH },
+ {0x38, GRUB_KEYBOARD_KEY_RIGHT_ALT},
+ {0x47, GRUB_KEYBOARD_KEY_HOME},
+ {0x48, GRUB_KEYBOARD_KEY_UP},
+ {0x49, GRUB_KEYBOARD_KEY_PPAGE},
+ {0x4b, GRUB_KEYBOARD_KEY_LEFT},
+ {0x4d, GRUB_KEYBOARD_KEY_RIGHT},
+ {0x4f, GRUB_KEYBOARD_KEY_END},
+ {0x50, GRUB_KEYBOARD_KEY_DOWN},
+ {0x51, GRUB_KEYBOARD_KEY_NPAGE},
+ {0x52, GRUB_KEYBOARD_KEY_INSERT},
+ {0x53, GRUB_KEYBOARD_KEY_DELETE},
+ };
+
+static const grub_uint8_t set2_mapping[256] =
+ {
+ /* 0x00 */ 0, GRUB_KEYBOARD_KEY_F9,
+ /* 0x02 */ 0, GRUB_KEYBOARD_KEY_F5,
+ /* 0x04 */ GRUB_KEYBOARD_KEY_F3, GRUB_KEYBOARD_KEY_F1,
+ /* 0x06 */ GRUB_KEYBOARD_KEY_F2, GRUB_KEYBOARD_KEY_F12,
+ /* 0x08 */ 0, GRUB_KEYBOARD_KEY_F10,
+ /* 0x0a */ GRUB_KEYBOARD_KEY_F8, GRUB_KEYBOARD_KEY_F6,
+ /* 0x0c */ GRUB_KEYBOARD_KEY_F4, GRUB_KEYBOARD_KEY_TAB,
+ /* 0x0e */ GRUB_KEYBOARD_KEY_RQUOTE, 0,
+ /* 0x10 */ 0, GRUB_KEYBOARD_KEY_LEFT_ALT,
+ /* 0x12 */ GRUB_KEYBOARD_KEY_LEFT_SHIFT, 0,
+ /* 0x14 */ GRUB_KEYBOARD_KEY_LEFT_CTRL, GRUB_KEYBOARD_KEY_Q,
+ /* 0x16 */ GRUB_KEYBOARD_KEY_1, 0,
+ /* 0x18 */ 0, 0,
+ /* 0x1a */ GRUB_KEYBOARD_KEY_Z, GRUB_KEYBOARD_KEY_S,
+ /* 0x1c */ GRUB_KEYBOARD_KEY_A, GRUB_KEYBOARD_KEY_W,
+ /* 0x1e */ GRUB_KEYBOARD_KEY_2, 0,
+ /* 0x20 */ 0, GRUB_KEYBOARD_KEY_C,
+ /* 0x22 */ GRUB_KEYBOARD_KEY_X, GRUB_KEYBOARD_KEY_D,
+ /* 0x24 */ GRUB_KEYBOARD_KEY_E, GRUB_KEYBOARD_KEY_4,
+ /* 0x26 */ GRUB_KEYBOARD_KEY_3, 0,
+ /* 0x28 */ 0, GRUB_KEYBOARD_KEY_SPACE,
+ /* 0x2a */ GRUB_KEYBOARD_KEY_V, GRUB_KEYBOARD_KEY_F,
+ /* 0x2c */ GRUB_KEYBOARD_KEY_T, GRUB_KEYBOARD_KEY_R,
+ /* 0x2e */ GRUB_KEYBOARD_KEY_5, 0,
+ /* 0x30 */ 0, GRUB_KEYBOARD_KEY_N,
+ /* 0x32 */ GRUB_KEYBOARD_KEY_B, GRUB_KEYBOARD_KEY_H,
+ /* 0x34 */ GRUB_KEYBOARD_KEY_G, GRUB_KEYBOARD_KEY_Y,
+ /* 0x36 */ GRUB_KEYBOARD_KEY_6, 0,
+ /* 0x38 */ 0, 0,
+ /* 0x3a */ GRUB_KEYBOARD_KEY_M, GRUB_KEYBOARD_KEY_J,
+ /* 0x3c */ GRUB_KEYBOARD_KEY_U, GRUB_KEYBOARD_KEY_7,
+ /* 0x3e */ GRUB_KEYBOARD_KEY_8, 0,
+ /* 0x40 */ 0, GRUB_KEYBOARD_KEY_COMMA,
+ /* 0x42 */ GRUB_KEYBOARD_KEY_K, GRUB_KEYBOARD_KEY_I,
+ /* 0x44 */ GRUB_KEYBOARD_KEY_O, GRUB_KEYBOARD_KEY_0,
+ /* 0x46 */ GRUB_KEYBOARD_KEY_9, 0,
+ /* 0x48 */ 0, GRUB_KEYBOARD_KEY_DOT,
+ /* 0x4a */ GRUB_KEYBOARD_KEY_SLASH, GRUB_KEYBOARD_KEY_L,
+ /* 0x4c */ GRUB_KEYBOARD_KEY_SEMICOLON, GRUB_KEYBOARD_KEY_P,
+ /* 0x4e */ GRUB_KEYBOARD_KEY_DASH, 0,
+ /* 0x50 */ 0, GRUB_KEYBOARD_KEY_JP_RO,
+ /* 0x52 */ GRUB_KEYBOARD_KEY_DQUOTE, 0,
+ /* 0x54 */ GRUB_KEYBOARD_KEY_LBRACKET, GRUB_KEYBOARD_KEY_EQUAL,
+ /* 0x56 */ 0, 0,
+ /* 0x58 */ GRUB_KEYBOARD_KEY_CAPS_LOCK, GRUB_KEYBOARD_KEY_RIGHT_SHIFT,
+ /* 0x5a */ GRUB_KEYBOARD_KEY_ENTER, GRUB_KEYBOARD_KEY_RBRACKET,
+ /* 0x5c */ 0, GRUB_KEYBOARD_KEY_BACKSLASH,
+ /* 0x5e */ 0, 0,
+ /* 0x60 */ 0, GRUB_KEYBOARD_KEY_102ND,
+ /* 0x62 */ 0, 0,
+ /* 0x64 */ 0, 0,
+ /* 0x66 */ GRUB_KEYBOARD_KEY_BACKSPACE, 0,
+ /* 0x68 */ 0, GRUB_KEYBOARD_KEY_NUM1,
+ /* 0x6a */ GRUB_KEYBOARD_KEY_JP_YEN, GRUB_KEYBOARD_KEY_NUM4,
+ /* 0x6c */ GRUB_KEYBOARD_KEY_NUM7, GRUB_KEYBOARD_KEY_KPCOMMA,
+ /* 0x6e */ 0, 0,
+ /* 0x70 */ GRUB_KEYBOARD_KEY_NUM0, GRUB_KEYBOARD_KEY_NUMDOT,
+ /* 0x72 */ GRUB_KEYBOARD_KEY_NUM2, GRUB_KEYBOARD_KEY_NUM5,
+ /* 0x74 */ GRUB_KEYBOARD_KEY_NUM6, GRUB_KEYBOARD_KEY_NUM8,
+ /* 0x76 */ GRUB_KEYBOARD_KEY_ESCAPE, GRUB_KEYBOARD_KEY_NUM_LOCK,
+ /* 0x78 */ GRUB_KEYBOARD_KEY_F11, GRUB_KEYBOARD_KEY_NUMPLUS,
+ /* 0x7a */ GRUB_KEYBOARD_KEY_NUM3, GRUB_KEYBOARD_KEY_NUMMINUS,
+ /* 0x7c */ GRUB_KEYBOARD_KEY_NUMMUL, GRUB_KEYBOARD_KEY_NUM9,
+ /* 0x7e */ GRUB_KEYBOARD_KEY_SCROLL_LOCK, 0,
+ /* 0x80 */ 0, 0,
+ /* 0x82 */ 0, GRUB_KEYBOARD_KEY_F7,
+ };
+
+static const struct
+{
+ grub_uint8_t from, to;
+} set2_e0_mapping[] =
+ {
+ {0x11, GRUB_KEYBOARD_KEY_RIGHT_ALT},
+ {0x14, GRUB_KEYBOARD_KEY_RIGHT_CTRL},
+ {0x4a, GRUB_KEYBOARD_KEY_NUMSLASH},
+ {0x5a, GRUB_KEYBOARD_KEY_NUMENTER},
+ {0x69, GRUB_KEYBOARD_KEY_END},
+ {0x6b, GRUB_KEYBOARD_KEY_LEFT},
+ {0x6c, GRUB_KEYBOARD_KEY_HOME},
+ {0x70, GRUB_KEYBOARD_KEY_INSERT},
+ {0x71, GRUB_KEYBOARD_KEY_DELETE},
+ {0x72, GRUB_KEYBOARD_KEY_DOWN},
+ {0x74, GRUB_KEYBOARD_KEY_RIGHT},
+ {0x75, GRUB_KEYBOARD_KEY_UP},
+ {0x7a, GRUB_KEYBOARD_KEY_NPAGE},
+ {0x7d, GRUB_KEYBOARD_KEY_PPAGE},
+ };
+
+static int
+fetch_key (struct grub_ps2_state *ps2_state, grub_uint8_t at_key, int *is_break)
+{
+ int was_ext = 0;
+ int ret = 0;
+
+ /* May happen if no keyboard is connected. Just ignore this. */
+ if (at_key == 0xff)
+ return -1;
+ if (at_key == 0xe0)
+ {
+ ps2_state->e0_received = 1;
+ return -1;
+ }
+
+ if ((ps2_state->current_set == 2 || ps2_state->current_set == 3) && at_key == 0xf0)
+ {
+ ps2_state->f0_received = 1;
+ return -1;
+ }
+
+ /* Setting LEDs may generate ACKs. */
+ if (at_key == GRUB_AT_ACK)
+ return -1;
+
+ was_ext = ps2_state->e0_received;
+ ps2_state->e0_received = 0;
+
+ switch (ps2_state->current_set)
+ {
+ case 1:
+ *is_break = !!(at_key & 0x80);
+ if (!was_ext)
+ ret = set1_mapping[at_key & 0x7f];
+ else
+ {
+ unsigned i;
+ for (i = 0; i < ARRAY_SIZE (set1_e0_mapping); i++)
+ if (set1_e0_mapping[i].from == (at_key & 0x7f))
+ {
+ ret = set1_e0_mapping[i].to;
+ break;
+ }
+ }
+ break;
+ case 2:
+ *is_break = ps2_state->f0_received;
+ ps2_state->f0_received = 0;
+ if (!was_ext)
+ ret = set2_mapping[at_key];
+ else
+ {
+ unsigned i;
+ for (i = 0; i < ARRAY_SIZE (set2_e0_mapping); i++)
+ if (set2_e0_mapping[i].from == at_key)
+ {
+ ret = set2_e0_mapping[i].to;
+ break;
+ }
+ }
+ break;
+ default:
+ return -1;
+ }
+ if (!ret)
+ {
+ if (was_ext)
+ grub_dprintf ("atkeyb", "Unknown key 0xe0+0x%02x from set %d\n",
+ at_key, ps2_state->current_set);
+ else
+ grub_dprintf ("atkeyb", "Unknown key 0x%02x from set %d\n",
+ at_key, ps2_state->current_set);
+ return -1;
+ }
+ return ret;
+}
+
+/* FIXME: This should become an interrupt service routine. For now
+ it's just used to catch events from control keys. */
+static int
+grub_keyboard_isr (struct grub_ps2_state *ps2_state,
+ grub_keyboard_key_t key, int is_break)
+{
+ if (!is_break)
+ switch (key)
+ {
+ case GRUB_KEYBOARD_KEY_LEFT_SHIFT:
+ ps2_state->at_keyboard_status |= GRUB_TERM_STATUS_LSHIFT;
+ return 1;
+ case GRUB_KEYBOARD_KEY_RIGHT_SHIFT:
+ ps2_state->at_keyboard_status |= GRUB_TERM_STATUS_RSHIFT;
+ return 1;
+ case GRUB_KEYBOARD_KEY_LEFT_CTRL:
+ ps2_state->at_keyboard_status |= GRUB_TERM_STATUS_LCTRL;
+ return 1;
+ case GRUB_KEYBOARD_KEY_RIGHT_CTRL:
+ ps2_state->at_keyboard_status |= GRUB_TERM_STATUS_RCTRL;
+ return 1;
+ case GRUB_KEYBOARD_KEY_RIGHT_ALT:
+ ps2_state->at_keyboard_status |= GRUB_TERM_STATUS_RALT;
+ return 1;
+ case GRUB_KEYBOARD_KEY_LEFT_ALT:
+ ps2_state->at_keyboard_status |= GRUB_TERM_STATUS_LALT;
+ return 1;
+ default:
+ return 0;
+ }
+ else
+ switch (key)
+ {
+ case GRUB_KEYBOARD_KEY_LEFT_SHIFT:
+ ps2_state->at_keyboard_status &= ~GRUB_TERM_STATUS_LSHIFT;
+ return 1;
+ case GRUB_KEYBOARD_KEY_RIGHT_SHIFT:
+ ps2_state->at_keyboard_status &= ~GRUB_TERM_STATUS_RSHIFT;
+ return 1;
+ case GRUB_KEYBOARD_KEY_LEFT_CTRL:
+ ps2_state->at_keyboard_status &= ~GRUB_TERM_STATUS_LCTRL;
+ return 1;
+ case GRUB_KEYBOARD_KEY_RIGHT_CTRL:
+ ps2_state->at_keyboard_status &= ~GRUB_TERM_STATUS_RCTRL;
+ return 1;
+ case GRUB_KEYBOARD_KEY_RIGHT_ALT:
+ ps2_state->at_keyboard_status &= ~GRUB_TERM_STATUS_RALT;
+ return 1;
+ case GRUB_KEYBOARD_KEY_LEFT_ALT:
+ ps2_state->at_keyboard_status &= ~GRUB_TERM_STATUS_LALT;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* If there is a key pending, return it; otherwise return GRUB_TERM_NO_KEY. */
+int
+grub_ps2_process_incoming_byte (struct grub_ps2_state *ps2_state,
+ grub_uint8_t at_key)
+{
+ int code;
+ int is_break = 0;
+
+ code = fetch_key (ps2_state, at_key, &is_break);
+ if (code == -1)
+ return GRUB_TERM_NO_KEY;
+
+ if (grub_keyboard_isr (ps2_state, code, is_break))
+ return GRUB_TERM_NO_KEY;
+ if (is_break)
+ return GRUB_TERM_NO_KEY;
+#ifdef DEBUG_AT_KEYBOARD
+ grub_dprintf ("atkeyb", "Detected key 0x%x\n", code);
+#endif
+ switch (code)
+ {
+ case GRUB_KEYBOARD_KEY_CAPS_LOCK:
+ ps2_state->at_keyboard_status ^= GRUB_TERM_STATUS_CAPS;
+ ps2_state->led_status ^= KEYBOARD_LED_CAPS;
+
+#ifdef DEBUG_AT_KEYBOARD
+ grub_dprintf ("atkeyb", "caps_lock = %d\n", !!(ps2_state->at_keyboard_status & GRUB_KEYBOARD_STATUS_CAPS_LOCK));
+#endif
+ return GRUB_TERM_NO_KEY;
+ case GRUB_KEYBOARD_KEY_NUM_LOCK:
+ ps2_state->at_keyboard_status ^= GRUB_TERM_STATUS_NUM;
+ ps2_state->led_status ^= KEYBOARD_LED_NUM;
+
+#ifdef DEBUG_AT_KEYBOARD
+ grub_dprintf ("atkeyb", "num_lock = %d\n", !!(ps2_state->at_keyboard_status & GRUB_KEYBOARD_STATUS_NUM_LOCK));
+#endif
+ return GRUB_TERM_NO_KEY;
+ case GRUB_KEYBOARD_KEY_SCROLL_LOCK:
+ ps2_state->at_keyboard_status ^= GRUB_TERM_STATUS_SCROLL;
+ ps2_state->led_status ^= KEYBOARD_LED_SCROLL;
+ return GRUB_TERM_NO_KEY;
+ default:
+ return grub_term_map_key (code, ps2_state->at_keyboard_status);
+ }
+}
diff --git a/grub-core/term/serial.c b/grub-core/term/serial.c
new file mode 100644
index 0000000..f9271b0
--- /dev/null
+++ b/grub-core/term/serial.c
@@ -0,0 +1,463 @@
+/*
+ * 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/serial.h>
+#include <grub/term.h>
+#include <grub/types.h>
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/terminfo.h>
+#if !defined (GRUB_MACHINE_EMU) && (defined(__mips__) || defined (__i386__) || defined (__x86_64__))
+#include <grub/cpu/io.h>
+#endif
+#include <grub/extcmd.h>
+#include <grub/i18n.h>
+#include <grub/list.h>
+#ifdef GRUB_MACHINE_MIPS_LOONGSON
+#include <grub/machine/kernel.h>
+#endif
+#ifdef GRUB_MACHINE_IEEE1275
+#include <grub/ieee1275/console.h>
+#endif
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define FOR_SERIAL_PORTS(var) FOR_LIST_ELEMENTS((var), (grub_serial_ports))
+
+enum
+ {
+ OPTION_UNIT,
+ OPTION_PORT,
+ OPTION_SPEED,
+ OPTION_WORD,
+ OPTION_PARITY,
+ OPTION_STOP,
+ OPTION_BASE_CLOCK,
+ OPTION_RTSCTS
+ };
+
+/* Argument options. */
+static const struct grub_arg_option options[] =
+{
+ {"unit", 'u', 0, N_("Set the serial unit."), 0, ARG_TYPE_INT},
+ {"port", 'p', 0, N_("Set the serial port address."), 0, ARG_TYPE_STRING},
+ {"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},
+ {"base-clock", 'b', 0, N_("Set the base frequency."), 0, ARG_TYPE_STRING},
+ {"rtscts", 'f', 0, N_("Enable/disable RTS/CTS."), "on|off", ARG_TYPE_STRING},
+ {0, 0, 0, 0, 0, 0}
+};
+
+static struct grub_serial_port *grub_serial_ports;
+
+struct grub_serial_output_state
+{
+ struct grub_terminfo_output_state tinfo;
+ struct grub_serial_port *port;
+};
+
+struct grub_serial_input_state
+{
+ struct grub_terminfo_input_state tinfo;
+ struct grub_serial_port *port;
+};
+
+static void
+serial_put (grub_term_output_t term, const int c)
+{
+ struct grub_serial_output_state *data = term->data;
+ data->port->driver->put (data->port, c);
+}
+
+static int
+serial_fetch (grub_term_input_t term)
+{
+ struct grub_serial_input_state *data = term->data;
+ return data->port->driver->fetch (data->port);
+}
+
+static const struct grub_serial_input_state grub_serial_terminfo_input_template =
+ {
+ .tinfo =
+ {
+ .readkey = serial_fetch
+ }
+ };
+
+static const struct grub_serial_output_state grub_serial_terminfo_output_template =
+ {
+ .tinfo =
+ {
+ .put = serial_put,
+ .size = { 80, 24 }
+ }
+ };
+
+static struct grub_serial_input_state grub_serial_terminfo_input;
+
+static struct grub_serial_output_state grub_serial_terminfo_output;
+
+static int registered = 0;
+
+static struct grub_term_input grub_serial_term_input =
+{
+ .name = "serial",
+ .init = grub_terminfo_input_init,
+ .getkey = grub_terminfo_getkey,
+ .data = &grub_serial_terminfo_input
+};
+
+static struct grub_term_output grub_serial_term_output =
+{
+ .name = "serial",
+ .init = grub_terminfo_output_init,
+ .putchar = grub_terminfo_putchar,
+ .getwh = grub_terminfo_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,
+ .progress_update_divisor = GRUB_PROGRESS_SLOW
+};
+
+
+
+struct grub_serial_port *
+grub_serial_find (const char *name)
+{
+ struct grub_serial_port *port;
+
+ FOR_SERIAL_PORTS (port)
+ if (grub_strcmp (port->name, name) == 0)
+ break;
+
+#if (defined(__mips__) || defined (__i386__) || defined (__x86_64__)) && !defined(GRUB_MACHINE_EMU) && !defined(GRUB_MACHINE_ARC)
+ if (!port && grub_memcmp (name, "port", sizeof ("port") - 1) == 0
+ && grub_isxdigit (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;
+ }
+#endif
+
+#ifdef GRUB_MACHINE_IEEE1275
+ if (!port && grub_memcmp (name, "ieee1275/", sizeof ("ieee1275/") - 1) == 0)
+ {
+ name = grub_ofserial_add_port (&name[sizeof ("ieee1275/") - 1]);
+ if (!name)
+ return NULL;
+
+ FOR_SERIAL_PORTS (port)
+ if (grub_strcmp (port->name, name) == 0)
+ break;
+ }
+#endif
+
+ return port;
+}
+
+static grub_err_t
+grub_cmd_serial (grub_extcmd_context_t ctxt, int argc, char **args)
+{
+ struct grub_arg_list *state = ctxt->state;
+ char pname[40];
+ const char *name = NULL;
+ struct grub_serial_port *port;
+ struct grub_serial_config config;
+ grub_err_t err;
+
+ if (state[OPTION_UNIT].set)
+ {
+ grub_snprintf (pname, sizeof (pname), "com%ld",
+ grub_strtoul (state[0].arg, 0, 0));
+ name = pname;
+ }
+
+ if (state[OPTION_PORT].set)
+ {
+ 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,
+ N_("serial port `%s' isn't found"),
+ name);
+
+ config = port->config;
+
+ if (state[OPTION_SPEED].set) {
+ config.speed = grub_strtoul (state[OPTION_SPEED].arg, 0, 0);
+ if (config.speed == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ N_("unsupported serial port parity"));
+ }
+
+ if (state[OPTION_WORD].set)
+ config.word_len = grub_strtoul (state[OPTION_WORD].arg, 0, 0);
+
+ if (state[OPTION_PARITY].set)
+ {
+ if (! grub_strcmp (state[OPTION_PARITY].arg, "no"))
+ config.parity = GRUB_SERIAL_PARITY_NONE;
+ else if (! grub_strcmp (state[OPTION_PARITY].arg, "odd"))
+ config.parity = GRUB_SERIAL_PARITY_ODD;
+ else if (! grub_strcmp (state[OPTION_PARITY].arg, "even"))
+ config.parity = GRUB_SERIAL_PARITY_EVEN;
+ else
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ N_("unsupported serial port parity"));
+ }
+
+ if (state[OPTION_RTSCTS].set)
+ {
+ if (grub_strcmp (state[OPTION_RTSCTS].arg, "on") == 0)
+ config.rtscts = 1;
+ else if (grub_strcmp (state[OPTION_RTSCTS].arg, "off") == 0)
+ config.rtscts = 0;
+ else
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ N_("unsupported serial port flow control"));
+ }
+
+ if (state[OPTION_STOP].set)
+ {
+ if (! grub_strcmp (state[OPTION_STOP].arg, "1"))
+ config.stop_bits = GRUB_SERIAL_STOP_BITS_1;
+ else if (! grub_strcmp (state[OPTION_STOP].arg, "2"))
+ config.stop_bits = GRUB_SERIAL_STOP_BITS_2;
+ else if (! grub_strcmp (state[OPTION_STOP].arg, "1.5"))
+ config.stop_bits = GRUB_SERIAL_STOP_BITS_1_5;
+ else
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ N_("unsupported serial port stop bits number"));
+ }
+
+ if (state[OPTION_BASE_CLOCK].set)
+ {
+ const char *ptr;
+ config.base_clock = grub_strtoull (state[OPTION_BASE_CLOCK].arg, &ptr, 0);
+ if (grub_errno)
+ return grub_errno;
+ if (ptr && *ptr == 'M')
+ config.base_clock *= 1000000;
+ if (ptr && (*ptr == 'k' || *ptr == 'K'))
+ config.base_clock *= 1000;
+ }
+
+ if (config.speed == 0)
+ config.speed = 9600;
+
+ /* Initialize with new settings. */
+ err = port->driver->configure (port, &config);
+ if (err)
+ return err;
+#if !defined (GRUB_MACHINE_EMU) && !defined(GRUB_MACHINE_ARC) && (defined(__mips__) || defined (__i386__) || defined (__x86_64__))
+
+ /* Compatibility kludge. */
+ if (port->driver == &grub_ns8250_driver)
+ {
+ if (!registered)
+ {
+ grub_terminfo_output_register (&grub_serial_term_output, "vt100");
+
+ 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;
+ }
+#endif
+ return GRUB_ERR_NONE;
+}
+
+#ifdef GRUB_MACHINE_MIPS_LOONGSON
+const char loongson_defserial[][6] =
+ {
+ [GRUB_ARCH_MACHINE_YEELOONG] = "com0",
+ [GRUB_ARCH_MACHINE_FULOONG2F] = "com2",
+ [GRUB_ARCH_MACHINE_FULOONG2E] = "com1"
+ };
+#endif
+
+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;
+
+ in = grub_malloc (sizeof (*in));
+ if (!in)
+ return grub_errno;
+
+ indata = grub_malloc (sizeof (*indata));
+ if (!indata)
+ {
+ grub_free (in);
+ return grub_errno;
+ }
+
+ 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_zalloc (sizeof (*out));
+ if (!out)
+ {
+ grub_free (indata);
+ grub_free ((char *) in->name);
+ grub_free (in);
+ return grub_errno;
+ }
+
+ outdata = grub_malloc (sizeof (*outdata));
+ if (!outdata)
+ {
+ grub_free (indata);
+ grub_free ((char *) in->name);
+ grub_free (out);
+ grub_free (in);
+ return grub_errno;
+ }
+
+ 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;
+ port->term_in = in;
+ port->term_out = out;
+ grub_terminfo_output_register (out, "vt100");
+#ifdef GRUB_MACHINE_MIPS_LOONGSON
+ if (grub_strcmp (port->name, loongson_defserial[grub_arch_machine]) == 0)
+ {
+ grub_term_register_input_active ("serial_*", in);
+ grub_term_register_output_active ("serial_*", out);
+ }
+ else
+ {
+ grub_term_register_input_inactive ("serial_*", in);
+ grub_term_register_output_inactive ("serial_*", out);
+ }
+#else
+ grub_term_register_input ("serial_*", in);
+ grub_term_register_output ("serial_*", out);
+#endif
+
+ return GRUB_ERR_NONE;
+}
+
+void
+grub_serial_unregister (struct grub_serial_port *port)
+{
+ if (port->driver->fini)
+ port->driver->fini (port);
+
+ if (port->term_in)
+ grub_term_unregister_input (port->term_in);
+ if (port->term_out)
+ grub_term_unregister_output (port->term_out);
+
+ grub_list_remove (GRUB_AS_LIST (port));
+}
+
+void
+grub_serial_unregister_driver (struct grub_serial_driver *driver)
+{
+ struct grub_serial_port *port, *next;
+ for (port = grub_serial_ports; port; port = next)
+ {
+ next = port->next;
+ if (port->driver == driver)
+ grub_serial_unregister (port);
+ }
+}
+
+static grub_extcmd_t cmd;
+
+GRUB_MOD_INIT(serial)
+{
+ cmd = grub_register_extcmd ("serial", grub_cmd_serial, 0,
+ N_("[OPTIONS...]"),
+ N_("Configure serial port."), options);
+ grub_memcpy (&grub_serial_terminfo_output,
+ &grub_serial_terminfo_output_template,
+ sizeof (grub_serial_terminfo_output));
+
+ grub_memcpy (&grub_serial_terminfo_input,
+ &grub_serial_terminfo_input_template,
+ sizeof (grub_serial_terminfo_input));
+
+#if !defined (GRUB_MACHINE_EMU) && !defined(GRUB_MACHINE_ARC) && (defined(__mips__) || defined (__i386__) || defined (__x86_64__))
+ grub_ns8250_init ();
+#endif
+#ifdef GRUB_MACHINE_IEEE1275
+ grub_ofserial_init ();
+#endif
+#ifdef GRUB_MACHINE_EFI
+ grub_efiserial_init ();
+#endif
+#ifdef GRUB_MACHINE_ARC
+ grub_arcserial_init ();
+#endif
+}
+
+GRUB_MOD_FINI(serial)
+{
+ while (grub_serial_ports)
+ grub_serial_unregister (grub_serial_ports);
+ if (registered)
+ {
+ grub_term_unregister_input (&grub_serial_term_input);
+ grub_term_unregister_output (&grub_serial_term_output);
+ }
+ grub_unregister_extcmd (cmd);
+}
diff --git a/grub-core/term/spkmodem.c b/grub-core/term/spkmodem.c
new file mode 100644
index 0000000..75c8a0f
--- /dev/null
+++ b/grub-core/term/spkmodem.c
@@ -0,0 +1,141 @@
+/* console.c -- Open Firmware console for GRUB. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/term.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/time.h>
+#include <grub/terminfo.h>
+#include <grub/dl.h>
+#include <grub/speaker.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+extern struct grub_terminfo_output_state grub_spkmodem_terminfo_output;
+
+static void
+make_tone (grub_uint16_t freq_count, unsigned int duration)
+{
+ /* Program timer 2. */
+ grub_outb (GRUB_PIT_CTRL_SELECT_2
+ | GRUB_PIT_CTRL_READLOAD_WORD
+ | GRUB_PIT_CTRL_SQUAREWAVE_GEN
+ | GRUB_PIT_CTRL_COUNT_BINARY, GRUB_PIT_CTRL);
+ grub_outb (freq_count & 0xff, GRUB_PIT_COUNTER_2); /* LSB */
+ grub_outb ((freq_count >> 8) & 0xff, GRUB_PIT_COUNTER_2); /* MSB */
+
+ /* Start speaker. */
+ grub_outb (grub_inb (GRUB_PIT_SPEAKER_PORT)
+ | GRUB_PIT_SPK_TMR2 | GRUB_PIT_SPK_DATA,
+ GRUB_PIT_SPEAKER_PORT);
+
+ for (; duration; duration--)
+ {
+ unsigned short counter, previous_counter = 0xffff;
+ while (1)
+ {
+ counter = grub_inb (GRUB_PIT_COUNTER_2);
+ counter |= ((grub_uint16_t) grub_inb (GRUB_PIT_COUNTER_2)) << 8;
+ if (counter > previous_counter)
+ {
+ previous_counter = counter;
+ break;
+ }
+ previous_counter = counter;
+ }
+ }
+}
+
+static int inited;
+
+static void
+put (struct grub_term_output *term __attribute__ ((unused)), const int c)
+{
+ int i;
+
+ make_tone (GRUB_SPEAKER_PIT_FREQUENCY / 200, 4);
+ for (i = 7; i >= 0; i--)
+ {
+ if ((c >> i) & 1)
+ make_tone (GRUB_SPEAKER_PIT_FREQUENCY / 2000, 20);
+ else
+ make_tone (GRUB_SPEAKER_PIT_FREQUENCY / 4000, 40);
+ make_tone (GRUB_SPEAKER_PIT_FREQUENCY / 1000, 10);
+ }
+ make_tone (GRUB_SPEAKER_PIT_FREQUENCY / 200, 0);
+}
+
+static grub_err_t
+grub_spkmodem_init_output (struct grub_term_output *term)
+{
+ /* Some models shutdown sound when not in use and it takes for it
+ around 30 ms to come back on which loses 3 bits. So generate a base
+ 200 Hz continously. */
+
+ make_tone (GRUB_SPEAKER_PIT_FREQUENCY / 200, 0);
+ grub_terminfo_output_init (term);
+
+ return 0;
+}
+
+static grub_err_t
+grub_spkmodem_fini_output (struct grub_term_output *term __attribute__ ((unused)))
+{
+ grub_speaker_beep_off ();
+ inited = 0;
+ return 0;
+}
+
+
+
+
+struct grub_terminfo_output_state grub_spkmodem_terminfo_output =
+ {
+ .put = put,
+ .size = { 80, 24 }
+ };
+
+static struct grub_term_output grub_spkmodem_term_output =
+ {
+ .name = "spkmodem",
+ .init = grub_spkmodem_init_output,
+ .fini = grub_spkmodem_fini_output,
+ .putchar = grub_terminfo_putchar,
+ .getxy = grub_terminfo_getxy,
+ .getwh = grub_terminfo_getwh,
+ .gotoxy = grub_terminfo_gotoxy,
+ .cls = grub_terminfo_cls,
+ .setcolorstate = grub_terminfo_setcolorstate,
+ .setcursor = grub_terminfo_setcursor,
+ .flags = GRUB_TERM_CODE_TYPE_ASCII,
+ .data = &grub_spkmodem_terminfo_output,
+ .progress_update_divisor = GRUB_PROGRESS_NO_UPDATE
+ };
+
+GRUB_MOD_INIT (spkmodem)
+{
+ grub_term_register_output ("spkmodem", &grub_spkmodem_term_output);
+}
+
+
+GRUB_MOD_FINI (spkmodem)
+{
+ grub_term_unregister_output (&grub_spkmodem_term_output);
+}
diff --git a/grub-core/term/terminfo.c b/grub-core/term/terminfo.c
new file mode 100644
index 0000000..85ecf06
--- /dev/null
+++ b/grub-core/term/terminfo.c
@@ -0,0 +1,796 @@
+/* terminfo.c - simple terminfo module */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003,2004,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/>.
+ */
+
+/*
+ * 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/term.h>
+#include <grub/terminfo.h>
+#include <grub/tparm.h>
+#include <grub/extcmd.h>
+#include <grub/i18n.h>
+#include <grub/time.h>
+#if defined(__powerpc__) && defined(GRUB_MACHINE_IEEE1275)
+#include <grub/ieee1275/ieee1275.h>
+#endif
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define ANSI_CSI 0x9b
+#define ANSI_CSI_STR "\x9b"
+
+static struct grub_term_output *terminfo_outputs;
+
+/* Get current terminfo name. */
+char *
+grub_terminfo_get_current (struct grub_term_output *term)
+{
+ struct grub_terminfo_output_state *data
+ = (struct grub_terminfo_output_state *) term->data;
+ return data->name;
+}
+
+/* Free *PTR and set *PTR to NULL, to prevent double-free. */
+static void
+grub_terminfo_free (char **ptr)
+{
+ grub_free (*ptr);
+ *ptr = 0;
+}
+
+static void
+grub_terminfo_all_free (struct grub_term_output *term)
+{
+ struct grub_terminfo_output_state *data
+ = (struct grub_terminfo_output_state *) term->data;
+
+ /* Free previously allocated memory. */
+ grub_terminfo_free (&data->name);
+ grub_terminfo_free (&data->gotoxy);
+ grub_terminfo_free (&data->cls);
+ grub_terminfo_free (&data->reverse_video_on);
+ grub_terminfo_free (&data->reverse_video_off);
+ grub_terminfo_free (&data->cursor_on);
+ grub_terminfo_free (&data->cursor_off);
+}
+
+/* Set current terminfo type. */
+grub_err_t
+grub_terminfo_set_current (struct grub_term_output *term,
+ const char *str)
+{
+ struct grub_terminfo_output_state *data
+ = (struct grub_terminfo_output_state *) term->data;
+ /* 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.
+ */
+
+ grub_terminfo_all_free (term);
+
+ if (grub_strcmp ("vt100", str) == 0)
+ {
+ data->name = grub_strdup ("vt100");
+ data->gotoxy = grub_strdup ("\e[%i%p1%d;%p2%dH");
+ data->cls = grub_strdup ("\e[H\e[J");
+ data->reverse_video_on = grub_strdup ("\e[7m");
+ data->reverse_video_off = grub_strdup ("\e[m");
+ data->cursor_on = grub_strdup ("\e[?25h");
+ data->cursor_off = grub_strdup ("\e[?25l");
+ data->setcolor = NULL;
+ return grub_errno;
+ }
+
+ if (grub_strcmp ("vt100-color", str) == 0)
+ {
+ data->name = grub_strdup ("vt100-color");
+ data->gotoxy = grub_strdup ("\e[%i%p1%d;%p2%dH");
+ data->cls = grub_strdup ("\e[H\e[J");
+ data->reverse_video_on = grub_strdup ("\e[7m");
+ data->reverse_video_off = grub_strdup ("\e[m");
+ data->cursor_on = grub_strdup ("\e[?25h");
+ data->cursor_off = grub_strdup ("\e[?25l");
+ data->setcolor = grub_strdup ("\e[3%p1%dm\e[4%p2%dm");
+ return grub_errno;
+ }
+
+ if (grub_strcmp ("arc", str) == 0)
+ {
+ data->name = grub_strdup ("arc");
+ data->gotoxy = grub_strdup (ANSI_CSI_STR "%i%p1%d;%p2%dH");
+ data->cls = grub_strdup (ANSI_CSI_STR "2J");
+ data->reverse_video_on = grub_strdup (ANSI_CSI_STR "7m");
+ data->reverse_video_off = grub_strdup (ANSI_CSI_STR "0m");
+ data->cursor_on = 0;
+ data->cursor_off = 0;
+ data->setcolor = grub_strdup (ANSI_CSI_STR "3%p1%dm"
+ ANSI_CSI_STR "4%p2%dm");
+ return grub_errno;
+ }
+
+ if (grub_strcmp ("ieee1275", str) == 0
+ || grub_strcmp ("ieee1275-nocursor", str) == 0)
+ {
+ data->name = grub_strdup ("ieee1275");
+ data->gotoxy = grub_strdup ("\e[%i%p1%d;%p2%dH");
+ /* Clear the screen. Using serial console, screen(1) only recognizes the
+ * ANSI escape sequence. Using video console, Apple Open Firmware
+ * (version 3.1.1) only recognizes the literal ^L. So use both. */
+ data->cls = grub_strdup (" \e[2J");
+ data->reverse_video_on = grub_strdup ("\e[7m");
+ data->reverse_video_off = grub_strdup ("\e[m");
+ if (grub_strcmp ("ieee1275", str) == 0)
+ {
+ data->cursor_on = grub_strdup ("\e[?25h");
+ data->cursor_off = grub_strdup ("\e[?25l");
+ }
+ else
+ {
+ data->cursor_on = 0;
+ data->cursor_off = 0;
+ }
+ data->setcolor = grub_strdup ("\e[3%p1%dm\e[4%p2%dm");
+ return grub_errno;
+ }
+
+ if (grub_strcmp ("dumb", str) == 0)
+ {
+ data->name = grub_strdup ("dumb");
+ data->gotoxy = NULL;
+ data->cls = NULL;
+ data->reverse_video_on = NULL;
+ data->reverse_video_off = NULL;
+ data->cursor_on = NULL;
+ data->cursor_off = NULL;
+ data->setcolor = NULL;
+ return grub_errno;
+ }
+
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unknown terminfo type `%s'"),
+ str);
+}
+
+grub_err_t
+grub_terminfo_output_register (struct grub_term_output *term,
+ const char *type)
+{
+ grub_err_t err;
+ struct grub_terminfo_output_state *data;
+
+ err = grub_terminfo_set_current (term, type);
+
+ if (err)
+ return err;
+
+ data = (struct grub_terminfo_output_state *) term->data;
+ data->next = terminfo_outputs;
+ terminfo_outputs = term;
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_terminfo_output_unregister (struct grub_term_output *term)
+{
+ struct grub_term_output **ptr;
+
+ for (ptr = &terminfo_outputs; *ptr;
+ ptr = &((struct grub_terminfo_output_state *) (*ptr)->data)->next)
+ if (*ptr == term)
+ {
+ grub_terminfo_all_free (term);
+ *ptr = ((struct grub_terminfo_output_state *) (*ptr)->data)->next;
+ return GRUB_ERR_NONE;
+ }
+ return grub_error (GRUB_ERR_BUG, "terminal not found");
+}
+
+/* Wrapper for grub_putchar to write strings. */
+static void
+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 (term, *str++);
+}
+
+struct grub_term_coordinate
+grub_terminfo_getxy (struct grub_term_output *term)
+{
+ struct grub_terminfo_output_state *data
+ = (struct grub_terminfo_output_state *) term->data;
+
+ return data->pos;
+}
+
+void
+grub_terminfo_gotoxy (struct grub_term_output *term,
+ struct grub_term_coordinate pos)
+{
+ struct grub_terminfo_output_state *data
+ = (struct grub_terminfo_output_state *) term->data;
+
+ if (pos.x > grub_term_width (term) || pos.y > grub_term_height (term))
+ {
+ grub_error (GRUB_ERR_BUG, "invalid point (%u,%u)", pos.x, pos.y);
+ return;
+ }
+
+ if (data->gotoxy)
+ putstr (term, grub_terminfo_tparm (data->gotoxy, pos.y, pos.x));
+ else
+ {
+ if ((pos.y == data->pos.y) && (pos.x == data->pos.x - 1))
+ data->put (term, '\b');
+ }
+
+ data->pos = pos;
+}
+
+/* Clear the screen. */
+void
+grub_terminfo_cls (struct grub_term_output *term)
+{
+ struct grub_terminfo_output_state *data
+ = (struct grub_terminfo_output_state *) term->data;
+
+ putstr (term, grub_terminfo_tparm (data->cls));
+ grub_terminfo_gotoxy (term, (struct grub_term_coordinate) { 0, 0 });
+}
+
+void
+grub_terminfo_setcolorstate (struct grub_term_output *term,
+ const grub_term_color_state state)
+{
+ struct grub_terminfo_output_state *data
+ = (struct grub_terminfo_output_state *) term->data;
+
+ if (data->setcolor)
+ {
+ int fg;
+ int bg;
+ /* Map from VGA to terminal colors. */
+ const int colormap[8]
+ = { 0, /* Black. */
+ 4, /* Blue. */
+ 2, /* Green. */
+ 6, /* Cyan. */
+ 1, /* Red. */
+ 5, /* Magenta. */
+ 3, /* Yellow. */
+ 7, /* White. */
+ };
+
+ switch (state)
+ {
+ case GRUB_TERM_COLOR_STANDARD:
+ case GRUB_TERM_COLOR_NORMAL:
+ fg = grub_term_normal_color & 0x0f;
+ bg = grub_term_normal_color >> 4;
+ break;
+ case GRUB_TERM_COLOR_HIGHLIGHT:
+ fg = grub_term_highlight_color & 0x0f;
+ bg = grub_term_highlight_color >> 4;
+ break;
+ default:
+ return;
+ }
+
+ putstr (term, grub_terminfo_tparm (data->setcolor, colormap[fg & 7],
+ colormap[bg & 7]));
+ return;
+ }
+
+ switch (state)
+ {
+ case GRUB_TERM_COLOR_STANDARD:
+ case GRUB_TERM_COLOR_NORMAL:
+ putstr (term, grub_terminfo_tparm (data->reverse_video_off));
+ break;
+ case GRUB_TERM_COLOR_HIGHLIGHT:
+ putstr (term, grub_terminfo_tparm (data->reverse_video_on));
+ break;
+ default:
+ break;
+ }
+}
+
+void
+grub_terminfo_setcursor (struct grub_term_output *term, const int on)
+{
+ struct grub_terminfo_output_state *data
+ = (struct grub_terminfo_output_state *) term->data;
+
+ if (on)
+ putstr (term, grub_terminfo_tparm (data->cursor_on));
+ else
+ putstr (term, grub_terminfo_tparm (data->cursor_off));
+}
+
+/* The terminfo version of putchar. */
+void
+grub_terminfo_putchar (struct grub_term_output *term,
+ const struct grub_unicode_glyph *c)
+{
+ struct grub_terminfo_output_state *data
+ = (struct grub_terminfo_output_state *) term->data;
+
+ /* Keep track of the cursor. */
+ switch (c->base)
+ {
+ case '\a':
+ break;
+
+ case '\b':
+ case 127:
+ if (data->pos.x > 0)
+ data->pos.x--;
+ break;
+
+ case '\n':
+ if (data->pos.y < grub_term_height (term) - 1)
+ data->pos.y++;
+ break;
+
+ case '\r':
+ data->pos.x = 0;
+ break;
+
+ default:
+ if ((int) data->pos.x + c->estimated_width >= (int) grub_term_width (term) + 1)
+ {
+ data->pos.x = 0;
+ if (data->pos.y < grub_term_height (term) - 1)
+ data->pos.y++;
+ data->put (term, '\r');
+ data->put (term, '\n');
+ }
+ data->pos.x += c->estimated_width;
+ break;
+ }
+
+ data->put (term, c->base);
+}
+
+struct grub_term_coordinate
+grub_terminfo_getwh (struct grub_term_output *term)
+{
+ struct grub_terminfo_output_state *data
+ = (struct grub_terminfo_output_state *) term->data;
+
+ return data->size;
+}
+
+static void
+grub_terminfo_readkey (struct grub_term_input *term, int *keys, int *len, int max_len,
+ int (*readkey) (struct grub_term_input *term))
+{
+ int c;
+
+#define CONTINUE_READ \
+ { \
+ grub_uint64_t start; \
+ /* On 9600 we have to wait up to 12 milliseconds. */ \
+ start = grub_get_time_ms (); \
+ do \
+ c = readkey (term); \
+ while (c == -1 && grub_get_time_ms () - start < 100); \
+ if (c == -1) \
+ return; \
+ \
+ if (*len >= max_len) \
+ return; \
+ \
+ keys[*len] = c; \
+ (*len)++; \
+ }
+
+ c = readkey (term);
+ if (c < 0)
+ {
+ *len = 0;
+ return;
+ }
+ *len = 1;
+ keys[0] = c;
+ if (c != ANSI_CSI && c != GRUB_TERM_ESC)
+ {
+ /* Backspace: Ctrl-h. */
+ if (c == 0x7f)
+ c = GRUB_TERM_BACKSPACE;
+ if (c < 0x20 && c != GRUB_TERM_TAB && c!= GRUB_TERM_BACKSPACE && c != '\n' && c != '\r')
+ c = GRUB_TERM_CTRL | (c - 1 + 'a');
+ *len = 1;
+ keys[0] = c;
+ return;
+ }
+
+ {
+ static struct
+ {
+ char key;
+ unsigned ascii;
+ }
+ three_code_table[] =
+ {
+ {'4', GRUB_TERM_KEY_DC},
+ {'A', GRUB_TERM_KEY_UP},
+ {'B', GRUB_TERM_KEY_DOWN},
+ {'C', GRUB_TERM_KEY_RIGHT},
+ {'D', GRUB_TERM_KEY_LEFT},
+ {'F', GRUB_TERM_KEY_END},
+ {'H', GRUB_TERM_KEY_HOME},
+ {'K', GRUB_TERM_KEY_END},
+ {'P', GRUB_TERM_KEY_DC},
+ {'?', GRUB_TERM_KEY_PPAGE},
+ {'/', GRUB_TERM_KEY_NPAGE},
+ {'@', GRUB_TERM_KEY_INSERT},
+ };
+
+ static unsigned four_code_table[] =
+ {
+ [1] = GRUB_TERM_KEY_HOME,
+ [3] = GRUB_TERM_KEY_DC,
+ [5] = GRUB_TERM_KEY_PPAGE,
+ [6] = GRUB_TERM_KEY_NPAGE,
+ [7] = GRUB_TERM_KEY_HOME,
+ [8] = GRUB_TERM_KEY_END,
+ [17] = GRUB_TERM_KEY_F6,
+ [18] = GRUB_TERM_KEY_F7,
+ [19] = GRUB_TERM_KEY_F8,
+ [20] = GRUB_TERM_KEY_F9,
+ [21] = GRUB_TERM_KEY_F10,
+ [23] = GRUB_TERM_KEY_F11,
+ [24] = GRUB_TERM_KEY_F12,
+ };
+ char fx_key[] =
+ { 'P', 'Q', 'w', 'x', 't', 'u',
+ 'q', 'r', 'p', 'M', 'A', 'B', 'H', 'F' };
+ unsigned fx_code[] =
+ { GRUB_TERM_KEY_F1, GRUB_TERM_KEY_F2, GRUB_TERM_KEY_F3,
+ GRUB_TERM_KEY_F4, GRUB_TERM_KEY_F5, GRUB_TERM_KEY_F6,
+ GRUB_TERM_KEY_F7, GRUB_TERM_KEY_F8, GRUB_TERM_KEY_F9,
+ GRUB_TERM_KEY_F10, GRUB_TERM_KEY_F11, GRUB_TERM_KEY_F12,
+ GRUB_TERM_KEY_HOME, GRUB_TERM_KEY_END };
+ unsigned i;
+
+ if (c == GRUB_TERM_ESC)
+ {
+ CONTINUE_READ;
+
+ if (c == 'O')
+ {
+ CONTINUE_READ;
+
+ for (i = 0; i < ARRAY_SIZE (fx_key); i++)
+ if (fx_key[i] == c)
+ {
+ keys[0] = fx_code[i];
+ *len = 1;
+ return;
+ }
+ }
+
+ if (c != '[')
+ return;
+ }
+
+ CONTINUE_READ;
+
+ for (i = 0; i < ARRAY_SIZE (three_code_table); i++)
+ if (three_code_table[i].key == c)
+ {
+ keys[0] = three_code_table[i].ascii;
+ *len = 1;
+ return;
+ }
+
+ switch (c)
+ {
+ case '[':
+ CONTINUE_READ;
+ if (c >= 'A' && c <= 'E')
+ {
+ keys[0] = GRUB_TERM_KEY_F1 + c - 'A';
+ *len = 1;
+ return;
+ }
+ return;
+ case 'O':
+ CONTINUE_READ;
+ for (i = 0; i < ARRAY_SIZE (fx_key); i++)
+ if (fx_key[i] == c)
+ {
+ keys[0] = fx_code[i];
+ *len = 1;
+ return;
+ }
+ return;
+
+ case '0':
+ {
+ int num = 0;
+ CONTINUE_READ;
+ if (c != '0' && c != '1')
+ return;
+ num = (c - '0') * 10;
+ CONTINUE_READ;
+ if (c < '0' || c > '9')
+ return;
+ num += (c - '0');
+ if (num == 0 || num > 12)
+ return;
+ CONTINUE_READ;
+ if (c != 'q')
+ return;
+ keys[0] = fx_code[num - 1];
+ *len = 1;
+ return;
+ }
+
+ case '1' ... '9':
+ {
+ unsigned val = c - '0';
+ CONTINUE_READ;
+ if (c >= '0' && c <= '9')
+ {
+ val = val * 10 + (c - '0');
+ CONTINUE_READ;
+ }
+ if (c != '~')
+ return;
+ if (val >= ARRAY_SIZE (four_code_table)
+ || four_code_table[val] == 0)
+ return;
+ keys[0] = four_code_table[val];
+ *len = 1;
+ return;
+ }
+ default:
+ return;
+ }
+ }
+#undef CONTINUE_READ
+}
+
+/* The terminfo version of getkey. */
+int
+grub_terminfo_getkey (struct grub_term_input *termi)
+{
+ struct grub_terminfo_input_state *data
+ = (struct grub_terminfo_input_state *) (termi->data);
+ if (data->npending)
+ {
+ int ret;
+ data->npending--;
+ ret = data->input_buf[0];
+ grub_memmove (data->input_buf, data->input_buf + 1, data->npending
+ * sizeof (data->input_buf[0]));
+ return ret;
+ }
+
+ grub_terminfo_readkey (termi, data->input_buf, &data->npending,
+ GRUB_TERMINFO_READKEY_MAX_LEN, data->readkey);
+
+#if defined(__powerpc__) && defined(GRUB_MACHINE_IEEE1275)
+ if (data->npending == 1 && data->input_buf[0] == GRUB_TERM_ESC
+ && grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_BROKEN_REPEAT)
+ && grub_get_time_ms () - data->last_key_time < 1000
+ && (data->last_key & GRUB_TERM_EXTENDED))
+ {
+ data->npending = 0;
+ data->last_key_time = grub_get_time_ms ();
+ return data->last_key;
+ }
+#endif
+
+ if (data->npending)
+ {
+ int ret;
+ data->npending--;
+ ret = data->input_buf[0];
+#if defined(__powerpc__) && defined(GRUB_MACHINE_IEEE1275)
+ if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_BROKEN_REPEAT))
+ {
+ data->last_key = ret;
+ data->last_key_time = grub_get_time_ms ();
+ }
+#endif
+ grub_memmove (data->input_buf, data->input_buf + 1, data->npending
+ * sizeof (data->input_buf[0]));
+ return ret;
+ }
+
+ return GRUB_TERM_NO_KEY;
+}
+
+grub_err_t
+grub_terminfo_input_init (struct grub_term_input *termi)
+{
+ struct grub_terminfo_input_state *data
+ = (struct grub_terminfo_input_state *) (termi->data);
+ data->npending = 0;
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_terminfo_output_init (struct grub_term_output *term)
+{
+ grub_terminfo_cls (term);
+ return GRUB_ERR_NONE;
+}
+
+/* GRUB Command. */
+
+static grub_err_t
+print_terminfo (void)
+{
+ const char *encoding_names[(GRUB_TERM_CODE_TYPE_MASK
+ >> GRUB_TERM_CODE_TYPE_SHIFT) + 1]
+ = {
+ /* VGA and glyph descriptor types are just for completeness,
+ they are not used on terminfo terminals.
+ */
+ [GRUB_TERM_CODE_TYPE_ASCII >> GRUB_TERM_CODE_TYPE_SHIFT] = _("ASCII"),
+ [GRUB_TERM_CODE_TYPE_CP437 >> GRUB_TERM_CODE_TYPE_SHIFT] = "CP-437",
+ [GRUB_TERM_CODE_TYPE_UTF8_LOGICAL >> GRUB_TERM_CODE_TYPE_SHIFT]
+ = _("UTF-8"),
+ [GRUB_TERM_CODE_TYPE_UTF8_VISUAL >> GRUB_TERM_CODE_TYPE_SHIFT]
+ /* TRANSLATORS: visually ordered UTF-8 is a non-compliant encoding
+ based on UTF-8 with right-to-left languages written in reverse.
+ Used on some terminals. Normal UTF-8 is refered as
+ "logically-ordered UTF-8" by opposition. */
+ = _("visually-ordered UTF-8"),
+ [GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS >> GRUB_TERM_CODE_TYPE_SHIFT]
+ = "Glyph descriptors",
+ _("Unknown encoding"), _("Unknown encoding"), _("Unknown encoding")
+ };
+ struct grub_term_output *cur;
+
+ grub_puts_ (N_("Current terminfo types:"));
+ for (cur = terminfo_outputs; cur;
+ cur = ((struct grub_terminfo_output_state *) cur->data)->next)
+ grub_printf ("%s: %s\t%s\t%dx%d\n", cur->name,
+ grub_terminfo_get_current(cur),
+ encoding_names[(cur->flags & GRUB_TERM_CODE_TYPE_MASK)
+ >> GRUB_TERM_CODE_TYPE_SHIFT],
+ ((struct grub_terminfo_output_state *) cur->data)->pos.x,
+ ((struct grub_terminfo_output_state *) cur->data)->pos.y);
+
+ return GRUB_ERR_NONE;
+}
+
+static const struct grub_arg_option options[] =
+{
+ {"ascii", 'a', 0, N_("Terminal is ASCII-only [default]."), 0, ARG_TYPE_NONE},
+ {"utf8", 'u', 0, N_("Terminal is logical-ordered UTF-8."), 0, ARG_TYPE_NONE},
+ {"visual-utf8", 'v', 0, N_("Terminal is visually-ordered UTF-8."), 0,
+ ARG_TYPE_NONE},
+ {"geometry", 'g', 0, N_("Terminal has specified geometry."),
+ /* TRANSLATORS: "x" has to be entered in, like an identifier, so please don't
+ use better Unicode codepoints. */
+ N_("WIDTHxHEIGHT."), ARG_TYPE_STRING},
+ {0, 0, 0, 0, 0, 0}
+};
+
+enum
+ {
+ OPTION_ASCII,
+ OPTION_UTF8,
+ OPTION_VISUAL_UTF8,
+ OPTION_GEOMETRY
+ };
+
+static grub_err_t
+grub_cmd_terminfo (grub_extcmd_context_t ctxt, int argc, char **args)
+{
+ struct grub_term_output *cur;
+ int encoding = GRUB_TERM_CODE_TYPE_ASCII;
+ struct grub_arg_list *state = ctxt->state;
+ int w = 0, h = 0;
+
+ if (argc == 0)
+ return print_terminfo ();
+
+ if (state[OPTION_ASCII].set)
+ encoding = GRUB_TERM_CODE_TYPE_ASCII;
+
+ if (state[OPTION_UTF8].set)
+ encoding = GRUB_TERM_CODE_TYPE_UTF8_LOGICAL;
+
+ if (state[OPTION_VISUAL_UTF8].set)
+ encoding = GRUB_TERM_CODE_TYPE_UTF8_VISUAL;
+
+ if (state[OPTION_GEOMETRY].set)
+ {
+ const char *ptr = state[OPTION_GEOMETRY].arg;
+ w = grub_strtoul (ptr, &ptr, 0);
+ if (grub_errno)
+ return grub_errno;
+ if (*ptr != 'x')
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ N_("incorrect terminal dimensions specification"));
+ ptr++;
+ h = grub_strtoul (ptr, &ptr, 0);
+ if (grub_errno)
+ return grub_errno;
+ }
+
+ for (cur = terminfo_outputs; cur;
+ cur = ((struct grub_terminfo_output_state *) cur->data)->next)
+ if (grub_strcmp (args[0], cur->name) == 0
+ || (grub_strcmp (args[0], "ofconsole") == 0
+ && grub_strcmp ("console", cur->name) == 0))
+ {
+ cur->flags = (cur->flags & ~GRUB_TERM_CODE_TYPE_MASK) | encoding;
+
+ if (w && h)
+ {
+ struct grub_terminfo_output_state *data
+ = (struct grub_terminfo_output_state *) cur->data;
+ data->size.x = w;
+ data->size.y = h;
+ }
+
+ if (argc == 1)
+ return GRUB_ERR_NONE;
+
+ return grub_terminfo_set_current (cur, args[1]);
+ }
+
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ N_("terminal %s isn't found or it's not handled by terminfo"),
+ args[0]);
+}
+
+static grub_extcmd_t cmd;
+
+GRUB_MOD_INIT(terminfo)
+{
+ cmd = grub_register_extcmd ("terminfo", grub_cmd_terminfo, 0,
+ N_("[[-a|-u|-v] [-g WxH] [TERM] [TYPE]]"),
+ N_("Set terminfo type of TERM to TYPE.\n"),
+ options);
+}
+
+GRUB_MOD_FINI(terminfo)
+{
+ grub_unregister_extcmd (cmd);
+}
diff --git a/grub-core/term/tparm.c b/grub-core/term/tparm.c
new file mode 100644
index 0000000..fb5b15a
--- /dev/null
+++ b/grub-core/term/tparm.c
@@ -0,0 +1,767 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1998-2003,2004,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 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/>.
+ */
+
+/**********************************************************************
+ * 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 <t.bubeck@reinform.de> 2002
+ *
+ * Resync with ncurses-5.4 by Omniflux <omniflux+devel@omniflux.com> 2005
+ **********************************************************************/
+
+/****************************************************************************
+ * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
+ * and: Eric S. Raymond <esr@snark.thyrsus.com> *
+ * 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$")
+
+/*
+ * 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));
+ /* FIX ME! handle out_buff == 0. */
+ }
+}
+
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+
+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_snprintf(out_buff + out_used, s_len + 1, 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_snprintf(out_buff + out_used, len + 1, fmt, number);
+ out_used += grub_strlen(out_buff + out_used);
+}
+
+#pragma GCC diagnostic error "-Wformat-nonliteral"
+
+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[] = "";
+
+ *popcount = 0;
+
+ 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);
+
+ 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();
+ /* GRUB has no signed divisions. */
+ npush(y ? ((unsigned)x / (unsigned)y) : 0);
+ break;
+
+ case 'm':
+ y = npop();
+ x = npop();
+ /* GRUB has no signed divisions. */
+ npush(y ? ((unsigned)x % (unsigned)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);
+}
+
+const char *
+grub_terminfo_tparm (const char *string, ...)
+{
+ va_list ap;
+ char *result;
+
+ if (!string)
+ return "";
+
+ va_start (ap, string);
+ result = tparam_internal (string, ap);
+ va_end (ap);
+ return result;
+}
diff --git a/grub-core/term/uboot/console.c b/grub-core/term/uboot/console.c
new file mode 100644
index 0000000..dfdbe99
--- /dev/null
+++ b/grub-core/term/uboot/console.c
@@ -0,0 +1,132 @@
+/* console.c - console interface layer for U-Boot platforms */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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/term.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/terminfo.h>
+#include <grub/uboot/uboot.h>
+#include <grub/uboot/console.h>
+
+static void
+put (struct grub_term_output *term __attribute__ ((unused)), const int c)
+{
+ grub_uboot_putc (c);
+}
+
+static int
+readkey (struct grub_term_input *term __attribute__ ((unused)))
+{
+ if (grub_uboot_tstc () > 0)
+ return grub_uboot_getc ();
+
+ return -1;
+}
+
+static void
+uboot_console_setcursor (struct grub_term_output *term
+ __attribute__ ((unused)), int on
+ __attribute__ ((unused)))
+{
+ grub_terminfo_setcursor (term, on);
+}
+
+static grub_err_t
+uboot_console_init_input (struct grub_term_input *term)
+{
+ return grub_terminfo_input_init (term);
+}
+
+extern struct grub_terminfo_output_state uboot_console_terminfo_output;
+
+
+static grub_err_t
+uboot_console_init_output (struct grub_term_output *term)
+{
+ grub_terminfo_output_init (term);
+
+ return 0;
+}
+
+struct grub_terminfo_input_state uboot_console_terminfo_input = {
+ .readkey = readkey
+};
+
+struct grub_terminfo_output_state uboot_console_terminfo_output = {
+ .put = put,
+ /* FIXME: In rare cases when console isn't serial,
+ determine real width. */
+ .size = { 80, 24 }
+};
+
+static struct grub_term_input uboot_console_term_input = {
+ .name = "console",
+ .init = uboot_console_init_input,
+ .getkey = grub_terminfo_getkey,
+ .data = &uboot_console_terminfo_input
+};
+
+static struct grub_term_output uboot_console_term_output = {
+ .name = "console",
+ .init = uboot_console_init_output,
+ .putchar = grub_terminfo_putchar,
+ .getwh = grub_terminfo_getwh,
+ .getxy = grub_terminfo_getxy,
+ .gotoxy = grub_terminfo_gotoxy,
+ .cls = grub_terminfo_cls,
+ .setcolorstate = grub_terminfo_setcolorstate,
+ .setcursor = uboot_console_setcursor,
+ .flags = GRUB_TERM_CODE_TYPE_ASCII,
+ .data = &uboot_console_terminfo_output,
+ .progress_update_divisor = GRUB_PROGRESS_FAST
+};
+
+void
+grub_console_init_early (void)
+{
+ grub_term_register_input ("console", &uboot_console_term_input);
+ grub_term_register_output ("console", &uboot_console_term_output);
+}
+
+
+/*
+ * grub_console_init_lately():
+ * Initializes terminfo formatting by registering terminal type.
+ * Called after heap has been configured.
+ *
+ */
+void
+grub_console_init_lately (void)
+{
+ const char *type;
+
+ /* See if explicitly set by U-Boot environment */
+ type = grub_uboot_env_get ("grub_term");
+ if (!type)
+ type = "vt100";
+
+ grub_terminfo_init ();
+ grub_terminfo_output_register (&uboot_console_term_output, type);
+}
+
+void
+grub_console_fini (void)
+{
+}
diff --git a/grub-core/term/usb_keyboard.c b/grub-core/term/usb_keyboard.c
new file mode 100644
index 0000000..e67b8f7
--- /dev/null
+++ b/grub-core/term/usb_keyboard.c
@@ -0,0 +1,471 @@
+/* Support for the HID Boot Protocol. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/term.h>
+#include <grub/time.h>
+#include <grub/misc.h>
+#include <grub/term.h>
+#include <grub/usb.h>
+#include <grub/dl.h>
+#include <grub/time.h>
+#include <grub/keyboard_layouts.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+
+
+enum
+ {
+ KEY_NO_KEY = 0x00,
+ KEY_ERR_BUFFER = 0x01,
+ KEY_ERR_POST = 0x02,
+ KEY_ERR_UNDEF = 0x03,
+ KEY_CAPS_LOCK = 0x39,
+ KEY_NUM_LOCK = 0x53,
+ };
+
+enum
+ {
+ LED_NUM_LOCK = 0x01,
+ LED_CAPS_LOCK = 0x02
+ };
+
+/* Valid values for bRequest. See HID definition version 1.11 section 7.2. */
+#define USB_HID_GET_REPORT 0x01
+#define USB_HID_GET_IDLE 0x02
+#define USB_HID_GET_PROTOCOL 0x03
+#define USB_HID_SET_REPORT 0x09
+#define USB_HID_SET_IDLE 0x0A
+#define USB_HID_SET_PROTOCOL 0x0B
+
+#define USB_HID_BOOT_SUBCLASS 0x01
+#define USB_HID_KBD_PROTOCOL 0x01
+
+#define GRUB_USB_KEYBOARD_LEFT_CTRL 0x01
+#define GRUB_USB_KEYBOARD_LEFT_SHIFT 0x02
+#define GRUB_USB_KEYBOARD_LEFT_ALT 0x04
+#define GRUB_USB_KEYBOARD_RIGHT_CTRL 0x10
+#define GRUB_USB_KEYBOARD_RIGHT_SHIFT 0x20
+#define GRUB_USB_KEYBOARD_RIGHT_ALT 0x40
+
+struct grub_usb_keyboard_data
+{
+ grub_usb_device_t usbdev;
+ grub_uint8_t status;
+ grub_uint16_t mods;
+ int interfno;
+ struct grub_usb_desc_endp *endp;
+ grub_usb_transfer_t transfer;
+ grub_uint8_t report[8];
+ int dead;
+ int last_key;
+ grub_uint64_t repeat_time;
+ grub_uint8_t current_report[8];
+ grub_uint8_t last_report[8];
+ int index;
+ int max_index;
+};
+
+static int grub_usb_keyboard_getkey (struct grub_term_input *term);
+static int grub_usb_keyboard_getkeystatus (struct grub_term_input *term);
+
+static struct grub_term_input grub_usb_keyboard_term =
+ {
+ .getkey = grub_usb_keyboard_getkey,
+ .getkeystatus = grub_usb_keyboard_getkeystatus,
+ .next = 0
+ };
+
+static struct grub_term_input grub_usb_keyboards[16];
+
+static int
+interpret_status (grub_uint8_t data0)
+{
+ int mods = 0;
+
+ /* Check Shift, Control, and Alt status. */
+ if (data0 & GRUB_USB_KEYBOARD_LEFT_SHIFT)
+ mods |= GRUB_TERM_STATUS_LSHIFT;
+ if (data0 & GRUB_USB_KEYBOARD_RIGHT_SHIFT)
+ mods |= GRUB_TERM_STATUS_RSHIFT;
+ if (data0 & GRUB_USB_KEYBOARD_LEFT_CTRL)
+ mods |= GRUB_TERM_STATUS_LCTRL;
+ if (data0 & GRUB_USB_KEYBOARD_RIGHT_CTRL)
+ mods |= GRUB_TERM_STATUS_RCTRL;
+ if (data0 & GRUB_USB_KEYBOARD_LEFT_ALT)
+ mods |= GRUB_TERM_STATUS_LALT;
+ if (data0 & GRUB_USB_KEYBOARD_RIGHT_ALT)
+ mods |= GRUB_TERM_STATUS_RALT;
+
+ return mods;
+}
+
+static void
+grub_usb_keyboard_detach (grub_usb_device_t usbdev,
+ int config __attribute__ ((unused)),
+ int interface __attribute__ ((unused)))
+{
+ unsigned i;
+ for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++)
+ {
+ struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data;
+
+ if (!data)
+ continue;
+
+ if (data->usbdev != usbdev)
+ continue;
+
+ if (data->transfer)
+ grub_usb_cancel_transfer (data->transfer);
+
+ grub_term_unregister_input (&grub_usb_keyboards[i]);
+ grub_free ((char *) grub_usb_keyboards[i].name);
+ grub_usb_keyboards[i].name = NULL;
+ grub_free (grub_usb_keyboards[i].data);
+ grub_usb_keyboards[i].data = 0;
+ }
+}
+
+static int
+grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno)
+{
+ unsigned curnum;
+ struct grub_usb_keyboard_data *data;
+ struct grub_usb_desc_endp *endp = NULL;
+ int j;
+
+ grub_dprintf ("usb_keyboard", "%x %x %x %d %d\n",
+ usbdev->descdev.class, usbdev->descdev.subclass,
+ usbdev->descdev.protocol, configno, interfno);
+
+ for (curnum = 0; curnum < ARRAY_SIZE (grub_usb_keyboards); curnum++)
+ if (!grub_usb_keyboards[curnum].data)
+ break;
+
+ if (curnum == ARRAY_SIZE (grub_usb_keyboards))
+ return 0;
+
+ if (usbdev->descdev.class != 0
+ || usbdev->descdev.subclass != 0 || usbdev->descdev.protocol != 0)
+ return 0;
+
+ if (usbdev->config[configno].interf[interfno].descif->subclass
+ != USB_HID_BOOT_SUBCLASS
+ || usbdev->config[configno].interf[interfno].descif->protocol
+ != USB_HID_KBD_PROTOCOL)
+ return 0;
+
+ for (j = 0; j < usbdev->config[configno].interf[interfno].descif->endpointcnt;
+ j++)
+ {
+ endp = &usbdev->config[configno].interf[interfno].descendp[j];
+
+ if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp)
+ == GRUB_USB_EP_INTERRUPT)
+ break;
+ }
+ if (j == usbdev->config[configno].interf[interfno].descif->endpointcnt)
+ return 0;
+
+ grub_dprintf ("usb_keyboard", "HID found!\n");
+
+ data = grub_malloc (sizeof (*data));
+ if (!data)
+ {
+ grub_print_error ();
+ return 0;
+ }
+
+ data->usbdev = usbdev;
+ data->interfno = interfno;
+ data->endp = endp;
+
+ /* Configure device */
+ grub_usb_set_configuration (usbdev, configno + 1);
+
+ /* Place the device in boot mode. */
+ grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
+ USB_HID_SET_PROTOCOL, 0, interfno, 0, 0);
+
+ /* Reports every time an event occurs and not more often than that. */
+ grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
+ USB_HID_SET_IDLE, 0<<8, interfno, 0, 0);
+
+ grub_memcpy (&grub_usb_keyboards[curnum], &grub_usb_keyboard_term,
+ sizeof (grub_usb_keyboards[curnum]));
+ grub_usb_keyboards[curnum].data = data;
+ usbdev->config[configno].interf[interfno].detach_hook
+ = grub_usb_keyboard_detach;
+ grub_usb_keyboards[curnum].name = grub_xasprintf ("usb_keyboard%d", curnum);
+ if (!grub_usb_keyboards[curnum].name)
+ {
+ grub_print_error ();
+ return 0;
+ }
+
+ /* Test showed that getting report may make the keyboard go nuts.
+ Moreover since we're reattaching keyboard it usually sends
+ an initial message on interrupt pipe and so we retrieve
+ the same keystatus.
+ */
+#if 0
+ {
+ grub_uint8_t report[8];
+ grub_usb_err_t err;
+ grub_memset (report, 0, sizeof (report));
+ err = grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_IN,
+ USB_HID_GET_REPORT, 0x0100, interfno,
+ sizeof (report), (char *) report);
+ if (err)
+ data->status = 0;
+ else
+ data->status = report[0];
+ }
+#else
+ data->status = 0;
+#endif
+
+ data->transfer = grub_usb_bulk_read_background (usbdev,
+ data->endp,
+ sizeof (data->report),
+ (char *) data->report);
+ if (!data->transfer)
+ {
+ grub_print_error ();
+ return 0;
+ }
+
+ data->last_key = -1;
+ data->mods = 0;
+ data->dead = 0;
+
+ grub_term_register_input_active ("usb_keyboard", &grub_usb_keyboards[curnum]);
+
+ return 1;
+}
+
+
+
+static void
+send_leds (struct grub_usb_keyboard_data *termdata)
+{
+ char report[1];
+ report[0] = 0;
+ if (termdata->mods & GRUB_TERM_STATUS_CAPS)
+ report[0] |= LED_CAPS_LOCK;
+ if (termdata->mods & GRUB_TERM_STATUS_NUM)
+ report[0] |= LED_NUM_LOCK;
+ grub_usb_control_msg (termdata->usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
+ USB_HID_SET_REPORT, 0x0200, termdata->interfno,
+ sizeof (report), (char *) report);
+ grub_errno = GRUB_ERR_NONE;
+}
+
+static int
+parse_keycode (struct grub_usb_keyboard_data *termdata)
+{
+ int index = termdata->index;
+ int i, keycode;
+
+ /* Sanity check */
+ if (index < 2)
+ index = 2;
+
+ for ( ; index < termdata->max_index; index++)
+ {
+ keycode = termdata->current_report[index];
+
+ if (keycode == KEY_NO_KEY
+ || keycode == KEY_ERR_BUFFER
+ || keycode == KEY_ERR_POST
+ || keycode == KEY_ERR_UNDEF)
+ {
+ /* Don't parse (rest of) this report */
+ termdata->index = 0;
+ if (keycode != KEY_NO_KEY)
+ /* Don't replace last report with current faulty report
+ * in future ! */
+ grub_memcpy (termdata->current_report,
+ termdata->last_report,
+ sizeof (termdata->report));
+ return GRUB_TERM_NO_KEY;
+ }
+
+ /* Try to find current keycode in last report. */
+ for (i = 2; i < 8; i++)
+ if (keycode == termdata->last_report[i])
+ break;
+ if (i < 8)
+ /* Keycode is in last report, it means it was not released,
+ * ignore it. */
+ continue;
+
+ if (keycode == KEY_CAPS_LOCK)
+ {
+ termdata->mods ^= GRUB_TERM_STATUS_CAPS;
+ send_leds (termdata);
+ continue;
+ }
+
+ if (keycode == KEY_NUM_LOCK)
+ {
+ termdata->mods ^= GRUB_TERM_STATUS_NUM;
+ send_leds (termdata);
+ continue;
+ }
+
+ termdata->last_key = grub_term_map_key (keycode,
+ interpret_status (termdata->current_report[0])
+ | termdata->mods);
+ termdata->repeat_time = grub_get_time_ms () + GRUB_TERM_REPEAT_PRE_INTERVAL;
+
+ grub_errno = GRUB_ERR_NONE;
+
+ index++;
+ if (index >= termdata->max_index)
+ termdata->index = 0;
+ else
+ termdata->index = index;
+
+ return termdata->last_key;
+ }
+
+ /* All keycodes parsed */
+ termdata->index = 0;
+ return GRUB_TERM_NO_KEY;
+}
+
+static int
+grub_usb_keyboard_getkey (struct grub_term_input *term)
+{
+ grub_usb_err_t err;
+ struct grub_usb_keyboard_data *termdata = term->data;
+ grub_size_t actual;
+ int keycode = GRUB_TERM_NO_KEY;
+
+ if (termdata->dead)
+ return GRUB_TERM_NO_KEY;
+
+ if (termdata->index)
+ keycode = parse_keycode (termdata);
+ if (keycode != GRUB_TERM_NO_KEY)
+ return keycode;
+
+ /* Poll interrupt pipe. */
+ err = grub_usb_check_transfer (termdata->transfer, &actual);
+
+ if (err == GRUB_USB_ERR_WAIT)
+ {
+ if (termdata->last_key != -1
+ && grub_get_time_ms () > termdata->repeat_time)
+ {
+ termdata->repeat_time = grub_get_time_ms ()
+ + GRUB_TERM_REPEAT_INTERVAL;
+ return termdata->last_key;
+ }
+ return GRUB_TERM_NO_KEY;
+ }
+
+ if (!err && (actual >= 3))
+ grub_memcpy (termdata->last_report,
+ termdata->current_report,
+ sizeof (termdata->report));
+
+ grub_memcpy (termdata->current_report,
+ termdata->report,
+ sizeof (termdata->report));
+
+ termdata->transfer = grub_usb_bulk_read_background (termdata->usbdev,
+ termdata->endp,
+ sizeof (termdata->report),
+ (char *) termdata->report);
+ if (!termdata->transfer)
+ {
+ grub_printf ("%s failed. Stopped\n", term->name);
+ termdata->dead = 1;
+ }
+
+ termdata->last_key = -1;
+
+ grub_dprintf ("usb_keyboard",
+ "err = %d, actual = %" PRIuGRUB_SIZE
+ " report: 0x%02x 0x%02x 0x%02x 0x%02x"
+ " 0x%02x 0x%02x 0x%02x 0x%02x\n",
+ err, actual,
+ termdata->current_report[0], termdata->current_report[1],
+ termdata->current_report[2], termdata->current_report[3],
+ termdata->current_report[4], termdata->current_report[5],
+ termdata->current_report[6], termdata->current_report[7]);
+
+ if (err || actual < 1)
+ return GRUB_TERM_NO_KEY;
+
+ termdata->status = termdata->current_report[0];
+
+ if (actual < 3)
+ return GRUB_TERM_NO_KEY;
+
+ termdata->index = 2; /* New data received. */
+ termdata->max_index = actual;
+
+ return parse_keycode (termdata);
+}
+
+static int
+grub_usb_keyboard_getkeystatus (struct grub_term_input *term)
+{
+ struct grub_usb_keyboard_data *termdata = term->data;
+
+ return interpret_status (termdata->status) | termdata->mods;
+}
+
+static struct grub_usb_attach_desc attach_hook =
+{
+ .class = GRUB_USB_CLASS_HID,
+ .hook = grub_usb_keyboard_attach
+};
+
+GRUB_MOD_INIT(usb_keyboard)
+{
+ grub_usb_register_attach_hook_class (&attach_hook);
+}
+
+GRUB_MOD_FINI(usb_keyboard)
+{
+ unsigned i;
+ for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++)
+ if (grub_usb_keyboards[i].data)
+ {
+ struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data;
+
+ if (!data)
+ continue;
+
+ if (data->transfer)
+ grub_usb_cancel_transfer (data->transfer);
+
+ grub_term_unregister_input (&grub_usb_keyboards[i]);
+ grub_free ((char *) grub_usb_keyboards[i].name);
+ grub_usb_keyboards[i].name = NULL;
+ grub_free (grub_usb_keyboards[i].data);
+ grub_usb_keyboards[i].data = 0;
+ }
+ grub_usb_unregister_attach_hook_class (&attach_hook);
+}
diff --git a/grub-core/term/xen/console.c b/grub-core/term/xen/console.c
new file mode 100644
index 0000000..a1f15f7
--- /dev/null
+++ b/grub-core/term/xen/console.c
@@ -0,0 +1,122 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2011 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/term.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/time.h>
+#include <grub/terminfo.h>
+#include <grub/xen.h>
+
+static int
+readkey (struct grub_term_input *term __attribute__ ((unused)))
+{
+ grub_size_t prod, cons;
+ int r;
+ mb ();
+ prod = grub_xen_xcons->in_prod;
+ cons = grub_xen_xcons->in_cons;
+ if (prod <= cons)
+ return -1;
+ r = grub_xen_xcons->in[cons];
+ cons++;
+ mb ();
+ grub_xen_xcons->in_cons = cons;
+ return r;
+}
+
+static int signal_sent = 1;
+
+static void
+refresh (struct grub_term_output *term __attribute__ ((unused)))
+{
+ struct evtchn_send send;
+ send.port = grub_xen_start_page_addr->console.domU.evtchn;
+ grub_xen_event_channel_op (EVTCHNOP_send, &send);
+ signal_sent = 1;
+ while (grub_xen_xcons->out_prod != grub_xen_xcons->out_cons)
+ {
+ grub_xen_sched_op (SCHEDOP_yield, 0);
+ }
+}
+
+static void
+put (struct grub_term_output *term __attribute__ ((unused)), const int c)
+{
+ grub_size_t prod, cons;
+
+ while (1)
+ {
+ mb ();
+ prod = grub_xen_xcons->out_prod;
+ cons = grub_xen_xcons->out_cons;
+ if (prod < cons + sizeof (grub_xen_xcons->out))
+ break;
+ if (!signal_sent)
+ refresh (term);
+ grub_xen_sched_op (SCHEDOP_yield, 0);
+ }
+ grub_xen_xcons->out[prod++ & (sizeof (grub_xen_xcons->out) - 1)] = c;
+ mb ();
+ grub_xen_xcons->out_prod = prod;
+ signal_sent = 0;
+}
+
+
+struct grub_terminfo_input_state grub_console_terminfo_input = {
+ .readkey = readkey
+};
+
+struct grub_terminfo_output_state grub_console_terminfo_output = {
+ .put = put,
+ .size = {80, 24}
+};
+
+static struct grub_term_input grub_console_term_input = {
+ .name = "console",
+ .init = 0,
+ .getkey = grub_terminfo_getkey,
+ .data = &grub_console_terminfo_input
+};
+
+static struct grub_term_output grub_console_term_output = {
+ .name = "console",
+ .init = 0,
+ .putchar = grub_terminfo_putchar,
+ .getxy = grub_terminfo_getxy,
+ .getwh = grub_terminfo_getwh,
+ .gotoxy = grub_terminfo_gotoxy,
+ .cls = grub_terminfo_cls,
+ .refresh = refresh,
+ .setcolorstate = grub_terminfo_setcolorstate,
+ .setcursor = grub_terminfo_setcursor,
+ .flags = GRUB_TERM_CODE_TYPE_ASCII,
+ .data = &grub_console_terminfo_output,
+};
+
+
+void
+grub_console_init (void)
+{
+ grub_term_register_input ("console", &grub_console_term_input);
+ grub_term_register_output ("console", &grub_console_term_output);
+
+ grub_terminfo_init ();
+ grub_terminfo_output_register (&grub_console_term_output, "vt100-color");
+}