automake commit without merge history

This commit is contained in:
BVK Chaitanya 2010-05-06 11:34:04 +05:30
parent 265d68cd10
commit 8c41176882
810 changed files with 4980 additions and 2508 deletions

View file

@ -0,0 +1,303 @@
/*
* 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>
static short at_keyboard_status = 0;
static int pending_key = -1;
#define KEYBOARD_STATUS_SHIFT_L (1 << 0)
#define KEYBOARD_STATUS_SHIFT_R (1 << 1)
#define KEYBOARD_STATUS_ALT_L (1 << 2)
#define KEYBOARD_STATUS_ALT_R (1 << 3)
#define KEYBOARD_STATUS_CTRL_L (1 << 4)
#define KEYBOARD_STATUS_CTRL_R (1 << 5)
#define KEYBOARD_STATUS_CAPS_LOCK (1 << 6)
#define KEYBOARD_STATUS_NUM_LOCK (1 << 7)
static grub_uint8_t led_status;
#define KEYBOARD_LED_SCROLL (1 << 0)
#define KEYBOARD_LED_NUM (1 << 1)
#define KEYBOARD_LED_CAPS (1 << 2)
static char keyboard_map[128] =
{
'\0', GRUB_TERM_ESC, '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '-', '=', GRUB_TERM_BACKSPACE, GRUB_TERM_TAB,
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '[', ']', '\n', '\0', 'a', 's',
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
'\'', '`', '\0', '\\', 'z', 'x', 'c', 'v',
'b', 'n', 'm', ',', '.', '/', '\0', '*',
'\0', ' ', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', GRUB_TERM_HOME,
GRUB_TERM_UP, GRUB_TERM_NPAGE, '-', GRUB_TERM_LEFT, '\0', GRUB_TERM_RIGHT, '+', GRUB_TERM_END,
GRUB_TERM_DOWN, GRUB_TERM_PPAGE, '\0', GRUB_TERM_DC, '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', OLPC_UP, OLPC_DOWN, OLPC_LEFT,
OLPC_RIGHT
};
static char keyboard_map_shift[128] =
{
'\0', '\0', '!', '@', '#', '$', '%', '^',
'&', '*', '(', ')', '_', '+', '\0', '\0',
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
'O', 'P', '{', '}', '\n', '\0', 'A', 'S',
'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
'\"', '~', '\0', '|', 'Z', 'X', 'C', 'V',
'B', 'N', 'M', '<', '>', '?'
};
static grub_uint8_t grub_keyboard_controller_orig;
static void
keyboard_controller_wait_until_ready (void)
{
while (! KEYBOARD_COMMAND_ISREADY (grub_inb (KEYBOARD_REG_STATUS)));
}
static void
grub_keyboard_controller_write (grub_uint8_t c)
{
keyboard_controller_wait_until_ready ();
grub_outb (KEYBOARD_COMMAND_WRITE, KEYBOARD_REG_STATUS);
grub_outb (c, KEYBOARD_REG_DATA);
}
static grub_uint8_t
grub_keyboard_controller_read (void)
{
keyboard_controller_wait_until_ready ();
grub_outb (KEYBOARD_COMMAND_READ, KEYBOARD_REG_STATUS);
return grub_inb (KEYBOARD_REG_DATA);
}
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);
}
/* FIXME: This should become an interrupt service routine. For now
it's just used to catch events from control keys. */
static void
grub_keyboard_isr (char key)
{
char is_make = KEYBOARD_ISMAKE (key);
key = KEYBOARD_SCANCODE (key);
if (is_make)
switch (key)
{
case SHIFT_L:
at_keyboard_status |= KEYBOARD_STATUS_SHIFT_L;
break;
case SHIFT_R:
at_keyboard_status |= KEYBOARD_STATUS_SHIFT_R;
break;
case CTRL:
at_keyboard_status |= KEYBOARD_STATUS_CTRL_L;
break;
case ALT:
at_keyboard_status |= KEYBOARD_STATUS_ALT_L;
break;
default:
/* Skip grub_dprintf. */
return;
}
else
switch (key)
{
case SHIFT_L:
at_keyboard_status &= ~KEYBOARD_STATUS_SHIFT_L;
break;
case SHIFT_R:
at_keyboard_status &= ~KEYBOARD_STATUS_SHIFT_R;
break;
case CTRL:
at_keyboard_status &= ~KEYBOARD_STATUS_CTRL_L;
break;
case ALT:
at_keyboard_status &= ~KEYBOARD_STATUS_ALT_L;
break;
default:
/* Skip grub_dprintf. */
return;
}
#ifdef DEBUG_AT_KEYBOARD
grub_dprintf ("atkeyb", "Control key 0x%0x was %s\n", key, is_make ? "pressed" : "unpressed");
#endif
}
/* If there is a raw key pending, return it; otherwise return -1. */
static int
grub_keyboard_getkey (void)
{
grub_uint8_t key;
if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
return -1;
key = grub_inb (KEYBOARD_REG_DATA);
/* FIXME */ grub_keyboard_isr (key);
if (! KEYBOARD_ISMAKE (key))
return -1;
return (KEYBOARD_SCANCODE (key));
}
/* If there is a character pending, return it; otherwise return -1. */
static int
grub_at_keyboard_getkey_noblock (void)
{
int code, key;
code = grub_keyboard_getkey ();
if (code == -1)
return -1;
#ifdef DEBUG_AT_KEYBOARD
grub_dprintf ("atkeyb", "Detected key 0x%x\n", key);
#endif
switch (code)
{
case CAPS_LOCK:
/* Caps lock sends scan code twice. Get the second one and discard it. */
while (grub_keyboard_getkey () == -1);
at_keyboard_status ^= KEYBOARD_STATUS_CAPS_LOCK;
led_status ^= KEYBOARD_LED_CAPS;
keyboard_controller_led (led_status);
#ifdef DEBUG_AT_KEYBOARD
grub_dprintf ("atkeyb", "caps_lock = %d\n", !!(at_keyboard_status & KEYBOARD_STATUS_CAPS_LOCK));
#endif
key = -1;
break;
case NUM_LOCK:
/* Num lock sends scan code twice. Get the second one and discard it. */
while (grub_keyboard_getkey () == -1);
at_keyboard_status ^= KEYBOARD_STATUS_NUM_LOCK;
led_status ^= KEYBOARD_LED_NUM;
keyboard_controller_led (led_status);
#ifdef DEBUG_AT_KEYBOARD
grub_dprintf ("atkeyb", "num_lock = %d\n", !!(at_keyboard_status & KEYBOARD_STATUS_NUM_LOCK));
#endif
key = -1;
break;
case SCROLL_LOCK:
/* For scroll lock we don't keep track of status. Only update its led. */
led_status ^= KEYBOARD_LED_SCROLL;
keyboard_controller_led (led_status);
key = -1;
break;
default:
if (at_keyboard_status & (KEYBOARD_STATUS_CTRL_L | KEYBOARD_STATUS_CTRL_R))
key = keyboard_map[code] - 'a' + 1;
else if ((at_keyboard_status & (KEYBOARD_STATUS_SHIFT_L | KEYBOARD_STATUS_SHIFT_R))
&& keyboard_map_shift[code])
key = keyboard_map_shift[code];
else
key = keyboard_map[code];
if (key == 0)
grub_dprintf ("atkeyb", "Unknown key 0x%x detected\n", code);
if (at_keyboard_status & KEYBOARD_STATUS_CAPS_LOCK)
{
if ((key >= 'a') && (key <= 'z'))
key += 'A' - 'a';
else if ((key >= 'A') && (key <= 'Z'))
key += 'a' - 'A';
}
}
return key;
}
static int
grub_at_keyboard_checkkey (void)
{
if (pending_key != -1)
return 1;
pending_key = grub_at_keyboard_getkey_noblock ();
if (pending_key != -1)
return 1;
return -1;
}
static int
grub_at_keyboard_getkey (void)
{
int key;
if (pending_key != -1)
{
key = pending_key;
pending_key = -1;
return key;
}
do
{
key = grub_at_keyboard_getkey_noblock ();
} while (key == -1);
return key;
}
static grub_err_t
grub_keyboard_controller_init (void)
{
pending_key = -1;
at_keyboard_status = 0;
grub_keyboard_controller_orig = grub_keyboard_controller_read ();
grub_keyboard_controller_write (grub_keyboard_controller_orig | KEYBOARD_SCANCODE_SET1);
return GRUB_ERR_NONE;
}
static grub_err_t
grub_keyboard_controller_fini (void)
{
grub_keyboard_controller_write (grub_keyboard_controller_orig);
return GRUB_ERR_NONE;
}
static struct grub_term_input grub_at_keyboard_term =
{
.name = "at_keyboard",
.init = grub_keyboard_controller_init,
.fini = grub_keyboard_controller_fini,
.checkkey = grub_at_keyboard_checkkey,
.getkey = grub_at_keyboard_getkey,
};
GRUB_MOD_INIT(at_keyboard)
{
grub_term_register_input ("at_keyboard", &grub_at_keyboard_term);
}
GRUB_MOD_FINI(at_keyboard)
{
grub_term_unregister_input (&grub_at_keyboard_term);
}

