blob: 3e8ea7f86fc624d1fd15091eb8ce8a7b0bfd0396 [file] [log] [blame]
/*
* Copyright 2016 Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "base/container_of.h"
#include "base/fwdb.h"
#include "base/init_funcs.h"
#include "base/keycodes.h"
#include "base/list.h"
#include "drivers/console/console.h"
#include "uefi/uefi.h"
#include <stdio.h>
#include <string.h>
EFI_GUID simple_text_input_ex_protocol_guid =
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
typedef struct {
Console console;
DcEvent exit_bs;
EFI_KEY_DATA last_key;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *con_out;
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *con_in;
} UefiConsole;
static void uefi_console_write(ConsoleOutputOps *me,
const void *buffer, size_t count)
{
UefiConsole *uefi = container_of(me, UefiConsole, console.output);
uint16_t output[0x100];
const uint8_t *input = buffer;
const size_t space = 0x100;
size_t out = 0;
size_t in = 0;
while (in < count) {
// If we're out of room, flush the output buffer. Never start
// converting a character with only one or two output slots
// left. If it's a newline and we need to inject a carriage
// return, and we need a place for the NULL terminator.
if (space - out < 3) {
output[out++] = '\0';
uefi->con_out->OutputString(uefi->con_out, output);
out = 0;
}
// Grab a character from the input buffer.
uint8_t in_char = input[in++];
// Stuff it into the output buffer.
output[out++] = in_char;
// If it was a newline, add a carriage return.
if (in_char == '\n')
output[out++] = '\r';
}
// Flush any leftovers from the output buffer.
if (out) {
output[out++] = '\0';
uefi->con_out->OutputString(uefi->con_out, output);
}
}
static int uefi_console_have_key(ConsoleInputOps *me)
{
UefiConsole *uefi = container_of(me, UefiConsole,
console.trusted_input);
if (!uefi->last_key.Key.ScanCode && !uefi->last_key.Key.UnicodeChar) {
EFI_KEY_DATA key;
if (uefi->con_in->ReadKeyStrokeEx(uefi->con_in, &key) !=
EFI_NOT_READY) {
memcpy(&uefi->last_key, &key, sizeof(key));
}
}
return uefi->last_key.Key.ScanCode || uefi->last_key.Key.UnicodeChar;
}
static int uefi_console_get_char(ConsoleInputOps *me)
{
UefiConsole *uefi = container_of(me, UefiConsole,
console.trusted_input);
while (!me->havekey(me))
{;}
uint16_t key = 0;
switch (uefi->last_key.Key.ScanCode) {
case 0x1:
key = KEY_UP;
break;
case 0x2:
key = KEY_DOWN;
break;
case 0x3:
key = KEY_RIGHT;
break;
case 0x4:
key = KEY_LEFT;
break;
default:
key = uefi->last_key.Key.UnicodeChar;
}
UINT32 shift_state = uefi->last_key.KeyState.KeyShiftState;
if ((shift_state & EFI_SHIFT_STATE_VALID) &&
(shift_state & (EFI_RIGHT_CONTROL_PRESSED |
EFI_LEFT_CONTROL_PRESSED))) {
if (key >= 'a' && key <= 'z')
key = key & 0x1f;
else
key = 0;
}
memset(&uefi->last_key, 0, sizeof(uefi->last_key));
return key;
}
static int uefi_console_disable(DcEvent *event)
{
UefiConsole *uefi = container_of(event, UefiConsole, exit_bs);
list_remove(&uefi->console.list_node);
return 0;
}
static int uefi_console_init(void)
{
EFI_SYSTEM_TABLE *sys = uefi_system_table_ptr();
if (!sys)
return 1;
static UefiConsole uefi = {
.console = {
.output = {
.write = &uefi_console_write
},
// There isn't a way to distinguish where input came
// from, so lets just trust everything when on UEFI.
.trusted_input = {
.havekey = &uefi_console_have_key,
.getchar = &uefi_console_get_char,
},
},
.exit_bs = {
.trigger = &uefi_console_disable
},
};
uefi.con_out = sys->ConOut;
EFI_BOOT_SERVICES *bs = sys->BootServices;
EFI_STATUS status = bs->HandleProtocol(
sys->ConsoleInHandle, &simple_text_input_ex_protocol_guid,
(void **)&uefi.con_in);
if (status != EFI_SUCCESS) {
printf("Failed to retrieve console input ex protocol.\n");
return 1;
}
list_insert_after(&uefi.console.list_node, &console_list);
uefi_add_exit_boot_services_event(&uefi.exit_bs);
return 0;
}
INIT_FUNC_CONSOLE(uefi_console_init)