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 6D6682194D3B9 for ; Mon, 10 Dec 2018 04:39:23 -0800 (PST) Received: by mail-ed1-x542.google.com with SMTP id y56so9305300edd.11 for ; Mon, 10 Dec 2018 04:39:23 -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=578N+myklQd1Mmm6ze8SRdGVZmZitY0HfhEW0Pd36g0=; b=l4CB63DBIcTwwAGSx+BgVXXXDHfpXUorHE4lT6EemyISkMeJXyK7/ZMgoeXZVliu7w /2YS5X9wya+UbzlyrWBmqmDfj8XW9Sfibhb0rOqwCMH/7Y/GJcACa2SsVEPaineyhIZ7 TCf+lG06iHVx3PpazwG9ibYHvLWqMpzNFxJohYpPEImpt5EMKa6Fu7a2CfuQoBMD0lz4 oB3yhcnuyWRBlXUf9EOUhSf9GxNxTS6HdqlM7rOUX4xNIiG98RlgoBAJOITDntVHyPAu lQ3eBvqZH2tHz+UTyiB0/JtF58e/jpAC9llGH2LNkf2WjwYRLnXAP40di6Gv8cnRMQzf 8ODw== 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=578N+myklQd1Mmm6ze8SRdGVZmZitY0HfhEW0Pd36g0=; b=ssY5gAr81BTtYfCZLTrWfltUvRmAWSnmi/JtDjSATadpLdRb1XKiMvAFEPU2Iq0LE3 IQPn+/ctbOBDssZeuFMLP3ommdmQS2bNz/jwSiNHdCyyETfPtZ7LiT/6AI0wyimV7f6h gc9R1QcIhV+l/cPEMTpy07pFYL0Sqa/N9Ui6QWxAJWbWOSdZykKEsmtfpc62kiwB7pkZ EL6qX86bF9apaeU8HaOxtYDGdZnCKylm2H06hJOe5vdFOqm/H9ELNolmxKNvP/hYNAD+ Qm2Ds2F4UUeJQD7jyqQaqcSpm20fL3SjoopmyT1TZvNW5FOdyS372bD0R4uTHHN4krjv c26Q== X-Gm-Message-State: AA+aEWYlRAWACPZM/v4ZcvGZcd44BLu6xcGR6qMAwmRmH5DrlAinwzBQ B46ylim59k5aMVq5kscdxT4W93xk7WA= X-Google-Smtp-Source: AFSGD/XchsAODEnVgfjfmfkliir/6Sv+DdvWD0nXcqjz6DDHF9TOPNetjlLU5hi0baj55s0ixm595g== X-Received: by 2002:a17:906:4684:: with SMTP id a4-v6mr9954272ejr.24.1544445560880; Mon, 10 Dec 2018 04:39:20 -0800 (PST) Received: from localhost.localdomain ([84.203.68.105]) by smtp.gmail.com with ESMTPSA id e14sm3296949edb.79.2018.12.10.04.39.18 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 10 Dec 2018 04:39:20 -0800 (PST) From: Pete Batard To: edk2-devel@lists.01.org Date: Mon, 10 Dec 2018 12:38:41 +0000 Message-Id: <20181210123853.4864-9-pete@akeo.ie> X-Mailer: git-send-email 2.17.0.windows.1 In-Reply-To: <20181210123853.4864-1-pete@akeo.ie> References: <20181210123853.4864-1-pete@akeo.ie> Subject: [PATCH v2 edk2-platforms 08/20] Platform/Broadcom/RPi3: 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, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:24 -0000 Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Pete Batard --- Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/ComponentName.c | 222 +++++++ Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.c | 606 ++++++++++++++++++++ Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.h | 43 ++ Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.inf | 71 +++ Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/Screenshot.c | 379 ++++++++++++ 5 files changed, 1321 insertions(+) diff --git a/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/ComponentName.c b/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/ComponentName.c new file mode 100644 index 000000000000..e639826c60b1 --- /dev/null +++ b/Platform/Broadcom/Bcm283x/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/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.c b/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.c new file mode 100644 index 000000000000..e8b7f67c8870 --- /dev/null +++ b/Platform/Broadcom/Bcm283x/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 "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 = ELES(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/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.h b/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.h new file mode 100644 index 000000000000..9fa3d4f6af83 --- /dev/null +++ b/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.h @@ -0,0 +1,43 @@ +/** @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 +#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/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.inf b/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.inf new file mode 100644 index 000000000000..1603edd4909d --- /dev/null +++ b/Platform/Broadcom/Bcm283x/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 = 0x00010005 + 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/Broadcom/Bcm283x/RaspberryPiPkg.dec + +[LibraryClasses] + BaseLib + UefiLib + MemoryAllocationLib + UefiDriverEntryPoint + IoLib + TimerLib + BmpSupportLib + UefiRuntimeServicesTableLib + +[Protocols] + gEfiLoadedImageProtocolGuid + gEfiDevicePathProtocolGuid + gEfiGraphicsOutputProtocolGuid + gRaspberryPiFirmwareProtocolGuid + gEfiCpuArchProtocolGuid + gEfiSimpleFileSystemProtocolGuid + gEfiSimpleTextInputExProtocolGuid + +[Pcd] + gRaspberryPiTokenSpaceGuid.PcdDisplayEnableVModes + gRaspberryPiTokenSpaceGuid.PcdDisplayEnableSShot + +[Guids] + +[Depex] + gEfiCpuArchProtocolGuid AND gRaspberryPiFirmwareProtocolGuid diff --git a/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/Screenshot.c b/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/Screenshot.c new file mode 100644 index 000000000000..a2feeba6084f --- /dev/null +++ b/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/Screenshot.c @@ -0,0 +1,379 @@ +/** @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