View file

@ -0,0 +1,380 @@
/*
* 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>
static grub_uint8_t
grub_console_standard_color = GRUB_EFI_TEXT_ATTR (GRUB_EFI_YELLOW,
GRUB_EFI_BACKGROUND_BLACK);
static grub_uint8_t
grub_console_normal_color = GRUB_EFI_TEXT_ATTR (GRUB_EFI_LIGHTGRAY,
GRUB_EFI_BACKGROUND_BLACK);
static grub_uint8_t
grub_console_highlight_color = GRUB_EFI_TEXT_ATTR (GRUB_EFI_BLACK,
GRUB_EFI_BACKGROUND_LIGHTGRAY);
static int read_key = -1;
static grub_uint32_t
map_char (grub_uint32_t c)
{
if (c > 0x7f)
{
/* Map some unicode characters to the EFI character. */
switch (c)
{
case 0x2190: /* left arrow */
c = 0x25c4;
break;
case 0x2191: /* up arrow */
c = 0x25b2;
break;
case 0x2192: /* right arrow */
c = 0x25ba;
break;
case 0x2193: /* down arrow */
c = 0x25bc;
break;
case 0x2501: /* horizontal line */
c = 0x2500;
break;
case 0x2503: /* vertical line */
c = 0x2502;
break;
case 0x250F: /* upper-left corner */
c = 0x250c;
break;
case 0x2513: /* upper-right corner */
c = 0x2510;
break;
case 0x2517: /* lower-left corner */
c = 0x2514;
break;
case 0x251B: /* lower-right corner */
c = 0x2518;
break;
default:
c = '?';
break;
}
}
return c;
}
static void
grub_console_putchar (grub_uint32_t c)
{
grub_efi_char16_t str[2];
grub_efi_simple_text_output_interface_t *o;
o = grub_efi_system_table->con_out;
/* For now, do not try to use a surrogate pair. */
if (c > 0xffff)
c = '?';
str[0] = (grub_efi_char16_t) map_char (c & 0xffff);
str[1] = 0;
/* Should this test be cached? */
if (c > 0x7f && efi_call_2 (o->test_string, o, str) != GRUB_EFI_SUCCESS)
return;
efi_call_2 (o->output_string, o, str);
}
static grub_ssize_t
grub_console_getcharwidth (grub_uint32_t c __attribute__ ((unused)))
{
/* For now, every printable character has the width 1. */
return 1;
}
static int
grub_console_checkkey (void)
{
grub_efi_simple_input_interface_t *i;
grub_efi_input_key_t key;
grub_efi_status_t status;
if (read_key >= 0)
return 1;
i = grub_efi_system_table->con_in;
status = efi_call_2 (i->read_key_stroke, i, &key);
#if 0
switch (status)
{
case GRUB_EFI_SUCCESS:
{
grub_uint16_t xy;
xy = grub_getxy ();
grub_gotoxy (0, 0);
grub_printf ("scan_code=%x,unicode_char=%x ",
(unsigned) key.scan_code,
(unsigned) key.unicode_char);
grub_gotoxy (xy >> 8, xy & 0xff);
}
break;
case GRUB_EFI_NOT_READY:
//grub_printf ("not ready ");
break;
default:
//grub_printf ("device error ");
break;
}
#endif
if (status == GRUB_EFI_SUCCESS)
{
switch (key.scan_code)
{
case 0x00:
read_key = key.unicode_char;
break;
case 0x01:
read_key = GRUB_TERM_UP;
break;
case 0x02:
read_key = GRUB_TERM_DOWN;
break;
case 0x03:
read_key = GRUB_TERM_RIGHT;
break;
case 0x04:
read_key = GRUB_TERM_LEFT;
break;
case 0x05:
read_key = GRUB_TERM_HOME;
break;
case 0x06:
read_key = GRUB_TERM_END;
break;
case 0x07:
break;
case 0x08:
read_key = GRUB_TERM_DC;
break;
case 0x09:
break;
case 0x0a:
break;
case 0x0b:
read_key = 24;
break;
case 0x0c:
read_key = 1;
break;
case 0x0d:
read_key = 5;
break;
case 0x0e:
read_key = 3;
break;
case 0x17:
read_key = '\e';
break;
default:
break;
}
}
return read_key;
}
static int
grub_console_getkey (void)
{
grub_efi_simple_input_interface_t *i;
grub_efi_boot_services_t *b;
grub_efi_uintn_t index;
grub_efi_status_t status;
int key;
if (read_key >= 0)
{
key = read_key;
read_key = -1;
return key;
}
i = grub_efi_system_table->con_in;
b = grub_efi_system_table->boot_services;
do
{
status = efi_call_3 (b->wait_for_event, 1, &(i->wait_for_key), &index);
if (status != GRUB_EFI_SUCCESS)
return -1;
grub_console_checkkey ();
}
while (read_key < 0);
key = read_key;
read_key = -1;
return key;
}
static grub_uint16_t
grub_console_getwh (void)
{
grub_efi_simple_text_output_interface_t *o;
grub_efi_uintn_t columns, rows;
o = grub_efi_system_table->con_out;
if (efi_call_4 (o->query_mode, o, o->mode->mode, &columns, &rows) != GRUB_EFI_SUCCESS)
{
/* Why does this fail? */
columns = 80;
rows = 25;
}
return ((columns << 8) | rows);
}
static grub_uint16_t
grub_console_getxy (void)
{
grub_efi_simple_text_output_interface_t *o;
o = grub_efi_system_table->con_out;
return ((o->mode->cursor_column << 8) | o->mode->cursor_row);
}
static void
grub_console_gotoxy (grub_uint8_t x, grub_uint8_t y)
{
grub_efi_simple_text_output_interface_t *o;
o = grub_efi_system_table->con_out;
efi_call_3 (o->set_cursor_position, o, x, y);
}
static void
grub_console_cls (void)
{
grub_efi_simple_text_output_interface_t *o;
grub_efi_int32_t orig_attr;
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 void
grub_console_setcolorstate (grub_term_color_state state)
{
grub_efi_simple_text_output_interface_t *o;
o = grub_efi_system_table->con_out;
switch (state) {
case GRUB_TERM_COLOR_STANDARD:
efi_call_2 (o->set_attributes, o, grub_console_standard_color);
break;
case GRUB_TERM_COLOR_NORMAL:
efi_call_2 (o->set_attributes, o, grub_console_normal_color);
break;
case GRUB_TERM_COLOR_HIGHLIGHT:
efi_call_2 (o->set_attributes, o, grub_console_highlight_color);
break;
default:
break;
}
}
static void
grub_console_setcolor (grub_uint8_t normal_color, grub_uint8_t highlight_color)
{
grub_console_normal_color = normal_color;
grub_console_highlight_color = highlight_color;
}
static void
grub_console_getcolor (grub_uint8_t *normal_color, grub_uint8_t *highlight_color)
{
*normal_color = grub_console_normal_color;
*highlight_color = grub_console_highlight_color;
}
static void
grub_console_setcursor (int on)
{
grub_efi_simple_text_output_interface_t *o;
o = grub_efi_system_table->con_out;
efi_call_2 (o->enable_cursor, o, on);
}
static struct grub_term_input grub_console_term_input =
{
.name = "console",
.checkkey = grub_console_checkkey,
.getkey = grub_console_getkey,
};
static struct grub_term_output grub_console_term_output =
{
.name = "console",
.putchar = grub_console_putchar,
.getcharwidth = grub_console_getcharwidth,
.getwh = grub_console_getwh,
.getxy = grub_console_getxy,
.gotoxy = grub_console_gotoxy,
.cls = grub_console_cls,
.setcolorstate = grub_console_setcolorstate,
.setcolor = grub_console_setcolor,
.getcolor = grub_console_getcolor,
.setcursor = grub_console_setcursor
};
void
grub_console_init (void)
{
/* FIXME: it is necessary to consider the case where no console control
is present but the default is already in text mode. */
if (! grub_efi_set_text_mode (1))
{
grub_error (GRUB_ERR_BAD_DEVICE, "cannot set text mode");
return;
}
grub_term_register_input ("console", &grub_console_term_input);
grub_term_register_output ("console", &grub_console_term_output);
}
void
grub_console_fini (void)
{
grub_term_unregister_input (&grub_console_term_input);
grub_term_unregister_output (&grub_console_term_output);
}

1200
grub-core/term/gfxterm.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,83 @@
/*
* 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>
static const struct grub_machine_bios_data_area *bios_data_area =
(struct grub_machine_bios_data_area *) GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR;
#define KEYBOARD_LEFT_SHIFT (1 << 0)
#define KEYBOARD_RIGHT_SHIFT (1 << 1)
#define KEYBOARD_CTRL (1 << 2)
#define KEYBOARD_ALT (1 << 3)
static int
grub_console_getkeystatus (void)
{
grub_uint8_t status = bios_data_area->keyboard_flag_lower;
int mods = 0;
if (status & (KEYBOARD_LEFT_SHIFT | KEYBOARD_RIGHT_SHIFT))
mods |= GRUB_TERM_STATUS_SHIFT;
if (status & KEYBOARD_CTRL)
mods |= GRUB_TERM_STATUS_CTRL;
if (status & KEYBOARD_ALT)
mods |= GRUB_TERM_STATUS_ALT;
return mods;
}
static struct grub_term_input grub_console_term_input =
{
.name = "console",
.checkkey = grub_console_checkkey,
.getkey = grub_console_getkey,
.getkeystatus = grub_console_getkeystatus,
};
static struct grub_term_output grub_console_term_output =
{
.name = "console",
.putchar = grub_console_putchar,
.getcharwidth = grub_console_getcharwidth,
.getwh = grub_console_getwh,
.getxy = grub_console_getxy,
.gotoxy = grub_console_gotoxy,
.cls = grub_console_cls,
.setcolorstate = grub_console_setcolorstate,
.setcolor = grub_console_setcolor,
.getcolor = grub_console_getcolor,
.setcursor = grub_console_setcursor
};
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);
}

View file

@ -0,0 +1,513 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
// TODO: Deprecated and broken. Needs to be converted to Video Driver!
#include <grub/machine/vga.h>
#include <grub/machine/console.h>
#include <grub/cpu/io.h>
#include <grub/term.h>
#include <grub/types.h>
#include <grub/dl.h>
#include <grub/misc.h>
#include <grub/font.h>
#define DEBUG_VGA 0
#define VGA_WIDTH 640
#define VGA_HEIGHT 350
#define CHAR_WIDTH 8
#define CHAR_HEIGHT 16
#define TEXT_WIDTH (VGA_WIDTH / CHAR_WIDTH)
#define TEXT_HEIGHT (VGA_HEIGHT / CHAR_HEIGHT)
#define VGA_MEM ((grub_uint8_t *) GRUB_MEMORY_MACHINE_VGA_ADDR)
#define PAGE_OFFSET(x) ((x) * (VGA_WIDTH * VGA_HEIGHT / 8))
#define DEFAULT_FG_COLOR 0xa
#define DEFAULT_BG_COLOR 0x0
struct colored_char
{
/* An Unicode codepoint. */
grub_uint32_t code;
/* Color indexes. */
unsigned char fg_color;
unsigned char bg_color;
/* The width of this character minus one. */
unsigned char width;
/* The column index of this character. */
unsigned char index;
};
static unsigned char text_mode;
static unsigned xpos, ypos;
static int cursor_state;
static unsigned char fg_color, bg_color;
static struct colored_char text_buf[TEXT_WIDTH * TEXT_HEIGHT];
static unsigned char saved_map_mask;
static int page = 0;
static grub_font_t font = 0;
#define SEQUENCER_ADDR_PORT 0x3C4
#define SEQUENCER_DATA_PORT 0x3C5
#define MAP_MASK_REGISTER 0x02
#define CRTC_ADDR_PORT 0x3D4
#define CRTC_DATA_PORT 0x3D5
#define START_ADDR_HIGH_REGISTER 0x0C
#define START_ADDR_LOW_REGISTER 0x0D
#define GRAPHICS_ADDR_PORT 0x3CE
#define GRAPHICS_DATA_PORT 0x3CF
#define READ_MAP_REGISTER 0x04
#define INPUT_STATUS1_REGISTER 0x3DA
#define INPUT_STATUS1_VERTR_BIT 0x08
static inline void
wait_vretrace (void)
{
/* Wait until there is a vertical retrace. */
while (! (grub_inb (INPUT_STATUS1_REGISTER) & INPUT_STATUS1_VERTR_BIT));
}
/* Get Map Mask Register. */
static unsigned char
get_map_mask (void)
{
unsigned char old_addr;
unsigned char old_data;
old_addr = grub_inb (SEQUENCER_ADDR_PORT);
grub_outb (MAP_MASK_REGISTER, SEQUENCER_ADDR_PORT);
old_data = grub_inb (SEQUENCER_DATA_PORT);
grub_outb (old_addr, SEQUENCER_ADDR_PORT);
return old_data;
}
/* Set Map Mask Register. */
static void
set_map_mask (unsigned char mask)
{
unsigned char old_addr;
old_addr = grub_inb (SEQUENCER_ADDR_PORT);
grub_outb (MAP_MASK_REGISTER, SEQUENCER_ADDR_PORT);
grub_outb (mask, SEQUENCER_DATA_PORT);
grub_outb (old_addr, SEQUENCER_ADDR_PORT);
}
/* Set Read Map Register. */
static void
set_read_map (unsigned char map)
{
unsigned char old_addr;
old_addr = grub_inb (GRAPHICS_ADDR_PORT);
grub_outb (READ_MAP_REGISTER, GRAPHICS_ADDR_PORT);
grub_outb (map, GRAPHICS_DATA_PORT);
grub_outb (old_addr, GRAPHICS_ADDR_PORT);
}
/* Set start address. */
static void
set_start_address (unsigned int start)
{
unsigned char old_addr;
old_addr = grub_inb (CRTC_ADDR_PORT);
grub_outb (START_ADDR_LOW_REGISTER, CRTC_ADDR_PORT);
grub_outb (start & 0xFF, CRTC_DATA_PORT);
grub_outb (START_ADDR_HIGH_REGISTER, CRTC_ADDR_PORT);
grub_outb (start >> 8, CRTC_DATA_PORT);
grub_outb (old_addr, CRTC_ADDR_PORT);
}
static grub_err_t
grub_vga_mod_init (void)
{
text_mode = grub_vga_set_mode (0x10);
cursor_state = 1;
fg_color = DEFAULT_FG_COLOR;
bg_color = DEFAULT_BG_COLOR;
saved_map_mask = get_map_mask ();
set_map_mask (0x0f);
set_start_address (PAGE_OFFSET (page));
font = grub_font_get (""); /* Choose any font, for now. */
if (!font)
return grub_error (GRUB_ERR_BAD_FONT, "no font loaded");
return GRUB_ERR_NONE;
}
static grub_err_t
grub_vga_mod_fini (void)
{
set_map_mask (saved_map_mask);
grub_vga_set_mode (text_mode);
return GRUB_ERR_NONE;
}
static int
check_vga_mem (void *p)
{
return (p >= (void *) (VGA_MEM + PAGE_OFFSET (page))
&& p <= (void *) (VGA_MEM + PAGE_OFFSET (page)
+ VGA_WIDTH * VGA_HEIGHT / 8));
}
static void
write_char (void)
{
struct colored_char *p = text_buf + xpos + ypos * TEXT_WIDTH;
struct grub_font_glyph *glyph;
unsigned char *mem_base;
unsigned plane;
mem_base = (VGA_MEM + xpos +
ypos * CHAR_HEIGHT * TEXT_WIDTH + PAGE_OFFSET (page)) - p->index;
p -= p->index;
/* Get glyph for character. */
glyph = grub_font_get_glyph (font, p->code);
for (plane = 0x01; plane <= 0x08; plane <<= 1)
{
unsigned y;
unsigned offset;
unsigned char *mem;
set_map_mask (plane);
for (y = 0, offset = 0, mem = mem_base;
y < CHAR_HEIGHT;
y++, mem += TEXT_WIDTH)
{
/* TODO Re-implement glyph drawing for vga module. */
#if 0
unsigned i;
unsigned char_width = 1; /* TODO Figure out wide characters. */
for (i = 0; i < char_width && offset < 32; i++)
{
unsigned char fg_mask, bg_mask;
fg_mask = (p->fg_color & plane) ? glyph->bitmap[offset] : 0;
bg_mask = (p->bg_color & plane) ? ~(glyph->bitmap[offset]) : 0;
offset++;
if (check_vga_mem (mem + i))
mem[i] = (fg_mask | bg_mask);
}
#endif /* 0 */
}
}
set_map_mask (0x0f);
}
static void
write_cursor (void)
{
unsigned char *mem = (VGA_MEM + PAGE_OFFSET (page) + xpos
+ (ypos * CHAR_HEIGHT + CHAR_HEIGHT - 3) * TEXT_WIDTH);
if (check_vga_mem (mem))
*mem = 0xff;
mem += TEXT_WIDTH;
if (check_vga_mem (mem))
*mem = 0xff;
}
static void
scroll_up (void)
{
unsigned i;
unsigned plane;
/* Do all the work in the other page. */
grub_memmove (text_buf, text_buf + TEXT_WIDTH,
sizeof (struct colored_char) * TEXT_WIDTH * (TEXT_HEIGHT - 1));
for (i = TEXT_WIDTH * (TEXT_HEIGHT - 1); i < TEXT_WIDTH * TEXT_HEIGHT; i++)
{
text_buf[i].code = ' ';
text_buf[i].fg_color = 0;
text_buf[i].bg_color = 0;
text_buf[i].width = 0;
text_buf[i].index = 0;
}
for (plane = 1; plane <= 4; plane++)
{
set_read_map (plane);
set_map_mask (1 << plane);
grub_memmove (VGA_MEM + PAGE_OFFSET (1 - page), VGA_MEM
+ PAGE_OFFSET (page) + VGA_WIDTH * CHAR_HEIGHT / 8,
VGA_WIDTH * (VGA_HEIGHT - CHAR_HEIGHT) / 8);
}
set_map_mask (0x0f);
grub_memset (VGA_MEM + PAGE_OFFSET (1 - page)
+ VGA_WIDTH * (VGA_HEIGHT - CHAR_HEIGHT) / 8, 0,
VGA_WIDTH * CHAR_HEIGHT / 8);
/* Activate the other page. */
page = 1 - page;
wait_vretrace ();
set_start_address (PAGE_OFFSET (page));
}
static void
grub_vga_putchar (grub_uint32_t c)
{
#if DEBUG_VGA
static int show = 1;
#endif
if (c == '\a')
/* FIXME */
return;
if (c == '\b' || c == '\n' || c == '\r')
{
/* Erase current cursor, if any. */
if (cursor_state)
write_char ();
switch (c)
{
case '\b':
if (xpos > 0)
xpos--;
break;
case '\n':
if (ypos >= TEXT_HEIGHT - 1)
scroll_up ();
else
ypos++;
break;
case '\r':
xpos = 0;
break;
}
if (cursor_state)
write_cursor ();
}
else
{
struct grub_font_glyph *glyph;
struct colored_char *p;
unsigned char_width = 1;
glyph = grub_font_get_glyph(font, c);
if (xpos + char_width > TEXT_WIDTH)
grub_putchar ('\n');
p = text_buf + xpos + ypos * TEXT_WIDTH;
p->code = c;
p->fg_color = fg_color;
p->bg_color = bg_color;
p->width = char_width - 1;
p->index = 0;
if (char_width > 1)
{
unsigned i;
for (i = 1; i < char_width; i++)
{
p[i].code = ' ';
p[i].width = char_width - 1;
p[i].index = i;
}
}
write_char ();
xpos += char_width;
if (xpos >= TEXT_WIDTH)
{
xpos = 0;
if (ypos >= TEXT_HEIGHT - 1)
scroll_up ();
else
ypos++;
}
if (cursor_state)
write_cursor ();
}
#if DEBUG_VGA
if (show)
{
grub_uint16_t pos = grub_getxy ();
show = 0;
grub_gotoxy (0, 0);
grub_printf ("[%u:%u]", (unsigned) (pos >> 8), (unsigned) (pos & 0xff));
grub_gotoxy (pos >> 8, pos & 0xff);
show = 1;
}
#endif
}
static grub_ssize_t
grub_vga_getcharwidth (grub_uint32_t c)
{
#if 0
struct grub_font_glyph glyph;
glyph = grub_font_get_glyph (c);
return glyph.char_width;
#else
(void) c; /* Prevent warning. */
return 1; /* TODO Fix wide characters? */
#endif
}
static grub_uint16_t
grub_vga_getwh (void)
{
return (TEXT_WIDTH << 8) | TEXT_HEIGHT;
}
static grub_uint16_t
grub_vga_getxy (void)
{
return ((xpos << 8) | ypos);
}
static void
grub_vga_gotoxy (grub_uint8_t x, grub_uint8_t y)
{
if (x >= TEXT_WIDTH || y >= TEXT_HEIGHT)
{
grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid point (%u,%u)",
(unsigned) x, (unsigned) y);
return;
}
if (cursor_state)
write_char ();
xpos = x;
ypos = y;
if (cursor_state)
write_cursor ();
}
static void
grub_vga_cls (void)
{
unsigned i;
wait_vretrace ();
for (i = 0; i < TEXT_WIDTH * TEXT_HEIGHT; i++)
{
text_buf[i].code = ' ';
text_buf[i].fg_color = 0;
text_buf[i].bg_color = 0;
text_buf[i].width = 0;
text_buf[i].index = 0;
}
grub_memset (VGA_MEM + PAGE_OFFSET (page), 0, VGA_WIDTH * VGA_HEIGHT / 8);
xpos = ypos = 0;
}
static void
grub_vga_setcolorstate (grub_term_color_state state)
{
switch (state)
{
case GRUB_TERM_COLOR_STANDARD:
case GRUB_TERM_COLOR_NORMAL:
fg_color = DEFAULT_FG_COLOR;
bg_color = DEFAULT_BG_COLOR;
break;
case GRUB_TERM_COLOR_HIGHLIGHT:
fg_color = DEFAULT_BG_COLOR;
bg_color = DEFAULT_FG_COLOR;
break;
default:
break;
}
}
static void
grub_vga_setcursor (int on)
{
if (cursor_state != on)
{
if (cursor_state)
write_char ();
else
write_cursor ();
cursor_state = on;
}
}
static struct grub_term_output grub_vga_term =
{
.name = "vga",
.init = grub_vga_mod_init,
.fini = grub_vga_mod_fini,
.putchar = grub_vga_putchar,
.getcharwidth = grub_vga_getcharwidth,
.getwh = grub_vga_getwh,
.getxy = grub_vga_getxy,
.gotoxy = grub_vga_gotoxy,
.cls = grub_vga_cls,
.setcolorstate = grub_vga_setcolorstate,
.setcursor = grub_vga_setcursor,
.flags = 0,
};
GRUB_MOD_INIT(vga)
{
grub_term_register_output ("vga", &grub_vga_term);
}
GRUB_MOD_FINI(vga)
{
grub_term_unregister_output (&grub_vga_term);
}

