diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Usb/UsbMouseAbsolutePointerDxe/UsbMouseAbsolutePointer.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Usb/UsbMouseAbsolutePointerDxe/UsbMouseAbsolutePointer.c | 1018 |
1 files changed, 1018 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Usb/UsbMouseAbsolutePointerDxe/UsbMouseAbsolutePointer.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Usb/UsbMouseAbsolutePointerDxe/UsbMouseAbsolutePointer.c new file mode 100644 index 00000000..a4049a47 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Usb/UsbMouseAbsolutePointerDxe/UsbMouseAbsolutePointer.c @@ -0,0 +1,1018 @@ +/** @file + USB Mouse Driver that manages USB mouse and produces Absolute Pointer Protocol. + +Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "UsbMouseAbsolutePointer.h" + +EFI_DRIVER_BINDING_PROTOCOL gUsbMouseAbsolutePointerDriverBinding = { + USBMouseAbsolutePointerDriverBindingSupported, + USBMouseAbsolutePointerDriverBindingStart, + USBMouseAbsolutePointerDriverBindingStop, + 0x1, + NULL, + NULL +}; + +/** + Entrypoint of USB Mouse Absolute Pointer Driver. + + This function is the entrypoint of USB Mouse 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 +USBMouseAbsolutePointerDriverBindingEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gUsbMouseAbsolutePointerDriverBinding, + ImageHandle, + &gUsbMouseAbsolutePointerComponentName, + &gUsbMouseAbsolutePointerComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} + + +/** + Check whether USB Mouse Absolute Pointer Driver supports this device. + + @param This The 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 +USBMouseAbsolutePointerDriverBindingSupported ( + 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 mouse device that can be managed by this driver. + // + Status = EFI_SUCCESS; + if (!IsUsbMouse (UsbIo)) { + Status = EFI_UNSUPPORTED; + } + + gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + + +/** + Starts the mouse device with this driver. + + This function consumes USB I/O Protocol, initializes USB mouse device, + installs Absolute Pointer Protocol, and submits Asynchronous Interrupt + Transfer to manage the USB mouse device. + + @param This The 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 +USBMouseAbsolutePointerDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_USB_IO_PROTOCOL *UsbIo; + USB_MOUSE_ABSOLUTE_POINTER_DEV *UsbMouseAbsolutePointerDevice; + UINT8 EndpointNumber; + EFI_USB_ENDPOINT_DESCRIPTOR EndpointDescriptor; + UINT8 Index; + UINT8 EndpointAddr; + UINT8 PollingInterval; + UINT8 PacketSize; + BOOLEAN Found; + EFI_TPL OldTpl; + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + // + // Open USB I/O Protocol + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + (VOID **) &UsbIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ErrorExit1; + } + + UsbMouseAbsolutePointerDevice = AllocateZeroPool (sizeof (USB_MOUSE_ABSOLUTE_POINTER_DEV)); + ASSERT (UsbMouseAbsolutePointerDevice != NULL); + + UsbMouseAbsolutePointerDevice->UsbIo = UsbIo; + UsbMouseAbsolutePointerDevice->Signature = USB_MOUSE_ABSOLUTE_POINTER_DEV_SIGNATURE; + + // + // Get the Device Path Protocol on Controller's handle + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &UsbMouseAbsolutePointerDevice->DevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + goto ErrorExit; + } + + // + // Report Status Code here since USB mouse will be detected next. + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + (EFI_PERIPHERAL_MOUSE | EFI_P_PC_PRESENCE_DETECT), + UsbMouseAbsolutePointerDevice->DevicePath + ); + + // + // Get interface & endpoint descriptor + // + UsbIo->UsbGetInterfaceDescriptor ( + UsbIo, + &UsbMouseAbsolutePointerDevice->InterfaceDescriptor + ); + + EndpointNumber = UsbMouseAbsolutePointerDevice->InterfaceDescriptor.NumEndpoints; + + // + // Traverse endpoints to find interrupt endpoint IN + // + Found = FALSE; + for (Index = 0; Index < EndpointNumber; Index++) { + UsbIo->UsbGetEndpointDescriptor ( + UsbIo, + Index, + &EndpointDescriptor + ); + + if (((EndpointDescriptor.Attributes & (BIT0 | BIT1)) == USB_ENDPOINT_INTERRUPT) && + ((EndpointDescriptor.EndpointAddress & USB_ENDPOINT_DIR_IN) != 0)) { + // + // We only care interrupt endpoint here + // + CopyMem (&UsbMouseAbsolutePointerDevice->IntEndpointDescriptor, &EndpointDescriptor, sizeof(EndpointDescriptor)); + Found = TRUE; + break; + } + } + + if (!Found) { + // + // Report Status Code to indicate that there is no USB mouse + // + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + (EFI_PERIPHERAL_MOUSE | EFI_P_EC_NOT_DETECTED) + ); + // + // No interrupt endpoint found, then return unsupported. + // + Status = EFI_UNSUPPORTED; + goto ErrorExit; + } + + // + // Report Status Code here since USB mouse has be detected. + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + (EFI_PERIPHERAL_MOUSE | EFI_P_PC_DETECTED), + UsbMouseAbsolutePointerDevice->DevicePath + ); + + Status = InitializeUsbMouseDevice (UsbMouseAbsolutePointerDevice); + if (EFI_ERROR (Status)) { + // + // Fail to initialize USB mouse device. + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + (EFI_PERIPHERAL_MOUSE | EFI_P_EC_INTERFACE_ERROR), + UsbMouseAbsolutePointerDevice->DevicePath + ); + + goto ErrorExit; + } + + // + // Initialize and install EFI Absolute Pointer Protocol. + // + UsbMouseAbsolutePointerDevice->AbsolutePointerProtocol.GetState = GetMouseAbsolutePointerState; + UsbMouseAbsolutePointerDevice->AbsolutePointerProtocol.Reset = UsbMouseAbsolutePointerReset; + UsbMouseAbsolutePointerDevice->AbsolutePointerProtocol.Mode = &UsbMouseAbsolutePointerDevice->Mode; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + UsbMouseAbsolutePointerWaitForInput, + UsbMouseAbsolutePointerDevice, + &((UsbMouseAbsolutePointerDevice->AbsolutePointerProtocol).WaitForInput) + ); + if (EFI_ERROR (Status)) { + goto ErrorExit; + } + + Status = gBS->InstallProtocolInterface ( + &Controller, + &gEfiAbsolutePointerProtocolGuid, + EFI_NATIVE_INTERFACE, + &UsbMouseAbsolutePointerDevice->AbsolutePointerProtocol + ); + + if (EFI_ERROR (Status)) { + goto ErrorExit; + } + + // + // The next step would be submitting Asynchronous Interrupt Transfer on this mouse device. + // After that we will be able to get key data from it. Thus this is deemed as + // the enable action of the mouse, so report status code accordingly. + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + (EFI_PERIPHERAL_MOUSE | EFI_P_PC_ENABLE), + UsbMouseAbsolutePointerDevice->DevicePath + ); + + // + // Submit Asynchronous Interrupt Transfer to manage this device. + // + EndpointAddr = UsbMouseAbsolutePointerDevice->IntEndpointDescriptor.EndpointAddress; + PollingInterval = UsbMouseAbsolutePointerDevice->IntEndpointDescriptor.Interval; + PacketSize = (UINT8) (UsbMouseAbsolutePointerDevice->IntEndpointDescriptor.MaxPacketSize); + + Status = UsbIo->UsbAsyncInterruptTransfer ( + UsbIo, + EndpointAddr, + TRUE, + PollingInterval, + PacketSize, + OnMouseInterruptComplete, + UsbMouseAbsolutePointerDevice + ); + + if (EFI_ERROR (Status)) { + // + // If submit error, uninstall that interface + // + gBS->UninstallProtocolInterface ( + Controller, + &gEfiAbsolutePointerProtocolGuid, + &UsbMouseAbsolutePointerDevice->AbsolutePointerProtocol + ); + goto ErrorExit; + } + + UsbMouseAbsolutePointerDevice->ControllerNameTable = NULL; + AddUnicodeString2 ( + "eng", + gUsbMouseAbsolutePointerComponentName.SupportedLanguages, + &UsbMouseAbsolutePointerDevice->ControllerNameTable, + L"Generic Usb Mouse Absolute Pointer", + TRUE + ); + AddUnicodeString2 ( + "en", + gUsbMouseAbsolutePointerComponentName2.SupportedLanguages, + &UsbMouseAbsolutePointerDevice->ControllerNameTable, + L"Generic Usb Mouse Absolute Pointer", + FALSE + ); + + gBS->RestoreTPL (OldTpl); + return EFI_SUCCESS; + +// +// Error handler +// +ErrorExit: + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + if (UsbMouseAbsolutePointerDevice != NULL) { + if ((UsbMouseAbsolutePointerDevice->AbsolutePointerProtocol).WaitForInput != NULL) { + gBS->CloseEvent ((UsbMouseAbsolutePointerDevice->AbsolutePointerProtocol).WaitForInput); + } + + FreePool (UsbMouseAbsolutePointerDevice); + UsbMouseAbsolutePointerDevice = NULL; + } + } + +ErrorExit1: + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + Stop the USB mouse device handled by this driver. + + @param This The 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 Absolute Pointer Protocol is not installed on Controller. + @retval Others Fail to uninstall protocols attached on the device. + +**/ +EFI_STATUS +EFIAPI +USBMouseAbsolutePointerDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + USB_MOUSE_ABSOLUTE_POINTER_DEV *UsbMouseAbsolutePointerDevice; + EFI_ABSOLUTE_POINTER_PROTOCOL *AbsolutePointerProtocol; + EFI_USB_IO_PROTOCOL *UsbIo; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiAbsolutePointerProtocolGuid, + (VOID **) &AbsolutePointerProtocol, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + UsbMouseAbsolutePointerDevice = USB_MOUSE_ABSOLUTE_POINTER_DEV_FROM_MOUSE_PROTOCOL (AbsolutePointerProtocol); + + UsbIo = UsbMouseAbsolutePointerDevice->UsbIo; + + // + // The key data input from this device will be disabled. + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + (EFI_PERIPHERAL_MOUSE | EFI_P_PC_DISABLE), + UsbMouseAbsolutePointerDevice->DevicePath + ); + + // + // Delete the Asynchronous Interrupt Transfer from this device + // + UsbIo->UsbAsyncInterruptTransfer ( + UsbIo, + UsbMouseAbsolutePointerDevice->IntEndpointDescriptor.EndpointAddress, + FALSE, + UsbMouseAbsolutePointerDevice->IntEndpointDescriptor.Interval, + 0, + NULL, + NULL + ); + + Status = gBS->UninstallProtocolInterface ( + Controller, + &gEfiAbsolutePointerProtocolGuid, + &UsbMouseAbsolutePointerDevice->AbsolutePointerProtocol + ); + if (EFI_ERROR (Status)) { + return Status; + } + + gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Free all resources. + // + gBS->CloseEvent (UsbMouseAbsolutePointerDevice->AbsolutePointerProtocol.WaitForInput); + + if (UsbMouseAbsolutePointerDevice->DelayedRecoveryEvent != NULL) { + gBS->CloseEvent (UsbMouseAbsolutePointerDevice->DelayedRecoveryEvent); + UsbMouseAbsolutePointerDevice->DelayedRecoveryEvent = NULL; + } + + if (UsbMouseAbsolutePointerDevice->ControllerNameTable != NULL) { + FreeUnicodeStringTable (UsbMouseAbsolutePointerDevice->ControllerNameTable); + } + + FreePool (UsbMouseAbsolutePointerDevice); + + return EFI_SUCCESS; + +} + + +/** + Uses USB I/O to check whether the device is a USB mouse device. + + @param UsbIo Pointer to a USB I/O protocol instance. + + @retval TRUE Device is a USB mouse device. + @retval FALSE Device is a not USB mouse device. + +**/ +BOOLEAN +IsUsbMouse ( + IN EFI_USB_IO_PROTOCOL *UsbIo + ) +{ + EFI_STATUS Status; + EFI_USB_INTERFACE_DESCRIPTOR InterfaceDescriptor; + + // + // Get the default interface descriptor + // + Status = UsbIo->UsbGetInterfaceDescriptor ( + UsbIo, + &InterfaceDescriptor + ); + + if (EFI_ERROR (Status)) { + return FALSE; + } + + if ((InterfaceDescriptor.InterfaceClass == CLASS_HID) && + (InterfaceDescriptor.InterfaceSubClass == SUBCLASS_BOOT) && + (InterfaceDescriptor.InterfaceProtocol == PROTOCOL_MOUSE) + ) { + return TRUE; + } + + return FALSE; +} + + +/** + Initialize the USB mouse device. + + This function retrieves and parses HID report descriptor, and + initializes state of USB_MOUSE_ABSOLUTE_POINTER_DEV. Then it sets indefinite idle + rate for the device. Finally it creates event for delayed recovery, + which deals with device error. + + @param UsbMouseAbsolutePointerDev Device instance to be initialized. + + @retval EFI_SUCCESS USB mouse device successfully initialized. + @retval EFI_UNSUPPORTED HID descriptor type is not report descriptor. + @retval Other USB mouse device was not initialized successfully. + +**/ +EFI_STATUS +InitializeUsbMouseDevice ( + IN USB_MOUSE_ABSOLUTE_POINTER_DEV *UsbMouseAbsolutePointerDev + ) +{ + EFI_USB_IO_PROTOCOL *UsbIo; + UINT8 Protocol; + EFI_STATUS Status; + EFI_USB_HID_DESCRIPTOR *MouseHidDesc; + UINT8 *ReportDesc; + EFI_USB_CONFIG_DESCRIPTOR ConfigDesc; + VOID *Buf; + UINT32 TransferResult; + UINT16 Total; + USB_DESC_HEAD *Head; + BOOLEAN Start; + + UsbIo = UsbMouseAbsolutePointerDev->UsbIo; + + // + // Get the current configuration descriptor. Note that it doesn't include other descriptors. + // + Status = UsbIo->UsbGetConfigDescriptor ( + UsbIo, + &ConfigDesc + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // By issuing Get_Descriptor(Configuration) request with total length, we get the Configuration descriptor, + // all Interface descriptors, all Endpoint descriptors, and the HID descriptor for each interface. + // + Buf = AllocateZeroPool (ConfigDesc.TotalLength); + if (Buf == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = UsbGetDescriptor ( + UsbIo, + (UINT16)((USB_DESC_TYPE_CONFIG << 8) | (ConfigDesc.ConfigurationValue - 1)), + 0, + ConfigDesc.TotalLength, + Buf, + &TransferResult + ); + if (EFI_ERROR (Status)) { + FreePool (Buf); + return Status; + } + + Total = 0; + Start = FALSE; + Head = (USB_DESC_HEAD *)Buf; + MouseHidDesc = NULL; + + // + // Get HID descriptor from the receipt of Get_Descriptor(Configuration) request. + // This algorithm is based on the fact that the HID descriptor shall be interleaved + // between the interface and endpoint descriptors for HID interfaces. + // + while (Total < ConfigDesc.TotalLength) { + if (Head->Type == USB_DESC_TYPE_INTERFACE) { + if ((((USB_INTERFACE_DESCRIPTOR *)Head)->InterfaceNumber == UsbMouseAbsolutePointerDev->InterfaceDescriptor.InterfaceNumber) && + (((USB_INTERFACE_DESCRIPTOR *)Head)->AlternateSetting == UsbMouseAbsolutePointerDev->InterfaceDescriptor.AlternateSetting)) { + Start = TRUE; + } + } + if (Start && (Head->Type == USB_DESC_TYPE_ENDPOINT)) { + break; + } + if (Start && (Head->Type == USB_DESC_TYPE_HID)) { + MouseHidDesc = (EFI_USB_HID_DESCRIPTOR *)Head; + break; + } + Total = Total + (UINT16)Head->Len; + Head = (USB_DESC_HEAD*)((UINT8 *)Buf + Total); + } + + if (MouseHidDesc == NULL) { + FreePool (Buf); + return EFI_UNSUPPORTED; + } + + // + // Get report descriptor + // + if (MouseHidDesc->HidClassDesc[0].DescriptorType != USB_DESC_TYPE_REPORT) { + FreePool (Buf); + return EFI_UNSUPPORTED; + } + + ReportDesc = AllocateZeroPool (MouseHidDesc->HidClassDesc[0].DescriptorLength); + ASSERT (ReportDesc != NULL); + + Status = UsbGetReportDescriptor ( + UsbIo, + UsbMouseAbsolutePointerDev->InterfaceDescriptor.InterfaceNumber, + MouseHidDesc->HidClassDesc[0].DescriptorLength, + ReportDesc + ); + + if (EFI_ERROR (Status)) { + FreePool (Buf); + FreePool (ReportDesc); + return Status; + } + + // + // Parse report descriptor + // + Status = ParseMouseReportDescriptor ( + UsbMouseAbsolutePointerDev, + ReportDesc, + MouseHidDesc->HidClassDesc[0].DescriptorLength + ); + + if (EFI_ERROR (Status)) { + FreePool (Buf); + FreePool (ReportDesc); + return Status; + } + + UsbMouseAbsolutePointerDev->Mode.AbsoluteMaxX = 1024; + UsbMouseAbsolutePointerDev->Mode.AbsoluteMaxY = 1024; + UsbMouseAbsolutePointerDev->Mode.AbsoluteMaxZ = 0; + UsbMouseAbsolutePointerDev->Mode.AbsoluteMinX = 0; + UsbMouseAbsolutePointerDev->Mode.AbsoluteMinY = 0; + UsbMouseAbsolutePointerDev->Mode.AbsoluteMinZ = 0; + UsbMouseAbsolutePointerDev->Mode.Attributes = 0x3; + + // + // Let the cursor's starting position is in the center of the screen. + // + UsbMouseAbsolutePointerDev->State.CurrentX = + DivU64x32 (UsbMouseAbsolutePointerDev->Mode.AbsoluteMaxX + UsbMouseAbsolutePointerDev->Mode.AbsoluteMinX, 2); + UsbMouseAbsolutePointerDev->State.CurrentY = + DivU64x32 (UsbMouseAbsolutePointerDev->Mode.AbsoluteMaxY + UsbMouseAbsolutePointerDev->Mode.AbsoluteMinY, 2); + + // + // Set boot protocol for the USB mouse. + // This driver only supports boot protocol. + // + UsbGetProtocolRequest ( + UsbIo, + UsbMouseAbsolutePointerDev->InterfaceDescriptor.InterfaceNumber, + &Protocol + ); + if (Protocol != BOOT_PROTOCOL) { + Status = UsbSetProtocolRequest ( + UsbIo, + UsbMouseAbsolutePointerDev->InterfaceDescriptor.InterfaceNumber, + BOOT_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + FreePool (Buf); + FreePool (ReportDesc); + return Status; + } + } + + FreePool (Buf); + FreePool (ReportDesc); + + // + // Create event for delayed recovery, which deals with device error. + // + if (UsbMouseAbsolutePointerDev->DelayedRecoveryEvent != NULL) { + gBS->CloseEvent (UsbMouseAbsolutePointerDev->DelayedRecoveryEvent); + UsbMouseAbsolutePointerDev->DelayedRecoveryEvent = 0; + } + + gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + USBMouseRecoveryHandler, + UsbMouseAbsolutePointerDev, + &UsbMouseAbsolutePointerDev->DelayedRecoveryEvent + ); + + return EFI_SUCCESS; +} + + +/** + Handler function for USB mouse's asynchronous interrupt transfer. + + This function is the handler function for USB mouse's asynchronous interrupt transfer + to manage the mouse. It parses data returned from asynchronous interrupt transfer, and + get button and movement state. + + @param Data A pointer to a buffer that is filled with key data which is + retrieved via asynchronous interrupt transfer. + @param DataLength Indicates the size of the data buffer. + @param Context Pointing to USB_KB_DEV instance. + @param Result Indicates the result of the asynchronous interrupt transfer. + + @retval EFI_SUCCESS Asynchronous interrupt transfer is handled successfully. + @retval EFI_DEVICE_ERROR Hardware error occurs. + +**/ +EFI_STATUS +EFIAPI +OnMouseInterruptComplete ( + IN VOID *Data, + IN UINTN DataLength, + IN VOID *Context, + IN UINT32 Result + ) +{ + USB_MOUSE_ABSOLUTE_POINTER_DEV *UsbMouseAbsolutePointerDevice; + EFI_USB_IO_PROTOCOL *UsbIo; + UINT8 EndpointAddr; + UINT32 UsbResult; + + UsbMouseAbsolutePointerDevice = (USB_MOUSE_ABSOLUTE_POINTER_DEV *) Context; + UsbIo = UsbMouseAbsolutePointerDevice->UsbIo; + + if (Result != EFI_USB_NOERROR) { + // + // Some errors happen during the process + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + (EFI_PERIPHERAL_MOUSE | EFI_P_EC_INPUT_ERROR), + UsbMouseAbsolutePointerDevice->DevicePath + ); + + if ((Result & EFI_USB_ERR_STALL) == EFI_USB_ERR_STALL) { + EndpointAddr = UsbMouseAbsolutePointerDevice->IntEndpointDescriptor.EndpointAddress; + + UsbClearEndpointHalt ( + UsbIo, + EndpointAddr, + &UsbResult + ); + } + + // + // Delete & Submit this interrupt again + // Handler of DelayedRecoveryEvent triggered by timer will re-submit the interrupt. + // + UsbIo->UsbAsyncInterruptTransfer ( + UsbIo, + UsbMouseAbsolutePointerDevice->IntEndpointDescriptor.EndpointAddress, + FALSE, + 0, + 0, + NULL, + NULL + ); + // + // EFI_USB_INTERRUPT_DELAY is defined in USB standard for error handling. + // + gBS->SetTimer ( + UsbMouseAbsolutePointerDevice->DelayedRecoveryEvent, + TimerRelative, + EFI_USB_INTERRUPT_DELAY + ); + return EFI_DEVICE_ERROR; + } + + // + // If no error and no data, just return EFI_SUCCESS. + // + if (DataLength == 0 || Data == NULL) { + return EFI_SUCCESS; + } + + // + // Check mouse Data + // USB HID Specification specifies following data format: + // Byte Bits Description + // 0 0 Button 1 + // 1 Button 2 + // 2 Button 3 + // 4 to 7 Device-specific + // 1 0 to 7 X displacement + // 2 0 to 7 Y displacement + // 3 to n 0 to 7 Device specific (optional) + // + if (DataLength < 3) { + return EFI_DEVICE_ERROR; + } + + UsbMouseAbsolutePointerDevice->StateChanged = TRUE; + + UsbMouseAbsolutePointerDevice->State.ActiveButtons = *(UINT8 *) Data & (BIT0 | BIT1 | BIT2); + + UsbMouseAbsolutePointerDevice->State.CurrentX = + MIN ( + MAX ((INT64) UsbMouseAbsolutePointerDevice->State.CurrentX + *((INT8 *) Data + 1), + (INT64) UsbMouseAbsolutePointerDevice->Mode.AbsoluteMinX), + (INT64) UsbMouseAbsolutePointerDevice->Mode.AbsoluteMaxX + ); + UsbMouseAbsolutePointerDevice->State.CurrentY = + MIN ( + MAX ((INT64) UsbMouseAbsolutePointerDevice->State.CurrentY + *((INT8 *) Data + 2), + (INT64) UsbMouseAbsolutePointerDevice->Mode.AbsoluteMinY), + (INT64) UsbMouseAbsolutePointerDevice->Mode.AbsoluteMaxY + ); + if (DataLength > 3) { + UsbMouseAbsolutePointerDevice->State.CurrentZ = + MIN ( + MAX ((INT64) UsbMouseAbsolutePointerDevice->State.CurrentZ + *((INT8 *) Data + 1), + (INT64) UsbMouseAbsolutePointerDevice->Mode.AbsoluteMinZ), + (INT64) UsbMouseAbsolutePointerDevice->Mode.AbsoluteMaxZ + ); + } + + return EFI_SUCCESS; +} + +/** + Retrieves the current state of a pointer device. + + @param This A pointer to the EFI_ABSOLUTE_POINTER_PROTOCOL instance. + @param MouseState A pointer to the state information on the pointer device. + + @retval EFI_SUCCESS The state of the pointer device was returned in State. + @retval EFI_NOT_READY The state of the pointer device has not changed since the last call to + GetState(). + @retval EFI_DEVICE_ERROR A device error occurred while attempting to retrieve the pointer device's + current state. + @retval EFI_INVALID_PARAMETER State is NULL. + +**/ +EFI_STATUS +EFIAPI +GetMouseAbsolutePointerState ( + IN EFI_ABSOLUTE_POINTER_PROTOCOL *This, + OUT EFI_ABSOLUTE_POINTER_STATE *State + ) +{ + USB_MOUSE_ABSOLUTE_POINTER_DEV *MouseAbsolutePointerDev; + + if (State == NULL) { + return EFI_INVALID_PARAMETER; + } + + MouseAbsolutePointerDev = USB_MOUSE_ABSOLUTE_POINTER_DEV_FROM_MOUSE_PROTOCOL (This); + + if (!MouseAbsolutePointerDev->StateChanged) { + return EFI_NOT_READY; + } + + // + // Retrieve mouse state from USB_MOUSE_ABSOLUTE_POINTER_DEV, + // which was filled by OnMouseInterruptComplete() + // + CopyMem ( + State, + &MouseAbsolutePointerDev->State, + sizeof (EFI_ABSOLUTE_POINTER_STATE) + ); + + MouseAbsolutePointerDev->StateChanged = FALSE; + + return EFI_SUCCESS; +} + + +/** + Resets the pointer device hardware. + + @param This A pointer to the EFI_ABSOLUTE_POINTER_PROTOCOL instance. + @param ExtendedVerification Indicates that the driver may perform a more exhaustive + verification operation of the device during reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning correctly and could not be reset. + +**/ +EFI_STATUS +EFIAPI +UsbMouseAbsolutePointerReset ( + IN EFI_ABSOLUTE_POINTER_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + USB_MOUSE_ABSOLUTE_POINTER_DEV *UsbMouseAbsolutePointerDevice; + + UsbMouseAbsolutePointerDevice = USB_MOUSE_ABSOLUTE_POINTER_DEV_FROM_MOUSE_PROTOCOL (This); + + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + (EFI_PERIPHERAL_MOUSE | EFI_P_PC_RESET), + UsbMouseAbsolutePointerDevice->DevicePath + ); + + // + // Clear mouse state. + // + ZeroMem ( + &UsbMouseAbsolutePointerDevice->State, + sizeof (EFI_ABSOLUTE_POINTER_STATE) + ); + + // + // Let the cursor's starting position is in the center of the screen. + // + UsbMouseAbsolutePointerDevice->State.CurrentX = + DivU64x32 (UsbMouseAbsolutePointerDevice->Mode.AbsoluteMaxX + UsbMouseAbsolutePointerDevice->Mode.AbsoluteMinX, 2); + UsbMouseAbsolutePointerDevice->State.CurrentY = + DivU64x32 (UsbMouseAbsolutePointerDevice->Mode.AbsoluteMaxY + UsbMouseAbsolutePointerDevice->Mode.AbsoluteMinY, 2); + + UsbMouseAbsolutePointerDevice->StateChanged = FALSE; + + return EFI_SUCCESS; +} + +/** + Event notification function for EFI_ABSOLUTE_POINTER_PROTOCOL.WaitForInput event. + + @param Event Event to be signaled when there's input from mouse. + @param Context Points to USB_MOUSE_ABSOLUTE_POINTER_DEV instance. + +**/ +VOID +EFIAPI +UsbMouseAbsolutePointerWaitForInput ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + USB_MOUSE_ABSOLUTE_POINTER_DEV *UsbMouseAbsolutePointerDev; + + UsbMouseAbsolutePointerDev = (USB_MOUSE_ABSOLUTE_POINTER_DEV *) Context; + + // + // If there's input from mouse, signal the event. + // + if (UsbMouseAbsolutePointerDev->StateChanged) { + gBS->SignalEvent (Event); + } +} + +/** + Handler for Delayed Recovery event. + + This function is the handler for Delayed Recovery event triggered + by timer. + After a device error occurs, the event would be triggered + with interval of EFI_USB_INTERRUPT_DELAY. EFI_USB_INTERRUPT_DELAY + is defined in USB standard for error handling. + + @param Event The Delayed Recovery event. + @param Context Points to the USB_MOUSE_ABSOLUTE_POINTER_DEV instance. + +**/ +VOID +EFIAPI +USBMouseRecoveryHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + USB_MOUSE_ABSOLUTE_POINTER_DEV *UsbMouseAbsolutePointerDev; + EFI_USB_IO_PROTOCOL *UsbIo; + + UsbMouseAbsolutePointerDev = (USB_MOUSE_ABSOLUTE_POINTER_DEV *) Context; + + UsbIo = UsbMouseAbsolutePointerDev->UsbIo; + + // + // Re-submit Asynchronous Interrupt Transfer for recovery. + // + UsbIo->UsbAsyncInterruptTransfer ( + UsbIo, + UsbMouseAbsolutePointerDev->IntEndpointDescriptor.EndpointAddress, + TRUE, + UsbMouseAbsolutePointerDev->IntEndpointDescriptor.Interval, + UsbMouseAbsolutePointerDev->IntEndpointDescriptor.MaxPacketSize, + OnMouseInterruptComplete, + UsbMouseAbsolutePointerDev + ); +} |