alder_lake_bios/Insyde/InsydeModulePkg/Bus/Usb/UsbMouseDxe/UsbMouse.c

2011 lines
56 KiB
C

/** @file
;******************************************************************************
;* Copyright (c) 2013 - 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 "UsbMouse.h"
#include <PostCode.h>
//
// Prototypes
// Driver model protocol interface
//
EFI_STATUS
EFIAPI
UsbMouseDriverBindingEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
);
EFI_STATUS
EFIAPI
UsbMouseDriverBindingSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
);
EFI_STATUS
EFIAPI
UsbMouseDriverBindingStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
);
EFI_STATUS
EFIAPI
UsbMouseDriverBindingStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer
);
EFI_STATUS
EFIAPI
UsbMouseUsbBindingSupported (
IN EFI_USB_IO_PROTOCOL *UsbIo,
IN EFI_USB_CORE_PROTOCOL *UsbCore,
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
);
EFI_STATUS
EFIAPI
UsbMouseUsbBindingStart (
IN EFI_USB_IO_PROTOCOL *UsbIo,
IN EFI_USB_CORE_PROTOCOL *UsbCore
);
EFI_STATUS
EFIAPI
UsbMouseUsbBindingStop (
IN EFI_USB_IO_PROTOCOL *UsbIo,
IN VOID *Handle
);
EFI_DRIVER_BINDING_PROTOCOL gUsbMouseDriverBinding = {
UsbMouseDriverBindingSupported,
UsbMouseDriverBindingStart,
UsbMouseDriverBindingStop,
0x10,
NULL,
NULL
};
//
// helper functions
//
STATIC
BOOLEAN
IsUsbMouse (
IN EFI_USB_IO_PROTOCOL *UsbIo
);
STATIC
EFI_STATUS
InitializeUsbMouseDevice (
IN USB_MOUSE_DEV *UsbMouseDevice
);
STATIC
VOID
EFIAPI
UsbMouseWaitForInput (
IN EFI_EVENT Event,
IN VOID *Context
);
//
// Mouse interrupt handler
//
STATIC
EFI_STATUS
EFIAPI
OnMouseInterruptComplete (
IN VOID *Data,
IN UINTN DataLength,
IN VOID *Context,
IN UINT32 Result
);
//
// Mouse Protocol
//
STATIC
EFI_STATUS
EFIAPI
GetMouseState (
IN EFI_SIMPLE_POINTER_PROTOCOL *This,
OUT EFI_SIMPLE_POINTER_STATE *MouseState
);
STATIC
EFI_STATUS
EFIAPI
UsbMouseReset (
IN EFI_SIMPLE_POINTER_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
);
typedef struct {
UINT16 IdVendor;
UINT16 IdProduct;
} MOUSE_ID_TABLE;
STATIC MOUSE_ID_TABLE IncompatibleMouse[] = {
{ 0x1BCF, 0x0007 }, // KTMS258U
{ 0x04ca, 0x006d }, // Lite-on wireless mouse
{ 0x04F2, 0x1558 }, // Chicony touch pad with keyboard
{ 0x1532, 0x0029 }, // Razer DeathAdder mouse
{ 0x1D57, 0xAD03 }, // SI8028 mouse
{ 0x0e8f, 0x0020 }, // Pansignal Technology PS2 to USB cable
{ 0x04d9, 0xf105 }, // Thunderobot M303 USB mouse
{ 0x24AE, 0x1813 }, // Rapoo 8000M Multi-mode Wireless Mouse
{ 0x0000, 0x0000 } // End of list
};
EFI_USB_CORE_PROTOCOL *mUsbCore;
BOOLEAN mInSmram;
/**
Retrieves the USB Device Descriptor.
@param This Indicates the calling context.
@param DeviceDescriptor A pointer to the caller allocated USB Device
Descriptor.
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER
@retval EFI_NOT_FOUND
**/
STATIC
EFI_STATUS
EFIAPI
UsbGetDeviceDescriptor (
IN EFI_USB_IO_PROTOCOL *This,
OUT EFI_USB_DEVICE_DESCRIPTOR *DeviceDescriptor
)
{
EFI_USB_IO_GET_DEVICE_DESCRIPTOR UsbGetDeviceDescriptorFunc = This->UsbGetDeviceDescriptor;
if (mInSmram) {
mUsbCore->AddressConvert (
SMM_ADDRESS,
(VOID*)(UINTN)UsbGetDeviceDescriptorFunc,
(VOID**)&UsbGetDeviceDescriptorFunc
);
}
return UsbGetDeviceDescriptorFunc (
This,
DeviceDescriptor
);
}
/**
Retrieves the interface Descriptor for that controller.
@param This Indicates the calling context.
@param InterfaceDescriptor A pointer to the caller allocated USB interface
Descriptor.
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER
@retval EFI_NOT_FOUND
**/
STATIC
EFI_STATUS
EFIAPI
UsbGetInterfaceDescriptor (
IN EFI_USB_IO_PROTOCOL *This,
OUT EFI_USB_INTERFACE_DESCRIPTOR *InterfaceDescriptor
)
{
EFI_USB_IO_GET_INTERFACE_DESCRIPTOR UsbGetInterfaceDescriptorFunc = This->UsbGetInterfaceDescriptor;
if (mInSmram) {
mUsbCore->AddressConvert (
SMM_ADDRESS,
(VOID*)(UINTN)UsbGetInterfaceDescriptorFunc,
(VOID**)&UsbGetInterfaceDescriptorFunc
);
}
return UsbGetInterfaceDescriptorFunc (
This,
InterfaceDescriptor
);
}
/**
Retrieves the endpoint Descriptor for a given endpoint.
@param This Indicates the calling context.
@param EndpointIndex Indicates which endpoint descriptor to retrieve.
The valid range is 0..15.
@param EndpointDescriptor A pointer to the caller allocated USB Endpoint
Descriptor of a USB controller.
@retval EFI_SUCCESS The endpoint descriptor was retrieved successfully.
@retval EFI_INVALID_PARAMETER EndpointIndex is not valid.
EndpointDescriptor is NULL.
@retval EFI_NOT_FOUND The endpoint descriptor cannot be found.
The device may not be correctly configured.
**/
STATIC
EFI_STATUS
EFIAPI
UsbGetEndpointDescriptor (
IN EFI_USB_IO_PROTOCOL *This,
IN UINT8 EndpointIndex,
OUT EFI_USB_ENDPOINT_DESCRIPTOR *EndpointDescriptor
)
{
EFI_USB_IO_GET_ENDPOINT_DESCRIPTOR UsbGetEndpointDescriptorFunc = This->UsbGetEndpointDescriptor;
if (mInSmram) {
mUsbCore->AddressConvert (
SMM_ADDRESS,
(VOID*)(UINTN)UsbGetEndpointDescriptorFunc,
(VOID**)&UsbGetEndpointDescriptorFunc
);
}
return UsbGetEndpointDescriptorFunc (
This,
EndpointIndex,
EndpointDescriptor
);
}
/*++
Usb Async Interrput Transfer
@param This Indicates calling context.
@param DeviceEndpoint The destination USB device endpoint to which the
device request is being sent.
@param IsNewTransfer If TRUE, a new transfer will be submitted to USB
controller. If FALSE, the interrupt transfer is
deleted from the device's interrupt transfer queue.
@param PollingInterval Indicates the periodic rate, in milliseconds, that
the transfer is to be executed.
@param DataLength Specifies the length, in bytes, of the data to be
received from the USB device.
@param InterruptCallBack The Callback function. This function is called if
the asynchronous interrupt transfer is completed.
@param Context Passed to InterruptCallback
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER
@retval EFI_OUT_OF_RESOURCES
--*/
STATIC
EFI_STATUS
EFIAPI
UsbAsyncInterruptTransfer (
IN EFI_USB_IO_PROTOCOL *This,
IN UINT8 DeviceEndpoint,
IN BOOLEAN IsNewTransfer,
IN UINTN PollingInterval, OPTIONAL
IN UINTN DataLength, OPTIONAL
IN EFI_ASYNC_USB_TRANSFER_CALLBACK InterruptCallBack, OPTIONAL
IN VOID *Context OPTIONAL
)
{
EFI_USB_IO_ASYNC_INTERRUPT_TRANSFER UsbAsyncInterruptTransferFunc = This->UsbAsyncInterruptTransfer;
if (mInSmram) {
mUsbCore->AddressConvert (
SMM_ADDRESS,
(VOID*)(UINTN)UsbAsyncInterruptTransferFunc,
(VOID**)&UsbAsyncInterruptTransferFunc
);
}
return UsbAsyncInterruptTransferFunc (
This,
DeviceEndpoint,
IsNewTransfer,
PollingInterval,
DataLength,
InterruptCallBack,
Context
);
}
/**
Entry point for EFI drivers.
@param ImageHandle EFI_HANDLE
@param SystemTable EFI_SYSTEM_TABLE
@retval EFI_SUCCESS
@retval others
**/
EFI_STATUS
EFIAPI
UsbMouseDriverBindingEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// Locate UsbCore protocol
//
Status = gBS->LocateProtocol (
&gEfiUsbCoreProtocolGuid,
NULL,
(VOID **)&mUsbCore
);
if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}
//
// Register module in DXE/SMM instance
//
mUsbCore->ModuleRegistration (ImageHandle);
//
// Check the phase of instance
//
mUsbCore->IsInSmm (&mInSmram);
if (!mInSmram) {
//
// Now in boot service, install protocols
//
Status = EfiLibInstallDriverBindingComponentName2 (
ImageHandle,
SystemTable,
&gUsbMouseDriverBinding,
ImageHandle,
&gUsbMouseComponentName,
&gUsbMouseComponentName2
);
if (EFI_ERROR (Status)) {
return Status;
}
} else {
//
// Setup UsbCore for use by SMM instance
//
mUsbCore->AddressConvert (
SMM_ADDRESS,
(VOID*)(UINTN)mUsbCore,
(VOID**)&mUsbCore
);
//
// Register UsbBinding for legacy hot plug mechanism
//
Status = mUsbCore->RegisterUsbBindingProtocol(
UsbMouseUsbBindingSupported,
UsbMouseUsbBindingStart,
UsbMouseUsbBindingStop
);
}
return Status;
}
/**
Test to see if this driver supports ControllerHandle. Any ControllerHandle
that has UsbHcProtocol installed will be supported.
@param This Protocol instance pointer.
@param Controller Handle of device to test
@param RemainingDevicePath Not used
@retval EFI_SUCCESS This driver supports this device.
@retval EFI_UNSUPPORTED This driver does not support this device.
**/
EFI_STATUS
EFIAPI
UsbMouseDriverBindingSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS OpenStatus;
EFI_USB_IO_PROTOCOL *UsbIo;
EFI_STATUS Status;
EFI_DEV_PATH_PTR Node;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
//
// Check Device Path
//
if (RemainingDevicePath != NULL) {
Node.DevPath = RemainingDevicePath;
if (Node.DevPath->Type != MESSAGING_DEVICE_PATH ||
Node.DevPath->SubType != MSG_USB_CLASS_DP ||
DevicePathNodeLength(Node.DevPath) != sizeof(USB_CLASS_DEVICE_PATH) ||
Node.UsbClass->DeviceClass != CLASS_HID) {
return EFI_UNSUPPORTED;
}
}
OpenStatus = gBS->OpenProtocol (
Controller,
&gEfiUsbIoProtocolGuid,
(VOID **)&UsbIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (OpenStatus) && (OpenStatus != EFI_ALREADY_STARTED)) {
return EFI_UNSUPPORTED;
}
if (OpenStatus == EFI_ALREADY_STARTED) {
return EFI_ALREADY_STARTED;
}
//
// Get the device path for CheckIgnoredDevice
//
Status = gBS->HandleProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
(VOID **)&DevicePath
);
if (EFI_ERROR (Status)) {
goto ErrorExit;
}
#if (KEEP_USBIO_FOR_IGNORED_DEVICE == 1)
//
// Filter out the USB devices which in the UsbIgnoreDevice list
//
Status = mUsbCore->CheckIgnoredDevice(DevicePath, UsbIo);
if (EFI_ERROR (Status)) {
goto ErrorExit;
}
#endif
//
// Use the USB I/O protocol interface to see the Controller is
// the Mouse controller that can be managed by this driver.
//
Status = EFI_SUCCESS;
if (!IsUsbMouse (UsbIo)) {
Status = EFI_UNSUPPORTED;
}
ErrorExit:
gBS->CloseProtocol (
Controller,
&gEfiUsbIoProtocolGuid,
This->DriverBindingHandle,
Controller
);
return Status;
}
/**
Starting the Usb Bus Driver
@param This Protocol instance pointer.
@param Controller Handle of device to test
@param RemainingDevicePath Not used
@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 Thios driver has been started
**/
EFI_STATUS
EFIAPI
UsbMouseDriverBindingStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
EFI_USB_IO_PROTOCOL *UsbIo;
EFI_USB_ENDPOINT_DESCRIPTOR EndpointDesc;
USB_MOUSE_DEV *UsbMouseDevice;
UINT8 EndpointNumber;
UINT8 Index;
UINT8 EndpointAddr;
UINT8 PollingInterval;
UINT8 PacketSize;
BOOLEAN EndpointFound;
USB_DEVICE UsbDevice;
POST_CODE (BDS_CONNECT_USB_DEVICE);
UsbMouseDevice = NULL;
Status = EFI_SUCCESS;
Status = gBS->OpenProtocol (
Controller,
&gEfiUsbIoProtocolGuid,
(VOID **)&UsbIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
goto ErrorExit;
}
Status = mUsbCore->AllocateBuffer (
sizeof (USB_MOUSE_DEV),
ALIGNMENT_32,
(VOID **)&UsbMouseDevice
);
if (EFI_ERROR(Status)) {
Status = EFI_OUT_OF_RESOURCES;
goto ErrorExit;
}
UsbMouseDevice->UsbIo = UsbIo;
UsbMouseDevice->Signature = USB_MOUSE_DEV_SIGNATURE;
//
// Get the Device Path Protocol on Controller's handle
//
Status = gBS->OpenProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
(VOID **) &UsbMouseDevice->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.
//
MouseReportStatusCode (
UsbMouseDevice,
EFI_PROGRESS_CODE,
(EFI_PERIPHERAL_MOUSE | EFI_P_PC_PRESENCE_DETECT)
);
//
// Get interface & endpoint descriptor
//
UsbGetInterfaceDescriptor (
UsbIo,
&UsbMouseDevice->InterfaceDescriptor
);
EndpointNumber = UsbMouseDevice->InterfaceDescriptor.NumEndpoints;
for (Index = 0, EndpointFound = FALSE; Index < EndpointNumber; Index++) {
UsbGetEndpointDescriptor (
UsbIo,
Index,
&EndpointDesc
);
if ((EndpointDesc.Attributes & 0x03) == 0x03 && (EndpointDesc.EndpointAddress & 0x80)) {
//
// We only care interrupt-in endpoint here
//
UsbMouseDevice->IntEndpointDescriptor = EndpointDesc;
EndpointFound = TRUE;
break;
}
}
if (!EndpointFound) {
//
// Report Status Code to indicate that there is no USB mouse
//
MouseReportStatusCode (
UsbMouseDevice,
EFI_ERROR_CODE | EFI_ERROR_MINOR,
(EFI_PERIPHERAL_MOUSE | EFI_P_EC_NOT_DETECTED)
);
//
// No interrupt endpoint, then error
//
Status = EFI_UNSUPPORTED;
goto ErrorExit;
}
//
// Report Status Code here since USB mouse has be detected.
//
MouseReportStatusCode (
UsbMouseDevice,
EFI_PROGRESS_CODE,
(EFI_PERIPHERAL_MOUSE | EFI_P_PC_DETECTED)
);
Status = InitializeUsbMouseDevice (UsbMouseDevice);
if (EFI_ERROR (Status)) {
MouseReportStatusCode (
UsbMouseDevice,
EFI_ERROR_CODE | EFI_ERROR_MINOR,
(EFI_PERIPHERAL_MOUSE | EFI_P_EC_INTERFACE_ERROR)
);
goto ErrorExit;
}
UsbMouseDevice->SimplePointerProtocol.GetState = GetMouseState;
UsbMouseDevice->SimplePointerProtocol.Reset = UsbMouseReset;
UsbMouseDevice->SimplePointerProtocol.Mode = &UsbMouseDevice->Mode;
Status = gBS->CreateEvent (
EVT_NOTIFY_WAIT,
TPL_NOTIFY,
UsbMouseWaitForInput,
UsbMouseDevice,
&((UsbMouseDevice->SimplePointerProtocol).WaitForInput)
);
if (EFI_ERROR (Status)) {
goto ErrorExit;
}
Status = gBS->InstallProtocolInterface (
&Controller,
&gEfiSimplePointerProtocolGuid,
EFI_NATIVE_INTERFACE,
&UsbMouseDevice->SimplePointerProtocol
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto ErrorExit;
}
//
// After Enabling Async Interrupt Transfer on this mouse Device
// we will be able to get key data from it. Thus this is deemed as
// the enable action of the mouse
//
MouseReportStatusCode (
UsbMouseDevice,
EFI_PROGRESS_CODE,
(EFI_PERIPHERAL_MOUSE | EFI_P_PC_ENABLE)
);
//
// submit async interrupt transfer
//
EndpointAddr = UsbMouseDevice->IntEndpointDescriptor.EndpointAddress;
PollingInterval = UsbMouseDevice->IntEndpointDescriptor.Interval;
PacketSize = (UINT8) (UsbMouseDevice->IntEndpointDescriptor.MaxPacketSize);
//
// Remove previous instance in case it has been installed by legacy binding process
//
UsbAsyncInterruptTransfer (
UsbIo,
EndpointAddr,
FALSE,
PollingInterval,
0,
NULL,
NULL
);
Status = UsbAsyncInterruptTransfer (
UsbIo,
EndpointAddr,
TRUE,
PollingInterval,
PacketSize,
OnMouseInterruptComplete,
UsbMouseDevice
);
if (!EFI_ERROR (Status)) {
UsbMouseDevice->ControllerNameTable = NULL;
AddUnicodeString2 (
LANGUAGE_CODE_ENGLISH_ISO639,
gUsbMouseComponentName.SupportedLanguages,
&UsbMouseDevice->ControllerNameTable,
CONTROLLER_DRIVER_NAME,
TRUE
);
AddUnicodeString2 (
LANGUAGE_CODE_ENGLISH_RFC4646,
gUsbMouseComponentName2.SupportedLanguages,
&UsbMouseDevice->ControllerNameTable,
CONTROLLER_DRIVER_NAME,
FALSE
);
//
// Remove UsbIoDevice in case it has been installed by legacy binding process
//
mUsbCore->RemoveUsbDevice (
UsbIo
);
//
// Insert UsbIoDevice into UsbCore for UsbLegacy
//
UsbDevice.UsbHID.Type = USB_CORE_USB_HID;
UsbDevice.UsbHID.UsbIo = UsbIo;
UsbDevice.UsbHID.Instance = UsbMouseDevice;
UsbDevice.UsbHID.SyncLED = NULL;
mUsbCore->InsertUsbDevice (
&UsbDevice
);
//
// Setup Smm address convert table for Smm security policy
//
mUsbCore->InsertAddressConvertTable (
ACT_INSTANCE_BODY,
UsbMouseDevice,
sizeof (USB_MOUSE_DEV)
);
mUsbCore->InsertAddressConvertTable (
ACT_INSTANCE_POINTER,
&UsbMouseDevice->UsbIo,
1
);
mUsbCore->InsertAddressConvertTable (
ACT_INSTANCE_POINTER,
&UsbMouseDevice->RecoveryPollingHandle,
1
);
return EFI_SUCCESS;
}
//
// If submit error, uninstall that interface
//
Status = EFI_DEVICE_ERROR;
gBS->UninstallProtocolInterface (
Controller,
&gEfiSimplePointerProtocolGuid,
&UsbMouseDevice->SimplePointerProtocol
);
ErrorExit:
if (EFI_ERROR (Status)) {
gBS->CloseProtocol (
Controller,
&gEfiUsbIoProtocolGuid,
This->DriverBindingHandle,
Controller
);
if (UsbMouseDevice != NULL) {
if ((UsbMouseDevice->SimplePointerProtocol).WaitForInput != NULL) {
gBS->CloseEvent ((UsbMouseDevice->SimplePointerProtocol).WaitForInput);
}
mUsbCore->FreeBuffer (
sizeof (USB_MOUSE_DEV),
UsbMouseDevice
);
}
}
return Status;
}
/**
Stop this driver on ControllerHandle. Support stoping any child handles
created by this driver.
@param This Protocol instance pointer.
@param Controller Handle of device to stop driver on
@param NumberOfChildren Number of Children in the ChildHandleBuffer
@param ChildHandleBuffer List of handles for the children we need to stop.
@retval EFI_SUCCESS
@retval EFI_DEVICE_ERROR
@retval others
**/
EFI_STATUS
EFIAPI
UsbMouseDriverBindingStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer
)
{
EFI_STATUS Status;
USB_MOUSE_DEV *UsbMouseDevice;
EFI_SIMPLE_POINTER_PROTOCOL *SimplePointerProtocol;
EFI_USB_IO_PROTOCOL *UsbIo;
//
// Get our context back.
//
Status = gBS->OpenProtocol (
Controller,
&gEfiSimplePointerProtocolGuid,
(VOID **)&SimplePointerProtocol,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}
UsbMouseDevice = USB_MOUSE_DEV_FROM_MOUSE_PROTOCOL (SimplePointerProtocol);
UsbIo = UsbMouseDevice->UsbIo;
//
// Set the UsbIo to NULL to preventing async interrupt transfer restarted
// by recovery handler after deleting the async interrupt transfer
//
UsbMouseDevice->UsbIo = NULL;
gBS->CloseProtocol (
Controller,
&gEfiSimplePointerProtocolGuid,
This->DriverBindingHandle,
Controller
);
//
// Remove the recovery handler before deleting the async interrupt transfer to preventing
// async interrupt transfer restarted by recovery handler
//
mUsbCore->RemovePeriodicTimer(UsbMouseDevice->RecoveryPollingHandle);
//
// Uninstall the Asyn Interrupt Transfer from this device
// will disable the mouse data input from this device
//
MouseReportStatusCode (
UsbMouseDevice,
EFI_PROGRESS_CODE,
(EFI_PERIPHERAL_MOUSE | EFI_P_PC_DISABLE)
);
//
// Delete Mouse Async Interrupt Transfer
//
UsbAsyncInterruptTransfer (
UsbIo,
UsbMouseDevice->IntEndpointDescriptor.EndpointAddress,
FALSE,
UsbMouseDevice->IntEndpointDescriptor.Interval,
0,
NULL,
NULL
);
gBS->CloseEvent (UsbMouseDevice->SimplePointerProtocol.WaitForInput);
Status = gBS->UninstallProtocolInterface (
Controller,
&gEfiSimplePointerProtocolGuid,
&UsbMouseDevice->SimplePointerProtocol
);
if (EFI_ERROR (Status)) {
return Status;
}
gBS->CloseProtocol (
Controller,
&gEfiUsbIoProtocolGuid,
This->DriverBindingHandle,
Controller
);
if (UsbMouseDevice->ControllerNameTable) {
FreeUnicodeStringTable (UsbMouseDevice->ControllerNameTable);
}
mUsbCore->FreeBuffer (
sizeof (USB_MOUSE_DEV),
UsbMouseDevice
);
//
// Remove UsbIoDevice from UsbCore for UsbLegacy
//
mUsbCore->RemoveUsbDevice (
UsbIo
);
//
// Remove Smm address convert table
//
mUsbCore->RemoveAddressConvertTable (
ACT_INSTANCE_BODY,
UsbMouseDevice
);
mUsbCore->RemoveAddressConvertTable (
ACT_INSTANCE_POINTER,
&UsbMouseDevice->UsbIo
);
mUsbCore->RemoveAddressConvertTable (
ACT_INSTANCE_POINTER,
&UsbMouseDevice->RecoveryPollingHandle
);
return EFI_SUCCESS;
}
/**
Supported for UsbIo(called from UsbCore).
@param UsbIo UsbIo
@retval EFI_UNSUPPORTED
@retval EFI_SUCCESS
**/
EFI_STATUS
EFIAPI
UsbMouseUsbBindingSupported (
IN EFI_USB_IO_PROTOCOL *UsbIo,
IN EFI_USB_CORE_PROTOCOL *UsbCore,
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
EFI_STATUS Status;
#if (KEEP_USBIO_FOR_IGNORED_DEVICE == 1)
//
// Filter out the USB devices which in the UsbIgnoreDevice list
//
Status = mUsbCore->CheckIgnoredDevice(DevicePath, UsbIo);
if (EFI_ERROR (Status)) {
return Status;
}
#endif
Status = EFI_UNSUPPORTED;
if (IsUsbMouse (UsbIo)) {
Status = EFI_SUCCESS;
}
return Status;
}
/**
Supported for UsbIo(called from UsbCore).
@param UsbIo UsbIo
@param UsbCore UsbCore
@param Handle The instance of UsbKeyboard for output
@retval EFI_SUCCESS
**/
EFI_STATUS
EFIAPI
UsbMouseUsbBindingStart (
IN EFI_USB_IO_PROTOCOL *UsbIo,
IN EFI_USB_CORE_PROTOCOL *UsbCore
)
{
EFI_STATUS Status;
EFI_USB_ENDPOINT_DESCRIPTOR EndpointDesc;
USB_MOUSE_DEV *UsbMouseDevice;
UINT8 EndpointNumber;
UINT8 Index;
UINT8 EndpointAddr;
UINT8 PollingInterval;
UINT8 PacketSize;
BOOLEAN EndpointFound;
USB_DEVICE UsbDevice;
POST_CODE (BDS_CONNECT_USB_DEVICE);
UsbMouseDevice = NULL;
Status = mUsbCore->AllocateBuffer (
sizeof (USB_MOUSE_DEV),
ALIGNMENT_32,
(VOID **)&UsbMouseDevice
);
if (EFI_ERROR(Status)) {
Status = EFI_OUT_OF_RESOURCES;
goto ErrorExit;
}
UsbMouseDevice->UsbIo = UsbIo;
UsbMouseDevice->Signature = USB_MOUSE_DEV_SIGNATURE;
//
// Get interface & endpoint descriptor
//
UsbGetInterfaceDescriptor (
UsbIo,
&UsbMouseDevice->InterfaceDescriptor
);
EndpointNumber = UsbMouseDevice->InterfaceDescriptor.NumEndpoints;
for (Index = 0, EndpointFound = FALSE; Index < EndpointNumber; Index++) {
UsbGetEndpointDescriptor (
UsbIo,
Index,
&EndpointDesc
);
if ((EndpointDesc.Attributes & 0x03) == 0x03 && (EndpointDesc.EndpointAddress & 0x80)) {
//
// We only care interrupt-in endpoint here
//
UsbMouseDevice->IntEndpointDescriptor = EndpointDesc;
EndpointFound = TRUE;
break;
}
}
if (!EndpointFound) {
//
// No interrupt endpoint, then error
//
Status = EFI_UNSUPPORTED;
goto ErrorExit;
}
Status = InitializeUsbMouseDevice (UsbMouseDevice);
if (EFI_ERROR (Status)) {
goto ErrorExit;
}
//
// submit async interrupt transfer
//
EndpointAddr = UsbMouseDevice->IntEndpointDescriptor.EndpointAddress;
PollingInterval = UsbMouseDevice->IntEndpointDescriptor.Interval;
PacketSize = (UINT8) (UsbMouseDevice->IntEndpointDescriptor.MaxPacketSize);
Status = UsbAsyncInterruptTransfer (
UsbIo,
EndpointAddr,
TRUE,
PollingInterval,
PacketSize,
OnMouseInterruptComplete,
UsbMouseDevice
);
if (EFI_ERROR (Status)) {
goto ErrorExit;
}
//
// Insert UsbIoDevice into UsbCore for UsbLegacy
//
UsbDevice.UsbHID.Type = USB_CORE_USB_HID;
UsbDevice.UsbHID.UsbIo = UsbIo;
UsbDevice.UsbHID.Instance = UsbMouseDevice;
UsbDevice.UsbHID.SyncLED = NULL;
mUsbCore->InsertUsbDevice (
&UsbDevice
);
return EFI_SUCCESS;
ErrorExit:
if (UsbMouseDevice != NULL) {
mUsbCore->FreeBuffer (
sizeof (USB_MOUSE_DEV),
UsbMouseDevice
);
}
return Status;
}
/**
Supported for UsbIo(called from UsbCore).
@param Handle The instance of UsbKeyboard allocated by UsbIoBindingStart
@retval EFI_SUCCESS
**/
EFI_STATUS
EFIAPI
UsbMouseUsbBindingStop (
IN EFI_USB_IO_PROTOCOL *UsbIo,
IN VOID *Handle
)
{
USB_MOUSE_DEV *UsbMouseDevice;
UINTN Mode;
UsbMouseDevice = (USB_MOUSE_DEV*)Handle;
if (UsbIo != UsbMouseDevice->UsbIo) {
//
// Due to UsbCore may pass wrong Handle for us
//
return EFI_NOT_FOUND;
}
//
// Remove the recovery handler before deleting the async interrupt transfer to preventing
// async interrupt transfer restarted by recovery handler
//
mUsbCore->RemovePeriodicTimer(UsbMouseDevice->RecoveryPollingHandle);
//
// Delete Mouse Async Interrupt Transfer
//
UsbAsyncInterruptTransfer (
UsbIo,
UsbMouseDevice->IntEndpointDescriptor.EndpointAddress,
FALSE,
UsbMouseDevice->IntEndpointDescriptor.Interval,
0,
NULL,
NULL
);
//
// Only destroy the instance when runtime mode because
// the instance will be referenced by subsequently
// binding stop function if system under POST time
//
mUsbCore->GetMode(&Mode);
if (Mode == USB_CORE_RUNTIME_MODE) {
mUsbCore->FreeBuffer (
sizeof (USB_MOUSE_DEV),
UsbMouseDevice
);
}
//
// Remove UsbIoDevice from UsbCore for UsbLegacy
//
mUsbCore->RemoveUsbDevice (
UsbIo
);
return EFI_SUCCESS;
}
/**
Tell if a Usb Controller is a mouse
@param UsbIo UsbIo protocol pointer.
@param UsbCore UsbCore protocol pointer
@retval TRUE It is a mouse
@retval FALSE It is not a mouse
**/
BOOLEAN
IsUsbMouse (
IN EFI_USB_IO_PROTOCOL *UsbIo
)
{
EFI_STATUS Status;
EFI_USB_INTERFACE_DESCRIPTOR InterfaceDescriptor;
EFI_USB_HID_DESCRIPTOR MouseHidDesc;
UINT8 *ReportDesc;
UINT16 DescriptorLength;
BOOLEAN MouseDeviceFound;
REPORT_FIELD_INFO ReportFieldInfo;
UINTN Index;
//
// Get the Default interface descriptor, now we only
// suppose it is interface 1
//
Status = UsbGetInterfaceDescriptor (
UsbIo,
&InterfaceDescriptor
);
if (EFI_ERROR (Status)) {
return FALSE;
}
if (InterfaceDescriptor.InterfaceClass == CLASS_HID) {
if (InterfaceDescriptor.InterfaceSubClass == SUBCLASS_BOOT &&
InterfaceDescriptor.InterfaceProtocol == PROTOCOL_MOUSE) {
return TRUE;
}
if (!((InterfaceDescriptor.InterfaceProtocol == PROTOCOL_MOUSE) ||
(InterfaceDescriptor.InterfaceProtocol == PROTOCOL_NONE && InterfaceDescriptor.InterfaceSubClass == SUBCLASS_NONE))) {
return FALSE;
}
Status = mUsbCore->UsbGetHidDescriptor (
UsbIo,
InterfaceDescriptor.InterfaceNumber,
&MouseHidDesc
);
if (EFI_ERROR (Status) || (InterfaceDescriptor.InterfaceProtocol == PROTOCOL_NONE && MouseHidDesc.HidClassDesc[0].DescriptorType != 0x22)) {
return FALSE;
}
DescriptorLength = MouseHidDesc.HidClassDesc[0].DescriptorLength;
Status = mUsbCore->AllocateBuffer (
DescriptorLength,
ALIGNMENT_32,
(VOID **)&ReportDesc
);
if (EFI_ERROR(Status)) {
return FALSE;
}
MouseDeviceFound = FALSE;
for (Index = 0; Index < 3; Index ++) {
Status = mUsbCore->UsbGetReportDescriptor (
UsbIo,
InterfaceDescriptor.InterfaceNumber,
DescriptorLength,
ReportDesc
);
if (!EFI_ERROR (Status)) {
break;
} else if (Status == EFI_DEVICE_ERROR) {
//
// Stall 10ms to waiting for potential signal unstable
//
mUsbCore->Stall (10 * 1000);
}
}
if (!EFI_ERROR (Status) &&
(MatchHidDeviceType (ReportDesc, DescriptorLength, MOUSE_CLASS_CODE) ||
MatchHidDeviceType (ReportDesc, DescriptorLength, POINTER_CLASS_CODE)) &&
!MatchHidDeviceType (ReportDesc, DescriptorLength, TOUCH_SCREEN_CLASS_CODE) &&
!MatchHidDeviceType (ReportDesc, DescriptorLength, PEN_CLASS_CODE)) {
//
// Check the X/Y value type, we only support the relative value due to it is requirement for SimplePointer protocol
//
ZeroMem (&ReportFieldInfo, sizeof (REPORT_FIELD_INFO));
Status = ParseReportDescriptor (
ReportDesc,
DescriptorLength,
&ReportFieldInfo
);
if (EFI_ERROR (Status)) {
return FALSE;
}
//
// Searching for qualified mouse report
//
for (Index = 0; Index < ReportFieldInfo.Total; Index ++) {
if (ReportFieldInfo.ReportGroup[Index].DataValid &&
ReportFieldInfo.ReportGroup[Index].DataAttr == ATTR_MOUSE_INPUT &&
ReportFieldInfo.ReportGroup[Index].Data.Mouse.FieldX.ValueType == RELATIVE_VALUE &&
ReportFieldInfo.ReportGroup[Index].Data.Mouse.FieldY.ValueType == RELATIVE_VALUE) {
MouseDeviceFound = TRUE;
break;
}
}
}
mUsbCore->FreeBuffer (
DescriptorLength,
ReportDesc
);
if (MouseDeviceFound) return TRUE;
}
return FALSE;
}
EFI_STATUS
SetDeviceMode (
IN USB_MOUSE_DEV *UsbMouseDevice
)
{
EFI_STATUS Status;
REPORT_GROUP *ReportGroup;
UINT8 *ReportData;
if ((UsbMouseDevice == NULL) ||
(UsbMouseDevice->UsbIo == NULL) ||
(UsbMouseDevice->ReportFieldInfo.DeviceConfigurationReportGroup == NULL)) {
return EFI_INVALID_PARAMETER;
}
ReportGroup = UsbMouseDevice->ReportFieldInfo.DeviceConfigurationReportGroup;
//
// Since Device Configuration must have its own report (see HUTRR34.pdf), Report ID must be
// reported by the USB touch pad device and can not be the reserved value of 0.
//
if ((ReportGroup->Id == 0) || (!(ReportGroup->DataValid))) {
return EFI_UNSUPPORTED;
}
Status = mUsbCore->AllocateBuffer (
ReportGroup->DataSize,
ALIGNMENT_32,
(VOID **)&ReportData
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Set Device Mode to Mouse
//
ReportData[0] = ReportGroup->Id;
Status = SetReportFieldValue (
ReportData,
ReportGroup->DataSize,
&ReportGroup->Data.TouchPanelMode.FieldDeviceMode,
HID_DIGITIZERS_DEVICE_MODE_MOUSE
);
if (EFI_ERROR (Status)) {
goto EXIT;
}
if (ReportGroup->Data.TouchPanelMode.FieldDeviceId.DataValid) {
//
// Use a Device ID of 0 to configure all the report groups (i.e. top-level collections)
// at the same time regardless of Device ID (see HUTRR34.pdf).
//
Status = SetReportFieldValue (
ReportData,
ReportGroup->DataSize,
&ReportGroup->Data.TouchPanelMode.FieldDeviceId,
0
);
if (EFI_ERROR (Status)) {
goto EXIT;
}
}
Status = mUsbCore->UsbSetReportRequest (
UsbMouseDevice->UsbIo,
UsbMouseDevice->InterfaceDescriptor.InterfaceNumber,
ReportGroup->Id,
HID_FEATURE_REPORT,
ReportGroup->DataSize,
ReportData
);
EXIT:
mUsbCore->FreeBuffer (
ReportGroup->DataSize,
ReportData
);
return Status;
}
/**
Initialize the Usb Mouse Device.
@param UsbMouseDevice Device instance to be initialized
@retval EFI_SUCCESS Success
@retval EFI_DEVICE_ERROR Init error.
@retval EFI_OUT_OF_RESOURCES Can't allocate memory
**/
STATIC
EFI_STATUS
InitializeUsbMouseDevice (
IN USB_MOUSE_DEV *UsbMouseDevice
)
{
EFI_USB_IO_PROTOCOL *UsbIo;
UINT8 Protocol;
BOOLEAN ProtocolChange;
EFI_STATUS Status;
EFI_USB_HID_DESCRIPTOR MouseHidDesc;
UINT8 *ReportDesc;
EFI_USB_DEVICE_DESCRIPTOR DeviceDescriptor;
UINTN Index;
REPORT_GROUP *ReportGroup;
UINT16 DescriptorLength;
EFI_USB_CONFIG_DESCRIPTOR ConfigDesc;
UsbIo = UsbMouseDevice->UsbIo;
DescriptorLength = 0;
ReportDesc = NULL;
//
// Get HID descriptor
//
Status = mUsbCore->UsbGetHidDescriptor (
UsbIo,
UsbMouseDevice->InterfaceDescriptor.InterfaceNumber,
&MouseHidDesc
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Get Report descriptor
//
if (MouseHidDesc.HidClassDesc[0].DescriptorType != 0x22) {
return EFI_UNSUPPORTED;
}
if (UsbMouseDevice->InterfaceDescriptor.InterfaceNumber != 0) {
//
// Stall 5ms for non-first interface devices to workaround some
// slow devices failed on accepting subsequently command issue
//
mUsbCore->Stall (5 * 1000);
}
//
// Set appropriate protocol to the mouse interface
//
Protocol = REPORT_PROTOCOL;
ProtocolChange = FALSE;
if (UsbMouseDevice->InterfaceDescriptor.InterfaceSubClass == SUBCLASS_BOOT) {
Status = mUsbCore->UsbGetProtocolRequest (
UsbIo,
UsbMouseDevice->InterfaceDescriptor.InterfaceNumber,
&Protocol
);
if (!EFI_ERROR (Status)) {
//
// Stall 2ms after GetProtocol command to workaround slow devices failed on
// accepting subsequently command issue
//
mUsbCore->Stall (2 * 1000);
if (Protocol == REPORT_PROTOCOL) {
//
// Apply boot protocol to multiple interface device to workaround some
// multiple interface device unable to accept different protocol in each
// of interface
//
Status = UsbIo->UsbGetConfigDescriptor (
UsbIo,
&ConfigDesc
);
if (!EFI_ERROR (Status) && ConfigDesc.NumInterfaces > 1) {
Protocol = BOOT_PROTOCOL;
ProtocolChange = TRUE;
}
}
if (Protocol == BOOT_PROTOCOL) {
//
// Get device descriptor for VID/PID for Incompatible BootProtocol Mouse detection
//
UsbGetDeviceDescriptor (
UsbIo,
&DeviceDescriptor
);
//
// Incompatible BootProtocol Mouse detection
//
for (Index = 0; IncompatibleMouse[Index].IdVendor != 0; Index ++) {
if (IncompatibleMouse[Index].IdVendor == DeviceDescriptor.IdVendor && IncompatibleMouse[Index].IdProduct == DeviceDescriptor.IdProduct) {
Protocol = REPORT_PROTOCOL;
ProtocolChange = TRUE;
break;
}
}
}
if (ProtocolChange) {
mUsbCore->UsbSetProtocolRequest (
UsbIo,
UsbMouseDevice->InterfaceDescriptor.InterfaceNumber,
Protocol
);
}
}
}
if (Protocol == REPORT_PROTOCOL) {
//
// Set to report protocol explicitly in case some device doesn't in report protocol
//
if (!ProtocolChange) {
mUsbCore->UsbSetProtocolRequest (
UsbIo,
UsbMouseDevice->InterfaceDescriptor.InterfaceNumber,
REPORT_PROTOCOL
);
}
DescriptorLength = MouseHidDesc.HidClassDesc[0].DescriptorLength;
Status = mUsbCore->AllocateBuffer (
DescriptorLength,
ALIGNMENT_32,
(VOID **)&ReportDesc
);
if (EFI_ERROR(Status)) {
return EFI_OUT_OF_RESOURCES;
}
Status = mUsbCore->UsbGetReportDescriptor (
UsbIo,
UsbMouseDevice->InterfaceDescriptor.InterfaceNumber,
DescriptorLength,
ReportDesc
);
if (EFI_ERROR (Status)) {
goto ErrorExit;
}
//
// Parse report descriptor for set device mode to mouse
//
Status = ParseReportDescriptor (
ReportDesc,
DescriptorLength,
&UsbMouseDevice->ReportFieldInfo
);
if (EFI_ERROR (Status)) {
goto ErrorExit;
}
//
// If Device Configuration feature report is found, this touch pad device has multiple operating
// modes (mouse, single-input, multi-input, etc.). Some devices only send "correct" report data
// (i.e. absolute coordinates for digitizer as opposed to relative displacements for mouse) when
// they are operating in mouse mode. However, some other devices do not change operating
// mode after SetDeviceMode (), but can still work with their report data; thus, we do not check
// the return status of SetDeviceMode ().
//
if (UsbMouseDevice->ReportFieldInfo.DeviceConfigurationReportGroup != NULL) {
SetDeviceMode (UsbMouseDevice);
}
//
// Searching for mouse report
//
for (Index = 0, ReportGroup = NULL; Index < UsbMouseDevice->ReportFieldInfo.Total; Index ++) {
if (UsbMouseDevice->ReportFieldInfo.ReportGroup[Index].DataValid &&
(UsbMouseDevice->ReportFieldInfo.ReportGroup[Index].DataClass == MOUSE_CLASS_CODE ||
UsbMouseDevice->ReportFieldInfo.ReportGroup[Index].DataClass == POINTER_CLASS_CODE) &&
UsbMouseDevice->ReportFieldInfo.ReportGroup[Index].DataType == HID_MAIN_ITEM_TAG_INPUT &&
UsbMouseDevice->ReportFieldInfo.ReportGroup[Index].DataSize > 0) {
ReportGroup = &UsbMouseDevice->ReportFieldInfo.ReportGroup[Index];
break;
}
}
if (ReportGroup == NULL) {
Status = EFI_UNSUPPORTED;
goto ErrorExit;
}
} else {
//
// Set ReportFieldInfo for boot protocol when target protocol is boot protocol
//
UsbMouseDevice->ReportFieldInfo.Total = 1;
ReportGroup = &UsbMouseDevice->ReportFieldInfo.ReportGroup[0];
ReportGroup->DataValid = TRUE;
ReportGroup->DataClass = MOUSE_CLASS_CODE;
ReportGroup->DataType = HID_MAIN_ITEM_TAG_INPUT;
ReportGroup->DataSize = 3;
ReportGroup->Id = 0;
ReportGroup->Data.Mouse.FieldButton1.DataValid = TRUE;
ReportGroup->Data.Mouse.FieldButton1.BitLength = 1;
ReportGroup->Data.Mouse.FieldButton1.BitOffset = 0;
ReportGroup->Data.Mouse.FieldButton1.Min = 0;
ReportGroup->Data.Mouse.FieldButton1.Max = 1;
ReportGroup->Data.Mouse.FieldButton2.DataValid = TRUE;
ReportGroup->Data.Mouse.FieldButton2.BitLength = 1;
ReportGroup->Data.Mouse.FieldButton2.BitOffset = 1;
ReportGroup->Data.Mouse.FieldButton2.Min = 0;
ReportGroup->Data.Mouse.FieldButton2.Max = 1;
ReportGroup->Data.Mouse.FieldX.DataValid = TRUE;
ReportGroup->Data.Mouse.FieldX.BitLength = 8;
ReportGroup->Data.Mouse.FieldX.BitOffset = 8;
ReportGroup->Data.Mouse.FieldX.Min = 0x80;
ReportGroup->Data.Mouse.FieldX.Max = 0x7f;
ReportGroup->Data.Mouse.FieldY.DataValid = TRUE;
ReportGroup->Data.Mouse.FieldY.BitLength = 8;
ReportGroup->Data.Mouse.FieldY.BitOffset = 16;
ReportGroup->Data.Mouse.FieldY.Min = 0x80;
ReportGroup->Data.Mouse.FieldY.Max = 0x7f;
ReportGroup->Data.Mouse.FieldZ.DataValid = TRUE;
ReportGroup->Data.Mouse.FieldZ.BitLength = 8;
ReportGroup->Data.Mouse.FieldZ.BitOffset = 24;
ReportGroup->Data.Mouse.FieldZ.Min = 0x80;
ReportGroup->Data.Mouse.FieldZ.Max = 0x7f;
}
UsbMouseDevice->ReportId = ReportGroup->Id;
if (ReportGroup->Data.Mouse.FieldButton1.DataValid) {
UsbMouseDevice->Mode.LeftButton = TRUE;
}
if (ReportGroup->Data.Mouse.FieldButton2.DataValid) {
UsbMouseDevice->Mode.RightButton = TRUE;
}
if (ReportGroup->Data.Mouse.FieldX.DataValid) {
UsbMouseDevice->Mode.ResolutionX = 8;
}
if (ReportGroup->Data.Mouse.FieldY.DataValid) {
UsbMouseDevice->Mode.ResolutionY = 8;
}
if (ReportGroup->Data.Mouse.FieldZ.DataValid) {
UsbMouseDevice->Mode.ResolutionZ = 8;
}
//
// Set indefinite Idle rate for USB Mouse
//
mUsbCore->UsbSetIdleRequest (
UsbIo,
UsbMouseDevice->InterfaceDescriptor.InterfaceNumber,
0,
0
);
if (UsbMouseDevice->RecoveryPollingHandle) {
mUsbCore->RemovePeriodicTimer(UsbMouseDevice->RecoveryPollingHandle);
UsbMouseDevice->RecoveryPollingHandle = NULL;
}
ErrorExit:
mUsbCore->FreeBuffer (
DescriptorLength,
ReportDesc
);
return Status;
}
#ifndef MIN_INT32
#define MIN_INT32 (((INT32) -2147483647) - 1)
#endif
INT32
PlusRelativeMovement (
IN INT32 FirstInteger,
IN INT32 SecondInteger
)
{
if (SecondInteger > 0) {
if (FirstInteger > MAX_INT32 - SecondInteger) {
FirstInteger = MAX_INT32;
} else {
FirstInteger += SecondInteger;
}
} else {
if (FirstInteger > (INT32)(MIN_INT32 - SecondInteger)) {
FirstInteger += SecondInteger;
} else {
FirstInteger = MIN_INT32;
}
}
return FirstInteger;
}
/**
It is called whenever there is data received from async interrupt
transfer.
@param Data Data received.
@param DataLength Length of Data
@param Context Passed in context
@param Result Async Interrupt Transfer result
@retval EFI_SUCCESS
@retval EFI_DEVICE_ERROR
**/
STATIC
EFI_STATUS
EFIAPI
OnMouseInterruptComplete (
IN VOID *Data,
IN UINTN DataLength,
IN VOID *Context,
IN UINT32 Result
)
{
USB_MOUSE_DEV *UsbMouseDevice;
EFI_USB_IO_PROTOCOL *UsbIo;
UINT8 EndpointAddr;
UINT32 UsbResult;
UINTN Mode;
UINTN Count;
INT8 MouseCode[4];
UINTN Index;
REPORT_GROUP *ReportGroup;
BOOLEAN LeftButton;
BOOLEAN RightButton;
UINTN BufferIn;
INT32 RelativeMovementX;
INT32 RelativeMovementY;
INT32 RelativeMovementZ;
EFI_SIMPLE_POINTER_STATE *MouseState;
UsbMouseDevice = (USB_MOUSE_DEV *) Context;
UsbIo = UsbMouseDevice->UsbIo;
if (UsbIo == NULL) return EFI_DEVICE_ERROR;
//
// Get current mode
//
mUsbCore->GetMode(&Mode);
//
// Analyzes the Result and performs corresponding action.
//
if (Result != EFI_USB_NOERROR) {
//
// Some errors happen during the process
//
MouseReportStatusCode (
UsbMouseDevice,
EFI_ERROR_CODE | EFI_ERROR_MINOR,
(EFI_PERIPHERAL_MOUSE | EFI_P_EC_INPUT_ERROR)
);
if ((Result & EFI_USB_ERR_STALL) == EFI_USB_ERR_STALL) {
EndpointAddr = UsbMouseDevice->IntEndpointDescriptor.EndpointAddress;
mUsbCore->UsbClearEndpointHalt (
UsbIo,
EndpointAddr,
&UsbResult
);
}
UsbAsyncInterruptTransfer (
UsbIo,
UsbMouseDevice->IntEndpointDescriptor.EndpointAddress,
FALSE,
0,
0,
NULL,
NULL
);
//
// Check is the device be detached
//
if (mUsbCore->CheckDeviceDetached(UsbIo) == EFI_SUCCESS) {
return EFI_DEVICE_ERROR;
}
mUsbCore->InsertPeriodicTimer(
USB_CORE_ONCE_TIMER,
UsbMouseRecoveryHandler,
UsbMouseDevice,
EFI_USB_INTERRUPT_DELAY / 10000,
&UsbMouseDevice->RecoveryPollingHandle
);
return EFI_DEVICE_ERROR;
}
if (DataLength == 0 || Data == NULL) {
return EFI_SUCCESS;
}
ReportGroup = &UsbMouseDevice->ReportFieldInfo.ReportGroup[0];
if (UsbMouseDevice->ReportId != 0) {
for (Index = 0; Index < UsbMouseDevice->ReportFieldInfo.Total; Index++) {
if ((UsbMouseDevice->ReportFieldInfo.ReportGroup[Index].Id == (*(UINT8*)Data)) &&
(UsbMouseDevice->ReportFieldInfo.ReportGroup[Index].DataValid)) {
ReportGroup = &UsbMouseDevice->ReportFieldInfo.ReportGroup[Index];
break;
}
}
}
if (Mode != USB_CORE_RUNTIME_MODE) {
UsbMouseDevice->StateChanged = TRUE;
//
// Check mouse Data
//
LeftButton = UsbMouseDevice->State[UsbMouseDevice->BufferIn].LeftButton;
RightButton = UsbMouseDevice->State[UsbMouseDevice->BufferIn].RightButton;
UsbMouseDevice->State[UsbMouseDevice->BufferIn].LeftButton = (BOOLEAN) GetReportFieldValue (Data, DataLength, &ReportGroup->Data.Mouse.FieldButton1);
UsbMouseDevice->State[UsbMouseDevice->BufferIn].RightButton = (BOOLEAN) GetReportFieldValue (Data, DataLength, &ReportGroup->Data.Mouse.FieldButton2);
RelativeMovementX = (INT8) GetReportFieldValue (Data, DataLength, &ReportGroup->Data.Mouse.FieldX);
RelativeMovementY = (INT8) GetReportFieldValue (Data, DataLength, &ReportGroup->Data.Mouse.FieldY);
RelativeMovementZ = 0;
if (ReportGroup->Data.Mouse.FieldZ.DataValid) {
RelativeMovementZ = (INT8) GetReportFieldValue (Data, DataLength, &ReportGroup->Data.Mouse.FieldZ);
}
if (LeftButton != UsbMouseDevice->State[UsbMouseDevice->BufferIn].LeftButton ||
RightButton != UsbMouseDevice->State[UsbMouseDevice->BufferIn].RightButton) {
//
// Advance the internal buffer only when button state changed to reduce the dragging phenomenon
//
BufferIn = UsbMouseDevice->BufferIn + 1;
if (BufferIn == STATE_BUFFER_SIZE) BufferIn = 0;
if (BufferIn != UsbMouseDevice->BufferOut) {
//
// Copy last state to new position for use of check procedure of last button state
//
CopyMem (
&UsbMouseDevice->State[BufferIn],
&UsbMouseDevice->State[UsbMouseDevice->BufferIn],
sizeof (EFI_SIMPLE_POINTER_STATE)
);
UsbMouseDevice->BufferIn = BufferIn;
MouseState = &UsbMouseDevice->State[UsbMouseDevice->BufferIn];
MouseState->RelativeMovementX = RelativeMovementX;
MouseState->RelativeMovementY = RelativeMovementY;
MouseState->RelativeMovementZ = RelativeMovementZ;
}
} else {
MouseState = &UsbMouseDevice->State[UsbMouseDevice->BufferIn];
MouseState->RelativeMovementX = PlusRelativeMovement (MouseState->RelativeMovementX, RelativeMovementX);
MouseState->RelativeMovementY = PlusRelativeMovement (MouseState->RelativeMovementY, RelativeMovementY);
MouseState->RelativeMovementZ = PlusRelativeMovement (MouseState->RelativeMovementZ, RelativeMovementZ);
}
} else if (mUsbCore->IsCsmEnabled () == EFI_SUCCESS) {
//
// Insert mouse KBC code for legacy mode
//
MouseCode[0] = (INT8)GetReportFieldValue (Data, DataLength, &ReportGroup->Data.Mouse.FieldButton1);
MouseCode[0] |= ((INT8)GetReportFieldValue (Data, DataLength, &ReportGroup->Data.Mouse.FieldButton2)) << 1;
MouseCode[0] |= 0x08;
MouseCode[1] = (INT8)GetReportFieldValue (Data, DataLength, &ReportGroup->Data.Mouse.FieldX);
MouseCode[2] = -((INT8)GetReportFieldValue (Data, DataLength, &ReportGroup->Data.Mouse.FieldY));
if (MouseCode[1] < 0) MouseCode[0] |= 0x10;
if (MouseCode[2] < 0) MouseCode[0] |= 0x20;
MouseCode[3] = 0;
Count = (EBDA(EBDA_PS2_AUX_TYPE) == 0x03) ? 4 : 3;
mUsbCore->InsertKbcKeyCode(USB_CORE_MOUSE_CODE, (UINT8 *)MouseCode, Count);
}
return EFI_SUCCESS;
}
/**
Get the mouse state, see SIMPLE POINTER PROTOCOL.
@param This Protocol instance pointer.
@param MouseState Current mouse state
@retval EFI_SUCCESS
@retval EFI_DEVICE_ERROR
@retval EFI_NOT_READY
**/
STATIC
EFI_STATUS
EFIAPI
GetMouseState (
IN EFI_SIMPLE_POINTER_PROTOCOL *This,
OUT EFI_SIMPLE_POINTER_STATE *MouseState
)
{
USB_MOUSE_DEV *MouseDev;
if (MouseState == NULL) {
return EFI_DEVICE_ERROR;
}
MouseDev = USB_MOUSE_DEV_FROM_MOUSE_PROTOCOL (This);
if (!MouseDev->StateChanged) {
return EFI_NOT_READY;
}
CopyMem (
MouseState,
&MouseDev->State[MouseDev->BufferOut],
sizeof (EFI_SIMPLE_POINTER_STATE)
);
ZeroMem (&MouseDev->State[MouseDev->BufferOut], sizeof (EFI_SIMPLE_POINTER_STATE));
if (MouseDev->BufferOut != MouseDev->BufferIn) {
MouseDev->BufferOut ++;
if (MouseDev->BufferOut == STATE_BUFFER_SIZE) MouseDev->BufferOut = 0;
}
if (MouseDev->BufferOut == MouseDev->BufferIn) {
MouseDev->StateChanged = FALSE;
}
return EFI_SUCCESS;
}
/**
Reset the mouse device, see SIMPLE POINTER PROTOCOL.
@param This Protocol instance pointer.
@param ExtendedVerification Ignored here
@retval EFI_SUCCESS
**/
STATIC
EFI_STATUS
EFIAPI
UsbMouseReset (
IN EFI_SIMPLE_POINTER_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
)
{
USB_MOUSE_DEV *UsbMouseDevice;
EFI_USB_IO_PROTOCOL *UsbIo;
UsbMouseDevice = USB_MOUSE_DEV_FROM_MOUSE_PROTOCOL (This);
UsbIo = UsbMouseDevice->UsbIo;
MouseReportStatusCode (
UsbMouseDevice,
EFI_PROGRESS_CODE,
(EFI_PERIPHERAL_MOUSE | EFI_P_PC_RESET)
);
ZeroMem (
UsbMouseDevice->State,
sizeof (EFI_SIMPLE_POINTER_STATE) * STATE_BUFFER_SIZE
);
UsbMouseDevice->StateChanged = FALSE;
UsbMouseDevice->BufferIn = 0;
UsbMouseDevice->BufferOut = 0;
return EFI_SUCCESS;
}
/**
Event notification function for SIMPLE_POINTER.WaitForInput event
Signal the event if there is input from mouse
@param Event Wait Event
@param Context Passed parameter to event handler
**/
STATIC
VOID
EFIAPI
UsbMouseWaitForInput (
IN EFI_EVENT Event,
IN VOID *Context
)
{
USB_MOUSE_DEV *UsbMouseDevice;
UsbMouseDevice = (USB_MOUSE_DEV *) Context;
//
// Someone is waiting on the mouse event, if there's
// input from mouse, signal the event
//
if (UsbMouseDevice->StateChanged) {
gBS->SignalEvent (Event);
}
}
/**
Timer handler for Delayed Recovery timer.
@param Event The Delayed Recovery event.
@param Context Points to the USB_KB_DEV instance.
**/
VOID
EFIAPI
UsbMouseRecoveryHandler (
IN UINTN Event,
IN VOID *Context
)
{
USB_MOUSE_DEV *UsbMouseDevice;
EFI_USB_IO_PROTOCOL *UsbIo;
UsbMouseDevice = (USB_MOUSE_DEV *) Context;
UsbMouseDevice->RecoveryPollingHandle = NULL;
UsbIo = UsbMouseDevice->UsbIo;
if (UsbIo == NULL) return;
UsbAsyncInterruptTransfer (
UsbIo,
UsbMouseDevice->IntEndpointDescriptor.EndpointAddress,
TRUE,
UsbMouseDevice->IntEndpointDescriptor.Interval,
UsbMouseDevice->IntEndpointDescriptor.MaxPacketSize,
OnMouseInterruptComplete,
UsbMouseDevice
);
}
/**
Report Status Code in Usb Bot Driver
@param DevicePath Use this to get Device Path
@param CodeType Status Code Type
@param CodeValue Status Code Value
**/
VOID
MouseReportStatusCode (
IN USB_MOUSE_DEV *UsbMouseDevice,
IN EFI_STATUS_CODE_TYPE CodeType,
IN EFI_STATUS_CODE_VALUE Value
)
{
UINTN Mode;
mUsbCore->GetMode(&Mode);
if (Mode != USB_CORE_RUNTIME_MODE) {
//
// Only report status code during POST and non-SMM mode
//
if (!mInSmram) {
ReportStatusCodeWithDevicePath (
CodeType,
Value,
UsbMouseDevice->DevicePath
);
}
}
}