View file

@ -0,0 +1,177 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 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/dl.h>
#include <grub/i386/vga_common.h>
#include <grub/i386/io.h>
#include <grub/types.h>
#define COLS 80
#define ROWS 25
static int grub_curr_x, grub_curr_y;
#define VGA_TEXT_SCREEN 0xb8000
#define CRTC_ADDR_PORT 0x3D4
#define CRTC_DATA_PORT 0x3D5
#define CRTC_CURSOR 0x0a
#define CRTC_CURSOR_ADDR_HIGH 0x0e
#define CRTC_CURSOR_ADDR_LOW 0x0f
#define CRTC_CURSOR_DISABLE (1 << 5)
static void
screen_write_char (int x, int y, short c)
{
((short *) VGA_TEXT_SCREEN)[y * COLS + x] = c;
}
static short
screen_read_char (int x, int y)
{
return ((short *) VGA_TEXT_SCREEN)[y * COLS + x];
}
static void
update_cursor (void)
{
unsigned int pos = grub_curr_y * COLS + grub_curr_x;
grub_outb (CRTC_CURSOR_ADDR_HIGH, CRTC_ADDR_PORT);
grub_outb (pos >> 8, CRTC_DATA_PORT);
grub_outb (CRTC_CURSOR_ADDR_LOW, CRTC_ADDR_PORT);
grub_outb (pos & 0xFF, CRTC_DATA_PORT);
}
static void
inc_y (void)
{
grub_curr_x = 0;
if (grub_curr_y < ROWS - 1)
grub_curr_y++;
else
{
int x, y;
for (y = 0; y < ROWS; y++)
for (x = 0; x < COLS; x++)
screen_write_char (x, y, screen_read_char (x, y + 1));
}
}
static void
inc_x (void)
{
if (grub_curr_x >= COLS - 1)
inc_y ();
else
grub_curr_x++;
}
void
grub_console_real_putchar (int c)
{
switch (c)
{
case '\b':
if (grub_curr_x != 0)
screen_write_char (grub_curr_x--, grub_curr_y, ' ');
break;
case '\n':
inc_y ();
break;
case '\r':
grub_curr_x = 0;
break;
default:
screen_write_char (grub_curr_x,
grub_curr_y, c | (grub_console_cur_color << 8));
inc_x ();
}
update_cursor ();
}
static grub_uint16_t
grub_vga_text_getxy (void)
{
return (grub_curr_x << 8) | grub_curr_y;
}
static void
grub_vga_text_gotoxy (grub_uint8_t x, grub_uint8_t y)
{
grub_curr_x = x;
grub_curr_y = y;
update_cursor ();
}
static void
grub_vga_text_cls (void)
{
int i;
for (i = 0; i < ROWS * COLS; i++)
((short *) VGA_TEXT_SCREEN)[i] = ' ' | (grub_console_cur_color << 8);
grub_vga_text_gotoxy (0, 0);
}
static void
grub_vga_text_setcursor (int on)
{
grub_uint8_t old;
grub_outb (CRTC_CURSOR, CRTC_ADDR_PORT);
old = grub_inb (CRTC_DATA_PORT);
if (on)
grub_outb (old & ~CRTC_CURSOR_DISABLE, CRTC_DATA_PORT);
else
grub_outb (old | CRTC_CURSOR_DISABLE, CRTC_DATA_PORT);
}
static grub_err_t
grub_vga_text_init_fini (void)
{
grub_vga_text_cls ();
return 0;
}
static struct grub_term_output grub_vga_text_term =
{
.name = "vga_text",
.init = grub_vga_text_init_fini,
.fini = grub_vga_text_init_fini,
.putchar = grub_console_putchar,
.getcharwidth = grub_console_getcharwidth,
.getwh = grub_console_getwh,
.getxy = grub_vga_text_getxy,
.gotoxy = grub_vga_text_gotoxy,
.cls = grub_vga_text_cls,
.setcolorstate = grub_console_setcolorstate,
.setcolor = grub_console_setcolor,
.getcolor = grub_console_getcolor,
.setcursor = grub_vga_text_setcursor,
};
GRUB_MOD_INIT(vga_text)
{
grub_term_register_output ("vga_text", &grub_vga_text_term);
}
GRUB_MOD_FINI(vga_text)
{
grub_term_unregister_output (&grub_vga_text_term);
}

