/** @file Setup Mouse Protocol implementation ;****************************************************************************** ;* Copyright (c) 2012 - 2021, Insyde Software Corp. All Rights Reserved. ;* ;* You may not reproduce, distribute, publish, display, perform, modify, adapt, ;* transmit, broadcast, present, recite, release, license or otherwise exploit ;* any part of this publication in any form, by any means, without the prior ;* written permission of Insyde Software Corporation. ;* ;****************************************************************************** */ #include "SetupMouse.h" #include #include #include #include PRIVATE_MOUSE_DATA *mPrivate = NULL; EFI_HII_HANDLE mImageHiiHandle; // // BugBug: // For detect device is disconnected, so we hook protocol's driver binding protocol stop function. // Besides we only except below function to been hooked // // ConSplitter using EFI_OPEN_PROTOCOL_BY_DRIVER for SimplePointer / AbsolutePointer protocol; // ConSplitterSimplePointerDriverBindingStop and ConSplitterAbsolutePointerDriverBindingStop // // GraphicsConsole using EFI_OPEN_PROTOCOL_BY_DRIVER for GraphicsOutput protocol // GraphicsConsoleControllerDriverStop // #define SETUP_MOUSE_DRIVER_BINDING_HOOK_LIST_COUNT 3 struct { EFI_GUID *ProtocolGuid; EFI_DRIVER_BINDING_PROTOCOL *DriverBinding; EFI_DRIVER_BINDING_STOP StopFunction; } mSetupMouseDriverBindingHookList[SETUP_MOUSE_DRIVER_BINDING_HOOK_LIST_COUNT] = { {&gEfiSimplePointerProtocolGuid, NULL, NULL}, {&gEfiAbsolutePointerProtocolGuid, NULL, NULL}, {&gEfiGraphicsOutputProtocolGuid, NULL, NULL} }; EFI_STATUS HookDeviceStopFunction ( IN EFI_GUID *ProtocolGuid ); /** Get number of physical device according to assigned protocol guid. @param [in] ProtocolGuid Search physical device protocol guid. @retval Count of physical device according to protocol guid. **/ STATIC UINT32 GetNumberOfDevices ( IN EFI_GUID *ProtocolGuid ) { EFI_STATUS Status; UINTN Index; UINTN NumberOfHandles; EFI_HANDLE *HandleBuffer; UINT32 Count; EFI_DEVICE_PATH_PROTOCOL *DevicePath; Status = gBS->LocateHandleBuffer ( ByProtocol, ProtocolGuid, NULL, &NumberOfHandles, &HandleBuffer ); if (EFI_ERROR (Status)) { return 0; } // // make sure it is physical device // Count = 0; for (Index = 0; Index < NumberOfHandles; Index++) { Status = gBS->HandleProtocol ( HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **) &DevicePath ); if (EFI_ERROR (Status)) { continue; } Count++; } FreePool (HandleBuffer); return Count; } /** Get GOP device path. @param[in] GraphicsOutput Pointer to the GOP. @return The pointer of device path or NULL if not found. **/ STATIC EFI_DEVICE_PATH_PROTOCOL * GetGopDevPath ( IN EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput ) { EFI_STATUS Status; EFI_HANDLE *HandleBuffer; UINTN Count; UINTN Index; EFI_GRAPHICS_OUTPUT_PROTOCOL *Instance; EFI_DEVICE_PATH_PROTOCOL *DevicePath; if (GraphicsOutput == NULL) { return NULL; } Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiGraphicsOutputProtocolGuid, NULL, &Count, &HandleBuffer ); if (EFI_ERROR (Status)) { return NULL; } for (Index = 0; Index < Count; Index++) { Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiGraphicsOutputProtocolGuid, (VOID **) &Instance); if (!EFI_ERROR (Status) && Instance == GraphicsOutput) { break; } } if (Index == Count) { FreePool (HandleBuffer); return NULL; } Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **) &DevicePath); FreePool (HandleBuffer); if (EFI_ERROR (Status)) { return NULL; } return DevicePath; } VOID FreeGopEntry ( IN OUT GOP_ENTRY *GopEntry, IN BOOLEAN RestoreHook ) { ASSERT (GopEntry != NULL); // // Restore GOP // if (RestoreHook) { if (GopEntry->GraphicsOutput != NULL && GopEntry->OriginalBlt != NULL) { GopEntry->GraphicsOutput->Blt = GopEntry->OriginalBlt; GopEntry->GraphicsOutput->SetMode = GopEntry->OriginalSetMode; } } if (GopEntry->BlendBuffer != NULL) { FreePool (GopEntry->BlendBuffer); } if (GopEntry->FillLine != NULL) { FreePool (GopEntry->FillLine); } if (GopEntry->CheckBuffer != NULL) { FreePool (GopEntry->CheckBuffer); } if (GopEntry->Screen.Image != NULL) { FreePool (GopEntry->Screen.Image); } FreePool (GopEntry); } EFI_STATUS InitialGopEntry ( GOP_ENTRY *GopEntry, EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput ) { EFI_STATUS Status; UINTN ScreenBufferSize; EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *ModeInfo; GopEntry->Signature = GOP_ENTRY_SIGNATURE; ModeInfo = GraphicsOutput->Mode->Info; GopEntry->BytesPerScanLine = sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * ModeInfo->HorizontalResolution; if (GopEntry->FillLine != NULL) { FreePool (GopEntry->FillLine); } GopEntry->FillLine = AllocateZeroPool (GopEntry->BytesPerScanLine); if (GopEntry->FillLine == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error; } SetRect (&GopEntry->Screen.ImageRc, 0, 0, ModeInfo->HorizontalResolution, ModeInfo->VerticalResolution); SetRect (&GopEntry->InvalidateRc, 0, 0, ModeInfo->HorizontalResolution, ModeInfo->VerticalResolution); ScreenBufferSize = ModeInfo->HorizontalResolution * ModeInfo->VerticalResolution * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL); if (GopEntry->Screen.Image != NULL) { FreePool (GopEntry->Screen.Image); } GopEntry->Screen.Image = AllocateZeroPool (ScreenBufferSize); if (GopEntry->Screen.Image == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error; } if (mPrivate->IsStart) { Status = GraphicsOutput->Blt ( GraphicsOutput, GopEntry->Screen.Image, EfiBltVideoToBltBuffer, 0, 0, 0, 0, ModeInfo->HorizontalResolution, ModeInfo->VerticalResolution, GopEntry->BytesPerScanLine ); ASSERT_EFI_ERROR (Status); } if (GopEntry->BlendBuffer != NULL) { FreePool (GopEntry->BlendBuffer); } GopEntry->BlendBuffer = AllocateZeroPool (ScreenBufferSize); if (GopEntry->BlendBuffer == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error; } if (GopEntry->CheckBuffer != NULL) { FreePool (GopEntry->CheckBuffer); } GopEntry->CheckBuffer = AllocateZeroPool (ScreenBufferSize); if (GopEntry->CheckBuffer == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error; } GopEntry->DevicePath = GetGopDevPath (GraphicsOutput); return EFI_SUCCESS; Error: if (GopEntry->FillLine != NULL) { FreePool (GopEntry->FillLine); GopEntry->FillLine = NULL; } if (GopEntry->Screen.Image != NULL) { FreePool (GopEntry->Screen.Image); GopEntry->Screen.Image = NULL; } if (GopEntry->BlendBuffer != NULL) { FreePool (GopEntry->BlendBuffer); GopEntry->BlendBuffer = NULL; } if (GopEntry->CheckBuffer != NULL) { FreePool (GopEntry->CheckBuffer); GopEntry->CheckBuffer = NULL; } return Status; } EFI_STATUS EFIAPI SetupMouseGopSetMode ( IN EFI_GRAPHICS_OUTPUT_PROTOCOL * This, IN UINT32 ModeNumber ) { PRIVATE_MOUSE_DATA *Private; LIST_ENTRY *Node; GOP_ENTRY *GopEntry; EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE GopSetMode; EFI_TPL Tpl; UINT32 X; UINT32 Y; BOOLEAN KeyboardStarted; EFI_STATUS ReturnStatus; BOOLEAN SetupMouseIsStart; Private = mPrivate; GopEntry = NULL; Tpl = gBS->RaiseTPL (TPL_NOTIFY); Node = GetFirstNode (&Private->GopList); while (!IsNull (&Private->GopList, Node)) { GopEntry = GOP_ENTRY_FROM_THIS (Node); if (GopEntry->GraphicsOutput == This) { break; } Node = GetNextNode (&Private->GopList, Node); } ASSERT (!IsNull (&Private->GopList, Node)); if (IsNull (&Private->GopList, Node) || GopEntry == NULL) { gBS->RestoreTPL (Tpl); return EFI_UNSUPPORTED; } KeyboardStarted = Private->Keyboard.Visible; X = 0; Y = 0; if (KeyboardStarted) { X = (INT32)Private->Keyboard.ImageRc.left; Y = (INT32)Private->Keyboard.ImageRc.top; } SetupMouseIsStart = Private->IsStart; GopSetMode = GopEntry->OriginalSetMode; if (Private->IsStart) { SetupMouseClose (&Private->SetupMouse); } // // Free GOP list // while (!IsListEmpty (&Private->GopList)) { GopEntry = GOP_ENTRY_FROM_THIS (GetFirstNode (&Private->GopList)); RemoveEntryList (&GopEntry->Link); FreeGopEntry (GopEntry, TRUE); } ReturnStatus = GopSetMode (This, ModeNumber); if (EFI_ERROR (ReturnStatus)) { gBS->RestoreTPL (Tpl); return ReturnStatus; } if (!SetupMouseIsStart) { if (Private->GopHotplugEvent != NULL) { gBS->SignalEvent (Private->GopHotplugEvent); } } else { SetupMouseStart (&Private->SetupMouse); if (KeyboardStarted) { StartKeyboard (&Private->SetupMouse, X, Y); } } gBS->RestoreTPL (Tpl); return ReturnStatus; } EFI_STATUS RetrieveGraphicsOutputInfo ( OUT PRIVATE_MOUSE_DATA *Private ) { EFI_STATUS Status; UINTN Index; UINTN Count; EFI_HANDLE *HandleBuffer; EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; EFI_GRAPHICS_OUTPUT_PROTOCOL **GraphicsOutputList; UINTN GopCount; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOut; EFI_DEVICE_PATH_PROTOCOL *DevicePath; GOP_ENTRY *GopEntry; LIST_ENTRY *Node; ASSERT_LOCKED (&Private->SetupMouseLock); Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiGraphicsOutputProtocolGuid, NULL, &Count, &HandleBuffer ); if (EFI_ERROR (Status)) { return Status; } // // Retrieve GOP list // GraphicsOutputList = AllocateZeroPool (Count * sizeof (EFI_GRAPHICS_OUTPUT_PROTOCOL*)); if (GraphicsOutputList == NULL) { FreePool (HandleBuffer); return EFI_OUT_OF_RESOURCES; } GopCount = 0; for (Index = 0; Index < Count; Index++) { Status = gBS->HandleProtocol(HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath); if (EFI_ERROR (Status)) { continue; } Status = gBS->HandleProtocol(HandleBuffer[Index], &gEfiSimpleTextOutProtocolGuid, (VOID **)&SimpleTextOut); if (EFI_ERROR (Status)) { continue; } Status = gBS->HandleProtocol(HandleBuffer[Index], &gEfiGraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput); if (EFI_ERROR (Status)) { continue; } GraphicsOutputList[GopCount++] = GraphicsOutput; } FreePool (HandleBuffer); // // check the gop list valid // Node = GetFirstNode (&Private->GopList); while (!IsNull (&Private->GopList, Node)) { GopEntry = GOP_ENTRY_FROM_THIS (Node); for (Index = 0; Index < GopCount; Index++) { if (GraphicsOutputList[Index] == GopEntry->GraphicsOutput) { GraphicsOutputList[Index] = NULL; break; } } // // remove invalid GopEntry // if (Index == GopCount) { Node = RemoveEntryList (&GopEntry->Link); FreeGopEntry (GopEntry, FALSE); continue; } Node = GetNextNode (&Private->GopList, Node); } // // add new GOP // for (Index = 0; Index < GopCount; Index++) { GraphicsOutput = GraphicsOutputList[Index]; if (GraphicsOutput == NULL) { continue; } GopEntry = AllocateZeroPool (sizeof (GOP_ENTRY)); if (GopEntry == NULL) { Status = EFI_OUT_OF_RESOURCES; continue; } Status = InitialGopEntry (GopEntry, GraphicsOutput); if (EFI_ERROR (Status)) { FreePool (GopEntry); continue; } InsertTailList (&Private->GopList, &GopEntry->Link); // // hook gop blt function // GopEntry->GraphicsOutput = GraphicsOutput; GopEntry->OriginalBlt = GraphicsOutput->Blt; GraphicsOutput->Blt = SetupMouseScreenBlt; GopEntry->OriginalSetMode = GraphicsOutput->SetMode; GraphicsOutput->SetMode = SetupMouseGopSetMode; } FreePool (GraphicsOutputList); return EFI_SUCCESS; } EFI_STATUS EFIAPI SetCursorPos ( IN UINTN X, IN UINTN Y ) { PRIVATE_MOUSE_DATA *Private; EFI_STATUS Status; Private = mPrivate; AcquireSetupMouseLock (Private); Status = InternalSetCursorPos (X, Y); ReleaseSetupMouseLock (Private); return Status; } /** Determine mouse or touch devices are existence. **/ VOID EFIAPI DetermineDevicesExistence ( VOID ) { PRIVATE_MOUSE_DATA *Private; UINT32 MouseCount; UINT32 TouchCount; GOP_ENTRY *GopEntry; Private = mPrivate; AcquireSetupMouseLock (Private); RetrieveGraphicsOutputInfo (Private); if (!Private->IsStart) { ReleaseSetupMouseLock (Private); return ; } MouseCount = GetNumberOfDevices (&gEfiSimplePointerProtocolGuid); TouchCount = GetNumberOfDevices (&gEfiAbsolutePointerProtocolGuid); // // Gop == 0 : reset cursor // SaveCursorX == -1 and SaveCursorY == -1 : set cursor in center of screen // if (IsListEmpty (&Private->GopList)) { Private->MouseRange.StartX = 0; Private->MouseRange.StartY = 0; Private->MouseRange.EndX = 0; Private->MouseRange.EndY = 0; Private->SaveCursorX = (UINT32) -1; Private->SaveCursorY = (UINT32) -1; ReleaseSetupMouseLock (Private); return ; } GopEntry = GOP_ENTRY_FROM_THIS (GetFirstNode (&Private->GopList)); Private->MouseRange.StartX = 0; Private->MouseRange.StartY = 0; Private->MouseRange.EndX = GopEntry->GraphicsOutput->Mode->Info->HorizontalResolution - 1; Private->MouseRange.EndY = GopEntry->GraphicsOutput->Mode->Info->VerticalResolution - 1; if ((Private->SaveCursorX == (UINT32) -1) && (Private->SaveCursorY == (UINT32) -1)) { Private->SaveCursorX = GopEntry->GraphicsOutput->Mode->Info->HorizontalResolution / 2; Private->SaveCursorY = GopEntry->GraphicsOutput->Mode->Info->VerticalResolution / 2; } InternalSetCursorPos (Private->SaveCursorX, Private->SaveCursorY); if (MouseCount == 0) { HideImage (&Private->Cursor); } else if (!Private->HideCursor) { ShowImage (&Private->Cursor); } ReleaseSetupMouseLock (Private); } /** Callback function for devices plugs in, it will hook device's DriverBinding stop function. @param [in] Event Event for SimplePointer / AbsolutePointer. @param [in] Context Context of event. **/ VOID EFIAPI InputDevicePluginCallback ( IN EFI_EVENT Event, IN VOID *Context ) { PRIVATE_MOUSE_DATA *Private; EFI_TPL Tpl; Private = mPrivate; HookDeviceStopFunction (&gEfiSimplePointerProtocolGuid); HookDeviceStopFunction (&gEfiAbsolutePointerProtocolGuid); Tpl = gBS->RaiseTPL (TPL_NOTIFY); DetermineDevicesExistence (); gBS->RestoreTPL (Tpl); } /** Callback function for devices plugs in, it will hook device's DriverBinding stop function. @param [in] Event Event for SimplePointer / AbsolutePointer. @param [in] Context Context of event. **/ VOID EFIAPI OutputDevicePluginCallback ( IN EFI_EVENT Event, IN VOID *Context ) { PRIVATE_MOUSE_DATA *Private; EFI_TPL Tpl; Private = mPrivate; HookDeviceStopFunction (&gEfiGraphicsOutputProtocolGuid); Tpl = gBS->RaiseTPL (TPL_NOTIFY); DetermineDevicesExistence (); gBS->RestoreTPL (Tpl); } /** Callback function for EDID active protocol to hook GOP Blt function again if it is restored by GOP driver. @param [in] Event Event for Edid active protocol @param [in] Context Context of event. **/ VOID EFIAPI EdidActivePluginCallback ( IN EFI_EVENT Event, IN VOID *Context ) { PRIVATE_MOUSE_DATA *Private; LIST_ENTRY *Node; GOP_ENTRY *GopEntry; EFI_DEVICE_PATH_PROTOCOL *DevicePath; Private = mPrivate; AcquireSetupMouseLock (Private); for (Node = GetFirstNode (&Private->GopList); !IsNull (&Private->GopList, Node); Node = GetNextNode (&Private->GopList, Node)) { GopEntry = GOP_ENTRY_FROM_THIS (Node); if (GopEntry->GraphicsOutput->Blt == SetupMouseScreenBlt && GopEntry->GraphicsOutput->SetMode == SetupMouseGopSetMode) { continue; } DevicePath = GetGopDevPath (GopEntry->GraphicsOutput); if (GopEntry->DevicePath != DevicePath) { GopEntry->GraphicsOutput->Blt = SetupMouseScreenBlt; GopEntry->GraphicsOutput->SetMode = SetupMouseGopSetMode; GopEntry->DevicePath = DevicePath; } } ReleaseSetupMouseLock (Private); } /** According to ProtocolGuid to search which DriverBinding protocol OpenProtocol by EFI_OPEN_PROTOCOL_BY_DRIVER, and then hook the DriverBinding protocol stop function @param [in] ControllerHandle To hook driver binding stop function from ControllerHandle. @param [in] ProtocolGuid ProtocolGuid which OpenProtocol by EFI_OPEN_PROTOCOL_BY_DRIVER. @retval EFI_SUCCESS Hook DriverBinding protocol stop function success. @retval EFI_NOT_FOUND Can't found the assigned DriverBinding protocol. @retval Others Fail to get return status data. **/ EFI_STATUS EFIAPI SetupMouseDriverBindingStop ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer ) { EFI_STATUS Status; UINTN Index; EFI_DRIVER_BINDING_STOP StopFunction; EFI_GUID *ProtocolGuid; PRIVATE_MOUSE_DATA *Private; Private = mPrivate; StopFunction = NULL; ProtocolGuid = NULL; for (Index = 0; Index < SETUP_MOUSE_DRIVER_BINDING_HOOK_LIST_COUNT; Index++) { if (This == mSetupMouseDriverBindingHookList[Index].DriverBinding) { ProtocolGuid = mSetupMouseDriverBindingHookList[Index].ProtocolGuid; StopFunction = mSetupMouseDriverBindingHookList[Index].StopFunction; break; } } ASSERT (StopFunction != NULL); ASSERT (ProtocolGuid != NULL); if (StopFunction == NULL || ProtocolGuid == NULL) { return EFI_INVALID_PARAMETER; } Status = StopFunction (This, ControllerHandle, NumberOfChildren, ChildHandleBuffer); if (EFI_ERROR (Status)) { return Status; } if (CompareGuid (ProtocolGuid, &gEfiGraphicsOutputProtocolGuid) || Private->IsStart) { DetermineDevicesExistence (); } return Status; } /** According to ProtocolGuid to search which DriverBinding protocol OpenProtocol by EFI_OPEN_PROTOCOL_BY_DRIVER, and then hook the DriverBinding protocol stop function @param [in] ControllerHandle To hook driver binding stop function from ControllerHandle. @param [in] ProtocolGuid ProtocolGuid which OpenProtocol by EFI_OPEN_PROTOCOL_BY_DRIVER. @retval EFI_SUCCESS Hook DriverBinding protocol stop function success. @retval EFI_NOT_FOUND Can't found the assigned DriverBinding protocol. @retval Others Fail to get return status data. **/ EFI_STATUS HookSingleDeviceStopFunction ( IN EFI_HANDLE ControllerHandle, IN EFI_GUID *ProtocolGuid ) { EFI_STATUS Status; EFI_HANDLE AgentHandle; EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; UINTN EntryCount; UINTN Index; EFI_DRIVER_BINDING_PROTOCOL *DriverBinding; PRIVATE_MOUSE_DATA *Private; EFI_TPL OriginalTPL; ASSERT (ProtocolGuid != NULL); Private = mPrivate; Status = gBS->OpenProtocolInformation ( ControllerHandle, (EFI_GUID *) ProtocolGuid, &OpenInfoBuffer, &EntryCount ); if (EFI_ERROR (Status)) { return Status; } AgentHandle = NULL; for (Index = 0; Index < EntryCount; Index++) { if ((OpenInfoBuffer[Index].ControllerHandle == ControllerHandle) && (OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) != 0) { AgentHandle = OpenInfoBuffer[Index].AgentHandle; break; } } FreePool (OpenInfoBuffer); if (AgentHandle == NULL) { return EFI_NOT_FOUND; } // // locate driver binding protocol // Status = gBS->HandleProtocol ( AgentHandle, &gEfiDriverBindingProtocolGuid, (VOID **) &DriverBinding ); if (EFI_ERROR (Status)) { return EFI_NOT_FOUND; } // // hook driver binding stop function // for (Index = 0; Index < SETUP_MOUSE_DRIVER_BINDING_HOOK_LIST_COUNT; Index++) { if (CompareGuid (mSetupMouseDriverBindingHookList[Index].ProtocolGuid, ProtocolGuid)) { if (DriverBinding->Stop != SetupMouseDriverBindingStop) { OriginalTPL = gBS->RaiseTPL(TPL_NOTIFY); mSetupMouseDriverBindingHookList[Index].DriverBinding = DriverBinding; mSetupMouseDriverBindingHookList[Index].StopFunction = DriverBinding->Stop; DriverBinding->Stop = SetupMouseDriverBindingStop; gBS->RestoreTPL (OriginalTPL); } return EFI_SUCCESS; } } return EFI_NOT_FOUND; } /** Search physical device protocol which OpenProtocol by OPEN_PROTOCOL_BY_DRIVER and then hook the DriverBinding protocol stop function for monitor the protocol. @param [in] ProtocolGuid Searched physical device protocol @retval EFI_SUCCESS Hook DriverBinding protocol stop function success. @retval Others Fail to get return status data. **/ EFI_STATUS HookDeviceStopFunction ( IN EFI_GUID *ProtocolGuid ) { EFI_STATUS Status; UINTN Index; UINTN NumberOfHandles; EFI_HANDLE *HandleBuffer; EFI_DEVICE_PATH_PROTOCOL *DevicePath; Status = gBS->LocateHandleBuffer ( ByProtocol, ProtocolGuid, NULL, &NumberOfHandles, &HandleBuffer ); if (EFI_ERROR (Status)) { return Status; } // // make sure it is physical device // for (Index = 0; Index < NumberOfHandles; Index++) { Status = gBS->HandleProtocol ( HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **) &DevicePath ); if (EFI_ERROR (Status)) { continue; } HookSingleDeviceStopFunction (HandleBuffer[Index], ProtocolGuid); } FreePool (HandleBuffer); return EFI_SUCCESS; } EFI_STATUS EFIAPI StartImageSupportVirtualKeyboard ( IN EFI_HANDLE ImageHandle, OUT UINTN *ExitDataSize, OUT CHAR16 **ExitData OPTIONAL ); /** This function uses to enable sync frame buffer @param Event Event whose notification function is being invoked. @param Context Pointer to the notification function's context. **/ VOID EFIAPI SetupMouseReadyToBootCallback ( IN EFI_EVENT Event, IN VOID *Context ) { PRIVATE_MOUSE_DATA *Private; Private = SETUP_MOUSE_DEV_FROM_THIS (Context); Private->NeedSyncFrameBuffer = TRUE; if (Private->OrgStartImage == NULL) { Private->OrgStartImage = gBS->StartImage; gBS->StartImage = StartImageSupportVirtualKeyboard; gBS->Hdr.CRC32 = 0; gBS->CalculateCrc32 ((UINT8 *)gBS, gBS->Hdr.HeaderSize, &gBS->Hdr.CRC32); } } /** This function uses to disable sync frame buffer @param Event Event whose notification function is being invoked. @param Context Pointer to the notification function's context. **/ VOID EFIAPI SetupMouseReturnFromImageCallback ( IN EFI_EVENT Event, IN VOID *Context ) { PRIVATE_MOUSE_DATA *Private; Private = SETUP_MOUSE_DEV_FROM_THIS (Context); Private->NeedSyncFrameBuffer = FALSE; } /** Initialize Setup @param [in] ImageHandle @param [in] SystemTable @retval EFI_SUCCESS Setup Mouse loaded. @return other Setup Mouse Error **/ EFI_STATUS EFIAPI InitializeSetupMouse ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; PRIVATE_MOUSE_DATA *Private; EFI_EVENT Event; VOID *Registration; Private = AllocateZeroPool (sizeof (PRIVATE_MOUSE_DATA)); if (Private == NULL) { return EFI_OUT_OF_RESOURCES; } Private->Signature = SETUP_MOUSE_SIGNATURE; Private->SetupMouse.Start = SetupMouseStart; Private->SetupMouse.Close = SetupMouseClose; Private->SetupMouse.QueryState = QueryState; Private->SetupMouse.StartKeyboard = StartKeyboard; Private->SetupMouse.CloseKeyboard = CloseKeyboard; Private->SetupMouse.SetMode = SetupMouseSetMode; Private->SetupMouse.SetKeyboardAttributes = SetupMouseSetKeyboardAttributes; Private->SetupMouse.GetKeyboardAttributes = SetupMouseGetKeyboardAttributes; Private->SetupMouse.SetMouseAttributes = SetupMouseSetMouseAttributes; Private->SetupMouse.GetMouseAttributes = SetupMouseGetMouseAttributes; Private->SaveCursorX = (UINT32) -1; Private->SaveCursorY = (UINT32) -1; Private->IsStart = FALSE; Private->HideCursor = FALSE; Private->HideCursorWhenTouch = TRUE; Private->NeedSyncFrameBuffer = FALSE; InitializeListHead (&Private->GopList); EfiInitializeLock (&Private->SetupMouseLock, TPL_NOTIFY); mPrivate = Private; Status = EfiCreateEventReadyToBootEx ( TPL_NOTIFY, SetupMouseReadyToBootCallback, (VOID *) &Private->SetupMouse, &Event ); ASSERT_EFI_ERROR (Status); Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, TPL_NOTIFY, SetupMouseReturnFromImageCallback, (VOID *) &Private->SetupMouse, &Event ); ASSERT_EFI_ERROR (Status); if (!EFI_ERROR (Status)) { Status = gBS->RegisterProtocolNotify ( &gReturnFromImageGuid, Event, &Registration ); ASSERT_EFI_ERROR (Status); } mImageHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, NULL, SetupMouseDxeImages, NULL); Status = gBS->InstallProtocolInterface ( &(Private->Handle), &gSetupMouseProtocolGuid, EFI_NATIVE_INTERFACE, &(Private->SetupMouse) ); return Status; } /** SetupMouse set to graphics / text mode coordinate Can avoid user call SetupMouse->Start in graphics Mode, but he want use SetupMouse in text mode and vice versa. @param [in] SetupMouse EFI_SETUP_MOUSE_PROTOCOL @param [in] Mode Graphics / Text mode @retval EFI_SUCCESS Set mode success @retval EFI_NOT_READY SetupMouse doesn't start **/ EFI_STATUS EFIAPI SetupMouseSetMode ( IN EFI_SETUP_MOUSE_PROTOCOL *SetupMouse, IN EFI_SETUP_MOUSE_SCREEN_MODE Mode ) { return EFI_UNSUPPORTED; } STATIC EFI_STATUS RetrievePointerProtocolInfo ( PRIVATE_MOUSE_DATA *Private ) { UINTN Count; EFI_STATUS Status; EFI_SIMPLE_POINTER_PROTOCOL *SimplePointer; EFI_ABSOLUTE_POINTER_PROTOCOL *AbsolutePointer; POINTER_PROTOCOL_INFO *PointerProtocolInfo; EFI_SIMPLE_POINTER_STATE SimplePointerState; Count = 0; // // Initial ConSplitter's Simple Pointer // SimplePointer = NULL; Status = gBS->HandleProtocol( gST->ConsoleInHandle, &gEfiSimplePointerProtocolGuid, (VOID**)&SimplePointer ); if (!EFI_ERROR (Status)) { Status = SimplePointer->Reset (SimplePointer, TRUE); if (!EFI_ERROR (Status)) { Count ++; } } // // Initial ConSplitter's Absolute Pointer // AbsolutePointer = NULL; Status = gBS->HandleProtocol( gST->ConsoleInHandle, &gEfiAbsolutePointerProtocolGuid, (VOID**)&AbsolutePointer ); if (!EFI_ERROR (Status)) { Status = AbsolutePointer->Reset (AbsolutePointer, TRUE); if (!EFI_ERROR (Status)) { Count ++; } } if (!Count) { return EFI_NOT_FOUND; } PointerProtocolInfo = (POINTER_PROTOCOL_INFO *) AllocateZeroPool ( sizeof (POINTER_PROTOCOL_INFO) + sizeof (POINTER_PROTOCOL_INFO_DATA) * Count); if (PointerProtocolInfo == NULL) { return EFI_OUT_OF_RESOURCES; } PointerProtocolInfo->Count = 0; // // Fill in PointerProtocolInfo with ConSplitter's Simple Pointer protocol // if (SimplePointer) { // // PS2 mouse GetState() will reset mouse in first time, it will waste much time. // So we call GetState() early to avoid interrupt other process. // SimplePointer->GetState(SimplePointer, &SimplePointerState); PointerProtocolInfo->Data[PointerProtocolInfo->Count].Attributes = ATTRIBUTE_VALUE_SIMPLE; PointerProtocolInfo->Data[PointerProtocolInfo->Count].PointerProtocol = SimplePointer; PointerProtocolInfo->Count++; } // // Fill in PointerProtocolInfo with information about all the Absolute Pointer protocols // if (AbsolutePointer) { PointerProtocolInfo->Data[PointerProtocolInfo->Count].Attributes = ATTRIBUTE_VALUE_ABSOLUTE; PointerProtocolInfo->Data[PointerProtocolInfo->Count].PointerProtocol = AbsolutePointer; PointerProtocolInfo->Count++; } // // Update PointerProtocolInfo to private data // Private->PointerProtocolInfo = PointerProtocolInfo; return EFI_SUCCESS; } VOID FreeGopEntryList ( PRIVATE_MOUSE_DATA *Private ) { GOP_ENTRY *GopEntry; while (!IsListEmpty (&Private->GopList)) { GopEntry = GOP_ENTRY_FROM_THIS (GetFirstNode (&Private->GopList)); RemoveEntryList (&GopEntry->Link); FreeGopEntry (GopEntry, TRUE); } } /** Start SetupMouse @param [in] SetupMouse EFI_SETUP_MOUSE_PROTOCOL @retval EFI_SUCCESS Start success @retval EFI_NOT_FOUND Can't find GOP protocol **/ EFI_STATUS EFIAPI SetupMouseStart ( IN EFI_SETUP_MOUSE_PROTOCOL *SetupMouse ) { EFI_STATUS Status; PRIVATE_MOUSE_DATA *Private; EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; VOID *Registration; GOP_ENTRY *GopEntry; LIST_ENTRY *Node; EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *ModeInfo; Private = SETUP_MOUSE_DEV_FROM_THIS (SetupMouse); if (Private->IsStart) { return EFI_SUCCESS; } AcquireSetupMouseLock (Private); Status = RetrievePointerProtocolInfo (Private); if (EFI_ERROR (Status)) { goto QUIT; } Status = InitializeCursor (Private); if (EFI_ERROR (Status)) { goto QUIT; } Private->MouseRange.StartX = 0; Private->MouseRange.StartY = 0; Private->MouseRange.EndX = 0; Private->MouseRange.EndY = 0; // // Reset setup mouse state queue // ZeroMem (Private->State, sizeof (Private->State)); Private->BufferIn = 0; Private->BufferOut = 0; // // Using CSM enable to make sure BDA existence. // Status = gBS->LocateProtocol ( &gEfiLegacyBiosProtocolGuid, NULL, (VOID **)&LegacyBios ); if (!EFI_ERROR (Status)) { Private->IsCsmEnabled = TRUE; } else { Private->IsCsmEnabled = FALSE; } // // Set start flag, let SetupMouseSetMode work // Private->IsStart = TRUE; // //Create Setup Mouse event // Status = gBS->CreateEvent ( EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY, ProcessMouse, (VOID*)SetupMouse, &SetupMouse->Event ); ASSERT_EFI_ERROR (Status); Status = gBS->SetTimer (SetupMouse->Event, TimerPeriodic, MOUSE_TIMER); if (EFI_ERROR (Status)) { goto QUIT; } Private->MouseHotplugEvent = EfiCreateProtocolNotifyEvent ( &gEfiSimplePointerProtocolGuid, TPL_NOTIFY - 1, InputDevicePluginCallback, NULL, &Registration ); Private->TouchHotplugEvent = EfiCreateProtocolNotifyEvent ( &gEfiAbsolutePointerProtocolGuid, TPL_NOTIFY - 1, InputDevicePluginCallback, NULL, &Registration ); Private->EdidActiveHotplugEvent = EfiCreateProtocolNotifyEvent ( &gEfiEdidActiveProtocolGuid, TPL_NOTIFY - 1, EdidActivePluginCallback, NULL, &Registration ); if (Private->GopHotplugEvent == NULL) { Private->GopHotplugEvent = EfiCreateProtocolNotifyEvent ( &gEfiSimpleTextOutProtocolGuid, TPL_NOTIFY - 1, OutputDevicePluginCallback, NULL, &Registration ); } Node = GetFirstNode (&mPrivate->GopList); while (!IsNull (&mPrivate->GopList, Node)) { GopEntry = GOP_ENTRY_FROM_THIS (Node); GraphicsOutput = GopEntry->GraphicsOutput; ModeInfo = GraphicsOutput->Mode->Info; Status = GraphicsOutput->Blt ( GraphicsOutput, GopEntry->Screen.Image, EfiBltVideoToBltBuffer, 0, 0, 0, 0, ModeInfo->HorizontalResolution, ModeInfo->VerticalResolution, GopEntry->BytesPerScanLine ); ASSERT_EFI_ERROR (Status); Node = GetNextNode (&Private->GopList, Node); } ReleaseSetupMouseLock (Private); DetermineDevicesExistence (); return EFI_SUCCESS; QUIT: ASSERT_EFI_ERROR (Status); Private->IsStart = FALSE; if (Private->PointerProtocolInfo != NULL) { FreePool (Private->PointerProtocolInfo); Private->PointerProtocolInfo = NULL; } ReleaseSetupMouseLock (Private); return Status; } EFI_STATUS EFIAPI SetupMouseClose ( IN EFI_SETUP_MOUSE_PROTOCOL *SetupMouse ) /*++ Routine Description: Stop SetupMouse Arguments: SetupMouse - EFI_SETUP_MOUSE_PROTOCOL Returns: Start setup mouse --*/ { PRIVATE_MOUSE_DATA *Private; GOP_ENTRY *GopEntry; Private = SETUP_MOUSE_DEV_FROM_THIS (SetupMouse); AcquireSetupMouseLock (Private); if (!Private->IsStart) { ReleaseSetupMouseLock (Private); return EFI_SUCCESS; } Private->IsStart = FALSE; Private->HideCursor = FALSE; gBS->CloseEvent (SetupMouse->Event); gBS->CloseEvent (Private->MouseHotplugEvent); gBS->CloseEvent (Private->TouchHotplugEvent); gBS->CloseEvent (Private->EdidActiveHotplugEvent); gBS->CloseEvent (Private->GopHotplugEvent); Private->GopHotplugEvent = NULL; HideImage (&Private->Cursor); InternalCloseKeyboard (SetupMouse); RenderImageForAllGop (Private); DestroyCursor (Private); // // Free GOP list // while (!IsListEmpty (&Private->GopList)) { GopEntry = GOP_ENTRY_FROM_THIS (GetFirstNode (&Private->GopList)); RemoveEntryList (&GopEntry->Link); FreeGopEntry (GopEntry, TRUE); } if (Private->PointerProtocolInfo) { FreePool (Private->PointerProtocolInfo); Private->PointerProtocolInfo = NULL; } Private->LButton = FALSE; Private->RButton = FALSE; ReleaseSetupMouseLock (Private); return EFI_SUCCESS; } /** Query cursor coordinate and button state, if ScreenMode is text, it will convert coordinate @param [in] SetupMouse EFI_SETUP_MOUSE_PROTOCOL @param [out] X X coordinate @param [out] Y Y coordinate @param [out] LeftButton Mouse left button is pressed or Touch panel is touched @param [out] RightButton Right button is touched @retval EFI_NOT_READY There was no keystroke data availiable @retval EFI_SUCCESS Query status success **/ EFI_STATUS EFIAPI QueryState ( IN EFI_SETUP_MOUSE_PROTOCOL *SetupMouse, OUT UINTN *X, OUT UINTN *Y, OUT BOOLEAN *LeftButton, OUT BOOLEAN *RightButton ) { PRIVATE_MOUSE_DATA *Private; SETUP_MOUSE_STATE *State; Private = SETUP_MOUSE_DEV_FROM_THIS (SetupMouse); if (!Private->IsStart) { return EFI_NOT_READY; } if (!Private->HaveRawData) { return EFI_NOT_READY; } AcquireSetupMouseLock (Private); State = &Private->State[Private->BufferOut]; *X = (UINTN) State->CurrentX; *Y = (UINTN) State->CurrentY; *LeftButton = State->LButton; *RightButton = State->RButton; if (Private->BufferOut != Private->BufferIn) { Private->BufferOut++; if (Private->BufferOut == STATE_BUFFER_SIZE) Private->BufferOut = 0; } if (Private->BufferOut == Private->BufferIn) { Private->HaveRawData = FALSE; } ReleaseSetupMouseLock (Private); return EFI_SUCCESS; } // // SetupMouse Virtual Keyboard // EFI_STATUS EFIAPI StartKeyboard ( IN EFI_SETUP_MOUSE_PROTOCOL *SetupMouse, IN UINTN X, IN UINTN Y ) { EFI_STATUS Status; PRIVATE_MOUSE_DATA *Private; Private = SETUP_MOUSE_DEV_FROM_THIS (SetupMouse); SetupMouse->Start (SetupMouse); if (PcdGetBool (PcdDisableVirtualKeyboard)) { return EFI_SUCCESS; } AcquireSetupMouseLock (Private); Status = InternalStartKeyboard (SetupMouse, X, Y); ReleaseSetupMouseLock (Private); return Status; } /** KeyboardData function restores data from blt buffer to the screen. @param [in] SetupMouse @retval EFI_SUCCESS Screen has been restored from blt buffer successfully **/ EFI_STATUS EFIAPI CloseKeyboard ( IN EFI_SETUP_MOUSE_PROTOCOL *SetupMouse ) { EFI_STATUS Status; PRIVATE_MOUSE_DATA *Private; Private = SETUP_MOUSE_DEV_FROM_THIS (SetupMouse); AcquireSetupMouseLock (Private); Status = InternalCloseKeyboard (SetupMouse); ReleaseSetupMouseLock (Private); return Status; } EFI_STATUS EFIAPI SetupMouseSetKeyboardAttributes ( IN EFI_SETUP_MOUSE_PROTOCOL *SetupMouse, IN KEYBOARD_ATTRIBUTES *KeyboardAttributes ) { EFI_STATUS Status; PRIVATE_MOUSE_DATA *Private; Private = SETUP_MOUSE_DEV_FROM_THIS (SetupMouse); AcquireSetupMouseLock (Private); Status = InternalSetupMouseSetKeyboardAttributes (SetupMouse, KeyboardAttributes); ReleaseSetupMouseLock (Private); return Status; } EFI_STATUS EFIAPI SetupMouseGetKeyboardAttributes ( IN EFI_SETUP_MOUSE_PROTOCOL *SetupMouse, IN KEYBOARD_ATTRIBUTES *KeyboardAttributes ) { EFI_STATUS Status; PRIVATE_MOUSE_DATA *Private; Private = SETUP_MOUSE_DEV_FROM_THIS (SetupMouse); AcquireSetupMouseLock (Private); Status = InternalSetupMouseGetKeyboardAttributes (SetupMouse, KeyboardAttributes); ReleaseSetupMouseLock (Private); return Status; } EFI_STATUS EFIAPI SetupMouseSetMouseAttributes ( IN EFI_SETUP_MOUSE_PROTOCOL *SetupMouse, IN MOUSE_ATTRIBUTES *MouseAttributes ) { PRIVATE_MOUSE_DATA *Private; if (SetupMouse == NULL || MouseAttributes == NULL) { return EFI_INVALID_PARAMETER; } Private = SETUP_MOUSE_DEV_FROM_THIS (SetupMouse); AcquireSetupMouseLock (Private); if ((MouseAttributes->Flags & MOUSE_FLAG_VISIBLE) == MOUSE_FLAG_VISIBLE) { Private->HideCursor = !MouseAttributes->Visible; if (MouseAttributes->Visible) { if (!Private->Cursor.Visible) { ShowImage (&Private->Cursor); RenderImageForAllGop (Private); } } else { if (Private->Cursor.Visible) { HideImage (&Private->Cursor); RenderImageForAllGop (Private); } } } ReleaseSetupMouseLock (Private); return EFI_SUCCESS; } EFI_STATUS EFIAPI SetupMouseGetMouseAttributes ( IN EFI_SETUP_MOUSE_PROTOCOL *SetupMouse, OUT MOUSE_ATTRIBUTES *MouseAttributes ) { PRIVATE_MOUSE_DATA *Private; if (SetupMouse == NULL || MouseAttributes == NULL) { return EFI_INVALID_PARAMETER; } Private = SETUP_MOUSE_DEV_FROM_THIS (SetupMouse); AcquireSetupMouseLock (Private); MouseAttributes->Length = sizeof (MOUSE_ATTRIBUTES); MouseAttributes->Flags = MOUSE_FLAG_VISIBLE; MouseAttributes->Visible = !Private->HideCursor; ReleaseSetupMouseLock (Private); return EFI_SUCCESS; }