/**
|
* @file UsbDisplayLink.c
|
* @brief USB DisplayLink Driver that manages USB DisplayLink device and produces Graphics Output Protocol
|
* This file implements the functions of the Driver Binding / Start / Stop / Unload interface
|
*
|
* Copyright (c) 2018-2019, DisplayLink (UK) Ltd. All rights reserved.
|
*
|
* SPDX-License-Identifier: BSD-2-Clause-Patent
|
*
|
**/
|
|
#include "UsbDisplayLink.h"
|
|
#include <Library/UefiRuntimeServicesTableLib.h>
|
#include <Library/PrintLib.h>
|
#include <Protocol/HiiFont.h>
|
|
#include "Edid.h"
|
#include "UsbDescriptors.h"
|
|
//
|
// Functions of Driver Binding Protocol
|
//
|
|
EFI_STATUS
|
EFIAPI
|
UsbDisplayLinkDriverBindingSupported (
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
IN EFI_HANDLE Controller,
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
);
|
|
EFI_STATUS
|
EFIAPI
|
UsbDisplayLinkDriverBindingStart (
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
IN EFI_HANDLE Controller,
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
);
|
|
EFI_STATUS
|
EFIAPI
|
UsbDisplayLinkDriverBindingStop (
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
IN EFI_HANDLE Controller,
|
IN UINTN NumberOfChildren,
|
IN EFI_HANDLE *ChildHandleBuffer
|
);
|
|
EFI_STATUS
|
EFIAPI
|
UsbDisplayLinkDriverBindingEntryPoint (
|
IN EFI_HANDLE ImageHandle,
|
IN EFI_SYSTEM_TABLE *SystemTable
|
);
|
|
|
EFI_STATUS
|
EFIAPI
|
UsbDisplayLinkDriverCombinedGopBindingEntryPoint (
|
IN EFI_HANDLE ImageHandle,
|
IN EFI_SYSTEM_TABLE *SystemTable,
|
IN EFI_HANDLE DriverBindingHandle
|
);
|
|
|
// Generated with https://www.guidgen.com/ - "B70E5A79-C6D6-4267-B02E-9108C989E287"
|
EFI_GUID gEfiDlGopVariableGuid = { 0xB70E5A79, 0xC6D6, 0x4267,{ 0xB0, 0x2E, 0x91, 0x08, 0xC9, 0x89, 0xE2, 0x87 } };
|
|
|
EFI_DRIVER_BINDING_PROTOCOL gUsbDisplayLinkDriverBinding = {
|
UsbDisplayLinkDriverBindingSupported,
|
UsbDisplayLinkDriverBindingStart,
|
UsbDisplayLinkDriverBindingStop,
|
INF_DRIVER_VERSION,
|
NULL,
|
NULL
|
};
|
|
|
/**
|
* Reads integer environment variable with default fallback.
|
* @param variableName variable name to read
|
* @param defaultValue default value to return if requested not found
|
*/
|
STATIC UINT32
|
ReadEnvironmentInt (
|
CHAR16 *VariableName,
|
UINT32 DefaultValue
|
)
|
{
|
UINT32 Result;
|
UINTN DataSize;
|
DataSize = sizeof (Result);
|
CONST EFI_STATUS Status = gRT->GetVariable (VariableName, &gEfiDlGopVariableGuid, (UINT32*)NULL, &DataSize, &Result);
|
if (!EFI_ERROR (Status) && (sizeof (Result) == DataSize)) {
|
return Result;
|
}
|
return DefaultValue;
|
}
|
|
/**
|
* Reads boolean environment variable with default fallback.
|
* @param variableName variable name to read
|
* @param defaultValue default value to return if requested not found
|
*/
|
STATIC BOOLEAN
|
ReadEnvironmentBool (
|
CHAR16 *VariableName,
|
BOOLEAN DefaultValue
|
)
|
{
|
return ReadEnvironmentInt (VariableName, DefaultValue ? 1 : 0) == 1;
|
}
|
|
|
/**
|
*
|
* @param UsbDisplayLinkDev
|
* @return
|
*/
|
STATIC EFI_STATUS
|
InitializeUsbDisplayLinkDevice (
|
IN OUT USB_DISPLAYLINK_DEV *UsbDisplayLinkDev
|
)
|
{
|
EFI_GRAPHICS_OUTPUT_PROTOCOL* Gop;
|
Gop = &UsbDisplayLinkDev->GraphicsOutputProtocol;
|
Gop->QueryMode = DisplayLinkQueryMode;
|
Gop->SetMode = DisplayLinkSetMode;
|
Gop->Blt = DisplayLinkBlt;
|
|
//
|
// Allocate buffer for Graphics Output Protocol mode information
|
//
|
Gop->Mode = (EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE*)AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE));
|
if (Gop->Mode == NULL) {
|
return EFI_OUT_OF_RESOURCES;
|
}
|
Gop->Mode->Info = (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION*)AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION));
|
if (Gop->Mode->Info == NULL) {
|
FreePool (Gop->Mode);
|
Gop->Mode = NULL;
|
return EFI_OUT_OF_RESOURCES;
|
}
|
|
Gop->Mode->MaxMode = MAX(1, DlEdidGetNumSupportedModesInEdid (UsbDisplayLinkDev->EdidActive.Edid, UsbDisplayLinkDev->EdidActive.SizeOfEdid));
|
|
Gop->Mode->Mode = GRAPHICS_OUTPUT_INVALID_MODE_NUMBER;
|
Gop->Mode->Info->Version = 0;
|
// Initialising the horizontal resolution prevents certain BIOSs from hanging on boot, but
|
// it is not yet clear why. See bug 28194.
|
Gop->Mode->Info->HorizontalResolution = DlVideoModeGetSupportedVideoMode (0)->HActive;
|
Gop->Mode->Info->VerticalResolution = 0;
|
Gop->Mode->Info->PixelFormat = PixelBltOnly;
|
Gop->Mode->Info->PixelsPerScanLine = 0;
|
Gop->Mode->SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
|
Gop->Mode->FrameBufferBase = (EFI_PHYSICAL_ADDRESS)(UINTN)NULL;
|
Gop->Mode->FrameBufferSize = 0;
|
|
// Prevent DlGopSendScreenUpdate from running until we are sure that the video mode is set
|
UsbDisplayLinkDev->LastY2 = 0;
|
UsbDisplayLinkDev->LastY1 = (UINTN)-1;
|
|
return EFI_SUCCESS;
|
}
|
|
/**
|
Look for alternate settings for the UsbIo handle's interface
|
which offer protocol DL_PROTOCOL_DIRECT_FB.
|
|
@retval -1 Not found
|
@retval Other The alternate setting
|
**/
|
STATIC INTN
|
GetDirectFbAltSetting (
|
IN EFI_USB_IO_PROTOCOL *UsbIo,
|
IN UINT8 ParentInterfaceNumber
|
)
|
{
|
EFI_STATUS Status;
|
INTN AltSettingIndex;
|
UINT16 InterfaceIndex;
|
|
AltSettingIndex = -1;
|
|
for (InterfaceIndex = 0; InterfaceIndex <= 0xFF; InterfaceIndex++) {
|
EFI_USB_INTERFACE_DESCRIPTOR interfaceDescriptor;
|
Status = UsbDisplayLinkGetInterfaceDescriptor (UsbIo, &interfaceDescriptor, (UINT8)InterfaceIndex);
|
if (EFI_ERROR (Status)) {
|
break;
|
}
|
|
if (interfaceDescriptor.InterfaceNumber == ParentInterfaceNumber &&
|
(interfaceDescriptor.InterfaceClass == CLASS_VENDOR) &&
|
interfaceDescriptor.InterfaceProtocol == INTERFACE_PROTOCOL_DIRECT_FB) {
|
AltSettingIndex = interfaceDescriptor.AlternateSetting;
|
break;
|
}
|
}
|
return AltSettingIndex;
|
}
|
|
/**
|
*
|
* @param UsbIo
|
* @param altSettingIndex
|
* @return
|
*/
|
STATIC EFI_STATUS
|
SelectAltSetting (
|
IN EFI_USB_IO_PROTOCOL *UsbIo,
|
IN UINTN AltSettingIndex)
|
{
|
// Set alternate setting 1 on the interface
|
EFI_STATUS Status;
|
UINT32 UsbStatus;
|
EFI_USB_DEVICE_REQUEST Request;
|
ZeroMem (&Request, sizeof (Request));
|
Request.RequestType = USB_REQ_TYPE_STANDARD | USB_TARGET_INTERFACE;
|
Request.Request = USB_REQ_SET_INTERFACE;
|
Request.Index = DISPLAYLINK_USB_INTERFACE_NUMBER_NIVO;
|
Request.Value = (UINT16)AltSettingIndex;
|
|
Status = UsbIo->UsbControlTransfer (
|
UsbIo,
|
&Request,
|
EfiUsbNoData,
|
DISPLAYLINK_USB_CTRL_TIMEOUT,
|
NULL,
|
0,
|
&UsbStatus);
|
|
if (EFI_ERROR (Status)) {
|
Status = EFI_UNSUPPORTED;
|
DEBUG ((DEBUG_ERROR, "USB control transfer failed while attempting to select alt setting %d on interface (code %r, USB status x%x). DisplayLink device has unsupported firmware version?\n", AltSettingIndex, Status, UsbStatus));
|
}
|
return Status;
|
}
|
|
|
/**
|
Report whether the driver can support the device attached via UsbIo
|
by seeing what if any capabilities it reports.
|
|
@retval TRUE Device has sufficient capabilities for this driver.
|
@retval FALSE Device lacks sufficient capabilities.
|
**/
|
STATIC BOOLEAN
|
CapabilitiesSupported (
|
IN EFI_USB_IO_PROTOCOL *UsbIo
|
)
|
{
|
UINT8 Buffer[256];
|
EFI_STATUS Status;
|
|
Status = ReadCapabilitiesDescriptor (UsbIo, Buffer, sizeof (Buffer));
|
if (EFI_ERROR (Status)) {
|
return FALSE;
|
}
|
|
VendorDescriptor descriptor;
|
Status = UsbDisplayLinkParseCapabilitiesDescriptor (Buffer, sizeof (Buffer), &descriptor);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "Failed to parse capabilities descriptor (code %r)\n", Status));
|
return FALSE;
|
}
|
|
return UsbDisplayLinkCapabilitiesSufficientToBind (&descriptor);
|
}
|
|
|
/**
|
*
|
* @param UsbIo
|
* @param InterfaceDescriptor
|
* @param altSettingIndex
|
* @return
|
*/
|
STATIC BOOLEAN
|
IsDLDirectFbCapableInterface (
|
IN EFI_USB_IO_PROTOCOL *UsbIo,
|
IN EFI_USB_INTERFACE_DESCRIPTOR *InterfaceDescriptor,
|
IN INTN *AltSettingIndex)
|
{
|
EFI_STATUS Status;
|
EFI_USB_DEVICE_DESCRIPTOR DeviceDescriptor;
|
|
Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DeviceDescriptor);
|
|
if (EFI_ERROR (Status)) {
|
return FALSE;
|
}
|
|
if (DeviceDescriptor.IdVendor != VENDOR_DISPLAYLINK) {
|
return FALSE;
|
}
|
|
Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, InterfaceDescriptor);
|
if (EFI_ERROR (Status)) {
|
return FALSE;
|
}
|
|
// We can assume that the interface that we want to talk to - the NIVO interface - is number 0
|
if (InterfaceDescriptor->InterfaceNumber != DISPLAYLINK_USB_INTERFACE_NUMBER_NIVO) {
|
return FALSE;
|
}
|
|
// Check if we have an interface (alt setting) descriptor with the correct interface protocol
|
*AltSettingIndex = GetDirectFbAltSetting (UsbIo, InterfaceDescriptor->InterfaceNumber);
|
|
if (*AltSettingIndex == -1) {
|
DEBUG ((DEBUG_ERROR, "DisplayLink GOP: Failed to find setting on device which supports GOP functionality. Check firmware / device version?\n"));
|
return FALSE;
|
}
|
|
// Now check that the capabilities that we need are properly supported
|
if (CapabilitiesSupported (UsbIo) == FALSE) {
|
DEBUG ((DEBUG_ERROR, "DisplayLink GOP: DL device detected, but it doesn't support the required GOP features. Check firmware / device version?\n"));
|
return FALSE;
|
}
|
|
return TRUE;
|
}
|
|
|
/**
|
* Prints a block of text in the framebuffer (helper function).
|
* @param X x coordinate
|
* @param Y y coordinate
|
*/
|
STATIC VOID
|
DisplayLinkPrintTextToScreenInternal (
|
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput,
|
UINTN X,
|
UINTN Y,
|
IN CHAR16 *Buffer
|
)
|
{
|
EFI_STATUS Status;
|
EFI_HII_FONT_PROTOCOL *HiiFont;
|
EFI_IMAGE_OUTPUT *Blt;
|
EFI_FONT_DISPLAY_INFO FontInfo;
|
EFI_HII_OUT_FLAGS Flags;
|
|
Blt = (EFI_IMAGE_OUTPUT*)NULL;
|
|
Status = gBS->LocateProtocol (&gEfiHiiFontProtocolGuid, NULL, (VOID **)&HiiFont);
|
if (!EFI_ERROR (Status)) {
|
Blt = (EFI_IMAGE_OUTPUT*)AllocateZeroPool (sizeof (EFI_IMAGE_OUTPUT));
|
Blt->Width = (UINT16)GraphicsOutput->Mode->Info->HorizontalResolution;
|
Blt->Height = (UINT16)GraphicsOutput->Mode->Info->VerticalResolution;
|
Blt->Image.Screen = GraphicsOutput;
|
|
ZeroMem (&FontInfo, sizeof (EFI_FONT_DISPLAY_INFO));
|
FontInfo.ForegroundColor.Red = 0;
|
FontInfo.ForegroundColor.Green = 0;
|
FontInfo.ForegroundColor.Blue = 0;
|
FontInfo.BackgroundColor.Red = 0xff;
|
FontInfo.BackgroundColor.Green = 0xff;
|
FontInfo.BackgroundColor.Blue = 0xff;
|
|
Flags = EFI_HII_IGNORE_IF_NO_GLYPH | EFI_HII_OUT_FLAG_CLIP |
|
EFI_HII_OUT_FLAG_CLIP_CLEAN_X | EFI_HII_OUT_FLAG_CLIP_CLEAN_Y |
|
EFI_HII_IGNORE_LINE_BREAK | EFI_HII_DIRECT_TO_SCREEN;
|
|
Status = HiiFont->StringToImage (
|
HiiFont,
|
Flags,
|
Buffer,
|
&FontInfo,
|
&Blt,
|
X,
|
Y,
|
(EFI_HII_ROW_INFO**)NULL,
|
(UINTN*)NULL,
|
(UINTN*)NULL);
|
}
|
|
if (Blt != NULL) {
|
FreePool (Blt);
|
}
|
}
|
|
|
/**
|
* Prints a block of text in the framebuffer.
|
* @param X x coordinate
|
* @param Y y coordinate
|
* @param Format string format similar to stdlib's vsnprintf
|
* @param ... arguments
|
*/
|
VOID
|
EFIAPI
|
DlGopPrintTextToScreen (
|
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput,
|
UINTN X,
|
UINTN Y,
|
IN CONST CHAR16 *Format,
|
...
|
)
|
{
|
VA_LIST Marker;
|
CHAR16 *Buffer;
|
UINTN BufferSize;
|
|
ASSERT (Format != NULL);
|
ASSERT (((UINTN) Format & BIT0) == 0);
|
|
VA_START(Marker, Format);
|
|
BufferSize = (PcdGet32 (PcdUefiLibMaxPrintBufferSize) + 1) * sizeof (CHAR16);
|
|
Buffer = (CHAR16*)AllocatePool (BufferSize);
|
ASSERT (Buffer != NULL);
|
|
UnicodeVSPrint (Buffer, BufferSize, Format, Marker);
|
|
VA_END(Marker);
|
|
DisplayLinkPrintTextToScreenInternal (GraphicsOutput, X, Y, Buffer);
|
|
FreePool (Buffer);
|
}
|
|
|
/**
|
* Sometimes platforms only write to the first GOP device that they find. Enabling this function allows us to copy the pixels from this device.
|
* @param UsbDisplayLinkDev
|
*/
|
#ifdef COPY_PIXELS_FROM_PRIMARY_GOP_DEVICE
|
STATIC VOID
|
DisplayLinkCopyFromPrimaryGopDevice (
|
IN USB_DISPLAYLINK_DEV* UsbDisplayLinkDev
|
)
|
{
|
UINTN HandleCount;
|
EFI_HANDLE *HandleBuffer;
|
UINTN HandleIndex;
|
EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;
|
|
gBS->LocateHandleBuffer (
|
ByProtocol,
|
&gEfiGraphicsOutputProtocolGuid,
|
NULL,
|
&HandleCount,
|
&HandleBuffer);
|
|
for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
|
gBS->HandleProtocol (HandleBuffer[HandleIndex], &gEfiGraphicsOutputProtocolGuid, (VOID**)&Gop);
|
if (Gop != &UsbDisplayLinkDev->GraphicsOutputProtocol && Gop->Mode->FrameBufferBase != (EFI_PHYSICAL_ADDRESS)(UINTN)NULL) {
|
|
// See if we need to do a screen update - calculate a really noddy hash to see if any screen updates have happened.
|
STATIC UINT32 prevframeBufferHash = 0; // 4 bytes per pixel
|
UINT32 currFrameBufferHash = 0;
|
UINTN i;
|
for (i = 0; i < (Gop->Mode->Info->HorizontalResolution * Gop->Mode->Info->VerticalResolution); i++) {
|
currFrameBufferHash += ((UINT32*)(UINTN)Gop->Mode->FrameBufferBase)[i];
|
}
|
|
if (currFrameBufferHash != prevframeBufferHash) {
|
prevframeBufferHash = currFrameBufferHash;
|
|
DisplayLinkBlt (
|
&UsbDisplayLinkDev->GraphicsOutputProtocol,
|
(EFI_GRAPHICS_OUTPUT_BLT_PIXEL*)(UINTN)Gop->Mode->FrameBufferBase,
|
EfiBltBufferToVideo,
|
0,
|
0,
|
0,
|
0,
|
Gop->Mode->Info->HorizontalResolution,
|
Gop->Mode->Info->VerticalResolution,
|
0);
|
}
|
break;
|
}
|
}
|
|
FreePool (HandleBuffer);
|
}
|
#endif // COPY_PIXELS_FROM_PRIMARY_GOP_DEVICE
|
|
|
/**
|
* Exit from boot services: signal handler.
|
*/
|
STATIC VOID
|
EFIAPI
|
DisplayLinkDriverExitBootServices (
|
IN EFI_EVENT Event,
|
IN VOID *Context
|
)
|
{
|
USB_DISPLAYLINK_DEV* UsbDisplayLinkDev;
|
UsbDisplayLinkDev = (USB_DISPLAYLINK_DEV*)Context;
|
|
gBS->CloseEvent (UsbDisplayLinkDev->TimerEvent);
|
}
|
|
/**
|
* Periodic screen update: timer callback.
|
*/
|
VOID
|
EFIAPI
|
DisplayLinkPeriodicTimer (
|
IN EFI_EVENT Event,
|
IN VOID* Context
|
)
|
{
|
EFI_STATUS Status;
|
USB_DISPLAYLINK_DEV* UsbDisplayLinkDev;
|
|
Status = EFI_SUCCESS;
|
UsbDisplayLinkDev = (USB_DISPLAYLINK_DEV*)Context;
|
|
// Drop out if we haven't set the video mode up yet
|
if (UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Mode == GRAPHICS_OUTPUT_INVALID_MODE_NUMBER) {
|
// Restart the one-shot timer to poll the status again.
|
Status = gBS->SetTimer (UsbDisplayLinkDev->TimerEvent, TimerRelative, DISPLAYLINK_SCREEN_UPDATE_TIMER_PERIOD);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "Failed to create timer.\n"));
|
}
|
return;
|
}
|
|
#ifdef COPY_PIXELS_FROM_PRIMARY_GOP_DEVICE
|
DisplayLinkCopyFromPrimaryGopDevice (UsbDisplayLinkDev);
|
#endif // COPY_PIXELS_FROM_PRIMARY_GOP_DEVICE
|
|
if (UsbDisplayLinkDev->ShowBandwidth) {
|
STATIC UINTN Count = 0;
|
|
if (Count++ % 50 == 0) {
|
DlGopPrintTextToScreen (&UsbDisplayLinkDev->GraphicsOutputProtocol, 32, 48, (CONST CHAR16*)L" Bandwidth: %d MB/s ", UsbDisplayLinkDev->DataSent * 10000000 / DISPLAYLINK_SCREEN_UPDATE_TIMER_PERIOD / 50 / 1024 / 1024);
|
UsbDisplayLinkDev->DataSent = 0;
|
}
|
}
|
|
if (UsbDisplayLinkDev->ShowTestPattern)
|
{
|
if (UsbDisplayLinkDev->ShowTestPattern == 5) {
|
DlGopSendTestPattern (UsbDisplayLinkDev, 0);
|
} else if (UsbDisplayLinkDev->ShowTestPattern >= 10) {
|
DlGopSendTestPattern (UsbDisplayLinkDev, 1);
|
UsbDisplayLinkDev->ShowTestPattern = 0;
|
}
|
UsbDisplayLinkDev->ShowTestPattern++;
|
|
}
|
|
// Send the latest version of the frame buffer to the DL device over USB
|
DlGopSendScreenUpdate (UsbDisplayLinkDev);
|
|
// Restart the timer now we've finished
|
Status = gBS->SetTimer (UsbDisplayLinkDev->TimerEvent, TimerRelative, DISPLAYLINK_SCREEN_UPDATE_TIMER_PERIOD);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "Failed to create timer.\n"));
|
}
|
}
|
|
/* ****************************************************************************************************** */
|
/* ****************************************************************************************************** */
|
/* ****************** START OF FUNCTIONS WHICH IMPLEMENT DRIVER BINDING INTERFACE ****************** */
|
/* ****************************************************************************************************** */
|
/* ****************************************************************************************************** */
|
|
/**
|
Check whether USB DisplayLink driver supports this device.
|
|
@param This The USB DisplayLink driver binding protocol.
|
@param Controller The controller handle to check.
|
@param RemainingDevicePath The remaining device path.
|
|
@retval EFI_SUCCESS The driver supports this controller.
|
@retval other This device isn't supported.
|
|
**/
|
EFI_STATUS
|
EFIAPI
|
UsbDisplayLinkDriverBindingSupported (
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
IN EFI_HANDLE Controller,
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
)
|
{
|
EFI_STATUS Status;
|
EFI_USB_IO_PROTOCOL *UsbIo;
|
|
Status = gBS->OpenProtocol (
|
Controller,
|
&gEfiUsbIoProtocolGuid,
|
(VOID **) &UsbIo,
|
This->DriverBindingHandle,
|
Controller,
|
EFI_OPEN_PROTOCOL_BY_DRIVER);
|
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
//
|
// Use the USB I/O Protocol interface to check whether Controller is
|
// a DisplayLink device that can be managed by this driver.
|
//
|
Status = EFI_UNSUPPORTED;
|
EFI_USB_INTERFACE_DESCRIPTOR DummyInterfaceDescriptor;
|
INTN DummyAltSettingIndex;
|
|
if (IsDLDirectFbCapableInterface (UsbIo, &DummyInterfaceDescriptor, &DummyAltSettingIndex)){
|
Status = EFI_SUCCESS;
|
}
|
|
gBS->CloseProtocol (
|
Controller,
|
&gEfiUsbIoProtocolGuid,
|
This->DriverBindingHandle,
|
Controller);
|
|
return Status;
|
}
|
|
|
/**
|
Starts the DisplayLink device with this driver.
|
|
This function consumes USB I/O Protocol, intializes USB DisplayLink device,
|
installs Graphics Output Protocol
|
Transfer to manage the USB DisplayLink device.
|
|
@param This The USB DisplayLink driver binding instance.
|
@param Controller Handle of device to bind driver to.
|
@param RemainingDevicePath Optional parameter use to pick a specific child
|
device to start.
|
|
@retval EFI_SUCCESS This driver supports this device.
|
@retval EFI_UNSUPPORTED This driver does not support this device.
|
@retval EFI_DEVICE_ERROR This driver cannot be started due to device Error.
|
@retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
|
@retval EFI_ALREADY_STARTED This driver has been started.
|
|
**/
|
EFI_STATUS
|
EFIAPI
|
UsbDisplayLinkDriverBindingStart (
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
IN EFI_HANDLE Controller,
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
)
|
{
|
EFI_STATUS Status;
|
USB_DISPLAYLINK_DEV *UsbDisplayLinkDev;
|
UINT8 EndpointNumber;
|
EFI_USB_ENDPOINT_DESCRIPTOR EndpointDescriptor;
|
UINT8 Index;
|
BOOLEAN FoundOut;
|
BOOLEAN FoundIn;
|
EFI_TPL OriginalTPL;
|
INTN altSettingIndex;
|
|
OriginalTPL = gBS->RaiseTPL (TPL_CALLBACK);
|
|
UsbDisplayLinkDev = (USB_DISPLAYLINK_DEV*)AllocateZeroPool (sizeof (USB_DISPLAYLINK_DEV));
|
if (UsbDisplayLinkDev == NULL) {
|
DEBUG ((DEBUG_ERROR, "Device initialialisation - Failed to allocate memory for device.\n"));
|
gBS->RestoreTPL (OriginalTPL);
|
return EFI_OUT_OF_RESOURCES;
|
}
|
|
UsbDisplayLinkDev->Signature = USB_DISPLAYLINK_DEV_SIGNATURE;
|
|
UsbDisplayLinkDev->ShowBandwidth = ReadEnvironmentBool (L"DisplayLinkShowBandwidth", FALSE);
|
UsbDisplayLinkDev->ShowTestPattern = ReadEnvironmentBool (L"DisplayLinkShowTestPatterns", FALSE);
|
|
//
|
// Open USB I/O Protocol
|
//
|
Status = gBS->OpenProtocol (
|
Controller,
|
&gEfiUsbIoProtocolGuid,
|
(VOID **) &UsbDisplayLinkDev->UsbIo,
|
This->DriverBindingHandle,
|
Controller,
|
EFI_OPEN_PROTOCOL_BY_DRIVER);
|
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "Failed to open usbio protocol. Is USB correctly supported on this platform?.\n"));
|
Status = EFI_UNSUPPORTED;
|
goto ErrorExit2;
|
}
|
|
if (!IsDLDirectFbCapableInterface (UsbDisplayLinkDev->UsbIo, &UsbDisplayLinkDev->InterfaceDescriptor, &altSettingIndex)) {
|
Status = EFI_UNSUPPORTED;
|
goto ErrorExit4;
|
}
|
|
Status = SelectAltSetting (UsbDisplayLinkDev->UsbIo, altSettingIndex);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "DisplayLink GOP: Failed to select alternate setting.\n"));
|
Status = EFI_UNSUPPORTED;
|
goto ErrorExit4;
|
}
|
|
//
|
// Parse endpoint descriptor
|
//
|
EndpointNumber = UsbDisplayLinkDev->InterfaceDescriptor.NumEndpoints;
|
|
//
|
// Traverse endpoints to find bulk endpoint
|
//
|
FoundOut = FALSE;
|
FoundIn = FALSE;
|
for (Index = 0; Index < EndpointNumber; Index++) {
|
UsbDisplayLinkDev->UsbIo->UsbGetEndpointDescriptor (
|
UsbDisplayLinkDev->UsbIo,
|
Index,
|
&EndpointDescriptor);
|
|
if ((EndpointDescriptor.Attributes & (BIT0 | BIT1)) == USB_ENDPOINT_BULK) {
|
if (!FoundOut && (EndpointDescriptor.EndpointAddress & BIT7) == 0) {
|
CopyMem (&UsbDisplayLinkDev->BulkOutEndpointDescriptor, &EndpointDescriptor, sizeof (EndpointDescriptor));
|
FoundOut = TRUE;
|
} else if (!FoundIn && (EndpointDescriptor.EndpointAddress & BIT7) == BIT7) {
|
CopyMem (&UsbDisplayLinkDev->BulkInEndpointDescriptor, &EndpointDescriptor, sizeof (EndpointDescriptor));
|
FoundIn = TRUE;
|
}
|
}
|
}
|
|
if (FoundOut == FALSE) {
|
Status = EFI_UNSUPPORTED;
|
DEBUG ((DEBUG_ERROR, "No endpoints found. Num endpoints searched = %d.\n", EndpointNumber));
|
goto ErrorExit4;
|
}
|
|
Status = DlReadEdid (UsbDisplayLinkDev);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "Failed to read monitor EDID from DisplayLink device (code %r)\n", Status));
|
goto ErrorExit7;
|
}
|
|
Status = InitializeUsbDisplayLinkDevice (UsbDisplayLinkDev);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "Failed to initialise DisplayLink device (code %r)\n", Status));
|
goto ErrorExit7;
|
}
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
&Controller,
|
&gEfiGraphicsOutputProtocolGuid,
|
&UsbDisplayLinkDev->GraphicsOutputProtocol,
|
&gEfiEdidDiscoveredProtocolGuid,
|
&UsbDisplayLinkDev->EdidDiscovered,
|
&gEfiEdidActiveProtocolGuid,
|
&UsbDisplayLinkDev->EdidActive,
|
NULL);
|
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "Failed to install Graphics Output and EDID protocol interfaces - driver not installed correctly - %r\n", Status));
|
goto ErrorExit8;
|
}
|
|
UsbDisplayLinkDev->ControllerNameTable = (EFI_UNICODE_STRING_TABLE*)NULL;
|
|
AddUnicodeString2 (
|
"eng",
|
mUsbDisplayLinkComponentName.SupportedLanguages,
|
&UsbDisplayLinkDev->ControllerNameTable,
|
(CONST CHAR16*)L"Generic Usb DisplayLink",
|
TRUE);
|
|
AddUnicodeString2 (
|
"en",
|
mUsbDisplayLinkComponentName2.SupportedLanguages,
|
&UsbDisplayLinkDev->ControllerNameTable,
|
(CONST CHAR16*)L"Generic Usb DisplayLink",
|
FALSE);
|
|
//
|
// Setup a periodic timer
|
//
|
Status = gBS->CreateEvent (
|
EVT_TIMER | EVT_NOTIFY_SIGNAL,
|
TPL_CALLBACK,
|
DisplayLinkPeriodicTimer,
|
UsbDisplayLinkDev,
|
&UsbDisplayLinkDev->TimerEvent);
|
|
if (EFI_ERROR (Status)) {
|
Status = EFI_OUT_OF_RESOURCES;
|
DEBUG ((DEBUG_ERROR, "Failed to create screeen update polling event.\n"));
|
goto ErrorExit8;
|
}
|
|
// Start one-shot timer. The rendering operations can take quite a long time, so we
|
// don't want another timer event to happen until we have finished; so we'll restart
|
// the timer from DisplayLinkPeriodicTimer, the event handler function.
|
Status = gBS->SetTimer (UsbDisplayLinkDev->TimerEvent, TimerRelative, DISPLAYLINK_SCREEN_UPDATE_TIMER_PERIOD);
|
if (EFI_ERROR (Status)) {
|
Status = EFI_OUT_OF_RESOURCES;
|
DEBUG ((DEBUG_ERROR, "Failed to create screen update polling timer.\n"));
|
goto ErrorExit8;
|
}
|
|
Status = gBS->CreateEventEx (
|
EVT_NOTIFY_SIGNAL,
|
TPL_NOTIFY,
|
DisplayLinkDriverExitBootServices,
|
UsbDisplayLinkDev,
|
&gEfiEventExitBootServicesGuid,
|
&UsbDisplayLinkDev->DriverExitBootServicesEvent);
|
|
if (EFI_ERROR (Status)) {
|
Status = EFI_OUT_OF_RESOURCES;
|
DEBUG ((DEBUG_ERROR, "Failed to create event for bootexit.\n"));
|
goto ErrorExit8;
|
}
|
|
gBS->RestoreTPL (OriginalTPL);
|
|
DEBUG ((DEBUG_INFO, "DisplayLink GOP driver successfully bound to device.\n"));
|
|
return EFI_SUCCESS;
|
|
//
|
// Error handler
|
//
|
ErrorExit8:
|
ErrorExit7:
|
ErrorExit4:
|
gBS->CloseProtocol (
|
Controller,
|
&gEfiUsbIoProtocolGuid,
|
This->DriverBindingHandle,
|
Controller);
|
|
ErrorExit2:
|
if (UsbDisplayLinkDev != NULL) {
|
FreePool (UsbDisplayLinkDev);
|
UsbDisplayLinkDev = (USB_DISPLAYLINK_DEV*)NULL;
|
}
|
|
DEBUG ((DEBUG_ERROR, "Exiting - Failed to initialise driver.\n"));
|
|
gBS->RestoreTPL (OriginalTPL);
|
return Status;
|
}
|
|
/**
|
Entrypoint of USB DisplayLink Driver.
|
|
This function is the entrypoint of a combined USB DisplayLink GOP Driver. It installs Driver Binding
|
Protocols together with Component Name Protocols.
|
|
@param ImageHandle The firmware allocated handle for the EFI image.
|
@param SystemTable A pointer to the EFI System Table.
|
@param DriverBindingHandle The Driver binding handle
|
|
@retval EFI_SUCCESS The entry point is executed successfully.
|
|
**/
|
EFI_STATUS
|
EFIAPI
|
UsbDisplayLinkDriverCombinedGopBindingEntryPoint (
|
IN EFI_HANDLE ImageHandle,
|
IN EFI_SYSTEM_TABLE *SystemTable,
|
IN EFI_HANDLE DriverBindingHandle
|
)
|
{
|
EFI_STATUS Status;
|
|
Status = EfiLibInstallDriverBindingComponentName2 (
|
ImageHandle,
|
SystemTable,
|
&gUsbDisplayLinkDriverBinding,
|
DriverBindingHandle,
|
&mUsbDisplayLinkComponentName,
|
&mUsbDisplayLinkComponentName2);
|
|
ASSERT_EFI_ERROR (Status);
|
|
return EFI_SUCCESS;
|
}
|
|
|
/**
|
Entrypoint of USB DisplayLink Driver.
|
|
This function is the entrypoint of USB DisplayLink Driver. It installs Driver Binding
|
Protocols together with Component Name Protocols.
|
|
@param ImageHandle The firmware allocated handle for the EFI image.
|
@param SystemTable A pointer to the EFI System Table.
|
|
@retval EFI_SUCCESS The entry point is executed successfully.
|
|
**/
|
EFI_STATUS
|
EFIAPI
|
UsbDisplayLinkDriverBindingEntryPoint (
|
IN EFI_HANDLE ImageHandle,
|
IN EFI_SYSTEM_TABLE *SystemTable
|
)
|
{
|
return UsbDisplayLinkDriverCombinedGopBindingEntryPoint (ImageHandle, SystemTable, ImageHandle);
|
}
|
|
|
/**
|
Unloads an image.
|
@param ImageHandle Handle that identifies the image to be unloaded.
|
@retval EFI_SUCCESS The image has been unloaded.
|
@retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.
|
**/
|
EFI_STATUS
|
EFIAPI
|
UsbDisplayLinkDriverCombinedGopUnload (
|
IN EFI_HANDLE ImageHandle
|
)
|
{
|
EFI_STATUS Status = EFI_SUCCESS;
|
EFI_STATUS handleDisconnectStatus;
|
EFI_HANDLE *HandleBuffer;
|
UINTN HandleCount;
|
UINTN Index;
|
|
//
|
// Retrieve array of all handles in the handle database
|
//
|
handleDisconnectStatus = gBS->LocateHandleBuffer (
|
AllHandles,
|
NULL,
|
NULL,
|
&HandleCount,
|
&HandleBuffer
|
);
|
if (! EFI_ERROR (handleDisconnectStatus)) {
|
//
|
// Disconnect the current driver from handles in the handle database
|
//
|
for (Index = 0; Index < HandleCount; Index++) {
|
Status = gBS->DisconnectController (HandleBuffer[Index], gImageHandle, NULL);
|
}
|
//
|
// Free the array of handles
|
//
|
if (HandleBuffer != NULL)
|
{
|
FreePool (HandleBuffer);
|
}
|
}
|
|
// Even if we didn't manage to disconnect the handles, try to uninstall the protocols
|
//
|
// Uninstall protocols installed in the driver entry point
|
//
|
Status = gBS->UninstallMultipleProtocolInterfaces (
|
ImageHandle,
|
&gEfiDriverBindingProtocolGuid,
|
&gUsbDisplayLinkDriverBinding,
|
&gEfiComponentNameProtocolGuid,
|
&mUsbDisplayLinkComponentName,
|
&gEfiComponentName2ProtocolGuid,
|
&mUsbDisplayLinkComponentName2,
|
NULL
|
);
|
|
if (EFI_ERROR (handleDisconnectStatus))
|
{
|
return handleDisconnectStatus;
|
}
|
return Status;
|
}
|
|
|
/**
|
Stop the USB DisplayLink device handled by this driver.
|
|
@param This The USB DisplayLink driver binding protocol.
|
@param Controller The controller to release.
|
@param NumberOfChildren The number of handles in ChildHandleBuffer.
|
@param ChildHandleBuffer The array of child handle.
|
|
@retval EFI_SUCCESS The device was stopped.
|
@retval EFI_UNSUPPORTED Simple Pointer Protocol is not installed on Controller.
|
@retval Others Fail to uninstall protocols attached on the device.
|
|
**/
|
EFI_STATUS
|
EFIAPI
|
UsbDisplayLinkDriverBindingStop (
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
IN EFI_HANDLE Controller,
|
IN UINTN NumberOfChildren,
|
IN EFI_HANDLE *ChildHandleBuffer
|
)
|
{
|
EFI_STATUS Status;
|
USB_DISPLAYLINK_DEV *UsbDisplayLinkDev;
|
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutputProtocol;
|
|
Status = gBS->OpenProtocol (
|
Controller,
|
&gEfiGraphicsOutputProtocolGuid,
|
(VOID **) &GraphicsOutputProtocol,
|
This->DriverBindingHandle,
|
Controller,
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
|
if (EFI_ERROR (Status)) {
|
return EFI_UNSUPPORTED;
|
}
|
|
UsbDisplayLinkDev = USB_DISPLAYLINK_DEV_FROM_GRAPHICS_OUTPUT_PROTOCOL(GraphicsOutputProtocol);
|
|
// Reset the video mode to clear the display. Don't drop out if there is a problem, just press on.
|
// Note that this will also clear the frame buffer, as the screen buffer will be re-allocated with AllocateZeroPool.
|
if ((GraphicsOutputProtocol->Mode != NULL) &&
|
(GraphicsOutputProtocol->Mode->Mode != GRAPHICS_OUTPUT_INVALID_MODE_NUMBER)) {
|
Status = DisplayLinkSetMode (GraphicsOutputProtocol, GraphicsOutputProtocol->Mode->Mode);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_WARN, "Driver stop - Problem resetting video mode - %r.\n", Status));
|
}
|
}
|
|
// Reset the alt setting on the interface (to the DL3 alt setting)
|
Status = SelectAltSetting (UsbDisplayLinkDev->UsbIo, 0);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_WARN, "Error resetting USB interface alternate setting - %r.\n", Status));
|
}
|
|
Status = gBS->UninstallMultipleProtocolInterfaces (
|
Controller,
|
&gEfiGraphicsOutputProtocolGuid,
|
&UsbDisplayLinkDev->GraphicsOutputProtocol,
|
&gEfiEdidDiscoveredProtocolGuid,
|
&UsbDisplayLinkDev->EdidDiscovered,
|
&gEfiEdidActiveProtocolGuid,
|
&UsbDisplayLinkDev->EdidActive,
|
NULL);
|
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_WARN, "Error uninstalling Graphics Output and EDID protocol interfaces - %r.\n", Status));
|
return Status;
|
}
|
|
gBS->CloseEvent (UsbDisplayLinkDev->TimerEvent);
|
|
gBS->CloseProtocol (
|
Controller,
|
&gEfiUsbIoProtocolGuid,
|
This->DriverBindingHandle,
|
Controller);
|
|
//
|
// Free all resources.
|
//
|
if (UsbDisplayLinkDev->ControllerNameTable != NULL) {
|
FreeUnicodeStringTable (UsbDisplayLinkDev->ControllerNameTable);
|
}
|
|
if (UsbDisplayLinkDev->Screen != NULL) {
|
FreePool (UsbDisplayLinkDev->Screen);
|
UsbDisplayLinkDev->Screen = NULL;
|
}
|
|
if (UsbDisplayLinkDev->GraphicsOutputProtocol.Mode) {
|
if (UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info) {
|
FreePool (UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info);
|
UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info = NULL;
|
}
|
FreePool (UsbDisplayLinkDev->GraphicsOutputProtocol.Mode);
|
UsbDisplayLinkDev->GraphicsOutputProtocol.Mode = NULL;
|
}
|
|
FreePool (UsbDisplayLinkDev);
|
|
return EFI_SUCCESS;
|
|
}
|
|