View file

@ -0,0 +1,125 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2002,2003,2005,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/i386/vga_common.h>
#include <grub/term.h>
#include <grub/types.h>
grub_uint8_t grub_console_cur_color = 0x7;
static grub_uint8_t grub_console_standard_color = 0x7;
static grub_uint8_t grub_console_normal_color = 0x7;
static grub_uint8_t grub_console_highlight_color = 0x70;
static grub_uint32_t
map_char (grub_uint32_t c)
{
if (c > 0x7f)
{
/* Map some unicode characters to the VGA font, if possible. */
switch (c)
{
case 0x2190: /* left arrow */
c = 0x1b;
break;
case 0x2191: /* up arrow */
c = 0x18;
break;
case 0x2192: /* right arrow */
c = 0x1a;
break;
case 0x2193: /* down arrow */
c = 0x19;
break;
case 0x2501: /* horizontal line */
c = 0xc4;
break;
case 0x2503: /* vertical line */
c = 0xb3;
break;
case 0x250F: /* upper-left corner */
c = 0xda;
break;
case 0x2513: /* upper-right corner */
c = 0xbf;
break;
case 0x2517: /* lower-left corner */
c = 0xc0;
break;
case 0x251B: /* lower-right corner */
c = 0xd9;
break;
default:
c = '?';
break;
}
}
return c;
}
void
grub_console_putchar (grub_uint32_t c)
{
grub_console_real_putchar (map_char (c));
}
grub_ssize_t
grub_console_getcharwidth (grub_uint32_t c __attribute__ ((unused)))
{
/* For now, every printable character has the width 1. */
return 1;
}
grub_uint16_t
grub_console_getwh (void)
{
return (80 << 8) | 25;
}
void
grub_console_setcolorstate (grub_term_color_state state)
{
switch (state) {
case GRUB_TERM_COLOR_STANDARD:
grub_console_cur_color = grub_console_standard_color;
break;
case GRUB_TERM_COLOR_NORMAL:
grub_console_cur_color = grub_console_normal_color;
break;
case GRUB_TERM_COLOR_HIGHLIGHT:
grub_console_cur_color = grub_console_highlight_color;
break;
default:
break;
}
}
void
grub_console_setcolor (grub_uint8_t normal_color, grub_uint8_t highlight_color)
{
grub_console_normal_color = normal_color;
grub_console_highlight_color = highlight_color;
}
void
grub_console_getcolor (grub_uint8_t *normal_color, grub_uint8_t *highlight_color)
{
*normal_color = grub_console_normal_color;
*highlight_color = grub_console_highlight_color;
}

