/** * @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 #include #include #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; }