1463 lines
50 KiB
C
1463 lines
50 KiB
C
/**@file
|
|
Touch Panel Driver. Works with all HID I2C - compatible touchpanels
|
|
|
|
@copyright
|
|
INTEL CONFIDENTIAL
|
|
Copyright 2013 - 2020 Intel Corporation.
|
|
|
|
The source code contained or described herein and all documents related to the
|
|
source code ("Material") are owned by Intel Corporation or its suppliers or
|
|
licensors. Title to the Material remains with Intel Corporation or its suppliers
|
|
and licensors. The Material may contain trade secrets and proprietary and
|
|
confidential information of Intel Corporation and its suppliers and licensors,
|
|
and is protected by worldwide copyright and trade secret laws and treaty
|
|
provisions. No part of the Material may be used, copied, reproduced, modified,
|
|
published, uploaded, posted, transmitted, distributed, or disclosed in any way
|
|
without Intel's prior express written permission.
|
|
|
|
No license under any patent, copyright, trade secret or other intellectual
|
|
property right is granted to or conferred upon you by disclosure or delivery
|
|
of the Materials, either expressly, by implication, inducement, estoppel or
|
|
otherwise. Any license under such intellectual property rights must be
|
|
express and approved by Intel in writing.
|
|
|
|
Unless otherwise agreed by Intel in writing, you may not remove or alter
|
|
this notice or any other notice embedded in Materials by Intel or
|
|
Intel's suppliers or licensors in any way.
|
|
|
|
This file contains a 'Sample Driver' and is licensed as such under the terms
|
|
of your license agreement with Intel or your vendor. This file may be modified
|
|
by the user, subject to the additional terms of the license agreement.
|
|
|
|
@par Specification Reference:
|
|
|
|
**/
|
|
|
|
#include "I2cTouchPanel.h"
|
|
|
|
#define OPCODE_SET_POWER 0x8
|
|
#define POWER_STATE_ON 0x0
|
|
#define POWER_STATE_SLEEP 0x1
|
|
#define OPCODE_RESET 0x1
|
|
#define RESET_REPORT_ID 0x0
|
|
|
|
EFI_GUID gSupportedTouchPanelGuid = { 0x1bf4b7ae, 0xc199, 0x4f10, { 0xa8, 0xc6, 0xd0, 0xc1, 0xd0, 0x10, 0x98, 0x06 } };
|
|
|
|
//
|
|
// TODO:
|
|
// decide how to consume platform-specific support functions (init, interrupt, getHID) :
|
|
// - put them in a lib and statically link the lib to driver [current solution], or
|
|
// - install them as protocol on touch device's handle, and load them dynamically in driver
|
|
//
|
|
|
|
///
|
|
/// Driver Support EFI Version Protocol instance
|
|
///
|
|
GLOBAL_REMOVE_IF_UNREFERENCED
|
|
EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gTouchPanelDriverDriverSupportedEfiVersion = {
|
|
sizeof (EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL),
|
|
0x0002001E
|
|
};
|
|
|
|
///
|
|
/// Driver Binding Protocol instance
|
|
///
|
|
GLOBAL_REMOVE_IF_UNREFERENCED EFI_DRIVER_BINDING_PROTOCOL gTouchPanelDriverDriverBinding = {
|
|
TouchPanelDriverDriverBindingSupported,
|
|
TouchPanelDriverDriverBindingStart,
|
|
TouchPanelDriverDriverBindingStop,
|
|
TOUCH_DRIVER_VERSION,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
|
|
EFI_I2C_REQUEST_PACKET*
|
|
NewRequestPacket (
|
|
UINTN Operations
|
|
)
|
|
{
|
|
EFI_I2C_REQUEST_PACKET *NewPacket;
|
|
if (Operations == 0) {
|
|
return NULL;
|
|
}
|
|
NewPacket = AllocateZeroPool (sizeof(EFI_I2C_REQUEST_PACKET) + (Operations -1) * sizeof(EFI_I2C_OPERATION));
|
|
|
|
if (NewPacket != NULL) {
|
|
NewPacket->OperationCount = Operations;
|
|
}
|
|
|
|
return NewPacket;
|
|
}
|
|
|
|
VOID
|
|
DeleteRequestPacket (
|
|
EFI_I2C_REQUEST_PACKET *Packet
|
|
)
|
|
{
|
|
FreePool(Packet);
|
|
}
|
|
|
|
|
|
/**
|
|
This function resets the pointer device hardware. As part of
|
|
initialization process, the firmware/device will make a quick
|
|
but reasonable attempt to verify that the device is
|
|
functioning. If the ExtendedVerification flag is TRUE the
|
|
firmware may take an extended amount of time to verify the
|
|
device is operating on reset. Otherwise the reset operation is
|
|
to occur as quickly as possible. The hardware verification
|
|
process is not defined by this specification and is left up to
|
|
the platform firmware or driver to implement.
|
|
|
|
@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
|
|
TouchPanelDriverAbsolutePointerReset (
|
|
IN EFI_ABSOLUTE_POINTER_PROTOCOL *This,
|
|
IN BOOLEAN ExtendedVerification
|
|
)
|
|
{
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
/**
|
|
The GetState() function retrieves the current state of a pointer
|
|
device. This includes information on the active state associated
|
|
with the pointer device and the current position of the axes
|
|
associated with the pointer device. If the state of the pointer
|
|
device has not changed since the last call to GetState(), then
|
|
EFI_NOT_READY is returned. If the state of the pointer device
|
|
has changed since the last call to GetState(), then the state
|
|
information is placed in State, and EFI_SUCCESS is returned. If
|
|
a device error occurs while attempting to retrieve the state
|
|
information, then EFI_DEVICE_ERROR is returned.
|
|
|
|
|
|
@param This A pointer to the EFI_ABSOLUTE_POINTER_PROTOCOL
|
|
instance.
|
|
|
|
@param State 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.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
TouchPanelDriverAbsolutePointerGetState (
|
|
IN EFI_ABSOLUTE_POINTER_PROTOCOL *This,
|
|
IN OUT EFI_ABSOLUTE_POINTER_STATE *State
|
|
)
|
|
{
|
|
ABSOLUTE_POINTER_DEV *TouchDev;
|
|
|
|
if (State == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
TouchDev = TOUCH_CONTEXT_FROM_PROTOCOL (This);
|
|
|
|
if (!TouchDev->NewStateAvailable) {
|
|
return EFI_NOT_READY;
|
|
}
|
|
|
|
//
|
|
// Retrieve touch state from _ABSOLUTE_POINTER_DEV,
|
|
// which was filled by CheckForPress()
|
|
//
|
|
State->CurrentX = TouchDev->State.CurrentX;
|
|
State->CurrentY = TouchDev->State.CurrentY;
|
|
State->CurrentZ = TouchDev->State.CurrentZ;
|
|
State->ActiveButtons = TouchDev->State.ActiveButtons;
|
|
|
|
//
|
|
// State data was consumed - clear availability field
|
|
//
|
|
TouchDev->NewStateAvailable = FALSE;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Unloads an image.
|
|
|
|
@param ImageHandle Handle that identifies the image to be unloaded.
|
|
|
|
@retval EFI_SUCCESS The image has been unloaded.
|
|
@retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
TouchPanelDriverUnload (
|
|
IN EFI_HANDLE ImageHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE *HandleBuffer;
|
|
UINTN HandleCount;
|
|
UINTN Index;
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
Status = gBS->LocateHandleBuffer ( AllHandles, NULL, NULL, &HandleCount, &HandleBuffer);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
for (Index = 0; Index < HandleCount; Index++) {
|
|
Status = gBS->DisconnectController (HandleBuffer[Index], ImageHandle, NULL);
|
|
}
|
|
|
|
FreePool (HandleBuffer);
|
|
|
|
Status = gBS->UninstallMultipleProtocolInterfaces (
|
|
ImageHandle,
|
|
&gEfiDriverSupportedEfiVersionProtocolGuid, &gTouchPanelDriverDriverSupportedEfiVersion,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = gBS->UninstallMultipleProtocolInterfaces (
|
|
ImageHandle,
|
|
&gEfiDriverBindingProtocolGuid, &gTouchPanelDriverDriverBinding,
|
|
&gEfiComponentNameProtocolGuid, &gTouchPanelDriverComponentName,
|
|
&gEfiComponentName2ProtocolGuid, &gTouchPanelDriverComponentName2,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
This is the declaration of an EFI image entry point. This entry point is
|
|
the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
|
|
both device drivers and bus drivers.
|
|
|
|
@param ImageHandle The firmware allocated handle for the UEFI image.
|
|
@param SystemTable A pointer to the EFI System Table.
|
|
|
|
@retval EFI_SUCCESS The operation completed successfully.
|
|
@retval Others An unexpected error occurred.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
TouchPanelDriverEntryPoint (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// Install UEFI Driver Model protocol(s).
|
|
//
|
|
Status = EfiLibInstallDriverBindingComponentName2 (
|
|
ImageHandle,
|
|
SystemTable,
|
|
&gTouchPanelDriverDriverBinding,
|
|
ImageHandle,
|
|
&gTouchPanelDriverComponentName,
|
|
&gTouchPanelDriverComponentName2
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
//
|
|
// Install Driver Supported EFI Version Protocol onto ImageHandle
|
|
//
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&ImageHandle,
|
|
&gEfiDriverSupportedEfiVersionProtocolGuid, &gTouchPanelDriverDriverSupportedEfiVersion,
|
|
NULL
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Looks for controllers which declare the EFI_I2C_IO_PROTOCOL and match the device path
|
|
supplied by the silicon vendor or third party I2C driver writer to the platform integrator.
|
|
|
|
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
|
|
@param[in] ControllerHandle The handle of the controller to test. This handle
|
|
must support a protocol interface that supplies
|
|
an I/O abstraction to the driver.
|
|
@param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
|
|
parameter is ignored by device drivers, and is optional for bus
|
|
drivers. For bus drivers, if this parameter is not NULL, then
|
|
the bus driver must determine if the bus controller specified
|
|
by ControllerHandle and the child controller specified
|
|
by RemainingDevicePath are both supported by this
|
|
bus driver.
|
|
|
|
@retval EFI_SUCCESS The device specified by ControllerHandle and
|
|
RemainingDevicePath is supported by the driver specified by This.
|
|
@retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
|
|
RemainingDevicePath is already being managed by the driver
|
|
specified by This.
|
|
@retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
|
|
RemainingDevicePath is already being managed by a different
|
|
driver or an application that requires exclusive access.
|
|
Currently not implemented.
|
|
@retval EFI_UNSUPPORTED The device specified by ControllerHandle and
|
|
RemainingDevicePath is not supported by the driver specified by This.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
TouchPanelDriverDriverBindingSupported (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE ControllerHandle,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_I2C_IO_PROTOCOL *I2cIoProtocol;
|
|
|
|
//
|
|
// Open protocol by driver to obtain device guid
|
|
//
|
|
Status = gBS->OpenProtocol(
|
|
ControllerHandle,
|
|
&gEfiI2cIoProtocolGuid,
|
|
(VOID **)&I2cIoProtocol,
|
|
This->DriverBindingHandle,
|
|
ControllerHandle,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Compare the known GUID with the GUID pointed to by the DeviceGuid field
|
|
//
|
|
if (CompareGuid (&gSupportedTouchPanelGuid, I2cIoProtocol->DeviceGuid)) {
|
|
DEBUG((DEBUG_INFO, "TouchPanel Supported: DeviceGuid = %g\n", I2cIoProtocol->DeviceGuid));
|
|
Status = gBS->CloseProtocol (ControllerHandle, &gEfiI2cIoProtocolGuid, This->DriverBindingHandle, ControllerHandle);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
gBS->CloseProtocol (ControllerHandle, &gEfiI2cIoProtocolGuid, This->DriverBindingHandle, ControllerHandle);
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
|
|
/**
|
|
Starts touchpanel device controller.
|
|
If initialization completes successfully, this driver will install AbsolutePointer protocol
|
|
on this device. This protocol can be further used for features such as virtual keyboard.
|
|
|
|
Initialization steps:
|
|
- Call init function supplied from platform-specific lib to initialize hardware
|
|
- Read device's HID descriptor, according to Microsoft's HID over I2C specification
|
|
- Using data from HID descriptor, build a table of input descriptors. A single HID device may
|
|
produce data in more than 1 format and each needs to be parsed differently.
|
|
|
|
Device context caches one touch event. Cache gets invalidated when its data is consumed by calling
|
|
GetState() from AbsolutePointer protocol.
|
|
Driver checks device's interrupt and potentially retrieves new touch data only if
|
|
cache is empty and either GetState() was called or a periodic timer event was triggered.
|
|
When new data is retrieved from device, absolute pointer's WaitForInput event gets signalled
|
|
to inform users of this protocol that new data is available.
|
|
Such construction allows the following uses of Absolute Pointer protocol:
|
|
- consumer calls gBS->WaitForEvent on AbsPtr's WaitForEvent event, then calls GetState which is
|
|
guaranteed to contain valid data (the suggested way of using AbsolutePointer protocol)
|
|
- consumer calls gBS->checkEvent on AbsPtr's WaitForevent event, and if it returned success then
|
|
consumer may call Getstate() which is guaranteed to contain valid data
|
|
- consumer calls GetState() without bothering with events; GetState() either returns valid data
|
|
or an error
|
|
|
|
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
|
|
@param[in] ControllerHandle The handle of the controller to start. This handle
|
|
must support I2C protocol interface.
|
|
@param[in] RemainingDevicePath ignored
|
|
|
|
@retval EFI_SUCCESS The device was started.
|
|
@retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented.
|
|
@retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
|
|
@retval Others The driver failded to start the device.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
TouchPanelDriverDriverBindingStart (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE ControllerHandle,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
|
|
)
|
|
{
|
|
ABSOLUTE_POINTER_DEV *TouchDev;
|
|
EFI_I2C_IO_PROTOCOL *I2cIoProtocol;
|
|
EFI_STATUS Status;
|
|
|
|
DEBUG ((DEBUG_INFO, "TOUCH start\n"));
|
|
|
|
Status = gBS->OpenProtocol (
|
|
ControllerHandle,
|
|
&gEfiI2cIoProtocolGuid,
|
|
(VOID **)&I2cIoProtocol,
|
|
This->DriverBindingHandle,
|
|
ControllerHandle,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Allocate the driver context
|
|
//
|
|
TouchDev = AllocateZeroPool (sizeof (ABSOLUTE_POINTER_DEV));
|
|
if (TouchDev == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
TouchDev->ControllerHandle = ControllerHandle;
|
|
TouchDev->Signature = TOUCH_SIGNATURE;
|
|
|
|
TouchDev->I2cIoProtocol = I2cIoProtocol;
|
|
|
|
TouchDev->AbsolutePointerProtocol.GetState = TouchPanelDriverAbsolutePointerGetState;
|
|
TouchDev->AbsolutePointerProtocol.Reset = TouchPanelDriverAbsolutePointerReset;
|
|
TouchDev->AbsolutePointerProtocol.Mode = &(TouchDev->Mode);
|
|
|
|
//
|
|
// platform-specific functions, supplied either as statically linked lib
|
|
// or a protocol installed on device handle. Current implementation: lib
|
|
//
|
|
TouchDev->HidI2cPlatformSupport.GetHidRegisterAddress = HidI2cGetHidRegisterAddress;
|
|
TouchDev->HidI2cPlatformSupport.CheckInterrupt = HidI2cCheckInterrupt;
|
|
TouchDev->HidI2cPlatformSupport.Initialize = HidI2cInitialize;
|
|
|
|
//
|
|
// Initialize hardware in platform-specific way
|
|
//
|
|
TouchDev->HidI2cPlatformSupport.Initialize(TouchDev->ControllerHandle);
|
|
|
|
AddUnicodeString2 (
|
|
"eng",
|
|
gTouchPanelDriverComponentName.SupportedLanguages,
|
|
&TouchDev->ControllerNameTable,
|
|
L"HID-compatible Touchpanel Driver",
|
|
TRUE
|
|
);
|
|
AddUnicodeString2 (
|
|
"en",
|
|
gTouchPanelDriverComponentName2.SupportedLanguages,
|
|
&TouchDev->ControllerNameTable,
|
|
L"HID-compatible Touchpanel Driver",
|
|
FALSE
|
|
);
|
|
|
|
Status = gBS->CreateEvent (
|
|
EVT_NOTIFY_WAIT,
|
|
TPL_CALLBACK,
|
|
CheckDataAvailableCallback,
|
|
(VOID*)TouchDev,
|
|
&TouchDev->AbsolutePointerProtocol.WaitForInput
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Initialize device by reading its HID descriptor
|
|
//
|
|
Status = TouchDevInit (TouchDev);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Status = gBS->CreateEvent (EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, CheckDataAvailableCallback, (VOID*)TouchDev, &TouchDev->PollingEvent);
|
|
if (EFI_ERROR(Status)) {
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Status = gBS->SetTimer (
|
|
TouchDev->PollingEvent,
|
|
TimerPeriodic,
|
|
EFI_TIMER_PERIOD_MICROSECONDS (1)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Failed to SetTimer PollingEvent, Error: %r\n", Status));
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&ControllerHandle,
|
|
&gEfiAbsolutePointerProtocolGuid,
|
|
&TouchDev->AbsolutePointerProtocol,
|
|
&gEdkiiTouchPanelGuid,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ErrorExit;
|
|
}
|
|
|
|
return Status;
|
|
|
|
ErrorExit:
|
|
DEBUG ((DEBUG_ERROR, "TOUCH start exiting, %r\n", Status));
|
|
|
|
if ((TouchDev != NULL) && (TouchDev->AbsolutePointerProtocol.WaitForInput != NULL)) {
|
|
gBS->CloseEvent (TouchDev->AbsolutePointerProtocol.WaitForInput);
|
|
}
|
|
|
|
if ((TouchDev != NULL) && (TouchDev->ControllerNameTable != NULL)) {
|
|
FreeUnicodeStringTable (TouchDev->ControllerNameTable);
|
|
}
|
|
|
|
if (TouchDev != NULL) {
|
|
FreePool (TouchDev);
|
|
}
|
|
|
|
gBS->CloseProtocol (
|
|
ControllerHandle,
|
|
&gEfiI2cIoProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
ControllerHandle
|
|
);
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
Stops a device controller or a bus controller.
|
|
|
|
The Stop() function is designed to be invoked from the EFI boot service DisconnectController().
|
|
As a result, much of the error checking on the parameters to Stop() has been moved
|
|
into this common boot service. It is legal to call Stop() from other locations,
|
|
but the following calling restrictions must be followed, or the system behavior will not be deterministic.
|
|
1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
|
|
same driver's Start() function.
|
|
2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
|
|
EFI_HANDLE. In addition, all of these handles must have been created in this driver's
|
|
Start() function, and the Start() function must have called OpenProtocol() on
|
|
ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
|
|
|
|
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
|
|
@param[in] ControllerHandle A handle to the device being stopped. The handle must
|
|
support a bus specific I/O protocol for the driver
|
|
to use to stop the device.
|
|
@param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
|
|
@param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
|
|
if NumberOfChildren is 0.
|
|
|
|
@retval EFI_SUCCESS The device was stopped.
|
|
@retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
TouchPanelDriverDriverBindingStop (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE ControllerHandle,
|
|
IN UINTN NumberOfChildren,
|
|
IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
|
|
)
|
|
{
|
|
ABSOLUTE_POINTER_DEV *TouchDev;
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// Unload any higher level drivers
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
ControllerHandle,
|
|
&gEfiAbsolutePointerProtocolGuid,
|
|
(VOID **)&TouchDev,
|
|
This->DriverBindingHandle,
|
|
ControllerHandle,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "INFO - TOUCH driver still in use, Status: %r\n", Status));
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Done with the upper level protocol
|
|
//
|
|
gBS->CloseProtocol (
|
|
ControllerHandle,
|
|
&gEfiAbsolutePointerProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
ControllerHandle
|
|
);
|
|
|
|
//
|
|
// Disable touch support
|
|
//
|
|
Status = gBS->UninstallMultipleProtocolInterfaces (
|
|
ControllerHandle,
|
|
&gEfiAbsolutePointerProtocolGuid,
|
|
&TouchDev->AbsolutePointerProtocol,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "ERROR - TOUCH driver failed to unload upper protocol, Status: %r\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
gBS->CloseEvent (TouchDev->AbsolutePointerProtocol.WaitForInput);
|
|
|
|
gBS->CloseProtocol (
|
|
ControllerHandle,
|
|
&gEfiI2cIoProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
ControllerHandle
|
|
);
|
|
|
|
FreeUnicodeStringTable (TouchDev->ControllerNameTable);
|
|
|
|
FreePool (TouchDev);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Helper function that concatenates two 8-bit variables into 16bit value
|
|
**/
|
|
UINT16
|
|
GetLength (
|
|
IN UINT8 Lsb,
|
|
IN UINT8 Msb
|
|
)
|
|
{
|
|
return ((Msb<<8) + Lsb);
|
|
}
|
|
|
|
/**
|
|
Event used for first read HID Descriptor transaction from Touch Device
|
|
|
|
@param[in] Event Event called by I2C HOST on the I2C request completion
|
|
@param[in] Context Context passed by the event creator
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
HidDescriptorReceivedEvent (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
if ((Event == NULL) || (Context == NULL)) {
|
|
return;
|
|
}
|
|
*((BOOLEAN *) Context) = FALSE;
|
|
}
|
|
|
|
/**
|
|
Get HID descriptor via I2C protocol.
|
|
|
|
@param DriverContext The code context.
|
|
|
|
@retval EFI_SUCCESS The operation completed successfully.
|
|
@retval EFI_DEVICE_ERROR Failed to obtain HID Descriptor.
|
|
@retval Others An unexpected error occurred.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
GetHidDescriptor (
|
|
IN OUT ABSOLUTE_POINTER_DEV *TouchDev
|
|
)
|
|
{
|
|
UINT16 HidDataLength;
|
|
EFI_STATUS Status;
|
|
UINT8 WriteBuffer[2];
|
|
EFI_I2C_REQUEST_PACKET *RequestPacket;
|
|
BOOLEAN WaitForResponse;
|
|
UINT8 ExitLoopCounter;
|
|
EFI_EVENT CallbackEvent;
|
|
|
|
WaitForResponse = TRUE;
|
|
ExitLoopCounter = 100;
|
|
|
|
WriteBuffer[0] = (UINT8)(TouchDev->HidI2cPlatformSupport.GetHidRegisterAddress(TouchDev->ControllerHandle) & 0xff);
|
|
WriteBuffer[1] = (UINT8)(TouchDev->HidI2cPlatformSupport.GetHidRegisterAddress(TouchDev->ControllerHandle) >> 8) & 0xff;
|
|
|
|
Status = gBS->CreateEvent (
|
|
EVT_NOTIFY_SIGNAL,
|
|
TPL_NOTIFY,
|
|
HidDescriptorReceivedEvent,
|
|
&WaitForResponse,
|
|
&CallbackEvent
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "GetHidDescriptor Failed to Create CallbackEvent with Status: %r\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
RequestPacket = NewRequestPacket(2);
|
|
if (RequestPacket == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
RequestPacket->Operation[0].Flags = 0;
|
|
RequestPacket->Operation[0].LengthInBytes = 2;
|
|
RequestPacket->Operation[0].Buffer = WriteBuffer;
|
|
RequestPacket->Operation[1].Flags = I2C_FLAG_READ;
|
|
RequestPacket->Operation[1].LengthInBytes = DEFAULT_HID_DESCRIPTOR_LENGTH-2;
|
|
RequestPacket->Operation[1].Buffer = (UINT8*)(&(TouchDev->HidDescriptor));
|
|
TouchDev->I2cIoProtocol->QueueRequest (TouchDev->I2cIoProtocol,0,CallbackEvent,RequestPacket,&Status);
|
|
while (WaitForResponse && (ExitLoopCounter > 0)) {
|
|
gBS->Stall (EFI_TIMER_PERIOD_MILLISECONDS(10));
|
|
ExitLoopCounter--;
|
|
}
|
|
|
|
gBS->CloseEvent (CallbackEvent);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "GetHidDescriptor Failed to read hid descriptor. Please check I2C descriptor and connection. Error: %r\n", Status));
|
|
DeleteRequestPacket (RequestPacket);
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
if (ExitLoopCounter == 0) {
|
|
DEBUG ((DEBUG_ERROR, "GetHidDescriptor Failed to read hid descriptor in 100ms\n"));
|
|
DeleteRequestPacket (RequestPacket);
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
HidDataLength = GetLength(TouchDev->HidDescriptor.HIDDescLengthLsb, TouchDev->HidDescriptor.HIDDescLengthLsb);
|
|
DEBUG ((DEBUG_ERROR, "GetHidDescriptor Length: %d\n", HidDataLength));
|
|
DeleteRequestPacket (RequestPacket);
|
|
if (HidDataLength == 0 || HidDataLength == 0xFFFF) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Set HID Power via I2C protocol.
|
|
|
|
@param DriverContext The code context.
|
|
|
|
@retval EFI_SUCCESS The operation completed successfully.
|
|
@retval Others An unexpected error occurred.
|
|
**/
|
|
EFI_STATUS
|
|
SetHidPower (
|
|
IN ABSOLUTE_POINTER_DEV *TouchDev
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 WriteBuffer[4];
|
|
EFI_I2C_REQUEST_PACKET *RequestPacket;
|
|
|
|
WriteBuffer[0] = TouchDev->HidDescriptor.CommandRegisterLsb;
|
|
WriteBuffer[1] = TouchDev->HidDescriptor.CommandRegisterMsb;
|
|
WriteBuffer[2] = POWER_STATE_ON;
|
|
WriteBuffer[3] = OPCODE_SET_POWER;
|
|
|
|
RequestPacket = NewRequestPacket(1);
|
|
if (RequestPacket == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
RequestPacket->Operation[0].Flags = 0;
|
|
RequestPacket->Operation[0].LengthInBytes = 4;
|
|
RequestPacket->Operation[0].Buffer = WriteBuffer;
|
|
Status = TouchDev->I2cIoProtocol->QueueRequest (TouchDev->I2cIoProtocol,0,NULL,RequestPacket,NULL);
|
|
DeleteRequestPacket(RequestPacket);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Set HID Reset via I2C protocol.
|
|
|
|
@param DriverContext The code context.
|
|
|
|
@retval EFI_SUCCESS The operation completed successfully.
|
|
@retval Others An unexpected error occurred.
|
|
**/
|
|
EFI_STATUS
|
|
SetHidReset (
|
|
IN ABSOLUTE_POINTER_DEV *TouchDev
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 WriteBuffer[4];
|
|
UINT8 ReadData[20];
|
|
EFI_I2C_REQUEST_PACKET *RequestPacket;
|
|
|
|
Status = EFI_SUCCESS;
|
|
WriteBuffer[0] = TouchDev->HidDescriptor.CommandRegisterLsb;
|
|
WriteBuffer[1] = TouchDev->HidDescriptor.CommandRegisterMsb;
|
|
WriteBuffer[2] = RESET_REPORT_ID;
|
|
WriteBuffer[3] = OPCODE_RESET;
|
|
|
|
RequestPacket = NewRequestPacket(2);
|
|
if (RequestPacket == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
RequestPacket->Operation[0].Flags = 0;
|
|
RequestPacket->Operation[0].LengthInBytes = 4;
|
|
RequestPacket->Operation[0].Buffer = WriteBuffer;
|
|
RequestPacket->Operation[1].Flags = I2C_FLAG_READ;
|
|
RequestPacket->Operation[1].LengthInBytes = 20;
|
|
RequestPacket->Operation[1].Buffer = ReadData;
|
|
Status = TouchDev->I2cIoProtocol->QueueRequest (TouchDev->I2cIoProtocol,0,NULL,RequestPacket,NULL);
|
|
DeleteRequestPacket(RequestPacket);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Get new touch data via I2C.
|
|
|
|
@param DriverContext The Code context.
|
|
@param *X The touch controller report x location.
|
|
@param *Y The touch controller report y location.
|
|
@param *Z The touch controller report z location.
|
|
@param ActiveButtons The touch controller report touch/release event.
|
|
|
|
@retval EFI_SUCCESS The operation completed successfully.
|
|
@retval Others An unexpected error occurred.
|
|
**/
|
|
EFI_STATUS
|
|
GetTouchCoordinates (
|
|
IN ABSOLUTE_POINTER_DEV *TouchDev,
|
|
IN OUT UINT16 *X,
|
|
IN OUT UINT16 *Y,
|
|
IN OUT UINT16 *Z,
|
|
IN OUT UINT16 *ActiveButtons
|
|
)
|
|
{
|
|
UINT8* ReadBuffer;
|
|
UINT32 InputLength;
|
|
EFI_STATUS Status = EFI_SUCCESS;
|
|
TOUCH_OUTPUT Output;
|
|
TOUCH_XY_BOUNDARY MinMax;
|
|
EFI_I2C_REQUEST_PACKET *RequestPacket;
|
|
|
|
InputLength = GetLength(TouchDev->HidDescriptor.MaxInputLengthLsb, TouchDev->HidDescriptor.MaxInputLengthMsb);
|
|
ReadBuffer = AllocatePool(InputLength);
|
|
if (ReadBuffer == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
RequestPacket = NewRequestPacket(1);
|
|
if (RequestPacket == NULL) {
|
|
FreePool(ReadBuffer);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
RequestPacket->Operation[0].Flags = I2C_FLAG_READ;
|
|
RequestPacket->Operation[0].LengthInBytes = InputLength;
|
|
RequestPacket->Operation[0].Buffer = ReadBuffer;
|
|
Status = TouchDev->I2cIoProtocol->QueueRequest (TouchDev->I2cIoProtocol,0,NULL,RequestPacket,NULL);
|
|
DeleteRequestPacket(RequestPacket);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool(ReadBuffer);
|
|
return Status;
|
|
}
|
|
|
|
Status = ParseInput(TouchDev->InputReportTable, ReadBuffer, &Output, &MinMax);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
*X = (UINT16)Output.X;
|
|
*Y = (UINT16)Output.Y;
|
|
*Z = 0;
|
|
*ActiveButtons = (UINT16)Output.B;
|
|
|
|
TouchDev->Mode.AbsoluteMaxX = MinMax.MaxX;
|
|
TouchDev->Mode.AbsoluteMaxY = MinMax.MaxY;
|
|
|
|
FreePool(ReadBuffer);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Check the touch device do send interrupt event.
|
|
|
|
@param[in] Event Event whose notification function is being invoked.
|
|
@param[in] Context Pointer to the notification function's context,
|
|
which is implementation-dependent. Context corresponds
|
|
to NotifyContext in CreateEventEx().
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
CheckDataAvailableCallback (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
ABSOLUTE_POINTER_DEV *TouchDev;
|
|
UINT16 X;
|
|
UINT16 Y;
|
|
UINT16 Z;
|
|
UINT16 ActiveButtons;
|
|
|
|
TouchDev = (ABSOLUTE_POINTER_DEV *)Context;
|
|
if (TouchDev->HidI2cPlatformSupport.CheckInterrupt(TouchDev->ControllerHandle)) {
|
|
if (GetTouchCoordinates(TouchDev, &X, &Y, &Z, &ActiveButtons) == EFI_SUCCESS) {
|
|
DEBUG ((DEBUG_INFO, "CheckDataAvailableCallback X: 0x%X Y: 0x%X Z: 0x%X Btn: 0x%X.\n", X, Y, Z, ActiveButtons));
|
|
TouchDev->NewStateAvailable = TRUE;
|
|
TouchDev->State.CurrentX = X;
|
|
TouchDev->State.CurrentY = Y;
|
|
TouchDev->State.CurrentZ = Z;
|
|
TouchDev->State.ActiveButtons = ActiveButtons? 1:0;
|
|
gBS->SignalEvent(TouchDev->AbsolutePointerProtocol.WaitForInput);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Gets HID descriptor and initializes dictionary for parsing touch input data
|
|
|
|
@param DriverContext The Code context.
|
|
|
|
@retval EFI_SUCCESS The operation completed successfully.
|
|
@retval Others An unexpected error occurred.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
TouchDevInit(
|
|
IN ABSOLUTE_POINTER_DEV *TouchDev
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = GetHidDescriptor (TouchDev);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "GetHidDescriptor: %r\n", Status));
|
|
return Status;
|
|
}
|
|
DEBUG ((DEBUG_INFO, "TouchDevInit VendorId 0x%02x.\n", TouchDev->HidDescriptor.VendorId));
|
|
DEBUG ((DEBUG_INFO, "TouchDevInit ProductId 0x%02x.\n", TouchDev->HidDescriptor.ProductId));
|
|
|
|
Status = SetHidPower (TouchDev);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "SetHidPower: %r\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
Status = SetHidReset (TouchDev);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "SetHidReset: %r\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
Status = ReadInputReportDescriptor (TouchDev);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "ReadInputReportDescriptor: %r\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
Status = SetInterruptFrequency (TouchDev);
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
/*
|
|
HID's InputReportDescriptor consists of variable length tokens.
|
|
On entry, Descriptor points to InputReportDescriptor's start or a boundary
|
|
between two tokens somewhere inside descriptor. Size means number of bytes
|
|
before end of Descriptor.
|
|
Function returns a single Token.
|
|
On exit, Descriptor points next boundary between tokens, just after the token
|
|
that was returned. Size is decreased by as many bytes as Descriptor pointer
|
|
was moved forward.
|
|
Tokens in Long Item format are only partially supported; they will return
|
|
invalid value but Descriptor pointer and Size will be updated correctly to
|
|
allow further parsing
|
|
*/
|
|
TOKEN
|
|
GetToken (
|
|
IN OUT UINT8** Descriptor,
|
|
IN OUT UINT16* Size
|
|
)
|
|
{
|
|
TOKEN Token;
|
|
UINT16 Length;
|
|
UINT8 DataSize;
|
|
Token.ID = ((**Descriptor)>>2);
|
|
Token.Value = 0;
|
|
DataSize = (**Descriptor)&0x3;
|
|
|
|
if (**Descriptor==0xFE) {
|
|
// Long Item format
|
|
Token.ID = *(*Descriptor+2);
|
|
//don't care about value - Long Items are not supported
|
|
Length = *(*Descriptor+1);
|
|
(*Descriptor)+=Length;
|
|
(*Size)-=Length;
|
|
return Token;
|
|
}
|
|
switch(DataSize) {
|
|
case 0:
|
|
Token.Value = 0;
|
|
break;
|
|
case 1:
|
|
Token.Value = *(*Descriptor+1);
|
|
break;
|
|
case 2:
|
|
Token.Value = *(*Descriptor+1) + (*(*Descriptor+2)<<8);
|
|
break;
|
|
case 3:
|
|
Token.Value = *(*Descriptor+1) + ((*(*Descriptor+2))<<8) + ((*(*Descriptor+3))<<16) + ((*(*Descriptor+4))<<24);
|
|
DataSize = 4;
|
|
break;
|
|
default: ;
|
|
}
|
|
// move descriptor pointer and decrease size by datasize + 1 byte for ID
|
|
(*Descriptor) += (1+DataSize);
|
|
(*Size) -= (1+DataSize);
|
|
|
|
return Token;
|
|
}
|
|
|
|
/*
|
|
Stack contains a set of data retrieved from parsing InputReportDescriptor.
|
|
This function checks if that set of data constitutes a valid InputData
|
|
dictionary, and if so then puts it into ReportTable, table of dictionaries.
|
|
Unless this is the 1st dictionary for a particular device, this means
|
|
allocating new bigger table and deallocating old table
|
|
|
|
@param Stack A pointer to the PARSER_STACK
|
|
@param ReportTable Pointer to the final report table that contains all the Reports
|
|
|
|
*/
|
|
VOID
|
|
ExportReport (
|
|
PARSER_STACK* Stack,
|
|
INPUT_REPORT_TABLE* ReportTable
|
|
)
|
|
{
|
|
INPUT_REPORT_FORMAT* ArrayOfReports;
|
|
|
|
if (Stack->TouchPanelUsage) {
|
|
ReportTable->TouchPanel = 1;
|
|
}
|
|
|
|
ArrayOfReports = (INPUT_REPORT_FORMAT*) AllocatePool(sizeof(INPUT_REPORT_FORMAT) * (ReportTable->Quantity + 1));
|
|
ASSERT(ArrayOfReports != NULL);
|
|
if (ArrayOfReports != NULL) {
|
|
if (ReportTable->Quantity != 0) {
|
|
CopyMem(ArrayOfReports, ReportTable->Report, sizeof(INPUT_REPORT_FORMAT) * ReportTable->Quantity);
|
|
FreePool(ReportTable->Report);
|
|
}
|
|
ReportTable->Report = ArrayOfReports;
|
|
CopyMem(&(ReportTable->Report[ReportTable->Quantity]), &(Stack->TempReport), sizeof(INPUT_REPORT_FORMAT));
|
|
ReportTable->Quantity++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Stack is pointer to a not-yet-complete InputReport dictionary.
|
|
This function consumes Token retrieved from InputReportDescriptor stream
|
|
and uses it to update the dictionary.
|
|
Once a dictionary is completed, it calls ExportReport() which puts the
|
|
dictionary in a table of dictionaries. Then it clears internal data
|
|
and prepares to build a new one.
|
|
This implementation of Descriptor parser ignores all types of data except for
|
|
information on how to decode Button presses and X/Y touch coordinates, as this
|
|
is the only info relevant for touchpanels.
|
|
|
|
@param Stack A pointer to the PARSER_STACK
|
|
@param Token Current Token
|
|
@param ReportTable Pointer to the final report table that contains all the Reports
|
|
*/
|
|
VOID
|
|
UpdateStack(
|
|
PARSER_STACK* Stack,
|
|
TOKEN Token,
|
|
INPUT_REPORT_TABLE* ReportTable
|
|
)
|
|
{
|
|
switch (Token.ID) {
|
|
case USE_PAGE: Stack->GlobalUsage = Token.Value; break;
|
|
case USAGE:
|
|
if (Token.Value>0xFFFF) { //special case for 32 bit usages - they indicate Usage + Use Page together
|
|
Stack->GlobalUsage = Token.Value >> 16;
|
|
Token.Value = Token.Value & 0xFFFF;
|
|
}
|
|
Stack->Usages++;
|
|
if (Stack->GlobalUsage == DIGITIZER && Token.Value == TOUCHPANEL) { Stack->TouchPanelUsage = 1; }
|
|
if (Stack->GlobalUsage == DIGITIZER && Token.Value == TIP_SWITCH) { Stack->UsageB = Stack->Usages;}
|
|
if (Stack->GlobalUsage == DESKTOP && Token.Value == X_AXIS) { Stack->UsageX = Stack->Usages; }
|
|
if (Stack->GlobalUsage == DESKTOP && Token.Value == Y_AXIS) { Stack->UsageY = Stack->Usages; }
|
|
break;
|
|
case LOG_MIN: Stack->LogMin = Token.Value; break;
|
|
case LOG_MAX: Stack->LogMax = Token.Value; break;
|
|
case REP_SIZE: Stack->ReportSize = Token.Value; break;
|
|
case REP_COUNT: Stack->ReportCount = Token.Value; break;
|
|
case REPORT_ID:
|
|
//
|
|
// Is new report?
|
|
//
|
|
if (Stack->TempReport.Id != 0xFFFF && Stack->TempReport.Id != Token.Value) {
|
|
ExportReport(Stack, ReportTable);
|
|
}
|
|
Stack->Usages = 0;
|
|
Stack->UsageX = 0;
|
|
Stack->UsageY = 0;
|
|
Stack->UsageB = 0;
|
|
Stack->LogMin = 0;
|
|
Stack->LogMax = 0;
|
|
Stack->TouchPanelUsage = 0;
|
|
Stack->ReportSize = 0;
|
|
Stack->ReportCount = 0;
|
|
Stack->TempReport.Id = Token.Value;
|
|
|
|
//
|
|
// New Report does not indicate new collection set!
|
|
//
|
|
ZeroMem(Stack->TempReport.Collection, sizeof(INPUT_REPORT_COLLECTION) * MAX_HID_COLLECTION);
|
|
Stack->TempReport.CollectionCount = 1;
|
|
Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].ValidCollection = FALSE;
|
|
|
|
break;
|
|
case INPUT:
|
|
if (Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitStopB == 0 && Stack->UsageB != 0 && Stack->Usages == Stack->UsageB) {
|
|
Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitStartB = Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitsTotal;
|
|
Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitStopB = Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitsTotal + (Stack->ReportSize * Stack->ReportCount);
|
|
Stack->LogMax = 0;
|
|
Stack->LogMin = 0;
|
|
}
|
|
if (Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitStopX == 0 && Stack->UsageX != 0 && Stack->Usages == Stack->UsageX) {
|
|
Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitStartX = Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitsTotal;
|
|
Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitStopX = Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitsTotal + (Stack->ReportSize * Stack->ReportCount);
|
|
Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].LogMaxX = Stack->LogMax;
|
|
Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].LogMinX = Stack->LogMin;
|
|
Stack->LogMax = 0;
|
|
Stack->LogMin = 0;
|
|
}
|
|
if (Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitStopY == 0 && Stack->UsageY != 0 && Stack->Usages == Stack->UsageY) {
|
|
Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitStartY = Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitsTotal;
|
|
Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitStopY = Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitsTotal + (Stack->ReportSize * Stack->ReportCount);
|
|
Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].LogMaxY = Stack->LogMax;
|
|
Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].LogMinY = Stack->LogMin;
|
|
Stack->LogMax = 0;
|
|
Stack->LogMin = 0;
|
|
}
|
|
Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitsTotal += (Stack->ReportSize * Stack->ReportCount);
|
|
break;
|
|
case COLLECTION:
|
|
if (Token.Value == APPLICATION) { }
|
|
else if (Token.Value == LOGICAL) {
|
|
if (Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].ValidCollection == TRUE) {
|
|
Stack->TempReport.CollectionCount += 1;
|
|
Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitsTotal = Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 2].BitsTotal;
|
|
}
|
|
Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].ValidCollection = TRUE;
|
|
}
|
|
break;
|
|
case END_COLLECTION:
|
|
Stack->Usages = 0;
|
|
Stack->UsageB = 0;
|
|
Stack->UsageX = 0;
|
|
Stack->UsageY = 0;
|
|
Stack->LogMin = 0;
|
|
Stack->LogMax = 0;
|
|
Stack->ReportSize = 0;
|
|
Stack->ReportCount = 0;
|
|
break;
|
|
default:;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Retrieves single bit from multi-byte stream
|
|
*/
|
|
UINT32
|
|
AccessBit (
|
|
UINT8* InputStream,
|
|
UINT32 Bit
|
|
)
|
|
{
|
|
return ( ( InputStream[Bit/8] & (1<<(Bit%8)) ) ? 1:0 );
|
|
}
|
|
|
|
VOID
|
|
ShowReportTable (
|
|
INPUT_REPORT_TABLE ReportTable
|
|
)
|
|
{
|
|
UINT32 Index;
|
|
UINT32 Collections;
|
|
|
|
for (Index = 0; Index < ReportTable.Quantity; Index++) {
|
|
DEBUG((DEBUG_INFO, "Report Id_%x \n", ReportTable.Report[Index].Id));
|
|
for (Collections = 0; Collections < ReportTable.Report[Index].CollectionCount; Collections++) {
|
|
DEBUG((DEBUG_INFO, " Collection Id_%x ", Collections));
|
|
DEBUG((DEBUG_INFO, "MaxX_%x ", ReportTable.Report[Index].Collection[Collections].LogMaxX));
|
|
DEBUG((DEBUG_INFO, "MinX_%x ", ReportTable.Report[Index].Collection[Collections].LogMinX));
|
|
DEBUG((DEBUG_INFO, "MaxY_%x ", ReportTable.Report[Index].Collection[Collections].LogMaxY));
|
|
DEBUG((DEBUG_INFO, "MinY_%x ", ReportTable.Report[Index].Collection[Collections].LogMinY));
|
|
DEBUG((DEBUG_INFO, "BitB_%d-%d ", ReportTable.Report[Index].Collection[Collections].BitStartB, ReportTable.Report[Index].Collection[Collections].BitStopB));
|
|
DEBUG((DEBUG_INFO, "BitX_%d-%d ", ReportTable.Report[Index].Collection[Collections].BitStartX, ReportTable.Report[Index].Collection[Collections].BitStopX));
|
|
DEBUG((DEBUG_INFO, "BitY_%d-%d ", ReportTable.Report[Index].Collection[Collections].BitStartY, ReportTable.Report[Index].Collection[Collections].BitStopY));
|
|
DEBUG((DEBUG_INFO, "\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
This function limits touchpanel's interrupt frequency to
|
|
prevent traffic congestion on I2C
|
|
*/
|
|
EFI_STATUS
|
|
SetInterruptFrequency (
|
|
IN ABSOLUTE_POINTER_DEV *TouchDev
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINT8 WriteBuffer [6];
|
|
EFI_STATUS Status;
|
|
EFI_I2C_REQUEST_PACKET *RequestPacket;
|
|
|
|
Status = EFI_SUCCESS;
|
|
RequestPacket = NewRequestPacket (1);
|
|
if (RequestPacket == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
RequestPacket->Operation[0].Flags = 0;
|
|
RequestPacket->Operation[0].LengthInBytes = 6;
|
|
RequestPacket->Operation[0].Buffer = WriteBuffer;
|
|
|
|
for (Index = 0; Index < TouchDev->InputReportTable.Quantity; Index++) {
|
|
//
|
|
// for each ReportID this driver understands, limit interrupt frequency to 100ms
|
|
// It is necessary due to extremely slow I2C driver operation
|
|
//
|
|
WriteBuffer[0] = TouchDev->HidDescriptor.DataRegisterLsb;
|
|
WriteBuffer[1] = TouchDev->HidDescriptor.DataRegisterMsb;
|
|
WriteBuffer[2] = 4; //data length LSB
|
|
WriteBuffer[3] = 0; //data length MSB
|
|
WriteBuffer[4] = 0x64; //time between interrupts, in ms (LSB)
|
|
WriteBuffer[5] = 0; //time between interrupts, in ms (MSB)
|
|
Status = TouchDev->I2cIoProtocol->QueueRequest (TouchDev->I2cIoProtocol,0,NULL,RequestPacket,NULL);
|
|
if (EFI_ERROR(Status)) {
|
|
break;
|
|
}
|
|
WriteBuffer[0] = TouchDev->HidDescriptor.CommandRegisterLsb;
|
|
WriteBuffer[1] = TouchDev->HidDescriptor.CommandRegisterMsb;
|
|
WriteBuffer[2] = (UINT8)TouchDev->InputReportTable.Report[Index].Id;
|
|
WriteBuffer[3] = SET_IDLE;
|
|
WriteBuffer[4] = 0;
|
|
WriteBuffer[5] = 0;
|
|
Status = TouchDev->I2cIoProtocol->QueueRequest (TouchDev->I2cIoProtocol,0,NULL,RequestPacket,NULL);
|
|
if (EFI_ERROR(Status)) {
|
|
break;
|
|
}
|
|
}
|
|
DeleteRequestPacket(RequestPacket);
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
A HID-compliant device may have more than 1 functionality, for example
|
|
digitizer/touchpanel/pen/button etc. Each functionality reports data in
|
|
a different format. In order to correctly parse that data, a set of dictionaries
|
|
must be built.
|
|
This function reads HID device's InputReportDescriptor and uses its data to
|
|
create dictionaries for all types of InputReports this device supports.
|
|
Such set of dictionaries is held in InputReportTable.
|
|
InputDataDescriptor is parsed sequentially. In a loop, GetToken() extracts single
|
|
data token from the descriptor stream. Those tokens are passed to UpdateStack()
|
|
which updates ParseStack, which is a not-yet-complete dictionary. Once
|
|
a dictionary is complete, it is either added to InputReportTable or discarded.
|
|
*/
|
|
EFI_STATUS
|
|
ReadInputReportDescriptor (
|
|
IN ABSOLUTE_POINTER_DEV *TouchDev
|
|
)
|
|
{
|
|
UINT8* InputReportDescriptor;
|
|
UINT8* MovingPointer;
|
|
UINT8 WriteBuffer[2];
|
|
UINT16 InputReportLength;
|
|
PARSER_STACK ParseStack;
|
|
TOKEN Token;
|
|
EFI_STATUS Status;
|
|
EFI_I2C_REQUEST_PACKET *RequestPacket;
|
|
|
|
TouchDev->InputReportTable.Quantity = 0;
|
|
TouchDev->InputReportTable.Report = NULL;
|
|
|
|
ZeroMem(&ParseStack, sizeof(PARSER_STACK));
|
|
|
|
InputReportLength = TouchDev->HidDescriptor.ReportDescRegLengthLsb + (TouchDev->HidDescriptor.ReportDescRegLengthMsb<<8);
|
|
InputReportDescriptor = AllocatePool(InputReportLength);
|
|
if (InputReportDescriptor == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
MovingPointer = InputReportDescriptor;
|
|
WriteBuffer[0] = TouchDev->HidDescriptor.ReportDescRegisterLsb;
|
|
WriteBuffer[1] = TouchDev->HidDescriptor.ReportDescRegisterMsb;
|
|
|
|
RequestPacket = NewRequestPacket(2);
|
|
if (RequestPacket == NULL) {
|
|
FreePool (InputReportDescriptor);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
RequestPacket->Operation[0].Flags = 0;
|
|
RequestPacket->Operation[0].LengthInBytes = sizeof (WriteBuffer);
|
|
RequestPacket->Operation[0].Buffer = WriteBuffer;
|
|
RequestPacket->Operation[1].Flags = I2C_FLAG_READ;
|
|
RequestPacket->Operation[1].LengthInBytes = InputReportLength;
|
|
RequestPacket->Operation[1].Buffer = InputReportDescriptor;
|
|
Status = TouchDev->I2cIoProtocol->QueueRequest (TouchDev->I2cIoProtocol,0,NULL,RequestPacket,NULL);
|
|
DeleteRequestPacket(RequestPacket);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (InputReportDescriptor);
|
|
return Status;
|
|
}
|
|
|
|
ParseStack.TempReport.Id = 0xFFFF; //Invalid/Default report
|
|
|
|
while(InputReportLength>0) {
|
|
Token = GetToken(&MovingPointer, &InputReportLength);
|
|
UpdateStack(&ParseStack, Token, &(TouchDev->InputReportTable) );
|
|
}
|
|
ExportReport(&ParseStack, &(TouchDev->InputReportTable) );
|
|
ShowReportTable(TouchDev->InputReportTable);
|
|
|
|
FreePool (InputReportDescriptor);
|
|
//if(TouchDev->InputReportTable.TouchPanel == 0) {
|
|
// this is to prevent driver from installing on touchpads, gyroscopes etc; the HID layer is compatible and driver would
|
|
// read input data correctly, but would interpret it in unexpected and misleading way
|
|
// return EFI_UNSUPPORTED;
|
|
//}
|
|
if(TouchDev->InputReportTable.Quantity == 0) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
This function uses dictionaries to parse incoming InputReport and convert it into X/Y coordinates plus Button info.
|
|
|
|
*/
|
|
EFI_STATUS
|
|
ParseInput (
|
|
INPUT_REPORT_TABLE ReportTable,
|
|
UINT8 *InputStream,
|
|
TOUCH_OUTPUT *Output,
|
|
TOUCH_XY_BOUNDARY *MinMax
|
|
)
|
|
{
|
|
UINT32 IdPresent = 0;
|
|
UINT32 Index;
|
|
UINT32 BitIndex;
|
|
UINT8* RawData;
|
|
UINT32 Collections;
|
|
|
|
Output->X = 0;
|
|
Output->Y = 0;
|
|
Output->B = 0;
|
|
|
|
|
|
MinMax->MaxX = 0;
|
|
MinMax->MinX = 0;
|
|
MinMax->MaxY = 0;
|
|
MinMax->MinY = 0;
|
|
|
|
//
|
|
// HID I2C devices support either a single or multiple InputReport formats.
|
|
// In case of multiple, every report is prefixed with ReportID.
|
|
// In case of single, ReportID may or may not be present. This information is
|
|
// stored in device's dictionaries
|
|
//
|
|
if (ReportTable.Quantity > 1 || (ReportTable.Quantity != 0 && ReportTable.Report[0].Id != 0)) {
|
|
IdPresent = 1;
|
|
}
|
|
//
|
|
//sets pointer to beginning of useful data. Skips Length bytes and ReportID byte if present.
|
|
//
|
|
RawData = InputStream + I2C_HID_LENGTH_PREFIX;
|
|
if (IdPresent) {
|
|
RawData++;
|
|
}
|
|
|
|
//
|
|
// Incoming data is parsed using proper dictionary selected by ReportID. In cases where
|
|
// ReportID is not provided, it is guaranteed that only a single dictionary exists
|
|
//
|
|
// Dictionary describes which bits from input stream hold X / Y / Button data.
|
|
// There's no guarantee coordinates will be byte-aligned, therefore we use bit access
|
|
//
|
|
for (Index = 0; Index < ReportTable.Quantity; Index++) {
|
|
if (!IdPresent || InputStream[2] == ReportTable.Report[Index].Id) {
|
|
Collections = 0;
|
|
for (Collections = 0; Collections < ReportTable.Report[Index].CollectionCount; Collections++) {
|
|
//
|
|
// ignore other collections if already got valid data
|
|
//
|
|
if (Output->X != 0 && Output->Y != 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
Output->B = 0;
|
|
Output->X = 0;
|
|
Output->Y = 0;
|
|
Output->B = 0;
|
|
MinMax->MaxX = 0;
|
|
MinMax->MinX = 0;
|
|
MinMax->MaxY = 0;
|
|
MinMax->MinY = 0;
|
|
for (BitIndex = ReportTable.Report[Index].Collection[Collections].BitStartX; BitIndex<ReportTable.Report[Index].Collection[Collections].BitStopX; BitIndex++) {
|
|
Output->X += AccessBit(RawData, BitIndex) << (BitIndex - ReportTable.Report[Index].Collection[Collections].BitStartX);
|
|
}
|
|
MinMax->MinX = ReportTable.Report[Index].Collection[Collections].LogMinX;
|
|
MinMax->MaxX = ReportTable.Report[Index].Collection[Collections].LogMaxX;
|
|
for (BitIndex = ReportTable.Report[Index].Collection[Collections].BitStartY; BitIndex<ReportTable.Report[Index].Collection[Collections].BitStopY; BitIndex++) {
|
|
Output->Y += AccessBit(RawData, BitIndex) << (BitIndex - ReportTable.Report[Index].Collection[Collections].BitStartY);
|
|
}
|
|
MinMax->MinY = ReportTable.Report[Index].Collection[Collections].LogMinY;
|
|
MinMax->MaxY = ReportTable.Report[Index].Collection[Collections].LogMaxY;
|
|
for (BitIndex = ReportTable.Report[Index].Collection[Collections].BitStartB; BitIndex<ReportTable.Report[Index].Collection[Collections].BitStopB; BitIndex++) {
|
|
Output->B += AccessBit(RawData, BitIndex) << (BitIndex - ReportTable.Report[Index].Collection[Collections].BitStartB);
|
|
}
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
return EFI_NOT_FOUND;
|
|
}
|