View file

@ -0,0 +1,515 @@
/* ofconsole.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/machine/console.h>
#include <grub/ieee1275/ieee1275.h>
static grub_ieee1275_ihandle_t stdout_ihandle;
static grub_ieee1275_ihandle_t stdin_ihandle;
static grub_uint8_t grub_ofconsole_width;
static grub_uint8_t grub_ofconsole_height;
static int grub_curr_x;
static int grub_curr_y;
static int grub_keybuf;
static int grub_buflen;
struct color
{
int red;
int green;
int blue;
};
static struct color colors[] =
{
// {R, G, B}
{0x00, 0x00, 0x00},
{0x00, 0x00, 0xA8}, // 1 = blue
{0x00, 0xA8, 0x00}, // 2 = green
{0x00, 0xA8, 0xA8}, // 3 = cyan
{0xA8, 0x00, 0x00}, // 4 = red
{0xA8, 0x00, 0xA8}, // 5 = magenta
{0xFE, 0xFE, 0x54}, // 6 = yellow
{0xFE, 0xFE, 0xFE} // 7 = white
};
static grub_uint8_t grub_ofconsole_normal_color = 0x7;
static grub_uint8_t grub_ofconsole_highlight_color = 0x70;
/* Write control characters to the console. */
static void
grub_ofconsole_writeesc (const char *str)
{
if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_ANSI))
return;
while (*str)
{
char chr = *(str++);
grub_ieee1275_write (stdout_ihandle, &chr, 1, 0);
}
}
static void
grub_ofconsole_putchar (grub_uint32_t c)
{
char chr;
if (c > 0x7F)
{
/* Better than nothing. */
switch (c)
{
case GRUB_TERM_DISP_LEFT:
c = '<';
break;
case GRUB_TERM_DISP_UP:
c = '^';
break;
case GRUB_TERM_DISP_RIGHT:
c = '>';
break;
case GRUB_TERM_DISP_DOWN:
c = 'v';
break;
case GRUB_TERM_DISP_HLINE:
c = '-';
break;
case GRUB_TERM_DISP_VLINE:
c = '|';
break;
case GRUB_TERM_DISP_UL:
case GRUB_TERM_DISP_UR:
case GRUB_TERM_DISP_LL:
case GRUB_TERM_DISP_LR:
c = '+';
break;
default:
c = '?';
break;
}
}
chr = c;
if (c == '\n')
{
grub_curr_y++;
grub_curr_x = 0;
}
else if (c == '\r')
{
grub_curr_x = 0;
}
else
{
grub_curr_x++;
if (grub_curr_x >= grub_ofconsole_width)
{
grub_ofconsole_putchar ('\n');
grub_ofconsole_putchar ('\r');
grub_curr_x++;
}
}
grub_ieee1275_write (stdout_ihandle, &chr, 1, 0);
}
static grub_ssize_t
grub_ofconsole_getcharwidth (grub_uint32_t c __attribute__((unused)))
{
return 1;
}
static void
grub_ofconsole_setcolorstate (grub_term_color_state state)
{
char setcol[256];
int fg;
int bg;
switch (state)
{
case GRUB_TERM_COLOR_STANDARD:
case GRUB_TERM_COLOR_NORMAL:
fg = grub_ofconsole_normal_color & 0x0f;
bg = grub_ofconsole_normal_color >> 4;
break;
case GRUB_TERM_COLOR_HIGHLIGHT:
fg = grub_ofconsole_highlight_color & 0x0f;
bg = grub_ofconsole_highlight_color >> 4;
break;
default:
return;
}
grub_snprintf (setcol, sizeof (setcol), "\e[3%dm\e[4%dm", fg, bg);
grub_ofconsole_writeesc (setcol);
}
static void
grub_ofconsole_setcolor (grub_uint8_t normal_color,
grub_uint8_t highlight_color)
{
/* Discard bright bit. */
grub_ofconsole_normal_color = normal_color & 0x77;
grub_ofconsole_highlight_color = highlight_color & 0x77;
}
static void
grub_ofconsole_getcolor (grub_uint8_t *normal_color, grub_uint8_t *highlight_color)
{
*normal_color = grub_ofconsole_normal_color;
*highlight_color = grub_ofconsole_highlight_color;
}
static int
grub_ofconsole_readkey (int *key)
{
char c;
grub_ssize_t actual = 0;
grub_ieee1275_read (stdin_ihandle, &c, 1, &actual);
if (actual > 0)
switch(c)
{
case 0x7f:
/* Backspace: Ctrl-h. */
c = '\b';
break;
case '\e':
{
grub_uint64_t start;
grub_ieee1275_read (stdin_ihandle, &c, 1, &actual);
/* On 9600 we have to wait up to 12 milliseconds. */
start = grub_get_time_ms ();
while (actual <= 0 && grub_get_time_ms () - start < 12)
grub_ieee1275_read (stdin_ihandle, &c, 1, &actual);
if (actual <= 0)
{
*key = '\e';
return 1;
}
if (c != '[')
return 0;
grub_ieee1275_read (stdin_ihandle, &c, 1, &actual);
/* On 9600 we have to wait up to 12 milliseconds. */
start = grub_get_time_ms ();
while (actual <= 0 && grub_get_time_ms () - start < 12)
grub_ieee1275_read (stdin_ihandle, &c, 1, &actual);
if (actual <= 0)
return 0;
switch (c)
{
case 'A':
/* Up: Ctrl-p. */
c = GRUB_TERM_UP;
break;
case 'B':
/* Down: Ctrl-n. */
c = GRUB_TERM_DOWN;
break;
case 'C':
/* Right: Ctrl-f. */
c = GRUB_TERM_RIGHT;
break;
case 'D':
/* Left: Ctrl-b. */
c = GRUB_TERM_LEFT;
break;
case '3':
{
grub_ieee1275_read (stdin_ihandle, &c, 1, &actual);
/* On 9600 we have to wait up to 12 milliseconds. */
start = grub_get_time_ms ();
while (actual <= 0 && grub_get_time_ms () - start < 12)
grub_ieee1275_read (stdin_ihandle, &c, 1, &actual);
if (actual <= 0)
return 0;
/* Delete: Ctrl-d. */
if (c == '~')
c = GRUB_TERM_DC;
else
return 0;
break;
}
break;
}
}
}
*key = c;
return actual > 0;
}
static int
grub_ofconsole_checkkey (void)
{
int key;
int read;
if (grub_buflen)
return 1;
read = grub_ofconsole_readkey (&key);
if (read)
{
grub_keybuf = key;
grub_buflen = 1;
return 1;
}
return -1;
}
static int
grub_ofconsole_getkey (void)
{
int key;
if (grub_buflen)
{
grub_buflen =0;
return grub_keybuf;
}
while (! grub_ofconsole_readkey (&key));
return key;
}
static grub_uint16_t
grub_ofconsole_getxy (void)
{
return ((grub_curr_x - 1) << 8) | grub_curr_y;
}
static void
grub_ofconsole_dimensions (void)
{
grub_ieee1275_ihandle_t options;
grub_ssize_t lval;
if (! grub_ieee1275_finddevice ("/options", &options)
&& options != (grub_ieee1275_ihandle_t) -1)
{
if (! grub_ieee1275_get_property_length (options, "screen-#columns",
&lval)
&& lval >= 0 && lval < 1024)
{
char val[lval];
if (! grub_ieee1275_get_property (options, "screen-#columns",
val, lval, 0))
grub_ofconsole_width = (grub_uint8_t) grub_strtoul (val, 0, 10);
}
if (! grub_ieee1275_get_property_length (options, "screen-#rows", &lval)
&& lval >= 0 && lval < 1024)
{
char val[lval];
if (! grub_ieee1275_get_property (options, "screen-#rows",
val, lval, 0))
grub_ofconsole_height = (grub_uint8_t) grub_strtoul (val, 0, 10);
}
}
/* Use a small console by default. */
if (! grub_ofconsole_width)
grub_ofconsole_width = 80;
if (! grub_ofconsole_height)
grub_ofconsole_height = 24;
}
static grub_uint16_t
grub_ofconsole_getwh (void)
{
return (grub_ofconsole_width << 8) | grub_ofconsole_height;
}
static void
grub_ofconsole_gotoxy (grub_uint8_t x, grub_uint8_t y)
{
if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_ANSI))
{
char s[256];
grub_curr_x = x;
grub_curr_y = y;
grub_snprintf (s, sizeof (s), "\e[%d;%dH", y + 1, x + 1);
grub_ofconsole_writeesc (s);
}
else
{
if ((y == grub_curr_y) && (x == grub_curr_x - 1))
{
char chr;
chr = '\b';
grub_ieee1275_write (stdout_ihandle, &chr, 1, 0);
}
grub_curr_x = x;
grub_curr_y = y;
}
}
static void
grub_ofconsole_cls (void)
{
/* 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. */
grub_ofconsole_writeesc (" \e[2J");
grub_ofconsole_gotoxy (0, 0);
}
static void
grub_ofconsole_setcursor (int on)
{
/* Understood by the Open Firmware flavour in OLPC. */
if (on)
grub_ieee1275_interpret ("cursor-on", 0);
else
grub_ieee1275_interpret ("cursor-off", 0);
}
static void
grub_ofconsole_refresh (void)
{
/* Do nothing, the current console state is ok. */
}
static grub_err_t
grub_ofconsole_init_input (void)
{
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 0;
}
static grub_err_t
grub_ofconsole_init_output (void)
{
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_ofconsole_setcolorstate (GRUB_TERM_COLOR_NORMAL);
}
grub_ofconsole_dimensions ();
return 0;
}
static grub_err_t
grub_ofconsole_fini (void)
{
return 0;
}
static struct grub_term_input grub_ofconsole_term_input =
{
.name = "ofconsole",
.init = grub_ofconsole_init_input,
.fini = grub_ofconsole_fini,
.checkkey = grub_ofconsole_checkkey,
.getkey = grub_ofconsole_getkey,
};
static struct grub_term_output grub_ofconsole_term_output =
{
.name = "ofconsole",
.init = grub_ofconsole_init_output,
.fini = grub_ofconsole_fini,
.putchar = grub_ofconsole_putchar,
.getcharwidth = grub_ofconsole_getcharwidth,
.getxy = grub_ofconsole_getxy,
.getwh = grub_ofconsole_getwh,
.gotoxy = grub_ofconsole_gotoxy,
.cls = grub_ofconsole_cls,
.setcolorstate = grub_ofconsole_setcolorstate,
.setcolor = grub_ofconsole_setcolor,
.getcolor = grub_ofconsole_getcolor,
.setcursor = grub_ofconsole_setcursor,
.refresh = grub_ofconsole_refresh
};
void
grub_console_init (void)
{
grub_term_register_input ("ofconsole", &grub_ofconsole_term_input);
grub_term_register_output ("ofconsole", &grub_ofconsole_term_output);
}
void
grub_console_fini (void)
{
grub_term_unregister_input (&grub_ofconsole_term_input);
grub_term_unregister_output (&grub_ofconsole_term_output);
}

