From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received-SPF: None (no SPF record) identity=mailfrom; client-ip=2a00:1450:4864:20::542; helo=mail-ed1-x542.google.com; envelope-from=pete@akeo.ie; receiver=edk2-devel@lists.01.org Received: from mail-ed1-x542.google.com (mail-ed1-x542.google.com [IPv6:2a00:1450:4864:20::542]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 6EF6E2194D387 for ; Mon, 28 Jan 2019 04:45:21 -0800 (PST) Received: by mail-ed1-x542.google.com with SMTP id b3so12940107ede.1 for ; Mon, 28 Jan 2019 04:45:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=akeo-ie.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=DAPGQzAL3UkY6bZNRr0yuDLANW/F/Fhywt/BXkVCjKo=; b=bu3VaVxKH1qXEnQ2EzdW0yaiRjDTJsBTST0q0MjSicQ0f26YUU8nMZtByZwsZVKAZ5 Pq+wMunIxXJwTe5FFzh3pmcmsfkmRG/y0YtVUgGH5k+4srLVLthJP7mGf49FifM2KKUd hDf7a8VvMHUwRANAl7KybJK8CmJ4T0pv7nYdqjD7qTTx6elXzXvn+j8bYcTF+rO5boBJ JQttPV/BmhEPpBw0+CFxDufSy9yIT9jTHfZfg0ANpHFcAQ0n6xrhCWDWnu4SfYjjeXGm idu5pdKQvL0xlCKQpJ8Vjc1T/RHEpoGuNsxx7gi5kJyrJrPSbnkWIqyiWSup5/IqgDT5 sohQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=DAPGQzAL3UkY6bZNRr0yuDLANW/F/Fhywt/BXkVCjKo=; b=TKnXUSjns3S1SYB3MHVtk6ZDzK6tf0PsOuT+Ol6uIo/i2FU3S+5Vauw08cTWBNTq7Q dvWt8YMsz+1PGBM477Cwd5GXSgTRTksi0WV4z+bOOC12zW1ntqgceaBMBcYPQkY0x2Kk doK9jChmQfL6jdpXBQ7tGpLXwA45OBIGGXyScTJ2Z5oGY2aEH1HDOHWwLmmo/yEQB4BD H/E13kQTQBSbPO0v7LUrcXzA/s+So8EyVyqlX71Gw3PfUgMu4WcbK6//C1uVql7XVL18 bC6nen/3wHhozF6PQEtIO1zuL87xjNROxuwjTl3IPuOPC3EMX13lZbbNsNoeaBanKbC1 ZOvw== X-Gm-Message-State: AJcUukdByhg00RQTCI6AYk7x5MjTlOC4OLnR3fGxQXJaF2siKVNn95yT UR0Ndx9L+upYEnIuzCqsiKeZoo4/a2E= X-Google-Smtp-Source: ALg8bN4kDmbU2xLDz4tcNSdTEKyt9iWX1juRDnDFnfijD7+IJLxqt58lFhHLq/e6PFNLSgRweb8qgA== X-Received: by 2002:a50:ae01:: with SMTP id c1mr22144942edd.12.1548679518870; Mon, 28 Jan 2019 04:45:18 -0800 (PST) Received: from localhost.localdomain ([84.203.95.186]) by smtp.gmail.com with ESMTPSA id y16sm13757367edb.41.2019.01.28.04.45.16 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 28 Jan 2019 04:45:17 -0800 (PST) From: Pete Batard To: edk2-devel@lists.01.org Date: Mon, 28 Jan 2019 12:44:32 +0000 Message-Id: <20190128124445.9868-11-pete@akeo.ie> X-Mailer: git-send-email 2.17.0.windows.1 In-Reply-To: <20190128124445.9868-1-pete@akeo.ie> References: <20190128124445.9868-1-pete@akeo.ie> Subject: [PATCH v3 edk2-platforms 10/23] Platform/Raspberry/Pi3: Add display driver X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:22 -0000 Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Pete Batard --- Platform/Raspberry/Pi3/Drivers/DisplayDxe/ComponentName.c | 222 +++++++ Platform/Raspberry/Pi3/Drivers/DisplayDxe/DisplayDxe.c | 606 ++++++++++++++++++++ Platform/Raspberry/Pi3/Drivers/DisplayDxe/DisplayDxe.h | 42 ++ Platform/Raspberry/Pi3/Drivers/DisplayDxe/DisplayDxe.inf | 71 +++ Platform/Raspberry/Pi3/Drivers/DisplayDxe/Screenshot.c | 375 ++++++++++++ 5 files changed, 1316 insertions(+) diff --git a/Platform/Raspberry/Pi3/Drivers/DisplayDxe/ComponentName.c b/Platform/Raspberry/Pi3/Drivers/DisplayDxe/ComponentName.c new file mode 100644 index 000000000000..9a84aea511f4 --- /dev/null +++ b/Platform/Raspberry/Pi3/Drivers/DisplayDxe/ComponentName.c @@ -0,0 +1,222 @@ +/** @file + * + * Copyright (c) 2018, Andrei Warkentin + * Copyright (c) 2006-2016, Intel Corporation. All rights reserved. + * + * This program and the accompanying materials + * are licensed and made available under the terms and conditions of the BSD License + * which accompanies this distribution. The full text of the license may be found at + * http://opensource.org/licenses/bsd-license.php + * + * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + * + **/ + +#include "DisplayDxe.h" + +STATIC +EFI_STATUS +EFIAPI +ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +STATIC +EFI_STATUS +EFIAPI +ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gComponentName = { + ComponentNameGetDriverName, + ComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)ComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)ComponentNameGetControllerName, + "en" +}; + + +STATIC EFI_UNICODE_STRING_TABLE mDriverName[] = { + { + "eng;en", + (CHAR16*)L"Raspberry Pi Display Driver" + }, + { + NULL, + NULL + } +}; + +STATIC EFI_UNICODE_STRING_TABLE mDeviceName[] = { + { + "eng;en", + (CHAR16*)L"Raspberry Pi Framebuffer" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +STATIC +EFI_STATUS +EFIAPI +ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mDriverName, + DriverName, + (BOOLEAN)(This == &gComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +STATIC +EFI_STATUS +EFIAPI +ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mDeviceName, + ControllerName, + (BOOLEAN)(This == &gComponentName) + ); +} diff --git a/Platform/Raspberry/Pi3/Drivers/DisplayDxe/DisplayDxe.c b/Platform/Raspberry/Pi3/Drivers/DisplayDxe/DisplayDxe.c new file mode 100644 index 000000000000..0e99194e8576 --- /dev/null +++ b/Platform/Raspberry/Pi3/Drivers/DisplayDxe/DisplayDxe.c @@ -0,0 +1,606 @@ +/** @file + * + * Copyright (c) 2017-2018, Andrei Warkentin + * Copyright (c) Microsoft Corporation. All rights reserved. + * + * This program and the accompanying materials + * are licensed and made available under the terms and conditions of the BSD License + * which accompanies this distribution. The full text of the license may be found at + * http://opensource.org/licenses/bsd-license.php + * + * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + * + **/ + +#include +#include "DisplayDxe.h" + +#define POS_TO_FB(posX, posY) ((UINT8*) \ + ((UINTN)This->Mode->FrameBufferBase + \ + (posY) * This->Mode->Info->PixelsPerScanLine * \ + PI3_BYTES_PER_PIXEL + \ + (posX) * PI3_BYTES_PER_PIXEL)) + +STATIC +EFI_STATUS +EFIAPI +DriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +STATIC +EFI_STATUS +EFIAPI +DriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +STATIC +EFI_STATUS +EFIAPI +DriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +STATIC +EFI_STATUS +EFIAPI +DisplayQueryMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN UINT32 ModeNumber, + OUT UINTN *SizeOfInfo, + OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info + ); + +STATIC +EFI_STATUS +EFIAPI +DisplaySetMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN UINT32 ModeNumber + ); + +STATIC +EFI_STATUS +EFIAPI +DisplayBlt ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL + IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta OPTIONAL + ); + +STATIC EFI_DRIVER_BINDING_PROTOCOL mDriverBinding = { + DriverSupported, + DriverStart, + DriverStop, + 0xa, + NULL, + NULL +}; + +typedef struct { + VENDOR_DEVICE_PATH DisplayDevicePath; + EFI_DEVICE_PATH EndDevicePath; +} DISPLAY_DEVICE_PATH; + +typedef struct { + UINT32 Width; + UINT32 Height; +} GOP_MODE_DATA; + +STATIC UINT32 mBootWidth; +STATIC UINT32 mBootHeight; +STATIC EFI_HANDLE mDevice; +STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL *mFwProtocol; +STATIC EFI_CPU_ARCH_PROTOCOL *mCpu; + +STATIC UINTN mLastMode; +STATIC GOP_MODE_DATA mGopModeData[] = { + { 800, 600 }, /* Legacy */ + { 640, 480 }, /* Legacy */ + { 1024, 768 }, /* Legacy */ + { 1280, 720 }, /* 720p */ + { 1920, 1080 }, /* 1080p */ + { 0, 0 }, /* Physical */ +}; + +STATIC DISPLAY_DEVICE_PATH mDisplayProtoDevicePath = +{ + { + { + HARDWARE_DEVICE_PATH, + HW_VENDOR_DP, + { + (UINT8)(sizeof (VENDOR_DEVICE_PATH)), + (UINT8)((sizeof (VENDOR_DEVICE_PATH)) >> 8), + } + }, + EFI_CALLER_ID_GUID, + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { + sizeof (EFI_DEVICE_PATH_PROTOCOL), + 0 + } + } +}; + +#define PI3_BITS_PER_PIXEL (32) +#define PI3_BYTES_PER_PIXEL (PI3_BITS_PER_PIXEL / 8) + +EFI_GRAPHICS_OUTPUT_PROTOCOL gDisplayProto = { + DisplayQueryMode, + DisplaySetMode, + DisplayBlt, + NULL +}; + +STATIC +EFI_STATUS +EFIAPI +DisplayQueryMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN UINT32 ModeNumber, + OUT UINTN *SizeOfInfo, + OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info + ) +{ + EFI_STATUS Status; + GOP_MODE_DATA *Mode; + + if (ModeNumber > mLastMode) { + return EFI_INVALID_PARAMETER; + } + + Status = gBS->AllocatePool ( + EfiBootServicesData, + sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION), + (VOID**)Info + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Mode = &mGopModeData[ModeNumber]; + + *SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION); + (*Info)->Version = This->Mode->Info->Version; + (*Info)->HorizontalResolution = Mode->Width; + (*Info)->VerticalResolution = Mode->Height; + (*Info)->PixelFormat = This->Mode->Info->PixelFormat; + (*Info)->PixelsPerScanLine = Mode->Width; + + return EFI_SUCCESS; +} + +STATIC +VOID +ClearScreen ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This + ) +{ + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Fill; + + Fill.Red = 0x00; + Fill.Green = 0x00; + Fill.Blue = 0x00; + This->Blt (This, &Fill, EfiBltVideoFill, + 0, 0, 0, 0, This->Mode->Info->HorizontalResolution, + This->Mode->Info->VerticalResolution, + This->Mode->Info->HorizontalResolution * + sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); +} + +STATIC +EFI_STATUS +EFIAPI +DisplaySetMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN UINT32 ModeNumber + ) +{ + UINTN FbSize; + UINTN FbPitch; + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS FbBase; + GOP_MODE_DATA *Mode = &mGopModeData[ModeNumber]; + + if (ModeNumber > mLastMode) { + return EFI_UNSUPPORTED; + } + + DEBUG ((DEBUG_INFO, "Setting mode %u from %u: %u x %u\n", + ModeNumber, This->Mode->Mode, Mode->Width, Mode->Height)); + Status = mFwProtocol->GetFB (Mode->Width, Mode->Height, + PI3_BITS_PER_PIXEL, &FbBase, + &FbSize, &FbPitch); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Could not set mode %u\n", ModeNumber)); + return EFI_DEVICE_ERROR; + } + + DEBUG ((DEBUG_INFO, "Mode %u: %u x %u framebuffer is %u bytes at %p\n", + ModeNumber, Mode->Width, Mode->Height, FbSize, FbBase)); + + if (FbPitch / PI3_BYTES_PER_PIXEL != Mode->Width) { + DEBUG ((DEBUG_ERROR, "Error: Expected width %u, got width %u\n", + Mode->Width, FbPitch / PI3_BYTES_PER_PIXEL)); + return EFI_DEVICE_ERROR; + } + + /* + * WT, because certain OS loaders access the frame buffer directly + * and we don't want to see corruption due to missing WB cache + * maintenance. Performance with WT is good. + */ + Status = mCpu->SetMemoryAttributes (mCpu, FbBase, + ALIGN_VALUE (FbSize, EFI_PAGE_SIZE), + EFI_MEMORY_WT); + if (Status != EFI_SUCCESS) { + DEBUG ((DEBUG_ERROR, "Couldn't set framebuffer attributes: %r\n", Status)); + return Status; + } + + This->Mode->Mode = ModeNumber; + This->Mode->Info->Version = 0; + This->Mode->Info->HorizontalResolution = Mode->Width; + This->Mode->Info->VerticalResolution = Mode->Height; + /* + * NOTE: Windows REQUIRES BGR in 32 or 24 bit format. + */ + This->Mode->Info->PixelFormat = PixelBlueGreenRedReserved8BitPerColor; + This->Mode->Info->PixelsPerScanLine = Mode->Width; + This->Mode->SizeOfInfo = sizeof (*This->Mode->Info); + This->Mode->FrameBufferBase = FbBase; + This->Mode->FrameBufferSize = FbSize; + + ClearScreen (This); + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +EFIAPI +DisplayBlt ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL + IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta OPTIONAL + ) +{ + UINT8 *VidBuf, *BltBuf, *VidBuf1; + UINTN i; + + switch (BltOperation) { + case EfiBltVideoFill: + BltBuf = (UINT8*)BltBuffer; + + for (i = 0; i < Height; i++) { + VidBuf = POS_TO_FB (DestinationX, DestinationY + i); + + SetMem32 (VidBuf, Width * PI3_BYTES_PER_PIXEL, *(UINT32*)BltBuf); + } + break; + + case EfiBltVideoToBltBuffer: + if (Delta == 0) { + Delta = Width * PI3_BYTES_PER_PIXEL; + } + + for (i = 0; i < Height; i++) { + VidBuf = POS_TO_FB (SourceX, SourceY + i); + + BltBuf = (UINT8*)((UINTN)BltBuffer + (DestinationY + i) * Delta + + DestinationX * PI3_BYTES_PER_PIXEL); + + gBS->CopyMem ((VOID*)BltBuf, (VOID*)VidBuf, PI3_BYTES_PER_PIXEL * Width); + } + break; + + case EfiBltBufferToVideo: + if (Delta == 0) { + Delta = Width * PI3_BYTES_PER_PIXEL; + } + + for (i = 0; i < Height; i++) { + VidBuf = POS_TO_FB (DestinationX, DestinationY + i); + BltBuf = (UINT8*)((UINTN)BltBuffer + (SourceY + i) * Delta + + SourceX * PI3_BYTES_PER_PIXEL); + + gBS->CopyMem ((VOID*)VidBuf, (VOID*)BltBuf, Width * PI3_BYTES_PER_PIXEL); + } + break; + + case EfiBltVideoToVideo: + for (i = 0; i < Height; i++) { + VidBuf = POS_TO_FB (SourceX, SourceY + i); + VidBuf1 = POS_TO_FB (DestinationX, DestinationY + i); + + gBS->CopyMem ((VOID*)VidBuf1, (VOID*)VidBuf, Width * PI3_BYTES_PER_PIXEL); + } + break; + + default: + ASSERT_EFI_ERROR (EFI_SUCCESS); + break; + } + + return EFI_SUCCESS; +} + +/** + Initialize the state information for the Display Dxe + + @param ImageHandle of the loaded driver + @param SystemTable Pointer to the System Table + + @retval EFI_SUCCESS Protocol registered + @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure + @retval EFI_DEVICE_ERROR Hardware problems + +**/ +EFI_STATUS +EFIAPI +DisplayDxeInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = gBS->LocateProtocol (&gRaspberryPiFirmwareProtocolGuid, NULL, + (VOID**)&mFwProtocol); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, + (VOID**)&mCpu); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + return Status; + } + + // Query the current display resolution from mailbox + Status = mFwProtocol->GetFBSize (&mBootWidth, &mBootHeight); + if (EFI_ERROR (Status)) { + return Status; + } + + DEBUG ((DEBUG_INFO, "Display boot mode is %u x %u\n", + mBootWidth, mBootHeight)); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &mDevice, &gEfiDevicePathProtocolGuid, + &mDisplayProtoDevicePath, &gEfiCallerIdGuid, + NULL, NULL); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &mDriverBinding, + ImageHandle, + &gComponentName, + &gComponentName2 + ); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + return Status; + } + + return Status; +} + +STATIC +EFI_STATUS +EFIAPI +DriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + VOID *Temp; + + if (Controller != mDevice) { + return EFI_UNSUPPORTED; + } + + if (gBS->HandleProtocol (Controller, &gEfiGraphicsOutputProtocolGuid, + (VOID**)&Temp) == EFI_SUCCESS) { + return EFI_ALREADY_STARTED; + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +EFIAPI +DriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + UINTN Index; + EFI_STATUS Status; + VOID *Dummy; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiCallerIdGuid, + (VOID**)&Dummy, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + gDisplayProto.Mode = AllocateZeroPool (sizeof (EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE)); + if (gDisplayProto.Mode == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + gDisplayProto.Mode->Info = AllocateZeroPool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION)); + if (gDisplayProto.Mode->Info == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + + if (PcdGet32 (PcdDisplayEnableVModes)) { + mLastMode = ARRAY_SIZE (mGopModeData) - 1; + } else { + mLastMode = 0; + /* + * mBootWidth x mBootHeight may not be sensible, + * so clean it up, since we won't be adding + * any other extra vmodes. + */ + if (mBootWidth < 640 || mBootHeight < 480) { + mBootWidth = 640; + mBootHeight = 480; + } + } + + mGopModeData[mLastMode].Width = mBootWidth; + mGopModeData[mLastMode].Height = mBootHeight; + + for (Index = 0; Index <= mLastMode; Index++) { + UINTN FbSize; + UINTN FbPitch; + EFI_PHYSICAL_ADDRESS FbBase; + + GOP_MODE_DATA *Mode = &mGopModeData[Index]; + + Status = mFwProtocol->GetFB (Mode->Width, Mode->Height, + PI3_BITS_PER_PIXEL, &FbBase, + &FbSize, &FbPitch); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // There is no way to communicate pitch back to OS. OS and even UEFI + // expect a fully linear frame buffer. So the width should + // be based on the frame buffer's pitch value. In some cases VC + // firmware would allocate ao frame buffer with some padding + // presumably to be 8 byte align. + // + Mode->Width = FbPitch / PI3_BYTES_PER_PIXEL; + + DEBUG ((DEBUG_INFO, "Mode %u: %u x %u framebuffer is %u bytes at %p\n", + Index, Mode->Width, Mode->Height, FbSize, FbBase)); + + ASSERT (FbPitch != 0); + ASSERT (FbBase != 0); + ASSERT (FbSize != 0); + } + + // Both set the mode and initialize current mode information. + gDisplayProto.Mode->MaxMode = mLastMode + 1; + DisplaySetMode (&gDisplayProto, 0); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, &gEfiGraphicsOutputProtocolGuid, + &gDisplayProto, NULL); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (PcdGet32 (PcdDisplayEnableSShot)) { + RegisterScreenshotHandlers (); + } else { + DEBUG ((DEBUG_INFO, "Screenshot capture disabled\n")); + } + +Done: + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Could not start DisplayDxe: %r\n", Status)); + if (gDisplayProto.Mode->Info != NULL) { + FreePool (gDisplayProto.Mode->Info); + gDisplayProto.Mode->Info = NULL; + } + + if (gDisplayProto.Mode != NULL) { + FreePool (gDisplayProto.Mode); + gDisplayProto.Mode = NULL; + } + + gBS->CloseProtocol ( + Controller, + &gEfiCallerIdGuid, + This->DriverBindingHandle, + Controller + ); + } + return Status; +} + +STATIC +EFI_STATUS +EFIAPI +DriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + + ClearScreen (&gDisplayProto); + + Status = gBS->UninstallMultipleProtocolInterfaces ( + Controller, &gEfiGraphicsOutputProtocolGuid, + &gDisplayProto, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + FreePool (gDisplayProto.Mode->Info); + gDisplayProto.Mode->Info = NULL; + FreePool (gDisplayProto.Mode); + gDisplayProto.Mode = NULL; + + gBS->CloseProtocol ( + Controller, + &gEfiCallerIdGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} diff --git a/Platform/Raspberry/Pi3/Drivers/DisplayDxe/DisplayDxe.h b/Platform/Raspberry/Pi3/Drivers/DisplayDxe/DisplayDxe.h new file mode 100644 index 000000000000..48a049de31d8 --- /dev/null +++ b/Platform/Raspberry/Pi3/Drivers/DisplayDxe/DisplayDxe.h @@ -0,0 +1,42 @@ +/** @file + * + * Copyright (c) 2017-2018, Andrei Warkentin + * Copyright (c) Microsoft Corporation. All rights reserved. + * + * This program and the accompanying materials + * are licensed and made available under the terms and conditions of the BSD License + * which accompanies this distribution. The full text of the license may be found at + * http://opensource.org/licenses/bsd-license.php + * + * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + * + **/ + +#ifndef _DISPLAY_H_ +#define _DISPLAY_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern EFI_GRAPHICS_OUTPUT_PROTOCOL gDisplayProto; +extern EFI_COMPONENT_NAME_PROTOCOL gComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gComponentName2; + +VOID +RegisterScreenshotHandlers ( + VOID + ); + +#endif /* _DISPLAY_H_ */ diff --git a/Platform/Raspberry/Pi3/Drivers/DisplayDxe/DisplayDxe.inf b/Platform/Raspberry/Pi3/Drivers/DisplayDxe/DisplayDxe.inf new file mode 100644 index 000000000000..24b1bf7b9a8a --- /dev/null +++ b/Platform/Raspberry/Pi3/Drivers/DisplayDxe/DisplayDxe.inf @@ -0,0 +1,71 @@ +#/** @file +# +# Component description file for Graphics Output module +# +# Copyright (c) 2017, Andrei Warkentin +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +#**/ + +[Defines] + INF_VERSION = 0x0001001A + BASE_NAME = DisplayDxe + FILE_GUID = c5deae31-fad2-4030-841b-cfc9644d2c5b + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = DisplayDxeInitialize + +# +# The following information is for reference only and not required by the build tools. +# +# DRIVER_BINDING = gGraphicsConsoleDriverBinding +# COMPONENT_NAME = gGraphicsConsoleComponentName +# COMPONENT_NAME2 = gGraphicsConsoleComponentName2 +# + +[Sources] + DisplayDxe.c + Screenshot.c + ComponentName.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + ArmPkg/ArmPkg.dec + Platform/Raspberry/Pi3/RPi3.dec + +[LibraryClasses] + BaseLib + UefiLib + MemoryAllocationLib + UefiDriverEntryPoint + IoLib + TimerLib + BmpSupportLib + UefiRuntimeServicesTableLib + +[Protocols] + gEfiLoadedImageProtocolGuid + gEfiDevicePathProtocolGuid + gEfiGraphicsOutputProtocolGuid + gEfiCpuArchProtocolGuid + gEfiSimpleFileSystemProtocolGuid + gEfiSimpleTextInputExProtocolGuid + gRaspberryPiFirmwareProtocolGuid + +[Pcd] + gRaspberryPiTokenSpaceGuid.PcdDisplayEnableVModes + gRaspberryPiTokenSpaceGuid.PcdDisplayEnableSShot + +[Guids] + +[Depex] + gEfiCpuArchProtocolGuid AND gRaspberryPiFirmwareProtocolGuid diff --git a/Platform/Raspberry/Pi3/Drivers/DisplayDxe/Screenshot.c b/Platform/Raspberry/Pi3/Drivers/DisplayDxe/Screenshot.c new file mode 100644 index 000000000000..a5d32d59c38a --- /dev/null +++ b/Platform/Raspberry/Pi3/Drivers/DisplayDxe/Screenshot.c @@ -0,0 +1,375 @@ +/** @file + * + * Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved. + * Copyright (c) 2018, Andrei Warkentin + * + * This program and the accompanying materials + * are licensed and made available under the terms and conditions of the BSD License + * which accompanies this distribution. The full text of the license may be found at + * http://opensource.org/licenses/bsd-license.php + * + * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + * + **/ + +/* + * Loosely based on CrScreenShotDxe (https://github.com/LongSoft/CrScreenshotDxe). + * + * Copyright (c) 2016, Nikolaj Schlej, All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "DisplayDxe.h" +#include +#include +#include +#include + +/* + * ShowStatus defs. + */ +#define STATUS_SQUARE_SIDE 5 +#define STATUS_YELLOW 0xff, 0xff, 0x00 +#define STATUS_GREEN 0x00, 0xff, 0x00 +#define STATUS_BLUE 0x00, 0x00, 0xff +#define STATUS_RED 0xff, 0x00, 0x00 + +EFI_STATUS +ShowStatus ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput, + IN UINT8 Red, + IN UINT8 Green, + IN UINT8 Blue + ) +{ + UINTN Index; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Square[STATUS_SQUARE_SIDE * STATUS_SQUARE_SIDE]; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Backup[STATUS_SQUARE_SIDE * STATUS_SQUARE_SIDE]; + + for (Index = 0; Index < STATUS_SQUARE_SIDE * STATUS_SQUARE_SIDE; Index++) { + Square[Index].Blue = Blue; + Square[Index].Green = Green; + Square[Index].Red = Red; + Square[Index].Reserved = 0x00; + } + + // Backup current image. + GraphicsOutput->Blt (GraphicsOutput, Backup, + EfiBltVideoToBltBuffer, 0, 0, 0, 0, + STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0); + + // Draw the status square. + GraphicsOutput->Blt (GraphicsOutput, Square, + EfiBltBufferToVideo, 0, 0, 0, 0, + STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0); + + // Wait 500ms. + gBS->Stall (500 * 1000); + + // Restore the backup. + GraphicsOutput->Blt (GraphicsOutput, Backup, + EfiBltBufferToVideo, 0, 0, 0, 0, + STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0); + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +FindWritableFs ( + OUT EFI_FILE_PROTOCOL **WritableFs + ) +{ + EFI_FILE_PROTOCOL *Fs = NULL; + EFI_HANDLE *HandleBuffer = NULL; + UINTN HandleCount; + UINTN Index; + + EFI_STATUS Status = gBS->LocateHandleBuffer (ByProtocol, + &gEfiSimpleFileSystemProtocolGuid, + NULL, &HandleCount, &HandleBuffer); + if (EFI_ERROR (Status)) { + return Status; + } + + for (Index = 0; Index < HandleCount; Index++) { + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFs = NULL; + EFI_FILE_PROTOCOL *File = NULL; + + Status = gBS->HandleProtocol (HandleBuffer[Index], + &gEfiSimpleFileSystemProtocolGuid, + (VOID**)&SimpleFs); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + /* + * Not supposed to happen. + */ + continue; + } + + Status = SimpleFs->OpenVolume (SimpleFs, &Fs); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a OpenVolume[%u] returned %r\n", __FUNCTION__, Index, Status)); + continue; + } + + Status = Fs->Open (Fs, &File, L"--------.---", + EFI_FILE_MODE_CREATE | EFI_FILE_MODE_READ | + EFI_FILE_MODE_WRITE, 0); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a Open[%u] returned %r\n", __FUNCTION__, Index, Status)); + continue; + } + + /* + * Okay, we have a writable filesystem! + */ + Fs->Delete (File); + *WritableFs = Fs; + Status = EFI_SUCCESS; + break; + } + + if (HandleBuffer) { + FreePool (HandleBuffer); + } + + return Status; +} + +STATIC +VOID +TakeScreenshot ( + VOID + ) +{ + VOID *BmpImage = NULL; + EFI_FILE_PROTOCOL *Fs = NULL; + EFI_FILE_PROTOCOL *File = NULL; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = &gDisplayProto; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Image = NULL; + EFI_STATUS Status; + CHAR16 FileName[8 + 1 + 3 + 1]; + UINT32 ScreenWidth; + UINT32 ScreenHeight; + UINTN ImageSize; + UINTN BmpSize; + UINTN Index; + EFI_TIME Time; + + Status = FindWritableFs (&Fs); + if (EFI_ERROR (Status)) { + ShowStatus (GraphicsOutput, STATUS_YELLOW); + } + + ScreenWidth = GraphicsOutput->Mode->Info->HorizontalResolution; + ScreenHeight = GraphicsOutput->Mode->Info->VerticalResolution; + ImageSize = ScreenWidth * ScreenHeight; + + Status = gRT->GetTime (&Time, NULL); + if (!EFI_ERROR (Status)) { + UnicodeSPrint (FileName, sizeof (FileName), L"%02d%02d%02d%02d.bmp", + Time.Day, Time.Hour, Time.Minute, Time.Second); + } else { + UnicodeSPrint (FileName, sizeof (FileName), L"scrnshot.bmp"); + } + + Image = AllocatePool (ImageSize * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); + if (Image == NULL) { + Status = EFI_OUT_OF_RESOURCES; + ShowStatus (GraphicsOutput, STATUS_RED); + goto Done; + } + + Status = GraphicsOutput->Blt (GraphicsOutput, Image, + EfiBltVideoToBltBuffer, 0, 0, 0, 0, + ScreenWidth, ScreenHeight, 0); + if (EFI_ERROR (Status)) { + ShowStatus (GraphicsOutput, STATUS_RED); + goto Done; + } + + for (Index = 0; Index < ImageSize; Index++) { + if (Image[Index].Red != 0x00 || + Image[Index].Green != 0x00 || + Image[Index].Blue != 0x00) { + break; + } + } + + if (Index == ImageSize) { + ShowStatus (GraphicsOutput, STATUS_BLUE); + goto Done; + } + + Status = TranslateGopBltToBmp (Image, ScreenHeight, ScreenWidth, + &BmpImage, (UINT32*)&BmpSize); + if (EFI_ERROR (Status)) { + ShowStatus (GraphicsOutput, STATUS_RED); + goto Done; + } + + Status = Fs->Open (Fs, &File, FileName, EFI_FILE_MODE_CREATE | + EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0); + if (EFI_ERROR (Status)) { + ShowStatus (GraphicsOutput, STATUS_RED); + goto Done; + } + + Status = File->Write (File, &BmpSize, BmpImage); + File->Close (File); + if (EFI_ERROR (Status)) { + ShowStatus (GraphicsOutput, STATUS_RED); + goto Done; + } + + ShowStatus (GraphicsOutput, STATUS_GREEN); + +Done: + if (BmpImage != NULL) { + FreePool (BmpImage); + } + + if (Image != NULL) { + FreePool (Image); + } +} + +STATIC +EFI_STATUS +EFIAPI +ScreenshotKeyHandler ( + IN EFI_KEY_DATA *KeyData + ) +{ + TakeScreenshot (); + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +ProcessScreenshotHandler ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + EFI_KEY_DATA ScreenshotKey; + + /* + * LCtrl+LAlt+F12 + */ + ScreenshotKey.Key.ScanCode = SCAN_F12; + ScreenshotKey.Key.UnicodeChar = 0; + ScreenshotKey.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID | + EFI_LEFT_CONTROL_PRESSED | EFI_LEFT_ALT_PRESSED; + ScreenshotKey.KeyState.KeyToggleState = 0; + + Status = SimpleTextInEx->RegisterKeyNotify ( + SimpleTextInEx, + &ScreenshotKey, + ScreenshotKeyHandler, + &Handle + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: couldn't register key notification: %r\n", __FUNCTION__, Status)); + return Status; + } + + return EFI_SUCCESS; +} + +STATIC +VOID +ProcessScreenshotHandlers ( + VOID + ) +{ + UINTN Index; + EFI_STATUS Status; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx; + + Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleTextInputExProtocolGuid, + NULL, &HandleCount, &HandleBuffer); + if (EFI_ERROR (Status)) { + return; + } + + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol (HandleBuffer[Index], + &gEfiSimpleTextInputExProtocolGuid, + (VOID**)&SimpleTextInEx); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + /* + * Not supposed to happen. + */ + continue; + } + + Status = ProcessScreenshotHandler (SimpleTextInEx); + if (EFI_ERROR (Status)) { + continue; + } + } +} + +STATIC +VOID +EFIAPI +OnTextInExInstall ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + ProcessScreenshotHandlers (); +} + +VOID +RegisterScreenshotHandlers ( + VOID +) +{ + EFI_STATUS Status; + EFI_EVENT TextInExInstallEvent; + VOID *TextInExInstallRegistration; + + ProcessScreenshotHandlers (); + + Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, + OnTextInExInstall, NULL, + &TextInExInstallEvent); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: couldn't create protocol install event: %r\n", __FUNCTION__, Status)); + return; + } + + Status = gBS->RegisterProtocolNotify (&gEfiSimpleTextInputExProtocolGuid, + TextInExInstallEvent, + &TextInExInstallRegistration); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: couldn't register protocol install notify: %r\n", __FUNCTION__, Status)); + return; + } +} -- 2.17.0.windows.1