[edk2] [PATCH v2 edk2-platforms 08/20] Platform/Broadcom/RPi3: Add Display driver

Ard Biesheuvel ard.biesheuvel at linaro.org
Fri Dec 14 07:06:59 PST 2018


On Mon, 10 Dec 2018 at 13:39, Pete Batard <pete at akeo.ie> wrote:
>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Pete Batard <pete at akeo.ie>

Please fix the usual things like INF_VERSION and space before ( in
function or macro invocations.

Otherwise, this looks fine
'
> ---
>  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 <andrey.warkentin at gmail.com>
> + *  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 <andrey.warkentin at gmail.com>
> + *  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 <andrey.warkentin at gmail.com>
> + *  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 <Library/BaseLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/UefiLib.h>
> +#include <Library/PcdLib.h>
> +#include <Library/IoLib.h>
> +#include <Library/TimerLib.h>
> +#include <Protocol/GraphicsOutput.h>
> +#include <Protocol/DevicePath.h>
> +#include <Protocol/RaspberryPiFirmware.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Protocol/Cpu.h>
> +#include <Utils.h>
> +
> +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 <andrey.warkentin at gmail.com>
> +#  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 <andrey.warkentin at gmail.com>
> + *
> + *  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 <Protocol/SimpleFileSystem.h>
> +#include <Library/PrintLib.h>
> +#include <Library/BmpSupportLib.h>
> +#include <Library/UefiRuntimeServicesTableLib.h>
> +
> +/*
> + * 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
>


More information about the edk2-devel mailing list