642
grub-core/term/serial.c Normal file
View file

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

189
grub-core/term/terminfo.c Normal file
View file

@ -0,0 +1,189 @@
/* 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/command.h>
#include <grub/i18n.h>
struct terminfo
{
char *name;
char *gotoxy;
char *cls;
char *reverse_video_on;
char *reverse_video_off;
char *cursor_on;
char *cursor_off;
};
static struct terminfo term;
/* Get current terminfo name. */
char *
grub_terminfo_get_current (void)
{
return term.name;
}
/* Free *PTR and set *PTR to NULL, to prevent double-free. */
static void
grub_terminfo_free (char **ptr)
{
grub_free (*ptr);
*ptr = 0;
}
/* Set current terminfo type. */
grub_err_t
grub_terminfo_set_current (const char *str)
{
/* TODO
* Lookup user specified terminfo type. If found, set term variables
* as appropriate. Otherwise return an error.
*
* How should this be done?
* a. A static table included in this module.
* - I do not like this idea.
* b. A table stored in the configuration directory.
* - Users must convert their terminfo settings if we have not already.
* c. Look for terminfo files in the configuration directory.
* - /usr/share/terminfo is 6.3M on my system.
* - /usr/share/terminfo is not on most users boot partition.
* + Copying the terminfo files you want to use to the grub
* configuration directory is easier then (b).
* d. Your idea here.
*/
/* Free previously allocated memory. */
grub_terminfo_free (&term.name);
grub_terminfo_free (&term.gotoxy);
grub_terminfo_free (&term.cls);
grub_terminfo_free (&term.reverse_video_on);
grub_terminfo_free (&term.reverse_video_off);
grub_terminfo_free (&term.cursor_on);
grub_terminfo_free (&term.cursor_off);
if (grub_strcmp ("vt100", str) == 0)
{
term.name = grub_strdup ("vt100");
term.gotoxy = grub_strdup ("\e[%i%p1%d;%p2%dH");
term.cls = grub_strdup ("\e[H\e[J");
term.reverse_video_on = grub_strdup ("\e[7m");
term.reverse_video_off = grub_strdup ("\e[m");
term.cursor_on = grub_strdup ("\e[?25h");
term.cursor_off = grub_strdup ("\e[?25l");
return grub_errno;
}
return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown terminfo type");
}
/* Wrapper for grub_putchar to write strings. */
static void
putstr (const char *str, grub_term_output_t oterm)
{
while (*str)
grub_putcode (*str++, oterm);
}
/* Move the cursor to the given position starting with "0". */
void
grub_terminfo_gotoxy (grub_uint8_t x, grub_uint8_t y, grub_term_output_t oterm)
{
putstr (grub_terminfo_tparm (term.gotoxy, y, x), oterm);
}
/* Clear the screen. */
void
grub_terminfo_cls (grub_term_output_t oterm)
{
putstr (grub_terminfo_tparm (term.cls), oterm);
}
/* Set reverse video mode on. */
void
grub_terminfo_reverse_video_on (grub_term_output_t oterm)
{
putstr (grub_terminfo_tparm (term.reverse_video_on), oterm);
}
/* Set reverse video mode off. */
void
grub_terminfo_reverse_video_off (grub_term_output_t oterm)
{
putstr (grub_terminfo_tparm (term.reverse_video_off), oterm);
}
/* Show cursor. */
void
grub_terminfo_cursor_on (grub_term_output_t oterm)
{
putstr (grub_terminfo_tparm (term.cursor_on), oterm);
}
/* Hide cursor. */
void
grub_terminfo_cursor_off (grub_term_output_t oterm)
{
putstr (grub_terminfo_tparm (term.cursor_off), oterm);
}
/* GRUB Command. */
static grub_err_t
grub_cmd_terminfo (grub_command_t cmd __attribute__ ((unused)),
int argc, char **args)
{
if (argc == 0)
{
grub_printf ("Current terminfo type: %s\n", grub_terminfo_get_current());
return GRUB_ERR_NONE;
}
else if (argc != 1)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "too many parameters");
else
return grub_terminfo_set_current (args[0]);
}
static grub_command_t cmd;
GRUB_MOD_INIT(terminfo)
{
cmd = grub_register_command ("terminfo", grub_cmd_terminfo,
N_("[TERM]"), N_("Set terminfo type."));
grub_terminfo_set_current ("vt100");
}
GRUB_MOD_FINI(terminfo)
{
grub_unregister_command (cmd);
}

