From 20bfb4855b3840d0ec55a01cffc7227cb5665b7e Mon Sep 17 00:00:00 2001 From: Michael Simon Date: Thu, 9 Jan 2025 19:31:30 +0100 Subject: [PATCH] feat(drivers): add UEFI driver (#7069) --- Kconfig | 21 + docs/details/integration/driver/index.rst | 1 + docs/details/integration/driver/uefi.rst | 121 +++++ lv_conf_template.h | 13 + src/core/lv_global.h | 4 + src/drivers/lv_drivers.h | 4 + src/drivers/uefi/lv_uefi.h | 106 ++++ src/drivers/uefi/lv_uefi_context.c | 91 ++++ src/drivers/uefi/lv_uefi_context.h | 71 +++ src/drivers/uefi/lv_uefi_display.c | 284 ++++++++++ src/drivers/uefi/lv_uefi_display.h | 65 +++ src/drivers/uefi/lv_uefi_edk2.h | 28 + src/drivers/uefi/lv_uefi_gnu_efi.h | 17 + src/drivers/uefi/lv_uefi_indev.h | 108 ++++ src/drivers/uefi/lv_uefi_indev_keyboard.c | 346 ++++++++++++ src/drivers/uefi/lv_uefi_indev_pointer.c | 285 ++++++++++ src/drivers/uefi/lv_uefi_indev_touch.c | 290 +++++++++++ src/drivers/uefi/lv_uefi_private.c | 225 ++++++++ src/drivers/uefi/lv_uefi_private.h | 107 ++++ src/drivers/uefi/lv_uefi_std_wrapper.h | 155 ++++++ src/libs/fsdrv/lv_fs_uefi.c | 607 ++++++++++++++++++++++ src/libs/fsdrv/lv_fsdrv.h | 4 + src/lv_conf_internal.h | 43 ++ src/lv_init.c | 15 + src/stdlib/uefi/lv_mem_core_uefi.c | 116 +++++ 25 files changed, 3127 insertions(+) create mode 100644 docs/details/integration/driver/uefi.rst create mode 100644 src/drivers/uefi/lv_uefi.h create mode 100644 src/drivers/uefi/lv_uefi_context.c create mode 100644 src/drivers/uefi/lv_uefi_context.h create mode 100644 src/drivers/uefi/lv_uefi_display.c create mode 100644 src/drivers/uefi/lv_uefi_display.h create mode 100644 src/drivers/uefi/lv_uefi_edk2.h create mode 100644 src/drivers/uefi/lv_uefi_gnu_efi.h create mode 100644 src/drivers/uefi/lv_uefi_indev.h create mode 100644 src/drivers/uefi/lv_uefi_indev_keyboard.c create mode 100644 src/drivers/uefi/lv_uefi_indev_pointer.c create mode 100644 src/drivers/uefi/lv_uefi_indev_touch.c create mode 100644 src/drivers/uefi/lv_uefi_private.c create mode 100644 src/drivers/uefi/lv_uefi_private.h create mode 100644 src/drivers/uefi/lv_uefi_std_wrapper.h create mode 100644 src/libs/fsdrv/lv_fs_uefi.c create mode 100644 src/stdlib/uefi/lv_mem_core_uefi.c diff --git a/Kconfig b/Kconfig index 772a57247..0d67fb6b1 100644 --- a/Kconfig +++ b/Kconfig @@ -1299,6 +1299,13 @@ menu "LVGL configuration" string "Set the working directory" depends on LV_USE_FS_ARDUINO_SD + config LV_USE_FS_UEFI + bool "File system on top of the UEFI EFI_SIMPLE_FILE_SYSTEM_PROTOCOL" + config LV_USE_FS_UEFI_LETTER + int "Set an upper cased letter on which the drive will accessible (e.g. 65 for 'A')" + default 0 + depends on LV_USE_FS_UEFI + config LV_USE_LODEPNG bool "PNG decoder library" @@ -1938,6 +1945,20 @@ menu "LVGL configuration" depends on LV_OS_WINDOWS default n + config LV_USE_UEFI + bool "Use LVGL UEFI backend" + default n + + config LV_USE_UEFI_INCLUDE + string "Header that hides the actual framework (EDK2, gnu-efi, ...)" + depends on LV_USE_UEFI + default "myefi.h" + + config LV_UEFI_USE_MEMORY_SERVICES + bool "Use the memory services from the boot services table" + depends on LV_USE_UEFI + default n + config LV_USE_OPENGLES bool "Use GLFW and OpenGL to open window on PC and handle mouse and keyboard" default n diff --git a/docs/details/integration/driver/index.rst b/docs/details/integration/driver/index.rst index 0a7c508cf..b34f962f6 100644 --- a/docs/details/integration/driver/index.rst +++ b/docs/details/integration/driver/index.rst @@ -14,3 +14,4 @@ Drivers wayland windows X11 + uefi diff --git a/docs/details/integration/driver/uefi.rst b/docs/details/integration/driver/uefi.rst new file mode 100644 index 000000000..2be48974e --- /dev/null +++ b/docs/details/integration/driver/uefi.rst @@ -0,0 +1,121 @@ +============================= +UEFI Display/Inputs driver +============================= + +Overview +-------- + +The **UEFI** display/input `driver `__ offers support for using LVGL with UEFI. + +Prerequisites +------------- + +You need the following protocols available: + +* *EFI_LOADED_IMAGE_PROTOCOL_GUID*, for file system support (used to determine the file system that was used to load the application) +* *EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID*, for file system support +* *EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID*, for keyboard support +* *EFI_SIMPLE_POINTER_PROTOCOL_GUID*, for mouse support +* *EFI_ABSOLUTE_POINTER_PROTOCOL_GUID*, for touch support +* *EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID*, for drawing +* *EFI_EDID_ACTIVE_PROTOCOL_GUID*, for drawing (optional) + +Configure UEFI driver +------------------------ + +- Enable the UEFI driver support in lv_conf.h + +.. code-block:: c + + #define LV_USE_UEFI 1 + +- Enable the the memory core functions, which are wrappers around AllocatePool and FreePool (using memory of type *EfiBootServicesData*) if you do not wan't to use your own implementations + +.. code-block:: c + + #define LV_UEFI_USE_MEMORY_SERVICES 1 + +- You can enable file system support for the file system from which the appliation got loaded (default letter 'E') + +.. code-block:: c + + #define LV_USE_FS_UEFI 1 + +- You need to define an include file which contains the basic UEFI definitions (protocols and types), there are 2 predefined files which can be used for EDK2 and gnu-efi + +.. code-block:: c + + #define LV_USE_UEFI_INCLUDE + // or + #define LV_USE_UEFI_INCLUDE + +Usage +----- + +.. code-block:: c + + #include "lvgl/lvgl.h" + #include "lvgl/examples/lv_examples.h" + #include "lvgl/demos/lv_demos.h" + + EFI_STATUS EFIAPI EfiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE * SystemTable) + { + lv_uefi_init(ImageHandle, SystemTable); + lv_init(); + + if(!lv_is_initialized()) return EFI_NOT_READY; + + EFI_HANDLE handle = NULL; + lv_display_t* display = NULL; + lv_indev_t* indev = NULL; + lv_group_t* group = NULL; + lv_obj_t* cursor = NULL; + // used to get out of the main loop + size_t counter; + + // Init the display + handle = lv_uefi_display_get_active(); + if(handle == NULL) { + handle = lv_uefi_display_get_any(); + } + if(handle == NULL) { + lv_deinit(); + return EFI_UNSUPPORTED; + } + + display = lv_uefi_display_create(handle); + lv_display_set_default(display); + + // Create the group + group = lv_group_create(); + lv_group_set_default(group); + + // Create an image that can be used as cursor + cursor = lv_image_create(lv_layer_top()); + lv_image_set_src(cursor, "E:cursor.png"); + + // Create the input devices + indev = lv_uefi_simple_text_input_indev_create(); + lv_indev_set_group(indev, lv_group_get_default()); + lv_uefi_simple_text_input_indev_add_all(indev); + + indev = lv_uefi_simple_pointer_indev_create(NULL); + lv_uefi_simple_pointer_indev_add_all(indev); + lv_indev_set_cursor(indev, cursor); + + indev = lv_uefi_absolute_pointer_indev_create(NULL); + lv_uefi_absolute_pointer_indev_add_all(indev); + + lv_demo_widgets(); + + // Run main loop for ~ 10 seconds + counter = 0; + while(counter < 10000) { + counter ++; + gBS->Stall(1000); + lv_tick_inc(1); + lv_timer_handler(); + } + + return EFI_SUCCESS; + } diff --git a/lv_conf_template.h b/lv_conf_template.h index 55e46b702..51340f448 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -852,6 +852,12 @@ #define LV_FS_ARDUINO_SD_PATH "" /**< Set the working directory. File/directory paths will be appended to it. */ #endif +/** API for UEFI */ +#define LV_USE_FS_UEFI 0 +#if LV_USE_FS_UEFI + #define LV_FS_UEFI_LETTER '\0' /**< Set an upper cased letter on which the drive will accessible (e.g. 'A') */ +#endif + /** LODEPNG decoder library */ #define LV_USE_LODEPNG 0 @@ -1206,6 +1212,13 @@ /** LVGL Windows backend */ #define LV_USE_WINDOWS 0 +/** LVGL UEFI backend */ +#define LV_USE_UEFI 0 +#if LV_USE_UEFI + #define LV_USE_UEFI_INCLUDE "myefi.h" /**< Header that hides the actual framework (EDK2, gnu-efi, ...) */ + #define LV_UEFI_USE_MEMORY_SERVICES 0 /**< Use the memory functions from the boot services table */ +#endif + /** Use OpenGL to open window on PC and handle mouse and keyboard */ #define LV_USE_OPENGLES 0 #if LV_USE_OPENGLES diff --git a/src/core/lv_global.h b/src/core/lv_global.h index 5d37b394a..52a643158 100644 --- a/src/core/lv_global.h +++ b/src/core/lv_global.h @@ -169,6 +169,10 @@ typedef struct _lv_global_t { lv_fs_drv_t win32_fs_drv; #endif +#if LV_USE_FS_UEFI + lv_fs_drv_t uefi_fs_drv; +#endif + #if LV_USE_FS_LITTLEFS lv_fs_drv_t littlefs_fs_drv; #endif diff --git a/src/drivers/lv_drivers.h b/src/drivers/lv_drivers.h index 621f15654..77cd816e0 100644 --- a/src/drivers/lv_drivers.h +++ b/src/drivers/lv_drivers.h @@ -54,6 +54,10 @@ extern "C" { #include "wayland/lv_wayland.h" +#include "uefi/lv_uefi_context.h" +#include "uefi/lv_uefi_indev.h" +#include "uefi/lv_uefi_display.h" + /********************* * DEFINES *********************/ diff --git a/src/drivers/uefi/lv_uefi.h b/src/drivers/uefi/lv_uefi.h new file mode 100644 index 000000000..6302555c1 --- /dev/null +++ b/src/drivers/uefi/lv_uefi.h @@ -0,0 +1,106 @@ +/** + * @file lv_uefi.h + * + */ + +#ifndef LV_UEFI_H +#define LV_UEFI_H + +#if LV_USE_UEFI + + #include LV_USE_UEFI_INCLUDE + + #if defined(__clang__) || defined(__GNUC__) + #if defined(__x86_64__) + #define __LV_UEFI_ARCH_X64__ + #define __LV_UEFI_64BIT__ + #elif defined(__i386__) + #define __LV_UEFI_ARCH_X86__ + #define __LV_UEFI_32BIT__ + #elif defined(__aarch64__) + #define __LV_UEFI_ARCH_AARCH64__ + #define __LV_UEFI_64BIT__ + #else + #error Architecture is not supported + #endif + #define LV_UEFI_STATIC_ASSERT _Static_assert + #elif defined(_MSC_VER) + #if defined(_M_AMD64) && !defined(_M_ARM64) + #define __LV_UEFI_ARCH_X64__ + #define __LV_UEFI_64BIT__ + #elif defined(_M_IX86) + #define __LV_UEFI_ARCH_X86__ + #define __LV_UEFI_32BIT__ + #elif defined(_M_ARM64) + #define __LV_UEFI_ARCH_AARCH64__ + #define __LV_UEFI_64BIT__ + #else + #error Architecture is not supported + #endif + #define LV_UEFI_STATIC_ASSERT static_assert + #else + #error Your compiler is not supported + #endif + + #ifdef LV_USE_UEFI_INCLUDE + #include LV_USE_UEFI_INCLUDE + #else + #error No UEFI headers available + #endif + + // Verify that all required protocols are known + #if !defined(EFI_LOADED_IMAGE_PROTOCOL_GUID) + #error Missing support for EFI_LOADED_IMAGE_PROTOCOL + #endif + #if !defined(EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID) + #error Missing support for EFI_SIMPLE_FILE_SYSTEM_PROTOCOL + #endif + #if !defined(EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID) + #error Missing support for EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL + #endif + #if !defined(EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID) + #error Missing support for EFI_SIMPLE_TEXT_INPUT_PROTOCOL + #endif + #if !defined(EFI_SIMPLE_POINTER_PROTOCOL_GUID) + #error Missing support for EFI_SIMPLE_POINTER_PROTOCOL + #endif + #if !defined(EFI_ABSOLUTE_POINTER_PROTOCOL_GUID) + #error Missing support for EFI_ABSOLUTE_POINTER_PROTOCOL + #endif + #if !defined(EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID) + #error Missing support for EFI_GRAPHICS_OUTPUT_PROTOCOL + #endif + #if !defined(EFI_EDID_ACTIVE_PROTOCOL_GUID) + #error Missing support for EFI_EDID_ACTIVE_PROTOCOL + #endif + #if !defined(EFI_FILE_INFO_ID) + #error Missing support for EFI_FILE_INFO + #endif + #if !defined(EFI_TIMESTAMP_PROTOCOL_GUID) + #error Missing support for EFI_TIMESTAMP_PROTOCOL_GUID + #endif + + // Verify that all types have the correct size + LV_UEFI_STATIC_ASSERT(sizeof(BOOLEAN) == 1, "Size check for 'BOOLEAN' failed."); + LV_UEFI_STATIC_ASSERT(sizeof(INT8) == 1, "Size check for 'INT8' failed."); + LV_UEFI_STATIC_ASSERT(sizeof(UINT8) == 1, "Size check for 'UINT8' failed."); + LV_UEFI_STATIC_ASSERT(sizeof(INT16) == 2, "Size check for 'INT16' failed."); + LV_UEFI_STATIC_ASSERT(sizeof(UINT16) == 2, "Size check for 'UINT16' failed."); + LV_UEFI_STATIC_ASSERT(sizeof(INT32) == 4, "Size check for 'INT32' failed."); + LV_UEFI_STATIC_ASSERT(sizeof(UINT32) == 4, "Size check for 'UINT32' failed."); + LV_UEFI_STATIC_ASSERT(sizeof(INT64) == 8, "Size check for 'INT64' failed."); + LV_UEFI_STATIC_ASSERT(sizeof(UINT64) == 8, "Size check for 'UINT64' failed."); + LV_UEFI_STATIC_ASSERT(sizeof(CHAR8) == 1, "Size check for 'CHAR8' failed."); + LV_UEFI_STATIC_ASSERT(sizeof(CHAR16) == 2, "Size check for 'CHAR16' failed."); + + #ifdef __LV_UEFI_32BIT__ + LV_UEFI_STATIC_ASSERT(sizeof(INTN) == 4, "Size check for 'INTN' failed."); + LV_UEFI_STATIC_ASSERT(sizeof(UINTN) == 4, "Size check for 'UINTN' failed."); + #else + LV_UEFI_STATIC_ASSERT(sizeof(INTN) == 8, "Size check for 'INTN' failed."); + LV_UEFI_STATIC_ASSERT(sizeof(UINTN) == 8, "Size check for 'UINTN' failed."); + #endif + +#endif + +#endif \ No newline at end of file diff --git a/src/drivers/uefi/lv_uefi_context.c b/src/drivers/uefi/lv_uefi_context.c new file mode 100644 index 000000000..7a8c52c1e --- /dev/null +++ b/src/drivers/uefi/lv_uefi_context.c @@ -0,0 +1,91 @@ +/** + * @file lv_uefi_context.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lvgl.h" + +#if LV_USE_UEFI + +#include "lv_uefi_context.h" +#include "lv_uefi_private.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * GOLBAL VARIABLES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * @brief Initialize the UEFI chache variables. + * @param image_handle The handle of the current image + * @param system_table Pointer to the system table + * @remark This has to be called before lv_init(). +*/ +void lv_uefi_init(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE * system_table) +{ + LV_ASSERT_NULL(image_handle); + LV_ASSERT_NULL(system_table); + + gLvEfiImageHandle = image_handle; + gLvEfiST = system_table; + gLvEfiBS = gLvEfiST->BootServices; + gLvEfiRT = gLvEfiST->RuntimeServices; +} + +/** + * @brief Initialize the LVGL UEFI backend. + * @remark This is a private API which is used for LVGL UEFI backend + * implementation. LVGL users shouldn't use that because the + * LVGL has already used it in lv_init. + */ +void lv_uefi_platform_init(void) +{ + LV_ASSERT_NULL(gLvEfiImageHandle); + LV_ASSERT_NULL(gLvEfiST); + LV_ASSERT_NULL(gLvEfiBS); + LV_ASSERT_NULL(gLvEfiRT); +} + +/** + * @brief Cleanup the LVGL UEFI backend. + * @remark This is a private API which is used for LVGL UEFI backend + * implementation. LVGL users shouldn't use that because the + * LVGL has already used it in lv_deinit. +*/ +void lv_uefi_platform_deinit(void) +{ + ; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif \ No newline at end of file diff --git a/src/drivers/uefi/lv_uefi_context.h b/src/drivers/uefi/lv_uefi_context.h new file mode 100644 index 000000000..b850dd97c --- /dev/null +++ b/src/drivers/uefi/lv_uefi_context.h @@ -0,0 +1,71 @@ +/** + * @file lv_uefi_context.h + * + */ + +#ifndef __LV_UEFI_CONTEXT_H__ +#define __LV_UEFI_CONTEXT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lvgl.h" + +#if LV_USE_UEFI + +#include "lv_uefi.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * @brief Initialize the UEFI chache variables. + * @param image_handle The handle of the current image + * @param system_table Pointer to the system table + * @remark This has to be called before lv_init(). +*/ +void lv_uefi_init( + EFI_HANDLE image_handle, + EFI_SYSTEM_TABLE * system_table); + +/** + * @brief Initialize the LVGL UEFI backend. + * @remark This is a private API which is used for LVGL UEFI backend + * implementation. LVGL users shouldn't use that because the + * LVGL has already used it in lv_init. +*/ +void lv_uefi_platform_init(void); + +/** + * @brief Cleanup the LVGL UEFI backend. + * @remark This is a private API which is used for LVGL UEFI backend + * implementation. LVGL users shouldn't use that because the + * LVGL has already used it in lv_deinit. +*/ +void lv_uefi_platform_deinit(void); + +/********************** + * MACROS + **********************/ + +#endif + +#ifdef __cplusplus +} +#endif + +#endif //__LV_UEFI_CONTEXT_H__ \ No newline at end of file diff --git a/src/drivers/uefi/lv_uefi_display.c b/src/drivers/uefi/lv_uefi_display.c new file mode 100644 index 000000000..8fa807ee8 --- /dev/null +++ b/src/drivers/uefi/lv_uefi_display.c @@ -0,0 +1,284 @@ +/** + * @file lv_uefi_display.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lvgl.h" + +#if LV_USE_UEFI + +#include "lv_uefi_display.h" +#include "lv_uefi_private.h" + +#if LV_COLOR_DEPTH != 32 + #error [lv_uefi] Unsupported LV_COLOR_DEPTH. +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +typedef struct _lv_uefi_display_context_t { + EFI_HANDLE handle; + EFI_GRAPHICS_OUTPUT_PROTOCOL * gop_protocol; + void * buffer; + size_t buffer_size; +} lv_uefi_display_context_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void _display_event_cb(lv_event_t * e); +static void _display_flush_cb(lv_display_t * display, const lv_area_t * area, uint8_t * px_map); + +static void _display_ctx_free(lv_uefi_display_context_t * display_ctx); +static bool _display_interface_is_valid(const EFI_GRAPHICS_OUTPUT_PROTOCOL * interface); + +/********************** + * GOLBAL VARIABLES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +static EFI_GUID _uefi_guid_graphics_output = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; +static EFI_GUID _uefi_guid_edid_active = EFI_EDID_ACTIVE_PROTOCOL_GUID; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * @brief Create a LVGL display object. + * @param handle The handle on which an instance of the EFI_GRAPHICS_OUTPUT_PROTOCOL protocol is installed. + * @return The created LVGL display object. + */ +lv_display_t * lv_uefi_display_create(void * handle) +{ + lv_display_t * display = NULL; + lv_uefi_display_context_t * display_ctx; + + if(!lv_uefi_protocol_test(handle, &_uefi_guid_graphics_output)) return NULL; + + display_ctx = lv_calloc(1, sizeof(lv_uefi_display_context_t)); + LV_ASSERT_MALLOC(display_ctx); + + display_ctx->handle = handle; + display_ctx->gop_protocol = (EFI_GRAPHICS_OUTPUT_PROTOCOL *)lv_uefi_protocol_open(handle, &_uefi_guid_graphics_output); + if(!_display_interface_is_valid(display_ctx->gop_protocol)) { + LV_LOG_WARN("[lv_uefi] The GOP interface is not valid."); + goto error; + } + + // 4 bytes per pixel + display_ctx->buffer_size = 4 * display_ctx->gop_protocol->Mode->Info->HorizontalResolution * + display_ctx->gop_protocol->Mode->Info->VerticalResolution; + display_ctx->buffer = lv_malloc(display_ctx->buffer_size); + LV_ASSERT_MALLOC(display_ctx->buffer); + + display = lv_display_create(display_ctx->gop_protocol->Mode->Info->HorizontalResolution, + display_ctx->gop_protocol->Mode->Info->VerticalResolution); + lv_display_add_event_cb(display, _display_event_cb, LV_EVENT_DELETE, display); + lv_display_set_flush_cb(display, _display_flush_cb); + lv_display_set_buffers(display, display_ctx->buffer, NULL, (uint32_t)display_ctx->buffer_size, + LV_DISPLAY_RENDER_MODE_DIRECT); + lv_display_set_user_data(display, display_ctx); + + goto finish; + +error: + if(display != NULL) { + lv_display_set_user_data(display, NULL); + lv_display_delete(display); + display = NULL; + } + + if(display_ctx != NULL) _display_ctx_free(display_ctx); + +finish: + return display; +} + +/** + * @brief Try to find the active display handle. + * @return The handle or NULL if not found. + * @remark The active display need interfaces for EFI_GRAPHICS_OUTPUT_PROTOCOL and EFI_EDID_ACTIVE_PROTOCOL +*/ +void * lv_uefi_display_get_active(void) +{ + EFI_STATUS status; + EFI_HANDLE active_handle = NULL; + EFI_HANDLE * handles = NULL; + UINTN no_handles; + UINTN index; + + status = gLvEfiBS->LocateHandleBuffer( + ByProtocol, + &_uefi_guid_graphics_output, + NULL, + &no_handles, + &handles); + if(status != EFI_SUCCESS) goto error; + + for(index = 0; index < no_handles; index++) { + if(!lv_uefi_protocol_test(handles[index], &_uefi_guid_edid_active)) continue; + if(!lv_uefi_protocol_test(handles[index], &_uefi_guid_graphics_output)) continue; + active_handle = handles[index]; + break; + } + + goto finish; + +error: + +finish: + if(handles != NULL) gLvEfiBS->FreePool(handles); + + return active_handle; +} + +/** + * @brief Try to find any display handle. + * @return The handle or NULL if not found. +*/ +void * lv_uefi_display_get_any(void) +{ + EFI_STATUS status; + EFI_HANDLE active_handle = NULL; + EFI_HANDLE * handles = NULL; + UINTN no_handles; + UINTN index; + + status = gLvEfiBS->LocateHandleBuffer( + ByProtocol, + &_uefi_guid_graphics_output, + NULL, + &no_handles, + &handles); + if(status != EFI_SUCCESS) goto error; + + for(index = 0; index < no_handles; index++) { + if(!lv_uefi_protocol_test(handles[index], &_uefi_guid_graphics_output)) continue; + active_handle = handles[index]; + break; + } + + goto finish; + +error: + +finish: + if(handles != NULL) gLvEfiBS->FreePool(handles); + + return active_handle; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void _display_event_cb(lv_event_t * e) +{ + lv_display_t * display; + lv_uefi_display_context_t * display_ctx; + + if(lv_event_get_code(e) != LV_EVENT_DELETE) return; + + display = (lv_display_t *)lv_event_get_user_data(e); + if(display == NULL) return; + + display_ctx = (lv_uefi_display_context_t *)lv_display_get_user_data(display); + lv_display_set_user_data(display, NULL); + + if(display_ctx != NULL) _display_ctx_free(display_ctx); +} + +static void _display_flush_cb(lv_display_t * display, const lv_area_t * area, uint8_t * px_map) +{ + EFI_STATUS status; + int32_t w; + int32_t h; + + lv_uefi_display_context_t * display_ctx = (lv_uefi_display_context_t *)lv_display_get_user_data(display); + LV_ASSERT_NULL(display_ctx); + + w = (int32_t)area->x2 - (int32_t)area->x1 + 1; + h = (int32_t)area->y2 - (int32_t)area->y1 + 1; + + if(w < 0 || h < 0) { + LV_LOG_ERROR("[lv_uefi] Invalid lv_display_flush_cb call (invalid rect)."); + goto error; + } + + if((uint32_t)(area->x1 + w) > display_ctx->gop_protocol->Mode->Info->HorizontalResolution) { + LV_LOG_ERROR("[lv_uefi] Invalid lv_display_flush_cb call (invalid width)."); + goto error; + } + + if((uint32_t)(area->y1 + h) > display_ctx->gop_protocol->Mode->Info->HorizontalResolution) { + LV_LOG_ERROR("[lv_uefi] Invalid lv_display_flush_cb call (invalid height)."); + goto error; + } + + status = display_ctx->gop_protocol->Blt( + display_ctx->gop_protocol, + (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)px_map, + EfiBltBufferToVideo, + area->x1, + area->y1, + area->x1, + area->y1, + w, + h, + display_ctx->gop_protocol->Mode->Info->HorizontalResolution * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); + if(status != EFI_SUCCESS) { + LV_LOG_ERROR("[lv_uefi] Blt failed with error code: %llx.", status); + goto error; + } + + goto finish; + +error: + +finish: + lv_display_flush_ready(display); +} + +static void _display_ctx_free(lv_uefi_display_context_t * display_ctx) +{ + if(display_ctx == NULL) { + return; + } + + if(display_ctx->gop_protocol != NULL) lv_uefi_protocol_close(display_ctx->handle, &_uefi_guid_graphics_output); + if(display_ctx->buffer != NULL) lv_free(display_ctx->buffer); + + lv_free(display_ctx); +} + +static bool _display_interface_is_valid(const EFI_GRAPHICS_OUTPUT_PROTOCOL * interface) +{ + if(interface == NULL) return FALSE; + if(interface->Mode == NULL) return FALSE; + if(interface->Mode->Info == NULL) return FALSE; + if(interface->Mode->Info->HorizontalResolution == 0) return FALSE; + if(interface->Mode->Info->HorizontalResolution >= 32767) return FALSE; + if(interface->Mode->Info->VerticalResolution == 0) return FALSE; + if(interface->Mode->Info->VerticalResolution >= 32767) return FALSE; + + return TRUE; +} +#endif \ No newline at end of file diff --git a/src/drivers/uefi/lv_uefi_display.h b/src/drivers/uefi/lv_uefi_display.h new file mode 100644 index 000000000..e56fb2ee2 --- /dev/null +++ b/src/drivers/uefi/lv_uefi_display.h @@ -0,0 +1,65 @@ +/** + * @file lv_uefi_display.h + * + */ + +#ifndef __LV_UEFI_DISPLAY_H__ +#define __LV_UEFI_DISPLAY_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../display/lv_display.h" + +#if LV_USE_UEFI + +#include "lv_uefi.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * @brief Create a LVGL display object. + * @param handle The handle on which an instance of the EFI_GRAPHICS_OUTPUT_PROTOCOL protocol is installed. + * @return The created LVGL display object. +*/ +lv_display_t * lv_uefi_display_create(void * handle); + +/** + * @brief Try to find the active display handle. + * @return The handle or NULL if not found. + * @remark The active display need interfaces for EFI_GRAPHICS_OUTPUT_PROTOCOL and EFI_EDID_ACTIVE_PROTOCOL +*/ +void * lv_uefi_display_get_active(void); + +/** + * @brief Try to find any display handle. + * @return The handle or NULL if not found. +*/ +void * lv_uefi_display_get_any(void); + +/********************** + * MACROS + **********************/ + +#endif + +#ifdef __cplusplus +} +#endif + +#endif //__LV_UEFI_DISPLAY_H__ \ No newline at end of file diff --git a/src/drivers/uefi/lv_uefi_edk2.h b/src/drivers/uefi/lv_uefi_edk2.h new file mode 100644 index 000000000..870b99782 --- /dev/null +++ b/src/drivers/uefi/lv_uefi_edk2.h @@ -0,0 +1,28 @@ +/** + * @file lv_uefi_edk2.h + * + */ + +#ifndef LV_UEFI_EDK2_H +#define LV_UEFI_EDK2_H + +#if LV_USE_UEFI + + #define LV_UEFI_EDK2_HEADERS 1 + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + +#endif + +#endif \ No newline at end of file diff --git a/src/drivers/uefi/lv_uefi_gnu_efi.h b/src/drivers/uefi/lv_uefi_gnu_efi.h new file mode 100644 index 000000000..23e9e4955 --- /dev/null +++ b/src/drivers/uefi/lv_uefi_gnu_efi.h @@ -0,0 +1,17 @@ +/** + * @file lv_uefi_gnu_efi.h + * + */ + +#ifndef LV_UEFI_GNU_EFI_H +#define LV_UEFI_GNU_EFI_H + +#if LV_USE_UEFI + + #define LV_UEFI_GNU_EFI_HEADERS 1 + + #include + +#endif + +#endif \ No newline at end of file diff --git a/src/drivers/uefi/lv_uefi_indev.h b/src/drivers/uefi/lv_uefi_indev.h new file mode 100644 index 000000000..69dee2fc2 --- /dev/null +++ b/src/drivers/uefi/lv_uefi_indev.h @@ -0,0 +1,108 @@ +/** + * @file lv_uefi_indev.h + * + */ + +#ifndef __LV_UEFI_INDEV_H__ +#define __LV_UEFI_INDEV_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../indev/lv_indev.h" + +#if LV_USE_UEFI + +#include "lv_uefi.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * @brief Create an indev object. + * @param display_res The resolution of the display in pixels, needed to scale the input. + * If NULL the resolution of the current default display will be used. + * @return The created LVGL indev object. +*/ +lv_indev_t * lv_uefi_simple_pointer_indev_create(lv_point_t * display_res); + +/** + * @brief Add an EFI_SIMPLE_POINTER_PROTOCOL interface to the indev. + * @param indev Indev that was created with lv_uefi_simple_pointer_indev_create. + * @param handle The handle on which an instance of the EFI_SIMPLE_POINTER_PROTOCOL protocol is installed. + * @return True if the interface was added. +*/ +bool lv_uefi_simple_pointer_indev_add_handle(lv_indev_t * indev, EFI_HANDLE handle); + +/** + * @brief Add all available EFI_SIMPLE_POINTER_PROTOCOL interfaces to the indev. + * @param indev Indev that was created with lv_uefi_simple_pointer_indev_create. +*/ +void lv_uefi_simple_pointer_indev_add_all(lv_indev_t * indev); + +/** + * @brief Create a LVGL indev object. + * @param display_res The resolution of the display in pixels, needed to scale the input. + * @return The created LVGL indev object. +*/ +lv_indev_t * lv_uefi_absolute_pointer_indev_create(lv_point_t * display_res); + +/** + * @brief Add an EFI_ABSOLUTE_POINTER_PROTOCOL interface to the indev. + * @param indev Indev that was created with lv_uefi_absolute_pointer_indev_create. + * @param handle The handle on which an instance of the EFI_ABSOLUTE_POINTER_PROTOCOL protocol is installed. + * @return True if the interface was added. +*/ +bool lv_uefi_absolute_pointer_indev_add_handle(lv_indev_t * indev, EFI_HANDLE handle); + +/** + * @brief Add all available EFI_ABSOLUTE_POINTER_PROTOCOL interfaces to the indev. + * @param indev Indev that was created with lv_uefi_absolute_pointer_indev_create. +*/ +void lv_uefi_absolute_pointer_indev_add_all(lv_indev_t * indev); + +/** + * @brief Create an indev object. + * @return The created LVGL indev object. +*/ +lv_indev_t * lv_uefi_simple_text_input_indev_create(void); + +/** + * @brief Add an EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL interface to the indev. + * @param indev Indev that was created with lv_uefi_simple_text_input_indev_create. + * @param handle The handle on which an instance of the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL protocol is installed. + * @return True if the interface was added. +*/ +bool lv_uefi_simple_text_input_indev_add_handle(lv_indev_t * indev, EFI_HANDLE handle); + +/** + * @brief Add all available EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL interfaces to the indev. + * @param indev Indev that was created with lv_uefi_simple_text_input_indev_create. +*/ +void lv_uefi_simple_text_input_indev_add_all(lv_indev_t * indev); + +/********************** + * MACROS + **********************/ + +#endif + +#ifdef __cplusplus +} +#endif + +#endif //__LV_UEFI_INDEV_H__ \ No newline at end of file diff --git a/src/drivers/uefi/lv_uefi_indev_keyboard.c b/src/drivers/uefi/lv_uefi_indev_keyboard.c new file mode 100644 index 000000000..83e99b58f --- /dev/null +++ b/src/drivers/uefi/lv_uefi_indev_keyboard.c @@ -0,0 +1,346 @@ +/** + * @file lv_uefi_indev.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lvgl.h" +#include "../../stdlib/lv_mem.h" +#include "../../misc/lv_types.h" +#include "../../misc/lv_text.h" + +#if LV_USE_UEFI + +#include "lv_uefi_indev.h" +#include "lv_uefi_private.h" + +/********************* + * DEFINES + *********************/ + +#define SIMPLE_TEXT_INPUT_INDEV_SIGNATURE 0x53495449 + +/********************** + * TYPEDEFS + **********************/ + +typedef struct _lv_uefi_simple_text_input_key_cache_t { + uint32_t key; /**< Key code*/ + bool pressed; /**< If true this is a pressed entry if false this is a release entry*/ +} lv_uefi_simple_text_input_key_cache_t; + +typedef struct _lv_uefi_simple_text_input_handle_context_t { + EFI_HANDLE handle; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * interface; +} lv_uefi_simple_text_input_handle_context_t; + +typedef struct _lv_uefi_simple_text_input_context_t { + uint32_t signature; /**< Has to be checked to avoid access to a different indev*/ + lv_ll_t handles; + lv_ll_t key_cache; +} lv_uefi_simple_text_input_context_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void _simple_text_input_event_cb(lv_event_t * e); +static void _simple_text_input_read_cb(lv_indev_t * indev, lv_indev_data_t * data); + +static void _simple_text_input_handle_context_free(void * ptr); +static void _simple_text_input_context_free(lv_uefi_simple_text_input_context_t * indev_ctx); + +static bool _simple_text_input_interface_is_valid(const EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * interface); + +static void _simple_text_input_read(lv_uefi_simple_text_input_context_t * indev_ctx, + lv_uefi_simple_text_input_handle_context_t * handle_ctx); + +static uint32_t _utf8_from_unicode(UINT32 unicode); +static uint32_t _key_from_uefi_key(const EFI_KEY_DATA * key); + +/********************** + * STATIC VARIABLES + **********************/ + +static EFI_GUID _uefi_guid_simple_text_input = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * @brief Create an indev object. + * @return The created LVGL indev object. +*/ +lv_indev_t * lv_uefi_simple_text_input_indev_create(void) +{ + lv_indev_t * indev = NULL; + lv_uefi_simple_text_input_context_t * indev_ctx = NULL; + + indev_ctx = lv_calloc(1, sizeof(lv_uefi_simple_text_input_context_t)); + LV_ASSERT_MALLOC(indev_ctx); + + indev_ctx->signature = SIMPLE_TEXT_INPUT_INDEV_SIGNATURE; + + lv_ll_init(&indev_ctx->handles, sizeof(lv_uefi_simple_text_input_handle_context_t)); + lv_ll_init(&indev_ctx->key_cache, sizeof(lv_uefi_simple_text_input_key_cache_t)); + + indev = lv_indev_create(); + lv_indev_set_type(indev, LV_INDEV_TYPE_KEYPAD); + lv_indev_set_user_data(indev, indev_ctx); + lv_indev_add_event_cb(indev, _simple_text_input_event_cb, LV_EVENT_DELETE, indev); + lv_indev_set_read_cb(indev, _simple_text_input_read_cb); + + return indev; +} + +/** + * @brief Add an EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL interface to the indev. + * @param indev Indev that was created with lv_uefi_simple_text_input_indev_create. + * @param handle The handle on which an instance of the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL protocol is installed. + * @return True if the interface was added. +*/ +bool lv_uefi_simple_text_input_indev_add_handle(lv_indev_t * indev, EFI_HANDLE handle) +{ + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * interface = NULL; + lv_uefi_simple_text_input_handle_context_t * handle_ctx = NULL; + + lv_uefi_simple_text_input_context_t * indev_ctx = (lv_uefi_simple_text_input_context_t *)lv_indev_get_user_data(indev); + LV_ASSERT_NULL(indev_ctx); + + if(indev_ctx->signature != SIMPLE_TEXT_INPUT_INDEV_SIGNATURE) return false; + + interface = (EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *)lv_uefi_protocol_open(handle, &_uefi_guid_simple_text_input); + if(!_simple_text_input_interface_is_valid(interface)) { + lv_uefi_protocol_close(handle, &_uefi_guid_simple_text_input); + LV_LOG_WARN("[lv_uefi] The SIMPLE_TEXT_INPUT interface is not valid."); + return false; + } + + handle_ctx = (lv_uefi_simple_text_input_handle_context_t *) lv_ll_ins_head(&indev_ctx->handles); + LV_ASSERT_MALLOC(handle_ctx); + + handle_ctx->handle = handle; + handle_ctx->interface = interface; + + return true; +} + +/** + * @brief Add all available EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL interfaces to the indev. + * @param indev Indev that was created with lv_uefi_simple_text_input_indev_create. +*/ +void lv_uefi_simple_text_input_indev_add_all(lv_indev_t * indev) +{ + EFI_STATUS status; + EFI_HANDLE * handles = NULL; + UINTN no_handles; + UINTN index; + + lv_uefi_simple_text_input_context_t * indev_ctx = (lv_uefi_simple_text_input_context_t *)lv_indev_get_user_data(indev); + LV_ASSERT_NULL(indev_ctx); + + if(indev_ctx->signature != SIMPLE_TEXT_INPUT_INDEV_SIGNATURE) return; + + status = gLvEfiBS->LocateHandleBuffer(ByProtocol, &_uefi_guid_simple_text_input, NULL, &no_handles, &handles); + if(status != EFI_SUCCESS) { + LV_LOG_ERROR("[lv_uefi] LocateHandleBuffer(SIMPLE_TEXT_INPUT_EX) failed with error code %llx.", status); + return; + } + + for(index = 0; index < no_handles; index++) { + lv_uefi_simple_text_input_indev_add_handle(indev, handles[index]); + } + + if(handles != NULL) gLvEfiBS->FreePool(handles); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void _simple_text_input_event_cb(lv_event_t * e) +{ + lv_indev_t * indev; + lv_uefi_simple_text_input_context_t * indev_ctx; + + if(lv_event_get_code(e) != LV_EVENT_DELETE) return; + + indev = (lv_indev_t *)lv_event_get_user_data(e); + if(indev == NULL) return; + + indev_ctx = (lv_uefi_simple_text_input_context_t *)lv_indev_get_user_data(indev); + lv_indev_set_user_data(indev, NULL); + + if(indev_ctx != NULL) _simple_text_input_context_free(indev_ctx); +} + +static void _simple_text_input_read_cb(lv_indev_t * indev, lv_indev_data_t * data) +{ + lv_uefi_simple_text_input_handle_context_t * handle_ctx = NULL; + lv_uefi_simple_text_input_key_cache_t * key_cache = NULL; + void * node = NULL; + + lv_uefi_simple_text_input_context_t * indev_ctx = (lv_uefi_simple_text_input_context_t *)lv_indev_get_user_data(indev); + LV_ASSERT_NULL(indev_ctx); + + /* Empty the buffer before reading new values */ + if(lv_ll_is_empty(&indev_ctx->key_cache)) { + // Read from all registered devices + for(node = lv_ll_get_head(&indev_ctx->handles); node != NULL; node = lv_ll_get_next(&indev_ctx->handles, node)) { + handle_ctx = (lv_uefi_simple_text_input_handle_context_t *) node; + _simple_text_input_read(indev_ctx, handle_ctx); + } + } + + /* Return the first value */ + node = lv_ll_get_head(&indev_ctx->key_cache); + if(node != NULL) { + key_cache = (lv_uefi_simple_text_input_key_cache_t *)node; + data->state = key_cache->pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; + data->key = key_cache->key; + lv_ll_remove(&indev_ctx->key_cache, node); + lv_free(key_cache); + } + + /* Continue reading if there are more values in the buffer */ + data->continue_reading = !lv_ll_is_empty(&indev_ctx->key_cache); +} + +static void _simple_text_input_context_free(lv_uefi_simple_text_input_context_t * indev_ctx) +{ + if(indev_ctx == NULL) return; + lv_ll_clear_custom(&indev_ctx->handles, _simple_text_input_handle_context_free); + lv_ll_clear(&indev_ctx->key_cache); + lv_free(indev_ctx); +} + +static bool _simple_text_input_interface_is_valid(const EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * interface) +{ + if(interface == NULL) return FALSE; + return TRUE; +} + +static void _simple_text_input_handle_context_free(void * ptr) +{ + lv_uefi_simple_text_input_handle_context_t * handle_ctx = (lv_uefi_simple_text_input_handle_context_t *)ptr; + + if(handle_ctx == NULL) return; + if(handle_ctx->interface) lv_uefi_protocol_close(handle_ctx->handle, &_uefi_guid_simple_text_input); + lv_free(handle_ctx); +} + +static void _simple_text_input_read(lv_uefi_simple_text_input_context_t * indev_ctx, + lv_uefi_simple_text_input_handle_context_t * handle_ctx) +{ + EFI_STATUS status; + EFI_KEY_DATA state; + uint32_t key; + lv_uefi_simple_text_input_key_cache_t * cache = NULL; + + LV_ASSERT_NULL(indev_ctx); + LV_ASSERT_NULL(handle_ctx); + + status = handle_ctx->interface->ReadKeyStrokeEx( + handle_ctx->interface, + &state); + if(status == EFI_NOT_READY) return; + if(status != EFI_SUCCESS) { + LV_LOG_ERROR("[lv_uefi] ReadKeyStrokeEx failed."); + return; + } + + key = _key_from_uefi_key(&state); + + /* insert the press */ + cache = (lv_uefi_simple_text_input_key_cache_t *) lv_ll_ins_tail(&indev_ctx->key_cache); + LV_ASSERT_MALLOC(cache); + cache->key = key; + cache->pressed = true; + + /* insert the release */ + cache = (lv_uefi_simple_text_input_key_cache_t *) lv_ll_ins_tail(&indev_ctx->key_cache); + LV_ASSERT_MALLOC(cache); + cache->key = key; + cache->pressed = false; +} + +static uint32_t _utf8_from_unicode(UINT32 unicode) +{ + uint8_t bytes[4] = {0, 0, 0, 0}; + + /* unicode < 128 -> 1 byte */ + if(unicode < 128) { + bytes[0] |= unicode; + } + /* unicode < 2048 -> 2 byte */ + else if(unicode < 2048) { + bytes[0] = 0xC0; + bytes[0] |= unicode >> 6; + bytes[1] = 0x80; + bytes[1] |= (unicode & 0x003F); + } + /* unicode < 65536 -> 3 byte */ + else if(unicode < 65536) { + bytes[0] = 0xE0; + bytes[0] |= unicode >> 12; + bytes[1] = 0x80; + bytes[1] |= ((unicode >> 6) & 0x003F); + bytes[2] = 0x80; + bytes[2] |= (unicode & 0x003F); + } + + return *((uint32_t *)bytes); +} + +static uint32_t _key_from_uefi_key(const EFI_KEY_DATA * key) +{ + LV_ASSERT_NULL(key); + + switch(key->Key.ScanCode) { + case 0x01: + return LV_KEY_UP; + case 0x02: + return LV_KEY_DOWN; + case 0x04: + return LV_KEY_LEFT; + case 0x03: + return LV_KEY_RIGHT; + case 0x08: + return LV_KEY_DEL; + case 0x05: + return LV_KEY_HOME; + case 0x06: + return LV_KEY_END; + case 0x17: + return LV_KEY_ESC; + /* ignore all other scan codes */ + default: + break; + } + + switch(key->Key.UnicodeChar) { + case 0x09: + return (key->KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) && + (key->KeyState.KeyShiftState & (EFI_RIGHT_SHIFT_PRESSED | EFI_LEFT_SHIFT_PRESSED)) ? + LV_KEY_PREV : + LV_KEY_NEXT; + case 0x08: + return LV_KEY_BACKSPACE; + case 0x0D: + return LV_KEY_ENTER; + case 0x18: + return LV_KEY_ESC; + default: + return _utf8_from_unicode(key->Key.UnicodeChar); + } +} + +#endif \ No newline at end of file diff --git a/src/drivers/uefi/lv_uefi_indev_pointer.c b/src/drivers/uefi/lv_uefi_indev_pointer.c new file mode 100644 index 000000000..a00b3a03e --- /dev/null +++ b/src/drivers/uefi/lv_uefi_indev_pointer.c @@ -0,0 +1,285 @@ +/** + * @file lv_uefi_indev.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lvgl.h" +#include "../../stdlib/lv_mem.h" +#include "../../misc/lv_types.h" +#include "../../misc/lv_text.h" + +#if LV_USE_UEFI + +#include "lv_uefi_indev.h" +#include "lv_uefi_private.h" + +/********************* + * DEFINES + *********************/ + +#define SIMPLE_POINTER_INDEV_SIGNATURE 0x53505449 + +/********************** + * TYPEDEFS + **********************/ + +typedef struct _lv_uefi_simple_pointer_handle_context_t { + EFI_HANDLE handle; + EFI_SIMPLE_POINTER_PROTOCOL * interface; + lv_point_t pixel_per_step_8; /**< How many pixels does the mouse cursor move in one step*/ +} lv_uefi_simple_pointer_handle_context_t; + +typedef struct _lv_uefi_simple_pointer_context_t { + uint32_t signature; /**< Has to be checked to avoid access to a different indev*/ + lv_point_t display_res; + lv_point_t position; + lv_ll_t handles; +} lv_uefi_simple_pointer_context_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void _simple_pointer_indev_event_cb(lv_event_t * e); +static void _simple_pointer_read_cb(lv_indev_t * indev, lv_indev_data_t * data); +static void _simple_pointer_handle_context_free(void * ptr); +static void _simple_pointer_context_free(lv_uefi_simple_pointer_context_t * indev_ctx); +static bool _simple_pointer_interface_is_valid(const EFI_SIMPLE_POINTER_PROTOCOL * interface); +static void _simple_pointer_read(lv_uefi_simple_pointer_context_t * indev_ctx, + lv_uefi_simple_pointer_handle_context_t * handle_ctx, bool * was_pressed); + +/********************** + * STATIC VARIABLES + **********************/ + +static EFI_GUID _uefi_guid_simple_pointer = EFI_SIMPLE_POINTER_PROTOCOL_GUID; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * @brief Create an indev object. + * @param display_res The resolution of the display in pixels, needed to scale the input. + * If NULL the resolution of the current default display will be used. + * @return The created LVGL indev object. +*/ +lv_indev_t * lv_uefi_simple_pointer_indev_create(lv_point_t * display_res) +{ + lv_indev_t * indev = NULL; + lv_uefi_simple_pointer_context_t * indev_ctx = NULL; + + indev_ctx = lv_calloc(1, sizeof(lv_uefi_simple_pointer_context_t)); + LV_ASSERT_MALLOC(indev_ctx); + + indev_ctx->signature = SIMPLE_POINTER_INDEV_SIGNATURE; + + if(display_res != NULL) { + indev_ctx->display_res.x = display_res->x; + indev_ctx->display_res.y = display_res->y; + } + else { + indev_ctx->display_res.x = lv_display_get_horizontal_resolution(lv_display_get_default()); + indev_ctx->display_res.y = lv_display_get_vertical_resolution(lv_display_get_default()); + } + + lv_ll_init(&indev_ctx->handles, sizeof(lv_uefi_simple_pointer_handle_context_t)); + + indev = lv_indev_create(); + lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); + lv_indev_set_user_data(indev, indev_ctx); + lv_indev_add_event_cb(indev, _simple_pointer_indev_event_cb, LV_EVENT_DELETE, indev); + lv_indev_set_read_cb(indev, _simple_pointer_read_cb); + + return indev; +} + +/** + * @brief Add an EFI_SIMPLE_POINTER_PROTOCOL interface to the indev. + * @param indev Indev that was created with lv_uefi_simple_pointer_indev_create. + * @param handle The handle on which an instance of the EFI_SIMPLE_POINTER_PROTOCOL protocol is installed. + * @return True if the interface was added. +*/ +bool lv_uefi_simple_pointer_indev_add_handle(lv_indev_t * indev, EFI_HANDLE handle) +{ + EFI_SIMPLE_POINTER_PROTOCOL * interface = NULL; + lv_uefi_simple_pointer_handle_context_t * handle_ctx = NULL; + + lv_uefi_simple_pointer_context_t * indev_ctx = (lv_uefi_simple_pointer_context_t *)lv_indev_get_user_data(indev); + LV_ASSERT_NULL(indev_ctx); + + if(indev_ctx->signature != SIMPLE_POINTER_INDEV_SIGNATURE) return false; + + interface = (EFI_SIMPLE_POINTER_PROTOCOL *)lv_uefi_protocol_open(handle, &_uefi_guid_simple_pointer); + if(!_simple_pointer_interface_is_valid(interface)) { + lv_uefi_protocol_close(handle, &_uefi_guid_simple_pointer); + LV_LOG_WARN("[lv_uefi] The SIMPLE_POINTER interface is not valid."); + return false; + } + + handle_ctx = (lv_uefi_simple_pointer_handle_context_t *) lv_ll_ins_head(&indev_ctx->handles); + LV_ASSERT_MALLOC(handle_ctx); + + handle_ctx->handle = handle; + handle_ctx->interface = interface; + handle_ctx->pixel_per_step_8.x = (((indev_ctx->display_res.x) << 8) / 50) / + interface->Mode->ResolutionX; + handle_ctx->pixel_per_step_8.y = (((indev_ctx->display_res.y) << 8) / 50) / + interface->Mode->ResolutionY; + + return true; +} + +/** + * @brief Add all available EFI_SIMPLE_POINTER_PROTOCOL interfaces to the indev. + * @param indev Indev that was created with lv_uefi_simple_pointer_indev_create. +*/ +void lv_uefi_simple_pointer_indev_add_all(lv_indev_t * indev) +{ + EFI_STATUS status; + EFI_HANDLE * handles = NULL; + UINTN no_handles; + UINTN index; + + lv_uefi_simple_pointer_context_t * indev_ctx = (lv_uefi_simple_pointer_context_t *)lv_indev_get_user_data(indev); + LV_ASSERT_NULL(indev_ctx); + + if(indev_ctx->signature != SIMPLE_POINTER_INDEV_SIGNATURE) return; + + status = gLvEfiBS->LocateHandleBuffer(ByProtocol, &_uefi_guid_simple_pointer, NULL, &no_handles, &handles); + if(status != EFI_SUCCESS) { + LV_LOG_ERROR("[lv_uefi] LocateHandleBuffer(SIMPLE_POINTER) failed with error code %llx.", status); + return; + } + + for(index = 0; index < no_handles; index++) { + lv_uefi_simple_pointer_indev_add_handle(indev, handles[index]); + } + + if(handles != NULL) gLvEfiBS->FreePool(handles); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void _simple_pointer_indev_event_cb(lv_event_t * e) +{ + lv_indev_t * indev; + lv_uefi_simple_pointer_context_t * indev_ctx; + + if(lv_event_get_code(e) != LV_EVENT_DELETE) return; + + indev = (lv_indev_t *)lv_event_get_user_data(e); + if(indev == NULL) return; + + indev_ctx = (lv_uefi_simple_pointer_context_t *)lv_indev_get_user_data(indev); + lv_indev_set_user_data(indev, NULL); + + if(indev_ctx != NULL) _simple_pointer_context_free(indev_ctx); +} + +static void _simple_pointer_read_cb(lv_indev_t * indev, lv_indev_data_t * data) +{ + void * node = NULL; + + lv_uefi_simple_pointer_context_t * indev_ctx = (lv_uefi_simple_pointer_context_t *)lv_indev_get_user_data(indev); + LV_ASSERT_NULL(indev_ctx); + + /* Read from all registered devices */ + for(node = lv_ll_get_head(&indev_ctx->handles); node != NULL; node = lv_ll_get_next(&indev_ctx->handles, node)) { + lv_uefi_simple_pointer_handle_context_t * handle_ctx = (lv_uefi_simple_pointer_handle_context_t *) node; + bool was_pressed = false; + + _simple_pointer_read(indev_ctx, handle_ctx, &was_pressed); + + data->state |= was_pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; + } + + /* Sanitize the events position */ + if(indev_ctx->position.x < 0) { + indev_ctx->position.x = 0; + } + else if(indev_ctx->position.x > indev_ctx->display_res.x - 1) { + indev_ctx->position.x = indev_ctx->display_res.x - 1; + } + + if(indev_ctx->position.y < 0) { + indev_ctx->position.y = 0; + } + else if(indev_ctx->position.y > indev_ctx->display_res.y - 1) { + indev_ctx->position.y = indev_ctx->display_res.y - 1; + } + + data->point.x = indev_ctx->position.x; + data->point.y = indev_ctx->position.y; + + data->continue_reading = FALSE; +} + +static void _simple_pointer_context_free(lv_uefi_simple_pointer_context_t * indev_ctx) +{ + if(indev_ctx == NULL) return; + lv_ll_clear_custom(&indev_ctx->handles, _simple_pointer_handle_context_free); + lv_free(indev_ctx); +} + +static bool _simple_pointer_interface_is_valid(const EFI_SIMPLE_POINTER_PROTOCOL * interface) +{ + if(interface == NULL) return FALSE; + if(interface->Mode == NULL) return FALSE; + if(interface->Mode->ResolutionX == 0) return FALSE; + if(interface->Mode->ResolutionX >= 256) return FALSE; + if(interface->Mode->ResolutionY == 0) return FALSE; + if(interface->Mode->ResolutionY >= 256) return FALSE; + return TRUE; +} + +static void _simple_pointer_handle_context_free(void * ptr) +{ + lv_uefi_simple_pointer_handle_context_t * handle_ctx = (lv_uefi_simple_pointer_handle_context_t *)ptr; + + if(handle_ctx == NULL) return; + if(handle_ctx->interface) lv_uefi_protocol_close(handle_ctx->handle, &_uefi_guid_simple_pointer); + lv_free(handle_ctx); +} + +static void _simple_pointer_read(lv_uefi_simple_pointer_context_t * indev_ctx, + lv_uefi_simple_pointer_handle_context_t * handle_ctx, bool * was_pressed) +{ + EFI_STATUS status; + EFI_SIMPLE_POINTER_STATE state; + lv_point_t pointer_mov; + + LV_ASSERT_NULL(indev_ctx); + LV_ASSERT_NULL(handle_ctx); + LV_ASSERT_NULL(was_pressed); + + status = handle_ctx->interface->GetState( + handle_ctx->interface, + &state); + if(status == EFI_NOT_READY) return; + if(status != EFI_SUCCESS) { + LV_LOG_ERROR("[lv_uefi] GetState failed."); + return; + } + + pointer_mov.x = (state.RelativeMovementX * handle_ctx->pixel_per_step_8.x) >> 8; + pointer_mov.y = (state.RelativeMovementY * handle_ctx->pixel_per_step_8.y) >> 8; + + indev_ctx->position.x += pointer_mov.x; + indev_ctx->position.y += pointer_mov.y; + + /* Set the state to pressed if one of the interfaces reports a press */ + *was_pressed = state.LeftButton; +} + +#endif \ No newline at end of file diff --git a/src/drivers/uefi/lv_uefi_indev_touch.c b/src/drivers/uefi/lv_uefi_indev_touch.c new file mode 100644 index 000000000..3fad4f087 --- /dev/null +++ b/src/drivers/uefi/lv_uefi_indev_touch.c @@ -0,0 +1,290 @@ +/** + * @file lv_uefi_indev.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lvgl.h" +#include "../../stdlib/lv_mem.h" +#include "../../misc/lv_types.h" +#include "../../misc/lv_text.h" + +#if LV_USE_UEFI + +#include "lv_uefi_indev.h" +#include "lv_uefi_private.h" + +/********************* + * DEFINES + *********************/ + +#define ABSOLUTE_POINTER_INDEV_SIGNATURE 0x41505449 + +/********************** + * TYPEDEFS + **********************/ + +typedef struct _lv_uefi_absolute_pointer_handle_context_t { + EFI_HANDLE handle; + EFI_ABSOLUTE_POINTER_PROTOCOL * interface; + lv_point_t range; /**< The touchscreen resolution*/ + lv_point_t factor_8; /**< The scaling factor between the touchscreen and the display resolution*/ +} lv_uefi_absolute_pointer_handle_context_t; + +typedef struct _lv_uefi_absolute_pointer_context_t { + uint32_t signature; /**< Has to be checked to avoid access to a different indev*/ + lv_point_t display_res; + lv_point_t position; + lv_ll_t handles; +} lv_uefi_absolute_pointer_context_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void _absolute_pointer_indev_event_cb(lv_event_t * e); +static void _absolute_pointer_read_cb(lv_indev_t * indev, lv_indev_data_t * data); +static void _absolute_pointer_handle_context_free(void * ptr); +static void _absolute_pointer_context_free(lv_uefi_absolute_pointer_context_t * indev_ctx); +static bool _absolute_pointer_interface_is_valid(const EFI_ABSOLUTE_POINTER_PROTOCOL * interface); +static void _absolute_pointer_read(lv_uefi_absolute_pointer_context_t * indev_ctx, + lv_uefi_absolute_pointer_handle_context_t * handle_ctx, bool * was_pressed); + +/********************** + * STATIC VARIABLES + **********************/ + +static EFI_GUID _uefi_guid_absolute_pointer = EFI_ABSOLUTE_POINTER_PROTOCOL_GUID; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * @brief Create a LVGL indev object. + * @param display_res The resolution of the display in pixels, needed to scale the input. + * @return The created LVGL indev object. +*/ +lv_indev_t * lv_uefi_absolute_pointer_indev_create(lv_point_t * display_res) +{ + lv_indev_t * indev = NULL; + lv_uefi_absolute_pointer_context_t * indev_ctx = NULL; + + indev_ctx = lv_calloc(1, sizeof(lv_uefi_absolute_pointer_context_t)); + LV_ASSERT_MALLOC(indev_ctx); + + indev_ctx->signature = ABSOLUTE_POINTER_INDEV_SIGNATURE; + + if(display_res != NULL) { + indev_ctx->display_res.x = display_res->x; + indev_ctx->display_res.y = display_res->y; + } + else { + indev_ctx->display_res.x = lv_display_get_horizontal_resolution(lv_display_get_default()); + indev_ctx->display_res.y = lv_display_get_vertical_resolution(lv_display_get_default()); + } + + lv_ll_init(&indev_ctx->handles, sizeof(lv_uefi_absolute_pointer_handle_context_t)); + + indev = lv_indev_create(); + lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); + lv_indev_set_user_data(indev, indev_ctx); + lv_indev_add_event_cb(indev, _absolute_pointer_indev_event_cb, LV_EVENT_DELETE, indev); + lv_indev_set_read_cb(indev, _absolute_pointer_read_cb); + + return indev; +} + +/** + * @brief Add an EFI_ABSOLUTE_POINTER_PROTOCOL interface to the indev. + * @param indev Indev that was created with lv_uefi_absolute_pointer_indev_create. + * @param handle The handle on which an instance of the EFI_ABSOLUTE_POINTER_PROTOCOL protocol is installed. + * @return True if the interface was added. +*/ +bool lv_uefi_absolute_pointer_indev_add_handle(lv_indev_t * indev, EFI_HANDLE handle) +{ + EFI_ABSOLUTE_POINTER_PROTOCOL * interface = NULL; + lv_uefi_absolute_pointer_handle_context_t * handle_ctx = NULL; + + lv_uefi_absolute_pointer_context_t * indev_ctx = (lv_uefi_absolute_pointer_context_t *)lv_indev_get_user_data(indev); + LV_ASSERT_NULL(indev_ctx); + + if(indev_ctx->signature != ABSOLUTE_POINTER_INDEV_SIGNATURE) return false; + + interface = (EFI_ABSOLUTE_POINTER_PROTOCOL *)lv_uefi_protocol_open(handle, &_uefi_guid_absolute_pointer); + if(!_absolute_pointer_interface_is_valid(interface)) { + lv_uefi_protocol_close(handle, &_uefi_guid_absolute_pointer); + LV_LOG_WARN("[lv_uefi] The ABSOLUTE_POINTER interface is not valid."); + return false; + } + + handle_ctx = (lv_uefi_absolute_pointer_handle_context_t *) lv_ll_ins_head(&indev_ctx->handles); + LV_ASSERT_MALLOC(handle_ctx); + + handle_ctx->handle = handle; + handle_ctx->interface = interface; + handle_ctx->range.x = handle_ctx->interface->Mode->AbsoluteMaxX - + handle_ctx->interface->Mode->AbsoluteMinX; + handle_ctx->range.y = handle_ctx->interface->Mode->AbsoluteMaxY - + handle_ctx->interface->Mode->AbsoluteMinY; + + handle_ctx->factor_8.x = (indev_ctx->display_res.x << 8) / handle_ctx->range.x; + handle_ctx->factor_8.y = (indev_ctx->display_res.y << 8) / handle_ctx->range.y; + + return true; +} + +/** + * @brief Add all available EFI_ABSOLUTE_POINTER_PROTOCOL interfaces to the indev. + * @param indev Indev that was created with lv_uefi_absolute_pointer_indev_create. +*/ +void lv_uefi_absolute_pointer_indev_add_all(lv_indev_t * indev) +{ + EFI_STATUS status; + EFI_HANDLE * handles = NULL; + UINTN no_handles; + UINTN index; + + lv_uefi_absolute_pointer_context_t * indev_ctx = (lv_uefi_absolute_pointer_context_t *)lv_indev_get_user_data(indev); + LV_ASSERT_NULL(indev_ctx); + + if(indev_ctx->signature != ABSOLUTE_POINTER_INDEV_SIGNATURE) return; + + status = gLvEfiBS->LocateHandleBuffer(ByProtocol, &_uefi_guid_absolute_pointer, NULL, &no_handles, &handles); + if(status != EFI_SUCCESS) { + LV_LOG_ERROR("[lv_uefi] LocateHandleBuffer(ABSOLUTE_POINTER) failed with error code %llx.", status); + return; + } + + for(index = 0; index < no_handles; index++) { + lv_uefi_absolute_pointer_indev_add_handle(indev, handles[index]); + } + + if(handles != NULL) gLvEfiBS->FreePool(handles); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void _absolute_pointer_indev_event_cb(lv_event_t * e) +{ + lv_indev_t * indev; + lv_uefi_absolute_pointer_context_t * indev_ctx; + + if(lv_event_get_code(e) != LV_EVENT_DELETE) return; + + indev = (lv_indev_t *)lv_event_get_user_data(e); + if(indev == NULL) return; + + indev_ctx = (lv_uefi_absolute_pointer_context_t *)lv_indev_get_user_data(indev); + lv_indev_set_user_data(indev, NULL); + + if(indev_ctx != NULL) _absolute_pointer_context_free(indev_ctx); +} + +static void _absolute_pointer_read_cb(lv_indev_t * indev, lv_indev_data_t * data) +{ + void * node = NULL; + + lv_uefi_absolute_pointer_context_t * indev_ctx = (lv_uefi_absolute_pointer_context_t *)lv_indev_get_user_data(indev); + LV_ASSERT_NULL(indev_ctx); + + /* Read from all registered devices */ + for(node = lv_ll_get_head(&indev_ctx->handles); node != NULL; node = lv_ll_get_next(&indev_ctx->handles, node)) { + lv_uefi_absolute_pointer_handle_context_t * handle_ctx = (lv_uefi_absolute_pointer_handle_context_t *) node; + bool was_pressed = false; + + _absolute_pointer_read(indev_ctx, handle_ctx, &was_pressed); + + data->state |= was_pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; + } + + /* Sanitize the events position */ + if(indev_ctx->position.x < 0) { + indev_ctx->position.x = 0; + } + else if(indev_ctx->position.x > indev_ctx->display_res.x - 1) { + indev_ctx->position.x = indev_ctx->display_res.x - 1; + } + + if(indev_ctx->position.y < 0) { + indev_ctx->position.y = 0; + } + else if(indev_ctx->position.y > indev_ctx->display_res.y - 1) { + indev_ctx->position.y = indev_ctx->display_res.y - 1; + } + + data->point.x = indev_ctx->position.x; + data->point.y = indev_ctx->position.y; + + data->continue_reading = FALSE; +} + +static void _absolute_pointer_context_free(lv_uefi_absolute_pointer_context_t * indev_ctx) +{ + if(indev_ctx == NULL) return; + lv_ll_clear_custom(&indev_ctx->handles, _absolute_pointer_handle_context_free); + lv_free(indev_ctx); +} + +static bool _absolute_pointer_interface_is_valid(const EFI_ABSOLUTE_POINTER_PROTOCOL * interface) +{ + if(interface == NULL) return FALSE; + if(interface->Mode == NULL) return FALSE; + if(interface->Mode->AbsoluteMaxX <= interface->Mode->AbsoluteMinX) return FALSE; + if(interface->Mode->AbsoluteMaxY <= interface->Mode->AbsoluteMinY) return FALSE; + return TRUE; +} + +static void _absolute_pointer_handle_context_free(void * ptr) +{ + lv_uefi_absolute_pointer_handle_context_t * handle_ctx = (lv_uefi_absolute_pointer_handle_context_t *) ptr; + + if(handle_ctx == NULL) return; + if(handle_ctx->interface) lv_uefi_protocol_close(handle_ctx->handle, &_uefi_guid_absolute_pointer); + lv_free(handle_ctx); +} + +static void _absolute_pointer_read(lv_uefi_absolute_pointer_context_t * indev_ctx, + lv_uefi_absolute_pointer_handle_context_t * handle_ctx, bool * was_pressed) +{ + EFI_STATUS status; + EFI_ABSOLUTE_POINTER_STATE state; + lv_point_t pointer_pos; + + LV_ASSERT_NULL(indev_ctx); + LV_ASSERT_NULL(handle_ctx); + LV_ASSERT_NULL(was_pressed); + + status = handle_ctx->interface->GetState( + handle_ctx->interface, + &state); + if(status == EFI_NOT_READY) return; + if(status != EFI_SUCCESS) { + LV_LOG_ERROR("[lv_uefi] GetState failed."); + return; + } + + /* verify the state */ + if(state.CurrentX < handle_ctx->interface->Mode->AbsoluteMinX) return; + if(state.CurrentY < handle_ctx->interface->Mode->AbsoluteMinY) return; + + pointer_pos.x = state.CurrentX - handle_ctx->interface->Mode->AbsoluteMinX; + pointer_pos.y = state.CurrentY - handle_ctx->interface->Mode->AbsoluteMinY; + + indev_ctx->position.x = (pointer_pos.x * handle_ctx->factor_8.x) >> 8; + indev_ctx->position.y = (pointer_pos.y * handle_ctx->factor_8.y) >> 8; + + /* Set the state to pressed if one of the interfaces reports a press */ + *was_pressed = (state.ActiveButtons & EFI_ABSP_TouchActive) != 0; +} + +#endif \ No newline at end of file diff --git a/src/drivers/uefi/lv_uefi_private.c b/src/drivers/uefi/lv_uefi_private.c new file mode 100644 index 000000000..2d06a90fb --- /dev/null +++ b/src/drivers/uefi/lv_uefi_private.c @@ -0,0 +1,225 @@ +/** + * @file lv_uefi_private.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lvgl.h" + +#if LV_USE_UEFI + +#include "lv_uefi_private.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * GOLBAL VARIABLES + **********************/ +EFI_HANDLE gLvEfiImageHandle = NULL; +EFI_SYSTEM_TABLE * gLvEfiST = NULL; +EFI_BOOT_SERVICES * gLvEfiBS = NULL; +EFI_RUNTIME_SERVICES * gLvEfiRT = NULL; + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * @brief Test if a protocol is installed at a handle. + * @param handle The handle on which the protocol might be installed. + * @param protocol The guid of the protocol. + * @return TRUE if the protocol is installed, FALSE if not. +*/ +bool lv_uefi_protocol_test(EFI_HANDLE handle, EFI_GUID * protocol) +{ + EFI_STATUS status; + void * interface = NULL; + + if(handle == NULL) return false; + if(protocol == NULL) return false; + + status = gLvEfiBS->OpenProtocol( + handle, + protocol, + &interface, + gLvEfiImageHandle, + NULL, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL); + if(status != EFI_SUCCESS && status != EFI_UNSUPPORTED) { + LV_LOG_WARN("couldn't test protocol"); + return FALSE; + } + else if(status == EFI_UNSUPPORTED) { + return FALSE; + } + + return TRUE; +} + +/** + * @brief Open a protocol. + * @param handle The handle on which the protocol is installed. + * @param protocol The guid of the protocol. + * @return A pointer to the interface, NULL if the protocol couldn't be opened. +*/ +void * lv_uefi_protocol_open(EFI_HANDLE handle, EFI_GUID * protocol) +{ + EFI_STATUS status; + void * interface = NULL; + + if(handle == NULL) return NULL; + if(protocol == NULL) return NULL; + + status = gLvEfiBS->OpenProtocol( + handle, + protocol, + &interface, + gLvEfiImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if(status != EFI_SUCCESS) { + LV_LOG_ERROR("[lv_uefi] Couldn't open protocol %08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X, error code: %llx.", + protocol->Data1, + protocol->Data2, + protocol->Data3, + protocol->Data4[0], + protocol->Data4[1], + protocol->Data4[2], + protocol->Data4[3], + protocol->Data4[4], + protocol->Data4[5], + protocol->Data4[6], + protocol->Data4[7], + status); + interface = NULL; + } + + return interface; +} + +/** + * @brief Close a protocol. + * @param handle The handle on which the protocol is installed. + * @param protocol The guid of the protocol. +*/ +void lv_uefi_protocol_close(EFI_HANDLE handle, EFI_GUID * protocol) +{ + EFI_STATUS status; + + if(handle == NULL) return; + if(protocol == NULL) return; + + status = gLvEfiBS->CloseProtocol( + handle, + protocol, + gLvEfiImageHandle, + NULL); + if(status != EFI_SUCCESS) { + LV_LOG_WARN("[lv_uefi] Couldn't close protocol %08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X, error code: %llx.", + protocol->Data1, + protocol->Data2, + protocol->Data3, + protocol->Data4[0], + protocol->Data4[1], + protocol->Data4[2], + protocol->Data4[3], + protocol->Data4[4], + protocol->Data4[5], + protocol->Data4[6], + protocol->Data4[7], + status); + } +} + +/** + * @brief Convert an UCS-2 string to an ASCII string. + * The string must contain only characters >= 0x20 and <= 0X7E. + * @param ucs2 The UCS-2 string. + * @param ascii The buffer to store the ASCII string. + * @param ascii_len The size of the buffer in ASCII characters. + * @return The number of characters written to the buffer or 0 if + * there was an error. +*/ +size_t lv_uefi_ucs2_to_ascii(const CHAR16 * ucs2, char * ascii, size_t ascii_len) +{ + size_t invalid_character_count; + size_t string_index; + + if(ucs2 == NULL || ascii == NULL || ascii_len == 0) { + return 0; + } + + invalid_character_count = 0; + + for(string_index = 0; ucs2[string_index] != 0x0000 && string_index < ascii_len - 1; string_index++) { + if(ucs2[string_index] < 0x20 || ucs2[string_index] > 0x7E) { + invalid_character_count++; + } + ascii[string_index] = (char) ucs2[string_index]; + } + + /* terminate the string even if there was an error */ + ascii[string_index] = 0x00; + + return invalid_character_count == 0 ? string_index : 0; +} + +/** + * @brief Convert an ASCII string to an UCS-2 string. + * The string must contain only characters >= 0x20 and <= 0X7E. + * @param ascii The ASCII string. + * @param ucs2 The buffer to store the UCS-2 string. + * @param ucs2_len The size of the buffer in UCS-2 characters. + * @return The number of bytes written to the buffer or 0 if + * there was an error. +*/ +size_t lv_uefi_ascii_to_ucs2(const char * ascii, CHAR16 * ucs2, size_t ucs2_len) +{ + size_t invalid_character_count; + size_t string_index; + + if(ascii == NULL || ucs2 == NULL || ucs2_len == 0) { + return 0; + } + + invalid_character_count = 0; + + for(string_index = 0; ascii[string_index] != 0x0000 && string_index < ucs2_len - 1; string_index++) { + if(ascii[string_index] < 0x20 || ascii[string_index] > 0x7E) { + invalid_character_count++; + } + ucs2[string_index] = (CHAR16) ascii[string_index]; + } + + /* terminate the string even if there was an error */ + ucs2[string_index] = 0x0000; + + return invalid_character_count == 0 ? string_index : 0; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif \ No newline at end of file diff --git a/src/drivers/uefi/lv_uefi_private.h b/src/drivers/uefi/lv_uefi_private.h new file mode 100644 index 000000000..7f917ce40 --- /dev/null +++ b/src/drivers/uefi/lv_uefi_private.h @@ -0,0 +1,107 @@ +/** + * @file lv_uefi_private.h + * + */ + +#ifndef __LV_UEFI_PRIVATE_H__ +#define __LV_UEFI_PRIVATE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lvgl.h" + +#if LV_USE_UEFI + +#include "lv_uefi.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Internal cache for the image handle (source: application entry point) + */ +extern EFI_HANDLE gLvEfiImageHandle; +/** + * Internal cache for the system table (source: application entry point) + */ +extern EFI_SYSTEM_TABLE * gLvEfiST; +/** + * Internal cache for the boot services table (source: gLvEfiST) + */ +extern EFI_BOOT_SERVICES * gLvEfiBS; +/** + * Internal cache for the boot runtime service table (source: gLvEfiST) + */ +extern EFI_RUNTIME_SERVICES * gLvEfiRT; + +/** + * @brief Test if a protocol is installed at a handle. + * @param handle The handle on which the protocol might be installed. + * @param protocol The guid of the protocol. + * @return TRUE if the protocol is installed, FALSE if not. +*/ +bool lv_uefi_protocol_test(EFI_HANDLE handle, EFI_GUID * protocol); + +/** + * @brief Open a protocol. + * @param handle The handle on which the protocol is installed. + * @param protocol The guid of the protocol. + * @return A pointer to the interface, NULL if the protocol couldn't be opened. +*/ +void * lv_uefi_protocol_open(EFI_HANDLE handle, EFI_GUID * protocol); + +/** + * @brief Close a protocol. + * @param handle The handle on which the protocol is installed. + * @param protocol The guid of the protocol. +*/ +void lv_uefi_protocol_close(EFI_HANDLE handle, EFI_GUID * protocol); + +/** + * @brief Convert an UCS-2 string to an ASCII string. + * The string must contain only characters >= 0x20 and <= 0X7E. + * @param ucs2 The UCS-2 string. + * @param ascii The buffer to store the ASCII string. + * @param ascii_len The size of the buffer in ASCII characters. + * @return The number of characters written to the buffer or 0 if + * there was an error. +*/ +size_t lv_uefi_ucs2_to_ascii(const CHAR16 * ucs2, char * ascii, size_t ascii_len); + +/** + * @brief Convert an ASCII string to an UCS-2 string. + * The string must contain only characters >= 0x20 and <= 0X7E. + * @param ascii The ASCII string. + * @param ucs2 The buffer to store the UCS-2 string. + * @param ucs2_len The size of the buffer in UCS-2 characters. + * @return The number of bytes written to the buffer or 0 if + * there was an error. +*/ +size_t lv_uefi_ascii_to_ucs2(const char * ascii, CHAR16 * ucs2, size_t ucs2_len); + +/********************** + * MACROS + **********************/ + +#endif + +#ifdef __cplusplus +} +#endif + +#endif //__LV_UEFI_PRIVATE_H__ \ No newline at end of file diff --git a/src/drivers/uefi/lv_uefi_std_wrapper.h b/src/drivers/uefi/lv_uefi_std_wrapper.h new file mode 100644 index 000000000..c5fb5cc8b --- /dev/null +++ b/src/drivers/uefi/lv_uefi_std_wrapper.h @@ -0,0 +1,155 @@ +/** + * @file lv_uefi_std_wrapper.h + * + */ + +#ifndef LV_UEFI_STD_WRAPPER_H +#define LV_UEFI_STD_WRAPPER_H + +#if LV_USE_UEFI + + #include LV_USE_UEFI_INCLUDE + + /************************************* + * TYPES + *************************************/ + typedef UINT8 uint8_t; + typedef UINT16 uint16_t; + typedef UINT32 uint32_t; + typedef UINT64 uint64_t; + typedef INT8 int8_t; + typedef INT16 int16_t; + typedef INT32 int32_t; + typedef INT64 int64_t; + + typedef uint32_t uint_fast32_t; + typedef UINTN uintptr_t; + typedef UINTN size_t; + typedef INTN intptr_t; + typedef INTN intmax_t; + typedef INTN ptrdiff_t; + + typedef UINT8 bool; + + /************************************* + * DEFINES + *************************************/ + #define false 0 + #define true 1 + + #define PRId8 "d" + #define PRId16 "d" + #define PRId32 "d" + #define PRId64 "d" + + #define PRIu8 "u" + #define PRIu16 "u" + #define PRIu32 "u" + #define PRIu64 "u" + + #define PRIx8 "x" + #define PRIx16 "x" + #define PRIx32 "x" + #define PRIx64 "x" + + #define PRIX8 "X" + #define PRIX16 "X" + #define PRIX32 "X" + #define PRIX64 "X" + + /************************************* + * LIMITS + *************************************/ + #ifndef INT8_MAX + #define INT8_MAX (0x7F) + #endif + + #ifndef UINT8_MAX + #define UINT8_MAX (0xFF) + #endif + + #ifndef INT16_MAX + #define INT16_MAX (0x7FFF) + #endif + + #ifndef UINT16_MAX + #define UINT16_MAX (0xFFFF) + #endif + + #ifndef INT32_MAX + #define INT32_MAX (0x7FFFFFFF) + #endif + + #ifndef UINT32_MAX + #define UINT32_MAX (0xFFFFFFFF) + #endif + + #ifndef INT64_MAX + #define INT64_MAX (0x7FFFFFFFFFFFFFFFULL) + #endif + + #ifndef UINT64_MAX + #define UINT64_MAX (0xFFFFFFFFFFFFFFFFULL) + #endif + + #ifndef INT_MAX + #define INT_MAX (0x7FFFFFFFFFFFFFFFULL) + #endif + + #ifndef UINT_MAX + #define UINT_MAX (0xFFFFFFFFFFFFFFFFULL) + #endif + + /// + /// Minimum values for the signed UEFI Data Types + /// + #ifndef INT8_MIN + #define INT8_MIN (( -127) - 1) + #endif + + #ifndef INT16_MIN + #define INT16_MIN (( -32767) - 1) + #endif + + #ifndef INT32_MIN + #define INT32_MIN (( -2147483647) - 1) + #endif + + #ifndef INT64_MIN + #define INT64_MIN (( -9223372036854775807LL) - 1) + #endif + + #ifndef SIZE_MAX + #define SIZE_MAX (0xFFFFFFFF) + #endif + + #ifndef LONG_MAX + #define LONG_MAX (0x7FFFFFFF) + #endif + + #ifndef ULONG_MAX + #define ULONG_MAX (0xFFFFFFFF) + #endif + + #ifndef USHRT_MAX + #define USHRT_MAX (0xFFFF) + #endif + + #ifndef CHAR_BIT + #define CHAR_BIT 8 + #endif + + /************************************* + * VA_ARG + *************************************/ + #if LV_UEFI_EDK2_HEADERS + #define va_list VA_LIST + #define va_start(Marker, Parameter) VA_START(Marker, Parameter) + #define va_arg(Marker, TYPE) VA_ARG(Marker, TYPE) + #define va_end(Marker) VA_END(Marker) + #define va_copy(Dest, Start) VA_COPY(Dest, Start) + #endif + +#endif + +#endif \ No newline at end of file diff --git a/src/libs/fsdrv/lv_fs_uefi.c b/src/libs/fsdrv/lv_fs_uefi.c new file mode 100644 index 000000000..a91c3d5a2 --- /dev/null +++ b/src/libs/fsdrv/lv_fs_uefi.c @@ -0,0 +1,607 @@ +/** + * @file lv_fs_uefi.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../../../lvgl.h" + +#if LV_USE_FS_UEFI && LV_USE_UEFI + +#include "../../drivers/uefi/lv_uefi_private.h" + +#include "../../core/lv_global.h" + +/********************* + * DEFINES + *********************/ +#if LV_FS_UEFI_LETTER == '\0' + #error "LV_FS_UEFI_LETTER must be set to a valid value" +#else + #if (LV_FS_UEFI_LETTER < 'A') || (LV_FS_UEFI_LETTER > 'Z') + #if LV_FS_DEFAULT_DRIVE_LETTER != '\0' /*When using default drive letter, strict format (X:) is mandatory*/ + #error "LV_FS_UEFI_LETTER must be an upper case ASCII letter" + #else /*Lean rules for backward compatibility*/ + #warning LV_FS_UEFI_LETTER should be an upper case ASCII letter. \ + Using a slash symbol as drive letter should be replaced with LV_FS_DEFAULT_DRIVE_LETTER mechanism + #endif + #endif +#endif + +/********************** + * TYPEDEFS + **********************/ + +typedef struct _lv_uefi_fs_file_context_t { + EFI_FILE_PROTOCOL * interface; +} lv_uefi_fs_file_context_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void lv_fs_drv_uefi_init(lv_fs_drv_t * drv, char fs_drive_letter, EFI_HANDLE fs_handle); +static void lv_fs_drv_uefi_deinit(lv_fs_drv_t * drv); + +static void lv_fs_uefi_lvgl_path_to_uefi_path(CHAR16 * path); +static void lv_fs_uefi_uefi_path_to_lvgl_path(CHAR16 * path); +static bool lv_fs_uefi_is_dot_path(CONST CHAR16 * path); +static bool lv_fs_uefi_is_dir(EFI_FILE_PROTOCOL * dir); +static bool lv_fs_uefi_is_file(EFI_FILE_PROTOCOL * file); +static EFI_FILE_INFO * lv_fs_uefi_get_info(EFI_FILE_PROTOCOL * file); + +static bool lv_fs_uefi_ready_cb(lv_fs_drv_t * drv); + +static void * lv_fs_uefi_open_cb(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode); +static lv_fs_res_t lv_fs_uefi_close_cb(lv_fs_drv_t * drv, void * file_p); +static lv_fs_res_t lv_fs_uefi_read_cb(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br); +static lv_fs_res_t lv_fs_uefi_write_cb(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw); +static lv_fs_res_t lv_fs_uefi_seek_cb(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence); +static lv_fs_res_t lv_fs_uefi_tell_cb(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p); + +static void * lv_fs_uefi_dir_open_cb(lv_fs_drv_t * drv, const char * path); +static lv_fs_res_t lv_fs_uefi_dir_read_cb(lv_fs_drv_t * drv, void * rddir_p, char * fn, uint32_t fn_len); +static lv_fs_res_t lv_fs_uefi_dir_close_cb(lv_fs_drv_t * drv, void * rddir_p); + +/********************** + * STATIC VARIABLES + **********************/ + +static EFI_GUID _uefi_guid_simple_file_system = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; +static EFI_GUID _uefi_guid_loaded_image = EFI_LOADED_IMAGE_PROTOCOL_GUID; +static EFI_GUID _uefi_guid_file_info = EFI_FILE_INFO_ID; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Register a driver for the File system interface + */ +void lv_fs_uefi_init(void) +{ + EFI_LOADED_IMAGE_PROTOCOL * interface_loaded_image = NULL; + EFI_HANDLE fs_handle = NULL; + + /*--------------------------------------------------- + * Register the file system interface in LVGL + *--------------------------------------------------*/ + + interface_loaded_image = lv_uefi_protocol_open(gLvEfiImageHandle, &_uefi_guid_loaded_image); + LV_ASSERT_NULL(interface_loaded_image); + + fs_handle = interface_loaded_image->DeviceHandle; + + if(fs_handle == NULL) return; + + lv_uefi_protocol_close(gLvEfiImageHandle, &_uefi_guid_loaded_image); + + /*Add a simple driver to open images*/ + lv_fs_drv_t * fs_drv_p = &(LV_GLOBAL_DEFAULT()->uefi_fs_drv); + lv_fs_drv_uefi_init(fs_drv_p, LV_FS_UEFI_LETTER, fs_handle); + + lv_fs_drv_register(fs_drv_p); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static bool lv_fs_uefi_ready_cb(lv_fs_drv_t * drv) +{ + EFI_HANDLE fs_handle = (EFI_HANDLE)drv->user_data; + + return fs_handle != NULL; +} + +static void * lv_fs_uefi_open_cb(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode) +{ + EFI_STATUS status; + EFI_FILE_PROTOCOL * fs_root = NULL; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL * fs_interface = NULL; + CHAR16 path_ucs2[LV_FS_MAX_PATH_LENGTH + 1]; + lv_uefi_fs_file_context_t * file_ctx = NULL; + UINT64 uefi_mode = 0; + + EFI_HANDLE fs_handle = (EFI_HANDLE)drv->user_data; + + fs_interface = lv_uefi_protocol_open(fs_handle, &_uefi_guid_simple_file_system); + if(fs_interface == NULL) { + LV_LOG_WARN("[lv_uefi] Unable to open file system protocol."); + goto error; + } + + status = fs_interface->OpenVolume(fs_interface, &fs_root); + if(status != EFI_SUCCESS) { + LV_LOG_WARN("[lv_uefi] Unable to open file system root."); + goto error; + } + + if(lv_uefi_ascii_to_ucs2(path, path_ucs2, LV_FS_MAX_PATH_LENGTH + 1) == 0) { + LV_LOG_WARN("[lv_uefi] Unable to convert the ASCII path into an UCS-2 path."); + goto error; + } + + lv_fs_uefi_lvgl_path_to_uefi_path(path_ucs2); + + file_ctx = lv_calloc(1, sizeof(lv_uefi_fs_file_context_t)); + LV_ASSERT_MALLOC(file_ctx); + + if(mode == LV_FS_MODE_WR) { + uefi_mode = EFI_FILE_MODE_CREATE | EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE; + } + else { + uefi_mode = EFI_FILE_MODE_READ; + } + + status = fs_root->Open( + fs_root, + &file_ctx->interface, + path_ucs2, + uefi_mode, + 0); + if(status != EFI_SUCCESS) { + LV_LOG_WARN("[lv_uefi] Unable to open file '%s'.", path); + goto error; + } + + if(!lv_fs_uefi_is_file(file_ctx->interface)) { + goto error; + } + + goto finish; + +error: + if(file_ctx != NULL) { + if(file_ctx->interface != NULL) file_ctx->interface->Close(file_ctx->interface); + lv_free(file_ctx); + file_ctx = NULL; + } + +finish: + if(fs_interface != NULL) lv_uefi_protocol_close(fs_handle, &_uefi_guid_simple_file_system); + if(fs_root != NULL) fs_root->Close(fs_root); + + return file_ctx; +} + +static lv_fs_res_t lv_fs_uefi_close_cb(lv_fs_drv_t * drv, void * file_p) +{ + EFI_STATUS status; + lv_uefi_fs_file_context_t * file_ctx = (lv_uefi_fs_file_context_t *)file_p; + + if(file_ctx == NULL || file_ctx->interface == NULL) return LV_FS_RES_INV_PARAM; + + status = file_ctx->interface->Close(file_ctx->interface); + if(status != EFI_SUCCESS) return LV_FS_RES_HW_ERR; + + lv_free(file_ctx); + + return LV_FS_RES_OK; +} + +static lv_fs_res_t lv_fs_uefi_read_cb(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br) +{ + EFI_STATUS status; + lv_uefi_fs_file_context_t * file_ctx = (lv_uefi_fs_file_context_t *)file_p; + UINTN buf_size = btr; + + if(file_ctx == NULL || file_ctx->interface == NULL) return LV_FS_RES_INV_PARAM; + + status = file_ctx->interface->Read( + file_ctx->interface, + &buf_size, + buf); + if(status != EFI_SUCCESS) return LV_FS_RES_HW_ERR; + + *br = (uint32_t) buf_size; + + return LV_FS_RES_OK; +} + +static lv_fs_res_t lv_fs_uefi_write_cb(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw) +{ + EFI_STATUS status; + lv_uefi_fs_file_context_t * file_ctx = (lv_uefi_fs_file_context_t *)file_p; + UINTN buf_size = btw; + + if(file_ctx == NULL || file_ctx->interface == NULL) return LV_FS_RES_INV_PARAM; + + status = file_ctx->interface->Write( + file_ctx->interface, + &buf_size, + (VOID *)buf); + if(status != EFI_SUCCESS) return LV_FS_RES_HW_ERR; + + file_ctx->interface->Flush(file_ctx->interface); + + *bw = (uint32_t) buf_size; + + return LV_FS_RES_OK; +} + +static lv_fs_res_t lv_fs_uefi_seek_cb(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence) +{ + EFI_STATUS status; + lv_uefi_fs_file_context_t * file_ctx = (lv_uefi_fs_file_context_t *)file_p; + UINT64 new_pos; + + if(file_ctx == NULL || file_ctx->interface == NULL) return LV_FS_RES_INV_PARAM; + + if(whence == LV_FS_SEEK_END) { + status = file_ctx->interface->SetPosition( + file_ctx->interface, + UINT64_MAX); + if(status != EFI_SUCCESS) return LV_FS_RES_HW_ERR; + + status = file_ctx->interface->GetPosition( + file_ctx->interface, + &new_pos); + if(status != EFI_SUCCESS) return LV_FS_RES_HW_ERR; + + if(new_pos < pos) { + new_pos = 0; + } + else { + new_pos -= pos; + } + } + else if(whence == LV_FS_SEEK_SET) { + new_pos = pos; + } + else if(whence == LV_FS_SEEK_CUR) { + status = file_ctx->interface->GetPosition( + file_ctx->interface, + &new_pos); + if(status != EFI_SUCCESS) return LV_FS_RES_HW_ERR; + + new_pos += pos; + } + else { + return LV_FS_RES_INV_PARAM; + } + + status = file_ctx->interface->SetPosition( + file_ctx->interface, + new_pos); + if(status != EFI_SUCCESS) return LV_FS_RES_HW_ERR; + + return LV_FS_RES_OK; +} + +static lv_fs_res_t lv_fs_uefi_tell_cb(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p) +{ + EFI_STATUS status; + lv_uefi_fs_file_context_t * file_ctx = (lv_uefi_fs_file_context_t *)file_p; + UINT64 pos; + + if(file_ctx == NULL || file_ctx->interface == NULL) return LV_FS_RES_INV_PARAM; + + status = file_ctx->interface->GetPosition( + file_ctx->interface, + &pos); + if(status != EFI_SUCCESS) return LV_FS_RES_HW_ERR; + + if(pos > UINT32_MAX) return LV_FS_RES_UNKNOWN; + + *pos_p = (uint32_t) pos; + + return LV_FS_RES_OK; +} + +static void * lv_fs_uefi_dir_open_cb(lv_fs_drv_t * drv, const char * path) +{ + EFI_STATUS status; + EFI_FILE_PROTOCOL * fs_root = NULL; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL * fs_interface = NULL; + CHAR16 path_ucs2[LV_FS_MAX_PATH_LENGTH + 1]; + lv_uefi_fs_file_context_t * file_ctx = NULL; + UINT64 mode = 0; + UINT64 attributes = 0; + + EFI_HANDLE fs_handle = (EFI_HANDLE)drv->user_data; + + fs_interface = lv_uefi_protocol_open(fs_handle, &_uefi_guid_simple_file_system); + if(fs_interface == NULL) { + LV_LOG_WARN("[lv_uefi] Unable to open file system protocol."); + goto error; + } + + status = fs_interface->OpenVolume(fs_interface, &fs_root); + if(status != EFI_SUCCESS) { + LV_LOG_WARN("[lv_uefi] Unable to open file system root."); + goto error; + } + + if(path != NULL && path[0] != '\0') { + if(lv_uefi_ascii_to_ucs2(path, path_ucs2, LV_FS_MAX_PATH_LENGTH + 1) == 0) { + LV_LOG_WARN("[lv_uefi] Unable to convert the ASCII path into an UCS-2 path."); + goto error; + } + } + else { + path_ucs2[0] = '\\'; + path_ucs2[1] = '\0'; + } + + lv_fs_uefi_lvgl_path_to_uefi_path(path_ucs2); + + file_ctx = lv_calloc(1, sizeof(lv_uefi_fs_file_context_t)); + LV_ASSERT_MALLOC(file_ctx); + + mode = EFI_FILE_MODE_CREATE | EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE; + attributes = EFI_FILE_DIRECTORY; + + status = fs_root->Open( + fs_root, + &file_ctx->interface, + path_ucs2, + mode, + attributes); + if(status != EFI_SUCCESS) { + LV_LOG_WARN("[lv_uefi] Unable to open directory '%s'.", path); + goto error; + } + + if(!lv_fs_uefi_is_dir(file_ctx->interface)) { + goto error; + } + + goto finish; + +error: + if(file_ctx != NULL) { + if(file_ctx->interface != NULL) { + file_ctx->interface->Close(file_ctx->interface); + } + lv_free(file_ctx); + file_ctx = NULL; + } + +finish: + if(fs_interface != NULL) lv_uefi_protocol_close(fs_handle, &_uefi_guid_simple_file_system); + if(fs_root != NULL) fs_root->Close(fs_root); + + return file_ctx; +} + +static lv_fs_res_t lv_fs_uefi_dir_read_cb(lv_fs_drv_t * drv, void * rddir_p, char * fn, uint32_t fn_len) +{ + lv_fs_res_t return_code; + EFI_STATUS status; + lv_uefi_fs_file_context_t * file_ctx = (lv_uefi_fs_file_context_t *)rddir_p; + + EFI_FILE_INFO * info = NULL; + UINTN size; + + CONST CHAR16 * fn_ucs2; + + if(fn == NULL || fn_len == 0) return LV_FS_RES_INV_PARAM; + if(file_ctx == NULL || file_ctx->interface == NULL) return LV_FS_RES_INV_PARAM; + + // skip . and .. + do { + if(info != NULL) lv_free(info); + info = NULL; + size = 0; + status = file_ctx->interface->Read( + file_ctx->interface, + &size, + info); + if(status == EFI_SUCCESS && size == 0) { + return_code = LV_FS_RES_OK; + *fn = '\0'; + goto finish; + } + else if(status != EFI_BUFFER_TOO_SMALL) { + return_code = LV_FS_RES_NOT_EX; + goto error; + } + + info = lv_calloc(1, size); + LV_ASSERT_MALLOC(info); + + status = file_ctx->interface->Read( + file_ctx->interface, + &size, + info); + if(status != EFI_SUCCESS) { + return_code = LV_FS_RES_HW_ERR; + goto error; + } + } while(lv_fs_uefi_is_dot_path(info->FileName)); + + lv_fs_uefi_uefi_path_to_lvgl_path(info->FileName); + + // skip leading \ and / + for(fn_ucs2 = info->FileName; *fn_ucs2 != L'\0'; fn_ucs2++) { + if(*fn_ucs2 != L'\\' && *fn_ucs2 != L'/') { + break; + } + } + + if((info->Attribute & EFI_FILE_DIRECTORY) != 0) { + if(fn_len == 0) { + return_code = LV_FS_RES_UNKNOWN; + goto error; + } + fn[0] = '/'; + fn++; + fn_len--; + } + + if(lv_uefi_ucs2_to_ascii(fn_ucs2, fn, fn_len) == 0) { + LV_LOG_WARN("[lv_uefi] Unable to convert the UCS-2 path into an ascii path."); + return_code = LV_FS_RES_UNKNOWN; + goto error; + } + + return_code = LV_FS_RES_OK; + goto finish; + +error: + +finish: + if(info) lv_free(info); + + return return_code; +} + +static lv_fs_res_t lv_fs_uefi_dir_close_cb(lv_fs_drv_t * drv, void * rddir_p) +{ + EFI_STATUS status; + lv_uefi_fs_file_context_t * file_ctx = (lv_uefi_fs_file_context_t *)rddir_p; + + if(file_ctx == NULL || file_ctx->interface == NULL) return LV_FS_RES_INV_PARAM; + + status = file_ctx->interface->Close(file_ctx->interface); + if(status != EFI_SUCCESS) return LV_FS_RES_HW_ERR; + + lv_free(file_ctx); + + return LV_FS_RES_OK; +} + +static void lv_fs_drv_uefi_init(lv_fs_drv_t * drv, char fs_drive_letter, EFI_HANDLE fs_handle) +{ + LV_ASSERT_NULL(drv); + LV_ASSERT_NULL(fs_handle); + + lv_fs_drv_init(drv); + + drv->letter = fs_drive_letter; + drv->cache_size = 0; + + drv->ready_cb = lv_fs_uefi_ready_cb; + drv->open_cb = lv_fs_uefi_open_cb; + drv->close_cb = lv_fs_uefi_close_cb; + drv->read_cb = lv_fs_uefi_read_cb; + drv->write_cb = lv_fs_uefi_write_cb; + drv->seek_cb = lv_fs_uefi_seek_cb; + drv->tell_cb = lv_fs_uefi_tell_cb; + + drv->dir_open_cb = lv_fs_uefi_dir_open_cb; + drv->dir_read_cb = lv_fs_uefi_dir_read_cb; + drv->dir_close_cb = lv_fs_uefi_dir_close_cb; + + drv->user_data = (void *) fs_handle; +} + +static void lv_fs_drv_uefi_deinit(lv_fs_drv_t * drv) +{ + LV_ASSERT_NULL(drv); + drv->user_data = NULL; +} + +static void lv_fs_uefi_lvgl_path_to_uefi_path(CHAR16 * path) +{ + if(path == NULL) return; + + for(; *path != '\0'; path++) { + if(*path == L'/') *path = L'\\'; + } +} + +static void lv_fs_uefi_uefi_path_to_lvgl_path(CHAR16 * path) +{ + if(path == NULL) return; + + for(; *path != '\0'; path++) { + if(*path == L'\\') *path = L'/'; + } +} + +static bool lv_fs_uefi_is_dot_path(CONST CHAR16 * path) +{ + if(path == NULL) return FALSE; + + if(path[0] == L'.' && path[1] == L'\0') return TRUE; + if(path[0] == L'.' && path[1] == L'.' && path[2] == L'\0') return TRUE; + + return FALSE; +} + +static bool lv_fs_uefi_is_dir(EFI_FILE_PROTOCOL * dir) +{ + UINT64 attributes; + + if(dir == NULL) return FALSE; + + EFI_FILE_INFO * info = lv_fs_uefi_get_info(dir); + if(info == NULL) return FALSE; + + attributes = info->Attribute; + lv_free(info); + + return (attributes & EFI_FILE_DIRECTORY) != 0; +} + +static bool lv_fs_uefi_is_file(EFI_FILE_PROTOCOL * file) +{ + UINT64 attributes; + + if(file == NULL) return FALSE; + + EFI_FILE_INFO * info = lv_fs_uefi_get_info(file); + if(info == NULL) return FALSE; + + attributes = info->Attribute; + lv_free(info); + + return (attributes & EFI_FILE_DIRECTORY) == 0; +} + +static EFI_FILE_INFO * lv_fs_uefi_get_info(EFI_FILE_PROTOCOL * file) +{ + EFI_STATUS status; + EFI_FILE_INFO * info = NULL; + UINTN size = 0; + + status = file->GetInfo(file, &_uefi_guid_file_info, &size, info); + if(status != EFI_BUFFER_TOO_SMALL) return NULL; + + info = lv_calloc(1, size); + LV_ASSERT_MALLOC(info); + + status = file->GetInfo(file, &_uefi_guid_file_info, &size, info); + if(status != EFI_SUCCESS) { + lv_free(info); + return NULL; + } + + return info; +} + +#else /* LV_FS_UEFI_LETTER == 0*/ + +#if defined(LV_FS_UEFI_LETTER) && LV_FS_UEFI_LETTER != '\0' + #warning "LV_FS_UEFI is not enabled but LV_FS_UEFI_LETTER is set" +#endif + +#endif diff --git a/src/libs/fsdrv/lv_fsdrv.h b/src/libs/fsdrv/lv_fsdrv.h index 380fd3e8c..cd0f5dd2a 100644 --- a/src/libs/fsdrv/lv_fsdrv.h +++ b/src/libs/fsdrv/lv_fsdrv.h @@ -63,6 +63,10 @@ void lv_fs_arduino_esp_littlefs_init(void); void lv_fs_arduino_sd_init(void); #endif +#if LV_USE_FS_UEFI +void lv_fs_uefi_init(void); +#endif + /********************** * MACROS **********************/ diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index dc7b7b3fd..bed25268c 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -2749,6 +2749,24 @@ #endif #endif +/** API for UEFI */ +#ifndef LV_USE_FS_UEFI + #ifdef CONFIG_LV_USE_FS_UEFI + #define LV_USE_FS_UEFI CONFIG_LV_USE_FS_UEFI + #else + #define LV_USE_FS_UEFI 0 + #endif +#endif +#if LV_USE_FS_UEFI + #ifndef LV_FS_UEFI_LETTER + #ifdef CONFIG_LV_FS_UEFI_LETTER + #define LV_FS_UEFI_LETTER CONFIG_LV_FS_UEFI_LETTER + #else + #define LV_FS_UEFI_LETTER '\0' /**< Set an upper cased letter on which the drive will accessible (e.g. 'A') */ + #endif + #endif +#endif + /** LODEPNG decoder library */ #ifndef LV_USE_LODEPNG #ifdef CONFIG_LV_USE_LODEPNG @@ -3901,6 +3919,31 @@ #endif #endif +/** LVGL UEFI backend */ +#ifndef LV_USE_UEFI + #ifdef CONFIG_LV_USE_UEFI + #define LV_USE_UEFI CONFIG_LV_USE_UEFI + #else + #define LV_USE_UEFI 0 + #endif +#endif +#if LV_USE_UEFI + #ifndef LV_USE_UEFI_INCLUDE + #ifdef CONFIG_LV_USE_UEFI_INCLUDE + #define LV_USE_UEFI_INCLUDE CONFIG_LV_USE_UEFI_INCLUDE + #else + #define LV_USE_UEFI_INCLUDE "myefi.h" /**< Header that hides the actual framework (EDK2, gnu-efi, ...) */ + #endif + #endif + #ifndef LV_UEFI_USE_MEMORY_SERVICES + #ifdef CONFIG_LV_UEFI_USE_MEMORY_SERVICES + #define LV_UEFI_USE_MEMORY_SERVICES CONFIG_LV_UEFI_USE_MEMORY_SERVICES + #else + #define LV_UEFI_USE_MEMORY_SERVICES 0 /**< Use the memory functions from the boot services table */ + #endif + #endif +#endif + /** Use OpenGL to open window on PC and handle mouse and keyboard */ #ifndef LV_USE_OPENGLES #ifdef CONFIG_LV_USE_OPENGLES diff --git a/src/lv_init.c b/src/lv_init.c index 463001060..18a2e0857 100644 --- a/src/lv_init.c +++ b/src/lv_init.c @@ -70,6 +70,9 @@ #if LV_USE_WINDOWS #include "drivers/windows/lv_windows_context.h" #endif +#if LV_USE_UEFI + #include "drivers/uefi/lv_uefi_context.h" +#endif /********************* * DEFINES @@ -247,6 +250,10 @@ void lv_init(void) lv_windows_platform_init(); #endif +#if LV_USE_UEFI + lv_uefi_platform_init(); +#endif + lv_obj_style_init(); /*Initialize the screen refresh system*/ @@ -332,6 +339,10 @@ void lv_init(void) lv_fs_arduino_sd_init(); #endif +#if LV_USE_FS_UEFI + lv_fs_uefi_init(); +#endif + #if LV_USE_LODEPNG lv_lodepng_init(); #endif @@ -413,6 +424,10 @@ void lv_deinit(void) lv_obj_style_deinit(); +#if LV_USE_UEFI + lv_uefi_platform_deinit(); +#endif + #if LV_USE_PXP #if LV_USE_DRAW_PXP || LV_USE_ROTATE_PXP lv_draw_pxp_deinit(); diff --git a/src/stdlib/uefi/lv_mem_core_uefi.c b/src/stdlib/uefi/lv_mem_core_uefi.c new file mode 100644 index 000000000..ea03e2c60 --- /dev/null +++ b/src/stdlib/uefi/lv_mem_core_uefi.c @@ -0,0 +1,116 @@ +/** + * @file lv_mem_core_uefi.c + */ + +/********************* + * INCLUDES + *********************/ +#include "../lv_mem.h" +#if LV_USE_UEFI +#if LV_UEFI_USE_MEMORY_SERVICES && LV_USE_STDLIB_MALLOC == LV_STDLIB_CUSTOM +#include "../drivers/uefi/lv_uefi_private.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +typedef struct _mem_header_t { + size_t size; + uint8_t data[0]; +} mem_header_t; + +void lv_mem_init(void) +{ + LV_ASSERT_NULL(gLvEfiBS); + + return; /*Nothing to init*/ +} + +void lv_mem_deinit(void) +{ + return; /*Nothing to deinit*/ +} + +void * lv_malloc_core(size_t size) +{ + size_t size_with_header = size + sizeof(mem_header_t); + mem_header_t * ptr = NULL; + + if(gLvEfiBS->AllocatePool(EfiBootServicesData, size_with_header, (void **)&ptr) != EFI_SUCCESS) return NULL; + + ptr->size = size; + + return ptr->data; +} + +void * lv_realloc_core(void * p, size_t new_size) +{ + mem_header_t * p_header = NULL; + uintptr_t p_address = (uintptr_t)p; + void * p_new = NULL; + + if(p == NULL) return lv_malloc_core(new_size); + // Check for invalid pointers + if(p_address < sizeof(mem_header_t)) return NULL; + + p_address -= sizeof(mem_header_t); + p_header = (mem_header_t *) p_address; + + // UEFI supportes no realloc, if the size grows a new memory block has to be allocated + if(p_header->size > new_size) return p; + + p_new = lv_malloc_core(new_size); + lv_memcpy(p_new, p, p_header->size); + lv_free_core(p); + + return p_new; +} + +void lv_free_core(void * p) +{ + uintptr_t p_address = (uintptr_t)p; + if(p_address < sizeof(mem_header_t)) return; + + p_address -= sizeof(mem_header_t); + + gLvEfiBS->FreePool((void *)p_address); +} + +void lv_mem_monitor_core(lv_mem_monitor_t * mon_p) +{ + /*Not supported*/ + LV_UNUSED(mon_p); + return; +} + +lv_result_t lv_mem_test_core(void) +{ + /*Not supported*/ + return LV_RESULT_OK; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif +#endif \ No newline at end of file