/** @file AmiEfiPointer to EfiPointer translator. Copyright (c) 2016, vit9696. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ #include "AIM.h" #include #include #include STATIC AMI_SHIM_POINTER mAmiShimPointer; STATIC UINT8 gIndex = 0; STATIC UINT8 gXCounts[5] = {0, 0, 0, 0, 0}; STATIC UINT8 gYCounts[5] = {0, 0, 0, 0, 0}; UINT8 EFIAPI Abs ( IN INT32 Value ) { return (UINT8)(Value < 0 ? -Value : Value); } INT32 EFIAPI InternalClamp ( IN INT32 Value, IN INT32 Min, IN INT32 Max ) { if (Value > Max) { return Max; } else if (Value < Min) { return Min; } return Value; } VOID EFIAPI AmiShimPointerFilterOut ( IN OUT UINT8 *AbsX, IN OUT UINT8 *AbsY, IN OUT INT32 *X, IN OUT INT32 *Y ) { UINT8 Index; if (*AbsX == 0) { gXCounts[gIndex] = 1; } else if (*AbsX <= 2) { for (Index = 0; Index < 5; Index++) { if (!gXCounts[Index]) break; } if (Index == 5) { *AbsX = 0; *X = 0; } gXCounts[gIndex] = 0; } if (*AbsY == 0) { gYCounts[gIndex] = 1; } else if (*AbsY <= 2) { for (Index = 0; Index < 5; Index++) { if (!gYCounts[Index]) break; } if (Index == 5) { *AbsY = 0; *Y = 0; } gYCounts[gIndex] = 0; } gIndex = gIndex < 4 ? gIndex + 1 : 0; } #ifdef AMI_SHIM_POINTER_AMI_SMOOTHING STATIC UINT8 gAbsRange[8] = {42, 36, 30, 24, 18, 12, 6, 1}; STATIC INT32 gMultipliers[8] = {8, 7, 6, 5, 4, 3, 2, 1}; STATIC INT32 gIndices[8] = {2, 2, 2, 2, 2, 2, 2, 0}; INT32 AmiShimPointerScale ( IN INT32 Value, IN UINT8 AbsValue ) { UINT8 TmpIndex; for (TmpIndex = 0; TmpIndex < 8; TmpIndex++) { if (AbsValue >= gAbsRange[TmpIndex]) { break; } } if (TmpIndex != 8) { if (Value >= 0) { Value = gIndices[TmpIndex] + Value * gMultipliers[TmpIndex]; } else { Value = Value * gMultipliers[TmpIndex] - gIndices[TmpIndex]; } } return Value; } VOID EFIAPI AmiShimPointerSmooth ( IN OUT INT32 *X, IN OUT INT32 *Y, IN OUT INT32 *Z ) { UINT8 AbsX, AbsY; *X = Clamp (*X, -16, 16); *Y = Clamp (*Y, -16, 16); // According to AMI it should not be reported *Z = 0; AbsX = Abs (*X); AbsY = Abs (*Y); if (*X == 0 && *Y == 0) { return; } AmiShimPointerFilterOut (&AbsX, &AbsY, X, Y); *X = AmiShimPointerScale(*X, AbsX); *Y = AmiShimPointerScale(*Y, AbsY); *X *= AIM_SCALE_FACTOR; *Y *= AIM_SCALE_FACTOR; } #else STATIC UINT8 gAbsRange[4] = {80, 64, 48, 1}; STATIC INT32 gMultipliers[4] = {4, 3, 2, 1}; VOID EFIAPI AmiShimPointerBoostValue ( IN OUT INT32 *Value, IN INT32 AbsValue ) { UINTN Index; for (Index = 0; Index < 4; Index++) { if (gAbsRange[Index] > AbsValue) { *Value *= gMultipliers[Index]; return; } } } VOID EFIAPI AmiShimPointerSmooth ( IN OUT INT32 *X, IN OUT INT32 *Y, IN OUT INT32 *Z ) { UINT8 AbsX, AbsY; *X = InternalClamp(*X, -96, 96); *Y = InternalClamp(*Y, -96, 96); *Z = 0; AbsX = Abs (*X); AbsY = Abs (*Y); if (*X == 0 && *Y == 0) { return; } AmiShimPointerFilterOut (&AbsX, &AbsY, X, Y); AmiShimPointerBoostValue(X, AbsX); AmiShimPointerBoostValue(Y, AbsY); } #endif VOID EFIAPI AmiShimPointerPositionHandler ( IN EFI_EVENT Event, IN VOID *Context ) { UINTN Index; AMI_SHIM_POINTER_INSTANCE *Pointer; AMI_POINTER_POSITION_STATE_DATA PositionState; // Do not poll until somebody actually starts using the mouse // Otherwise first move will be quite random if (!mAmiShimPointer.UsageStarted) { return; } // This is important to do quickly and separately, because AMI stores positioning data in INT8. // If we move the mouse quickly it will overflow and return invalid data. for (Index = 0; Index < AIM_MAX_POINTERS; Index++) { Pointer = &mAmiShimPointer.PointerMap[Index]; if (Pointer->DeviceHandle != NULL) { PositionState.Changed = 0; Pointer->EfiPointer->GetPositionState (Pointer->EfiPointer, &PositionState); if (PositionState.Changed == 1) { if (PositionState.Absolute == 0) { DEBUG ((DEBUG_VERBOSE, "Position: %d %d %d %d\n", PositionState.Changed, PositionState.PositionX, PositionState.PositionY, PositionState.PositionZ)); AmiShimPointerSmooth(&PositionState.PositionX, &PositionState.PositionY, &PositionState.PositionZ); if (PositionState.PositionX != 0 || PositionState.PositionY != 0 || PositionState.PositionZ != 0) { Pointer->PositionX += PositionState.PositionX; Pointer->PositionY += PositionState.PositionY; Pointer->PositionZ += PositionState.PositionZ; Pointer->PositionChanged = TRUE; } } else { //FIXME: Add support for devices with absolute positioning } } } } } EFI_STATUS EFIAPI AmiShimPointerUpdateState ( IN AMI_SHIM_POINTER_INSTANCE *Pointer, OUT EFI_SIMPLE_POINTER_STATE *State ) { AMI_POINTER_BUTTON_STATE_DATA ButtonState; if (!mAmiShimPointer.UsageStarted) { mAmiShimPointer.UsageStarted = TRUE; AmiShimPointerPositionHandler(NULL, NULL); } ButtonState.Changed = 0; Pointer->EfiPointer->GetButtonState (Pointer->EfiPointer, &ButtonState); if (ButtonState.Changed == 0 && Pointer->PositionChanged == 0) { return EFI_NOT_READY; } DEBUG ((DEBUG_VERBOSE, "Button: %d %d %d %d, Position: %d %d %d %d\n", ButtonState.Changed, ButtonState.LeftButton, ButtonState.MiddleButton, ButtonState.RightButton, Pointer->PositionChanged, Pointer->PositionX, Pointer->PositionY, Pointer->PositionZ)); if (ButtonState.Changed) { State->LeftButton = ButtonState.LeftButton; State->RightButton = ButtonState.RightButton; } else { State->LeftButton = State->RightButton = FALSE; } if (Pointer->PositionChanged) { State->RelativeMovementX = Pointer->PositionX; State->RelativeMovementY = Pointer->PositionY; State->RelativeMovementZ = Pointer->PositionZ; } else { State->RelativeMovementX = State->RelativeMovementY = State->RelativeMovementZ = 0; } Pointer->PositionChanged = 0; Pointer->PositionX = Pointer->PositionY = Pointer->PositionZ = 0; return EFI_SUCCESS; } EFI_STATUS EFIAPI AmiShimPointerGetState ( IN EFI_SIMPLE_POINTER_PROTOCOL *This, IN OUT EFI_SIMPLE_POINTER_STATE *State ) { EFI_STATUS Status; UINTN Index; AMI_SHIM_POINTER_INSTANCE *Pointer; if (This == NULL || State == NULL) { return EFI_INVALID_PARAMETER; } for (Index = 0, Pointer = mAmiShimPointer.PointerMap; Index < AIM_MAX_POINTERS; Index++, Pointer++) { if (Pointer->SimplePointer == This) { break; } } if (Index != AIM_MAX_POINTERS) { Status = AmiShimPointerUpdateState (Pointer, State); if (!EFI_ERROR (Status)) { if (State->RelativeMovementX != 0 || State->RelativeMovementY != 0 || State->RelativeMovementZ != 0) { DEBUG ((DEBUG_VERBOSE, "Received[%p] %d %d %d <%d, %d>\n", This, State->RelativeMovementX, State->RelativeMovementY, State->RelativeMovementZ, State->LeftButton, State->RightButton)); } else { DEBUG ((DEBUG_VERBOSE, "Received[%p] %d %d %d\n", This, State->RelativeMovementX, State->RelativeMovementY, State->RelativeMovementZ)); } } } else { DEBUG ((DEBUG_VERBOSE, "Received unknown this %p\n", This)); Status = EFI_INVALID_PARAMETER; } return Status; } EFI_STATUS EFIAPI AmiShimPointerTimerSetup ( VOID ) { EFI_STATUS Status; if (mAmiShimPointer.TimersInitialised) { return EFI_ALREADY_STARTED; } Status = gBS->CreateEvent (EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY, AmiShimPointerPositionHandler, NULL, &mAmiShimPointer.PositionEvent); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "AmiShimPointerPositionHandler event creation failed %d\n", Status)); return Status; } Status = gBS->SetTimer (mAmiShimPointer.PositionEvent, TimerPeriodic, AIM_POSITION_POLL_INTERVAL); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "AmiShimPointerPositionHandler timer setting failed %d\n", Status)); gBS->CloseEvent (mAmiShimPointer.PositionEvent); return Status; } mAmiShimPointer.TimersInitialised = TRUE; return EFI_SUCCESS; } EFI_STATUS EFIAPI AmiShimPointerTimerUninstall ( VOID ) { EFI_STATUS Status; if (!mAmiShimPointer.TimersInitialised) { return EFI_SUCCESS; } if (mAmiShimPointer.PositionEvent != NULL) { Status = gBS->SetTimer (mAmiShimPointer.PositionEvent, TimerCancel, 0); if (!EFI_ERROR (Status)) { gBS->CloseEvent (mAmiShimPointer.PositionEvent); mAmiShimPointer.PositionEvent = NULL; } else { DEBUG((DEBUG_INFO, "AmiShimPointerPositionHandler timer unsetting failed %d\n", Status)); } } mAmiShimPointer.TimersInitialised = FALSE; return EFI_SUCCESS; } EFI_STATUS EFIAPI AmiShimPointerInstallOnHandle ( IN EFI_HANDLE DeviceHandle, IN AMI_EFIPOINTER_PROTOCOL *EfiPointer, IN EFI_SIMPLE_POINTER_PROTOCOL *SimplePointer ) { UINTN Index; AMI_SHIM_POINTER_INSTANCE *Pointer; AMI_SHIM_POINTER_INSTANCE *FreePointer; FreePointer = NULL; for (Index = 0; Index < AIM_MAX_POINTERS; Index++) { Pointer = &mAmiShimPointer.PointerMap[Index]; if (Pointer->DeviceHandle == NULL && FreePointer == NULL) { FreePointer = Pointer; } else if (Pointer->DeviceHandle == DeviceHandle) { return EFI_ALREADY_STARTED; } } if (FreePointer == NULL) { return EFI_OUT_OF_RESOURCES; } DEBUG ((DEBUG_INFO, "Installed onto %X\n", DeviceHandle)); FreePointer->DeviceHandle = DeviceHandle; FreePointer->EfiPointer = EfiPointer; FreePointer->SimplePointer = SimplePointer; if (FreePointer->SimplePointer->GetState == AmiShimPointerGetState) { FreePointer->OriginalGetState = NULL; DEBUG ((DEBUG_INFO, "Function is already hooked\n")); } else { FreePointer->OriginalGetState = FreePointer->SimplePointer->GetState; FreePointer->SimplePointer->GetState = AmiShimPointerGetState; } return EFI_SUCCESS; } EFI_STATUS EFIAPI AmiShimPointerInstall ( VOID ) { EFI_STATUS Status; UINTN NoHandles; EFI_HANDLE *Handles; UINTN Index; BOOLEAN Installed; AMI_EFIPOINTER_PROTOCOL *EfiPointer; EFI_SIMPLE_POINTER_PROTOCOL *SimplePointer; Status = gBS->LocateHandleBuffer (ByProtocol, &gAmiEfiPointerProtocolGuid, NULL, &NoHandles, &Handles); if (EFI_ERROR(Status)) { return EFI_NOT_FOUND; } DEBUG ((DEBUG_INFO, "Found %d Handles located by protocol\n", NoHandles)); Installed = FALSE; for (Index = 0; Index < NoHandles; Index++) { Status = gBS->HandleProtocol (Handles[Index], &gAmiEfiPointerProtocolGuid, (VOID **)&EfiPointer); if (EFI_ERROR(Status)) { DEBUG ((DEBUG_INFO, "Handle %d has no AmiEfiPointerl %d\n", Index, Status)); continue; } Status = gBS->HandleProtocol (Handles[Index], &gEfiSimplePointerProtocolGuid, (VOID **)&SimplePointer); if (EFI_ERROR(Status)) { DEBUG ((DEBUG_INFO, "Handle %d has no EfiSimplePointer %d\n", Index, Status)); continue; } Status = AmiShimPointerInstallOnHandle (Handles[Index], EfiPointer, SimplePointer); if (EFI_ERROR(Status)) { DEBUG ((DEBUG_INFO, "Handle %d failed to get installed %d\n", Index, Status)); continue; } Installed = TRUE; } gBS->FreePool (Handles); if (!Installed) { return EFI_NOT_FOUND; } if (!mAmiShimPointer.TimersInitialised) { return AmiShimPointerTimerSetup(); } return EFI_SUCCESS; } EFI_STATUS EFIAPI AmiShimPointerUninstall ( VOID ) { UINTN Index; AMI_SHIM_POINTER_INSTANCE *Pointer; AmiShimPointerTimerUninstall(); for (Index = 0; Index < AIM_MAX_POINTERS; Index++) { Pointer = &mAmiShimPointer.PointerMap[Index]; if (Pointer->DeviceHandle != NULL) { Pointer->SimplePointer->GetState = Pointer->OriginalGetState; gBS->DisconnectController(Pointer->DeviceHandle, NULL, NULL); Pointer->DeviceHandle = NULL; } } return EFI_SUCCESS; } VOID EFIAPI AmiShimPointerArriveHandler ( IN EFI_EVENT Event, IN VOID *Context ) { AmiShimPointerInstall(); } EFI_STATUS AIMInit ( VOID ) { EFI_STATUS Status; VOID *Registration; Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_NOTIFY, AmiShimPointerArriveHandler, NULL, &mAmiShimPointer.ProtocolArriveEvent); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "AmiShimPointerArriveHandler event creation failed %d\n", Status)); return Status; } // EfiSimplePointer gets installed after AMI proprietary protocol Status = gBS->RegisterProtocolNotify (&gEfiSimplePointerProtocolGuid, mAmiShimPointer.ProtocolArriveEvent, &Registration); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "AmiShimProtocolArriveHandler protocol registration failed %d\n", Status)); gBS->CloseEvent (mAmiShimPointer.ProtocolArriveEvent); return Status; } return AmiShimPointerInstall(); } EFI_STATUS AIMExit ( VOID ) { return AmiShimPointerUninstall(); }