758
grub-core/term/tparm.c Normal file
View file

@ -0,0 +1,758 @@
/*
* 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. */
}
}
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);
}
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();
npush(y ? (x / y) : 0);
break;
case 'm':
y = npop();
x = npop();
npush(y ? (x % y) : 0);
break;
case 'A':
npush(npop() && npop());
break;
case 'O':
npush(npop() || npop());
break;
case '&':
npush(npop() & npop());
break;
case '|':
npush(npop() | npop());
break;
case '^':
npush(npop() ^ npop());
break;
case '=':
y = npop();
x = npop();
npush(x == y);
break;
case '<':
y = npop();
x = npop();
npush(x < y);
break;
case '>':
y = npop();
x = npop();
npush(x > y);
break;
case '!':
npush(!npop());
break;
case '~':
npush(~npop());
break;
case 'i':
if (p_is_s[0] == 0)
param[0]++;
if (p_is_s[1] == 0)
param[1]++;
break;
case '?':
break;
case 't':
x = npop();
if (!x) {
/* scan forward for %e or %; at level zero */
cp++;
level = 0;
while (*cp) {
if (*cp == '%') {
cp++;
if (*cp == '?')
level++;
else if (*cp == ';') {
if (level > 0)
level--;
else
break;
} else if (*cp == 'e' && level == 0)
break;
}
if (*cp)
cp++;
}
}
break;
case 'e':
/* scan forward for a %; at level zero */
cp++;
level = 0;
while (*cp) {
if (*cp == '%') {
cp++;
if (*cp == '?')
level++;
else if (*cp == ';') {
if (level > 0)
level--;
else
break;
}
}
if (*cp)
cp++;
}
break;
case ';':
break;
} /* endswitch (*cp) */
} /* endelse (*cp == '%') */
if (*cp == '\0')
break;
cp++;
} /* endwhile (*cp) */
get_space(1);
out_buff[out_used] = '\0';
return (out_buff);
}
char *
grub_terminfo_tparm (const char *string, ...)
{
va_list ap;
char *result;
va_start (ap, string);
result = tparam_internal (string, ap);
va_end (ap);
return result;
}

View file

@ -0,0 +1,329 @@
/* 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/machine/console.h>
#include <grub/time.h>
#include <grub/cpu/io.h>
#include <grub/misc.h>
#include <grub/term.h>
#include <grub/usb.h>
#include <grub/dl.h>
#include <grub/time.h>
static char keyboard_map[128] =
{
'\0', '\0', '\0', '\0', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
'3', '4', '5', '6', '7', '8', '9', '0',
'\n', GRUB_TERM_ESC, GRUB_TERM_BACKSPACE, GRUB_TERM_TAB, ' ', '-', '=', '[',
']', '\\', '#', ';', '\'', '`', ',', '.',
'/', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', GRUB_TERM_HOME, GRUB_TERM_PPAGE, GRUB_TERM_DC, GRUB_TERM_END, GRUB_TERM_NPAGE, GRUB_TERM_RIGHT,
GRUB_TERM_LEFT, GRUB_TERM_DOWN, GRUB_TERM_UP
};
static char keyboard_map_shift[128] =
{
'\0', '\0', '\0', '\0', 'A', 'B', 'C', 'D',
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
'#', '$', '%', '^', '&', '*', '(', ')',
'\n', '\0', '\0', '\0', ' ', '_', '+', '{',
'}', '|', '#', ':', '"', '`', '<', '>',
'?'
};
static grub_usb_device_t usbdev;
/* Valid values for bmRequestType. See HID definition version 1.11 section
7.2. */
#define USB_HID_HOST_TO_DEVICE 0x21
#define USB_HID_DEVICE_TO_HOST 0xA1
/* 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
static void
grub_usb_hid (void)
{
struct grub_usb_desc_device *descdev;
auto int usb_iterate (grub_usb_device_t dev);
int usb_iterate (grub_usb_device_t dev)
{
descdev = &dev->descdev;
grub_dprintf ("usb_keyboard", "%x %x %x\n",
descdev->class, descdev->subclass, descdev->protocol);
#if 0
if (descdev->class != 0x09
|| descdev->subclass == 0x01
|| descdev->protocol != 0x02)
return 0;
#endif
if (descdev->class != 0 || descdev->subclass != 0 || descdev->protocol != 0)
return 0;
grub_printf ("HID found!\n");
usbdev = dev;
return 1;
}
grub_usb_iterate (usb_iterate);
/* Place the device in boot mode. */
grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_PROTOCOL,
0, 0, 0, 0);
/* Reports every time an event occurs and not more often than that. */
grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE,
0<<8, 0, 0, 0);
}
static grub_err_t
grub_usb_keyboard_getreport (grub_usb_device_t dev, grub_uint8_t *report)
{
return grub_usb_control_msg (dev, USB_HID_DEVICE_TO_HOST, USB_HID_GET_REPORT,
0, 0, 8, (char *) report);
}
static int
grub_usb_keyboard_checkkey (void)
{
grub_uint8_t data[8];
int key;
grub_err_t err;
grub_uint64_t currtime;
int timeout = 50;
data[2] = 0;
currtime = grub_get_time_ms ();
do
{
/* Get_Report. */
err = grub_usb_keyboard_getreport (usbdev, data);
/* Implement a timeout. */
if (grub_get_time_ms () > currtime + timeout)
break;
}
while (err || !data[2]);
if (err || !data[2])
return -1;
grub_dprintf ("usb_keyboard",
"report: 0x%02x 0x%02x 0x%02x 0x%02x"
" 0x%02x 0x%02x 0x%02x 0x%02x\n",
data[0], data[1], data[2], data[3],
data[4], data[5], data[6], data[7]);
/* Check if the Control or Shift key was pressed. */
if (data[0] & 0x01 || data[0] & 0x10)
key = keyboard_map[data[2]] - 'a' + 1;
else if (data[0] & 0x02 || data[0] & 0x20)
key = keyboard_map_shift[data[2]];
else
key = keyboard_map[data[2]];
if (key == 0)
grub_printf ("Unknown key 0x%x detected\n", data[2]);
#if 0
/* Wait until the key is released. */
while (!err && data[2])
{
err = grub_usb_control_msg (usbdev, USB_HID_DEVICE_TO_HOST,
USB_HID_GET_REPORT, 0, 0,
sizeof (data), (char *) data);
grub_dprintf ("usb_keyboard",
"report2: 0x%02x 0x%02x 0x%02x 0x%02x"
" 0x%02x 0x%02x 0x%02x 0x%02x\n",
data[0], data[1], data[2], data[3],
data[4], data[5], data[6], data[7]);
}
#endif
grub_errno = GRUB_ERR_NONE;
return key;
}
typedef enum
{
GRUB_HIDBOOT_REPEAT_NONE,
GRUB_HIDBOOT_REPEAT_FIRST,
GRUB_HIDBOOT_REPEAT
} grub_usb_keyboard_repeat_t;
static int
grub_usb_keyboard_getkey (void)
{
int key;
grub_err_t err;
grub_uint8_t data[8];
grub_uint64_t currtime;
int timeout;
static grub_usb_keyboard_repeat_t repeat = GRUB_HIDBOOT_REPEAT_NONE;
again:
do
{
key = grub_usb_keyboard_checkkey ();
} while (key == -1);
data[2] = !0; /* Or whatever. */
err = 0;
switch (repeat)
{
case GRUB_HIDBOOT_REPEAT_FIRST:
timeout = 500;
break;
case GRUB_HIDBOOT_REPEAT:
timeout = 50;
break;
default:
timeout = 100;
break;
}
/* Wait until the key is released. */
currtime = grub_get_time_ms ();
while (!err && data[2])
{
/* Implement a timeout. */
if (grub_get_time_ms () > currtime + timeout)
{
if (repeat == 0)
repeat = 1;
else
repeat = 2;
grub_errno = GRUB_ERR_NONE;
return key;
}
err = grub_usb_keyboard_getreport (usbdev, data);
}
if (repeat)
{
repeat = 0;
goto again;
}
repeat = 0;
grub_errno = GRUB_ERR_NONE;
return key;
}
static int
grub_usb_keyboard_getkeystatus (void)
{
grub_uint8_t data[8];
int mods = 0;
grub_err_t err;
grub_uint64_t currtime;
int timeout = 50;
/* Set idle time to the minimum offered by the spec (4 milliseconds) so
that we can find out the current state. */
grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE,
0<<8, 0, 0, 0);
currtime = grub_get_time_ms ();
do
{
/* Get_Report. */
err = grub_usb_keyboard_getreport (usbdev, data);
/* Implement a timeout. */
if (grub_get_time_ms () > currtime + timeout)
break;
}
while (err || !data[0]);
/* Go back to reporting every time an event occurs and not more often than
that. */
grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE,
0<<8, 0, 0, 0);
/* We allowed a while for modifiers to show up in the report, but it is
not an error if they never did. */
if (err)
return -1;
grub_dprintf ("usb_keyboard",
"report: 0x%02x 0x%02x 0x%02x 0x%02x"
" 0x%02x 0x%02x 0x%02x 0x%02x\n",
data[0], data[1], data[2], data[3],
data[4], data[5], data[6], data[7]);
/* Check Shift, Control, and Alt status. */
if (data[0] & 0x02 || data[0] & 0x20)
mods |= GRUB_TERM_STATUS_SHIFT;
if (data[0] & 0x01 || data[0] & 0x10)
mods |= GRUB_TERM_STATUS_CTRL;
if (data[0] & 0x04 || data[0] & 0x40)
mods |= GRUB_TERM_STATUS_ALT;
grub_errno = GRUB_ERR_NONE;
return mods;
}
static struct grub_term_input grub_usb_keyboard_term =
{
.name = "usb_keyboard",
.checkkey = grub_usb_keyboard_checkkey,
.getkey = grub_usb_keyboard_getkey,
.getkeystatus = grub_usb_keyboard_getkeystatus,
.next = 0
};
GRUB_MOD_INIT(usb_keyboard)
{
grub_usb_hid ();
grub_term_register_input ("usb_keyboard", &grub_usb_keyboard_term);
}
GRUB_MOD_FINI(usb_keyboard)
{
grub_term_unregister_input (&grub_usb_keyboard_term);
}